From ef1bd417895a7083cc91be54658add9cde7a8723 Mon Sep 17 00:00:00 2001 From: Fabian Franz BSc Date: Thu, 14 May 2020 19:59:07 +0200 Subject: [PATCH] nextcloud: add support for upstream LDAP accounts (#4103) --- .../app/library/OPNsense/Backup/Nextcloud.php | 98 +++++++++++++++---- 1 file changed, 78 insertions(+), 20 deletions(-) diff --git a/src/opnsense/mvc/app/library/OPNsense/Backup/Nextcloud.php b/src/opnsense/mvc/app/library/OPNsense/Backup/Nextcloud.php index a8b2fd9ff..d197342a2 100644 --- a/src/opnsense/mvc/app/library/OPNsense/Backup/Nextcloud.php +++ b/src/opnsense/mvc/app/library/OPNsense/Backup/Nextcloud.php @@ -107,14 +107,15 @@ class Nextcloud extends Base implements IBackupProvider * @param array $conf configuration array * @return array of validation errors when not saved * @throws \OPNsense\Base\ModelException + * @throws \ReflectionException */ public function setConfiguration($conf) { - $nextcloud = new NextcloudSettings(); - $this->setModelProperties($nextcloud, $conf); - $validation_messages = $this->validateModel($nextcloud); + $nextCloud = new NextcloudSettings(); + $this->setModelProperties($nextCloud, $conf); + $validation_messages = $this->validateModel($nextCloud); if (empty($validation_messages)) { - $nextcloud->serializeToConfig(); + $nextCloud->serializeToConfig(); Config::getInstance()->save(); } return $validation_messages; @@ -124,6 +125,7 @@ class Nextcloud extends Base implements IBackupProvider * perform backup * @return array filelist * @throws \OPNsense\Base\ModelException + * @throws \ReflectionException */ public function backup() { @@ -144,21 +146,23 @@ class Nextcloud extends Base implements IBackupProvider $confdata = $this->encrypt($confdata, $crypto_password); } try { - $directories = $this->listFiles($url, $username, $password, '/'); + $internal_username = $this->getInternalUsername($url, $username, $password); + $directories = $this->listFiles($url, $username, $password, $internal_username, '/'); if (!in_array("/$backupdir/", $directories)) { - $this->create_directory($url, $username, $password, $backupdir); + $this->create_directory($url, $username, $password, $internal_username, $backupdir); } $this->upload_file_content( $url, $username, $password, + $internal_username, $backupdir, $configname, $confdata ); // do not list directories return array_filter( - $this->listFiles($url, $username, $password, "/$backupdir/", false), + $this->listFiles($url, $username, $password, $internal_username, "/$backupdir/", false), function ($filename) { return (substr($filename, -1) !== '/'); } @@ -174,15 +178,16 @@ class Nextcloud extends Base implements IBackupProvider * @param string $url remote location * @param string $username username * @param string $password password to use + * @param string $internal_username internal username for the webdav directory * @param string $directory location to list * @param bool $only_dirs only list directories * @return array * @throws \Exception */ - public function listFiles($url, $username, $password, $directory = '/', $only_dirs = true) + public function listFiles($url, $username, $password, $internal_username, $directory = '/', $only_dirs = true) { $result = $this->curl_request( - "$url/remote.php/dav/files/$username$directory", + "$url/remote.php/dav/files/$internal_username$directory", $username, $password, 'PROPFIND', @@ -195,7 +200,7 @@ class Nextcloud extends Base implements IBackupProvider // d:response if ($response->getName() == 'response') { $fileurl = (string)$response->href; - $dirname = explode("/remote.php/dav/files/$username", $fileurl, 2)[1]; + $dirname = explode("/remote.php/dav/files/$internal_username", $fileurl, 2)[1]; if ( $response->propstat->prop->resourcetype->children()->count() > 0 && $response->propstat->prop->resourcetype->children()[0]->getName() == 'collection' && @@ -220,10 +225,10 @@ class Nextcloud extends Base implements IBackupProvider * @param string $local_file_content contents to save * @throws \Exception when upload fails */ - public function upload_file_content($url, $username, $password, $backupdir, $filename, $local_file_content) + public function upload_file_content($url, $username, $password, $internal_username, $backupdir, $filename, $local_file_content) { $this->curl_request( - $url . "/remote.php/dav/files/$username/$backupdir/$filename", + $url . "/remote.php/dav/files/$internal_username/$backupdir/$filename", $username, $password, 'PUT', @@ -240,10 +245,10 @@ class Nextcloud extends Base implements IBackupProvider * @param string $backupdir remote directory * @throws \Exception when create dir fails */ - public function create_directory($url, $username, $password, $backupdir) + public function create_directory($url, $username, $password, $internal_username, $backupdir) { $this->curl_request( - $url . "/remote.php/dav/files/$username/$backupdir", + $url . "/remote.php/dav/files/$internal_username/$backupdir", $username, $password, 'MKCOL', @@ -251,6 +256,30 @@ class Nextcloud extends Base implements IBackupProvider ); } + public function getInternalUsername($url, $username, $password) : string { + $xml_response = $this->ocs_request( + "$url/ocs/v1.php/cloud/user", + $username, + $password, + "GET", + "Cannot get real username" + ); + + try { + $data = $xml_response->data; + if ($data == null) { + return $username; // no data found, return the old username + } + $real_username = $data->id; + if ($real_username == null) { + return $username; + } + return $real_username; + } catch (\Exception $exception) { + return $username; // error - continue with old username + } + } + /** * @param string $url remote location * @param string $username remote user @@ -258,10 +287,12 @@ class Nextcloud extends Base implements IBackupProvider * @param string $method http method, PUT, GET, ... * @param string $error_message message to log on failure * @param null|string $postdata http body + * @param array $headers HTTP headers * @return array response status * @throws \Exception when request fails */ - public function curl_request($url, $username, $password, $method, $error_message, $postdata = null) + public function curl_request($url, $username, $password, $method, $error_message, $postdata = null, + $headers = array("User-Agent: OPNsense Firewall")) { $curl = curl_init(); curl_setopt_array($curl, array( @@ -273,9 +304,7 @@ class Nextcloud extends Base implements IBackupProvider CURLOPT_TIMEOUT => 60, // maximum time: 1 min CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_USERPWD => $username . ":" . $password, - CURLOPT_HTTPHEADER => array( - "User-Agent: OPNsense Firewall" - ) + CURLOPT_HTTPHEADER => $headers )); if ($postdata != null) { curl_setopt($curl, CURLOPT_POSTFIELDS, $postdata); @@ -292,13 +321,42 @@ class Nextcloud extends Base implements IBackupProvider return array('response' => $response, 'info' => $info); } + /** + * @param $url string URL to call + * @param $username string username + * @param $password string password + * @param $method string HTTP verb + * @param $error_message string error message to forward to the http calling method + * @param null $postdata post data if any (can be null) + * @return array|\SimpleXMLElement|null + * @throws \Exception + */ + public function ocs_request($url, $username, $password, $method, $error_message, $postdata = null) { + $headers = $headers = array("User-Agent: OPNsense Firewall", "OCS-APIRequest: true"); + $result = $this->curl_request($url, $username, $password, $method, $error_message, $postdata, $headers); + if (array_key_exists('content_type', $result['info'])) { + if (stripos($result['info']['content_type'], 'xml') !== FALSE) { + return new \SimpleXMLElement($result['response']); + } + if (stripos($result['info']['content_type'], 'json') !== FALSE) { + return json_decode($result['response'], true); + } + + throw new \Exception(); + } + + return null; + } + /** * Is this provider enabled * @return boolean enabled status + * @throws \OPNsense\Base\ModelException + * @throws \ReflectionException */ public function isEnabled() { - $nextcloud = new NextcloudSettings(); - return (string)$nextcloud->enabled === "1"; + $nextCloud = new NextcloudSettings(); + return (string)$nextCloud->enabled === "1"; } }