/** * Checks the given token for validity and sets the token authentication status * accordingly (success, wrong credentials or no credentials given). * * @param \TYPO3\Flow\Security\Authentication\TokenInterface $authenticationToken The token to be authenticated * @return void * @throws \TYPO3\Flow\Security\Exception\UnsupportedAuthenticationTokenException */ public function authenticate(TokenInterface $authenticationToken) { if (!$authenticationToken instanceof UsernamePassword) { throw new UnsupportedAuthenticationTokenException('This provider cannot authenticate the given token.', 1217339840); } /** @var $account \TYPO3\Flow\Security\Account */ $account = null; $credentials = $authenticationToken->getCredentials(); if (is_array($credentials) && isset($credentials['username'])) { $providerName = $this->name; $accountRepository = $this->accountRepository; $this->securityContext->withoutAuthorizationChecks(function () use($credentials, $providerName, $accountRepository, &$account) { $account = $accountRepository->findActiveByAccountIdentifierAndAuthenticationProviderName($credentials['username'], $providerName); }); } if (is_object($account)) { if ($this->hashService->validatePassword($credentials['password'], $account->getCredentialsSource())) { $account->authenticationAttempted(TokenInterface::AUTHENTICATION_SUCCESSFUL); $authenticationToken->setAuthenticationStatus(TokenInterface::AUTHENTICATION_SUCCESSFUL); $authenticationToken->setAccount($account); } else { $account->authenticationAttempted(TokenInterface::WRONG_CREDENTIALS); $authenticationToken->setAuthenticationStatus(TokenInterface::WRONG_CREDENTIALS); } $this->accountRepository->update($account); $this->persistenceManager->whitelistObject($account); } elseif ($authenticationToken->getAuthenticationStatus() !== TokenInterface::AUTHENTICATION_SUCCESSFUL) { $authenticationToken->setAuthenticationStatus(TokenInterface::NO_CREDENTIALS_GIVEN); } }
/** * @param string $emailAddress * @param string $requirement * * @throws \Exception * * @return string */ public function resetPasswordAction($emailAddress, $requirement = '') { if ($requirement !== '') { throw new \Exception('Bot detection', 12393182738); } $locale = new Locale('nl'); $account = $this->accountRepository->findActiveByAccountIdentifierAndAuthenticationProviderName($emailAddress, 'DefaultProvider'); if ($account instanceof Account) { try { /** @var Person $profile */ $profile = $this->profileService->getProfileNodeOfAccount($account); $password = $this->randomPassword(); $hashedPassword = $this->hashService->hashPassword($password, 'default'); $this->mailerService->sendEmail(array('email' => $emailAddress, 'name' => $profile->getDisplayName()), 'Nieuw wachtwoord', 'Packages/Application/BuJitsuDo.Authentication/Resources/Private/Templates/Email/PasswordReset.html', array('password' => $password, 'profile' => $profile)); $account->setCredentialsSource($hashedPassword); $this->accountRepository->update($account); $this->persistenceManager->persistAll(); } catch (\Exception $exception) { return $exception->getMessage(); } } else { $this->response->setHeader('Notification', $this->translator->translateById('profile.reset.password.response.failure', [], NULL, $locale, 'Main', 'BuJitsuDo.Authentication')); $this->response->setHeader('NotificationType', 'alert'); return ''; } $this->response->setHeader('Notification', $this->translator->translateById('profile.reset.password.response.success', [], NULL, $locale, 'Main', 'BuJitsuDo.Authentication')); $this->response->setHeader('NotificationType', 'success'); return ''; }
/** * Sets a new password for the given user * * @param User $user The user to set the password for * @param string $password A new password * @return void */ public function setUserPassword(User $user, $password) { foreach ($user->getAccounts() as $account) { /** @var Account $account */ $account->setCredentialsSource($this->hashService->hashPassword($password, 'default')); $this->accountRepository->update($account); } }
/** * @param TokenInterface $foreignAccountToken * @param AbstractClientToken $possibleOAuthTokenAuthenticatedWithoutParty */ public function setPartyOfAuthenticatedTokenAndAttachToAccountFor(TokenInterface $foreignAccountToken, AbstractClientToken $possibleOAuthTokenAuthenticatedWithoutParty) { $oauthAccount = $possibleOAuthTokenAuthenticatedWithoutParty->getAccount(); // TODO: this must be properly specifiable (the Roles to add) #$oauthAccount->setRoles(); $this->partyService->assignAccountToParty($oauthAccount, $this->partyService->getAssignedPartyOfAccount($foreignAccountToken)); $this->accountRepository->update($oauthAccount); }
/** * Deactivates the given user * * @param User $user The user to deactivate * @return void * @api */ public function deactivateUser(User $user) { foreach ($user->getAccounts() as $account) { /** @var Account $account */ $account->setExpirationDate($this->now); $this->accountRepository->update($account); } $this->emitUserDeactivated($user); }
/** * Tries to authenticate the given token. Sets isAuthenticated to TRUE if authentication succeeded. * * @param TokenInterface $authenticationToken The token to be authenticated * @throws \TYPO3\Flow\Security\Exception\UnsupportedAuthenticationTokenException * @return void */ public function authenticate(TokenInterface $authenticationToken) { if (!$authenticationToken instanceof AbstractClientToken) { throw new UnsupportedAuthenticationTokenException('This provider cannot authenticate the given token.', 1383754993); } $credentials = $authenticationToken->getCredentials(); // There is no way to validate the Token or check the scopes at the moment apart from "trying" (and possibly receiving an access denied) // we could check the validity of the Token and the scopes here in the future when Instagram provides that // Only check if an access Token is present at this time and do a single test call if (isset($credentials['accessToken']) && $credentials['accessToken'] !== NULL) { // check if a secure request is possible (https://www.instagram.com/developer/secure-api-requests/) $userInfo = $this->instagramTokenEndpoint->validateSecureRequestCapability($credentials['accessToken']); if ($userInfo === FALSE) { $authenticationToken->setAuthenticationStatus(TokenInterface::WRONG_CREDENTIALS); $this->securityLogger->log('A secure call to the API with the provided accessToken and clientSecret was not possible', LOG_NOTICE); return FALSE; } } else { } // From here, we surely know the user is considered authenticated against the remote service, // yet to check if there is an immanent account present. $authenticationToken->setAuthenticationStatus(TokenInterface::AUTHENTICATION_SUCCESSFUL); /** @var $account \TYPO3\Flow\Security\Account */ $account = NULL; $providerName = $this->name; $accountRepository = $this->accountRepository; $this->securityContext->withoutAuthorizationChecks(function () use($userInfo, $providerName, $accountRepository, &$account) { $account = $accountRepository->findByAccountIdentifierAndAuthenticationProviderName($userInfo['id'], $providerName); }); if ($account === NULL) { $account = new Account(); $account->setAccountIdentifier($userInfo['id']); $account->setAuthenticationProviderName($providerName); $this->accountRepository->add($account); } $authenticationToken->setAccount($account); // the access token is valid for an "undefined time" according to instagram (so we cannot know when the user needs to log in again) $account->setCredentialsSource($credentials['accessToken']); $this->accountRepository->update($account); // check if a user is already attached to this account if ($this->partyService->getAssignedPartyOfAccount($account) === null || count($this->partyService->getAssignedPartyOfAccount($account)) < 1) { $user = $this->userService->getCurrentUser(); if ($user !== null) { $user->addAccount($account); $this->userService->updateUser($user); $this->persistenceManager->whitelistObject($user); } else { $this->securityLogger->logException(new Exception("The InstagramProvider was unable to determine the backend user, make sure the configuration Typo3BackendProvider requestPattern matches the Instagram Controller and the authentication strategy is set to 'atLeastOne' Token")); } } // persistAll is called automatically at the end of this function, account gets whitelisted to allow // persisting for an object thats tinkered with via a GET request $this->persistenceManager->whitelistObject($account); }
/** * @param ResetPasswordFlow $resetPasswordFlow */ public function updatePasswordAction(ResetPasswordFlow $resetPasswordFlow) { $account = $this->accountRepository->findActiveByAccountIdentifierAndAuthenticationProviderName($resetPasswordFlow->getEmail(), 'Sandstorm.UserManagement:Login'); if (!$account) { $this->view->assign('accountNotFound', true); return; } $this->view->assign('success', true); $account->setCredentialsSource($resetPasswordFlow->getEncryptedPassword()); $this->accountRepository->update($account); $this->resetPasswordFlowRepository->remove($resetPasswordFlow); }
/** * Adds new roles from CAS server since last authentication if some was added in CAS-Server. * Is used only if Account was persisted. See persistAccount() method. * * @param string $providerName Provider name. WARNING: not in settings set useStaticProviderNameByPersistingAccounts. * @param Account $account * * @return void * * @todo : move persistAll() at shutdown */ private function updateRolesInAccount($providerName, Account &$account) { $casAttributes = $this->casManager->getCasAttributes($providerName); $casServerRoles = $this->getRoles($providerName, $casAttributes); $accountMustBeUpdated = false; foreach ($casServerRoles as $casServerRole) { $accountMustBeUpdated = $accountMustBeUpdated == true ? $accountMustBeUpdated : !$account->hasRole($casServerRole); $account->addRole($casServerRole); } if ($accountMustBeUpdated) { $this->accountRepository->update($account); } $this->persistenceManager->persistAll(); }
/** * @param \DLigo\Animaltool\Domain\Model\User $user * @param array $username * @Flow\Validate(argumentName="$username", type="notEmpty") * @param array $password * @param string $role * @Flow\Validate(argumentName="$password", type="\DLigo\Animaltool\Validation\Validator\PasswordValidator", options={"allowEmpty"=true}) * @Flow\Validate(argumentName="$username", type="\DLigo\Animaltool\Validation\Validator\AccountExistsValidator") * @return void */ public function updateAction(User $user, $username, $password = null, $role = null) { if ($role) { $roleObj = $this->policyService->getRole($role); foreach ($user->getAccounts() as $account) { $account->setRoles(array($role => $roleObj)); $account->setAccountIdentifier($username['new']); $account->setCredentialsSource($this->hashService->hashPassword($password[0], 'default')); $this->accountRepository->update($account); } } $this->userRepository->update($user); $this->addFlashMessage('Updated the user.', '', \TYPO3\Flow\Error\Message::SEVERITY_OK, array(), 'flash.user.update'); $this->redirect('index'); }
/** * Tries to authenticate the given token. Sets isAuthenticated to TRUE if authentication succeeded. * * @param TokenInterface $authenticationToken The token to be authenticated * @throws \TYPO3\Flow\Security\Exception\UnsupportedAuthenticationTokenException * @return void */ public function authenticate(TokenInterface $authenticationToken) { if (!$authenticationToken instanceof AbstractClientToken) { throw new UnsupportedAuthenticationTokenException('This provider cannot authenticate the given token.', 1383754993); } $credentials = $authenticationToken->getCredentials(); // Inspect the received access token as documented in https://developers.facebook.com/docs/facebook-login/login-flow-for-web-no-jssdk/ $tokenInformation = $this->facebookTokenEndpoint->requestValidatedTokenInformation($credentials['accessToken']); if ($tokenInformation === FALSE) { $authenticationToken->setAuthenticationStatus(TokenInterface::WRONG_CREDENTIALS); return; } // Check if the permitted scopes suffice: $necessaryScopes = $this->options['scopes']; $scopesHavingPermissionFor = $tokenInformation['scopes']; $requiredButNotPermittedScopes = array_diff($necessaryScopes, $scopesHavingPermissionFor); if (count($requiredButNotPermittedScopes) > 0) { $authenticationToken->setAuthenticationStatus(TokenInterface::WRONG_CREDENTIALS); $this->securityLogger->log('The permitted scopes do not satisfy the required once.', LOG_NOTICE, array('necessaryScopes' => $necessaryScopes, 'allowedScopes' => $scopesHavingPermissionFor)); return; } // From here, we surely know the user is considered authenticated against the remote service, // yet to check if there is an immanent account present. $authenticationToken->setAuthenticationStatus(TokenInterface::AUTHENTICATION_SUCCESSFUL); /** @var $account \TYPO3\Flow\Security\Account */ $account = NULL; $providerName = $this->name; $accountRepository = $this->accountRepository; $this->securityContext->withoutAuthorizationChecks(function () use($tokenInformation, $providerName, $accountRepository, &$account) { $account = $accountRepository->findByAccountIdentifierAndAuthenticationProviderName($tokenInformation['user_id'], $providerName); }); if ($account === NULL) { $account = new Account(); $account->setAccountIdentifier($tokenInformation['user_id']); $account->setAuthenticationProviderName($providerName); $this->accountRepository->add($account); } $authenticationToken->setAccount($account); // request long-live token and attach that to the account $longLivedToken = $this->facebookTokenEndpoint->requestLongLivedToken($credentials['accessToken']); $account->setCredentialsSource($longLivedToken); $this->accountRepository->update($account); }
/** * Set a new password for the given user * * @param string $username user to modify * @param string $password new password * @param string $authenticationProvider Name of the authentication provider to use for finding the user. Default: "Sandstorm.UserManagement:Login". * @return void */ public function setPasswordCommand($username, $password, $authenticationProvider = 'Sandstorm.UserManagement:Login') { // If we're in Neos context, we simply forward the command to the Neos command controller. if ($this->shouldUseNeosService()) { $cliRequest = new Request($this->request); $cliRequest->setControllerObjectName(UserCommandController::class); $cliRequest->setControllerCommandName('setPassword'); $cliRequest->setArguments(['username' => $username, 'password' => $password, 'authenticationProvider' => $authenticationProvider]); $cliResponse = new Response($this->response); $this->dispatcher->dispatch($cliRequest, $cliResponse); $this->quit(0); } // Otherwise, we use our own logic. $account = $this->accountRepository->findByAccountIdentifierAndAuthenticationProviderName($username, $authenticationProvider); if ($account === null) { $this->outputLine('The user <b>' . $username . '</b> could not be found with auth provider <b>' . $authenticationProvider . '</b>.'); $this->quit(1); } $encrypted = $this->hashService->hashPassword($password); $account->setCredentialsSource($encrypted); $this->accountRepository->update($account); $this->outputLine('Password for user <b>' . $username . '</b> changed.'); }
/** * @param string $password, * @param string $passwordconfirm * @param string $code * @return string|void */ public function changePasswordAction($password = NULL, $passwordconfirm = NULL, $code = NULL) { if ($code !== NULL) { $cryptJson = $code; $cryptKey = md5($this->providerName); $uncryptJson = base64_decode($cryptJson); $uncryptJson = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $cryptKey, $uncryptJson, MCRYPT_MODE_CBC, md5($cryptKey)); $uncryptJson = rtrim($uncryptJson, ""); $json = json_decode($uncryptJson); } else { $json = NULL; } $this->view->assign('code', $code); // @TODO Check if User has random number if ($json != NULL) { if ($this->time->getTimestamp() - $json->date > 86400) { $this->flashMessageContainer->addMessage(new \TYPO3\Flow\Error\Error($this->translator->translateById('login.messages.registration.not_valid', array(), NULL, NULL, 'Main', 'Incvisio.LostFound'))); $this->redirect('index', 'Standard', NULL, array()); } else { $account = $this->accountRepository->findByAccountIdentifierAndAuthenticationProviderName($json->username, $this->providerName); if ($password == $passwordconfirm && $password !== NULL) { $account->setExpirationDate(NULL); $account->setCredentialsSource($this->hashService->hashPassword($password, 'default')); $this->accountRepository->update($account); $this->flashMessageContainer->addMessage(new Message($this->translator->translateById('login.login.update', array(), NULL, NULL, 'Main', 'Incvisio.LostFound'))); $this->redirect('index', 'Standard', NULL, array()); } else { if ($password !== NULL) { $this->flashMessageContainer->addMessage(new \TYPO3\Flow\Error\Error("Sorry")); } } } } else { $this->flashMessageContainer->addMessage(new \TYPO3\Flow\Error\Error($this->translator->translateById('login.messages.registration.not_valid', array(), NULL, NULL, 'Main', 'Incvisio.LostFound'))); $this->redirect('index', 'Standard', NULL, array()); } }
/** * Sends an email to a user with the new password * * @param \TYPO3\Flow\Security\Account $account * @param array $settings * @param string $newEnteredPassword * @return boolean $success */ public function sendMail(Account $account, $settings, $newEnteredPassword = NULL) { if ($newEnteredPassword !== NULL) { $newPassword = $newEnteredPassword; } else { $newPassword = $this->algorithms->generateRandomString(10); $account->setCredentialsSource($this->hashService->hashPassword($newPassword, 'default')); $this->accountRepository->update($account); } // @TODO: Localize the email format $mailBody[] = 'Dear %1$s'; $mailBody[] = ''; $mailBody[] = 'Your password for First Visit.'; $mailBody[] = 'The password is %2$s'; $mailBody[] = ''; $mailBody[] = 'If you haven\'t requested this information, please change your password at once'; $mailBody[] = 'as others might be able to access your account'; $success = FALSE; $message = new SwiftMessage(); if ($message->setTo(array($account->getAccountIdentifier() => $account->getParty()->getName()))->setFrom(array($settings['PasswordRecovery']['Sender']['Email'] => $settings['PasswordRecovery']['Sender']['Name']))->setSubject($settings['PasswordRecovery']['Subject'])->setBody(vsprintf(implode(PHP_EOL, $mailBody), array($account->getParty()->getName(), $newPassword)))->send()) { $success = TRUE; } return $success; }
/** * @param AbstractClientToken $token * @throws InvalidPartyDataException */ public function createPartyAndAttachToAccountFor(AbstractClientToken $token) { $userData = $this->authenticationServicesUserData[(string) $token]; $party = new Person(); $party->setName(new PersonName('', $userData['first_name'], '', $userData['last_name'])); // Todo: this is not covered by the Person implementation, we should have a solution for that #$party->setBirthDate(\DateTime::createFromFormat('!m/d/Y', $userData['birthday'], new \DateTimeZone('UTC'))); #$party->setGender(substr($userData['gender'], 0, 1)); $electronicAddress = new ElectronicAddress(); $electronicAddress->setType(ElectronicAddress::TYPE_EMAIL); $electronicAddress->setIdentifier($userData['email']); $party->addElectronicAddress($electronicAddress); $partyValidator = $this->validatorResolver->getBaseValidatorConjunction('TYPO3\\Party\\Domain\\Model\\Person'); $validationResult = $partyValidator->validate($party); if ($validationResult->hasErrors()) { throw new InvalidPartyDataException('The created party does not satisfy the requirements', 1384266207); } $account = $token->getAccount(); $account->setParty($party); // TODO: this must be properly specifiable (the Roles to add) #$account->setRoles(); $this->accountRepository->update($account); $this->partyRepository->add($party); }
/** * Set a new password for the given account * * This allows for setting a new password for an existing user account. * * @param Account $account * @param $password * @param string $passwordHashingStrategy * * @return boolean */ public function resetPassword(Account $account, $password, $passwordHashingStrategy = 'default') { $account->setCredentialsSource($this->hashService->hashPassword($password, $passwordHashingStrategy)); $this->accountRepository->update($account); return TRUE; }