/**
  * Log signed request pattern failures
  *
  * @Flow\AfterReturning("setting(Flowpack.SingleSignOn.Client.log.logFailedSignedRequests) && method(Flowpack\SingleSignOn\Client\Security\RequestPattern\SignedRequestPattern->emitSignatureNotVerified())")
  * @param \TYPO3\Flow\Aop\JoinPointInterface $joinPoint The current joinpoint
  */
 public function logSignedRequestPatternFailures(\TYPO3\Flow\Aop\JoinPointInterface $joinPoint)
 {
     $request = $joinPoint->getMethodArgument('request');
     if ($request instanceof \TYPO3\Flow\Mvc\RequestInterface) {
         if ($request->getControllerObjectName() === 'Flowpack\\SingleSignOn\\Client\\Controller\\SessionController') {
             $this->securityLogger->log('Signature for call to Session service could not be verified', LOG_NOTICE, array('identifier' => $joinPoint->getMethodArgument('identifier'), 'publicKeyFingerprint' => $joinPoint->getMethodArgument('publicKeyFingerprint'), 'signature' => base64_encode($joinPoint->getMethodArgument('signature')), 'signData' => $joinPoint->getMethodArgument('signData'), 'content' => $joinPoint->getMethodArgument('request')->getHttpRequest()->getContent()));
         }
     }
 }
 /**
  * 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);
 }
 /**
  * Logs calls and results of decideOnResource()
  *
  * @Flow\AfterThrowing("method(TYPO3\Flow\Security\Authorization\AccessDecisionVoterManager->decideOnResource())")
  * @param \TYPO3\Flow\Aop\JoinPointInterface $joinPoint
  * @throws \Exception
  * @return void
  */
 public function logResourceAccessDecisions(\TYPO3\Flow\Aop\JoinPointInterface $joinPoint)
 {
     $exception = $joinPoint->getException();
     $message = $exception->getMessage() . ' on resource "' . $joinPoint->getMethodArgument('resource') . '".';
     $this->securityLogger->log($message, \LOG_INFO);
     throw $exception;
 }
 /**
  * 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);
 }
 /**
  * Inspect the received access token as documented in https://developers.facebook.com/docs/facebook-login/access-tokens/, section Getting Info about Tokens and Debugging
  *
  * @param string $tokenToInspect
  * @return array
  * @throws OAuth2Exception
  */
 public function requestValidatedTokenInformation($tokenToInspect)
 {
     $applicationToken = $this->requestClientCredentialsGrantAccessToken();
     $requestArguments = array('input_token' => $tokenToInspect, 'access_token' => $applicationToken);
     $request = Request::create(new Uri('https://graph.facebook.com/debug_token?' . http_build_query($requestArguments)));
     $response = $this->requestEngine->sendRequest($request);
     $responseContent = $response->getContent();
     if ($response->getStatusCode() !== 200) {
         throw new OAuth2Exception(sprintf('The response was not of type 200 but gave code and error %d "%s"', $response->getStatusCode(), $responseContent), 1383758360);
     }
     $responseArray = json_decode($responseContent, TRUE, 16, JSON_BIGINT_AS_STRING);
     $responseArray['data']['app_id'] = (string) $responseArray['data']['app_id'];
     $responseArray['data']['user_id'] = (string) $responseArray['data']['user_id'];
     if (!$responseArray['data']['is_valid'] || $responseArray['data']['app_id'] !== $this->clientIdentifier) {
         $this->securityLogger->log('Requesting validated token information from the Facebook endpoint did not succeed.', LOG_NOTICE, array('response' => var_export($responseArray, TRUE), 'clientIdentifier' => $this->clientIdentifier));
         return FALSE;
     } else {
         return $responseArray['data'];
     }
 }
 /**
  * Updates the authentication credentials, the authentication manager needs to authenticate this token.
  * This could be a username/password from a login controller.
  * This method is called while initializing the security context. By returning TRUE you
  * make sure that the authentication manager will (re-)authenticate the tokens with the current credentials.
  * Note: You should not persist the credentials!
  *
  * @param ActionRequest $actionRequest The current request instance
  * @throws \InvalidArgumentException
  * @return boolean TRUE if this token needs to be (re-)authenticated
  */
 public function updateCredentials(ActionRequest $actionRequest)
 {
     if ($actionRequest->getHttpRequest()->getMethod() !== 'GET' || $actionRequest->getInternalArgument('__oauth2Provider') !== $this->authenticationProviderName) {
         return;
     }
     if (!$actionRequest->hasArgument('code')) {
         $this->setAuthenticationStatus(TokenInterface::WRONG_CREDENTIALS);
         $this->securityLogger->log('There was no argument `code` provided.', LOG_NOTICE);
         return;
     }
     $code = $actionRequest->getArgument('code');
     $redirectUri = $this->oauthUriBuilder->getRedirectionEndpointUri($this->authenticationProviderName);
     try {
         $this->credentials['accessToken'] = $this->tokenEndpoint->requestAuthorizationCodeGrantAccessToken($code, $redirectUri);
         $this->setAuthenticationStatus(TokenInterface::AUTHENTICATION_NEEDED);
     } catch (Exception $exception) {
         $this->setAuthenticationStatus(TokenInterface::WRONG_CREDENTIALS);
         $this->securityLogger->logException($exception);
         return;
     }
 }
 /**
  * @param string $resource
  * @param array $requestArguments
  * @param string $method
  * @return mixed
  * @throws OAuth2Exception
  */
 public function query($resource, $requestArguments = array(), $method = 'GET')
 {
     $requestArguments['access_token'] = $this->currentAccessToken;
     $requestArguments['sig'] = $this->generate_sig($resource, $requestArguments);
     // test the secure API call by getting information of the own user - scope: basic (also available in sandbox mode)
     $request = Request::create(new Uri($this->endpoint . $resource . "?" . http_build_query($requestArguments)));
     $response = $this->requestEngine->sendRequest($request);
     $responseContent = $response->getContent();
     if ($response->getStatusCode() !== 200) {
         $this->securityLogger->log('Error in Instagram Query: ' . $responseContent);
         throw new OAuth2Exception(sprintf('The response was not of type 200 but gave code and error %d "%s"', $response->getStatusCode(), $responseContent), 1455261376);
     }
     return json_decode($responseContent, true);
 }
 /**
  * Advices the dispatch method so that illegal action requests are blocked before
  * invoking any controller.
  *
  * The "request" referred to within this method is an ActionRequest or some other
  * dispatchable request implementing RequestInterface. Note that we don't deal
  * with HTTP requests here.
  *
  * @Flow\Around("setting(TYPO3.Flow.security.enable) && method(TYPO3\Flow\Mvc\Dispatcher->dispatch())")
  * @param \TYPO3\Flow\Aop\JoinPointInterface $joinPoint The current joinpoint
  * @return mixed Result of the advice chain
  * @throws \Exception|\TYPO3\Flow\Security\Exception\AccessDeniedException
  * @throws \Exception|\TYPO3\Flow\Security\Exception\AuthenticationRequiredException
  */
 public function blockIllegalRequestsAndForwardToAuthenticationEntryPoints(JoinPointInterface $joinPoint)
 {
     $request = $joinPoint->getMethodArgument('request');
     if (!$request instanceof ActionRequest || $this->securityContext->areAuthorizationChecksDisabled()) {
         return $joinPoint->getAdviceChain()->proceed($joinPoint);
     }
     try {
         $this->firewall->blockIllegalRequests($request);
         return $joinPoint->getAdviceChain()->proceed($joinPoint);
     } catch (AuthenticationRequiredException $exception) {
         $response = $joinPoint->getMethodArgument('response');
         $entryPointFound = FALSE;
         /** @var $token \TYPO3\Flow\Security\Authentication\TokenInterface */
         foreach ($this->securityContext->getAuthenticationTokens() as $token) {
             $entryPoint = $token->getAuthenticationEntryPoint();
             if ($entryPoint !== NULL) {
                 $entryPointFound = TRUE;
                 if ($entryPoint instanceof WebRedirect) {
                     $this->securityLogger->log('Redirecting to authentication entry point', LOG_INFO, $entryPoint->getOptions());
                 } else {
                     $this->securityLogger->log('Starting authentication with entry point of type ' . get_class($entryPoint), LOG_INFO);
                 }
                 $this->securityContext->setInterceptedRequest($request->getMainRequest());
                 $entryPoint->startAuthentication($request->getHttpRequest(), $response);
             }
         }
         if ($entryPointFound === FALSE) {
             $this->securityLogger->log('No authentication entry point found for active tokens, therefore cannot authenticate or redirect to authentication automatically.', LOG_NOTICE);
             throw $exception;
         }
     } catch (AccessDeniedException $exception) {
         $this->securityLogger->log('Access denied', LOG_WARNING);
         throw $exception;
     }
     return NULL;
 }
