Skip to content

Commit

Permalink
prov api to be able to edit multivalue properties
Browse files Browse the repository at this point in the history
- adding as usual
- deleting and scope setting via additional endpoint

Signed-off-by: Arthur Schiwon <[email protected]>
  • Loading branch information
blizzz committed Jun 14, 2021
1 parent 61d800c commit 3637af2
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 4 deletions.
1 change: 1 addition & 0 deletions apps/provisioning_api/appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
['root' => '/cloud', 'name' => 'Users#getEditableFields', 'url' => '/user/fields', 'verb' => 'GET'],
['root' => '/cloud', 'name' => 'Users#getEditableFieldsForUser', 'url' => '/user/fields/{userId}', 'verb' => 'GET'],
['root' => '/cloud', 'name' => 'Users#editUser', 'url' => '/users/{userId}', 'verb' => 'PUT'],
['root' => '/cloud', 'name' => 'Users#editUserMultiValue', 'url' => '/users/{userId}/{collectionName}', 'verb' => 'PUT', 'requirements' => ['collectionName' => '[^(enable|disable)]']],
['root' => '/cloud', 'name' => 'Users#wipeUserDevices', 'url' => '/users/{userId}/wipe', 'verb' => 'POST'],
['root' => '/cloud', 'name' => 'Users#deleteUser', 'url' => '/users/{userId}', 'verb' => 'DELETE'],
['root' => '/cloud', 'name' => 'Users#enableUser', 'url' => '/users/{userId}/enable', 'verb' => 'PUT'],
Expand Down
98 changes: 98 additions & 0 deletions apps/provisioning_api/lib/Controller/UsersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
use OC\User\Backend;
use OCA\Settings\Mailer\NewUserMailHelper;
use OCP\Accounts\IAccountManager;
use OCP\Accounts\IAccountProperty;
use OCP\App\IAppManager;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
Expand Down Expand Up @@ -601,6 +602,85 @@ public function getEditableFieldsForUser(string $userId): DataResponse {
return new DataResponse($permittedFields);
}

/**
* @throws OCSException
*/
public function editUserMultiValue(
string $userId,
string $collectionName,
string $key,
string $value
): DataResponse {
$currentLoggedInUser = $this->userSession->getUser();
if ($currentLoggedInUser === null) {
throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
}

$targetUser = $this->userManager->get($userId);
if ($targetUser === null) {
throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
}

$permittedFields = [];
if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
// Editing self (display, email)
$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
$permittedFields[] = IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX;
} else {
// Check if admin / subadmin
$subAdminManager = $this->groupManager->getSubAdmin();
if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())
|| $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
// They have permissions over the user

$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
} else {
// No rights
throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
}
}

// Check if permitted to edit this field
if (!in_array($collectionName, $permittedFields)) {
throw new OCSException('', 103);
}

switch ($collectionName) {
case IAccountManager::COLLECTION_EMAIL:
$userAccount = $this->accountManager->getAccount($targetUser);
$mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
$mailCollection->removePropertyByValue($key);
if ($value !== '') {
// "replace on"
$mailCollection->addPropertyWithDefaults($value);
}
$this->accountManager->updateAccount($userAccount);
break;

case IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX:
$userAccount = $this->accountManager->getAccount($targetUser);
$mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
$targetProperty = null;
foreach ($mailCollection->getProperties() as $property) {
if ($property->getValue() === $value) {
$targetProperty = $property;
break;
}
}
if ($targetProperty instanceof IAccountProperty) {
$targetProperty->setScope($value);
$this->accountManager->updateAccount($userAccount);
} else {
throw new OCSException('', 102);
}
break;

default:
throw new OCSException('', 103);
}
return new DataResponse();
}

