Browse Source
Merge pull request #3162 from nextcloud/ldap-ocs
Merge pull request #3162 from nextcloud/ldap-ocs
Part 1 of LDAP Backend OCS Apipull/3272/head
committed by
GitHub
9 changed files with 545 additions and 19 deletions
-
11.drone.yml
-
10apps/user_ldap/appinfo/routes.php
-
28apps/user_ldap/lib/Command/CreateEmptyConfig.php
-
9apps/user_ldap/lib/Configuration.php
-
322apps/user_ldap/lib/Controller/ConfigAPIController.php
-
19apps/user_ldap/lib/Helper.php
-
10build/integration/config/behat.yml
-
85build/integration/features/bootstrap/LDAPContext.php
-
70build/integration/ldap_features/ldap-ocs.feature
@ -0,0 +1,322 @@ |
|||
<?php |
|||
/** |
|||
* @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de> |
|||
* |
|||
* @author Arthur Schiwon <blizzz@arthur-schiwon.de> |
|||
* |
|||
* @license GNU AGPL version 3 or any later version |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU Affero General Public License as |
|||
* published by the Free Software Foundation, either version 3 of the |
|||
* License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* 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 |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
* |
|||
*/ |
|||
|
|||
namespace OCA\User_LDAP\Controller; |
|||
|
|||
use OC\CapabilitiesManager; |
|||
use OC\Core\Controller\OCSController; |
|||
use OC\Security\Bruteforce\Throttler; |
|||
use OC\Security\IdentityProof\Manager; |
|||
use OCA\User_LDAP\Configuration; |
|||
use OCA\User_LDAP\Helper; |
|||
use OCP\AppFramework\Http\DataResponse; |
|||
use OCP\AppFramework\OCS\OCSBadRequestException; |
|||
use OCP\AppFramework\OCS\OCSException; |
|||
use OCP\AppFramework\OCS\OCSNotFoundException; |
|||
use OCP\ILogger; |
|||
use OCP\IRequest; |
|||
use OCP\IUserManager; |
|||
use OCP\IUserSession; |
|||
|
|||
class ConfigAPIController extends OCSController { |
|||
|
|||
/** @var Helper */ |
|||
private $ldapHelper; |
|||
|
|||
/** @var ILogger */ |
|||
private $logger; |
|||
|
|||
public function __construct( |
|||
$appName, |
|||
IRequest $request, |
|||
CapabilitiesManager $capabilitiesManager, |
|||
IUserSession $userSession, |
|||
IUserManager $userManager, |
|||
Throttler $throttler, |
|||
Manager $keyManager, |
|||
Helper $ldapHelper, |
|||
ILogger $logger |
|||
) { |
|||
parent::__construct( |
|||
$appName, |
|||
$request, |
|||
$capabilitiesManager, |
|||
$userSession, |
|||
$userManager, |
|||
$throttler, |
|||
$keyManager |
|||
); |
|||
|
|||
|
|||
$this->ldapHelper = $ldapHelper; |
|||
$this->logger = $logger; |
|||
} |
|||
|
|||
/** |
|||
* creates a new (empty) configuration and returns the resulting prefix |
|||
* |
|||
* Example: curl -X POST -H "OCS-APIREQUEST: true" -u $admin:$password \ |
|||
* https://nextcloud.server/ocs/v2.php/apps/user_ldap/api/v1/config |
|||
* |
|||
* results in: |
|||
* |
|||
* <?xml version="1.0"?>
|
|||
* <ocs> |
|||
* <meta> |
|||
* <status>ok</status> |
|||
* <statuscode>200</statuscode> |
|||
* <message>OK</message> |
|||
* </meta> |
|||
* <data> |
|||
* <configID>s40</configID> |
|||
* </data> |
|||
* </ocs> |
|||
* |
|||
* Failing example: if an exception is thrown (e.g. Database connection lost) |
|||
* the detailed error will be logged. The output will then look like: |
|||
* |
|||
* <?xml version="1.0"?>
|
|||
* <ocs> |
|||
* <meta> |
|||
* <status>failure</status> |
|||
* <statuscode>999</statuscode> |
|||
* <message>An issue occurred when creating the new config.</message> |
|||
* </meta> |
|||
* <data/> |
|||
* </ocs> |
|||
* |
|||
* For JSON output provide the format=json parameter |
|||
* |
|||
* @return DataResponse |
|||
* @throws OCSException |
|||
*/ |
|||
public function create() { |
|||
try { |
|||
$configPrefix = $this->ldapHelper->getNextServerConfigurationPrefix(); |
|||
$configHolder = new Configuration($configPrefix); |
|||
$configHolder->saveConfiguration(); |
|||
} catch (\Exception $e) { |
|||
$this->logger->logException($e); |
|||
throw new OCSException('An issue occurred when creating the new config.'); |
|||
} |
|||
return new DataResponse(['configID' => $configPrefix]); |
|||
} |
|||
|
|||
/** |
|||
* Deletes a LDAP configuration, if present. |
|||
* |
|||
* Example: |
|||
* curl -X DELETE -H "OCS-APIREQUEST: true" -u $admin:$password \ |
|||
* https://nextcloud.server/ocs/v2.php/apps/user_ldap/api/v1/config/s60 |
|||
* |
|||
* <?xml version="1.0"?>
|
|||
* <ocs> |
|||
* <meta> |
|||
* <status>ok</status> |
|||
* <statuscode>200</statuscode> |
|||
* <message>OK</message> |
|||
* </meta> |
|||
* <data/> |
|||
* </ocs> |
|||
* |
|||
* @param string $configID |
|||
* @return DataResponse |
|||
* @throws OCSBadRequestException |
|||
* @throws OCSException |
|||
*/ |
|||
public function delete($configID) { |
|||
try { |
|||
$this->ensureConfigIDExists($configID); |
|||
if(!$this->ldapHelper->deleteServerConfiguration($configID)) { |
|||
throw new OCSException('Could not delete configuration'); |
|||
} |
|||
} catch(OCSException $e) { |
|||
throw $e; |
|||
} catch(\Exception $e) { |
|||
$this->logger->logException($e); |
|||
throw new OCSException('An issue occurred when deleting the config.'); |
|||
} |
|||
|
|||
return new DataResponse(); |
|||
} |
|||
|
|||
/** |
|||
* modifies a configuration |
|||
* |
|||
* Example: |
|||
* curl -X PUT -d "configData[ldapHost]=ldaps://my.ldap.server&configData[ldapPort]=636" \ |
|||
* -H "OCS-APIREQUEST: true" -u $admin:$password \ |
|||
* https://nextcloud.server/ocs/v2.php/apps/user_ldap/api/v1/config/s60 |
|||
* |
|||
* <?xml version="1.0"?>
|
|||
* <ocs> |
|||
* <meta> |
|||
* <status>ok</status> |
|||
* <statuscode>200</statuscode> |
|||
* <message>OK</message> |
|||
* </meta> |
|||
* <data/> |
|||
* </ocs> |
|||
* |
|||
* @param string $configID |
|||
* @param array $configData |
|||
* @return DataResponse |
|||
* @throws OCSException |
|||
*/ |
|||
public function modify($configID, $configData) { |
|||
try { |
|||
$this->ensureConfigIDExists($configID); |
|||
|
|||
if(!is_array($configData)) { |
|||
throw new OCSBadRequestException('configData is not properly set'); |
|||
} |
|||
|
|||
$configuration = new Configuration($configID); |
|||
$configKeys = $configuration->getConfigTranslationArray(); |
|||
|
|||
foreach ($configKeys as $i => $key) { |
|||
if(isset($configData[$key])) { |
|||
$configuration->$key = $configData[$key]; |
|||
} |
|||
} |
|||
|
|||
$configuration->saveConfiguration(); |
|||
} catch(OCSException $e) { |
|||
throw $e; |
|||
} catch (\Exception $e) { |
|||
$this->logger->logException($e); |
|||
throw new OCSException('An issue occurred when modifying the config.'); |
|||
} |
|||
|
|||
return new DataResponse(); |
|||
} |
|||
|
|||
/** |
|||
* retrieves a configuration |
|||
* |
|||
* <?xml version="1.0"?>
|
|||
* <ocs> |
|||
* <meta> |
|||
* <status>ok</status> |
|||
* <statuscode>200</statuscode> |
|||
* <message>OK</message> |
|||
* </meta> |
|||
* <data> |
|||
* <ldapHost>ldaps://my.ldap.server</ldapHost> |
|||
* <ldapPort>7770</ldapPort> |
|||
* <ldapBackupHost></ldapBackupHost> |
|||
* <ldapBackupPort></ldapBackupPort> |
|||
* <ldapBase>ou=small,dc=my,dc=ldap,dc=server</ldapBase> |
|||
* <ldapBaseUsers>ou=users,ou=small,dc=my,dc=ldap,dc=server</ldapBaseUsers> |
|||
* <ldapBaseGroups>ou=small,dc=my,dc=ldap,dc=server</ldapBaseGroups> |
|||
* <ldapAgentName>cn=root,dc=my,dc=ldap,dc=server</ldapAgentName> |
|||
* <ldapAgentPassword>clearTextWithShowPassword=1</ldapAgentPassword> |
|||
* <ldapTLS>1</ldapTLS> |
|||
* <turnOffCertCheck>0</turnOffCertCheck> |
|||
* <ldapIgnoreNamingRules/> |
|||
* <ldapUserDisplayName>displayname</ldapUserDisplayName> |
|||
* <ldapUserDisplayName2>uid</ldapUserDisplayName2> |
|||
* <ldapUserFilterObjectclass>inetOrgPerson</ldapUserFilterObjectclass> |
|||
* <ldapUserFilterGroups></ldapUserFilterGroups> |
|||
* <ldapUserFilter>(&(objectclass=nextcloudUser)(nextcloudEnabled=TRUE))</ldapUserFilter> |
|||
* <ldapUserFilterMode>1</ldapUserFilterMode> |
|||
* <ldapGroupFilter>(&(|(objectclass=nextcloudGroup)))</ldapGroupFilter> |
|||
* <ldapGroupFilterMode>0</ldapGroupFilterMode> |
|||
* <ldapGroupFilterObjectclass>nextcloudGroup</ldapGroupFilterObjectclass> |
|||
* <ldapGroupFilterGroups></ldapGroupFilterGroups> |
|||
* <ldapGroupDisplayName>cn</ldapGroupDisplayName> |
|||
* <ldapGroupMemberAssocAttr>memberUid</ldapGroupMemberAssocAttr> |
|||
* <ldapLoginFilter>(&(|(objectclass=inetOrgPerson))(uid=%uid))</ldapLoginFilter> |
|||
* <ldapLoginFilterMode>0</ldapLoginFilterMode> |
|||
* <ldapLoginFilterEmail>0</ldapLoginFilterEmail> |
|||
* <ldapLoginFilterUsername>1</ldapLoginFilterUsername> |
|||
* <ldapLoginFilterAttributes></ldapLoginFilterAttributes> |
|||
* <ldapQuotaAttribute></ldapQuotaAttribute> |
|||
* <ldapQuotaDefault></ldapQuotaDefault> |
|||
* <ldapEmailAttribute>mail</ldapEmailAttribute> |
|||
* <ldapCacheTTL>20</ldapCacheTTL> |
|||
* <ldapUuidUserAttribute>auto</ldapUuidUserAttribute> |
|||
* <ldapUuidGroupAttribute>auto</ldapUuidGroupAttribute> |
|||
* <ldapOverrideMainServer></ldapOverrideMainServer> |
|||
* <ldapConfigurationActive>1</ldapConfigurationActive> |
|||
* <ldapAttributesForUserSearch>uid;sn;givenname</ldapAttributesForUserSearch> |
|||
* <ldapAttributesForGroupSearch></ldapAttributesForGroupSearch> |
|||
* <ldapExperiencedAdmin>0</ldapExperiencedAdmin> |
|||
* <homeFolderNamingRule></homeFolderNamingRule> |
|||
* <hasPagedResultSupport></hasPagedResultSupport> |
|||
* <hasMemberOfFilterSupport></hasMemberOfFilterSupport> |
|||
* <useMemberOfToDetectMembership>1</useMemberOfToDetectMembership> |
|||
* <ldapExpertUsernameAttr>uid</ldapExpertUsernameAttr> |
|||
* <ldapExpertUUIDUserAttr>uid</ldapExpertUUIDUserAttr> |
|||
* <ldapExpertUUIDGroupAttr></ldapExpertUUIDGroupAttr> |
|||
* <lastJpegPhotoLookup>0</lastJpegPhotoLookup> |
|||
* <ldapNestedGroups>0</ldapNestedGroups> |
|||
* <ldapPagingSize>500</ldapPagingSize> |
|||
* <turnOnPasswordChange>1</turnOnPasswordChange> |
|||
* <ldapDynamicGroupMemberURL></ldapDynamicGroupMemberURL> |
|||
* </data> |
|||
* </ocs> |
|||
* |
|||
* @param string $configID |
|||
* @param bool|string $showPassword |
|||
* @return DataResponse |
|||
* @throws OCSException |
|||
*/ |
|||
public function show($configID, $showPassword = false) { |
|||
try { |
|||
$this->ensureConfigIDExists($configID); |
|||
|
|||
$config = new Configuration($configID); |
|||
$data = $config->getConfiguration(); |
|||
if(!boolval(intval($showPassword))) { |
|||
$data['ldapAgentPassword'] = '***'; |
|||
} |
|||
foreach ($data as $key => $value) { |
|||
if(is_array($value)) { |
|||
$value = implode(';', $value); |
|||
$data[$key] = $value; |
|||
} |
|||
} |
|||
} catch(OCSException $e) { |
|||
throw $e; |
|||
} catch (\Exception $e) { |
|||
$this->logger->logException($e); |
|||
throw new OCSException('An issue occurred when modifying the config.'); |
|||
} |
|||
|
|||
return new DataResponse($data); |
|||
} |
|||
|
|||
/** |
|||
* if the given config ID is not available, an exception is thrown |
|||
* |
|||
* @param string $configID |
|||
* @throws OCSNotFoundException |
|||
*/ |
|||
private function ensureConfigIDExists($configID) { |
|||
$prefixes = $this->ldapHelper->getServerConfigurationPrefixes(); |
|||
if(!in_array($configID, $prefixes, true)) { |
|||
throw new OCSNotFoundException('Config ID not found'); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,85 @@ |
|||
<?php |
|||
|
|||
/** |
|||
* @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de> |
|||
* |
|||
* @author Arthur Schiwon <blizzz@arthur-schiwon.de> |
|||
* |
|||
* @license GNU AGPL version 3 or any later version |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU Affero General Public License as |
|||
* published by the Free Software Foundation, either version 3 of the |
|||
* License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* 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 |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
* |
|||
*/ |
|||
|
|||
use Behat\Behat\Context\Context; |
|||
use Behat\Gherkin\Node\TableNode; |
|||
|
|||
class LDAPContext implements Context { |
|||
use BasicStructure; |
|||
|
|||
protected $configID; |
|||
|
|||
protected $apiUrl; |
|||
|
|||
/** |
|||
* @Given /^the response should contain a tag "([^"]*)"$/
|
|||
*/ |
|||
public function theResponseShouldContainATag($arg1) { |
|||
$configID = $this->response->xml()->data[0]->$arg1; |
|||
PHPUnit_Framework_Assert::assertInstanceOf(SimpleXMLElement::class, $configID[0]); |
|||
} |
|||
|
|||
/** |
|||
* @Given /^creating an LDAP configuration at "([^"]*)"$/
|
|||
*/ |
|||
public function creatingAnLDAPConfigurationAt($apiUrl) { |
|||
$this->apiUrl = $apiUrl; |
|||
$this->sendingToWith('POST', $this->apiUrl, null); |
|||
$configElements = $this->response->xml()->data[0]->configID; |
|||
$this->configID = $configElements[0]; |
|||
} |
|||
|
|||
/** |
|||
* @When /^deleting the LDAP configuration$/ |
|||
*/ |
|||
public function deletingTheLDAPConfiguration() { |
|||
$this->sendingToWith('DELETE', $this->apiUrl . '/' . $this->configID, null); |
|||
} |
|||
|
|||
/** |
|||
* @Given /^the response should contain a tag "([^"]*)" with value "([^"]*)"$/ |
|||
*/ |
|||
public function theResponseShouldContainATagWithValue($tagName, $expectedValue) { |
|||
$data = $this->response->xml()->data[0]->$tagName; |
|||
PHPUnit_Framework_Assert::assertEquals($expectedValue, $data[0]); |
|||
} |
|||
|
|||
/** |
|||
* @When /^getting the LDAP configuration with showPassword "([^"]*)"$/
|
|||
*/ |
|||
public function gettingTheLDAPConfigurationWithShowPassword($showPassword) { |
|||
$this->sendingToWith( |
|||
'GET', |
|||
$this->apiUrl . '/' . $this->configID . '?showPassword=' . $showPassword, |
|||
null |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* @Given /^setting the LDAP configuration to$/ |
|||
*/ |
|||
public function settingTheLDAPConfigurationTo(TableNode $configData) { |
|||
$this->sendingToWith('PUT', $this->apiUrl . '/' . $this->configID, $configData); |
|||
} |
|||
} |
|||
@ -0,0 +1,70 @@ |
|||
Feature: LDAP |
|||
Background: |
|||
Given using api version "2" |
|||
|
|||
Scenario: Creating an new, empty configuration |
|||
Given As an "admin" |
|||
When sending "POST" to "/apps/user_ldap/api/v1/config" |
|||
Then the OCS status code should be "200" |
|||
And the HTTP status code should be "200" |
|||
And the response should contain a tag "configID" |
|||
|
|||
Scenario: Delete a non-existing configuration |
|||
Given As an "admin" |
|||
When sending "DELETE" to "/apps/user_ldap/api/v1/config/s666" |
|||
Then the OCS status code should be "404" |
|||
And the HTTP status code should be "404" |
|||
|
|||
Scenario: Create and delete a configuration |
|||
Given As an "admin" |
|||
And creating an LDAP configuration at "/apps/user_ldap/api/v1/config" |
|||
When deleting the LDAP configuration |
|||
Then the OCS status code should be "200" |
|||
And the HTTP status code should be "200" |
|||
|
|||
Scenario: Create and modify a configuration |
|||
Given As an "admin" |
|||
And creating an LDAP configuration at "/apps/user_ldap/api/v1/config" |
|||
When setting the LDAP configuration to |
|||
| configData[ldapHost] | ldaps://my.ldap.server | |
|||
Then the OCS status code should be "200" |
|||
And the HTTP status code should be "200" |
|||
|
|||
Scenario: Modifying a non-existing configuration |
|||
Given As an "admin" |
|||
When sending "PUT" to "/apps/user_ldap/api/v1/config/s666" with |
|||
| configData[ldapHost] | ldaps://my.ldap.server | |
|||
Then the OCS status code should be "404" |
|||
And the HTTP status code should be "404" |
|||
|
|||
Scenario: Modifying an existing configuration with malformed configData |
|||
Given As an "admin" |
|||
And creating an LDAP configuration at "/apps/user_ldap/api/v1/config" |
|||
When setting the LDAP configuration to |
|||
| configData | ldapHost=ldaps://my.ldap.server | |
|||
Then the OCS status code should be "400" |
|||
And the HTTP status code should be "400" |
|||
|
|||
Scenario: create, modify and get a configuration |
|||
Given As an "admin" |
|||
And creating an LDAP configuration at "/apps/user_ldap/api/v1/config" |
|||
And setting the LDAP configuration to |
|||
| configData[ldapHost] | ldaps://my.ldap.server | |
|||
| configData[ldapLoginFilter] | (&(\|(objectclass=inetOrgPerson))(uid=%uid)) | |
|||
| configData[ldapAgentPassword] | psst,secret | |
|||
When getting the LDAP configuration with showPassword "0" |
|||
Then the OCS status code should be "200" |
|||
And the HTTP status code should be "200" |
|||
And the response should contain a tag "ldapHost" with value "ldaps://my.ldap.server" |
|||
And the response should contain a tag "ldapLoginFilter" with value "(&(|(objectclass=inetOrgPerson))(uid=%uid))" |
|||
And the response should contain a tag "ldapAgentPassword" with value "***" |
|||
|
|||
Scenario: receiving password in plain text |
|||
Given As an "admin" |
|||
And creating an LDAP configuration at "/apps/user_ldap/api/v1/config" |
|||
And setting the LDAP configuration to |
|||
| configData[ldapAgentPassword] | psst,secret | |
|||
When getting the LDAP configuration with showPassword "1" |
|||
Then the OCS status code should be "200" |
|||
And the HTTP status code should be "200" |
|||
And the response should contain a tag "ldapAgentPassword" with value "psst,secret" |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue