/** * @param Site $site * @param string $userId ID of the user that initiates the session. It is used to track individual sessions so they can be destroyed * on demand. This value is signed, so it cannot be intercepted. * @param string|null $userName User to log in as. Pass null to use the user with ID 1, which should always exist and have full privileges * (hardcoded in Drupal). This value is signed, so it cannot be intercepted. * * @return UriInterface */ public function generateUrl(Site $site, $userId, $userName = null) { $requestId = bin2hex(random_bytes(16)); $requestExpiresAt = time() + 86400; $query = ['oxygenRequestId' => $requestId, 'requestExpiresAt' => $requestExpiresAt, 'actionName' => 'site.login', 'signature' => \Undine\Functions\openssl_sign_data($site->getPrivateKey(), sprintf('%s|%d|%s|%s', $requestId, $requestExpiresAt, $userId, (string) $userName)), 'userName' => $userName, 'userId' => $userId]; $url = $site->getUrl(); if ($site->hasHttpCredentials()) { $url = $url->withUserInfo($site->getHttpCredentials()->getUsername(), $site->getHttpCredentials()->getPassword()); } return $url->withQuery(\GuzzleHttp\Psr7\build_query($query)); }
public function __invoke(RequestInterface $request, array $options) { $fn = $this->nextHandler; if (!isset($options['oxygen_site']) || !$options['oxygen_site'] instanceof Site) { throw new \RuntimeException(sprintf('The option "oxygen_site" is expected to contain an instance of %s.', Site::class)); } if (!isset($options['oxygen_action']) || !$options['oxygen_action'] instanceof ActionInterface) { throw new \RuntimeException(sprintf('The option "oxygen_action" is expected to contain an instance of %s.', ActionInterface::class)); } $transferInfo = $previousCallable = null; if (isset($options['on_stats'])) { $previousCallable = $options['on_stats']; } $options['on_stats'] = function (TransferStats $stats) use(&$transferInfo, $previousCallable) { $transferInfo = new TransferInfo($stats->getHandlerStats()); if ($previousCallable) { /* @var callable $previousCallable */ $previousCallable($stats); } }; /** @var Site $site */ $site = $options['oxygen_site']; /** @var ActionInterface $action */ $action = $options['oxygen_action']; $options['request_id'] = $requestId = \Undine\Functions\generate_uuid(); // Kind of like str_rot8, for hexadecimal strings. $responseId = strtr($requestId, 'abcdef0123456789', '23456789abcdef01'); $expiresAt = time() + 86400; $userName = ''; $stateParameters = $this->stateTracker->getParameters($site); $requestData = ['oxygenRequestId' => $requestId, 'requestExpiresAt' => $expiresAt, 'publicKey' => $site->getPublicKey(), 'signature' => \Undine\Functions\openssl_sign_data($site->getPrivateKey(), sprintf('%s|%d', $requestId, $expiresAt)), 'handshakeKey' => $this->handshakeKeyName, 'handshakeSignature' => \Undine\Functions\openssl_sign_data($this->handshakeKeyValue, $this->getUrlSlug($site->getUrl())), 'version' => $this->moduleVersion, 'baseUrl' => (string) $site->getUrl(), 'actionName' => $action->getName(), 'actionParameters' => $action->getParameters(), 'userName' => $userName, 'userId' => $site->getUser()->getId(), 'stateParameters' => $stateParameters]; $oxygenRequest = $request->withHeader('accept', 'text/html,application/json,application/oxygen')->withBody(\GuzzleHttp\Psr7\stream_for(json_encode($requestData))); if ($site->hasHttpCredentials()) { $oxygenRequest = $oxygenRequest->withHeader('Authorization', 'Basic ' . base64_encode(sprintf('%s:%s', $site->getHttpCredentials()->getUsername(), $site->getHttpCredentials()->getPassword()))); } return $fn($oxygenRequest, $options)->then(function (ResponseInterface $response) use($site, $request, $options, &$transferInfo, $responseId, $action) { $responseData = $this->extractData($responseId, $request, $options, $response, $transferInfo); try { $reaction = $this->createReaction($action, $responseData); } catch (ExceptionInterface $e) { throw new ResponseException(ResponseException::ACTION_RESULT_MALFORMED, $request, $options, $response, $transferInfo, $e); } try { $this->stateTracker->setResult($site, $responseData['stateResult']); } catch (\Exception $e) { throw new ResponseException(ResponseException::STATE_MALFORMED, $request, $options, $response, $transferInfo, $e); } return $reaction; }, function (RequestException $e) use($options, &$transferInfo) { throw new NetworkException($e->getHandlerContext()['errno'], $e->getRequest(), $options, $e->getResponse(), $transferInfo); })->otherwise(function (\Exception $exception) use($site) { $this->stateTracker->setException($site, $exception); throw $exception; }); }