- Merging ulmus-auth branch into master

This commit is contained in:
mcndave committed 2026-10-19 16:18:01 -04:00
commit ca4b5c98a1
14 files changed
+122 -53

No files matched your search

+32 -14
View File
@@ -1,9 +1,17 @@
<?php <?php
use Ulmus\User\{Entity, use Lean\Factory\HttpFactoryInterface;
use Ulmus\User\{
Authorize\CookieAuthentication,
Authorize\HeaderAuthentication,
Entity,
Lib\Authenticate, Lib\Authenticate,
Lib\Authorize, Lib\Authorize,
Middleware\AuthenticationMiddleware,
Middleware\HeaderAuthenticationMiddleware, Middleware\HeaderAuthenticationMiddleware,
Middleware\CookieAuthenticationMiddleware,
Middleware\PostRequestAuthenticationMiddleware, Middleware\PostRequestAuthenticationMiddleware,
Middleware\LocalMacAddressMiddleware, Middleware\LocalMacAddressMiddleware,
Authorize\MacAddressAuthentication, Authorize\MacAddressAuthentication,
@@ -13,18 +21,27 @@ use Picea\Picea;
use Storage\{ Cookie, Session }; use Storage\{ Cookie, Session };
use function DI\{get, create}; use Psr\Container\ContainerInterface;
use function DI\{get, autowire, create};
return [ return [
Authenticate::class => create(Authenticate::class)->constructor(get(Entity\UserInterface::class), get(Session::class), get(Cookie::class), get('authentication.method')),
Authorize::class => create(Authorize::class)->constructor(get(Entity\UserInterface::class), get(Session::class), get(Cookie::class), get('authentication.method')), Authorize::class => create(Authorize::class)->constructor(get(Entity\UserInterface::class), get(Session::class), get(Cookie::class), get('authentication.method')),
HeaderAuthenticationMiddleware::class => create(HeaderAuthenticationMiddleware::class)->constructor(get(Authorize::class), get(Entity\UserInterface::class), get('authorize.error')), Authenticate::class => create(Authenticate::class)->constructor(get(Entity\UserInterface::class), get(Session::class), get(Cookie::class), get('authentication.method')),
PostRequestAuthentication::class => create(PostRequestAuthentication::class)->constructor(get(Authenticate::class), "email", "email", "password"), AuthenticationMiddleware::class => create(AuthenticationMiddleware::class)->constructor(get(Authorize::class), get('authorize.error')),
PostRequestAuthenticationMiddleware::class => create(PostRequestAuthenticationMiddleware::class)->constructor(get(Entity\UserInterface::class), get('authentication.error'), get(PostRequestAuthentication::class)), HeaderAuthentication::class => autowire(HeaderAuthentication::class),
HeaderAuthenticationMiddleware::class => create(HeaderAuthenticationMiddleware::class)->constructor(get(HeaderAuthentication::class)),
CookieAuthentication::class => autowire(CookieAuthentication::class),
CookieAuthenticationMiddleware::class => create(CookieAuthenticationMiddleware::class)->constructor(get(CookieAuthentication::class), get(Cookie::class)),
PostRequestAuthentication::class => create(PostRequestAuthentication::class)->constructor("email", "email", "password"),
PostRequestAuthenticationMiddleware::class => create(PostRequestAuthenticationMiddleware::class)->constructor(get(PostRequestAuthentication::class)),
MacAddressAuthentication::class => create(MacAddressAuthentication::class)->constructor(get(Authenticate::class), "mac"), MacAddressAuthentication::class => create(MacAddressAuthentication::class)->constructor(get(Authenticate::class), "mac"),
@@ -32,18 +49,19 @@ return [
'authentication.method' => null, 'authentication.method' => null,
'authorize.error' => function($c) { 'authorize.error' => function(ContainerInterface $c) {
return function(array $errorData) use ($c) { return function(array $errorData) use ($c) {
return $c->get(\Lean\Factory\HttpFactoryInterface::class)::createJsonResponse($errorData + [ return $c->get(HttpFactoryInterface::class)::createJsonResponse($errorData + [
'api.error' => "Authorization failed", 'api.error' => "Authorization failed",
'api.datetime' => (new \DateTime)->format(\DateTime::ATOM), 'api.datetime' => (new \DateTime)->format(\DateTime::ATOM),
], 403); ], 403);
}; };
}, },
'authentication.error' => function($c, Picea $picea) { 'authentication.error' => function($c, Picea $picea) {
return function($message) use ($picea, $c) { return function($message) use ($c, $picea) {
return $c->get(\Lean\Factory\HttpFactoryInterface::class)::createHtmlResponse($picea->renderHtml('lean/error/500', [ return $c->get(HttpFactoryInterface::class)::createHtmlResponse($picea->renderHtml('lean/error/500', [
'title' => "Authentication failed", 'title' => "Authentication failed",
'subtitle' => "", 'subtitle' => "",
'message' => $message, 'message' => $message,
+37 -1
View File
@@ -1,11 +1,18 @@
<?php <?php
use Lean\ApplicationStrategy\{ ThrowableHandler, ThrowableHandlerInterface, NotFoundDecoratorInterface, NotFoundDecorator, MethodNotAllowedInterface, }; use Lean\ApplicationStrategy\{ ThrowableHandler, ThrowableHandlerInterface, NotFoundDecoratorInterface, NotFoundDecorator, MethodNotAllowedInterface, };
use function DI\autowire, DI\create, DI\get;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Laminas\{ Diactoros\ServerRequestFactory, HttpHandlerRunner\Emitter\EmitterInterface, HttpHandlerRunner\Emitter\SapiEmitter }; use Laminas\{ Diactoros\ServerRequestFactory, HttpHandlerRunner\Emitter\EmitterInterface, HttpHandlerRunner\Emitter\SapiEmitter };
use Lean\Factory\{ HttpFactory, HttpFactoryInterface };
use Picea\Picea;
use Storage\Cookie,
Storage\Session,
Storage\SessionMiddleware;
use function DI\{ autowire, create, get };
return [ return [
ServerRequestInterface::class => function ($c) { ServerRequestInterface::class => function ($c) {
@@ -19,4 +26,33 @@ return [
NotFoundDecoratorInterface::class => autowire(NotFoundDecorator::class), NotFoundDecoratorInterface::class => autowire(NotFoundDecorator::class),
# MethodNotAllowedInterface::class => autowire(MethodNotAllowedDecorator::class), # MethodNotAllowedInterface::class => autowire(MethodNotAllowedDecorator::class),
HttpFactoryInterface::class => create(HttpFactory::class),
Cookie::class => create(Cookie::class)->constructor([
'secure' => true,
'path' => getenv('URL_BASE') ?: '/',
'httponly' => true,
'samesite'=> "Lax",
], getenv("LEAN_RANDOM")),
Session::class => create(Session::class)->constructor(get(Cookie::class), [ 'path' => getenv('URL_BASE') ?: '/', ]),
SessionMiddleware::class => create(SessionMiddleware::class)->constructor(get(Cookie::class), [
'name' => "lean_sess_" . substr(md5(getenv("LEAN_RANDOM")), 0, 12),
'path' => getenv('URL_BASE') ?: '/',
'samesite' => getenv("LEAN_COOKIE_SAMESITE") ?? "Lax",
'secure' => (bool) getenv('LEAN_COOKIE_SECURE') ?? false
]),
'error.401' => function(ContainerInterface $c, Picea $picea) {
return $c->get(HttpFactoryInterface::class)::createHtmlResponse($picea->renderHtml("lean/error/401", []), 401);
},
'error.404' => function(ContainerInterface $c, Picea $picea) {
return $c->get(HttpFactoryInterface::class)::createHtmlResponse($picea->renderHtml("lean/error/404", []), 404);
},
'error.500' => function(ContainerInterface $c, Picea $picea) {
return $c->get(HttpFactoryInterface::class)::createHtmlResponse($picea->renderHtml("lean/error/500", []), 500);
},
]; ];
+7 -1
View File
@@ -15,6 +15,11 @@ use Notes\Breadcrumb\Breadcrumb;
use Storage\SessionMiddleware; use Storage\SessionMiddleware;
use Ulmus\User\Middleware\{
HeaderAuthenticationMiddleware,
PostRequestAuthenticationMiddleware
};
use Psr\Http\Message\{ ResponseInterface, ServerRequestInterface }; use Psr\Http\Message\{ ResponseInterface, ServerRequestInterface };
return [ return [
@@ -65,7 +70,8 @@ return [
} }
} }
$router->addPatternMatcher('email', '[^@ \t\r\n]+@[^@ \t\r\n]+\.[^@ \t\r\n/]+'); $router->addPatternMatcher('email', '[^@ \t\r\n/]+@[^@ \t\r\n/]+\.[^@ \t\r\n/]+');
$router->addPatternMatcher('ids', '\b\d[\d,]*\b');
$routing = $container->get(Lean\Routing::class); $routing = $container->get(Lean\Routing::class);
$routing->registerRoute($container, getenv('URL_BASE')); $routing->registerRoute($container, getenv('URL_BASE'));
-9
View File
@@ -6,9 +6,6 @@ use TheBugs\JavascriptMiddleware;
use Lean\Lean; use Lean\Lean;
use Storage\Cookie,
Storage\Session,
Storage\SessionMiddleware;
use function DI\autowire, DI\create, DI\get, DI\add; use function DI\autowire, DI\create, DI\get, DI\add;
@@ -78,12 +75,6 @@ return [
JavascriptMiddleware::class => create(JavascriptMiddleware::class), JavascriptMiddleware::class => create(JavascriptMiddleware::class),
Cookie::class => create(Cookie::class)->constructor([ 'secure' => true, 'path' => getenv('URL_BASE') ?: '/', ], getenv("LEAN_RANDOM")),
Session::class => create(Session::class)->constructor(get(Cookie::class), [ 'path' => getenv('URL_BASE') ?: '/', ]),
SessionMiddleware::class => create(SessionMiddleware::class)->constructor(get(Cookie::class), [ 'name' => "lean_sess_" . substr(md5(getenv("LEAN_RANDOM")), 0, 12), 'path' => getenv('URL_BASE') ?: '/' ]),
'git.commit' => function($c) { 'git.commit' => function($c) {
if ( getenv("DEBUG") ) { if ( getenv("DEBUG") ) {
return uniqid("debug_"); return uniqid("debug_");
Executable → Regular
View File
File mode changed.
-1
View File
@@ -37,7 +37,6 @@
"mcnd/picea-ui": "dev-master", "mcnd/picea-ui": "dev-master",
"mcnd/picea-asset": "~1.0.2", "mcnd/picea-asset": "~1.0.2",
"mcnd/cronard": "dev-master", "mcnd/cronard": "dev-master",
"mcnd/lean-api": "dev-master",
"mcnd/lean-console": "~1.3.2", "mcnd/lean-console": "~1.3.2",
"mcnd/kash": "dev-master", "mcnd/kash": "dev-master",
"mcnd/taxus": "dev-master", "mcnd/taxus": "dev-master",
+1 -1
View File
@@ -10,7 +10,7 @@ return [
], ],
'keys' => (function() { 'keys' => (function() {
return explode(',', getenv('KEYS') ?? "") ; return explode(',', getenvonce('KEYS') ?? "") ;
})(), })(),
'meta' => [ 'meta' => [
+8 -5
View File
@@ -4,6 +4,7 @@ use function DI\autowire, DI\create, DI\get;
use %NAMESPACE%\Entity; use %NAMESPACE%\Entity;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Ulmus\Entity\Field\Datetime, use Ulmus\Entity\Field\Datetime,
@@ -21,16 +22,18 @@ use TheBugs\Email\{ EmailConfiguration, MailerInterface, SwiftMailer };
use Taxus\{ Taxus, PermissionGrantInterface }; use Taxus\{ Taxus, PermissionGrantInterface };
use Lean\Factory\HttpFactoryInterface;
return [ return [
\Ulmus\User\Entity\UserInterface::class => autowire(Entity\User::class), \Ulmus\User\Entity\UserInterface::class => autowire(Entity\User::class),
SecurityHandler::class => create(SecurityHandler::class)->constructor(function() { SecurityHandler::class => create(SecurityHandler::class)->constructor(function(ContainerInterface $c) {
return Lean\Factory\HttpFactory::createRedirectResponse(getenv("URL_BASE")."/login"); return $c->get(HttpFactoryInterface::class)::createRedirectResponse(getenv("URL_BASE")."/login");
}, get('authentication.unauthorize'), get(Taxus::class)), }, get('authentication.unauthorize'), get(Taxus::class)),
'authentication.unauthorize' => function($c, Picea $picea) { 'authentication.unauthorize' => function(ContainerInterface $c, Picea $picea) {
return function($message) use ($picea) { return function($message) use ($c, $picea) {
return Lean\Factory\HttpFactory::createHtmlResponse($picea->renderHtml('lean/error/401', [ return $c->get(HttpFactoryInterface::class)::createHtmlResponse($picea->renderHtml('lean/error/401', [
'title' => "", 'title' => "",
'subtitle' => "", 'subtitle' => "",
'message' => $message, 'message' => $message,
+4 -4
View File
@@ -4,7 +4,7 @@ use Picea\Picea;
use Negundo\Client\{ NegundoMiddleware, SoftwareConfig }; use Negundo\Client\{ NegundoMiddleware, SoftwareConfig };
use Lean\Factory\HttpFactory; use Lean\Factory\HttpFactoryInterface;
use Psr\Http\Server\MiddlewareInterface, use Psr\Http\Server\MiddlewareInterface,
Psr\Http\Message\ServerRequestInterface, Psr\Http\Message\ServerRequestInterface,
@@ -31,11 +31,11 @@ return [
"errorHandler" => create(NegundoMiddleware::class)->constructor(get(SoftwareConfig::class), null, get('app.errorhandler.html')), "errorHandler" => create(NegundoMiddleware::class)->constructor(get(SoftwareConfig::class), null, get('app.errorhandler.html')),
'app.errorhandler.html' => function($c, Picea $picea) { 'app.errorhandler.html' => function(\Psr\Container\ContainerInterface $c, Picea $picea) {
return function(\Throwable $exception) use ($picea) { return function(\Throwable $exception) use ($c, $picea) {
error_log($exception->getMessage()); error_log($exception->getMessage());
return HttpFactory::createHtmlResponse($picea->renderHtml('lean/error/500', [ return $c->get(HttpFactoryInterface::class)::createHtmlResponse($picea->renderHtml('lean/error/500', [
'title' => "Une erreur s'est produite lors de l'exécution du script.", 'title' => "Une erreur s'est produite lors de l'exécution du script.",
'subtitle' => "Êtes-vous connecté avec le bon compte ?", 'subtitle' => "Êtes-vous connecté avec le bon compte ?",
'message' => $exception->getMessage(), 'message' => $exception->getMessage(),
@@ -39,7 +39,7 @@ class NotFoundDecorator implements NotFoundDecoratorInterface
public function throw404(ServerRequestInterface $request) : ResponseInterface public function throw404(ServerRequestInterface $request) : ResponseInterface
{ {
return $this->checkAssetTrigger($request) ?: $this->httpFactory->createHtmlResponse($this->picea->renderHtml("lean/error/404", [], $this), 404); return $this->checkAssetTrigger($request) ?: $this->container->get('error.404');
} }
protected function checkAssetTrigger(ServerRequestInterface $request) : false|ResponseInterface protected function checkAssetTrigger(ServerRequestInterface $request) : false|ResponseInterface
+10 -1
View File
@@ -7,7 +7,6 @@ use Lean\Factory\HttpFactory;
use Notes\Attribute\Ignore; use Notes\Attribute\Ignore;
use Notes\Route\Attribute\Object\Route; use Notes\Route\Attribute\Object\Route;
use Notes\Security\Attribute\Security; use Notes\Security\Attribute\Security;
use Picea, Picea\Ui\Method\FormContext;
use Picea\Ui\Method\FormContextInterface; use Picea\Ui\Method\FormContextInterface;
use TheBugs\Email\MailerInterface; use TheBugs\Email\MailerInterface;
use Storage\{ Cookie, Session }; use Storage\{ Cookie, Session };
@@ -210,6 +209,16 @@ trait ControllerTrait {
return $this->picea->compiler->getExtensionFromToken('route')->buildRouteUrl($name, $parameters); return $this->picea->compiler->getExtensionFromToken('route')->buildRouteUrl($name, $parameters);
} }
#[Ignore]
public function form(FormInterface $form, ? FormContextInterface $formContext = null) : Picea\Ui\Method\FormHandler
{
if (! $this->picea ) {
throw new \Exception("A picea object must be provided on class initialization to use this method.");
}
return $this->picea->compiler->getExtensionInstanceOf(Picea\Ui\Method\Form::class)->formClass($form, $formContext);
}
#[Ignore] #[Ignore]
public function json($data, int $flags = 0) : string public function json($data, int $flags = 0) : string
{ {
-4
View File
@@ -2,9 +2,6 @@
namespace Lean\Factory; namespace Lean\Factory;
use Laminas\Diactoros\Response\{EmptyResponse, HtmlResponse, JsonResponse, RedirectResponse, TextResponse};
use Lean\Response\{ DownloadResponse, ImageResponse, FileDownloadResponse, PdfResponse };
use Laminas\Diactoros\Response;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface; use Psr\Http\Message\StreamInterface;
@@ -20,6 +17,5 @@ interface HttpFactoryInterface
public static function createDownloadableResponse(string $data, string $filename, int $code = 200, array $headers = []) : ResponseInterface; public static function createDownloadableResponse(string $data, string $filename, int $code = 200, array $headers = []) : ResponseInterface;
public static function createImageResponse(string $data, int $code = 200, array $headers = []) : ResponseInterface; public static function createImageResponse(string $data, int $code = 200, array $headers = []) : ResponseInterface;
public static function createFileDownloadResponse(string $path, int $code = 200, array $headers = []) : ResponseInterface; public static function createFileDownloadResponse(string $path, int $code = 200, array $headers = []) : ResponseInterface;
public static function createStream(string $content) : StreamInterface; public static function createStream(string $content) : StreamInterface;
} }
+17 -11
View File
@@ -11,13 +11,16 @@ use League\Route\Strategy\ApplicationStrategy;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Tell\I18n;
use Ulmus\Container\AdapterProxy;
use Laminas\Diactoros\ServerRequestFactory, use Laminas\Diactoros\ServerRequestFactory,
Laminas\HttpHandlerRunner\Emitter\EmitterInterface; Laminas\HttpHandlerRunner\Emitter\EmitterInterface;
use Ulmus\Ulmus; use Ulmus\Ulmus;
class Kernel class Kernel {
{
protected Container $container; protected Container $container;
protected string $locale; protected string $locale;
@@ -34,10 +37,12 @@ class Kernel
public function __construct( public function __construct(
public string $projectPath public string $projectPath
) ) {
{
# Still not sure about adding a folder check each call just to add a basic project setup error ... # Still not sure about adding a folder check each call just to add a basic project setup error ...
# File::assertFolderExists($this->projectPath); # File::assertFolderExists($this->projectPath);
define('IS_CLI', php_sapi_name() === 'cli' OR defined('STDIN'));
$this->setEnvironment(); $this->setEnvironment();
$this->initializeEngine(); $this->initializeEngine();
$this->setupContainer(); $this->setupContainer();
@@ -74,12 +79,14 @@ class Kernel
// Paths and directories // Paths and directories
foreach($this->paths as $name => $envkey) { foreach($this->paths as $name => $envkey) {
if (!getenv($name)) { if ( ! getenv($name) ) {
$path = getenv("PROJECT_PATH") . DIRECTORY_SEPARATOR . getenv($envkey); $path = getenv("PROJECT_PATH") . DIRECTORY_SEPARATOR . getenv($envkey);
if (getenv('DEBUG') && ! file_exists($path)) { if (getenv('DEBUG') && ! file_exists($path)) {
if (false === mkdir($path, 0755, true)) { if (false === @mkdir($path, 0755, true)) {
throw new \Exception(sprintf("Folder '%s' could not be created", $path)); if (! IS_CLI) {
throw new \Exception(sprintf("Folder '%s' could not be created", $path));
}
} }
} }
@@ -96,7 +103,7 @@ class Kernel
setlocale(LC_ALL, $this->locale = getenv("DEFAULT_LOCAL")); setlocale(LC_ALL, $this->locale = getenv("DEFAULT_LOCAL"));
setlocale(LC_TIME, getenv("DEFAULT_TIME"), getenv("DEFAULT_TIME_FALLBACK")); setlocale(LC_TIME, getenv("DEFAULT_TIME"), getenv("DEFAULT_TIME_FALLBACK"));
if (class_exists('Locale')) { if ( class_exists('Locale') ) {
\Locale::setDefault($this->locale); \Locale::setDefault($this->locale);
} }
@@ -122,8 +129,7 @@ class Kernel
if (getenv("APP_ENV") === "prod") { if (getenv("APP_ENV") === "prod") {
if (getenv("CACHE_PATH")) { if (getenv("CACHE_PATH")) {
# check if ACPU is there first, # check if ACPU is there first, $containerBuilder->enableDefinitionCache();
# $containerBuilder->enableDefinitionCache(); # Do not work .. ?
$containerBuilder->enableCompilation(getenv("CACHE_PATH") . "/di/"); $containerBuilder->enableCompilation(getenv("CACHE_PATH") . "/di/");
# No proxies yet... $containerBuilder->writeProxiesToFile(true, getenv("CACHE_PATH") . "/di/"); # No proxies yet... $containerBuilder->writeProxiesToFile(true, getenv("CACHE_PATH") . "/di/");
@@ -190,4 +196,4 @@ class Kernel
return $this; return $this;
} }
} };
+5
View File
@@ -44,6 +44,11 @@ class Routing {
$container->set(ServerRequestInterface::class, $request); $container->set(ServerRequestInterface::class, $request);
# Decoding arguments received from the router
foreach($arguments as &$arg) {
$arg = urldecode($arg);
}
return $this->response ?? $object->$method($request, $arguments); return $this->response ?? $object->$method($request, $arguments);
}); });
} }