diff --git a/lib/Controller/SAMLController.php b/lib/Controller/SAMLController.php index 22cbcab51..5946f80d7 100644 --- a/lib/Controller/SAMLController.php +++ b/lib/Controller/SAMLController.php @@ -119,6 +119,8 @@ private function autoprovisionIfPossible(array $auth) { throw new \InvalidArgumentException('No valid uid given, please check your attribute mapping. Given uid: ' . $uid); } + $uid = $this->userBackend->testEncodedObjectGUID($uid); + // if this server acts as a global scale master and the user is not // a local admin of the server we just create the user and continue // no need to update additional attributes diff --git a/lib/UserBackend.php b/lib/UserBackend.php index bf1539a2b..a5c3361bb 100644 --- a/lib/UserBackend.php +++ b/lib/UserBackend.php @@ -701,4 +701,46 @@ public function countUsers() { return $result->fetchColumn(); } + + /** + * returns the plain text UUID if the provided $uid string is a + * base64-encoded binary string representing e.g. the objectGUID. Otherwise + * + */ + public function testEncodedObjectGUID(string $uid): string { + $candidate = base64_decode($uid, true); + if($candidate === false) { + return $uid; + } + $candidate = $this->convertObjectGUID2Str($candidate); + // the regex only matches the structure of the UUID, not its semantic + // (i.e. version or variant) simply to be future compatible + if(preg_match('/^[a-f0-9]{8}(-[a-f0-9]{4}){4}[a-f0-9]{8}$/i', $candidate) === 1) { + $uid = $candidate; + } + return $uid; + } + + /** + * @see \OCA\User_LDAP\Access::convertObjectGUID2Str + */ + protected function convertObjectGUID2Str($oguid) { + $hex_guid = bin2hex($oguid); + $hex_guid_to_guid_str = ''; + for($k = 1; $k <= 4; ++$k) { + $hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2); + } + $hex_guid_to_guid_str .= '-'; + for($k = 1; $k <= 2; ++$k) { + $hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2); + } + $hex_guid_to_guid_str .= '-'; + for($k = 1; $k <= 2; ++$k) { + $hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2); + } + $hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4); + $hex_guid_to_guid_str .= '-' . substr($hex_guid, 20); + + return strtoupper($hex_guid_to_guid_str); + } } diff --git a/tests/unit/Controller/SAMLControllerTest.php b/tests/unit/Controller/SAMLControllerTest.php index 903732b97..952e24b6e 100644 --- a/tests/unit/Controller/SAMLControllerTest.php +++ b/tests/unit/Controller/SAMLControllerTest.php @@ -69,6 +69,9 @@ public function setUp() { $this->userSession = $this->createMock(IUserSession::class); $this->samlSettings = $this->createMock(SAMLSettings::class); $this->userBackend = $this->createMock(UserBackend::class); + $this->userBackend->expects($this->any()) + ->method('testEncodedObjectGUID') + ->willReturnArgument(0); $this->config = $this->createMock(IConfig::class); $this->urlGenerator = $this->createMock(IURLGenerator::class); $this->userManager = $this->createMock(IUserManager::class); @@ -275,11 +278,11 @@ public function testLoginWithEnvVariableAndNotExistingUserWithProvisioning() { ->with('/') ->willReturn('https://nextcloud.com/absolute/'); $this->userBackend - ->expects($this->at(0)) + ->expects($this->once()) ->method('autoprovisionAllowed') ->willReturn(true); $this->userBackend - ->expects($this->at(1)) + ->expects($this->once()) ->method('createUserIfNotExists') ->with('MyUid'); $this->userBackend @@ -332,11 +335,11 @@ public function testLoginWithEnvVariableAndNotExistingUserWithMalfunctioningBack ->with('user_saml.SAML.notProvisioned') ->willReturn('https://nextcloud.com/notprovisioned/'); $this->userBackend - ->expects($this->at(0)) + ->expects($this->once()) ->method('autoprovisionAllowed') ->willReturn(true); $this->userBackend - ->expects($this->at(1)) + ->expects($this->once()) ->method('createUserIfNotExists') ->with('MyUid'); $this->userBackend diff --git a/tests/unit/UserBackendTest.php b/tests/unit/UserBackendTest.php index e3bc722c7..28bd4353e 100644 --- a/tests/unit/UserBackendTest.php +++ b/tests/unit/UserBackendTest.php @@ -281,5 +281,25 @@ public function testUpdateAttributesQuotaDefaultFallback() { $this->userBackend->updateAttributes('ExistingUser', ['email' => 'new@example.com', 'displayname' => 'New Displayname', 'quota' => '']); } + public function objectGuidProvider() { + return [ + ['Joey No Conversion', 'Joey No Conversion'], + ['no@convers.ion', 'no@convers.ion'], + ['a0aa9ed8-6b48-1034-8ad7-8fb78330d80a', 'a0aa9ed8-6b48-1034-8ad7-8fb78330d80a'], + ['EDE70D16-B9D5-4E9A-ABD7-614D17246E3F', 'EDE70D16-B9D5-4E9A-ABD7-614D17246E3F'], + ['Tm8gY29udmVyc2lvbgo=', 'Tm8gY29udmVyc2lvbgo='], + ['ASfjU2OYEd69ZgAVF4pePA==', '53E32701-9863-DE11-BD66-0015178A5E3C'], + ]; + } + + /** + * @dataProvider objectGuidProvider + */ + public function testTestEncodedObjectGUID(string $input, string $expectation) { + $this->getMockedBuilder(['getDisplayName', 'setDisplayName']); + $uid = $this->userBackend->testEncodedObjectGUID($input); + $this->assertSame($expectation, $uid); + } + }