diff --git a/src/Authorize/AuthorizeMethodInterface.php b/src/Authorize/AuthorizeMethodInterface.php index 36a43db..9bea485 100644 --- a/src/Authorize/AuthorizeMethodInterface.php +++ b/src/Authorize/AuthorizeMethodInterface.php @@ -7,5 +7,7 @@ use Ulmus\User\Entity\UserInterface; interface AuthorizeMethodInterface { - public function connect(ServerRequestInterface $request) : UserInterface|false; + public function connect(ServerRequestInterface $request, UserInterface $user) : UserInterface|false; + + public function catchRequest(ServerRequestInterface $request) : bool; } \ No newline at end of file diff --git a/src/Authorize/BasicAuthentication.php b/src/Authorize/BasicAuthentication.php deleted file mode 100644 index fc21214..0000000 --- a/src/Authorize/BasicAuthentication.php +++ /dev/null @@ -1,34 +0,0 @@ -getHeader('Authorization') ) { - list($method, $userPass) = explode(' ', $auth, 2) + [ null, null ]; - - if (! $method ) { - throw new \InvalidArgumentException("An authentication method must be provided"); - } - elseif (! $userPass ) { - throw new \InvalidArgumentException("A base64-encoded 'user:password' value must be provided"); - } - - - return false; - } - - return false; - } - - protected function basicMethod(string $header) : UserInterface|false - { - - } - -} \ No newline at end of file diff --git a/src/Authorize/HeaderAuthentication.php b/src/Authorize/HeaderAuthentication.php new file mode 100644 index 0000000..6be24c6 --- /dev/null +++ b/src/Authorize/HeaderAuthentication.php @@ -0,0 +1,65 @@ +getHeaderLine('Authorization') ) ) { + + list($method, $userPass) = explode(' ', $auth, 2) + [ null, null ]; + + switch(strtolower(strtolower($method))) { + case "basic": + return $this->basicMethod($userPass); + + default: + throw new \InvalidArgumentException("An authentication method must be provided"); + } + } + + return false; + } + + protected function basicMethod(string $userPassword) : UserInterface|false + { + if ( false === $decoded = base64_decode($userPassword) ) { + throw new \RuntimeException("Base64 decoding of given username:password failed"); + } + + list($user, $password) = explode(':', $decoded) + [ null, null ]; + + if ( empty($user) ) { + throw new \RuntimeException("A username must be provided"); + } + elseif ( empty($password) ) { + throw new \RuntimeException("A password must be provided"); + } + + $authenticate = new Authenticate(); + + + return false; + } + + public function catchRequest(ServerRequestInterface $request) : bool + { + foreach(array_merge($request->getHeader('Accept'), $request->getHeader('Content-Type')) as $accept) { + foreach(explode(',', $accept) as $contentType) { + if ( AuthorizeContentTypeEnum::tryFrom(strtolower($contentType)) ) { + return true; + } + } + } + + return false; + } + +} \ No newline at end of file diff --git a/src/Authorize/PostRequestAuthentication.php b/src/Authorize/PostRequestAuthentication.php new file mode 100644 index 0000000..02c7109 --- /dev/null +++ b/src/Authorize/PostRequestAuthentication.php @@ -0,0 +1,31 @@ +getParsedBody(); + + return $this->authenticate->authenticate($user::repository(), [ $this->fieldUser => $post[$this->postFieldUser] ], $post[$this->postFieldPassword]); + } + + public function catchRequest(ServerRequestInterface $request): bool + { + $post = $request->getParsedBody(); + + return strtoupper( $request->getMethod() ) === "POST" && isset($post[$this->postFieldUser], $post[$this->postFieldPassword]); + } +} \ No newline at end of file diff --git a/src/Common/AuthorizeContentTypeEnum.php b/src/Common/AuthorizeContentTypeEnum.php new file mode 100644 index 0000000..c7e914f --- /dev/null +++ b/src/Common/AuthorizeContentTypeEnum.php @@ -0,0 +1,10 @@ +firstName} {$this->lastName}"; + return $this->fullName(); } - public function setPassword($password) : self + public function setPassword($password) : static { $this->password = $password; return $this->hashPassword(); } - public function hashPassword(? string $password = null) : self + public function hashPassword(? string $password = null) : static { $this->password = password_hash($password ?: $this->password, PASSWORD_DEFAULT); @@ -79,8 +80,13 @@ class User { return password_verify($password, $this->password); } - public function fullname() : string + public function fullName() : string { return trim( ( $this->firstName ?? "" ) . " " . ( $this->lastName ?? "" ) ); } + + public function loggedIn(): bool + { + return $this->logged; + } } diff --git a/src/Entity/UserInterface.php b/src/Entity/UserInterface.php index 7c1b010..8ba1621 100644 --- a/src/Entity/UserInterface.php +++ b/src/Entity/UserInterface.php @@ -2,7 +2,13 @@ namespace Ulmus\User\Entity; -interface UserInterface +use Ulmus\Entity\EntityInterface; + +interface UserInterface extends EntityInterface { public function __toString() : string; + public function loggedIn() : bool; + public function verifyPassword(string $password) : bool; + public function hashPassword(? string $password = null) : static; + public function setPassword($password) : static; } \ No newline at end of file diff --git a/src/Lib/Authenticate.php b/src/Lib/Authenticate.php index f605988..3540bb7 100644 --- a/src/Lib/Authenticate.php +++ b/src/Lib/Authenticate.php @@ -10,27 +10,18 @@ use Ulmus\User\Entity\User; use Ulmus\Exception; class Authenticate { - - protected ? Session $session; - - protected ? Cookie $cookie; - protected bool $logged = false; protected Closure $authenticationEvent; public ? User $user = null; - public string $connection_fields = 'email'; - public function __construct( - ? Session $session = null, - ? Cookie $cookie = null, + protected ? Session $session = null, + protected ? Cookie $cookie = null, ? Closure $authenticationEvent = null ) { - $this->session = $session; - $this->cookie = $cookie; - $this->authenticationEvent = $authenticationEvent ?: function(bool $authenticated, string $message, ? User $user, array $data = []) : ? bool {return null;} ; + $this->authenticationEvent = $authenticationEvent ?: fn(bool $authenticated, string $message, ? User $user, array $data = []) : ? bool => null; } public function rememberMe(\Ulmus\Repository $repository) : ? User @@ -41,7 +32,7 @@ class Authenticate { } $user->logged = true; - + return $user; }; @@ -110,9 +101,6 @@ class Authenticate { return $this->user; } - /** - * Force user disconnection and handle memory trashing - */ public function logout() : self { if ( $this->session ) { @@ -123,10 +111,10 @@ class Authenticate { $this->cookie->delete('user.id'); } - if (isset($this->user)) { + if ( isset($this->user) ) { $this->user->logged = false; } return $this; } -} +} \ No newline at end of file diff --git a/src/Middleware/AuthenticateMiddleware.php b/src/Middleware/AuthenticateMiddleware.php deleted file mode 100644 index 718df86..0000000 --- a/src/Middleware/AuthenticateMiddleware.php +++ /dev/null @@ -1,29 +0,0 @@ -loginFailed; - } - - return $handler->handle($request); - } -} diff --git a/src/Middleware/AuthorizeMiddleware.php b/src/Middleware/AuthorizeMiddleware.php deleted file mode 100644 index 7b8bd13..0000000 --- a/src/Middleware/AuthorizeMiddleware.php +++ /dev/null @@ -1,26 +0,0 @@ -loginFailed; - } - - return $handler->handle($request); - } -} diff --git a/src/Middleware/HeaderAuthenticationMiddleware.php b/src/Middleware/HeaderAuthenticationMiddleware.php new file mode 100644 index 0000000..23b27f2 --- /dev/null +++ b/src/Middleware/HeaderAuthenticationMiddleware.php @@ -0,0 +1,36 @@ +authenticator = $authenticator ?: new HeaderAuthentication(); + } + + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface + { + if ( $this->authenticator->catchRequest($request) ) { + if ( ! $this->authenticator->connect($request, $this->entity) ) { + return call_user_func($this->loginFailedResponse, [ 'api.process' => "Auth failed" ]); + } + } + + return $handler->handle($request); + } +} diff --git a/src/Middleware/PostRequestAuthenticationMiddleware.php b/src/Middleware/PostRequestAuthenticationMiddleware.php new file mode 100644 index 0000000..b2b0077 --- /dev/null +++ b/src/Middleware/PostRequestAuthenticationMiddleware.php @@ -0,0 +1,37 @@ +authenticator = $authenticator ?: new PostRequestAuthentication(new Authenticate()); + } + + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface + { + if ( $this->authenticator->catchRequest($request) ) { + if ( ! $this->authenticator->connect($request, $this->entity) ) { + return call_user_func($this->loginFailedResponse, "Login failed"); + } + } + + return $handler->handle($request); + } +} diff --git a/src/Role/RoleAnonymousInterface.php b/src/Role/RoleAnonymousInterface.php new file mode 100644 index 0000000..344899b --- /dev/null +++ b/src/Role/RoleAnonymousInterface.php @@ -0,0 +1,5 @@ +