AccessTest.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch>
  5. *
  6. * @author Andreas Fischer <bantu@owncloud.com>
  7. * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
  8. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  9. * @author Joas Schilling <coding@schilljs.com>
  10. * @author Jörn Friedrich Dreyer <jfd@butonic.de>
  11. * @author Lukas Reschke <lukas@statuscode.ch>
  12. * @author Morris Jobke <hey@morrisjobke.de>
  13. * @author Roeland Jago Douma <roeland@famdouma.nl>
  14. * @author Roger Szabo <roger.szabo@web.de>
  15. * @author root <root@localhost.localdomain>
  16. * @author Thomas Müller <thomas.mueller@tmit.eu>
  17. * @author Victor Dubiniuk <dubiniuk@owncloud.com>
  18. *
  19. * @license AGPL-3.0
  20. *
  21. * This code is free software: you can redistribute it and/or modify
  22. * it under the terms of the GNU Affero General Public License, version 3,
  23. * as published by the Free Software Foundation.
  24. *
  25. * This program is distributed in the hope that it will be useful,
  26. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  27. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  28. * GNU Affero General Public License for more details.
  29. *
  30. * You should have received a copy of the GNU Affero General Public License, version 3,
  31. * along with this program. If not, see <http://www.gnu.org/licenses/>
  32. *
  33. */
  34. namespace OCA\User_LDAP\Tests;
  35. use OCA\User_LDAP\Access;
  36. use OCA\User_LDAP\Connection;
  37. use OCA\User_LDAP\Exceptions\ConstraintViolationException;
  38. use OCA\User_LDAP\FilesystemHelper;
  39. use OCA\User_LDAP\Helper;
  40. use OCA\User_LDAP\ILDAPWrapper;
  41. use OCA\User_LDAP\LDAP;
  42. use OCA\User_LDAP\LogWrapper;
  43. use OCA\User_LDAP\Mapping\UserMapping;
  44. use OCA\User_LDAP\User\Manager;
  45. use OCA\User_LDAP\User\OfflineUser;
  46. use OCA\User_LDAP\User\User;
  47. use OCP\IAvatarManager;
  48. use OCP\IConfig;
  49. use OCP\IDBConnection;
  50. use OCP\Image;
  51. use OCP\IUserManager;
  52. use OCP\Notification\IManager as INotificationManager;
  53. use Test\TestCase;
  54. /**
  55. * Class AccessTest
  56. *
  57. * @group DB
  58. *
  59. * @package OCA\User_LDAP\Tests
  60. */
  61. class AccessTest extends TestCase {
  62. /** @var UserMapping|\PHPUnit_Framework_MockObject_MockObject */
  63. protected $userMapper;
  64. /** @var Connection|\PHPUnit_Framework_MockObject_MockObject */
  65. private $connection;
  66. /** @var LDAP|\PHPUnit_Framework_MockObject_MockObject */
  67. private $ldap;
  68. /** @var Manager|\PHPUnit_Framework_MockObject_MockObject */
  69. private $userManager;
  70. /** @var Helper|\PHPUnit_Framework_MockObject_MockObject */
  71. private $helper;
  72. /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */
  73. private $config;
  74. /** @var IUserManager|\PHPUnit_Framework_MockObject_MockObject */
  75. private $ncUserManager;
  76. /** @var Access */
  77. private $access;
  78. protected function setUp(): void {
  79. $this->connection = $this->createMock(Connection::class);
  80. $this->ldap = $this->createMock(LDAP::class);
  81. $this->userManager = $this->createMock(Manager::class);
  82. $this->helper = $this->createMock(Helper::class);
  83. $this->config = $this->createMock(IConfig::class);
  84. $this->userMapper = $this->createMock(UserMapping::class);
  85. $this->ncUserManager = $this->createMock(IUserManager::class);
  86. $this->access = new Access(
  87. $this->connection,
  88. $this->ldap,
  89. $this->userManager,
  90. $this->helper,
  91. $this->config,
  92. $this->ncUserManager
  93. );
  94. $this->access->setUserMapper($this->userMapper);
  95. }
  96. private function getConnectorAndLdapMock() {
  97. $lw = $this->createMock(ILDAPWrapper::class);
  98. $connector = $this->getMockBuilder(Connection::class)
  99. ->setConstructorArgs([$lw, null, null])
  100. ->getMock();
  101. $um = $this->getMockBuilder(Manager::class)
  102. ->setConstructorArgs([
  103. $this->createMock(IConfig::class),
  104. $this->createMock(FilesystemHelper::class),
  105. $this->createMock(LogWrapper::class),
  106. $this->createMock(IAvatarManager::class),
  107. $this->createMock(Image::class),
  108. $this->createMock(IDBConnection::class),
  109. $this->createMock(IUserManager::class),
  110. $this->createMock(INotificationManager::class)])
  111. ->getMock();
  112. $helper = new Helper(\OC::$server->getConfig());
  113. return [$lw, $connector, $um, $helper];
  114. }
  115. public function testEscapeFilterPartValidChars() {
  116. $input = 'okay';
  117. $this->assertTrue($input === $this->access->escapeFilterPart($input));
  118. }
  119. public function testEscapeFilterPartEscapeWildcard() {
  120. $input = '*';
  121. $expected = '\\\\*';
  122. $this->assertTrue($expected === $this->access->escapeFilterPart($input));
  123. }
  124. public function testEscapeFilterPartEscapeWildcard2() {
  125. $input = 'foo*bar';
  126. $expected = 'foo\\\\*bar';
  127. $this->assertTrue($expected === $this->access->escapeFilterPart($input));
  128. }
  129. /**
  130. * @dataProvider convertSID2StrSuccessData
  131. * @param array $sidArray
  132. * @param $sidExpected
  133. */
  134. public function testConvertSID2StrSuccess(array $sidArray, $sidExpected) {
  135. $sidBinary = implode('', $sidArray);
  136. $this->assertSame($sidExpected, $this->access->convertSID2Str($sidBinary));
  137. }
  138. public function convertSID2StrSuccessData() {
  139. return [
  140. [
  141. [
  142. "\x01",
  143. "\x04",
  144. "\x00\x00\x00\x00\x00\x05",
  145. "\x15\x00\x00\x00",
  146. "\xa6\x81\xe5\x0e",
  147. "\x4d\x6c\x6c\x2b",
  148. "\xca\x32\x05\x5f",
  149. ],
  150. 'S-1-5-21-249921958-728525901-1594176202',
  151. ],
  152. [
  153. [
  154. "\x01",
  155. "\x02",
  156. "\xFF\xFF\xFF\xFF\xFF\xFF",
  157. "\xFF\xFF\xFF\xFF",
  158. "\xFF\xFF\xFF\xFF",
  159. ],
  160. 'S-1-281474976710655-4294967295-4294967295',
  161. ],
  162. ];
  163. }
  164. public function testConvertSID2StrInputError() {
  165. $sidIllegal = 'foobar';
  166. $sidExpected = '';
  167. $this->assertSame($sidExpected, $this->access->convertSID2Str($sidIllegal));
  168. }
  169. public function testGetDomainDNFromDNSuccess() {
  170. $inputDN = 'uid=zaphod,cn=foobar,dc=my,dc=server,dc=com';
  171. $domainDN = 'dc=my,dc=server,dc=com';
  172. $this->ldap->expects($this->once())
  173. ->method('explodeDN')
  174. ->with($inputDN, 0)
  175. ->willReturn(explode(',', $inputDN));
  176. $this->assertSame($domainDN, $this->access->getDomainDNFromDN($inputDN));
  177. }
  178. public function testGetDomainDNFromDNError() {
  179. $inputDN = 'foobar';
  180. $expected = '';
  181. $this->ldap->expects($this->once())
  182. ->method('explodeDN')
  183. ->with($inputDN, 0)
  184. ->willReturn(false);
  185. $this->assertSame($expected, $this->access->getDomainDNFromDN($inputDN));
  186. }
  187. public function dnInputDataProvider() {
  188. return [[
  189. [
  190. 'input' => 'foo=bar,bar=foo,dc=foobar',
  191. 'interResult' => [
  192. 'count' => 3,
  193. 0 => 'foo=bar',
  194. 1 => 'bar=foo',
  195. 2 => 'dc=foobar'
  196. ],
  197. 'expectedResult' => true
  198. ],
  199. [
  200. 'input' => 'foobarbarfoodcfoobar',
  201. 'interResult' => false,
  202. 'expectedResult' => false
  203. ]
  204. ]];
  205. }
  206. /**
  207. * @dataProvider dnInputDataProvider
  208. * @param array $case
  209. */
  210. public function testStringResemblesDN($case) {
  211. list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock();
  212. /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject $config */
  213. $config = $this->createMock(IConfig::class);
  214. $access = new Access($con, $lw, $um, $helper, $config, $this->ncUserManager);
  215. $lw->expects($this->exactly(1))
  216. ->method('explodeDN')
  217. ->willReturnCallback(function ($dn) use ($case) {
  218. if($dn === $case['input']) {
  219. return $case['interResult'];
  220. }
  221. return null;
  222. });
  223. $this->assertSame($case['expectedResult'], $access->stringResemblesDN($case['input']));
  224. }
  225. /**
  226. * @dataProvider dnInputDataProvider
  227. * @param $case
  228. */
  229. public function testStringResemblesDNLDAPmod($case) {
  230. list(, $con, $um, $helper) = $this->getConnectorAndLdapMock();
  231. /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject $config */
  232. $config = $this->createMock(IConfig::class);
  233. $lw = new LDAP();
  234. $access = new Access($con, $lw, $um, $helper, $config, $this->ncUserManager);
  235. if(!function_exists('ldap_explode_dn')) {
  236. $this->markTestSkipped('LDAP Module not available');
  237. }
  238. $this->assertSame($case['expectedResult'], $access->stringResemblesDN($case['input']));
  239. }
  240. public function testCacheUserHome() {
  241. $this->connection->expects($this->once())
  242. ->method('writeToCache');
  243. $this->access->cacheUserHome('foobar', '/foobars/path');
  244. }
  245. public function testBatchApplyUserAttributes() {
  246. $this->ldap->expects($this->any())
  247. ->method('isResource')
  248. ->willReturn(true);
  249. $this->ldap->expects($this->any())
  250. ->method('getAttributes')
  251. ->willReturn(['displayname' => ['bar', 'count' => 1]]);
  252. /** @var UserMapping|\PHPUnit_Framework_MockObject_MockObject $mapperMock */
  253. $mapperMock = $this->createMock(UserMapping::class);
  254. $mapperMock->expects($this->any())
  255. ->method('getNameByDN')
  256. ->willReturn(false);
  257. $mapperMock->expects($this->any())
  258. ->method('map')
  259. ->willReturn(true);
  260. $userMock = $this->createMock(User::class);
  261. // also returns for userUuidAttribute
  262. $this->access->connection->expects($this->any())
  263. ->method('__get')
  264. ->willReturn('displayName');
  265. $this->access->setUserMapper($mapperMock);
  266. $displayNameAttribute = strtolower($this->access->connection->ldapUserDisplayName);
  267. $data = [
  268. [
  269. 'dn' => ['foobar'],
  270. $displayNameAttribute => 'barfoo'
  271. ],
  272. [
  273. 'dn' => ['foo'],
  274. $displayNameAttribute => 'bar'
  275. ],
  276. [
  277. 'dn' => ['raboof'],
  278. $displayNameAttribute => 'oofrab'
  279. ]
  280. ];
  281. $userMock->expects($this->exactly(count($data)))
  282. ->method('processAttributes');
  283. $this->userManager->expects($this->exactly(count($data) * 2))
  284. ->method('get')
  285. ->willReturn($userMock);
  286. $this->access->batchApplyUserAttributes($data);
  287. }
  288. public function testBatchApplyUserAttributesSkipped() {
  289. /** @var UserMapping|\PHPUnit_Framework_MockObject_MockObject $mapperMock */
  290. $mapperMock = $this->createMock(UserMapping::class);
  291. $mapperMock->expects($this->any())
  292. ->method('getNameByDN')
  293. ->willReturn('a_username');
  294. $userMock = $this->createMock(User::class);
  295. $this->access->connection->expects($this->any())
  296. ->method('__get')
  297. ->willReturn('displayName');
  298. $this->access->setUserMapper($mapperMock);
  299. $displayNameAttribute = strtolower($this->access->connection->ldapUserDisplayName);
  300. $data = [
  301. [
  302. 'dn' => ['foobar'],
  303. $displayNameAttribute => 'barfoo'
  304. ],
  305. [
  306. 'dn' => ['foo'],
  307. $displayNameAttribute => 'bar'
  308. ],
  309. [
  310. 'dn' => ['raboof'],
  311. $displayNameAttribute => 'oofrab'
  312. ]
  313. ];
  314. $userMock->expects($this->never())
  315. ->method('processAttributes');
  316. $this->userManager->expects($this->any())
  317. ->method('get')
  318. ->willReturn($this->createMock(User::class));
  319. $this->access->batchApplyUserAttributes($data);
  320. }
  321. public function testBatchApplyUserAttributesDontSkip() {
  322. /** @var UserMapping|\PHPUnit_Framework_MockObject_MockObject $mapperMock */
  323. $mapperMock = $this->createMock(UserMapping::class);
  324. $mapperMock->expects($this->any())
  325. ->method('getNameByDN')
  326. ->willReturn('a_username');
  327. $userMock = $this->createMock(User::class);
  328. $this->access->connection->expects($this->any())
  329. ->method('__get')
  330. ->willReturn('displayName');
  331. $this->access->setUserMapper($mapperMock);
  332. $displayNameAttribute = strtolower($this->access->connection->ldapUserDisplayName);
  333. $data = [
  334. [
  335. 'dn' => ['foobar'],
  336. $displayNameAttribute => 'barfoo'
  337. ],
  338. [
  339. 'dn' => ['foo'],
  340. $displayNameAttribute => 'bar'
  341. ],
  342. [
  343. 'dn' => ['raboof'],
  344. $displayNameAttribute => 'oofrab'
  345. ]
  346. ];
  347. $userMock->expects($this->exactly(count($data)))
  348. ->method('processAttributes');
  349. $this->userManager->expects($this->exactly(count($data) * 2))
  350. ->method('get')
  351. ->willReturn($userMock);
  352. $this->access->batchApplyUserAttributes($data);
  353. }
  354. public function dNAttributeProvider() {
  355. // corresponds to Access::resemblesDN()
  356. return [
  357. 'dn' => ['dn'],
  358. 'uniqueMember' => ['uniquemember'],
  359. 'member' => ['member'],
  360. 'memberOf' => ['memberof']
  361. ];
  362. }
  363. /**
  364. * @dataProvider dNAttributeProvider
  365. * @param $attribute
  366. */
  367. public function testSanitizeDN($attribute) {
  368. list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock();
  369. /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject $config */
  370. $config = $this->createMock(IConfig::class);
  371. $dnFromServer = 'cn=Mixed Cases,ou=Are Sufficient To,ou=Test,dc=example,dc=org';
  372. $lw->expects($this->any())
  373. ->method('isResource')
  374. ->willReturn(true);
  375. $lw->expects($this->any())
  376. ->method('getAttributes')
  377. ->willReturn([
  378. $attribute => ['count' => 1, $dnFromServer]
  379. ]);
  380. $access = new Access($con, $lw, $um, $helper, $config, $this->ncUserManager);
  381. $values = $access->readAttribute('uid=whoever,dc=example,dc=org', $attribute);
  382. $this->assertSame($values[0], strtolower($dnFromServer));
  383. }
  384. public function testSetPasswordWithDisabledChanges() {
  385. $this->expectException(\Exception::class);
  386. $this->expectExceptionMessage('LDAP password changes are disabled');
  387. $this->connection
  388. ->method('__get')
  389. ->willReturn(false);
  390. /** @noinspection PhpUnhandledExceptionInspection */
  391. $this->access->setPassword('CN=foo', 'MyPassword');
  392. }
  393. public function testSetPasswordWithLdapNotAvailable() {
  394. $this->connection
  395. ->method('__get')
  396. ->willReturn(true);
  397. $connection = $this->createMock(LDAP::class);
  398. $this->connection
  399. ->expects($this->once())
  400. ->method('getConnectionResource')
  401. ->willReturn($connection);
  402. $this->ldap
  403. ->expects($this->once())
  404. ->method('isResource')
  405. ->with($connection)
  406. ->willReturn(false);
  407. /** @noinspection PhpUnhandledExceptionInspection */
  408. $this->assertFalse($this->access->setPassword('CN=foo', 'MyPassword'));
  409. }
  410. public function testSetPasswordWithRejectedChange() {
  411. $this->expectException(\OC\HintException::class);
  412. $this->expectExceptionMessage('Password change rejected.');
  413. $this->connection
  414. ->method('__get')
  415. ->willReturn(true);
  416. $connection = $this->createMock(LDAP::class);
  417. $this->connection
  418. ->expects($this->once())
  419. ->method('getConnectionResource')
  420. ->willReturn($connection);
  421. $this->ldap
  422. ->expects($this->once())
  423. ->method('isResource')
  424. ->with($connection)
  425. ->willReturn(true);
  426. $this->ldap
  427. ->expects($this->once())
  428. ->method('modReplace')
  429. ->with($connection, 'CN=foo', 'MyPassword')
  430. ->willThrowException(new ConstraintViolationException());
  431. /** @noinspection PhpUnhandledExceptionInspection */
  432. $this->access->setPassword('CN=foo', 'MyPassword');
  433. }
  434. public function testSetPassword() {
  435. $this->connection
  436. ->method('__get')
  437. ->willReturn(true);
  438. $connection = $this->createMock(LDAP::class);
  439. $this->connection
  440. ->expects($this->once())
  441. ->method('getConnectionResource')
  442. ->willReturn($connection);
  443. $this->ldap
  444. ->expects($this->once())
  445. ->method('isResource')
  446. ->with($connection)
  447. ->willReturn(true);
  448. $this->ldap
  449. ->expects($this->once())
  450. ->method('modReplace')
  451. ->with($connection, 'CN=foo', 'MyPassword')
  452. ->willReturn(true);
  453. /** @noinspection PhpUnhandledExceptionInspection */
  454. $this->assertTrue($this->access->setPassword('CN=foo', 'MyPassword'));
  455. }
  456. protected function prepareMocksForSearchTests(
  457. $base,
  458. $fakeConnection,
  459. $fakeSearchResultResource,
  460. $fakeLdapEntries
  461. ) {
  462. $this->connection
  463. ->expects($this->any())
  464. ->method('getConnectionResource')
  465. ->willReturn($fakeConnection);
  466. $this->connection->expects($this->any())
  467. ->method('__get')
  468. ->willReturnCallback(function($key) use ($base) {
  469. if(stripos($key, 'base') !== false) {
  470. return $base;
  471. }
  472. return null;
  473. });
  474. $this->ldap
  475. ->expects($this->any())
  476. ->method('isResource')
  477. ->willReturnCallback(function ($resource) use ($fakeConnection) {
  478. return $resource === $fakeConnection;
  479. });
  480. $this->ldap
  481. ->expects($this->any())
  482. ->method('errno')
  483. ->willReturn(0);
  484. $this->ldap
  485. ->expects($this->once())
  486. ->method('search')
  487. ->willReturn([$fakeSearchResultResource]);
  488. $this->ldap
  489. ->expects($this->exactly(count($base)))
  490. ->method('getEntries')
  491. ->willReturn($fakeLdapEntries);
  492. $this->helper->expects($this->any())
  493. ->method('sanitizeDN')
  494. ->willReturnArgument(0);
  495. }
  496. public function testSearchNoPagedSearch() {
  497. // scenario: no pages search, 1 search base
  498. $filter = 'objectClass=nextcloudUser';
  499. $base = ['ou=zombies,dc=foobar,dc=nextcloud,dc=com'];
  500. $fakeConnection = new \stdClass();
  501. $fakeSearchResultResource = new \stdClass();
  502. $fakeLdapEntries = [
  503. 'count' => 2,
  504. [
  505. 'dn' => 'uid=sgarth,' . $base[0],
  506. ],
  507. [
  508. 'dn' => 'uid=wwilson,' . $base[0],
  509. ]
  510. ];
  511. $expected = $fakeLdapEntries;
  512. unset($expected['count']);
  513. $this->prepareMocksForSearchTests($base, $fakeConnection, $fakeSearchResultResource, $fakeLdapEntries);
  514. /** @noinspection PhpUnhandledExceptionInspection */
  515. $result = $this->access->search($filter, $base);
  516. $this->assertSame($expected, $result);
  517. }
  518. public function testFetchListOfUsers() {
  519. $filter = 'objectClass=nextcloudUser';
  520. $base = ['ou=zombies,dc=foobar,dc=nextcloud,dc=com'];
  521. $attrs = ['dn', 'uid'];
  522. $fakeConnection = new \stdClass();
  523. $fakeSearchResultResource = new \stdClass();
  524. $fakeLdapEntries = [
  525. 'count' => 2,
  526. [
  527. 'dn' => 'uid=sgarth,' . $base[0],
  528. 'uid' => [ 'sgarth' ],
  529. ],
  530. [
  531. 'dn' => 'uid=wwilson,' . $base[0],
  532. 'uid' => [ 'wwilson' ],
  533. ]
  534. ];
  535. $expected = $fakeLdapEntries;
  536. unset($expected['count']);
  537. array_walk($expected, function(&$v) {
  538. $v['dn'] = [$v['dn']]; // dn is translated into an array internally for consistency
  539. });
  540. $this->prepareMocksForSearchTests($base, $fakeConnection, $fakeSearchResultResource, $fakeLdapEntries);
  541. $this->connection->expects($this->exactly($fakeLdapEntries['count']))
  542. ->method('writeToCache')
  543. ->with($this->stringStartsWith('userExists'), true);
  544. $this->userMapper->expects($this->exactly($fakeLdapEntries['count']))
  545. ->method('getNameByDN')
  546. ->willReturnCallback(function($fdn) {
  547. $parts = ldap_explode_dn($fdn, false);
  548. return $parts[0];
  549. });
  550. /** @noinspection PhpUnhandledExceptionInspection */
  551. $list = $this->access->fetchListOfUsers($filter, $attrs);
  552. $this->assertSame($expected, $list);
  553. }
  554. public function intUsernameProvider() {
  555. // system dependent :-/
  556. $translitExpected = @iconv('UTF-8', 'ASCII//TRANSLIT', 'fränk') ? 'frank' : 'frnk';
  557. return [
  558. ['alice', 'alice'],
  559. ['b/ob', 'bob'],
  560. ['charly🐬', 'charly'],
  561. ['debo rah', 'debo_rah'],
  562. ['epost@poste.test', 'epost@poste.test'],
  563. ['fränk', $translitExpected],
  564. [' gerda ', 'gerda'],
  565. ['🕱🐵🐘🐑', null]
  566. ];
  567. }
  568. /**
  569. * @dataProvider intUsernameProvider
  570. *
  571. * @param $name
  572. * @param $expected
  573. */
  574. public function testSanitizeUsername($name, $expected) {
  575. if($expected === null) {
  576. $this->expectException(\InvalidArgumentException::class);
  577. }
  578. $sanitizedName = $this->access->sanitizeUsername($name);
  579. $this->assertSame($expected, $sanitizedName);
  580. }
  581. public function testUserStateUpdate() {
  582. $this->connection->expects($this->any())
  583. ->method('__get')
  584. ->willReturnMap([
  585. [ 'ldapUserDisplayName', 'displayName' ],
  586. [ 'ldapUserDisplayName2', null],
  587. ]);
  588. $offlineUserMock = $this->createMock(OfflineUser::class);
  589. $offlineUserMock->expects($this->once())
  590. ->method('unmark');
  591. $regularUserMock = $this->createMock(User::class);
  592. $this->userManager->expects($this->atLeastOnce())
  593. ->method('get')
  594. ->with('detta')
  595. ->willReturnOnConsecutiveCalls($offlineUserMock, $regularUserMock);
  596. /** @var UserMapping|\PHPUnit_Framework_MockObject_MockObject $mapperMock */
  597. $mapperMock = $this->createMock(UserMapping::class);
  598. $mapperMock->expects($this->any())
  599. ->method('getNameByDN')
  600. ->with('uid=detta,ou=users,dc=hex,dc=ample')
  601. ->willReturn('detta');
  602. $this->access->setUserMapper($mapperMock);
  603. $records = [
  604. [
  605. 'dn' => ['uid=detta,ou=users,dc=hex,dc=ample'],
  606. 'displayName' => ['Detta Detkova'],
  607. ]
  608. ];
  609. $this->access->nextcloudUserNames($records);
  610. }
  611. }