2012-09-07 08:09:41 -04:00
< ? php
/**
2016-07-21 10:49:16 -04:00
* @ copyright Copyright ( c ) 2016 , ownCloud , Inc .
*
2015-10-05 14:54:56 -04:00
* @ author Andreas Fischer < bantu @ owncloud . com >
2019-02-15 16:41:29 -05:00
* @ author Arthur Schiwon < blizzz @ arthur - schiwon . de >
2015-03-26 06:44:34 -04:00
* @ author Bart Visscher < bartv @ thisnet . nl >
2016-05-26 13:56:05 -04:00
* @ author Björn Schießle < bjoern @ schiessle . org >
2020-03-31 04:49:10 -04:00
* @ author Christoph Wurst < christoph @ winzerhof - wurst . at >
2016-05-26 13:56:05 -04:00
* @ author Frank Karlitschek < frank @ karlitschek . de >
2016-01-12 09:02:16 -05:00
* @ author Jesús Macias < jmacias @ solidgear . es >
2016-07-21 10:49:16 -04:00
* @ author Joas Schilling < coding @ schilljs . com >
* @ author Juan Pablo Villafáñez < jvillafanez @ solidgear . es >
2019-12-03 13:57:53 -05:00
* @ author Julius Härtl < jus @ bitgrid . net >
2016-05-26 13:56:05 -04:00
* @ author Lukas Reschke < lukas @ statuscode . ch >
2015-03-26 06:44:34 -04:00
* @ author Michael Gapczynski < GapczynskiM @ gmail . com >
* @ author Morris Jobke < hey @ morrisjobke . de >
* @ author Philipp Kapfer < philipp . kapfer @ gmx . at >
2016-07-21 12:13:36 -04:00
* @ author Robin Appelman < robin @ icewind . nl >
2016-01-12 09:02:16 -05:00
* @ author Robin McCorkell < robin @ mccorkell . me . uk >
2017-11-06 09:56:42 -05:00
* @ author Roeland Jago Douma < roeland @ famdouma . nl >
2015-03-26 06:44:34 -04:00
* @ author Thomas Müller < thomas . mueller @ tmit . eu >
2020-12-16 08:54:15 -05:00
* @ author Vincent Petry < vincent @ nextcloud . com >
2014-12-11 11:35:11 -05:00
*
2015-03-26 06:44:34 -04:00
* @ license AGPL - 3.0
2014-12-11 11:35:11 -05:00
*
2015-03-26 06:44:34 -04:00
* This code is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License , version 3 ,
* as published by the Free Software Foundation .
2014-12-11 11:35:11 -05:00
*
2015-03-26 06:44:34 -04:00
* This program is distributed in the hope that it will be useful ,
2014-12-11 11:35:11 -05:00
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
2015-03-26 06:44:34 -04:00
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Affero General Public License for more details .
*
* You should have received a copy of the GNU Affero General Public License , version 3 ,
2019-12-03 13:57:53 -05:00
* along with this program . If not , see < http :// www . gnu . org / licenses />
2014-12-11 11:35:11 -05:00
*
2015-02-26 05:37:37 -05:00
*/
2020-07-09 17:39:58 -04:00
namespace OCA\Files_External ;
2019-02-11 17:18:08 -05:00
use OCA\Files_External\Config\IConfigHandler ;
2019-11-22 14:52:10 -05:00
use OCA\Files_External\Config\UserContext ;
use OCA\Files_External\Lib\Backend\Backend ;
use OCA\Files_External\Service\BackendService ;
2018-01-25 17:16:13 -05:00
use OCA\Files_External\Service\GlobalStoragesService ;
2019-11-22 14:52:10 -05:00
use OCA\Files_External\Service\UserGlobalStoragesService ;
2018-01-25 17:16:13 -05:00
use OCA\Files_External\Service\UserStoragesService ;
2019-11-22 14:52:10 -05:00
use OCP\Files\StorageNotAvailableException ;
use phpseclib\Crypt\AES ;
2015-08-03 00:39:53 -04:00
2015-02-26 05:37:37 -05:00
/**
* Class to configure mount . json globally and for users
2014-03-18 16:15:11 -04:00
*/
2020-07-09 17:39:58 -04:00
class MountConfig {
2014-03-19 07:20:48 -04:00
// TODO: make this class non-static and give it a proper namespace
2012-09-07 08:09:41 -04:00
2020-04-10 10:54:27 -04:00
public const MOUNT_TYPE_GLOBAL = 'global' ;
public const MOUNT_TYPE_GROUP = 'group' ;
public const MOUNT_TYPE_USER = 'user' ;
public const MOUNT_TYPE_PERSONAL = 'personal' ;
2012-09-07 08:09:41 -04:00
2014-03-18 13:29:08 -04:00
// whether to skip backend test (for unit tests, as this static class is not mockable)
public static $skipTest = false ;
2021-03-26 10:07:39 -04:00
/** @var UserGlobalStoragesService */
private $userGlobalStorageService ;
/** @var UserStoragesService */
private $userStorageService ;
/** @var GlobalStoragesService */
private $globalStorageService ;
public function __construct (
UserGlobalStoragesService $userGlobalStorageService ,
UserStoragesService $userStorageService ,
GlobalStoragesService $globalStorageService
) {
$this -> userGlobalStorageService = $userGlobalStorageService ;
$this -> userStorageService = $userStorageService ;
$this -> globalStorageService = $globalStorageService ;
2014-03-19 07:20:48 -04:00
}
2019-02-11 17:18:08 -05:00
/**
* @ param mixed $input
2019-07-25 11:57:22 -04:00
* @ param string | null $userId
2019-02-11 17:18:08 -05:00
* @ return mixed
* @ throws \OCP\AppFramework\QueryException
* @ since 16.0 . 0
*/
2019-07-25 11:57:22 -04:00
public static function substitutePlaceholdersInConfig ( $input , string $userId = null ) {
2019-02-11 17:18:08 -05:00
/** @var BackendService $backendService */
2021-03-26 10:07:39 -04:00
$backendService = \OC :: $server -> get ( BackendService :: class );
2019-02-11 17:18:08 -05:00
/** @var IConfigHandler[] $handlers */
$handlers = $backendService -> getConfigHandlers ();
foreach ( $handlers as $handler ) {
2019-07-25 12:50:28 -04:00
if ( $handler instanceof UserContext && $userId !== null ) {
$handler -> setUserId ( $userId );
2019-07-25 03:31:39 -04:00
}
2019-02-11 17:18:08 -05:00
$input = $handler -> handle ( $input );
2015-03-26 14:24:37 -04:00
}
return $input ;
2014-03-19 07:20:48 -04:00
}
/**
* Test connecting using the given backend configuration
2014-12-11 11:35:11 -05:00
*
2014-03-19 07:20:48 -04:00
* @ param string $class backend class name
* @ param array $options backend configuration options
2015-11-30 05:29:06 -05:00
* @ param boolean $isPersonal
2014-10-31 06:41:07 -04:00
* @ return int see self :: STATUS_ *
2021-03-16 17:06:00 -04:00
* @ throws \Exception
2014-03-19 07:20:48 -04:00
*/
2016-06-08 06:48:33 -04:00
public static function getBackendStatus ( $class , $options , $isPersonal , $testOnly = true ) {
2014-03-18 13:29:08 -04:00
if ( self :: $skipTest ) {
2015-11-26 11:51:47 -05:00
return StorageNotAvailableException :: STATUS_SUCCESS ;
2014-03-18 13:29:08 -04:00
}
2019-04-02 17:08:34 -04:00
foreach ( $options as $key => & $option ) {
2020-04-10 08:19:56 -04:00
if ( $key === 'password' ) {
2019-04-02 17:08:34 -04:00
// no replacements in passwords
continue ;
}
2019-02-11 17:18:08 -05:00
$option = self :: substitutePlaceholdersInConfig ( $option );
2012-12-24 13:45:52 -05:00
}
if ( class_exists ( $class )) {
try {
2015-11-28 07:17:34 -05:00
/** @var \OC\Files\Storage\Common $storage */
2012-12-28 12:00:48 -05:00
$storage = new $class ( $options );
2015-01-23 16:53:21 -05:00
try {
2016-06-07 12:25:17 -04:00
$result = $storage -> test ( $isPersonal , $testOnly );
2015-01-23 16:53:21 -05:00
$storage -> setAvailability ( $result );
if ( $result ) {
2015-11-26 11:51:47 -05:00
return StorageNotAvailableException :: STATUS_SUCCESS ;
2015-01-23 16:53:21 -05:00
}
} catch ( \Exception $e ) {
$storage -> setAvailability ( false );
throw $e ;
2014-10-31 06:41:07 -04:00
}
2021-03-16 17:06:00 -04:00
} catch ( \Exception $exception ) {
2018-03-12 13:10:59 -04:00
\OC :: $server -> getLogger () -> logException ( $exception , [ 'app' => 'files_external' ]);
2015-11-06 06:29:24 -05:00
throw $exception ;
2012-12-24 13:45:52 -05:00
}
}
2015-11-26 11:51:47 -05:00
return StorageNotAvailableException :: STATUS_ERROR ;
2012-12-24 13:45:52 -05:00
}
2012-09-07 08:09:41 -04:00
/**
2014-12-11 11:35:11 -05:00
* Read the mount points in the config file into an array
*
* @ param string | null $user If not null , personal for $user , otherwise system
* @ return array
*/
2014-10-31 06:41:07 -04:00
public static function readData ( $user = null ) {
2014-04-21 16:41:45 -04:00
if ( isset ( $user )) {
2015-08-18 17:49:29 -04:00
$jsonFile = \OC :: $server -> getUserManager () -> get ( $user ) -> getHome () . '/mount.json' ;
2012-09-07 08:09:41 -04:00
} else {
2015-08-18 17:49:29 -04:00
$config = \OC :: $server -> getConfig ();
$datadir = $config -> getSystemValue ( 'datadirectory' , \OC :: $SERVERROOT . '/data/' );
$jsonFile = $config -> getSystemValue ( 'mount_file' , $datadir . '/mount.json' );
2012-09-07 08:09:41 -04:00
}
2013-02-15 19:50:40 -05:00
if ( is_file ( $jsonFile )) {
$mountPoints = json_decode ( file_get_contents ( $jsonFile ), true );
if ( is_array ( $mountPoints )) {
return $mountPoints ;
}
2012-09-07 08:09:41 -04:00
}
2020-03-26 04:30:18 -04:00
return [];
2012-09-07 08:09:41 -04:00
}
2012-12-10 15:10:28 -05:00
/**
2015-08-11 13:45:07 -04:00
* Get backend dependency message
* TODO : move into AppFramework along with templates
*
2015-11-28 07:17:34 -05:00
* @ param Backend [] $backends
2015-08-11 13:45:07 -04:00
* @ return string
2012-12-10 15:10:28 -05:00
*/
2015-08-11 13:45:07 -04:00
public static function dependencyMessage ( $backends ) {
2015-08-18 17:49:29 -04:00
$l = \OC :: $server -> getL10N ( 'files_external' );
2015-08-11 13:45:07 -04:00
$message = '' ;
$dependencyGroups = [];
foreach ( $backends as $backend ) {
foreach ( $backend -> checkDependencies () as $dependency ) {
if ( $message = $dependency -> getMessage ()) {
2016-08-09 19:01:39 -04:00
$message .= '<p>' . $message . '</p>' ;
2013-08-02 09:44:56 -04:00
} else {
2015-08-11 13:45:07 -04:00
$dependencyGroups [ $dependency -> getDependency ()][] = $backend ;
2013-05-30 11:37:47 -04:00
}
}
2015-08-11 13:45:07 -04:00
}
2013-05-30 11:37:47 -04:00
2015-08-11 13:45:07 -04:00
foreach ( $dependencyGroups as $module => $dependants ) {
2020-04-09 07:53:40 -04:00
$backends = implode ( ', ' , array_map ( function ( $backend ) {
2016-08-09 19:01:39 -04:00
return '"' . $backend -> getText () . '"' ;
2015-08-11 13:45:07 -04:00
}, $dependants ));
2020-07-09 17:39:58 -04:00
$message .= '<p>' . MountConfig :: getSingleDependencyMessage ( $l , $module , $backends ) . '</p>' ;
2013-05-30 11:37:47 -04:00
}
2015-08-11 13:45:07 -04:00
return $message ;
2013-08-02 09:44:56 -04:00
}
2013-05-30 11:37:47 -04:00
2013-08-02 09:44:56 -04:00
/**
* Returns a dependency missing message
2014-12-11 11:35:11 -05:00
*
2015-08-18 17:49:29 -04:00
* @ param \OCP\IL10N $l
2014-05-13 07:29:25 -04:00
* @ param string $module
* @ param string $backend
2015-11-30 17:00:51 -05:00
* @ return string
2013-08-02 09:44:56 -04:00
*/
2015-08-18 17:49:29 -04:00
private static function getSingleDependencyMessage ( \OCP\IL10N $l , $module , $backend ) {
2013-08-02 09:44:56 -04:00
switch ( strtolower ( $module )) {
case 'curl' :
2021-01-11 06:57:03 -05:00
return $l -> t ( 'The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it.' , [ $backend ]);
2013-08-02 09:44:56 -04:00
case 'ftp' :
2021-01-11 06:57:03 -05:00
return $l -> t ( 'The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it.' , [ $backend ]);
2013-08-02 09:44:56 -04:00
default :
2021-01-11 06:57:03 -05:00
return $l -> t ( '"%1$s" is not installed. Mounting of %2$s is not possible. Please ask your system administrator to install it.' , [ $module , $backend ]);
2013-08-02 09:44:56 -04:00
}
2012-12-10 15:10:28 -05:00
}
2014-03-18 16:15:11 -04:00
/**
* Encrypt passwords in the given config options
2014-12-11 11:35:11 -05:00
*
2014-03-18 16:15:11 -04:00
* @ param array $options mount options
* @ return array updated options
*/
2014-10-31 06:41:07 -04:00
public static function encryptPasswords ( $options ) {
2014-03-18 16:15:11 -04:00
if ( isset ( $options [ 'password' ])) {
2014-03-19 06:42:22 -04:00
$options [ 'password_encrypted' ] = self :: encryptPassword ( $options [ 'password' ]);
2014-03-19 12:56:36 -04:00
// do not unset the password, we want to keep the keys order
// on load... because that's how the UI currently works
$options [ 'password' ] = '' ;
2014-03-18 16:15:11 -04:00
}
return $options ;
}
/**
* Decrypt passwords in the given config options
2014-12-11 11:35:11 -05:00
*
2014-03-18 16:15:11 -04:00
* @ param array $options mount options
* @ return array updated options
*/
2014-10-31 06:41:07 -04:00
public static function decryptPasswords ( $options ) {
2014-03-18 16:15:11 -04:00
// note: legacy options might still have the unencrypted password in the "password" field
if ( isset ( $options [ 'password_encrypted' ])) {
2014-03-19 06:42:22 -04:00
$options [ 'password' ] = self :: decryptPassword ( $options [ 'password_encrypted' ]);
2014-03-18 16:15:11 -04:00
unset ( $options [ 'password_encrypted' ]);
}
return $options ;
}
2014-03-19 06:42:22 -04:00
/**
* Encrypt a single password
2014-12-11 11:35:11 -05:00
*
2014-03-19 06:42:22 -04:00
* @ param string $password plain text password
2014-05-13 07:29:25 -04:00
* @ return string encrypted password
2014-03-19 06:42:22 -04:00
*/
private static function encryptPassword ( $password ) {
$cipher = self :: getCipher ();
2018-01-13 13:41:34 -05:00
$iv = \OC :: $server -> getSecureRandom () -> generate ( 16 );
2014-03-19 06:42:22 -04:00
$cipher -> setIV ( $iv );
return base64_encode ( $iv . $cipher -> encrypt ( $password ));
}
/**
* Decrypts a single password
2014-12-11 11:35:11 -05:00
*
2014-03-19 06:42:22 -04:00
* @ param string $encryptedPassword encrypted password
2014-05-13 07:29:25 -04:00
* @ return string plain text password
2014-03-19 06:42:22 -04:00
*/
private static function decryptPassword ( $encryptedPassword ) {
$cipher = self :: getCipher ();
$binaryPassword = base64_decode ( $encryptedPassword );
$iv = substr ( $binaryPassword , 0 , 16 );
$cipher -> setIV ( $iv );
$binaryPassword = substr ( $binaryPassword , 16 );
return $cipher -> decrypt ( $binaryPassword );
}
2014-03-18 16:15:11 -04:00
/**
* Returns the encryption cipher
2015-12-09 01:53:09 -05:00
*
* @ return AES
2014-03-18 16:15:11 -04:00
*/
private static function getCipher () {
2015-08-03 00:39:53 -04:00
$cipher = new AES ( AES :: MODE_CBC );
2014-12-17 05:12:37 -05:00
$cipher -> setKey ( \OC :: $server -> getConfig () -> getSystemValue ( 'passwordsalt' , null ));
2014-03-19 06:42:22 -04:00
return $cipher ;
2014-03-18 16:15:11 -04:00
}
2014-03-26 07:10:17 -04:00
/**
* Computes a hash based on the given configuration .
* This is mostly used to find out whether configurations
* are the same .
2015-12-09 01:53:09 -05:00
*
* @ param array $config
* @ return string
2014-03-26 07:10:17 -04:00
*/
2015-03-16 07:18:01 -04:00
public static function makeConfigHash ( $config ) {
2014-03-26 07:10:17 -04:00
$data = json_encode (
2020-03-26 04:30:18 -04:00
[
2015-08-12 15:03:11 -04:00
'c' => $config [ 'backend' ],
'a' => $config [ 'authMechanism' ],
2014-03-26 07:10:17 -04:00
'm' => $config [ 'mountpoint' ],
2015-03-16 07:18:01 -04:00
'o' => $config [ 'options' ],
'p' => isset ( $config [ 'priority' ]) ? $config [ 'priority' ] : - 1 ,
'mo' => isset ( $config [ 'mountOptions' ]) ? $config [ 'mountOptions' ] : [],
2020-03-26 04:30:18 -04:00
]
2014-03-26 07:10:17 -04:00
);
return hash ( 'md5' , $data );
}
2012-09-07 08:09:41 -04:00
}