Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7769b1a1ca | ||
|
|
1559ceb248 | ||
|
|
c7b04d7e46 | ||
|
|
c6fc5d020b | ||
|
|
944c524cfb | ||
|
|
fbf9bc871c | ||
|
|
ad802fc35d | ||
|
|
4664a8779a | ||
|
|
cef536caa5 | ||
|
|
10ad77d607 | ||
|
|
015f0c70b1 | ||
|
|
267fe5cadd |
No files matched your search
+8
-2
@@ -6,12 +6,18 @@
|
|||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Dave Mc Nicoll",
|
"name": "Dave Mc Nicoll",
|
||||||
"email": "mcndave@gmail.com"
|
"email": "info@mcnd.ca"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"mcnd/notes": "master-dev"
|
"mcnd/ulmus": "dev-master"
|
||||||
},
|
},
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "vcs",
|
||||||
|
"url": "https://git.mcnd.ca/mcNdave/ulmus.git"
|
||||||
|
}
|
||||||
|
],
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Ulmus\\User\\": "src/"
|
"Ulmus\\User\\": "src/"
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\User\Authorize;
|
||||||
|
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Ulmus\User\Entity\UserInterface;
|
||||||
|
|
||||||
|
interface AuthorizeMethodInterface
|
||||||
|
{
|
||||||
|
public function connect(ServerRequestInterface $request, UserInterface $user) : UserInterface|false;
|
||||||
|
|
||||||
|
public function catchRequest(ServerRequestInterface $request) : bool;
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\User\Authorize;
|
||||||
|
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Ulmus\User\Common\AuthorizeContentTypeEnum;
|
||||||
|
use Ulmus\User\Entity\User;
|
||||||
|
use Ulmus\User\Entity\UserInterface;
|
||||||
|
use Ulmus\User\Lib\Authenticate;
|
||||||
|
|
||||||
|
class HeaderAuthentication implements AuthorizeMethodInterface
|
||||||
|
{
|
||||||
|
public function connect(ServerRequestInterface $request, UserInterface $user): UserInterface|false
|
||||||
|
{
|
||||||
|
if ( null !== ( $auth = $request->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\User\Authorize;
|
||||||
|
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Ulmus\User\Entity\UserInterface;
|
||||||
|
use Ulmus\User\Lib\Authenticate;
|
||||||
|
|
||||||
|
class PostRequestAuthentication implements AuthorizeMethodInterface
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected Authenticate $authenticate,
|
||||||
|
protected string $fieldUser = "username",
|
||||||
|
protected string $postFieldUser = "username",
|
||||||
|
protected string $postFieldPassword = "password",
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function connect(ServerRequestInterface $request, UserInterface $user): UserInterface|false
|
||||||
|
{
|
||||||
|
$post = $request->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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\User\Common;
|
||||||
|
|
||||||
|
enum AuthorizeContentTypeEnum: string
|
||||||
|
{
|
||||||
|
case ApplicationJson = "application/json";
|
||||||
|
case ApplicationLDJson = "application/+json";
|
||||||
|
case ApplicationVndApiJson = "application/vnd.api+json";
|
||||||
|
}
|
||||||
+15
-9
@@ -2,11 +2,12 @@
|
|||||||
|
|
||||||
namespace Ulmus\User\Entity;
|
namespace Ulmus\User\Entity;
|
||||||
|
|
||||||
|
use Ulmus\Entity\EntityInterface;
|
||||||
use Ulmus\Entity\Field\Datetime;
|
use Ulmus\Entity\Field\Datetime;
|
||||||
|
|
||||||
use Ulmus\Attribute\Property\Field;
|
use Ulmus\Attribute\Property\Field;
|
||||||
|
|
||||||
class User {
|
abstract class User implements UserInterface {
|
||||||
|
|
||||||
#[Field\Id(readonly: true)]
|
#[Field\Id(readonly: true)]
|
||||||
public int $id;
|
public int $id;
|
||||||
@@ -47,40 +48,45 @@ class User {
|
|||||||
#[Field]
|
#[Field]
|
||||||
public string $password;
|
public string $password;
|
||||||
|
|
||||||
#[Field\UpdatedAt(readonly: true, name: "updated_at")]
|
#[Field\UpdatedAt(name: "updated_at", readonly: true)]
|
||||||
public ? Datetime $updatedAt;
|
public ? Datetime $updatedAt;
|
||||||
|
|
||||||
#[Field\CreatedAt(readonly: true, name: "created_at")]
|
#[Field\CreatedAt(name: "created_at", readonly: true)]
|
||||||
public Datetime $createdAt;
|
public Datetime $createdAt;
|
||||||
|
|
||||||
public bool $logged = false;
|
public bool $logged = false;
|
||||||
|
|
||||||
public function __toString() : string
|
public function __toString() : string
|
||||||
{
|
{
|
||||||
return $this->fullname();
|
return $this->fullName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setPassword($password) : self
|
public function setPassword($password) : static
|
||||||
{
|
{
|
||||||
$this->password = $password;
|
$this->password = $password;
|
||||||
|
|
||||||
return $this->hashPassword();
|
return $this->hashPassword();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hashPassword() : self
|
public function hashPassword(? string $password = null) : static
|
||||||
{
|
{
|
||||||
$this->password = password_hash($this->password, PASSWORD_DEFAULT);
|
$this->password = password_hash($password ?: $this->password, PASSWORD_DEFAULT);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function verifyPassword(string $password) : bool
|
public function verifyPassword(string $password) : bool
|
||||||
{
|
{
|
||||||
return password_verify($password, $this->password );
|
return password_verify($password, $this->password);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function fullname() : string
|
public function fullName() : string
|
||||||
{
|
{
|
||||||
return trim( ( $this->firstName ?? "" ) . " " . ( $this->lastName ?? "" ) );
|
return trim( ( $this->firstName ?? "" ) . " " . ( $this->lastName ?? "" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function loggedIn(): bool
|
||||||
|
{
|
||||||
|
return $this->logged;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\User\Entity;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -10,34 +10,25 @@ use Ulmus\User\Entity\User;
|
|||||||
use Ulmus\Exception;
|
use Ulmus\Exception;
|
||||||
|
|
||||||
class Authenticate {
|
class Authenticate {
|
||||||
|
|
||||||
protected ? Session $session;
|
|
||||||
|
|
||||||
protected ? Cookie $cookie;
|
|
||||||
|
|
||||||
protected bool $logged = false;
|
protected bool $logged = false;
|
||||||
|
|
||||||
protected Closure $authenticationEvent;
|
protected Closure $authenticationEvent;
|
||||||
|
|
||||||
public ? User $user = null;
|
public ? User $user = null;
|
||||||
|
|
||||||
public string $connection_fields = 'email';
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
? Session $session = null,
|
protected ? Session $session = null,
|
||||||
? Cookie $cookie = null,
|
protected ? Cookie $cookie = null,
|
||||||
? Closure $authenticationEvent = null
|
? Closure $authenticationEvent = null
|
||||||
) {
|
) {
|
||||||
$this->session = $session;
|
$this->authenticationEvent = $authenticationEvent ?: fn(bool $authenticated, string $message, ? User $user, array $data = []) : ? bool => null;
|
||||||
$this->cookie = $cookie;
|
|
||||||
$this->authenticationEvent = $authenticationEvent ?: function(bool $authenticated, string $message, ? User $user, array $data = []) : ? bool {return null;} ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rememberMe(\Ulmus\Repository $repository) : ? User
|
public function rememberMe(\Ulmus\Repository $repository) : ? User
|
||||||
{
|
{
|
||||||
$logUser = function(int $id) use ($repository) {
|
$logUser = function(? int $id) use ($repository) {
|
||||||
if ( null === ( $user = $repository->loadFromPk($id) ) ) {
|
if ( $id === null || null === ( $user = $repository->loadFromPk($id) ) ) {
|
||||||
throw new \Exception("User not found.");
|
throw new \InvalidArgumentException(sprintf("User having id '%s' was not found.", $id));
|
||||||
}
|
}
|
||||||
|
|
||||||
$user->logged = true;
|
$user->logged = true;
|
||||||
@@ -110,9 +101,6 @@ class Authenticate {
|
|||||||
return $this->user;
|
return $this->user;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Force user disconnection and handle memory trashing
|
|
||||||
*/
|
|
||||||
public function logout() : self
|
public function logout() : self
|
||||||
{
|
{
|
||||||
if ( $this->session ) {
|
if ( $this->session ) {
|
||||||
@@ -123,7 +111,9 @@ class Authenticate {
|
|||||||
$this->cookie->delete('user.id');
|
$this->cookie->delete('user.id');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->user->logged = false;
|
if ( isset($this->user) ) {
|
||||||
|
$this->user->logged = false;
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\User\Lib;
|
||||||
|
|
||||||
|
use Ulmus\User\Entity\User;
|
||||||
|
|
||||||
|
class Authorize extends Authenticate
|
||||||
|
{
|
||||||
|
public function rememberMe(\Ulmus\Repository $repository) : ? User
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function authorize(\Ulmus\Repository $repository, array $userLogin, string $password) : User
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\User\Middleware;
|
||||||
|
|
||||||
|
use Psr\Http\{
|
||||||
|
Message\ResponseInterface,
|
||||||
|
Message\ServerRequestInterface,
|
||||||
|
Server\MiddlewareInterface,
|
||||||
|
Server\RequestHandlerInterface
|
||||||
|
};
|
||||||
|
use Ulmus\User\Entity\UserInterface;
|
||||||
|
use Ulmus\User\Authorize\HeaderAuthentication;
|
||||||
|
|
||||||
|
class HeaderAuthenticationMiddleware implements MiddlewareInterface
|
||||||
|
{
|
||||||
|
protected HeaderAuthentication $authenticator;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
protected UserInterface $entity,
|
||||||
|
protected \Closure $loginFailedResponse,
|
||||||
|
HeaderAuthentication $authenticator = null,
|
||||||
|
) {
|
||||||
|
$this->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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\User\Middleware;
|
||||||
|
|
||||||
|
use Psr\Http\{
|
||||||
|
Message\ResponseInterface,
|
||||||
|
Message\ServerRequestInterface,
|
||||||
|
Server\MiddlewareInterface,
|
||||||
|
Server\RequestHandlerInterface
|
||||||
|
};
|
||||||
|
use Ulmus\User\Entity\UserInterface;
|
||||||
|
use Ulmus\User\Authorize\PostRequestAuthentication;
|
||||||
|
use Ulmus\User\Lib\Authenticate;
|
||||||
|
|
||||||
|
class PostRequestAuthenticationMiddleware implements MiddlewareInterface
|
||||||
|
{
|
||||||
|
protected PostRequestAuthentication $authenticator;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
protected UserInterface $entity,
|
||||||
|
protected \Closure $loginFailedResponse,
|
||||||
|
PostRequestAuthentication $authenticator = null,
|
||||||
|
) {
|
||||||
|
$this->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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\User\Role;
|
||||||
|
|
||||||
|
interface RoleAnonymousInterface {}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\User\Role;
|
||||||
|
|
||||||
|
interface RoleIdentifiedInterface {}
|
||||||
Reference in new issue
Block a user