/** * In this method, actually create the user / account. * * NOTE: After this method is called, the $registrationFlow is DESTROYED, so you need to store all attributes * in your object as you need them. * * @param RegistrationFlow $registrationFlow * @return void */ public function createUserAndAccount(RegistrationFlow $registrationFlow) { // Create the account $account = new Account(); $account->setAccountIdentifier($registrationFlow->getEmail()); $account->setCredentialsSource($registrationFlow->getEncryptedPassword()); $account->setAuthenticationProviderName('Sandstorm.UserManagement:Login'); // Assign pre-configured roles foreach ($this->rolesForNewUsers as $roleString) { $account->addRole(new Role($roleString)); } // Create the user $user = new User(); $user->setAccount($account); $user->setEmail($registrationFlow->getEmail()); if (array_key_exists('salutation', $registrationFlow->getAttributes())) { $user->setGender($registrationFlow->getAttributes()['salutation']); } if (array_key_exists('firstName', $registrationFlow->getAttributes())) { $user->setFirstName($registrationFlow->getAttributes()['firstName']); } if (array_key_exists('lastName', $registrationFlow->getAttributes())) { $user->setLastName($registrationFlow->getAttributes()['lastName']); } // Persist user $this->userRepository->add($user); $this->persistenceManager->whitelistObject($user); $this->persistenceManager->whitelistObject($account); }
/** * @test */ public function testUserTracing() { $account = new Account(); $account->setAccountIdentifier('testUserTracing'); $account->setAuthenticationProviderName('TestingProvider'); $this->persistenceManager->add($account); $this->persistenceManager->persistAll(); $this->authenticateAccount($account); $entity = new Entity(); $this->entityRepository->add($entity); $this->persistenceManager->persistAll(); $this->assertEquals($entity->getCreatedBy(), $account); $this->assertNull($entity->getUpdatedBy()); $entity->setType('testUserTracing'); $this->entityRepository->update($entity); $this->persistenceManager->persistAll(); $this->assertEquals($entity->getUpdatedBy(), $account); $account2 = new Account(); $account2->setAccountIdentifier('testUserTracing2'); $account2->setAuthenticationProviderName('TestingProvider'); $this->persistenceManager->add($account2); $this->persistenceManager->persistAll(); $this->authenticateAccount($account2); $entity->setType('testUserTracing2'); $this->entityRepository->update($entity); $this->persistenceManager->persistAll(); $this->assertEquals($entity->getUpdatedBy(), $account2); $this->assertNotEquals($entity->getCreatedBy(), $account2); }
/** * Creates a new account and sets the given password and roles * * @param string $identifier Identifier of the account, must be unique * @param string $password The clear text password * @param array $roleIdentifiers Optionally an array of role identifiers to assign to the new account * @param string $authenticationProviderName Optional name of the authentication provider the account is affiliated with * @param string $passwordHashingStrategy Optional password hashing strategy to use for the password * @return Account A new account, not yet added to the account repository */ public function createAccountWithPassword($identifier, $password, $roleIdentifiers = [], $authenticationProviderName = 'DefaultProvider', $passwordHashingStrategy = 'default') { $account = new Account(); $account->setAccountIdentifier($identifier); $account->setCredentialsSource($this->hashService->hashPassword($password, $passwordHashingStrategy)); $account->setAuthenticationProviderName($authenticationProviderName); $roles = []; foreach ($roleIdentifiers as $roleIdentifier) { $roles[] = $this->policyService->getRole($roleIdentifier); } $account->setRoles($roles); return $account; }
/** * 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); }
/** * In this method, actually create the user / account. * * NOTE: After this method is called, the $registrationFlow is DESTROYED, so you need to store all attributes * in your object as you need them. * * @param RegistrationFlow $registrationFlow * @return void */ public function createUserAndAccount(RegistrationFlow $registrationFlow) { // Create the account $account = new Account(); $account->setAccountIdentifier($registrationFlow->getEmail()); $account->setCredentialsSource($registrationFlow->getEncryptedPassword()); $account->setAuthenticationProviderName('Sandstorm.UserManagement:Login'); // Assign preconfigured roles foreach ($this->rolesForNewUsers as $roleString) { $account->addRole(new Role($roleString)); } // Create the user $user = new User(); $name = new PersonName('', $registrationFlow->getAttributes()['firstName'], '', $registrationFlow->getAttributes()['lastName'], '', $registrationFlow->getEmail()); $user->setName($name); // Assign them to each other and persist $this->getPartyService()->assignAccountToParty($account, $user); $this->getPartyRepository()->add($user); $this->accountRepository->add($account); $this->persistenceManager->whitelistObject($user); $this->persistenceManager->whitelistObject($user->getPreferences()); $this->persistenceManager->whitelistObject($name); $this->persistenceManager->whitelistObject($account); }
/** * Authenticate the current token. If it's not possible to connect to the LDAP server the provider * tries to authenticate against cached credentials in the database that were * cached on the last successful login for the user to authenticate. * * @param TokenInterface $authenticationToken The token to be authenticated * @throws UnsupportedAuthenticationTokenException * @return void */ public function authenticate(TokenInterface $authenticationToken) { if (!$authenticationToken instanceof UsernamePassword) { throw new UnsupportedAuthenticationTokenException('This provider cannot authenticate the given token.', 1217339840); } $account = null; $credentials = $authenticationToken->getCredentials(); if (is_array($credentials) && isset($credentials['username'])) { if ($this->directoryService->isServerOnline()) { try { $ldapUser = $this->directoryService->authenticate($credentials['username'], $credentials['password']); if ($ldapUser) { $account = $this->accountRepository->findActiveByAccountIdentifierAndAuthenticationProviderName($credentials['username'], $this->name); $newlyCreatedAccount = false; if ($account === null) { $account = new Account(); $account->setAccountIdentifier($credentials['username']); $account->setAuthenticationProviderName($this->name); $this->createParty($account, $ldapUser); $this->accountRepository->add($account); $newlyCreatedAccount = true; } if ($account instanceof Account) { if ($this->allowStandinAuthentication === true) { // Cache the password to have cached login if LDAP is unavailable $account->setCredentialsSource($this->hashService->generateHmac($credentials['password'])); } $authenticationToken->setAuthenticationStatus(TokenInterface::AUTHENTICATION_SUCCESSFUL); $authenticationToken->setAccount($account); $this->setRoles($account, $ldapUser); $this->emitRolesSet($account, $ldapUser); if ($newlyCreatedAccount === false) { $this->updateParty($account, $ldapUser); } $this->emitAccountAuthenticated($account, $ldapUser); $this->accountRepository->update($account); } elseif ($authenticationToken->getAuthenticationStatus() !== TokenInterface::AUTHENTICATION_SUCCESSFUL) { $authenticationToken->setAuthenticationStatus(TokenInterface::NO_CREDENTIALS_GIVEN); } } else { $authenticationToken->setAuthenticationStatus(TokenInterface::WRONG_CREDENTIALS); } } catch (\Exception $exception) { $this->logger->log('Authentication failed: ' . $exception->getMessage(), LOG_ALERT); } } elseif ($this->allowStandinAuthentication === true) { $account = $this->accountRepository->findActiveByAccountIdentifierAndAuthenticationProviderName($credentials['username'], $this->name); // Server not available, fallback to the cached password hash if ($account instanceof Account) { if ($this->hashService->validateHmac($credentials['password'], $account->getCredentialsSource())) { $authenticationToken->setAuthenticationStatus(TokenInterface::AUTHENTICATION_SUCCESSFUL); $authenticationToken->setAccount($account); } else { $authenticationToken->setAuthenticationStatus(TokenInterface::WRONG_CREDENTIALS); } } elseif ($authenticationToken->getAuthenticationStatus() !== TokenInterface::AUTHENTICATION_SUCCESSFUL) { $authenticationToken->setAuthenticationStatus(TokenInterface::NO_CREDENTIALS_GIVEN); } } else { $this->logger->log('Authentication failed: directory server offline and standin authentication is disabled', LOG_ALERT); $authenticationToken->setAuthenticationStatus(TokenInterface::WRONG_CREDENTIALS); } } else { $authenticationToken->setAuthenticationStatus(TokenInterface::NO_CREDENTIALS_GIVEN); } }
/** * Authenticates against a crowd instance. * * @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.', 1217339845); } $credentials = $authenticationToken->getCredentials(); if (is_array($credentials) && isset($credentials['username']) && isset($credentials['password'])) { $crowdAuthenticationResponse = $this->crowdClient->authenticate($credentials['username'], $credentials['password']); if ($crowdAuthenticationResponse !== NULL) { /** @var $account \TYPO3\Flow\Security\Account */ $account = NULL; $providerName = $this->name; $accountRepository = $this->accountRepository; $this->securityContext->withoutAuthorizationChecks(function () use($credentials, $providerName, $accountRepository, &$account) { $account = $accountRepository->findActiveByAccountIdentifierAndAuthenticationProviderName($credentials['username'], $providerName); }); if ($account === NULL) { $account = new Account(); $account->setAuthenticationProviderName($providerName); $account->setAccountIdentifier($credentials['username']); $this->accountRepository->add($account); } $authenticateRole = $this->policyService->getRole($this->options['authenticateRole']); if ($account->hasRole($authenticateRole) === FALSE) { $account->addRole($authenticateRole); } $crowdUser = $this->partyService->getAssignedPartyOfAccount($account); if ($crowdUser instanceof Person) { if ($crowdUser->getName()->getFirstName() !== $crowdAuthenticationResponse['first-name']) { $crowdUser->getName()->setFirstName($crowdAuthenticationResponse['first-name']); $this->partyRepository->update($crowdUser); } if ($crowdUser->getName()->getLastName() !== $crowdAuthenticationResponse['last-name']) { $crowdUser->getName()->setLastName($crowdAuthenticationResponse['last-name']); $this->partyRepository->update($crowdUser); } if ($crowdUser->getPrimaryElectronicAddress()->getIdentifier() !== $crowdAuthenticationResponse['email']) { $crowdUser->getPrimaryElectronicAddress()->setIdentifier($crowdAuthenticationResponse['email']); $this->partyRepository->update($crowdUser); } } else { $crowdUser = new Person(); $crowdUser->setName(new PersonName('', $crowdAuthenticationResponse['first-name'], '', $crowdAuthenticationResponse['last-name'])); $email = new ElectronicAddress(); $email->setIdentifier($crowdAuthenticationResponse['email']); $email->setType(ElectronicAddress::TYPE_EMAIL); $crowdUser->setPrimaryElectronicAddress($email); $this->partyRepository->add($crowdUser); $this->partyService->assignAccountToParty($account, $crowdUser); } $authenticationToken->setAuthenticationStatus(TokenInterface::AUTHENTICATION_SUCCESSFUL); $authenticationToken->setAccount($account); } else { $authenticationToken->setAuthenticationStatus(TokenInterface::WRONG_CREDENTIALS); } } elseif ($authenticationToken->getAuthenticationStatus() !== TokenInterface::AUTHENTICATION_SUCCESSFUL) { $authenticationToken->setAuthenticationStatus(TokenInterface::NO_CREDENTIALS_GIVEN); } }
/** * @param array $userdata * @return Account */ protected function createAccount(array $userdata) { if (!isset($userdata['username'])) { return; } $account = new Account(); $account->setCredentialsSource('typo3.org SSO'); $account->setAuthenticationProviderName($this->name); $account->setRoles(array($this->policyService->getRole('T3DD.Backend:Authenticated'))); $account->setAccountIdentifier($userdata['username']); $person = new Person(); $this->partyRepository->add($person); $this->partyService->assignAccountToParty($account, $person); $this->updatePerson($person, $userdata); $this->accountRepository->add($account); $this->persistenceManager->persistAll(); return $account; }
/** * @test */ public function shutdownCreatesSpecialDataEntryForSessionWithAuthenticatedAccounts() { $session = new Session(); $this->inject($session, 'bootstrap', $this->mockBootstrap); $this->inject($session, 'objectManager', $this->mockObjectManager); $this->inject($session, 'settings', $this->settings); $this->inject($session, 'metaDataCache', $this->createCache('Meta')); $this->inject($session, 'storageCache', $this->createCache('Storage')); $session->initializeObject(); $session->start(); $account = new Account(); $account->setAccountIdentifier('admin'); $account->setAuthenticationProviderName('MyProvider'); $token = new UsernamePassword(); $token->setAuthenticationStatus(TokenInterface::AUTHENTICATION_SUCCESSFUL); $token->setAccount($account); $this->mockSecurityContext->expects($this->any())->method('isInitialized')->will($this->returnValue(TRUE)); $this->mockSecurityContext->expects($this->any())->method('getAuthenticationTokens')->will($this->returnValue(array($token))); $session->close(); $this->httpRequest->setCookie($this->httpResponse->getCookie('TYPO3_Flow_Session')); $session->resume(); $this->assertEquals(array('MyProvider:admin'), $session->getData('TYPO3_Flow_Security_Accounts')); }
/** * @param string $username Crowd Username * @param string $providerName Name of the authentication provider, this account should be used with * @return Account */ public function getLocalAccountForCrowdUser($username, $providerName) { $accountRepository = $this->accountRepository; $this->securityContext->withoutAuthorizationChecks(function () use($username, $providerName, $accountRepository, &$account) { $account = $accountRepository->findActiveByAccountIdentifierAndAuthenticationProviderName($username, $providerName); }); if ($account === NULL) { if ($this->getUser($username) === NULL) { return NULL; } $account = new Account(); $account->setAuthenticationProviderName($providerName); $account->setAccountIdentifier($username); $roleIdentifier = $this->configurationManager->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, 'TYPO3.Flow.security.authentication.providers.' . $providerName . '.providerOptions.authenticateRole'); $account->addRole($this->policyService->getRole($roleIdentifier)); $this->accountRepository->add($account); $this->persistenceManager->persistAll(); } return $account; }
/** * 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); }
/** * {@inheritDoc} */ public function setAuthenticationProviderName($authenticationProviderName) { $this->__initializer__ && $this->__initializer__->__invoke($this, 'setAuthenticationProviderName', array($authenticationProviderName)); return parent::setAuthenticationProviderName($authenticationProviderName); }
/** * Returns Account. * * @param string $providerName Provider name to fetch an account from. * @param array $casAttributes * * @throws JasigPhpCasSecurityException * * @return Account */ public function getAccount($providerName, array $casAttributes) { $accountSettings = $this->settings[$providerName]['Account']; $account = new Account(); $account->setAuthenticationProviderName($providerName); $accountIdentifier = ArraysUtility::getValueByPath($casAttributes, $accountSettings['accountidentifier']); if (is_string($accountIdentifier)) { $account->setAccountIdentifier($accountIdentifier); } else { throw new JasigPhpCasSecurityException(sprintf('Cas attribute for ... .%s.providerOptions.Mapping.Account.accountidentifier is not a string. Doubtless you configured path to CAS-Attributes-array-value wrong.', $providerName)); } if (isset($accountSettings['useStaticProviderName']) && empty($accountSettings['forceUseStaticProviderName'])) { $account->setAuthenticationProviderName($accountSettings['useStaticProviderName']); } if (isset($accountSettings['forceUseStaticProviderName'])) { $account->setAuthenticationProviderName($accountSettings['forceUseStaticProviderName']); } if (isset($accountSettings['periodOfValidity']) && is_int($accountSettings['periodOfValidity'])) { $date = new \DateTime(); $date->modify('+' . $accountSettings['periodOfValidity'] . ' day'); $account->setExpirationDate($date); } return $account; }