saml = $saml; $this->users = $users; $this->tokens = $tokens; $this->salt = $salt; } /** * @inheritDoc */ public function attempt(Presenter $presenter, array $credentials): void { $email = $credentials['email'] ?? null; $password = $credentials['password'] ?? null; if (!$email || !$password) { throw new AuthenticationException(); } $user = $this->users->findByEmail(strtolower($email)); if (!$user || !$user->getPassword() || !$user->isActiveForDate(Carbon::now()) || !$user->getPassword()->matches($password)) { /* * We must have found a user, they must be active, and their password must be correct */ throw new AuthenticationException(); } $token = $this->tokens->createLoginToken($user->getId(), $this->salt); $response = new ResponseModel($user, $token->getRaw(), $token->getToken()); $presenter->present($response); } /** * @inheritDoc */ public function handToSaml(array $options = []): string { return $this->saml->login($options); } /** * @inheritDoc */ public function handleSamlLogin(Presenter $presenter): void { $samlUser = $this->saml->handleLogin(); if (!$samlUser) { throw new UserCreationException(); } // First check to see if the user exists in the database. // 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 */ $newlyCreated = false; if (!$user) { 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 { // We want to direct new users to the $newlyCreated = true; $user = $this->users->create(new User( 0, $samlUser->getFirstName(), $samlUser->getLastName(), $samlUser->getDisplayName(), $samlUser->getEmail(), $samlUser->getEmplid(), null, null, Carbon::now()->addYears(5), )); } } 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(), $user->getDisplayName(), $samlUser->getEmail(), $samlUser->getEmplid(), $user->getPassword(), $user->getDoorcode(), $user->getExpiresAt(), $user->getCreatedAt(), $user->getUpdatedAt() )); } if (!$user) { throw new UserCreationException(); } if (!$user->isActiveForDate(Carbon::now())) { throw new AuthorizationException(); } $token = $this->tokens->createLoginToken($user->getId(), $this->salt); $response = new ResponseModel($user, $token->getRaw(), $token->getToken(), $newlyCreated); $presenter->present($response); } /** * @inheritDoc */ public function samlLogout(?string $token): string { if ($token) { $this->tokens->invalidateToken(HashedSearchable::hash($this->salt, $token)); } return $this->saml->logout(); } }