Repository: fr05t1k/esia
Branch: master
Commit: c1750575b7eb
Files: 42
Total size: 73.0 KB
Directory structure:
gitextract_ro8_8jf5/
├── .gitignore
├── .travis.yml
├── README.md
├── _config.yml
├── codeception.yml
├── composer.json
├── src/
│ └── Esia/
│ ├── Config.php
│ ├── Exceptions/
│ │ ├── AbstractEsiaException.php
│ │ ├── ForbiddenException.php
│ │ ├── InvalidConfigurationException.php
│ │ └── RequestFailException.php
│ ├── Http/
│ │ ├── Exceptions/
│ │ │ └── HttpException.php
│ │ └── GuzzleHttpClient.php
│ ├── OpenId.php
│ └── Signer/
│ ├── AbstractSignerPKCS7.php
│ ├── CliSignerPKCS7.php
│ ├── Exceptions/
│ │ ├── CannotGenerateRandomIntException.php
│ │ ├── CannotReadCertificateException.php
│ │ ├── CannotReadPrivateKeyException.php
│ │ ├── NoSuchCertificateFileException.php
│ │ ├── NoSuchKeyFileException.php
│ │ ├── NoSuchTmpDirException.php
│ │ └── SignFailException.php
│ ├── SignerInterface.php
│ └── SignerPKCS7.php
└── tests/
├── .configure-gost-openssl.sh
├── _bootstrap.php
├── _data/
│ ├── server-gost.crt
│ ├── server-gost.key
│ ├── server.crt
│ ├── server.csr
│ └── server.key
├── _support/
│ ├── Helper/
│ │ └── Unit.php
│ ├── UnitTester.php
│ └── _generated/
│ └── UnitTesterActions.php
├── unit/
│ ├── ConfigTest.php
│ ├── Http/
│ │ └── GuzzleHttpClientTest.php
│ ├── OpenIdCliOpensslTest.php
│ ├── OpenIdTest.php
│ ├── Signer/
│ │ └── SignerPKCS7Test.php
│ └── _bootstrap.php
└── unit.suite.yml
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.idea
tests/tmp/*
tests/_output/*
public/*
vendor
tests/_data/non_readable_file
================================================
FILE: .travis.yml
================================================
dist: bionic
language: php
addons:
apt:
packages:
- libengine-gost-openssl1.1
before_install:
- sudo bash tests/.configure-gost-openssl.sh
php:
- 7.1
- 7.2
- 7.3
- 7.4
- 8.0
install:
- travis_retry composer self-update
- travis_retry composer --version
- travis_retry composer update --prefer-dist --no-interaction
script:
- chmod 000 tests/_data/non_readable_file
- php vendor/codeception/codeception/codecept run
================================================
FILE: README.md
================================================
# Единая система идентификации и аутентификации (ЕСИА) OpenId
[](https://travis-ci.org/fr05t1k/esia)
# Описание
Компонент для авторизации на портале "Госуслуги".
# Внимание!
Получив токен вы можете выполнять любые API запросы. Библиотека не поддерживает все существующие методы в API, а предоставляет только самые базовые. Основная цель библиотеки - получение токена.
# Установка
При помощи [composer](https://getcomposer.org/download/):
```
composer require --prefer-dist fr05t1k/esia
```
Или добавьте в composer.json
```
"fr05t1k/esia" : "^2.0"
```
# Как использовать
Пример получения ссылки для авторизации
```php
<?php
$config = new \Esia\Config([
'clientId' => 'INSP03211',
'redirectUrl' => 'http://my-site.com/response.php',
'portalUrl' => 'https://esia-portal1.test.gosuslugi.ru/',
'scope' => ['fullname', 'birthdate'],
]);
$esia = new \Esia\OpenId($config);
$esia->setSigner(new \Esia\Signer\SignerPKCS7(
'my-site.com.pem',
'my-site.com.pem',
'password',
'/tmp'
));
?>
<a href="<?=$esia->buildUrl()?>">Войти через портал госуслуги</a>
```
После редиректа на ваш `redirectUrl` вы получите в `$_GET['code']` код для получения токена
Пример получения токена и информации о пользователе
```php
$esia = new \Esia\OpenId($config);
// Вы можете использовать токен в дальнейшем вместе с oid
$token = $esia->getToken($_GET['code']);
$personInfo = $esia->getPersonInfo();
$addressInfo = $esia->getAddressInfo();
$contactInfo = $esia->getContactInfo();
$documentInfo = $esia->getDocInfo();
```
# Конфиг
`clientId` - ID вашего приложения.
`redirectUrl` - URL куда будет перенаправлен ответ с кодом.
`portalUrl` - по умолчанию: `https://esia-portal1.test.gosuslugi.ru/`. Домен портала для авторизация (только домен).
`codeUrlPath` - по умолчанию: `aas/oauth2/ac`. URL для получения кода.
`tokenUrlPath` - по умолчанию: `aas/oauth2/te`. URL для получение токена.
`scope` - по умолчанию: `fullname birthdate gender email mobile id_doc snils inn`. Запрашиваемые права у пользователя.
`privateKeyPath` - путь до приватного ключа.
`privateKeyPassword` - пароль от приватного ключа.
`certPath` - путь до сертификата.
`tmpPath` - путь до дериктории где будет проходить подпись (должна быть доступна для записи).
# Токен и oid
Токен - jwt токен которые вы получаете от ЕСИА для дальнейшего взаимодействия
oid - уникальный идентификатор владельца токена
## Как получить oid?
Если 2 способа:
1. oid содержится в jwt токене, расшифровав его
2. После получения токена oid сохраняется в config и получить можно так
```php
$esia->getConfig()->getOid();
```
## Переиспользование Токена
Дополнительно укажите токен и идентификатор в конфиге
```php
$config->setToken($jwt);
$config->setOid($oid);
```
================================================
FILE: _config.yml
================================================
theme: jekyll-theme-cayman
================================================
FILE: codeception.yml
================================================
actor: Tester
paths:
tests: tests
log: tests/_output
data: tests/_data
support: tests/_support
envs: tests/_envs
bootstrap: _bootstrap.php
settings:
colors: true
memory_limit: 1024M
extensions:
enabled:
- Codeception\Extension\RunFailed
coverage:
enabled: true
remote: false
whitelist:
include:
- src/*
================================================
FILE: composer.json
================================================
{
"name": "fr05t1k/esia",
"license": "MIT",
"description": "OpenID ESIA authenticating",
"keywords": [
"esia",
"openid",
"egov"
],
"autoload": {
"psr-4": {
"Esia\\": "src/Esia"
}
},
"autoload-dev": {
"psr-4": {
"tests\\" : "tests"
}
},
"require": {
"php": "^7.1|^8.0",
"guzzlehttp/guzzle": "^6.1.0|^7.0",
"psr/log": "^1.0",
"psr/http-message": "^1.0",
"psr/http-client": "^1.0"
},
"suggest": {
"ext-openssl": "SignerPKCS7 support"
},
"require-dev": {
"roave/security-advisories": "dev-latest",
"codeception/codeception": "^4.0",
"codeception/module-asserts": "^1.3"
}
}
================================================
FILE: src/Esia/Config.php
================================================
<?php
namespace Esia;
use Esia\Exceptions\InvalidConfigurationException;
class Config
{
private $clientId;
private $redirectUrl;
private $privateKeyPath;
private $certPath;
private $portalUrl = 'http://esia-portal1.test.gosuslugi.ru/';
private $tokenUrlPath = 'aas/oauth2/te';
private $codeUrlPath = 'aas/oauth2/ac';
private $personUrlPath = 'rs/prns';
private $logoutUrlPath = 'idp/ext/Logout';
private $privateKeyPassword = '';
private $scope = [
'fullname',
'birthdate',
'gender',
'email',
'mobile',
'id_doc',
'snils',
'inn',
];
private $tmpPath = '/var/tmp';
private $responseType = 'code';
private $accessType = 'offline';
private $token = '';
private $oid = '';
/**
* Config constructor.
*
* @throws InvalidConfigurationException
*/
public function __construct(array $config = [])
{
// Required params
$this->clientId = $config['clientId'] ?? $this->clientId;
if (!$this->clientId) {
throw new InvalidConfigurationException('Please provide clientId');
}
$this->redirectUrl = $config['redirectUrl'] ?? $this->redirectUrl;
if (!$this->redirectUrl) {
throw new InvalidConfigurationException('Please provide redirectUrl');
}
$this->privateKeyPath = $config['privateKeyPath'] ?? $this->privateKeyPath;
if (!$this->privateKeyPath) {
throw new InvalidConfigurationException('Please provide privateKeyPath');
}
$this->certPath = $config['certPath'] ?? $this->certPath;
if (!$this->certPath) {
throw new InvalidConfigurationException('Please provide certPath');
}
$this->portalUrl = $config['portalUrl'] ?? $this->portalUrl;
$this->tokenUrlPath = $config['tokenUrlPath'] ?? $this->tokenUrlPath;
$this->codeUrlPath = $config['codeUrlPath'] ?? $this->codeUrlPath;
$this->personUrlPath = $config['personUrlPath'] ?? $this->personUrlPath;
$this->logoutUrlPath = $config['logoutUrlPath'] ?? $this->logoutUrlPath;
$this->privateKeyPassword = $config['privateKeyPassword'] ?? $this->privateKeyPassword;
$this->oid = $config['oid'] ?? $this->oid;
$this->scope = $config['scope'] ?? $this->scope;
if (!is_array($this->scope)) {
throw new InvalidConfigurationException('scope must be array of strings');
}
$this->responseType = $config['responseType'] ?? $this->responseType;
$this->accessType = $config['accessType'] ?? $this->accessType;
$this->tmpPath = $config['tmpPath'] ?? $this->tmpPath;
$this->token = $config['token'] ?? $this->token;
}
public function getPortalUrl(): string
{
return $this->portalUrl;
}
public function getPrivateKeyPath(): string
{
return $this->privateKeyPath;
}
public function getPrivateKeyPassword(): string
{
return $this->privateKeyPassword;
}
public function getCertPath(): string
{
return $this->certPath;
}
public function getOid(): string
{
return $this->oid;
}
public function setOid(string $oid): void
{
$this->oid = $oid;
}
public function getScope(): array
{
return $this->scope;
}
public function getScopeString(): string
{
return implode(' ', $this->scope);
}
public function getResponseType(): string
{
return $this->responseType;
}
public function getAccessType(): string
{
return $this->accessType;
}
public function getTmpPath(): string
{
return $this->tmpPath;
}
public function getToken(): ?string
{
return $this->token;
}
public function setToken(string $token): void
{
$this->token = $token;
}
public function getClientId(): string
{
return $this->clientId;
}
public function getRedirectUrl(): string
{
return $this->redirectUrl;
}
/**
* Return an url for request to get an access token
*/
public function getTokenUrl(): string
{
return $this->portalUrl . $this->tokenUrlPath;
}
/**
* Return an url for request to get an authorization code
*/
public function getCodeUrl(): string
{
return $this->portalUrl . $this->codeUrlPath;
}
/**
* @return string
* @throws InvalidConfigurationException
*/
public function getPersonUrl(): string
{
if (!$this->oid) {
throw new InvalidConfigurationException('Please provide oid');
}
return $this->portalUrl . $this->personUrlPath . '/' . $this->oid;
}
/**
* Return an url for logout
*/
public function getLogoutUrl(): string
{
return $this->portalUrl . $this->logoutUrlPath;
}
}
================================================
FILE: src/Esia/Exceptions/AbstractEsiaException.php
================================================
<?php
namespace Esia\Exceptions;
use Exception;
abstract class AbstractEsiaException extends Exception
{
}
================================================
FILE: src/Esia/Exceptions/ForbiddenException.php
================================================
<?php
namespace Esia\Exceptions;
class ForbiddenException extends AbstractEsiaException
{
protected function getMessageForCode(int $code): string
{
return 'Forbidden';
}
}
================================================
FILE: src/Esia/Exceptions/InvalidConfigurationException.php
================================================
<?php
namespace Esia\Exceptions;
class InvalidConfigurationException extends AbstractEsiaException
{
}
================================================
FILE: src/Esia/Exceptions/RequestFailException.php
================================================
<?php
namespace Esia\Exceptions;
class RequestFailException extends AbstractEsiaException
{
}
================================================
FILE: src/Esia/Http/Exceptions/HttpException.php
================================================
<?php
namespace Esia\Http\Exceptions;
use Psr\Http\Client\ClientExceptionInterface;
use RuntimeException;
class HttpException extends RuntimeException implements ClientExceptionInterface
{
}
================================================
FILE: src/Esia/Http/GuzzleHttpClient.php
================================================
<?php
namespace Esia\Http;
use Esia\Http\Exceptions\HttpException;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
class GuzzleHttpClient implements ClientInterface
{
/**
* @var Client
*/
private $guzzle;
/**
* GuzzleHttpClient constructor.
*/
public function __construct(Client $guzzle)
{
$this->guzzle = $guzzle;
}
/**
* Sends a PSR-7 request and returns a PSR-7 response.
*
* Every technically correct HTTP response MUST be returned as is, even if it represents a HTTP
* error response or a redirect instruction. The only special case is 1xx responses, which MUST
* be assembled in the HTTP client.
*
* The client MAY do modifications to the Request before sending it. Because PSR-7 objects are
* immutable, one cannot assume that the object passed to ClientInterface::sendRequest() will be the same
* object that is actually sent. For example the Request object that is returned by an exception MAY
* be a different object than the one passed to sendRequest, so comparison by reference (===) is not possible.
*
* {@link https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-7-http-message-meta.md#why-value-objects}
*
* @param RequestInterface $request
*
* @return ResponseInterface
*
* @throws ClientExceptionInterface If an error happens during processing the request.
*/
public function sendRequest(RequestInterface $request): ResponseInterface
{
try {
return $this->guzzle->send($request);
} catch (GuzzleException $e) {
throw new HttpException($e->getMessage(), $e->getCode(), $e);
}
}
}
================================================
FILE: src/Esia/OpenId.php
================================================
<?php
namespace Esia;
use Esia\Exceptions\AbstractEsiaException;
use Esia\Exceptions\ForbiddenException;
use Esia\Exceptions\RequestFailException;
use Esia\Http\GuzzleHttpClient;
use Esia\Signer\Exceptions\CannotGenerateRandomIntException;
use Esia\Signer\Exceptions\SignFailException;
use Esia\Signer\SignerInterface;
use Esia\Signer\SignerPKCS7;
use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Psr7\Request;
use InvalidArgumentException;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\NullLogger;
use RuntimeException;
/**
* Class OpenId
*/
class OpenId
{
use LoggerAwareTrait;
/**
* @var SignerInterface
*/
private $signer;
/**
* Http Client
*
* @var ClientInterface
*/
private $client;
/**
* Config
*
* @var Config
*/
private $config;
public function __construct(Config $config, ClientInterface $client = null)
{
$this->config = $config;
$this->client = $client ?? new GuzzleHttpClient(new Client());
$this->logger = new NullLogger();
$this->signer = new SignerPKCS7(
$config->getCertPath(),
$config->getPrivateKeyPath(),
$config->getPrivateKeyPassword(),
$config->getTmpPath()
);
}
/**
* Replace default signer
*/
public function setSigner(SignerInterface $signer): void
{
$this->signer = $signer;
}
/**
* Get config
*/
public function getConfig(): Config
{
return $this->config;
}
/**
* Return an url for authentication
*
* ```php
* <a href="<?=$esia->buildUrl()?>">Login</a>
* ```
*
* @return string|false
* @throws SignFailException
*/
public function buildUrl()
{
$timestamp = $this->getTimeStamp();
$state = $this->buildState();
$message = $this->config->getScopeString()
. $timestamp
. $this->config->getClientId()
. $state;
$clientSecret = $this->signer->sign($message);
$url = $this->config->getCodeUrl() . '?%s';
$params = [
'client_id' => $this->config->getClientId(),
'client_secret' => $clientSecret,
'redirect_uri' => $this->config->getRedirectUrl(),
'scope' => $this->config->getScopeString(),
'response_type' => $this->config->getResponseType(),
'state' => $state,
'access_type' => $this->config->getAccessType(),
'timestamp' => $timestamp,
];
$request = http_build_query($params);
return sprintf($url, $request);
}
/**
* Return an url for logout
*/
public function buildLogoutUrl(string $redirectUrl = null): string
{
$url = $this->config->getLogoutUrl() . '?%s';
$params = [
'client_id' => $this->config->getClientId(),
];
if ($redirectUrl) {
$params['redirect_url'] = $redirectUrl;
}
$request = http_build_query($params);
return sprintf($url, $request);
}
/**
* Method collect a token with given code
*
* @throws SignFailException
* @throws AbstractEsiaException
*/
public function getToken(string $code): string
{
$timestamp = $this->getTimeStamp();
$state = $this->buildState();
$clientSecret = $this->signer->sign(
$this->config->getScopeString()
. $timestamp
. $this->config->getClientId()
. $state
);
$body = [
'client_id' => $this->config->getClientId(),
'code' => $code,
'grant_type' => 'authorization_code',
'client_secret' => $clientSecret,
'state' => $state,
'redirect_uri' => $this->config->getRedirectUrl(),
'scope' => $this->config->getScopeString(),
'timestamp' => $timestamp,
'token_type' => 'Bearer',
'refresh_token' => $state,
];
$payload = $this->sendRequest(
new Request(
'POST',
$this->config->getTokenUrl(),
[
'Content-Type' => 'application/x-www-form-urlencoded',
],
http_build_query($body)
)
);
$this->logger->debug('Payload: ', $payload);
$token = $payload['access_token'];
$this->config->setToken($token);
# get object id from token
$chunks = explode('.', $token);
$payload = json_decode($this->base64UrlSafeDecode($chunks[1]), true);
$this->config->setOid($payload['urn:esia:sbj_id']);
return $token;
}
/**
* Fetch person info from current person
*
* You must collect token person before
* calling this method
*
* @throws AbstractEsiaException
*/
public function getPersonInfo(): array
{
$url = $this->config->getPersonUrl();
return $this->sendRequest(new Request('GET', $url));
}
/**
* Fetch contact info about current person
*
* You must collect token person before
* calling this method
*
* @throws Exceptions\InvalidConfigurationException
* @throws AbstractEsiaException
*/
public function getContactInfo(): array
{
$url = $this->config->getPersonUrl() . '/ctts';
$payload = $this->sendRequest(new Request('GET', $url));
if ($payload && $payload['size'] > 0) {
return $this->collectArrayElements($payload['elements']);
}
return $payload;
}
/**
* Fetch address from current person
*
* You must collect token person before
* calling this method
*
* @throws Exceptions\InvalidConfigurationException
* @throws AbstractEsiaException
*/
public function getAddressInfo(): array
{
$url = $this->config->getPersonUrl() . '/addrs';
$payload = $this->sendRequest(new Request('GET', $url));
if ($payload['size'] > 0) {
return $this->collectArrayElements($payload['elements']);
}
return $payload;
}
/**
* Fetch documents info about current person
*
* You must collect token person before
* calling this method
*
* @throws Exceptions\InvalidConfigurationException
* @throws AbstractEsiaException
*/
public function getDocInfo(): array
{
$url = $this->config->getPersonUrl() . '/docs';
$payload = $this->sendRequest(new Request('GET', $url));
if ($payload && $payload['size'] > 0) {
return $this->collectArrayElements($payload['elements']);
}
return $payload;
}
/**
* This method can iterate on each element
* and fetch entities from esia by url
*
* @throws AbstractEsiaException
*/
private function collectArrayElements($elements): array
{
$result = [];
foreach ($elements as $elementUrl) {
$elementPayload = $this->sendRequest(new Request('GET', $elementUrl));
if ($elementPayload) {
$result[] = $elementPayload;
}
}
return $result;
}
/**
* @throws AbstractEsiaException
*/
private function sendRequest(RequestInterface $request): array
{
try {
if ($this->config->getToken()) {
/** @noinspection CallableParameterUseCaseInTypeContextInspection */
$request = $request->withHeader('Authorization', 'Bearer ' . $this->config->getToken());
}
$response = $this->client->sendRequest($request);
$responseBody = json_decode($response->getBody()->getContents(), true);
if (!is_array($responseBody)) {
throw new RuntimeException(
sprintf(
'Cannot decode response body. JSON error (%d): %s',
json_last_error(),
json_last_error_msg()
)
);
}
return $responseBody;
} catch (ClientExceptionInterface $e) {
$this->logger->error('Request was failed', ['exception' => $e]);
$prev = $e->getPrevious();
// Only for Guzzle
if ($prev instanceof BadResponseException
&& $prev->getResponse() !== null
&& $prev->getResponse()->getStatusCode() === 403
) {
throw new ForbiddenException('Request is forbidden', 0, $e);
}
throw new RequestFailException('Request is failed', 0, $e);
} catch (RuntimeException $e) {
$this->logger->error('Cannot read body', ['exception' => $e]);
throw new RequestFailException('Cannot read body', 0, $e);
} catch (InvalidArgumentException $e) {
$this->logger->error('Wrong header', ['exception' => $e]);
throw new RequestFailException('Wrong header', 0, $e);
}
}
private function getTimeStamp(): string
{
return date('Y.m.d H:i:s O');
}
/**
* Generate state with uuid
*
* @throws SignFailException
*/
private function buildState(): string
{
try {
return sprintf(
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
random_int(0, 0xffff),
random_int(0, 0xffff),
random_int(0, 0xffff),
random_int(0, 0x0fff) | 0x4000,
random_int(0, 0x3fff) | 0x8000,
random_int(0, 0xffff),
random_int(0, 0xffff),
random_int(0, 0xffff)
);
} catch (Exception $e) {
throw new CannotGenerateRandomIntException('Cannot generate random integer', $e);
}
}
/**
* Url safe for base64
*/
private function base64UrlSafeDecode(string $string): string
{
$base64 = strtr($string, '-_', '+/');
return base64_decode($base64);
}
}
================================================
FILE: src/Esia/Signer/AbstractSignerPKCS7.php
================================================
<?php
namespace Esia\Signer;
use Esia\Signer\Exceptions\CannotReadCertificateException;
use Esia\Signer\Exceptions\CannotReadPrivateKeyException;
use Esia\Signer\Exceptions\NoSuchCertificateFileException;
use Esia\Signer\Exceptions\NoSuchKeyFileException;
use Esia\Signer\Exceptions\NoSuchTmpDirException;
use Esia\Signer\Exceptions\SignFailException;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\NullLogger;
abstract class AbstractSignerPKCS7
{
use LoggerAwareTrait;
/**
* Path to the certificate
*
* @var string
*/
protected $certPath;
/**
* Path to the private key
*
* @var string
*/
protected $privateKeyPath;
/**
* Password for the private key
*
* @var string
*/
protected $privateKeyPassword;
/**
* SignerPKCS7 constructor.
*/
public function __construct(
string $certPath,
string $privateKeyPath,
?string $privateKeyPassword,
string $tmpPath
) {
$this->certPath = $certPath;
$this->privateKeyPath = $privateKeyPath;
$this->privateKeyPassword = $privateKeyPassword;
$this->tmpPath = $tmpPath;
$this->logger = new NullLogger();
}
/**
* Temporary directory for message signing (must me writable)
*
* @var string
*/
protected $tmpPath;
/**
* @throws SignFailException
*/
protected function checkFilesExists(): void
{
if (!file_exists($this->certPath)) {
throw new NoSuchCertificateFileException('Certificate does not exist');
}
if (!is_readable($this->certPath)) {
throw new CannotReadCertificateException('Cannot read the certificate');
}
if (!file_exists($this->privateKeyPath)) {
throw new NoSuchKeyFileException('Private key does not exist');
}
if (!is_readable($this->privateKeyPath)) {
throw new CannotReadPrivateKeyException('Cannot read the private key');
}
if (!file_exists($this->tmpPath)) {
throw new NoSuchTmpDirException('Temporary folder is not found');
}
if (!is_writable($this->tmpPath)) {
throw new NoSuchTmpDirException('Temporary folder is not writable');
}
}
/**
* Generate random unique string
*/
protected function getRandomString(): string
{
return md5(uniqid(mt_rand(), true));
}
/**
* Url safe for base64
*/
protected function urlSafe(string $string): string
{
return rtrim(strtr(trim($string), '+/', '-_'), '=');
}
}
================================================
FILE: src/Esia/Signer/CliSignerPKCS7.php
================================================
<?php
namespace Esia\Signer;
use Esia\Signer\Exceptions\SignFailException;
class CliSignerPKCS7 extends AbstractSignerPKCS7 implements SignerInterface
{
/**
* @throws SignFailException
*/
public function sign(string $message): string
{
$this->checkFilesExists();
// random unique directories for sign
$messageFile = $this->tmpPath . DIRECTORY_SEPARATOR . $this->getRandomString();
$signFile = $this->tmpPath . DIRECTORY_SEPARATOR . $this->getRandomString();
file_put_contents($messageFile, $message);
$this->run(
'openssl ' .
'smime -engine gost -sign -binary -outform DER -noattr ' .
'-signer ' . escapeshellarg($this->certPath) . ' ' .
'-inkey ' . escapeshellarg($this->privateKeyPath) . ' ' .
'-passin ' . escapeshellarg('pass:' . $this->privateKeyPassword) . ' ' .
'-in ' . escapeshellarg($messageFile) . ' ' .
'-out ' . escapeshellarg($signFile)
);
$signed = file_get_contents($signFile);
if ($signed === false) {
$message = sprintf('cannot read %s file', $signFile);
$this->logger->error($message);
throw new SignFailException($message);
}
$sign = $this->urlSafe(base64_encode($signed));
unlink($signFile);
unlink($messageFile);
return $sign;
}
/**
* @throws SignFailException
*/
private function run(string $command): void
{
$process = proc_open(
$command,
[
['pipe', 'w'], // stdout
['pipe', 'w'], // stderr
],
$pipes
);
$result = stream_get_contents($pipes[0]);
fclose($pipes[0]);
$errors = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$code = proc_close($process);
if (0 !== $code || $result === false) {
$errors = $errors ?: 'unknown';
$this->logger->error('Sign fail');
$this->logger->error('SSL error: ' . $errors);
throw new SignFailException($errors);
}
}
}
================================================
FILE: src/Esia/Signer/Exceptions/CannotGenerateRandomIntException.php
================================================
<?php
namespace Esia\Signer\Exceptions;
class CannotGenerateRandomIntException extends SignFailException
{
}
================================================
FILE: src/Esia/Signer/Exceptions/CannotReadCertificateException.php
================================================
<?php
namespace Esia\Signer\Exceptions;
class CannotReadCertificateException extends SignFailException
{
}
================================================
FILE: src/Esia/Signer/Exceptions/CannotReadPrivateKeyException.php
================================================
<?php
namespace Esia\Signer\Exceptions;
class CannotReadPrivateKeyException extends SignFailException
{
}
================================================
FILE: src/Esia/Signer/Exceptions/NoSuchCertificateFileException.php
================================================
<?php
namespace Esia\Signer\Exceptions;
class NoSuchCertificateFileException extends SignFailException
{
}
================================================
FILE: src/Esia/Signer/Exceptions/NoSuchKeyFileException.php
================================================
<?php
namespace Esia\Signer\Exceptions;
class NoSuchKeyFileException extends SignFailException
{
}
================================================
FILE: src/Esia/Signer/Exceptions/NoSuchTmpDirException.php
================================================
<?php
namespace Esia\Signer\Exceptions;
class NoSuchTmpDirException extends SignFailException
{
}
================================================
FILE: src/Esia/Signer/Exceptions/SignFailException.php
================================================
<?php
namespace Esia\Signer\Exceptions;
use Esia\Exceptions\AbstractEsiaException;
class SignFailException extends AbstractEsiaException
{
protected function getMessageForCode(int $code): string
{
return 'Signing is failed';
}
}
================================================
FILE: src/Esia/Signer/SignerInterface.php
================================================
<?php
namespace Esia\Signer;
use Esia\Signer\Exceptions\SignFailException;
interface SignerInterface
{
/**
* @throws SignFailException
*/
public function sign(string $message): string;
}
================================================
FILE: src/Esia/Signer/SignerPKCS7.php
================================================
<?php
namespace Esia\Signer;
use Esia\Signer\Exceptions\CannotReadCertificateException;
use Esia\Signer\Exceptions\CannotReadPrivateKeyException;
use Esia\Signer\Exceptions\SignFailException;
class SignerPKCS7 extends AbstractSignerPKCS7 implements SignerInterface
{
private $pkcs7Flags = PKCS7_DETACHED;
public function addPKCS7Flag(int $pkcs7Flag): void
{
$this->pkcs7Flags |= $pkcs7Flag;
}
/**
* @throws SignFailException
*/
public function sign(string $message): string
{
$this->checkFilesExists();
$certContent = file_get_contents($this->certPath);
$keyContent = file_get_contents($this->privateKeyPath);
$cert = openssl_x509_read($certContent);
if ($cert === false) {
throw new CannotReadCertificateException('Cannot read the certificate: ' . openssl_error_string());
}
$this->logger->debug('Cert: ' . print_r($cert, true), ['cert' => $cert]);
$privateKey = openssl_pkey_get_private($keyContent, $this->privateKeyPassword);
if ($privateKey === false) {
throw new CannotReadPrivateKeyException('Cannot read the private key: ' . openssl_error_string());
}
$this->logger->debug('Private key: : ' . print_r($privateKey, true), ['privateKey' => $privateKey]);
// random unique directories for sign
$messageFile = $this->tmpPath . DIRECTORY_SEPARATOR . $this->getRandomString();
$signFile = $this->tmpPath . DIRECTORY_SEPARATOR . $this->getRandomString();
file_put_contents($messageFile, $message);
$signResult = openssl_pkcs7_sign(
$messageFile,
$signFile,
$cert,
$privateKey,
[],
$this->pkcs7Flags
);
if ($signResult) {
$this->logger->debug('Sign success');
} else {
$this->logger->error('Sign fail');
$this->logger->error('SSL error: ' . openssl_error_string());
throw new SignFailException('Cannot sign the message');
}
$signed = file_get_contents($signFile);
# split by section
$signed = explode("\n\n", $signed);
# get third section which contains sign and join into one line
$sign = str_replace("\n", '', $this->urlSafe($signed[3]));
unlink($signFile);
unlink($messageFile);
return $sign;
}
}
================================================
FILE: tests/.configure-gost-openssl.sh
================================================
sed -i '1iopenssl_conf=openssl_def' /usr/lib/ssl/openssl.cnf
tee -a /usr/lib/ssl/openssl.cnf <<EOF
[openssl_def]
engines = engine_section
[engine_section]
gost = gost_section
[gost_section]
engine_id = gost
dynamic_path = /usr/lib/x86_64-linux-gnu/engines-1.1/gost.so
default_algorithms = ALL
CRYPT_PARAMS = id-Gost28147-89-CryptoPro-A-ParamSet
EOF
================================================
FILE: tests/_bootstrap.php
================================================
<?php
// This is global bootstrap for autoloading
require_once __DIR__ . '/../vendor/autoload.php';
================================================
FILE: tests/_data/server-gost.crt
================================================
-----BEGIN CERTIFICATE-----
MIIBnDCCAUmgAwIBAgIJAP+MfsgMHfr6MAoGBiqFAwICAwUAMCgxCzAJBgNVBAYT
AlJVMRkwFwYDVQQDDBBUZXN0ZXIgR09TVCAyMDAxMCAXDTE4MDUyMjA5MzQ1NloY
DzIwNjgwNTA5MDkzNDU2WjAoMQswCQYDVQQGEwJSVTEZMBcGA1UEAwwQVGVzdGVy
IEdPU1QgMjAwMTBjMBwGBiqFAwICEzASBgcqhQMCAiMBBgcqhQMCAh4BA0MABEDL
FFQ4czv3WOfm3+6m84epehMScB/6vJtrLgodrpdByvtk3LJyFoujKFNamQlzeJMy
1Pfr/62l+8BAR4x0uMFIo1AwTjAdBgNVHQ4EFgQUe3YXXQwYzL0r+vo+FHrvc+O7
5J8wHwYDVR0jBBgwFoAUe3YXXQwYzL0r+vo+FHrvc+O75J8wDAYDVR0TBAUwAwEB
/zAKBgYqhQMCAgMFAANBANZ+qRGMoXesUdgW2nDiFxFoJpsWSW/Njr3QY98VY/F6
/Q5xYGGd4pvnv71Mp2FHSR6YjWDR3/yV4q6z1TBQrDE=
-----END CERTIFICATE-----
================================================
FILE: tests/_data/server-gost.key
================================================
-----BEGIN PRIVATE KEY-----
MEMCAQAwHAYGKoUDAgITMBIGByqFAwICIwEGByqFAwICHgEEIOjpBFHdMSI7NioS
EjRMxGuikBUuBmvzIm+2JP9Z5yrK
-----END PRIVATE KEY-----
================================================
FILE: tests/_data/server.crt
================================================
-----BEGIN CERTIFICATE-----
MIICATCCAWoCCQDuV6K/drn++DANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJS
VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMB4XDTE1MTAwNjA1MjQwM1oXDTI1MTAwMzA1MjQwM1owRTELMAkG
A1UEBhMCUlUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0
IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwKEW
Moa/dyEGJ1OI8M07lP0r6R6Js05RoarMKJpVRbkmiO9byqcbCEiPJLZfdcNsORGZ
c9L09YBbUjJkpy4TY+qzNOpQMoScfeHA/5LzlXqzOdDapYUuXkxHQmOfAHBR3any
UuVuCBJABp0jxxMVTXxKtxgpfqLH2Jmr31yu6vMCAwEAATANBgkqhkiG9w0BAQUF
AAOBgQCJwsCDtw4IoPQiVw1fpEc8aS/QlujwChvyaTJUJ96cWHQs5hz7M23rPTib
TZ4ooJ6jbP/e7vaxpXpXShNBnnP3RVCOKX0P0t1XS1nCvr651KXlIwNWTXZxSZ72
vl5ySU71uOSaVPjicxIaPG93IFe5X7VTV4QDanSZqQqLwZsUGQ==
-----END CERTIFICATE-----
================================================
FILE: tests/_data/server.csr
================================================
-----BEGIN CERTIFICATE REQUEST-----
MIIBhDCB7gIBADBFMQswCQYDVQQGEwJSVTETMBEGA1UECBMKU29tZS1TdGF0ZTEh
MB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEB
AQUAA4GNADCBiQKBgQDAoRYyhr93IQYnU4jwzTuU/SvpHomzTlGhqswomlVFuSaI
71vKpxsISI8ktl91w2w5EZlz0vT1gFtSMmSnLhNj6rM06lAyhJx94cD/kvOVerM5
0NqlhS5eTEdCY58AcFHdqfJS5W4IEkAGnSPHExVNfEq3GCl+osfYmavfXK7q8wID
AQABoAAwDQYJKoZIhvcNAQEFBQADgYEAn8N4/LFU6fUO4wdBPB8uWV7+rHQURFAU
4qzsmcl+9l2EsHsloMRZzqKnJJHsJETxfb1zzzg4dve2bJy+50EklRMylnlG3nOi
7QKdlHyCmq9YKcra3OiyeYsQ5FnpY/WEbwFoWLt9WYwHHNrbWzSpEj8Mb7DzP8S/
kMPnUe1fH/Q=
-----END CERTIFICATE REQUEST-----
================================================
FILE: tests/_data/server.key
================================================
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,332C0B168FE46450
T7FYR235atS5fngeCLDhNrTYRz4BBMAPcTbLy5QrMMo39hjOclqol+D/QkYY7BXC
xerxuE7cGvQEKwr3PAjNOZ8PREDR7saGZhrPNoup1lWjtkR9RiJrNcg7Ya7fCCX4
a+dsDAjUt+gxg1IfMd39nQR2gO3VxYjdNFRwwaFPQpYMotSkCutUCTC8BbtWEm5G
k8bNuoJPvZfgqyZSbImlXvlht6JoUBYHoJHsin/drYxCZNDDBPe+d6yYaza8Qh0g
5a1/btkSmy8Sq5Cd2gbGICxOKDRItASvO7nBauPbGJ/N7+wt/WwT8g9XGRugzNOj
n+a9Kmqs6NwmFmaqeAZBTp3n+EmKii5tdXPmOkF9oMRLRWJsAUkzkjwPws1i3WfX
GQR0n8MmcDcLdQ9jCPV8SMp2VR49EjKInh3NRlfBwpSJCaoEAzt9Je0rj4b0eYFO
pMoNjSpWwVsLPJ3GTW0Pz8nUIlhOppZI2ARHGRSfwjtY8/IBPX8Ja5gGjDu/QT8u
EIAG+bFCk5JgYa+Sz0EX5Ok9fWymqDguF4Mr/w4Xsq0vvT3wZwq6lJzSduv+W6/l
cwncRCXyg6HLbuW+5jKPcZEF3K70zXTktLyDjZlHzqoIfBgdU/cah+X/g4N5Swfx
vjWAlUNa5YjBk6kuuTwZ8GvlBGjrYBmN4zSHAXCngGtYb+oV06hDECHVxRBu3J+2
yQXYnX8W/PfAAPxWIX8fXGzaVjrGh4tVicI1kHrma0vaCOtEKVamg9wsvSTw+YWn
TDThQ+rESGmt5PPmtlrLXhHmYBp8Xd2b95iwhMHvDYNVmRLnESH/Og==
-----END RSA PRIVATE KEY-----
================================================
FILE: tests/_support/Helper/Unit.php
================================================
<?php
namespace Helper;
// here you can define custom actions
// all public methods declared in helper class will be available in $I
use Codeception\Module;
use Codeception\TestCase;
class Unit extends Module
{
}
================================================
FILE: tests/_support/UnitTester.php
================================================
<?php
/**
* Inherited Methods
* @method void wantToTest($text)
* @method void wantTo($text)
* @method void execute($callable)
* @method void expectTo($prediction)
* @method void expect($prediction)
* @method void amGoingTo($argumentation)
* @method void am($role)
* @method void lookForwardTo($achieveValue)
* @method void comment($description)
* @method \Codeception\Lib\Friend haveFriend($name, $actorClass = null)
*
* @SuppressWarnings(PHPMD)
*/
class UnitTester extends \Codeception\Actor
{
use _generated\UnitTesterActions;
/**
* Define custom actions here
*/
}
================================================
FILE: tests/_support/_generated/UnitTesterActions.php
================================================
<?php
//[STAMP] c2c9446f63b7a0bf8b43e42b9b1b6f87
namespace _generated;
// This class was automatically generated by build task
// You should not change it manually as it will be overwritten on next build
// @codingStandardsIgnoreFile
use Codeception\Scenario;
use Codeception\Step\Action;
trait UnitTesterActions
{
/**
* @return Scenario
*/
abstract protected function getScenario();
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks that two variables are equal. If you're comparing floating-point values,
* you can specify the optional "delta" parameter which dictates how great of a precision
* error are you willing to tolerate in order to consider the two values equal.
*
* Regular example:
* ```php
* <?php
* $I->assertEquals(5, $element->getChildrenCount());
* ```
*
* Floating-point example:
* ```php
* <?php
* $I->assertEquals(0.3, $calculator->add(0.1, 0.2), 'Calculator should add the two numbers correctly.', 0.01);
* ```
*
* @param $expected
* @param $actual
* @param string $message
* @param float $delta
* @see \Codeception\Module\Asserts::assertEquals()
*/
public function assertEquals($expected, $actual, $message = null, $delta = null) {
return $this->getScenario()->runStep(new Action('assertEquals', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks that two variables are not equal. If you're comparing floating-point values,
* you can specify the optional "delta" parameter which dictates how great of a precision
* error are you willing to tolerate in order to consider the two values not equal.
*
* Regular example:
* ```php
* <?php
* $I->assertNotEquals(0, $element->getChildrenCount());
* ```
*
* Floating-point example:
* ```php
* <?php
* $I->assertNotEquals(0.4, $calculator->add(0.1, 0.2), 'Calculator should add the two numbers correctly.', 0.01);
* ```
*
* @param $expected
* @param $actual
* @param string $message
* @param float $delta
* @see \Codeception\Module\Asserts::assertNotEquals()
*/
public function assertNotEquals($expected, $actual, $message = null, $delta = null) {
return $this->getScenario()->runStep(new Action('assertNotEquals', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks that two variables are same
*
* @param $expected
* @param $actual
* @param string $message
* @see \Codeception\Module\Asserts::assertSame()
*/
public function assertSame($expected, $actual, $message = null) {
return $this->getScenario()->runStep(new Action('assertSame', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks that two variables are not same
*
* @param $expected
* @param $actual
* @param string $message
* @see \Codeception\Module\Asserts::assertNotSame()
*/
public function assertNotSame($expected, $actual, $message = null) {
return $this->getScenario()->runStep(new Action('assertNotSame', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks that actual is greater than expected
*
* @param $expected
* @param $actual
* @param string $message
* @see \Codeception\Module\Asserts::assertGreaterThan()
*/
public function assertGreaterThan($expected, $actual, $message = null) {
return $this->getScenario()->runStep(new Action('assertGreaterThan', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks that actual is greater or equal than expected
*
* @param $expected
* @param $actual
* @param string $message
* @see \Codeception\Module\Asserts::assertGreaterThanOrEqual()
*/
public function assertGreaterThanOrEqual($expected, $actual, $message = null) {
return $this->getScenario()->runStep(new Action('assertGreaterThanOrEqual', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks that actual is less than expected
*
* @param $expected
* @param $actual
* @param string $message
* @see \Codeception\Module\Asserts::assertLessThan()
*/
public function assertLessThan($expected, $actual, $message = null) {
return $this->getScenario()->runStep(new Action('assertLessThan', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks that actual is less or equal than expected
*
* @param $expected
* @param $actual
* @param string $message
* @see \Codeception\Module\Asserts::assertLessThanOrEqual()
*/
public function assertLessThanOrEqual($expected, $actual, $message = null) {
return $this->getScenario()->runStep(new Action('assertLessThanOrEqual', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks that haystack contains needle
*
* @param $needle
* @param $haystack
* @param string $message
* @see \Codeception\Module\Asserts::assertContains()
*/
public function assertContains($needle, $haystack, $message = null) {
return $this->getScenario()->runStep(new Action('assertContains', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks that haystack doesn't contain needle.
*
* @param $needle
* @param $haystack
* @param string $message
* @see \Codeception\Module\Asserts::assertNotContains()
*/
public function assertNotContains($needle, $haystack, $message = null) {
return $this->getScenario()->runStep(new Action('assertNotContains', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks that string match with pattern
*
* @param string $pattern
* @param string $string
* @param string $message
* @see \Codeception\Module\Asserts::assertRegExp()
*/
public function assertRegExp($pattern, $string, $message = null) {
return $this->getScenario()->runStep(new Action('assertRegExp', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks that string not match with pattern
*
* @param string $pattern
* @param string $string
* @param string $message
* @see \Codeception\Module\Asserts::assertNotRegExp()
*/
public function assertNotRegExp($pattern, $string, $message = null) {
return $this->getScenario()->runStep(new Action('assertNotRegExp', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks that a string starts with the given prefix.
*
* @param string $prefix
* @param string $string
* @param string $message
* @see \Codeception\Module\Asserts::assertStringStartsWith()
*/
public function assertStringStartsWith($prefix, $string, $message = null) {
return $this->getScenario()->runStep(new Action('assertStringStartsWith', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks that a string doesn't start with the given prefix.
*
* @param string $prefix
* @param string $string
* @param string $message
* @see \Codeception\Module\Asserts::assertStringStartsNotWith()
*/
public function assertStringStartsNotWith($prefix, $string, $message = null) {
return $this->getScenario()->runStep(new Action('assertStringStartsNotWith', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks that variable is empty.
*
* @param $actual
* @param string $message
* @see \Codeception\Module\Asserts::assertEmpty()
*/
public function assertEmpty($actual, $message = null) {
return $this->getScenario()->runStep(new Action('assertEmpty', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks that variable is not empty.
*
* @param $actual
* @param string $message
* @see \Codeception\Module\Asserts::assertNotEmpty()
*/
public function assertNotEmpty($actual, $message = null) {
return $this->getScenario()->runStep(new Action('assertNotEmpty', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks that variable is NULL
*
* @param $actual
* @param string $message
* @see \Codeception\Module\Asserts::assertNull()
*/
public function assertNull($actual, $message = null) {
return $this->getScenario()->runStep(new Action('assertNull', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks that variable is not NULL
*
* @param $actual
* @param string $message
* @see \Codeception\Module\Asserts::assertNotNull()
*/
public function assertNotNull($actual, $message = null) {
return $this->getScenario()->runStep(new Action('assertNotNull', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks that condition is positive.
*
* @param $condition
* @param string $message
* @see \Codeception\Module\Asserts::assertTrue()
*/
public function assertTrue($condition, $message = null)
{
return $this->getScenario()->runStep(new Action('assertTrue', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks that the condition is NOT true (everything but true)
*
* @param $condition
* @param string $message
* @see \Codeception\Module\Asserts::assertNotTrue()
*/
public function assertNotTrue($condition, $message = null)
{
return $this->getScenario()->runStep(new Action('assertNotTrue', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks that condition is negative.
*
* @param $condition
* @param string $message
* @see \Codeception\Module\Asserts::assertFalse()
*/
public function assertFalse($condition, $message = null)
{
return $this->getScenario()->runStep(new Action('assertFalse', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks that the condition is NOT false (everything but false)
*
* @param $condition
* @param string $message
* @see \Codeception\Module\Asserts::assertNotFalse()
*/
public function assertNotFalse($condition, $message = null)
{
return $this->getScenario()->runStep(new Action('assertNotFalse', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks if file exists
*
* @param string $filename
* @param string $message
* @see \Codeception\Module\Asserts::assertFileExists()
*/
public function assertFileExists($filename, $message = null)
{
return $this->getScenario()->runStep(new Action('assertFileExists', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks if file doesn't exist
*
* @param string $filename
* @param string $message
* @see \Codeception\Module\Asserts::assertFileNotExists()
*/
public function assertFileNotExists($filename, $message = null) {
return $this->getScenario()->runStep(new Action('assertFileNotExists', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* @param $expected
* @param $actual
* @param $description
* @see \Codeception\Module\Asserts::assertGreaterOrEquals()
*/
public function assertGreaterOrEquals($expected, $actual, $description = null) {
return $this->getScenario()->runStep(new Action('assertGreaterOrEquals', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* @param $expected
* @param $actual
* @param $description
* @see \Codeception\Module\Asserts::assertLessOrEquals()
*/
public function assertLessOrEquals($expected, $actual, $description = null) {
return $this->getScenario()->runStep(new Action('assertLessOrEquals', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* @param $actual
* @param $description
* @see \Codeception\Module\Asserts::assertIsEmpty()
*/
public function assertIsEmpty($actual, $description = null) {
return $this->getScenario()->runStep(new Action('assertIsEmpty', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* @param $key
* @param $actual
* @param $description
* @see \Codeception\Module\Asserts::assertArrayHasKey()
*/
public function assertArrayHasKey($key, $actual, $description = null) {
return $this->getScenario()->runStep(new Action('assertArrayHasKey', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* @param $key
* @param $actual
* @param $description
* @see \Codeception\Module\Asserts::assertArrayNotHasKey()
*/
public function assertArrayNotHasKey($key, $actual, $description = null) {
return $this->getScenario()->runStep(new Action('assertArrayNotHasKey', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Checks that array contains subset.
*
* @param array $subset
* @param array $array
* @param bool $strict
* @param string $message
* @see \Codeception\Module\Asserts::assertArraySubset()
*/
public function assertArraySubset($subset, $array, $strict = null, $message = null) {
return $this->getScenario()->runStep(new Action('assertArraySubset', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* @param $expectedCount
* @param $actual
* @param $description
* @see \Codeception\Module\Asserts::assertCount()
*/
public function assertCount($expectedCount, $actual, $description = null) {
return $this->getScenario()->runStep(new Action('assertCount', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* @param $class
* @param $actual
* @param $description
* @see \Codeception\Module\Asserts::assertInstanceOf()
*/
public function assertInstanceOf($class, $actual, $description = null) {
return $this->getScenario()->runStep(new Action('assertInstanceOf', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* @param $class
* @param $actual
* @param $description
* @see \Codeception\Module\Asserts::assertNotInstanceOf()
*/
public function assertNotInstanceOf($class, $actual, $description = null) {
return $this->getScenario()->runStep(new Action('assertNotInstanceOf', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* @param $type
* @param $actual
* @param $description
* @see \Codeception\Module\Asserts::assertInternalType()
*/
public function assertInternalType($type, $actual, $description = null) {
return $this->getScenario()->runStep(new Action('assertInternalType', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Fails the test with message.
*
* @param $message
* @see \Codeception\Module\Asserts::fail()
*/
public function fail($message) {
return $this->getScenario()->runStep(new Action('fail', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Handles and checks exception called inside callback function.
* Either exception class name or exception instance should be provided.
*
* ```php
* <?php
* $I->expectException(MyException::class, function() {
* $this->doSomethingBad();
* });
*
* $I->expectException(new MyException(), function() {
* $this->doSomethingBad();
* });
* ```
* If you want to check message or exception code, you can pass them with exception instance:
* ```php
* <?php
* // will check that exception MyException is thrown with "Don't do bad things" message
* $I->expectException(new MyException("Don't do bad things"), function() {
* $this->doSomethingBad();
* });
* ```
*
* @param $exception string or \Exception
* @param $callback
*
* @deprecated Use expectThrowable instead
* @see \Codeception\Module\Asserts::expectException()
*/
public function expectException($exception, $callback)
{
return $this->getScenario()->runStep(new Action('expectException', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Handles and checks throwables (Exceptions/Errors) called inside the callback function.
* Either throwable class name or throwable instance should be provided.
*
* ```php
* <?php
* $I->expectThrowable(MyThrowable::class, function() {
* $this->doSomethingBad();
* });
*
* $I->expectThrowable(new MyException(), function() {
* $this->doSomethingBad();
* });
* ```
* If you want to check message or throwable code, you can pass them with throwable instance:
* ```php
* <?php
* // will check that throwable MyError is thrown with "Don't do bad things" message
* $I->expectThrowable(new MyError("Don't do bad things"), function() {
* $this->doSomethingBad();
* });
* ```
*
* @param $throwable string or \Throwable
* @param $callback
* @see \Codeception\Module\Asserts::expectThrowable()
*/
public function expectThrowable($throwable, $callback)
{
return $this->getScenario()->runStep(new Action('expectThrowable', func_get_args()));
}
}
================================================
FILE: tests/unit/ConfigTest.php
================================================
<?php
namespace tests\unit;
use Codeception\Test\Unit;
use Esia\Config;
use Esia\Exceptions\InvalidConfigurationException;
/**
* Class ConfigTest
*
* @coversDefaultClass \Esia\Config
*/
class ConfigTest extends Unit
{
/**
* Getter for scope string
*
* @throws \Esia\Exceptions\InvalidConfigurationException
*/
public function testGetScopeString(): void
{
$config = new Config([
'clientId' => 'test',
'redirectUrl' => 'http://google.com',
'privateKeyPath' => '/tmp',
'certPath' => '/tmp',
'scope' => ['test', 'test2', 'test3'],
]);
$this->assertSame('test test2 test3', $config->getScopeString());
}
/**
* Data provider for @see ConfigTest::testConstruct()
*
* @return array
*/
public function dataProviderForConstructor(): array
{
return [
'min' => [
[
'clientId' => 'test',
'redirectUrl' => 'http://google.com',
'privateKeyPath' => '/tmp',
'certPath' => '/tmp',
'scope' => ['test', 'test2', 'test3'],
],
null,
],
'max' => [
[
'clientId' => 'test',
'redirectUrl' => 'http://google.com',
'privateKeyPath' => '/tmp',
'certPath' => '/tmp',
'portalUrl' => 'google.com',
'tokenUrlPath' => 'test',
'codeUrlPath' => 'test',
'personUrlPath' => 'test',
'logoutUrlPath' => 'test',
'privateKeyPassword' => 'test',
'oid' => 'test',
'responseType' => 'test',
'accessType' => 'test',
'tmpPath' => 'test',
'token' => 'test',
'scope' => ['test', 'test2', 'test3'],
],
null,
],
'No cert path' => [
[
'clientId' => 'test',
'redirectUrl' => 'http://google.com',
'privateKeyPath' => '/tmp',
'scope' => ['test', 'test2', 'test3'],
],
InvalidConfigurationException::class,
],
'No private key path' => [
[
'clientId' => 'test',
'redirectUrl' => 'http://google.com',
'certPath' => '/tmp',
'scope' => ['test', 'test2', 'test3'],
],
InvalidConfigurationException::class,
],
'No redirect url' => [
[
'clientId' => 'test',
'privateKeyPath' => '/tmp',
'certPath' => '/tmp',
'scope' => ['test', 'test2', 'test3'],
],
InvalidConfigurationException::class,
],
'No client id' => [
[
'redirectUrl' => 'http://google.com',
'privateKeyPath' => '/tmp',
'certPath' => '/tmp',
'scope' => ['test', 'test2', 'test3'],
],
InvalidConfigurationException::class,
],
'invalid scope' => [
[
'redirectUrl' => 'http://google.com',
'privateKeyPath' => '/tmp',
'certPath' => '/tmp',
'scope' => 'test test2 test3',
],
InvalidConfigurationException::class,
],
];
}
/**
* @param $config
* @param string|null $expectedException
* @throws \Esia\Exceptions\InvalidConfigurationException
*
* @dataProvider dataProviderForConstructor
*/
public function testConstruct($config, string $expectedException = null): void
{
if ($expectedException) {
$this->expectException($expectedException);
}
new Config($config);
}
/**
* @throws InvalidConfigurationException
*/
public function testGetTokenUrl(): void
{
$config = new Config([
'clientId' => 'test',
'redirectUrl' => 'http://google.com',
'privateKeyPath' => '/tmp',
'certPath' => '/tmp',
'portalUrl' => 'https://google.com/',
'tokenUrlPath' => 'test',
'scope' => ['test', 'test2', 'test3'],
]);
$this->assertSame('https://google.com/test', $config->getTokenUrl());
}
/**
* @throws InvalidConfigurationException
*/
public function testGetCodeUrl(): void
{
$config = new Config([
'clientId' => 'test',
'redirectUrl' => 'http://google.com',
'privateKeyPath' => '/tmp',
'certPath' => '/tmp',
'portalUrl' => 'https://google.com/',
'codeUrlPath' => 'test',
'scope' => ['test', 'test2', 'test3'],
]);
$this->assertSame('https://google.com/test', $config->getCodeUrl());
}
/**
* @throws InvalidConfigurationException
*/
public function testGetPersonUrl(): void
{
$config = new Config([
'clientId' => 'test',
'redirectUrl' => 'http://google.com',
'privateKeyPath' => '/tmp',
'certPath' => '/tmp',
'portalUrl' => 'https://google.com/',
'personUrlPath' => 'test',
'oid' => 'test',
'scope' => ['test', 'test2', 'test3'],
]);
$this->assertSame('https://google.com/test/test', $config->getPersonUrl());
}
/**
* @throws InvalidConfigurationException
*/
public function testGetPersonUrlWithoutOid(): void
{
$config = new Config([
'clientId' => 'test',
'redirectUrl' => 'http://google.com',
'privateKeyPath' => '/tmp',
'certPath' => '/tmp',
'portalUrl' => 'https://google.com/',
'personUrlPath' => 'test',
'scope' => ['test', 'test2', 'test3'],
]);
$this->expectException(InvalidConfigurationException::class);
$this->assertSame('https://google.com/test/test', $config->getPersonUrl());
}
/**
* @throws InvalidConfigurationException
*/
public function testGetLogoutUrl(): void
{
$config = new Config([
'clientId' => 'test',
'redirectUrl' => 'http://google.com',
'privateKeyPath' => '/tmp',
'certPath' => '/tmp',
'portalUrl' => 'https://google.com/',
'logoutUrlPath' => 'test',
'scope' => ['test', 'test2', 'test3'],
]);
$this->assertSame('https://google.com/test', $config->getLogoutUrl());
}
}
================================================
FILE: tests/unit/Http/GuzzleHttpClientTest.php
================================================
<?php
namespace tests\unit\Http;
use Codeception\Test\Unit;
use Esia\Http\GuzzleHttpClient;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use HttpException;
use Psr\Http\Client\ClientExceptionInterface;
/**
* Class GuzzleHttpClientTest
*
* @coversDefaultClass \Esia\Http\GuzzleHttpClient
*/
class GuzzleHttpClientTest extends Unit
{
/**
* @throws ClientExceptionInterface
* @throws HttpException
*/
public function testSendRequest(): void
{
$mock = new MockHandler([
new Response(),
new RequestException('Error Communicating with Server', new Request('GET', 'test'))
]);
$handler = HandlerStack::create($mock);
$guzzleClient = new Client(['handler' => $handler]);
$client = new GuzzleHttpClient($guzzleClient);
$response = $client->sendRequest(new Request('GET', '/'));
self::assertSame(200, $response->getStatusCode());
$this->expectException(ClientExceptionInterface::class);
$client->sendRequest(new Request('GET', '/'));
}
}
================================================
FILE: tests/unit/OpenIdCliOpensslTest.php
================================================
<?php
namespace tests\unit;
use Esia\Config;
use Esia\Exceptions\AbstractEsiaException;
use Esia\Exceptions\InvalidConfigurationException;
use Esia\OpenId;
use Esia\Signer\CliSignerPKCS7;
use GuzzleHttp\Psr7\Response;
class OpenIdCliOpensslTest extends OpenIdTest
{
/**
* @throws InvalidConfigurationException
*/
public function setUp(): void
{
$this->config = [
'clientId' => 'INSP03211',
'redirectUrl' => 'http://my-site.com/response.php',
'portalUrl' => 'https://esia-portal1.test.gosuslugi.ru/',
'privateKeyPath' => codecept_data_dir('server-gost.key'),
'privateKeyPassword' => 'test',
'certPath' => codecept_data_dir('server-gost.crt'),
'tmpPath' => codecept_log_dir(),
];
$config = new Config($this->config);
$this->openId = new OpenId($config);
$this->openId->setSigner(new CliSignerPKCS7(
$this->config['certPath'],
$this->config['privateKeyPath'],
$this->config['privateKeyPassword'],
$this->config['tmpPath']
));
}
/**
* @throws AbstractEsiaException
* @throws InvalidConfigurationException
*/
public function testGetToken(): void
{
$config = new Config($this->config);
$oid = '123';
$oidBase64 = base64_encode('{ "urn:esia:sbj_id" : ' . $oid . '}');
$client = $this->buildClientWithResponses([
new Response(200, [], '{ "access_token": "test.' . $oidBase64 . '.test"}'),
]);
$openId = new OpenId($config, $client);
$openId->setSigner(new CliSignerPKCS7(
$this->config['certPath'],
$this->config['privateKeyPath'],
$this->config['privateKeyPassword'],
$this->config['tmpPath']
));
$token = $openId->getToken('test');
self::assertNotEmpty($token);
self::assertSame($oid, $openId->getConfig()->getOid());
}
}
================================================
FILE: tests/unit/OpenIdTest.php
================================================
<?php
namespace tests\unit;
use Codeception\Test\Unit;
use Esia\Config;
use Esia\Exceptions\AbstractEsiaException;
use Esia\Exceptions\InvalidConfigurationException;
use Esia\Http\GuzzleHttpClient;
use Esia\OpenId;
use Esia\Signer\Exceptions\SignFailException;
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;
use Psr\Http\Client\ClientInterface;
class OpenIdTest extends Unit
{
public $config;
/**
* @var OpenId
*/
public $openId;
/**
* @throws InvalidConfigurationException
*/
public function setUp(): void
{
$this->config = [
'clientId' => 'INSP03211',
'redirectUrl' => 'http://my-site.com/response.php',
'portalUrl' => 'https://esia-portal1.test.gosuslugi.ru/',
'privateKeyPath' => codecept_data_dir('server.key'),
'privateKeyPassword' => 'test',
'certPath' => codecept_data_dir('server.crt'),
'tmpPath' => codecept_log_dir(),
];
$config = new Config($this->config);
$this->openId = new OpenId($config);
}
/**
* @throws SignFailException
* @throws AbstractEsiaException
* @throws InvalidConfigurationException
*/
public function testGetToken(): void
{
$config = new Config($this->config);
$oid = '123';
$oidBase64 = base64_encode('{ "urn:esia:sbj_id" : ' . $oid . '}');
$client = $this->buildClientWithResponses([
new Response(200, [], '{ "access_token": "test.' . $oidBase64 . '.test"}'),
]);
$openId = new OpenId($config, $client);
$token = $openId->getToken('test');
self::assertNotEmpty($token);
self::assertSame($oid, $openId->getConfig()->getOid());
}
/**
* @throws InvalidConfigurationException
* @throws AbstractEsiaException
*/
public function testGetPersonInfo(): void
{
$config = new Config($this->config);
$oid = '123';
$config->setOid($oid);
$config->setToken('test');
$client = $this->buildClientWithResponses([
new Response(200, [], '{"username": "test"}'),
]);
$openId = new OpenId($config, $client);
$info = $openId->getPersonInfo();
self::assertNotEmpty($info);
self::assertSame(['username' => 'test'], $info);
}
/**
* @throws InvalidConfigurationException
* @throws AbstractEsiaException
*/
public function testGetContactInfo(): void
{
$config = new Config($this->config);
$oid = '123';
$config->setOid($oid);
$config->setToken('test');
$client = $this->buildClientWithResponses([
new Response(200, [], '{"size": 2, "elements": ["phone", "email"]}'),
new Response(200, [], '{"phone": "555 555 555"}'),
new Response(200, [], '{"email": "test@gmail.com"}'),
]);
$openId = new OpenId($config, $client);
$info = $openId->getContactInfo();
self::assertNotEmpty($info);
self::assertSame([['phone' => '555 555 555'], ['email' => 'test@gmail.com']], $info);
}
/**
* @throws InvalidConfigurationException
* @throws AbstractEsiaException
*/
public function testGetAddressInfo(): void
{
$config = new Config($this->config);
$oid = '123';
$config->setOid($oid);
$config->setToken('test');
$client = $this->buildClientWithResponses([
new Response(200, [], '{"size": 2, "elements": ["phone", "email"]}'),
new Response(200, [], '{"phone": "555 555 555"}'),
new Response(200, [], '{"email": "test@gmail.com"}'),
]);
$openId = new OpenId($config, $client);
$info = $openId->getAddressInfo();
self::assertNotEmpty($info);
self::assertSame([['phone' => '555 555 555'], ['email' => 'test@gmail.com']], $info);
}
/**
* @throws InvalidConfigurationException
* @throws AbstractEsiaException
*/
public function testGetDocInfo(): void
{
$config = new Config($this->config);
$oid = '123';
$config->setOid($oid);
$config->setToken('test');
$client = $this->buildClientWithResponses([
new Response(200, [], '{"size": 2, "elements": ["phone", "email"]}'),
new Response(200, [], '{"phone": "555 555 555"}'),
new Response(200, [], '{"email": "test@gmail.com"}'),
]);
$openId = new OpenId($config, $client);
$info = $openId->getDocInfo();
self::assertNotEmpty($info);
self::assertSame([['phone' => '555 555 555'], ['email' => 'test@gmail.com']], $info);
}
/**
* @throws InvalidConfigurationException
*/
public function testBuildLogoutUrl(): void
{
$config = $this->openId->getConfig();
$url = $config->getLogoutUrl() . '?client_id=' . $config->getClientId();
$logoutUrl = $this->openId->buildLogoutUrl();
self::assertSame($url, $logoutUrl);
}
/**
* @throws InvalidConfigurationException
*/
public function testBuildLogoutUrlWithRedirect(): void
{
$config = $this->openId->getConfig();
$redirectUrl = 'test.example.com';
$url = $config->getLogoutUrl() . '?client_id=' . $config->getClientId() . '&redirect_url=' . $redirectUrl;
$logoutUrl = $this->openId->buildLogoutUrl($redirectUrl);
self::assertSame($url, $logoutUrl);
}
/**
* Client with prepared responses
*
* @param array $responses
* @return ClientInterface
*/
protected function buildClientWithResponses(array $responses): ClientInterface
{
$mock = new MockHandler($responses);
$handler = HandlerStack::create($mock);
$guzzleClient = new Client(['handler' => $handler]);
return new GuzzleHttpClient($guzzleClient);
}
}
================================================
FILE: tests/unit/Signer/SignerPKCS7Test.php
================================================
<?php
namespace tests\unit\Signer;
use Codeception\Test\Unit;
use Esia\Signer\Exceptions\CannotReadCertificateException;
use Esia\Signer\Exceptions\CannotReadPrivateKeyException;
use Esia\Signer\Exceptions\NoSuchCertificateFileException;
use Esia\Signer\Exceptions\NoSuchKeyFileException;
use Esia\Signer\Exceptions\NoSuchTmpDirException;
use Esia\Signer\Exceptions\SignFailException;
use Esia\Signer\SignerPKCS7;
/**
* Class SignerPKCS7Test
*
* @coversDefaultClass \Esia\Signer\SignerPKCS7
*/
class SignerPKCS7Test extends Unit
{
/**
* @throws SignFailException
*/
public function testSign(): void
{
$signer = new SignerPKCS7(
codecept_data_dir('server.crt'),
codecept_data_dir('server.key'),
'test',
codecept_log_dir()
);
$sign = $signer->sign('test');
self::assertNotEmpty($sign);
}
/**
* @throws SignFailException
*/
public function testSignCertDoesNotExists(): void
{
$signer = new SignerPKCS7(
'/test',
codecept_data_dir('server.key'),
'test',
codecept_log_dir()
);
$this->expectException(NoSuchCertificateFileException::class);
$signer->sign('test');
}
/**
* @throws SignFailException
*/
public function testPrivateKeyDoesNotExists(): void
{
$signer = new SignerPKCS7(
codecept_data_dir('server.crt'),
'/test',
'test',
codecept_log_dir()
);
$this->expectException(NoSuchKeyFileException::class);
$signer->sign('test');
}
/**
* @throws SignFailException
*/
public function testTmpDirDoesNotExists(): void
{
$signer = new SignerPKCS7(
codecept_data_dir('server.crt'),
codecept_data_dir('server.key'),
'test',
'/'
);
$this->expectException(NoSuchTmpDirException::class);
$signer->sign('test');
}
/**
* @throws SignFailException
*/
public function testTmpDirIsNotWritable(): void
{
$signer = new SignerPKCS7(
codecept_data_dir('server.crt'),
codecept_data_dir('server.key'),
'test',
codecept_log_dir('non_writable_directory')
);
$this->expectException(NoSuchTmpDirException::class);
$signer->sign('test');
}
/**
* @throws SignFailException
*/
public function testCertificateIsNotReadable(): void
{
$signer = new SignerPKCS7(
codecept_data_dir('non_readable_file'),
codecept_data_dir('server.key'),
'test',
codecept_log_dir()
);
$this->expectException(CannotReadCertificateException::class);
$signer->sign('test');
}
/**
* @throws SignFailException
*/
public function testPrivateKeyIsNotReadable(): void
{
$signer = new SignerPKCS7(
codecept_data_dir('server.crt'),
codecept_data_dir('non_readable_file'),
'test',
codecept_log_dir()
);
$this->expectException(CannotReadPrivateKeyException::class);
$signer->sign('test');
}
}
================================================
FILE: tests/unit/_bootstrap.php
================================================
<?php
// Here you can initialize variables that will be available to your tests
================================================
FILE: tests/unit.suite.yml
================================================
# Codeception Test Suite Configuration
#
# Suite for unit (internal) tests.
class_name: UnitTester
modules:
enabled:
- Asserts
- \Helper\Unit
gitextract_ro8_8jf5/
├── .gitignore
├── .travis.yml
├── README.md
├── _config.yml
├── codeception.yml
├── composer.json
├── src/
│ └── Esia/
│ ├── Config.php
│ ├── Exceptions/
│ │ ├── AbstractEsiaException.php
│ │ ├── ForbiddenException.php
│ │ ├── InvalidConfigurationException.php
│ │ └── RequestFailException.php
│ ├── Http/
│ │ ├── Exceptions/
│ │ │ └── HttpException.php
│ │ └── GuzzleHttpClient.php
│ ├── OpenId.php
│ └── Signer/
│ ├── AbstractSignerPKCS7.php
│ ├── CliSignerPKCS7.php
│ ├── Exceptions/
│ │ ├── CannotGenerateRandomIntException.php
│ │ ├── CannotReadCertificateException.php
│ │ ├── CannotReadPrivateKeyException.php
│ │ ├── NoSuchCertificateFileException.php
│ │ ├── NoSuchKeyFileException.php
│ │ ├── NoSuchTmpDirException.php
│ │ └── SignFailException.php
│ ├── SignerInterface.php
│ └── SignerPKCS7.php
└── tests/
├── .configure-gost-openssl.sh
├── _bootstrap.php
├── _data/
│ ├── server-gost.crt
│ ├── server-gost.key
│ ├── server.crt
│ ├── server.csr
│ └── server.key
├── _support/
│ ├── Helper/
│ │ └── Unit.php
│ ├── UnitTester.php
│ └── _generated/
│ └── UnitTesterActions.php
├── unit/
│ ├── ConfigTest.php
│ ├── Http/
│ │ └── GuzzleHttpClientTest.php
│ ├── OpenIdCliOpensslTest.php
│ ├── OpenIdTest.php
│ ├── Signer/
│ │ └── SignerPKCS7Test.php
│ └── _bootstrap.php
└── unit.suite.yml
SYMBOL INDEX (140 symbols across 27 files)
FILE: src/Esia/Config.php
class Config (line 7) | class Config
method __construct (line 45) | public function __construct(array $config = [])
method getPortalUrl (line 85) | public function getPortalUrl(): string
method getPrivateKeyPath (line 90) | public function getPrivateKeyPath(): string
method getPrivateKeyPassword (line 95) | public function getPrivateKeyPassword(): string
method getCertPath (line 100) | public function getCertPath(): string
method getOid (line 105) | public function getOid(): string
method setOid (line 110) | public function setOid(string $oid): void
method getScope (line 115) | public function getScope(): array
method getScopeString (line 120) | public function getScopeString(): string
method getResponseType (line 125) | public function getResponseType(): string
method getAccessType (line 130) | public function getAccessType(): string
method getTmpPath (line 135) | public function getTmpPath(): string
method getToken (line 140) | public function getToken(): ?string
method setToken (line 145) | public function setToken(string $token): void
method getClientId (line 150) | public function getClientId(): string
method getRedirectUrl (line 155) | public function getRedirectUrl(): string
method getTokenUrl (line 163) | public function getTokenUrl(): string
method getCodeUrl (line 171) | public function getCodeUrl(): string
method getPersonUrl (line 180) | public function getPersonUrl(): string
method getLogoutUrl (line 191) | public function getLogoutUrl(): string
FILE: src/Esia/Exceptions/AbstractEsiaException.php
class AbstractEsiaException (line 7) | abstract class AbstractEsiaException extends Exception
FILE: src/Esia/Exceptions/ForbiddenException.php
class ForbiddenException (line 5) | class ForbiddenException extends AbstractEsiaException
method getMessageForCode (line 7) | protected function getMessageForCode(int $code): string
FILE: src/Esia/Exceptions/InvalidConfigurationException.php
class InvalidConfigurationException (line 5) | class InvalidConfigurationException extends AbstractEsiaException
FILE: src/Esia/Exceptions/RequestFailException.php
class RequestFailException (line 5) | class RequestFailException extends AbstractEsiaException
FILE: src/Esia/Http/Exceptions/HttpException.php
class HttpException (line 8) | class HttpException extends RuntimeException implements ClientExceptionI...
FILE: src/Esia/Http/GuzzleHttpClient.php
class GuzzleHttpClient (line 13) | class GuzzleHttpClient implements ClientInterface
method __construct (line 23) | public function __construct(Client $guzzle)
method sendRequest (line 48) | public function sendRequest(RequestInterface $request): ResponseInterface
FILE: src/Esia/OpenId.php
class OpenId (line 28) | class OpenId
method __construct (line 51) | public function __construct(Config $config, ClientInterface $client = ...
method setSigner (line 67) | public function setSigner(SignerInterface $signer): void
method getConfig (line 75) | public function getConfig(): Config
method buildUrl (line 90) | public function buildUrl()
method buildLogoutUrl (line 122) | public function buildLogoutUrl(string $redirectUrl = null): string
method getToken (line 144) | public function getToken(string $code): string
method getPersonInfo (line 201) | public function getPersonInfo(): array
method getContactInfo (line 217) | public function getContactInfo(): array
method getAddressInfo (line 239) | public function getAddressInfo(): array
method getDocInfo (line 260) | public function getDocInfo(): array
method collectArrayElements (line 279) | private function collectArrayElements($elements): array
method sendRequest (line 296) | private function sendRequest(RequestInterface $request): array
method getTimeStamp (line 339) | private function getTimeStamp(): string
method buildState (line 349) | private function buildState(): string
method base64UrlSafeDecode (line 371) | private function base64UrlSafeDecode(string $string): string
FILE: src/Esia/Signer/AbstractSignerPKCS7.php
class AbstractSignerPKCS7 (line 14) | abstract class AbstractSignerPKCS7
method __construct (line 42) | public function __construct(
method checkFilesExists (line 65) | protected function checkFilesExists(): void
method getRandomString (line 90) | protected function getRandomString(): string
method urlSafe (line 98) | protected function urlSafe(string $string): string
FILE: src/Esia/Signer/CliSignerPKCS7.php
class CliSignerPKCS7 (line 7) | class CliSignerPKCS7 extends AbstractSignerPKCS7 implements SignerInterface
method sign (line 12) | public function sign(string $message): string
method run (line 47) | private function run(string $command): void
FILE: src/Esia/Signer/Exceptions/CannotGenerateRandomIntException.php
class CannotGenerateRandomIntException (line 5) | class CannotGenerateRandomIntException extends SignFailException
FILE: src/Esia/Signer/Exceptions/CannotReadCertificateException.php
class CannotReadCertificateException (line 5) | class CannotReadCertificateException extends SignFailException
FILE: src/Esia/Signer/Exceptions/CannotReadPrivateKeyException.php
class CannotReadPrivateKeyException (line 5) | class CannotReadPrivateKeyException extends SignFailException
FILE: src/Esia/Signer/Exceptions/NoSuchCertificateFileException.php
class NoSuchCertificateFileException (line 5) | class NoSuchCertificateFileException extends SignFailException
FILE: src/Esia/Signer/Exceptions/NoSuchKeyFileException.php
class NoSuchKeyFileException (line 5) | class NoSuchKeyFileException extends SignFailException
FILE: src/Esia/Signer/Exceptions/NoSuchTmpDirException.php
class NoSuchTmpDirException (line 5) | class NoSuchTmpDirException extends SignFailException
FILE: src/Esia/Signer/Exceptions/SignFailException.php
class SignFailException (line 7) | class SignFailException extends AbstractEsiaException
method getMessageForCode (line 9) | protected function getMessageForCode(int $code): string
FILE: src/Esia/Signer/SignerInterface.php
type SignerInterface (line 7) | interface SignerInterface
method sign (line 12) | public function sign(string $message): string;
FILE: src/Esia/Signer/SignerPKCS7.php
class SignerPKCS7 (line 9) | class SignerPKCS7 extends AbstractSignerPKCS7 implements SignerInterface
method addPKCS7Flag (line 13) | public function addPKCS7Flag(int $pkcs7Flag): void
method sign (line 21) | public function sign(string $message): string
FILE: tests/_support/Helper/Unit.php
class Unit (line 9) | class Unit extends Module
FILE: tests/_support/UnitTester.php
class UnitTester (line 19) | class UnitTester extends \Codeception\Actor
FILE: tests/_support/_generated/UnitTesterActions.php
type UnitTesterActions (line 12) | trait UnitTesterActions
method getScenario (line 17) | abstract protected function getScenario();
method assertEquals (line 45) | public function assertEquals($expected, $actual, $message = null, $del...
method assertNotEquals (line 75) | public function assertNotEquals($expected, $actual, $message = null, $...
method assertSame (line 90) | public function assertSame($expected, $actual, $message = null) {
method assertNotSame (line 105) | public function assertNotSame($expected, $actual, $message = null) {
method assertGreaterThan (line 120) | public function assertGreaterThan($expected, $actual, $message = null) {
method assertGreaterThanOrEqual (line 135) | public function assertGreaterThanOrEqual($expected, $actual, $message ...
method assertLessThan (line 150) | public function assertLessThan($expected, $actual, $message = null) {
method assertLessThanOrEqual (line 165) | public function assertLessThanOrEqual($expected, $actual, $message = n...
method assertContains (line 180) | public function assertContains($needle, $haystack, $message = null) {
method assertNotContains (line 195) | public function assertNotContains($needle, $haystack, $message = null) {
method assertRegExp (line 210) | public function assertRegExp($pattern, $string, $message = null) {
method assertNotRegExp (line 225) | public function assertNotRegExp($pattern, $string, $message = null) {
method assertStringStartsWith (line 240) | public function assertStringStartsWith($prefix, $string, $message = nu...
method assertStringStartsNotWith (line 255) | public function assertStringStartsNotWith($prefix, $string, $message =...
method assertEmpty (line 269) | public function assertEmpty($actual, $message = null) {
method assertNotEmpty (line 283) | public function assertNotEmpty($actual, $message = null) {
method assertNull (line 297) | public function assertNull($actual, $message = null) {
method assertNotNull (line 311) | public function assertNotNull($actual, $message = null) {
method assertTrue (line 325) | public function assertTrue($condition, $message = null)
method assertNotTrue (line 340) | public function assertNotTrue($condition, $message = null)
method assertFalse (line 355) | public function assertFalse($condition, $message = null)
method assertNotFalse (line 370) | public function assertNotFalse($condition, $message = null)
method assertFileExists (line 385) | public function assertFileExists($filename, $message = null)
method assertFileNotExists (line 400) | public function assertFileNotExists($filename, $message = null) {
method assertGreaterOrEquals (line 413) | public function assertGreaterOrEquals($expected, $actual, $description...
method assertLessOrEquals (line 426) | public function assertLessOrEquals($expected, $actual, $description = ...
method assertIsEmpty (line 438) | public function assertIsEmpty($actual, $description = null) {
method assertArrayHasKey (line 451) | public function assertArrayHasKey($key, $actual, $description = null) {
method assertArrayNotHasKey (line 464) | public function assertArrayNotHasKey($key, $actual, $description = nul...
method assertArraySubset (line 480) | public function assertArraySubset($subset, $array, $strict = null, $me...
method assertCount (line 493) | public function assertCount($expectedCount, $actual, $description = nu...
method assertInstanceOf (line 506) | public function assertInstanceOf($class, $actual, $description = null) {
method assertNotInstanceOf (line 519) | public function assertNotInstanceOf($class, $actual, $description = nu...
method assertInternalType (line 532) | public function assertInternalType($type, $actual, $description = null) {
method fail (line 545) | public function fail($message) {
method expectException (line 581) | public function expectException($exception, $callback)
method expectThrowable (line 616) | public function expectThrowable($throwable, $callback)
FILE: tests/unit/ConfigTest.php
class ConfigTest (line 14) | class ConfigTest extends Unit
method testGetScopeString (line 21) | public function testGetScopeString(): void
method dataProviderForConstructor (line 39) | public function dataProviderForConstructor(): array
method testConstruct (line 128) | public function testConstruct($config, string $expectedException = nul...
method testGetTokenUrl (line 140) | public function testGetTokenUrl(): void
method testGetCodeUrl (line 158) | public function testGetCodeUrl(): void
method testGetPersonUrl (line 176) | public function testGetPersonUrl(): void
method testGetPersonUrlWithoutOid (line 194) | public function testGetPersonUrlWithoutOid(): void
method testGetLogoutUrl (line 212) | public function testGetLogoutUrl(): void
FILE: tests/unit/Http/GuzzleHttpClientTest.php
class GuzzleHttpClientTest (line 21) | class GuzzleHttpClientTest extends Unit
method testSendRequest (line 27) | public function testSendRequest(): void
FILE: tests/unit/OpenIdCliOpensslTest.php
class OpenIdCliOpensslTest (line 12) | class OpenIdCliOpensslTest extends OpenIdTest
method setUp (line 17) | public function setUp(): void
method testGetToken (line 44) | public function testGetToken(): void
FILE: tests/unit/OpenIdTest.php
class OpenIdTest (line 18) | class OpenIdTest extends Unit
method setUp (line 30) | public function setUp(): void
method testGetToken (line 52) | public function testGetToken(): void
method testGetPersonInfo (line 73) | public function testGetPersonInfo(): void
method testGetContactInfo (line 94) | public function testGetContactInfo(): void
method testGetAddressInfo (line 117) | public function testGetAddressInfo(): void
method testGetDocInfo (line 140) | public function testGetDocInfo(): void
method testBuildLogoutUrl (line 162) | public function testBuildLogoutUrl(): void
method testBuildLogoutUrlWithRedirect (line 174) | public function testBuildLogoutUrlWithRedirect(): void
method buildClientWithResponses (line 190) | protected function buildClientWithResponses(array $responses): ClientI...
FILE: tests/unit/Signer/SignerPKCS7Test.php
class SignerPKCS7Test (line 19) | class SignerPKCS7Test extends Unit
method testSign (line 24) | public function testSign(): void
method testSignCertDoesNotExists (line 40) | public function testSignCertDoesNotExists(): void
method testPrivateKeyDoesNotExists (line 56) | public function testPrivateKeyDoesNotExists(): void
method testTmpDirDoesNotExists (line 72) | public function testTmpDirDoesNotExists(): void
method testTmpDirIsNotWritable (line 88) | public function testTmpDirIsNotWritable(): void
method testCertificateIsNotReadable (line 104) | public function testCertificateIsNotReadable(): void
method testPrivateKeyIsNotReadable (line 120) | public function testPrivateKeyIsNotReadable(): void
Condensed preview — 42 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (82K chars).
[
{
"path": ".gitignore",
"chars": 80,
"preview": ".idea\ntests/tmp/*\ntests/_output/*\npublic/*\nvendor\n\ntests/_data/non_readable_file"
},
{
"path": ".travis.yml",
"chars": 453,
"preview": "dist: bionic\nlanguage: php\naddons:\n apt:\n packages:\n - libengine-gost-openssl1.1\n\nbefore_install:\n - sudo bash"
},
{
"path": "README.md",
"chars": 2810,
"preview": "\n# Единая система идентификации и аутентификации (ЕСИА) OpenId \n\n[\n * @method void wantTo($text)\n * @method void execute"
},
{
"path": "tests/_support/_generated/UnitTesterActions.php",
"chars": 19830,
"preview": "<?php\n//[STAMP] c2c9446f63b7a0bf8b43e42b9b1b6f87\nnamespace _generated;\n\n// This class was automatically generated by bui"
},
{
"path": "tests/unit/ConfigTest.php",
"chars": 7053,
"preview": "<?php\n\nnamespace tests\\unit;\n\nuse Codeception\\Test\\Unit;\nuse Esia\\Config;\nuse Esia\\Exceptions\\InvalidConfigurationExcept"
},
{
"path": "tests/unit/Http/GuzzleHttpClientTest.php",
"chars": 1212,
"preview": "<?php\n\nnamespace tests\\unit\\Http;\n\nuse Codeception\\Test\\Unit;\nuse Esia\\Http\\GuzzleHttpClient;\nuse GuzzleHttp\\Client;\nuse"
},
{
"path": "tests/unit/OpenIdCliOpensslTest.php",
"chars": 2010,
"preview": "<?php\n\nnamespace tests\\unit;\n\nuse Esia\\Config;\nuse Esia\\Exceptions\\AbstractEsiaException;\nuse Esia\\Exceptions\\InvalidCon"
},
{
"path": "tests/unit/OpenIdTest.php",
"chars": 6011,
"preview": "<?php\n\nnamespace tests\\unit;\n\nuse Codeception\\Test\\Unit;\nuse Esia\\Config;\nuse Esia\\Exceptions\\AbstractEsiaException;\nuse"
},
{
"path": "tests/unit/Signer/SignerPKCS7Test.php",
"chars": 3289,
"preview": "<?php\n\nnamespace tests\\unit\\Signer;\n\nuse Codeception\\Test\\Unit;\nuse Esia\\Signer\\Exceptions\\CannotReadCertificateExceptio"
},
{
"path": "tests/unit/_bootstrap.php",
"chars": 80,
"preview": "<?php\n// Here you can initialize variables that will be available to your tests\n"
},
{
"path": "tests/unit.suite.yml",
"chars": 162,
"preview": "# Codeception Test Suite Configuration\n#\n# Suite for unit (internal) tests.\n\nclass_name: UnitTester\nmodules:\n enabled"
}
]
About this extraction
This page contains the full source code of the fr05t1k/esia GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 42 files (73.0 KB), approximately 20.6k tokens, and a symbol index with 140 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.