Пример #9
0
 /**
  * Sets the roles for the LDAP account.
  * Extend this Provider class and implement this method to update the party
  *
  * @param Account $account
  * @param array $ldapSearchResult
  * @return void
  */
 protected function setRoles(Account $account, array $ldapSearchResult)
 {
     if (is_array($this->rolesConfiguration)) {
         $contextVariables = array('ldapUser' => $ldapSearchResult);
         if (isset($this->defaultContext) && is_array($this->defaultContext)) {
             foreach ($this->defaultContext as $contextVariable => $objectName) {
                 $object = $this->objectManager->get($objectName);
                 $contextVariables[$contextVariable] = $object;
             }
         }
         foreach ($this->rolesConfiguration['default'] as $roleIdentifier) {
             $role = $this->policyService->getRole($roleIdentifier);
             $account->addRole($role);
         }
         $eelContext = new Context($contextVariables);
         if (isset($this->partyConfiguration['dn'])) {
             $dn = $this->eelEvaluator->evaluate($this->partyConfiguration['dn'], $eelContext);
             foreach ($this->rolesConfiguration['userMapping'] as $roleIdentifier => $userDns) {
                 if (in_array($dn, $userDns)) {
                     $role = $this->policyService->getRole($roleIdentifier);
                     $account->addRole($role);
                 }
             }
         } elseif (!empty($this->rolesConfiguration['userMapping'])) {
             $this->logger->log('User mapping found but no party mapping for dn set', LOG_ALERT);
         }
         if (isset($this->partyConfiguration['username'])) {
             $username = $this->eelEvaluator->evaluate($this->partyConfiguration['username'], $eelContext);
             $groupMembership = $this->directoryService->getGroupMembership($username);
             foreach ($this->rolesConfiguration['groupMapping'] as $roleIdentifier => $remoteRoleIdentifiers) {
                 foreach ($remoteRoleIdentifiers as $remoteRoleIdentifier) {
                     $role = $this->policyService->getRole($roleIdentifier);
                     if (isset($groupMembership[$remoteRoleIdentifier])) {
                         $account->addRole($role);
                     }
                 }
             }
         } elseif (!empty($this->rolesConfiguration['groupMapping'])) {
             $this->logger->log('Group mapping found but no party mapping for username set', LOG_ALERT);
         }
     }
 }
 /**
  * Merges the session and manager tokens. All manager tokens types will be in the result array
  * If a specific type is found in the session this token replaces the one (of the same type)
  * given by the manager.
  *
  * @param array $managerTokens Array of tokens provided by the authentication manager
  * @param array $sessionTokens Array of tokens restored from the session
  * @return array Array of \TYPO3\Flow\Security\Authentication\TokenInterface objects
  */
 protected function mergeTokens($managerTokens, $sessionTokens)
 {
     $resultTokens = array();
     if (!is_array($managerTokens)) {
         return $resultTokens;
     }
     /** @var $managerToken \TYPO3\Flow\Security\Authentication\TokenInterface */
     foreach ($managerTokens as $managerToken) {
         $noCorrespondingSessionTokenFound = true;
         if (!is_array($sessionTokens)) {
             continue;
         }
         /** @var $sessionToken \TYPO3\Flow\Security\Authentication\TokenInterface */
         foreach ($sessionTokens as $sessionToken) {
             if ($sessionToken->getAuthenticationProviderName() === $managerToken->getAuthenticationProviderName()) {
                 $session = $this->sessionManager->getCurrentSession();
                 $this->securityLogger->log(sprintf('Session %s contains auth token %s for provider %s. Status: %s', $session->getId(), get_class($sessionToken), $sessionToken->getAuthenticationProviderName(), $this->tokenStatusLabels[$sessionToken->getAuthenticationStatus()]), LOG_INFO, null, 'Flow');
                 $resultTokens[$sessionToken->getAuthenticationProviderName()] = $sessionToken;
                 $noCorrespondingSessionTokenFound = false;
             }
         }
         if ($noCorrespondingSessionTokenFound) {
             $resultTokens[$managerToken->getAuthenticationProviderName()] = $managerToken;
         }
     }
     return $resultTokens;
 }
 /**
  * Logs calls and result of isPrivilegeTargetGranted()
  *
  * @Flow\After("method(TYPO3\Flow\Security\Authorization\PrivilegeManager->isPrivilegeTargetGranted())")
  * @param JoinPointInterface $joinPoint
  * @return void
  */
 public function logPrivilegeAccessDecisions(JoinPointInterface $joinPoint)
 {
     $decision = $joinPoint->getResult() === true ? 'GRANTED' : 'DENIED';
     $message = sprintf('Decided "%s" on privilege "%s".', $decision, $joinPoint->getMethodArgument('privilegeTargetIdentifier'));
     $this->securityLogger->log($message, \LOG_INFO);
 }