разработана аутентификация и регистрация аккаунта

This commit is contained in:
2023-03-08 21:43:33 +10:00
parent 0d6ceef0f9
commit 87bd15640a
24 changed files with 1082 additions and 1353 deletions

View File

@@ -6,18 +6,7 @@ namespace mirzaev\site\account\controllers;
// Файлы проекта
use mirzaev\site\account\controllers\core,
mirzaev\site\account\models\account as model,
mirzaev\site\account\models\session,
mirzaev\site\account\models\vk;
// Фреймворк для ВКонтакте
use mirzaev\vk\core as api;
// Библиотека для ArangoDB
use ArangoDBClient\Document as _document;
// Встроенные библиотеки
use stdClass;
mirzaev\site\account\models\account as model;
/**
* Контроллер аккаунта
@@ -36,114 +25,4 @@ final class account extends core
{
return null;
}
/**
* Инициализация
*
* @param array $parameters Параметры запроса
*/
public function initialization(array $parameters = []): ?string
{
if ($this->variables['account'] instanceof _document) {
// Найден аккаунт
if ($this->variables['vk'] instanceof _document) {
// Найден аккаунт ВКонтакте
// Инициализация данных аккаунта ВКонтакте
vk::parse($this->variables['vk'], $this->variables['errors']['vk']);
}
// Запись кода ответа
http_response_code(200);
return null;
} else {
// Не найден аккаунт
// Запись кода ответа
http_response_code(401);
// Запись заголовка ответа с ключом аккаунта
header('session: ' . $this->variables['session']->hash);
return null;
}
// Запись кода ответа
http_response_code(500);
return null;
}
/**
* Связь аккаунта с аккаунтом ВКонтакте
*
* @param array $parameters Параметры запроса
*/
public function connect(array $parameters = []): ?string
{
if ($this->variables['session']->hash === $parameters['state']) {
// Совпадает хеш сессии с полученным хешем из ответа ВКонтакте
if (!empty($response = vk::key($parameters['code'], $this->variables['errors']['vk']))) {
// Получены данные аккаунта ВКонтакте
if (($this->variables['vk'] = vk::initialization($response, $this->variables['errors']['vk'])) instanceof _document) {
// Инициализирован аккаунт ВКонтакте
if (($this->variables['account'] = vk::account($this->variables['vk'])) instanceof _document) {
// Найден аккаунт (существующий)
if (session::connect($this->variables['session'], $this->variables['account'], $this->variables['errors']['session'])) {
// Связана сессия с аккаунтом
}
} else if (($this->variables['account'] = model::create($this->variables['errors']['account'])) instanceof _document) {
// Найден аккаунт (создан новый)
if (session::connect($this->variables['session'], $this->variables['account'], $this->variables['errors']['session'])) {
// Связана сессия с аккаунтом
if (account::connect($this->variables['account'], $this->variables['vk'], $this->variables['errors']['account'])) {
// Связан аккаунт с аккаунтом ВКонтакте
}
}
}
// Инициализация робота для аккаунта ВКонтакте
$this->vk = api::init()->user(key: $this->variables['vk']->access['key']);
if ($this->variables['vk'] instanceof _document) {
// Инициализирован робот для аккаунта ВКонтакте
// Инициализация данных аккаунта ВКонтакте
$data = vk::parse($this->vk, $this->variables['errors']['vk']);
var_dump($data);
die;
if ($data instanceof stdClass) {
// Получены данные ВКонтакте
// Запись в базу данных
vk::update($this->variables['vk'], $data, $this->variables['errors']['vk']);
}
}
}
}
}
// Генерация представления
return $this->view->render(DIRECTORY_SEPARATOR . 'account' . DIRECTORY_SEPARATOR . 'vk.html', $this->variables);
}
/**
* Генерация панели аккаунта
*
* @param array $parameters Параметры запроса
*/
public function panel(array $parameters = []): ?string
{
// Генерация представления
return $this->view->render(DIRECTORY_SEPARATOR . 'account' . DIRECTORY_SEPARATOR . 'panel.html', $this->variables);
}
}

View File