/**
* @NoAdminRequired
* @NoSubAdminRequired
Expand Down Expand Up @@ -637,6 +717,8 @@ public function editUser(string $userId, string $key, string $value): DataRespon
$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX;
$permittedFields[] = IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX;

$permittedFields[] = IAccountManager::COLLECTION_EMAIL;

$permittedFields[] = 'password';
if ($this->config->getSystemValue('force_language', false) === false ||
$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
Expand Down Expand Up @@ -675,6 +757,7 @@ public function editUser(string $userId, string $key, string $value): DataRespon
$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
}
$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
$permittedFields[] = 'password';
$permittedFields[] = 'language';
$permittedFields[] = 'locale';
Expand Down Expand Up @@ -747,6 +830,21 @@ public function editUser(string $userId, string $key, string $value): DataRespon
throw new OCSException('', 102);
}
break;
case IAccountManager::COLLECTION_EMAIL:
if (filter_var($value, FILTER_VALIDATE_EMAIL) && $value !== $targetUser->getEMailAddress()) {
$userAccount = $this->accountManager->getAccount($targetUser);
$mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
foreach ($mailCollection->getProperties() as $property) {
if ($property->getValue() === $value) {
break;
}
}
$mailCollection->addPropertyWithDefaults($value);
$this->accountManager->updateAccount($userAccount);
} else {
throw new OCSException('', 102);
}
break;
case IAccountManager::PROPERTY_PHONE:
case IAccountManager::PROPERTY_ADDRESS:
case IAccountManager::PROPERTY_WEBSITE:
Expand Down
49 changes: 45 additions & 4 deletions build/integration/features/bootstrap/Provisioning.php
Original file line number Diff line number Diff line change
Expand Up @@ -169,13 +169,20 @@ public function userHasSetting($user, $settings) {
foreach ($settings->getRows() as $setting) {
$value = json_decode(json_encode(simplexml_load_string($response->getBody())->data->{$setting[0]}), 1);
if (isset($value[0])) {
Assert::assertEquals($setting[1], $value[0], "", 0.0, 10, true);
if (in_array($setting[0], ['additional_mail', 'additional_mailScope'], true)) {
$expectedValues = explode(';', $setting[1]);
foreach ($expectedValues as $expected) {
Assert::assertTrue(in_array($expected, $value, true));
}
} else {
Assert::assertEquals($setting[1], $value[0], "", 0.0, 10, true);
}
} else {
Assert::assertEquals('', $setting[1]);
}
}
}

/**
* @Then /^group "([^"]*)" has$/
*
Expand All @@ -194,7 +201,7 @@ public function groupHasSetting($group, $settings) {
$options['headers'] = [
'OCS-APIREQUEST' => 'true',
];

$response = $client->get($fullUrl, $options);
$groupDetails = simplexml_load_string($response->getBody())->data[0]->groups[0]->element;
foreach ($settings->getRows() as $setting) {
Expand All @@ -206,7 +213,7 @@ public function groupHasSetting($group, $settings) {
}
}
}


/**
* @Then /^user "([^"]*)" has editable fields$/
Expand Down Expand Up @@ -967,4 +974,38 @@ public function cleanupGroups() {
}
$this->usingServer($previousServer);
}

/**
* @Then /^user "([^"]*)" has not$/
*/
public function userHasNotSetting($user, \Behat\Gherkin\Node\TableNode $settings) {
$fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/users/$user";
$client = new Client();
$options = [];
if ($this->currentUser === 'admin') {
$options['auth'] = $this->adminUser;
} else {
$options['auth'] = [$this->currentUser, $this->regularUser];
}
$options['headers'] = [
'OCS-APIREQUEST' => 'true',
];

$response = $client->get($fullUrl, $options);
foreach ($settings->getRows() as $setting) {
$value = json_decode(json_encode(simplexml_load_string($response->getBody())->data->{$setting[0]}), 1);
if (isset($value[0])) {
if (in_array($setting[0], ['additional_mail', 'additional_mailScope'], true)) {
$expectedValues = explode(';', $setting[1]);
foreach ($expectedValues as $expected) {
Assert::assertFalse(in_array($expected, $value, true));
}
} else {
Assert::assertNotEquals($setting[1], $value[0], "", 0.0, 10, true);
}
} else {
Assert::assertNotEquals('', $setting[1]);
}
}
}
}
75 changes: 75 additions & 0 deletions build/integration/features/provisioning-v1.feature
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,16 @@ Feature: provisioning
| value | no-reply@nextcloud.com |
And the OCS status code should be "100"
And the HTTP status code should be "200"
And sending "PUT" to "/cloud/users/brand-new-user" with
| key | additional_email |
| value | no.reply@nextcloud.com |
And the OCS status code should be "100"
And the HTTP status code should be "200"
And sending "PUT" to "/cloud/users/brand-new-user" with
| key | additional_email |
| value | noreply@nextcloud.com |
And the OCS status code should be "100"
And the HTTP status code should be "200"
And sending "PUT" to "/cloud/users/brand-new-user" with
| key | phone |
| value | +49 711 / 25 24 28-90 |
Expand All @@ -127,6 +137,7 @@ Feature: provisioning
| id | brand-new-user |
| displayname | Brand New User |
| email | no-reply@nextcloud.com |
| additional_mail | no.reply@nextcloud.com;noreply@nextcloud.com |
| phone | +4971125242890 |
| address | Foo Bar Town |
| website | https://nextcloud.com |
Expand Down Expand Up @@ -180,6 +191,33 @@ Feature: provisioning
| displaynameScope | v2-federated |
| avatarScope | v2-local |

