Commit 099ef776 authored by Jacob Priddy's avatar Jacob Priddy 👌

Fix saml user importing

parent 8b391448
Pipeline #12446 passed with stages
in 3 minutes and 5 seconds
......@@ -250,6 +250,19 @@ class User
return $this->getEmail() === strtolower($email);
}
/**
* @param string|null $emplid
* @return bool
*/
public function hasEmplidOf(?string $emplid): bool
{
if (!$emplid) {
return false;
}
return $this->getEmplid() === $emplid;
}
/**
* @param string|null $name
* @return bool
......
......@@ -195,4 +195,19 @@ class DatabaseUsersRepository implements UsersRepository
return self::makeUserFromDbUser($user);
}
/**
* @inheritDoc
*/
public function findByEmplid(string $emplid): ?User
{
/** @var \App\User|null $user */
$user = \App\User::query()->where('emplid', $emplid)->first();
if (!$user) {
return null;
}
return self::makeUserFromDbUser($user);
}
}
......@@ -134,4 +134,18 @@ class InMemoryUsersRepository implements UsersRepository
return null;
}
/**
* @inheritDoc
*/
public function findByEmplid(string $emplid): ?User
{
foreach ($this->users as $user) {
if ($user->hasEmplidOf($emplid)) {
return $user;
}
}
return null;
}
}
......@@ -62,4 +62,12 @@ interface UsersRepository
* @return User|null
*/
public function findByEmail(string $email): ?User;
/**
* Find a user by emplid
*
* @param string $emplid
* @return \Source\Entities\User|null
*/
public function findByEmplid(string $emplid): ?User;
}
......@@ -95,24 +95,65 @@ class Authenticate implements AuthenticateUseCase
}
// First check to see if the user exists in the database.
$user = $this->users->findByEmail($samlUser->getEmail());
// If the user does not exist, create them.
// We check by emplid as university emails can be recycled,
// but emplids are always unique. In our db structure,
// emails are also unique so we get both and have to do some
// extra checking to make sure we get the right user.
$user = $this->users->findByEmplid($samlUser->getEmplid());
$emailUser = $this->users->findByEmail($samlUser->getEmail());
/*
* Determine Update Or Create with Incoming SAML User
*
* At this point, we have several options.
* - Emplid not found, email is found
* Update found email user with saml attributes, but clear set password
* A person can get in this state by either having same email as somebody previously who left,
* or if they were created without having an emplid be put in
* - Emplid not found, email not found
* Create a brand new user
* - Emplid found, email found
* Update found emplid user if they are the same, if not, delete email user and update emplid user
* - Emplid found, email not found
* Update found emplid user
*/
if (!$user) {
$user = $this->users->create(new User(
0,
$samlUser->getFirstName(),
$samlUser->getLastName(),
$samlUser->getDisplayName(),
$samlUser->getEmail(),
$samlUser->getEmplid()
));
if ($emailUser) {
$user = $this->users->update($emailUser->getId(), new User(
$emailUser->getId(),
$samlUser->getFirstName(),
$samlUser->getLastName(),
$samlUser->getDisplayName(),
$samlUser->getEmail(),
$samlUser->getEmplid(),
null,
$emailUser->getDoorcode(),
$emailUser->getExpiresAt(),
$emailUser->getCreatedAt(),
$emailUser->getUpdatedAt()
));
} else {
$user = $this->users->create(new User(
0,
$samlUser->getFirstName(),
$samlUser->getLastName(),
$samlUser->getDisplayName(),
$samlUser->getEmail(),
$samlUser->getEmplid()
));
}
} else {
// If they are not the same users, something is weird,
// so we'll delete the email user and use the emplid user
if ($emailUser && !$user->hasIdOf($emailUser->getId())) {
$this->users->delete($emailUser->getId());
}
$user = $this->users->update($user->getId(), new User(
$user->getId(),
$samlUser->getFirstName(),
$samlUser->getLastName(),
$samlUser->getDisplayName(),
$user->getDisplayName(),
$samlUser->getEmail(),
$samlUser->getEmplid(),
$user->getPassword(),
......
......@@ -40,6 +40,7 @@ class UserDatabaseTest extends DatabaseTestCase
/**
* @test
* @throws \Source\Exceptions\EntityExistsException
*/
public function it_gets_all_users(): void
{
......@@ -57,6 +58,7 @@ class UserDatabaseTest extends DatabaseTestCase
/**
* @test
* @throws \Source\Exceptions\EntityExistsException
*/
public function it_creates_a_user(): void
{
......@@ -70,7 +72,10 @@ class UserDatabaseTest extends DatabaseTestCase
$this->assertEquals('password', $user->getPassword()->getHash());
}
/** @test */
/**
* @test
* @throws \Source\Exceptions\EntityExistsException
*/
public function it_updates_a_user(): void
{
$user = $this->users->create($this->createUser());
......@@ -131,6 +136,7 @@ class UserDatabaseTest extends DatabaseTestCase
/**
* @test
* @throws \Source\Exceptions\EntityExistsException
*/
public function it_deletes_a_user(): void
{
......@@ -147,6 +153,7 @@ class UserDatabaseTest extends DatabaseTestCase
/**
* @test
* @throws \Source\Exceptions\EntityExistsException
*/
public function it_gets_a_user(): void
{
......@@ -167,6 +174,7 @@ class UserDatabaseTest extends DatabaseTestCase
/**
* @test
* @throws \Source\Exceptions\EntityExistsException
*/
public function it_checks_existence_of_users(): void
{
......@@ -179,6 +187,7 @@ class UserDatabaseTest extends DatabaseTestCase
/**
* @test
* @throws \Source\Exceptions\EntityExistsException
*/
public function it_can_find_users(): void
{
......@@ -187,6 +196,9 @@ class UserDatabaseTest extends DatabaseTestCase
$this->assertEquals($user, $this->users->findByEmail('email'));
$this->assertNull($this->users->findByEmail('email1'));
$this->assertEquals($user, $this->users->findByEmplid('emplid'));
$this->assertNull($this->users->findByEmplid('non existent'));
$this->assertEquals($user, $this->users->findByDoorcode(new HashedSearchable('doorcode')));
$this->assertNull($this->users->findByDoorcode(new HashedSearchable('what doorcode')));
$this->assertNull($this->users->findByDoorcode(null));
......
......@@ -89,4 +89,12 @@ class InMemoryUsersRepositoryStub implements UsersRepository
{
return null;
}
/**
* @inheritDoc
*/
public function findByEmplid(string $emplid): ?User
{
return null;
}
}
......@@ -6,6 +6,7 @@ namespace Tests\Unit\Source\UseCases\Users\Authenticate;
use Carbon\Carbon;
use Source\Entities\User;
use Source\Entities\Token;
use Source\Entities\Password;
use Source\Entities\SamlUser;
use Source\Entities\HashedSearchable;
use Source\Exceptions\AuthorizationException;
......@@ -16,6 +17,7 @@ use Source\UseCases\Users\Authenticate\UserCreationException;
class SamlUseCaseTest extends UseCaseBaseTest
{
protected const VALID_EMAIL = 'email';
protected const VALID_EMPLID = '245855';
/**
* @param \Source\Entities\SamlUser|null $samlUser
......@@ -36,12 +38,24 @@ class SamlUseCaseTest extends UseCaseBaseTest
}
/**
* @param string $email
* @param string|null $emplid
* @return \Source\Entities\User
* @throws \Source\Exceptions\EntityExistsException
*/
protected function createUser(): User
protected function createUser(string $email = self::VALID_EMAIL, ?string $emplid = null): User
{
return $this->users->create(new User(0, '', '', '', self::VALID_EMAIL));
return $this->users->create(
new User(
0,
'',
'',
'',
$email,
$emplid,
new Password('hash')
)
);
}
/**
......@@ -49,7 +63,7 @@ class SamlUseCaseTest extends UseCaseBaseTest
*/
protected function createSamlUser(): SamlUser
{
return new SamlUser('first', 'last', 'emplid', strtoupper(self::VALID_EMAIL));
return new SamlUser('first', 'last', self::VALID_EMPLID, strtoupper(self::VALID_EMAIL));
}
/**
......@@ -135,8 +149,8 @@ class SamlUseCaseTest extends UseCaseBaseTest
$this->assertEquals('First', $user->getFirstName());
$this->assertEquals('Last', $user->getLastName());
$this->assertEquals('First Last', $user->getDisplayName());
$this->assertEquals('emplid', $user->getEmplid());
$this->assertEquals('email', $user->getEmail());
$this->assertEquals(self::VALID_EMPLID, $user->getEmplid());
$this->assertEquals(self::VALID_EMAIL, $user->getEmail());
$this->assertNull($user->getPassword());
$this->assertNull($user->getDoorcode());
}
......@@ -231,4 +245,87 @@ class SamlUseCaseTest extends UseCaseBaseTest
$this->handleLoginTest($samlUser);
}
/**
* @test
* @throws \Source\Exceptions\AuthorizationException
* @throws \Source\Exceptions\EntityExistsException
* @throws \Source\Exceptions\EntityNotFoundException
* @throws \Source\UseCases\Users\Authenticate\UserCreationException
*/
public function it_merges_into_user_found_by_email(): void
{
$id = $this->createUser(self::VALID_EMAIL, 'not valid emplid')->getId();
$this->handleLoginTest($this->createSamlUser());
$u = $this->users->get($id);
$this->assertEquals(self::VALID_EMPLID, $u->getEmplid());
}
/**
* @test
* @throws \Source\Exceptions\AuthorizationException
* @throws \Source\Exceptions\EntityExistsException
* @throws \Source\Exceptions\EntityNotFoundException
* @throws \Source\UseCases\Users\Authenticate\UserCreationException
*/
public function it_creates_a_new_user_if_nothing_is_found(): void
{
$this->handleLoginTest($this->createSamlUser());
$users = $this->users->search();
$this->assertCount(1, $users);
$u = $users[0];
$this->assertEquals(self::VALID_EMAIL, $u->getEmail());
$this->assertEquals(self::VALID_EMPLID, $u->getEmplid());
}
/**
* @test
* @throws \Source\Exceptions\AuthorizationException
* @throws \Source\Exceptions\EntityExistsException
* @throws \Source\Exceptions\EntityNotFoundException
* @throws \Source\UseCases\Users\Authenticate\UserCreationException
*/
public function it_deletes_duplicate_user_if_joined_by_saml(): void
{
$this->createUser(self::VALID_EMAIL, null);
$this->createUser('other email', self::VALID_EMPLID);
$this->assertCount(2, $this->users->search());
$this->handleLoginTest($this->createSamlUser());
$this->assertCount(1, $this->users->search());
$u = $this->users->search()[0];
$this->assertEquals(self::VALID_EMPLID, $u->getEmplid());
$this->assertEquals(self::VALID_EMAIL, $u->getEmail());
}
/**
* @test
* @throws \Source\Exceptions\AuthorizationException
* @throws \Source\Exceptions\EntityExistsException
* @throws \Source\Exceptions\EntityNotFoundException
* @throws \Source\UseCases\Users\Authenticate\UserCreationException
*/
public function it_updates_found_user(): void
{
$this->createUser(self::VALID_EMAIL, self::VALID_EMPLID);
$this->handleLoginTest($this->createSamlUser());
$this->assertCount(1, $this->users->search());
$u = $this->users->search()[0];
$this->assertEquals('', $u->getDisplayName());
$this->assertEquals(self::VALID_EMPLID, $u->getEmplid());
$this->assertEquals(self::VALID_EMAIL, $u->getEmail());
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment