<commit_before><?php
namespace TYPO3\SingleSignOn\Client\Domain\Model;

/*                                                                        *
 * This script belongs to the TYPO3 Flow package "TYPO3.SingleSignOn.Client".*
 *                                                                        *
 *                                                                        */

use TYPO3\Flow\Annotations as Flow;
use Doctrine\ORM\Mapping as ORM;
use \TYPO3\Flow\Http\Uri;

use \TYPO3\SingleSignOn\Client\Exception;
use \TYPO3\SingleSignOn\Client\Domain\Model\SsoClient;

/**
 * SSO server
 *
 * Will be configured using settings.
 */
class SsoServer {

	/**
	 * The public key
	 * @var string
	 */
	protected $publicKey;

	/**
	 * The SSO server endpoint URI
	 * @var string
	 */
	protected $endpointUri;

	/**
	 * The SSO server service base URI
	 * @var string
	 */
	protected $serviceBaseUri;

	/**
	 * @Flow\Inject
	 * @var \TYPO3\Flow\Security\Cryptography\RsaWalletServiceInterface
	 */
	protected $rsaWalletService;

	/**
	 * @Flow\Inject
	 * @var \TYPO3\Flow\Http\Client\CurlEngine
	 */
	protected $requestEngine;

	/**
	 * @Flow\Inject
	 * @var \TYPO3\SingleSignOn\Client\Security\RequestSigner
	 */
	protected $requestSigner;

	/**
	 * Build a URI to redirect to the server authentication endpoint
	 *
	 * @param \TYPO3\SingleSignOn\Client\Domain\Model\SsoClient $ssoClient The SSO client that wants to authenticate against the server
	 * @param string $callbackUri A URI where the server should redirect back after successful authentication on the server
	 * @return string The URI for the redirect
	 */
	public function buildAuthenticationEndpointUri(SsoClient $ssoClient, $callbackUri) {
		$uri = new Uri($this->endpointUri);
		$arguments = array(
			'callbackUri' => (string)$callbackUri,
			'ssoClientIdentifier' => $ssoClient->getServiceBaseUri()
		);
		ksort($arguments);
		$uri->setQuery(http_build_query($arguments));

		$signature = $this->rsaWalletService->sign((string)$uri, $ssoClient->getKeyPairUuid());
		$arguments['signature'] = base64_encode($signature);
		$uri->setQuery(http_build_query($arguments));

		return (string)$uri;
	}

	/**
	 * Verify the signature of a callback redirect to the client
	 *
	 * @param string $accessTokenCipher
	 * @param string $signature
	 * @return boolean
	 */
	public function verifyCallbackSignature($accessTokenCipher, $signature) {
		return $this->rsaWalletService->verifySignature($accessTokenCipher, $signature, $this->publicKey);
	}

	/**
	 * @param \TYPO3\SingleSignOn\Client\Domain\Model\SsoClient $ssoClient
	 * @param string $accessToken
	 * @param string $clientSessionId
	 * @return array
	 */
	public function redeemAccessToken(SsoClient $ssoClient, $accessToken, $clientSessionId = NULL) {
		$serviceUri = new Uri($this->serviceBaseUri . '/token/' . urlencode($accessToken) . '/redeem');
		$request = \TYPO3\Flow\Http\Request::create($serviceUri, 'POST');
		$request->setHeader('Accept', 'application/json');
		if ($clientSessionId !== NULL) {
			$request->setContent(http_build_query(array('clientSessionId' => $clientSessionId)));
		}

		$signedRequest = $this->requestSigner->signRequest($request, $ssoClient->getKeyPairUuid(), $ssoClient->getKeyPairUuid());

		$response = $this->requestEngine->sendRequest($signedRequest);
		if ($response->getStatusCode() !== 201) {
			throw new Exception('Unexpected status code for redeem access token when calling "' . (string)$serviceUri . '": "' . $response->getStatus() . '"', 1352754575);
		}

		if ($response->getHeader('Content-Type') !== 'application/json') {
			throw new Exception('Unexpected content type for redeem access token when calling "' . (string)$serviceUri . '": "' . $response->getHeader('Content-Type') . '", expected "application/json"', 1352994795);
		}

		$authenticationData = json_decode($response->getContent(), TRUE);
		if ($authenticationData === NULL) {
			throw new Exception('Could not decode JSON response: Error ' . json_last_error(), 1352994936);
		}

		// TODO Validate content of authentication data

		return $authenticationData;
	}

	/**
	 * Touch a global session
	 *
	 * This method is thought to work asynchronously to not block when the
	 * server is responding slow.
	 *
	 * @param \TYPO3\SingleSignOn\Client\Domain\Model\SsoClient $ssoClient
	 * @param string $sessionId
	 * @return void
	 * @throws \TYPO3\SingleSignOn\Client\Exception\SessionNotFoundException
	 */
	public function touchSession(SsoClient $ssoClient, $sessionId) {
		$serviceUri = $this->serviceBaseUri . '/session/' . urlencode($sessionId) . '/touch';
		$request = \TYPO3\Flow\Http\Request::create(new Uri($serviceUri), 'POST');

		$signedRequest = $this->requestSigner->signRequest($request, $ssoClient->getKeyPairUuid(), $ssoClient->getKeyPairUuid());

		// TODO Handle timeout and other server errors (client should keep running!)
		$response = $this->requestEngine->sendRequest($signedRequest);
		if ($response->getStatusCode() === 404 && $response->getHeader('Content-Type') === 'application/json') {
			$data = json_decode($response->getContent(), TRUE);
			if (is_array($data) && isset($data['error']) && $data['error'] === 'SessionNotFound') {
				throw new \TYPO3\SingleSignOn\Client\Exception\SessionNotFoundException();
			}
		}
		if ($response->getStatusCode() !== 200) {
			throw new Exception('Unexpected status code for touch session when calling "' . (string)$serviceUri . '": "' . $response->getStatus() . '"', 1354030063);
		}
	}

	/**
	 * Destroy the given global session
	 *
	 * @param \TYPO3\SingleSignOn\Client\Domain\Model\SsoClient $ssoClient
	 * @param $sessionId
	 * @return void
	 */
	public function destroySession(SsoClient $ssoClient, $sessionId) {
		$serviceUri = $this->serviceBaseUri . '/session/' . urlencode($sessionId) . '/destroy';
		$request = \TYPO3\Flow\Http\Request::create(new Uri($serviceUri), 'DELETE');

		$signedRequest = $this->requestSigner->signRequest($request, $ssoClient->getKeyPairUuid(), $ssoClient->getKeyPairUuid());

		// TODO Send request asynchronously
		$response = $this->requestEngine->sendRequest($signedRequest);
		if ($response->getStatusCode() !== 200 && $response->getStatusCode() !== 404) {
			throw new Exception('Unexpected status code for destroy session when calling "' . (string)$serviceUri . '": "' . $response->getStatus() . '"', 1354132939);
		}
	}

