Skip to content

Commit f2df997

Browse files
authored
Merge pull request #954 from nextcloud/fix/293/groups-from-env
fix(env-mode): accept multiple comma-separated groups
2 parents 07df5e6 + 0c0bace commit f2df997

File tree

6 files changed

+65
-323
lines changed

6 files changed

+65
-323
lines changed

lib/Controller/SAMLController.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ private function autoprovisionIfPossible(): void {
9494
$autoProvisioningAllowed = $this->userBackend->autoprovisionAllowed();
9595
if ($userExists) {
9696
if ($autoProvisioningAllowed) {
97-
$this->userBackend->updateAttributes($uid, $auth);
97+
$this->userBackend->updateAttributes($uid);
9898
}
9999
return;
100100
}
@@ -104,7 +104,7 @@ private function autoprovisionIfPossible(): void {
104104
throw new NoUserFoundException('Auto provisioning not allowed and user ' . $uid . ' does not exist');
105105
} elseif (!$userExists && $autoProvisioningAllowed) {
106106
$this->userBackend->createUserIfNotExists($uid, $auth);
107-
$this->userBackend->updateAttributes($uid, $auth);
107+
$this->userBackend->updateAttributes($uid);
108108
return;
109109
}
110110
}

lib/UserBackend.php

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ private function formatUserData($attributes): array {
355355
}
356356

357357
try {
358-
$result['formatted']['groups'] = $this->getAttributeArrayValue('saml-attribute-mapping-group_mapping', $attributes);
358+
$result['formatted']['groups'] = $this->userData->getGroups();
359359
} catch (\InvalidArgumentException) {
360360
$result['formatted']['groups'] = null;
361361
}
@@ -474,24 +474,8 @@ private function getAttributeValue($name, array $attributes) {
474474
return $value;
475475
}
476476

