diff options
| -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"; +} | 
