Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
prov api to be able to edit multivalue properties
- adding as usual
- deleting and scope setting via additional endpoint

Signed-off-by: Arthur Schiwon <[email protected]>
  • Loading branch information
blizzz authored and backportbot[bot] committed Jun 30, 2021
commit 148a62939f3adddb29942a13d08725b3bdbd76f4
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 | [email protected] |
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_mail |
| value | [email protected] |
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_mail |
| value | [email protected] |
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 | [email protected] |
| additional_mail | [email protected];[email protected] |
| 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_mail |
| value | [email protected] |
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_mail |
| value | [email protected] |
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_mailScope" with
| key | [email protected] |
| 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_mailScope" with
| key | [email protected] |
| 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_mailScope | 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_mail |
| value | [email protected] |
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_mailScope" with
| key | [email protected] |
| 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_mail |
| value | [email protected] |
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_mail |
| value | [email protected] |
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_mail" with
| key | [email protected] |
| value | |
And the OCS status code should be "100"
And the HTTP status code should be "200"
Then user "brand-new-user" has
| additional_mail | [email protected] |
Then user "brand-new-user" has not
| additional_mail | [email protected] |

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