477-
private function getAttributeArrayValue($name, array $attributes) {
478-
$keys = $this->getAttributeKeys($name);
479-
480-
$value = [];
481-
foreach ($keys as $key) {
482-
if (isset($attributes[$key])) {
483-
if (is_array($attributes[$key])) {
484-
$value = array_merge($value, array_values($attributes[$key]));
485-
} else {
486-
$value[] = $attributes[$key];
487-
}
488-
}
489-
}
490-
491-
return $value;
492-
}
493-
494-
public function updateAttributes(string $uid, array $attributes): void {
477+
public function updateAttributes(string $uid): void {
478+
$attributes = $this->userData->getAttributes();
495479
$user = $this->userManager->get($uid);
496480
try {
497481
$newEmail = $this->getAttributeValue('saml-attribute-mapping-email_mapping', $attributes);
@@ -519,7 +503,7 @@ public function updateAttributes(string $uid, array $attributes): void {
519503
}
520504

521505
try {
522-
$newGroups = $this->getAttributeArrayValue('saml-attribute-mapping-group_mapping', $attributes);
506+
$newGroups = $this->userData->getGroups();
523507
$this->logger->debug('Group attribute content: {groups}', ['app' => 'user_saml', 'groups' => json_encode($newGroups)]);
524508
} catch (\InvalidArgumentException $e) {
525509
$this->logger->debug('Failed to fetch group attribute: {exception}', ['app' => 'user_saml', 'exception' => $e->getMessage()]);

tests/integration/features/EnvironmentVariable.feature

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ Feature: EnvironmentVariable
33
Scenario: Authenticating using environment variable with SSO and no check if user exists on backend
44
And The setting "type" is set to "environment-variable"
55
And The setting "general-uid_mapping" is set to "REMOTE_USER"
6+
And The setting "saml-attribute-mapping-group_mapping" is set to "REMOTE_GROUPS"
67
And The environment variable "REMOTE_USER" is set to "not-provisioned-user"
8+
And The environment variable "REMOTE_GROUPS" is set to "Department A,Team B"
79
When I send a GET request to "http://localhost:8080/index.php/login"
810
Then I should be redirected to "http://localhost:8080/index.php/apps/dashboard/"
911
Then The user value "id" should be "not-provisioned-user"
1012
And The last login timestamp of "not-provisioned-user" should not be empty
13+
And User "not-provisioned-user" is part of the groups "SAML_Department A, SAML_Team B"
1114

1215
Scenario: Authenticating using environment variable with SSO and successful check if user exists on backend
1316
Given A local user with uid "provisioned-user" exists

tests/integration/features/bootstrap/FeatureContext.php

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -549,16 +549,45 @@ public function theLastLoginTimestampOfShouldNotBeEmpty($uid) {
549549
}
550550
}
551551

552+
/**
553+
* @Then User :userId is part of the groups :groups
554+
*/
555+
public function theUserIsPartOfTheseGroups(string $userId, string $groups) {
556+
$response = shell_exec(
557+
sprintf(
558+
'%s %s user:info %s --output=json',
559+
PHP_BINARY,
560+
__DIR__ . '/../../../../../../occ',
561+
$userId
562+
)
563+
);
564+
565+
$groupsActual = json_decode(trim($response), true)['groups'];
566+
$groupsExpected = array_map('trim', explode(',', $groups));
567+
568+
foreach ($groupsExpected as $expectedGroup) {
569+
if (!in_array($expectedGroup, $groupsActual)) {
570+
$actualGroupStr = implode(', ', $groupsActual);
571+
throw new UnexpectedValueException("Expected to find $expectedGroup in '$actualGroupStr'");
572+
}
573+
}
574+
}
575+
552576
/**
553577
* @Given The environment variable :key is set to :value
554578
*/
555579
public function theEnvironmentVariableIsSetTo($key, $value) {
556-
// Attention, this works currently for one value only. It generates an
557-
// extra config file that injects the value to $_SERVER (as used in
558-
// `SAMLController::login()`), so that it stays across requests in PHPs
559-
// built-in server.
560-
$envConfigPhp = <<<EOF
580+
// It generates an extra config file that injects the value to $_SERVER
581+
// (as used in `SAMLController::login()`), so that it stays across
582+
// requests in PHPs built-in server.
583+
if (file_exists(self::ENV_CONFIG_FILE)) {
584+
$envConfigPhp = file_get_contents(self::ENV_CONFIG_FILE) . PHP_EOL;
585+
} else {
586+
$envConfigPhp = <<<EOF
561587
<?php
588+
EOF . PHP_EOL;
589+
}
590+
$envConfigPhp .= <<<EOF
562591
\$_SERVER["$key"] = "$value";
563592
EOF;
564593
file_put_contents(self::ENV_CONFIG_FILE, $envConfigPhp);

tests/unit/UserBackendTest.php

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,13 @@ public function testUpdateAttributes() {
155155
/** @var IUser|MockObject $user */
156156
$user = $this->createMock(IUser::class);
157157

158+
$attributes = [
159+
'email' => '[email protected]',
160+
'displayname' => 'New Displayname',
161+
'quota' => '50MB',
162+
'groups' => ['groupB', 'groupC'],
163+
];
164+
158165
// Replace at() matcher with willReturnCallback to avoid deprecation warning
159166
$this->config
160167
->method('getAppValue')
@@ -204,19 +211,20 @@ public function testUpdateAttributes() {
204211
->expects($this->once())
205212
->method('handleIncomingGroups')
206213
->with($user, ['groupB', 'groupC']);
207-
$this->userBackend->updateAttributes('ExistingUser', [
208-
'email' => '[email protected]',
209-
'displayname' => 'New Displayname',
210-
'quota' => '50MB',
211-
'groups' => ['groupB', 'groupC'],
212-
]);
214+
$this->userData->expects($this->any())
215+
->method('getAttributes')
216+
->willReturn($attributes);
217+
$this->userData->expects($this->any())
218+
->method('getGroups')
219+
->willReturn($attributes['groups']);
220+
$this->userBackend->updateAttributes('ExistingUser');
213221
}
214222

215223
public function testUpdateAttributesQuotaDefaultFallback() {
216224
$this->getMockedBuilder(['getDisplayName', 'setDisplayName']);
217225
/** @var IUser|MockObject $user */
218226
$user = $this->createMock(IUser::class);
219-
227+
$attributes = ['email' => '[email protected]', 'displayname' => 'New Displayname', 'quota' => ''];
220228

221229
$this->config->method('getAppValue')
222230
->willReturnCallback(fn (string $appId, string $key, string $default) =>
@@ -261,6 +269,12 @@ public function testUpdateAttributesQuotaDefaultFallback() {
261269
->expects($this->once())
262270
->method('handleIncomingGroups')
263271
->with($user, []);
264-
$this->userBackend->updateAttributes('ExistingUser', ['email' => '[email protected]', 'displayname' => 'New Displayname', 'quota' => '']);
272+
$this->userData->expects($this->any())
273+
->method('getAttributes')
274+
->willReturn($attributes);
275+
$this->userData->expects($this->any())
276+
->method('getGroups')
277+
->willReturn([]);
278+
$this->userBackend->updateAttributes('ExistingUser');
265279
}
266280
}

0 commit comments

Comments
 (0)