diff options
author | Daniel Weipert <git@mail.dweipert.de> | 2025-08-14 14:37:56 +0200 |
---|---|---|
committer | Daniel Weipert <git@mail.dweipert.de> | 2025-08-14 14:37:56 +0200 |
commit | c135fcf9041c604b32827a1cb027010bca5915ab (patch) | |
tree | 95a2331d5f0bc4a736aa0e716330c39b3fa1ea26 | |
parent | 3f4b51b99a4f4dc41dbdce7f34afe7e15d3d426e (diff) |
POST login possible
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Readme.md | 2 | ||||
-rw-r--r-- | src/App.php | 1 | ||||
-rw-r--r-- | src/Controllers/LoginController.php | 25 | ||||
-rw-r--r-- | src/ErrorResponse.php | 16 | ||||
-rw-r--r-- | src/Errors/ErrorCode.php (renamed from src/ErrorCode.php) | 2 | ||||
-rw-r--r-- | src/Errors/ErrorResponse.php | 30 | ||||
-rw-r--r-- | src/Errors/Exception.php | 21 | ||||
-rw-r--r-- | src/Errors/RateLimitError.php | 20 | ||||
-rw-r--r-- | src/Errors/UnknownError.php | 16 | ||||
-rw-r--r-- | src/Router/Router.php (renamed from src/Router.php) | 23 | ||||
-rw-r--r-- | src/Router/routes_client_server.php (renamed from src/routes.php) | 18 | ||||
-rw-r--r-- | src/Router/routes_server_server.php | 24 | ||||
-rw-r--r-- | src/Support/Parser.php | 27 | ||||
-rw-r--r-- | src/Types/AuthenticationType.php | 14 |
15 files changed, 202 insertions, 40 deletions
@@ -3,4 +3,5 @@ .env -credentials.json +/credentials.json +/store/ @@ -1 +1 @@ -Matrix Specification: https://spec.matrix.org/v1.5/ +Matrix Specification: https://spec.matrix.org/v1.15/ diff --git a/src/App.php b/src/App.php index 33f71ef..9b1edf9 100644 --- a/src/App.php +++ b/src/App.php @@ -2,6 +2,7 @@ namespace App; +use App\Router\Router; use Symfony\Component\Dotenv\Dotenv; class App diff --git a/src/Controllers/LoginController.php b/src/Controllers/LoginController.php index f5ca3be..d48628b 100644 --- a/src/Controllers/LoginController.php +++ b/src/Controllers/LoginController.php @@ -2,6 +2,8 @@ namespace App\Controllers; +use App\Errors\UnknownError; +use App\Support\Parser; use App\Types\LoginFlow; use App\Types\LoginType; use Symfony\Component\HttpFoundation\Request; @@ -10,6 +12,10 @@ use Symfony\Component\HttpFoundation\JsonResponse; class LoginController { + /** + * GET /_matrix/client/r0/login + * GET /_matrix/client/v3/login + */ public function supportedLoginTypes(): Response { return new JsonResponse([ @@ -19,16 +25,33 @@ class LoginController ]); } + /** + * POST /_matrix/client/v3/login + */ public function login(): Response { $request = Request::createFromGlobals(); + $content = json_decode($request->getContent(), true); + + // validate login type + $loginType = null; + try { + $loginType = LoginType::from($content["type"]); + } catch (\ValueError $error) { + throw new UnknownError("Bad login type.", Response::HTTP_BAD_REQUEST); + } + + // get user name + $user = Parser::parseUser($content["identifier"]["user"]); + + #if ($loginType == LoginType::PASSWORD) {} return new JsonResponse([ "access_token" => "abc123", "device_id" => "ABC", "expires_in_ms" => 60000, "refresh_token" => "def456", - "user_id" => "@php:localhost", + "user_id" => "@{$user["username"]}:{$_ENV["DOMAIN"]}", #"well_known" => [], ]); } diff --git a/src/ErrorResponse.php b/src/ErrorResponse.php deleted file mode 100644 index 05a77d1..0000000 --- a/src/ErrorResponse.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -namespace App; - -use Symfony\Component\HttpFoundation\JsonResponse; - -class ErrorResponse extends JsonResponse -{ - public function __construct(ErrorCode $code, string $message) - { - parent::__construct([ - "errcode" => $code, - "error" => $message, - ]); - } -} diff --git a/src/ErrorCode.php b/src/Errors/ErrorCode.php index 2149d37..b59f0a1 100644 --- a/src/ErrorCode.php +++ b/src/Errors/ErrorCode.php @@ -1,6 +1,6 @@ <?php -namespace App; +namespace App\Errors; enum ErrorCode: string { diff --git a/src/Errors/ErrorResponse.php b/src/Errors/ErrorResponse.php new file mode 100644 index 0000000..6248659 --- /dev/null +++ b/src/Errors/ErrorResponse.php @@ -0,0 +1,30 @@ +<?php + +namespace App\Errors; + +use Symfony\Component\HttpFoundation\JsonResponse; + +class ErrorResponse extends JsonResponse +{ + public function __construct(ErrorCode $code, string $message, int $httpCode) + { + parent::__construct( + [ + "errcode" => $code, + "error" => $message, + ], + $httpCode + ); + } + + public static function fromException(Exception $exception): self + { + $self = new self($exception->getErrorCode(), $exception->getMessage(), $exception->getCode()); + + $self->setData( + json_decode($self->data, true) + $exception->getAdditionalData() + ); + + return $self; + } +} diff --git a/src/Errors/Exception.php b/src/Errors/Exception.php new file mode 100644 index 0000000..ccb124e --- /dev/null +++ b/src/Errors/Exception.php @@ -0,0 +1,21 @@ +<?php + +namespace App\Errors; + +abstract class Exception extends \RuntimeException +{ + public function __construct(private ErrorCode $errorCode, string $message, int $httpCode) + { + parent::__construct($message, $httpCode); + } + + public function getErrorCode(): ErrorCode + { + return $this->errorCode; + } + + /** + * @return void + */ + abstract public function getAdditionalData(): array; +} diff --git a/src/Errors/RateLimitError.php b/src/Errors/RateLimitError.php new file mode 100644 index 0000000..d84f66a --- /dev/null +++ b/src/Errors/RateLimitError.php @@ -0,0 +1,20 @@ +<?php + +namespace App\Errors; + +use Symfony\Component\HttpFoundation\Response; + +class RateLimitError extends Exception +{ + public function __construct(private int $retryAfter) + { + parent::__construct(ErrorCode::LIMIT_EXCEEDED, "Too many requests", Response::HTTP_TOO_MANY_REQUESTS); + } + + public function getAdditionalData(): array + { + return [ + "retry_after_ms" => $this->retryAfter, + ]; + } +} diff --git a/src/Errors/UnknownError.php b/src/Errors/UnknownError.php new file mode 100644 index 0000000..f861597 --- /dev/null +++ b/src/Errors/UnknownError.php @@ -0,0 +1,16 @@ +<?php + +namespace App\Errors; + +class UnknownError extends Exception +{ + public function __construct(string $message, int $httpCode) + { + parent::__construct(ErrorCode::UNKNOWN, $message, $httpCode); + } + + public function getAdditionalData(): array + { + return []; + } +} diff --git a/src/Router.php b/src/Router/Router.php index 437d995..b167142 100644 --- a/src/Router.php +++ b/src/Router/Router.php @@ -1,9 +1,15 @@ <?php -namespace App; +namespace App\Router; +use App\Errors\ErrorCode; +use App\Errors\ErrorResponse; +use App\Errors\Exception; +use App\Singleton; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Exception\MethodNotAllowedException; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator; use Symfony\Component\Routing\Matcher\UrlMatcher; use Symfony\Component\Routing\RequestContext; @@ -54,8 +60,14 @@ class Router $method = $match["_controller"][1]; return (new $class)->$method(); + } catch (Exception $exception) { + return ErrorResponse::fromException($exception); + } catch (ResourceNotFoundException $exception) { + return new ErrorResponse(ErrorCode::NOT_FOUND, "404", Response::HTTP_NOT_FOUND); + } catch (MethodNotAllowedException $exception) { + return new ErrorResponse(ErrorCode::FORBIDDEN, "403", Response::HTTP_FORBIDDEN); } catch (\Exception $exception) { - return new ErrorResponse(ErrorCode::UNKNOWN, "Unknown error occured"); + return new ErrorResponse(ErrorCode::UNKNOWN, "Unknown error occured", Response::HTTP_INTERNAL_SERVER_ERROR); } } @@ -64,7 +76,10 @@ class Router */ private function addRoutes(): void { - $routes = include_once(__DIR__ . "/routes.php"); - $routes($this->configurator); + $routesClientServer = include_once(__DIR__ . "/routes_client_server.php"); + $routesClientServer($this->configurator); + + $routesServerServer = include_once(__DIR__ . "/routes_server_server.php"); + $routesServerServer($this->configurator); } } diff --git a/src/routes.php b/src/Router/routes_client_server.php index 2d1c3e0..eff0be5 100644 --- a/src/routes.php +++ b/src/Router/routes_client_server.php @@ -1,6 +1,6 @@ <?php -namespace App; +namespace App\Router; use App\Controllers\LoginController; use App\Controllers\ServerDiscoveryController; @@ -10,11 +10,6 @@ use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator; return function (RouteConfigurator $routes): void { $routes - ->add("well_known_matrix_server", "/.well-known/matrix/server") - ->controller([ServerDiscoveryController::class, "server"]) - ->methods(["GET"]); - - $routes ->add("well_known_matrix_client", "/.well-known/matrix/client") ->controller([ServerDiscoveryController::class, "client"]) ->methods(["GET"]); @@ -25,19 +20,10 @@ return function (RouteConfigurator $routes): void ->methods(["GET"]); $routes - ->add("matrix_federation_version", "/_matrix/federation/v1/version") - ->controller([ServerImplementationController::class, "version"]) - ->methods(["GET"]); - - $routes ->add("matrix_client_versions", "/_matrix/client/versions") ->controller([ServerImplementationController::class, "versions"]) ->methods(["GET"]); - # /_matrix/key/v2/server - # /_matrix/key/v2/query - # /_matrix/key/v2/query/{serverName} - $supportedLoginTypes = [LoginController::class, "supportedLoginTypes"]; $routes ->add("matrix_client_r0_login_types", "/_matrix/client/r0/login") @@ -50,6 +36,6 @@ return function (RouteConfigurator $routes): void $routes ->add("matrix_client_v3_login", "/_matrix/client/v3/login") - ->controller($supportedLoginTypes) + ->controller([LoginController::class, "login"]) ->methods(["POST"]); }; diff --git a/src/Router/routes_server_server.php b/src/Router/routes_server_server.php new file mode 100644 index 0000000..2e85a17 --- /dev/null +++ b/src/Router/routes_server_server.php @@ -0,0 +1,24 @@ +<?php + +namespace App\Router; + +use App\Controllers\ServerDiscoveryController; +use App\Controllers\ServerImplementationController; +use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator; + +return function (RouteConfigurator $routes): void +{ + $routes + ->add("well_known_matrix_server", "/.well-known/matrix/server") + ->controller([ServerDiscoveryController::class, "server"]) + ->methods(["GET"]); + + $routes + ->add("matrix_federation_version", "/_matrix/federation/v1/version") + ->controller([ServerImplementationController::class, "version"]) + ->methods(["GET"]); + + # /_matrix/key/v2/server + # /_matrix/key/v2/query + # /_matrix/key/v2/query/{serverName} +}; diff --git a/src/Support/Parser.php b/src/Support/Parser.php new file mode 100644 index 0000000..d850de1 --- /dev/null +++ b/src/Support/Parser.php @@ -0,0 +1,27 @@ +<?php + +namespace App\Support; + +class Parser +{ + /** + * @return array<string, string> + */ + public static function parseUser(string $user): array + { + $username = $user; + $server = ""; + + if (str_starts_with($user, "@")) { + $username = substr($user, 1); + $usernameParts = explode(":", $username); + $username = $usernameParts[0]; + $server = $usernameParts[1]; + } + + return [ + "username" => $username, + "server" => $server, + ]; + } +} diff --git a/src/Types/AuthenticationType.php b/src/Types/AuthenticationType.php new file mode 100644 index 0000000..dc768d0 --- /dev/null +++ b/src/Types/AuthenticationType.php @@ -0,0 +1,14 @@ +<?php + +namespace App\Types; + +enum AuthenticationType: string +{ + case PASSWORD = "m.login.password"; + case RECAPTCHA = "m.login.recaptcha"; + case SSO = "m.login.sso"; + case EMAIL_IDENTITY = "m.login.email.identity"; + case MSISDN = "m.login.msisdn"; + case DUMMY = "m.login.dummy"; + case REGISTRATION_TOKEN = "m.login.registration_token"; +} |