	/**
	 * Get the Sso server's public key
	 *
	 * @return string The Sso server's public key
	 */
	public function getPublicKey() {<commit_msg>[BUGFIX] Fix wrong Uri assignment<commit_after><?php
namespace TYPO3\SingleSignOn\Client\Domain\Model;

/*                                                                        *
 * This script belongs to the TYPO3 Flow package "TYPO3.SingleSignOn.Client".*
 *                                                                        *
 *                                                                        */

use TYPO3\Flow\Annotations as Flow;
use Doctrine\ORM\Mapping as ORM;
use \TYPO3\Flow\Http\Uri;

use \TYPO3\SingleSignOn\Client\Exception;
use \TYPO3\SingleSignOn\Client\Domain\Model\SsoClient;

/**
 * SSO server
 *
 * Will be configured using settings.
 */
class SsoServer {

	/**
	 * The public key
	 * @var string
	 */
	protected $publicKey;

	/**
	 * The SSO server endpoint URI
	 * @var string
	 */
	protected $endpointUri;

	/**
	 * The SSO server service base URI
	 * @var string
	 */
	protected $serviceBaseUri;

	/**
	 * @Flow\Inject
	 * @var \TYPO3\Flow\Security\Cryptography\RsaWalletServiceInterface
	 */
	protected $rsaWalletService;

	/**
	 * @Flow\Inject
	 * @var \TYPO3\Flow\Http\Client\CurlEngine
	 */
	protected $requestEngine;

	/**
	 * @Flow\Inject
	 * @var \TYPO3\SingleSignOn\Client\Security\RequestSigner
	 */
	protected $requestSigner;

	/**
	 * Build a URI to redirect to the server authentication endpoint
	 *
	 * @param \TYPO3\SingleSignOn\Client\Domain\Model\SsoClient $ssoClient The SSO client that wants to authenticate against the server
	 * @param string $callbackUri A URI where the server should redirect back after successful authentication on the server
	 * @return string The URI for the redirect
	 */
	public function buildAuthenticationEndpointUri(SsoClient $ssoClient, $callbackUri) {
		$uri = new Uri($this->endpointUri);
		$arguments = array(
			'callbackUri' => (string)$callbackUri,
			'ssoClientIdentifier' => $ssoClient->getServiceBaseUri()
		);
		ksort($arguments);
		$uri->setQuery(http_build_query($arguments));

		$signature = $this->rsaWalletService->sign((string)$uri, $ssoClient->getKeyPairUuid());
		$arguments['signature'] = base64_encode($signature);
		$uri->setQuery(http_build_query($arguments));

		return (string)$uri;
	}

	/**
	 * Verify the signature of a callback redirect to the client
	 *
	 * @param string $accessTokenCipher
	 * @param string $signature
	 * @return boolean
	 */
	public function verifyCallbackSignature($accessTokenCipher, $signature) {
		return $this->rsaWalletService->verifySignature($accessTokenCipher, $signature, $this->publicKey);
	}

	/**
	 * @param \TYPO3\SingleSignOn\Client\Domain\Model\SsoClient $ssoClient
	 * @param string $accessToken
	 * @param string $clientSessionId
	 * @return array
	 */
	public function redeemAccessToken(SsoClient $ssoClient, $accessToken, $clientSessionId = NULL) {
		$serviceUri = new Uri($this->serviceBaseUri . '/token/' . urlencode($accessToken) . '/redeem');
		$request = \TYPO3\Flow\Http\Request::create($serviceUri, 'POST');
		$request->setHeader('Accept', 'application/json');
		if ($clientSessionId !== NULL) {
			$request->setContent(http_build_query(array('clientSessionId' => $clientSessionId)));
		}

		$signedRequest = $this->requestSigner->signRequest($request, $ssoClient->getKeyPairUuid(), $ssoClient->getKeyPairUuid());

		$response = $this->requestEngine->sendRequest($signedRequest);
		if ($response->getStatusCode() !== 201) {
			throw new Exception('Unexpected status code for redeem access token when calling "' . (string)$serviceUri . '": "' . $response->getStatus() . '"', 1352754575);
		}

		if ($response->getHeader('Content-Type') !== 'application/json') {
			throw new Exception('Unexpected content type for redeem access token when calling "' . (string)$serviceUri . '": "' . $response->getHeader('Content-Type') . '", expected "application/json"', 1352994795);
		}

		$authenticationData = json_decode($response->getContent(), TRUE);
		if ($authenticationData === NULL) {
			throw new Exception('Could not decode JSON response: Error ' . json_last_error(), 1352994936);
		}

		// TODO Validate content of authentication data

		return $authenticationData;
	}

	/**
	 * Touch a global session
	 *
	 * This method is thought to work asynchronously to not block when the
	 * server is responding slow.
	 *
	 * @param \TYPO3\SingleSignOn\Client\Domain\Model\SsoClient $ssoClient
	 * @param string $sessionId
	 * @return void
	 * @throws \TYPO3\SingleSignOn\Client\Exception\SessionNotFoundException
	 */
	public function touchSession(SsoClient $ssoClient, $sessionId) {
		$serviceUri = $this->serviceBaseUri . '/session/' . urlencode($sessionId) . '/touch';
		$request = \TYPO3\Flow\Http\Request::create(new Uri($serviceUri), 'POST');

		$signedRequest = $this->requestSigner->signRequest($request, $ssoClient->getKeyPairUuid(), $ssoClient->getKeyPairUuid());

		// TODO Handle timeout and other server errors (client should keep running!)
		$response = $this->requestEngine->sendRequest($signedRequest);
		if ($response->getStatusCode() === 404 && $response->getHeader('Content-Type') === 'application/json') {
			$data = json_decode($response->getContent(), TRUE);
			if (is_array($data) && isset($data['error']) && $data['error'] === 'SessionNotFound') {
				throw new \TYPO3\SingleSignOn\Client\Exception\SessionNotFoundException();
			}
		}
		if ($response->getStatusCode() !== 200) {
			throw new Exception('Unexpected status code for touch session when calling "' . (string)$serviceUri . '": "' . $response->getStatus() . '"', 1354030063);
		}
	}

	/**
	 * Destroy the given global session
	 *
	 * @param \TYPO3\SingleSignOn\Client\Domain\Model\SsoClient $ssoClient
	 * @param $sessionId
	 * @return void
	 */
	public function destroySession(SsoClient $ssoClient, $sessionId) {
		$serviceUri = new Uri($this->serviceBaseUri . '/session/' . urlencode($sessionId) . '/destroy');
		$serviceUri->setQuery(http_build_query(array('clientIdentifier' => $ssoClient->getServiceBaseUri())));
		$request = \TYPO3\Flow\Http\Request::create($serviceUri, 'DELETE');

		$signedRequest = $this->requestSigner->signRequest($request, $ssoClient->getKeyPairUuid(), $ssoClient->getKeyPairUuid());

		// TODO Send request asynchronously
		$response = $this->requestEngine->sendRequest($signedRequest);
		if ($response->getStatusCode() !== 200 && $response->getStatusCode() !== 404) {
			throw new Exception('Unexpected status code for destroy session when calling "' . (string)$serviceUri . '": "' . $response->getStatus() . '"', 1354132939);
		}
	}

	/**
	 * Get the Sso server's public key
	 *
	 * @return string The Sso server's public key
	 */
	public function getPublicKey() {
{'fraction_non_alphanumeric': 0.14490215487702734, 'fraction_numerical': 0.013096779106068683, 'mean_word_length': 3.8572485207100593, 'pattern_counts': {'":': 8, '<': 5, '<?xml version=': 0, '>': 104, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2766856', 'n_tokens_mistral': 4173, 'n_tokens_neox': 3792, 'n_words': 878}