Scenario: Edit a user account multivalue property scopes
Given user "brand-new-user" exists
And As an "brand-new-user"
When sending "PUT" to "/cloud/users/brand-new-user" with
| key | additional_email |
| value | no.reply@nextcloud.com |
And the OCS status code should be "100"
And the HTTP status code should be "200"
And sending "PUT" to "/cloud/users/brand-new-user" with
| key | additional_email |
| value | noreply@nextcloud.com |
And the OCS status code should be "100"
And the HTTP status code should be "200"
When sending "PUT" to "/cloud/users/brand-new-user/additional_emailScope" with
| key | no.reply@nextcloud.com |
| value | v2-federated |
Then the OCS status code should be "100"
And the HTTP status code should be "200"
When sending "PUT" to "/cloud/users/brand-new-user/additional_emailScope" with
| key | noreply@nextcloud.com |
| value | v2-published |
Then the OCS status code should be "100"
And the HTTP status code should be "200"
Then user "brand-new-user" has
| id | brand-new-user |
| additional_emailScope | v2-federated;v2-published |

Scenario: Edit a user account properties scopes with invalid or unsupported value
Given user "brand-new-user" exists
And As an "brand-new-user"
Expand All @@ -199,6 +237,43 @@ Feature: provisioning
Then the OCS status code should be "102"
And the HTTP status code should be "200"

Scenario: Edit a user account multi-value property scopes with invalid or unsupported value
Given user "brand-new-user" exists
And As an "brand-new-user"
When sending "PUT" to "/cloud/users/brand-new-user" with
| key | additional_email |
| value | no.reply@nextcloud.com |
And the OCS status code should be "100"
And the HTTP status code should be "200"
When sending "PUT" to "/cloud/users/brand-new-user/additional_emailScope" with
| key | no.reply@nextcloud.com |
| value | invalid |
Then the OCS status code should be "102"
And the HTTP status code should be "200"

Scenario: Delete a user account multi-value property value
Given user "brand-new-user" exists
And As an "brand-new-user"
When sending "PUT" to "/cloud/users/brand-new-user" with
| key | additional_email |
| value | no.reply@nextcloud.com |
And the OCS status code should be "100"
And the HTTP status code should be "200"
And sending "PUT" to "/cloud/users/brand-new-user" with
| key | additional_email |
| value | noreply@nextcloud.com |
And the OCS status code should be "100"
And the HTTP status code should be "200"
When sending "PUT" to "/cloud/users/brand-new-user/additional_email" with
| key | no.reply@nextcloud.com |
| value | |
And the OCS status code should be "100"
And the HTTP status code should be "200"
Then user "brand-new-user" has
| additional_email | noreply@nextcloud.com |
Then user "brand-new-user" has not
| additional_email | no.reply@nextcloud.com |

Scenario: An admin cannot edit user account property scopes
Given As an "admin"
And user "brand-new-user" exists
Expand Down
13 changes: 13 additions & 0 deletions lib/private/Accounts/AccountPropertyCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
namespace OC\Accounts;

use InvalidArgumentException;
use OCP\Accounts\IAccountManager;
use OCP\Accounts\IAccountProperty;
use OCP\Accounts\IAccountPropertyCollection;

Expand Down Expand Up @@ -63,6 +64,18 @@ public function addProperty(IAccountProperty $property): IAccountPropertyCollect
return $this;
}

public function addPropertyWithDefaults(string $value): IAccountPropertyCollection {
$property = new AccountProperty(
$this->collectionName,
$value,
IAccountManager::SCOPE_LOCAL,
IAccountManager::NOT_VERIFIED,
''
);
$this->addProperty($property);
return $this;
}

public function removeProperty(IAccountProperty $property): IAccountPropertyCollection {
$ref = array_search($property, $this->properties, true);
if ($ref !== false) {
Expand Down
8 changes: 8 additions & 0 deletions lib/public/Accounts/IAccountPropertyCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ public function getProperties(): array;
*/
public function addProperty(IAccountProperty $property): IAccountPropertyCollection;

/**
* adds a property to this collection with only specifying the value
*
* @throws InvalidArgumentException
* @since 22.0.0
*/
public function addPropertyWithDefaults(string $value): IAccountPropertyCollection;

/**
* removes a property of this collection
*
Expand Down

0 comments on commit 3637af2

Please sign in to comment.