@@ -53,7 +53,7 @@ final class api extends core
default => throw new exception("Параметр не найден: $parameter")
};
} catch (exception $e) {
// Запись в журнал ошибок
// Запись в реестр ошибок
$this->errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),

View File

@@ -5,20 +5,16 @@ declare(strict_types=1);
namespace mirzaev\site\account\controllers;
// Файлы проекта
use mirzaev\site\account\views\templater;
use mirzaev\site\account\models\core as models;
use mirzaev\site\account\models\account;
use mirzaev\site\account\models\session;
// Библиотека для ArangoDB
use ArangoDBClient\Document as _document;
use mirzaev\site\account\views\templater,
mirzaev\site\account\models\core as models,
mirzaev\site\account\models\account,
mirzaev\site\account\models\session;
// Фреймворк PHP
use mirzaev\minimal\controller;
// Фреймворк ВКонтакте
use mirzaev\vk\core as vk;
use mirzaev\vk\robots\user as robot;
// Встроенные библиотеки
use exception;
/**
* Ядро контроллеров
@@ -28,11 +24,6 @@ use mirzaev\vk\robots\user as robot;
*/
class core extends controller
{
/**
* Переменные окружения
*/
protected robot $vk;
/**
* Инстанция сессии
*/
@@ -59,45 +50,46 @@ class core extends controller
/**
* Конструктор
*
* @return void
* @param bool $initialize Инициализировать контроллер?
*/
public function __construct()
public function __construct(bool $initialize = true)
{
parent::__construct();
parent::__construct($initialize);
// Инициализация ядра моделей (соединение с базой данных...)
new models();
if ($initialize) {
// Запрошена инициализация
// Инициализация шаблонизатора представлений
$this->view = new templater;
// Инициализация ядра моделей (соединение с базой данных...)
new models();
// Инициализация даты до которой будет активна сессия
$expires = time() + 604800;
// Инициализация даты до которой будет активна сессия
$expires = time() + 604800;
// Инициализация сессии (без журналирования)
$this->session = new session($_COOKIE["session"] ?? null, $expires) ??
header('Location: https://mirzaev.sexy/error?code=500&text=Не+удалось+инициализировать+сессию');
// Инициализация значения по умолчанию
$_COOKIE["session"] ??= null;
if ($_COOKIE["session"] ?? null !== $this->session->hash) {
// Изменился хеш сессии (подразумевается, что сессия устарела)
// Инициализация сессии
$this->session = new session($_COOKIE["session"], $expires);
// Запись хеша новой сессии
setcookie('session', $this->session->hash, [
'expires' => $expires,
'domain' => 'mirzaev.sexy',
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'strict'
]);
}
if ($_COOKIE["session"] !== $this->session->hash) {
// Изменился хеш сессии (подразумевается, что сессия устарела)
// Инициализация аккаунта (без журналирования)
$this->account = $this->session->account();
// Запись хеша новой сессии
setcookie('session', $this->session->hash, [
'expires' => $expires,
'domain' => 'mirzaev.sexy',
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'strict'
]);
}
if ($this->account instanceof _document) {
// Инициализирован аккаунт
// Инициализация аккаунта
$this->account = new account($this->session);
// Инициализация шаблонизатора представлений
$this->view = new templater($this->session, $this->account);
}
}
}

View File

@@ -15,68 +15,65 @@ use mirzaev\site\account\controllers\core;
*/
final class hotline extends core
{
/**
* Страница с бегущей строкой
*
* Можно использовать совместно с элементом <iframe> для изоляции
* содержимого бегущей строки от поисковых роботов
*
* @param array $parameters
*/
public function index(array $parameters = []): ?string
{
// Инициализация элементов для генерации в головном элементе
$this->variables['head'] = [
'title' => 'Бегущая строка',
'metas' => [
[
'attributes' => [
'name' => 'robots',
'content' => 'nofollow'
]
]
]
];
/**
* Страница с бегущей строкой
*
* Можно использовать совместно с элементом <iframe> для изоляции
* содержимого бегущей строки от поисковых роботов
*
* @param array $parameters
*/
public function index(array $parameters = []): ?string
{
// Инициализация элементов для генерации в головном элементе
$this->variables['head'] = [
'title' => 'Бегущая строка',
'metas' => [
[
'attributes' => [
'name' => 'robots',
'content' => 'nofollow'
]
]
]
];
// Инициализация бегущей строки
$this->variables['hotline'] = [
'id' => $this->variables['request']['id'] ?? 'hotline'
];
// Инициализация бегущей строки
$this->variables['hotline'] = [
'id' => $this->variables['request']['id'] ?? 'hotline'
];
// Инициализация параметров бегущей строки
$this->variables['hotline']['parameters'] = [
// 'step' => 2
];
// Инициализация параметров бегущей строки
$this->variables['hotline']['parameters'] = [
// 'step' => 2
];
// Инициализация аттрибутов бегущей строки
$this->variables['hotline']['attributes'] = [
// Инициализация аттрибутов бегущей строки
$this->variables['hotline']['attributes'] = [];
];
// Инициализация элементов бегущей строки
$this->variables['hotline']['elements'] = [
['content' => '1'],
[
'tag' => 'article',
'content' => '2'
],
['content' => '3'],
['content' => '4'],
['content' => '5'],
['content' => '6'],
['content' => '7'],
['content' => '8'],
['content' => '9'],
['content' => '10'],
['content' => '11'],
['content' => '12'],
['content' => '13'],
['content' => '14'],
['content' => '15']
];
// Генерация представления
return $this->view->render(DIRECTORY_SEPARATOR . 'hotline' . DIRECTORY_SEPARATOR . 'index.html', $this->variables);
}
// Инициализация элементов бегущей строки
$this->variables['hotline']['elements'] = [
['content' => '1'],
[
'tag' => 'article',
'content' => '2'
],
['content' => '3'],
['content' => '4'],
['content' => '5'],
['content' => '6'],
['content' => '7'],
['content' => '8'],
['content' => '9'],
['content' => '10'],
['content' => '11'],
['content' => '12'],
['content' => '13'],
['content' => '14'],
['content' => '15']
];
// Генерация представления
return $this->view->render(DIRECTORY_SEPARATOR . 'hotline' . DIRECTORY_SEPARATOR . 'index.html', $this->variables);
}
}

View File

@@ -24,7 +24,9 @@ final class index extends core
{
// Инициализация узлов
$this->view->nodes = [
'account' => $this->view->render(DIRECTORY_SEPARATOR . 'nodes' . DIRECTORY_SEPARATOR . (isset($this->account) ? 'profile.html' : 'authentication.html'))
'account' => $this->view->render(DIRECTORY_SEPARATOR . (isset($this->account->document)
? 'nodes' . DIRECTORY_SEPARATOR . 'profile.html'
: 'pages' . DIRECTORY_SEPARATOR . 'entry.html'))
/* 'account' => $this->view->render(DIRECTORY_SEPARATOR . 'nodes' . DIRECTORY_SEPARATOR . (isset($this->account) ? 'profile.html' : 'connect.html')) */
];

View File

@@ -24,16 +24,16 @@ final class session extends core
use errors;
/**
* Записать входной псевдоним
* Записать входной псевдоним в буфер сессии
*
* Проверяет существование аккаунта с этим входным псевдонимом
* и запоминает для использования в процессе аутентификации
*
* @param array $parameters Параметры запроса
*
* @return string JSON-документ с запрашиваемыми параметрами
* @return void В буфер вывода JSON-документ с запрашиваемыми параметрами
*/
public function login(array $parameters = []): string
public function login(array $parameters = []): void
{
// Инициализация буфера ответа
$buffer = [];
@@ -51,20 +51,27 @@ final class session extends core
// Проверка параметров на соответствование требованиям
if ($length === 0) throw new exception('Входной псевдоним не может быть пустым');
if ($length > 100) throw new exception('Входной псевдоним не может быть длиннее 100 символов');
if (preg_match_all('/[^\w\s\r\n\t\0]+/u', $parameters['login'], $matches) > 0) throw new exception('Нельзя использовать символы: ' . implode(', ', ...$matches));
// Поиск аккаунта
$account = account::read($parameters['login'], $this->errors['account']);
$account = account::login($parameters['login']);
// Генерация ответа по запрашиваемым параметрам
foreach ($return as $parameter) match ($parameter) {
'exist' => $buffer['exist'] = isset($account->instance),
'exist' => $buffer['exist'] = isset($account->document),
'account' => (function () use ($parameters, &$buffer) {
// Запись в буфер сессии
if (isset($parameters['remember']) && $parameters['remember'] === '1')
$this->session->write(['entry' => ['login' => $parameters['login']]], $this->errors);
// Поиск аккаунта и запись в буфер вывода
$buffer['account'] = isset((new account($this->session, authenticate: true, errors: $this->errors))->document);
})(),
'errors' => null,
default => throw new exception("Параметр не найден: $parameter")
};
if ($parameters['remember'] === '1') $this->session->remember('account.identification.login', $parameters['login']);
} catch (exception $e) {
// Запись в журнал ошибок
// Запись в реестр ошибок
$this->errors['session'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
@@ -76,23 +83,40 @@ final class session extends core
// Запись реестра ошибок в буфер ответа
if (in_array('errors', $return, true)) $buffer['errors'] = self::parse_only_text($this->errors);
// Запись заголовка ответа
// Запись заголовков ответа
header('Content-Type: application/json');
header('Content-Encoding: none');
header('X-Accel-Buffering: no');
return json_encode($buffer);
// Инициализация буфера вывода
ob_start();
// Генерация ответа
echo json_encode($buffer);
// Запись заголовков ответа
header('Content-Length: ' . ob_get_length());
// Отправка и деинициализация буфера вывода
ob_end_flush();
flush();
// Запись в буфер сессии
if (!in_array('account', $return, true) && isset($parameters['remember']) && $parameters['remember'] === '1')
$this->session->write(['entry' => ['login' => $parameters['login']]]);
}
/**
* Записать пароль
* Записать пароль в буфер сессии
*
* Проверяет на соответствие требованиям
* и запоминает для использования в процессе аутентификации
*
* @param array $parameters Параметры запроса
*
* @return string JSON-документ с запрашиваемыми параметрами
* @return void В буфер вывода JSON-документ с запрашиваемыми параметрами
*/
public function password(array $parameters = []): string
public function password(array $parameters = []): void
{
// Инициализация буфера ответа
$buffer = [];
@@ -101,57 +125,74 @@ final class session extends core
$return = explode(',', $parameters['return'], 50);
try {
// Проверка наличия обязательных параметров
if (empty($parameters['password'])) throw new exception('Необходимо передать пароль');
// Вычисление длины
$length = strlen($parameters['password']);
// Проверка параметров на соответствование требованиям
if ($length === 0) throw new exception('Пароль не может быть пустым');
if ($length > 300) throw new exception('Пароль не может быть длиннее 300 символов');
if (preg_match_all('/[^\w\s\r\n\t\0]+/u', $parameters['password'], $matches) > 0) throw new exception('Нельзя использовать символы: ' . implode(', ', ...$matches));
// Генерация ответа по запрашиваемым параметрам
foreach ($return as $parameter) match ($parameter) {
'verify' => $buffer['verify'] = true,
'account' => (function() use ($parameters, &$buffer) {
// Запись в буфер сессии
if (isset($parameters['remember']) && $parameters['remember'] === '1')
$this->session->write(['entry' => ['password' => $parameters['password']]], $this->errors);
// Поиск аккаунта и запись в буфер вывода
$buffer['account'] = isset((new account($this->session, authenticate: true, register: true, errors: $this->errors))->document);
})(),
'errors' => null,
default => throw new exception("Параметр не найден: $parameter")
};
if ($parameters['remember'] === '1') throw new exception('Запоминать пароль не безопасно');
} catch (exception $e) {
// Запись в журнал ошибок
// Запись в реестр ошибок
$this->errors['session'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
// Запись реестра ошибок в буфер ответа
if (in_array('verify', $return, true)) $buffer['verify'] = false;
}
// Запись реестра ошибок в буфер ответа
if (in_array('errors', $return, true)) $buffer['errors'] = self::parse_only_text($this->errors);
// Запись заголовка ответа
// Запись заголовков ответа
header('Content-Type: application/json');
header('Content-Encoding: none');
header('X-Accel-Buffering: no');
return json_encode($buffer);
// Инициализация буфера вывода
ob_start();
// Генерация ответа
echo json_encode($buffer);
// Запись заголовков ответа
header('Content-Length: ' . ob_get_length());
// Отправка и деинициализация буфера вывода
ob_end_flush();
flush();
// Запись в буфер сессии
if (!in_array('account', $return, true) && isset($parameters['remember']) && $parameters['remember'] === '1')
$this->session->write(['entry' => ['password' => $parameters['password']]]);
}
/**
* Записать код приглашения
* Записать код приглашения в буфер сессии
*
* Проверяет существование приглашения с этим кодом
* и запоминает для использования в процессе регистрации
*
* @param array $parameters Параметры запроса
*
* @return string JSON-документ с запрашиваемыми параметрами
* @return void В буфер вывода JSON-документ с запрашиваемыми параметрами
*/
public function invite(array $parameters = []): string
public function invite(array $parameters = []): void
{
// Инициализация буфера ответа
$buffer = [];
@@ -168,22 +209,29 @@ final class session extends core
// Проверка параметров на соответствование требованиям
if ($length === 0) throw new exception('Получен пустой ключ приглашения');
if (preg_match_all('/[^\w\s\r\n\t\0]+/u', $parameters['invite'], $matches) > 0) throw new exception('Нельзя использовать символы: ' . implode(', ', ...$matches));
// Поиск приглашения
$invite = invite::read($parameters['invite'], $this->errors['session']);
$invite = invite::read($parameters['invite']);
// Генерация ответа по запрашиваемым параметрам
foreach ($return as $parameter) match ($parameter) {
'exist' => $buffer['exist'] = isset($invite->instance),
'exist' => $buffer['exist'] = isset($invite->document),
// from временное решение пока не будет разработана система сессий
'from' => $return['from'] = ['login' => 'mirzaev'] ?? $invite->from(),
'from' => $buffer['from'] = ['login' => 'mirzaev'] ?? $invite->from(),
'account' => (function () use ($parameters, &$buffer) {
// Запись в буфер сессии
if (isset($parameters['remember']) && $parameters['remember'] === '1')
$this->session->write(['entry' => ['invite' => $parameters['invite']]], $this->errors);
// Поиск аккаунта и запись в буфер вывода
$buffer['account'] = isset((new account($this->session, authenticate: true, errors: $this->errors))->document);
})(),
'errors' => null,
default => throw new exception("Параметр не найден: $parameter")
};
if ($parameters['remember'] === '1') $this->session->remember('account.registration.invite', $parameters['invite']);
} catch (exception $e) {
// Запись в журнал ошибок
// Запись в реестр ошибок
$this->errors['session'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
@@ -195,9 +243,26 @@ final class session extends core
// Запись реестра ошибок в буфер ответа
if (in_array('errors', $return, true)) $buffer['errors'] = self::parse_only_text($this->errors);
// Запись заголовка ответа
// Запись заголовков ответа
header('Content-Type: application/json');
return json_encode($buffer);
header('Content-Encoding: none');
header('X-Accel-Buffering: no');
// Инициализация буфера вывода
ob_start();
// Генерация ответа
echo json_encode($buffer);
// Запись заголовков ответа
header('Content-Length: ' . ob_get_length());
// Отправка и деинициализация буфера вывода
ob_end_flush();
flush();
// Запись в буфер сессии
if (!in_array('account', $return, true) && isset($parameters['remember']) && $parameters['remember'] === '1')
$this->session->write(['entry' => ['invite' => $parameters['invite']]]);
}
}

View File

@@ -22,7 +22,7 @@ trait errors
// Проверка на вложенность и запись в буфер вывода (вход в рекурсию)
if (isset($error['text'])) $buffer[] = $error['text'];
else if (is_array($error)) $buffer[$offset] = static::parse_only_text($error);
else if (is_array($error) && count($error) > 0) $buffer[$offset] = static::parse_only_text($error);
}
return $buffer;