Commit 7b5d91a5 authored by Jordan Breton's avatar Jordan Breton

WIP modules integration feature + securing access by default (going through...

WIP modules integration feature + securing access by default (going through autorize packages/modules instead of restricting)
parent 04281b5a
......@@ -25,6 +25,7 @@ try{
$site = dirname(__DIR__,2)."/site";
$engine = dirname(__DIR__,2)."/engine";
$modules = dirname(__DIR__,2)."/modules";
if($argvReader->exists('-install')){
$args = $argvReader->get('-install');
......@@ -32,10 +33,21 @@ try{
$tmp = explode("/",$package);
$location = $site.'/package';
$l = 'site';
if(count($tmp)===2){
$location = ($tmp[0] === 'engine')
? $engine."/package/".($p=$tmp[1]) : "$location/".($p=$tmp[1]);
$l = $tmp[0];
$m = false;
if(count($tmp) > 1){
$l = array_shift($tmp);
$p = implode("/",$tmp);
switch($tmp[0]){
case 'engine' :
$location = "$engine/package/$p";
break;
case 'modules' :
$location = "$modules/$p";
$m = true;
break;
default :
$location = "$location/$p";
}
}
else $location = "$location/".($p = $tmp[0]);
if(is_dir($location)){
......@@ -44,7 +56,8 @@ try{
if(!is_dir("$site/webroot/$dir")) mkdir("$site/webroot/$dir");
if(is_link("$site/webroot/$dir/$p")) unlink("$site/webroot/$dir/$p");
chdir("$site/webroot/$dir");
$exec("ln -s \"../../../$l/package/$p/webroot/$dir\" \"$p\"");
if(!$m) $exec("ln -s \"../../../$l/package/$p/webroot/$dir\" \"$p\"");
else $exec("ln -s \"../../../$l/$p/webroot/$dir\" \"$p\"");
}
}else fwrite(STDOUT,"\e[33mWFW_installer UNKNOWN_PACKAGE\e[0m : $package\n");
fwrite(STDOUT,"$package installed.\n");
......
<?php
use wfw\engine\package\contact\data\model\ContactModel;
use wfw\engine\package\news\data\model\ArticleModel;
return [
ArticleModel::class,
ContactModel::class
];
\ No newline at end of file
return [];
\ No newline at end of file
......@@ -102,7 +102,7 @@ try{
$confs->getKVSContainer($name),
$confs->getKVSDefaultStorage($name) ?? null
),
require $confs->getModelsToLoadPath($name)
array_unique(require $confs->getModelsToLoadPath($name))
),
$confs->getUsers($name),
$confs->getGroups($name),
......
<?php
use wfw\engine\package\users\data\model\UserModel;
return [
UserModel::class,
\wfw\engine\package\contact\data\model\ContactModel::class,
\wfw\engine\package\news\data\model\ArticleModel::class
\wfw\engine\package\users\data\model\UserModel::class
];
\ No newline at end of file
......@@ -12,7 +12,6 @@ use wfw\engine\core\action\IAction;
use wfw\engine\core\action\IActionHook;
use wfw\engine\core\action\IActionHookFactory;
use wfw\engine\core\action\MultiHook;
use wfw\engine\core\action\NotFoundHook;
use wfw\engine\core\app\factory\DiceBasedFactory;
use wfw\engine\core\app\factory\IGenericAppFactory;
use wfw\engine\core\command\CommandHandlerFactory;
......@@ -76,8 +75,7 @@ use wfw\engine\core\security\data\sanitizer\HTMLPurifierBasedSanitizer;
use wfw\engine\core\security\data\sanitizer\IHTMLSanitizer;
use wfw\engine\core\security\IAccessControlCenter;
use wfw\engine\core\security\IAccessRuleFactory;
use wfw\engine\core\security\rules\RequireAuthentification;
use wfw\engine\core\security\rules\ValidToken;
use wfw\engine\core\security\WFWDefaultSecurityPolicy;
use wfw\engine\core\session\handlers\PHPSessionHandler;
use wfw\engine\core\session\ISession;
use wfw\engine\core\session\Session;
......@@ -106,17 +104,12 @@ use wfw\engine\package\contact\data\model\ContactModelAccess;
use wfw\engine\package\contact\data\model\IContactModelAccess;
use wfw\engine\package\contact\domain\repository\ContactRepository;
use wfw\engine\package\contact\domain\repository\IContactRepository;
use wfw\engine\package\contact\security\ContactAccessControlPolicies;
use wfw\engine\package\general\handlers\response\AjaxHandler;
use wfw\engine\package\general\handlers\response\ErrorHandler;
use wfw\engine\package\general\security\GeneralAccessControlPolicies;
use wfw\engine\package\miel\security\MielAccessControlPolicies;
use wfw\engine\package\news\data\model\ArticleModelAccess;
use wfw\engine\package\news\data\model\IArticleModelAccess;
use wfw\engine\package\news\domain\repository\ArticleRepository;
use wfw\engine\package\news\domain\repository\IArticleRepository;
use wfw\engine\package\news\security\NewsAccessControlPolicies;
use wfw\engine\package\uploader\security\UploaderAccessControlPolicies;
use wfw\engine\package\users\data\model\IUserModelAccess;
use wfw\engine\package\users\data\model\UserModelAccess;
use wfw\engine\package\users\domain\repository\IUserRepository;
......@@ -129,7 +122,6 @@ use wfw\engine\package\users\lib\mail\IUserResetPasswordMail;
use wfw\engine\package\users\lib\mail\UserMailChangedMail;
use wfw\engine\package\users\lib\mail\UserRegisteredMail;
use wfw\engine\package\users\lib\mail\UserResetPasswordMail;
use wfw\engine\package\users\security\UsersAccessControlPolicies;
/**
* Contexte de production
......@@ -175,6 +167,7 @@ class DefaultContext implements IWebAppContext {
?array $confFiles = null,
?string $projectName = null
){
//pass installed packages to layout/action/response resolver
if(is_null($projectName))
$projectName = dirname(__DIR__,4);
$genericFactory = new DiceBasedFactory($this->_dice = $dice = new Dice());
......@@ -182,29 +175,13 @@ class DefaultContext implements IWebAppContext {
//TODO : init if not set for commands and queries
$commandRules = $securityRules["command"] ?? [];
$accessRules = $securityRules["access"] ?? [];
$queryRules = $securityRules["query"] ?? [];
if(count($accessRules) === 0) $accessRules = [
RequireAuthentification::class => [
array_merge(
ContactAccessControlPolicies::REQUIRE_AUTH,
MielAccessControlPolicies::REQUIRE_AUTH,
NewsAccessControlPolicies::REQUIRE_AUTH,
UploaderAccessControlPolicies::REQUIRE_AUTH,
UsersAccessControlPolicies::REQUIRE_AUTH
),
true
],
ValidToken::class => []
];
if(count($hooks) === 0) $hooks = [
NotFoundHook::class => [
array_merge(
UsersAccessControlPolicies::RESTRICT_MODE,
GeneralAccessControlPolicies::DISABLE_ZIP_CODES
)
]
];
$accessRules = $securityRules["access"] ?? [];
if(count($accessRules) === 0) $accessRules = WFWDefaultSecurityPolicy::accessPolicy();
if(count($hooks) === 0) $hooks = WFWDefaultSecurityPolicy::hooksPolicy();
if(count($commandRules) === 0) $commandRules = WFWDefaultSecurityPolicy::commandsPolicy();
if(count($queryRules) === 0) $queryRules = WFWDefaultSecurityPolicy::queriesPolicy();
$this->_dice->addRules([
ICacheSystem::class => [
'instanceOf' => APCUBasedCache::class,
......
......@@ -10,17 +10,17 @@ abstract class Command implements ICommand {
/** @var UUID */
private $_uuid;
/** @var null|string $_userId */
private $_userId;
private $_initiatorId;
/** @var float */
private $_generationDate;
/**
* Command constructor.
*
* @param null|string $userId
* @param null|string $initiatorId
*/
public function __construct(?string $userId=null){
$this->_userId = $userId;
public function __construct(?string $initiatorId=null){
$this->_initiatorId = $initiatorId;
$this->_uuid = new UUID();
$this->_generationDate = microtime(true);
}
......@@ -35,8 +35,8 @@ abstract class Command implements ICommand {
/**
* @return null|string User ID that try to execute the command
*/
public function getUserId(): ?string {
return $this->_userId;
public function getInitiatorId(): ?string {
return $this->_initiatorId;
}
/**
......
......@@ -15,7 +15,7 @@ interface ICommand {
/**
* @return null|string Uner that initates the command (if available)
*/
public function getUserId():?string;
public function getInitiatorId():?string;
/**
* @return float
......
<?php
namespace wfw\engine\core\conf;
/**
* Allow to search for modules to collect.
*/
interface IAppModulesCollector {
/**
* @return string[] [Class => dirname ] Registered modules descriptor classes
* associated to the dirname of the module
*/
public static function modules():array;
/**
* @param string $fileName Name of register files for modules
*/
public static function collectModules(string $fileName = "module.registration.php"):void;
/**
* @param string ...$modules List of class that implements IModuleDescriptor interface.
*/
public static function registerModules(string ...$modules):void;
}
\ No newline at end of file
<?php
namespace wfw\engine\core\conf;
/**
* Interface for module configurators.
*/
interface IModuleDescriptor {
/**
* @return array Dependency injection rules to add to the general DI
*/
public static function di():array;
/**
* @return string Module root directory.
*/
public static function root():string;
/**
* @return string[] List of file confs needed by the module.
* Confs applyance order is engine -> modules -> project
*/
public static function confs():array;
/**
* @return string[] List of translation files.
*/
public static function langs():array;
/**
* @return string[] List of model class that must be registered in site/config/site.models.php
*/
public static function models():array;
/**
* @return string[] List of class that implements ISecurityPolicy and that must be applyed.
*/
public static function securityPolicies():array;
}
\ No newline at end of file
<?php
namespace wfw\engine\core\conf;
/**
* Base module descriptor. All module descript may extends this in order to prevent incompatibilities
* if new methods are added to the interface.
*/
abstract class ModuleDescriptor implements IModuleDescriptor{
/**
* @return array Dependency injection rules to add to the general DI
*/
public static function di(): array {
return [];
}
/**
* @return string[] List of file confs needed by the module.
* Confs applyance order is engine -> modules -> project
*/
public static function confs(): array {
return [];
}
/**
* @return string[] List of model class that must be registered in site/config/site.models.php
*/
public static function models(): array {
return [];
}
/**
* @return string[] List of class that implements ISecurityPolicy and that must be applyed.
*/
public static function securityPolicies(): array {
return [];
}
/**
* @return string[] List of translation files.
*/
public static function langs(): array {
return [];
}
/**
* @return string
* @throws \ReflectionException
*/
public static function root():string{
$reflected = new \ReflectionClass(static::class);
return dirname($reflected->getFileName());
}
}
\ No newline at end of file
<?php
namespace wfw\engine\core\conf;
use wfw\engine\core\security\WFWDefaultSecurityPolicy;
/**
* Base descriptor
*/
final class WFWModulesCollector extends ModuleDescriptor implements IAppModulesCollector {
private static $_modules = [];
/**
* @return array
*/
public static function securityPolicies(): array {
return [ WFWDefaultSecurityPolicy::class ];
}
/**
* @param string ...$modules List of class that implements IModuleDescriptor interface.
*/
public static function registerModules(string ...$modules): void {
foreach($modules as $module){
if(is_a($module,IModuleDescriptor::class)){
self::$_modules[] = $module;
}else throw new \InvalidArgumentException(
"$module doesn't implements ".IModuleDescriptor::class
);
}
}
/**
* @param string $fileName Name of register files for modules
*/
public static function collectModules(string $fileName = "module.registration.php"): void {
$dir = dirname(__DIR__,3);
$out = [];
exec("find \"$dir\" -f -name \"$fileName\"",$out);
foreach($out as $file) require_once $file;
}
/**
* @return string[] Registered modules descriptor classes.
*/
public static function modules(): array {
return self::$_modules;
}
}
\ No newline at end of file
......@@ -26,7 +26,7 @@ class KVStoreBasedModelLoader implements IModelLoader {
foreach($allowedToLoad as $model){
if(!is_string($model) || !class_exists($model)
|| !is_a($model,InMemoryEventBasedModel::class,true)){
throw new \InvalidArgumentException("$model is not a valide model name !");
throw new \InvalidArgumentException("$model is not a compatible Model !");
}
}
$this->_allowedToLoad = $allowedToLoad;
......
<?php
namespace wfw\engine\core\security;
/**
* Default security interface
*/
interface ISecurityPolicy {
/** @var array This constant must be defined to disable a full module */
public const DISABLE = [];
/**
* @return array [AccessRuleClass => params]
*/
public static function accessPolicy():array;
/**
* @return array [CommandAccessRuleClass => params]
*/
public static function commandsPolicy():array;
/**
* @return array [QueryAccessRuleClass => params]
*/
public static function queriesPolicy():array;
/**
* @return array [HookClass => params ]
*/
public static function hooksPolicy():array;
}
\ No newline at end of file
<?php
namespace wfw\engine\core\security;
/**
* SecurityPolicy baseclass. Must be extended in order to ensure that a further update will not
* break any SecurityPolicy if new methods are added to the ISecurityPolicy interface.
*/
class SecurityPolicy implements ISecurityPolicy {
/**
* @return array [AccessRuleClass => params]
*/
public static function accessPolicy(): array {
return [];
}
/**
* @return array [CommandAccessRuleClass => params]
*/
public static function commandsPolicy(): array {
return [];
}
/**
* @return array [QueryAccessRuleClass => params]
*/
public static function queriesPolicy(): array {
return [];
}
/**
* @return array [HookClass => params ]
*/
public static function hooksPolicy(): array {
return [];
}
}
\ No newline at end of file
<?php
namespace wfw\engine\core\security;
use wfw\engine\core\action\NotFoundHook;
use wfw\engine\core\security\rules\RequireAuthentification;
use wfw\engine\core\security\rules\ValidToken;
use wfw\engine\package\contact\security\ContactAccessControlPolicies;
use wfw\engine\package\general\security\GeneralAccessControlPolicies;
use wfw\engine\package\miel\security\MielAccessControlPolicies;
use wfw\engine\package\news\security\NewsAccessControlPolicies;
use wfw\engine\package\uploader\security\UploaderAccessControlPolicies;
use wfw\engine\package\users\security\UsersAccessControlPolicies;
/**
* WFW's own default security policy. Can be used to define the basic policy and will be used
* if no security have been defined for a project.
*/
final class WFWDefaultSecurityPolicy extends SecurityPolicy {
/**
* @param array $policies [AccessRuleClass=>params]
* @param bool $includeBase If true, will include default's WFW policies.
* @param array $templateArray Template array to specify the access policies order, as the
* method will use array_merge_recursive.
* @return array [AccessRuleClass => params]
*/
public static function accessPolicy(
array $policies=[],
bool $includeBase=true,
array $templateArray = [
RequireAuthentification::class => [],
ValidToken::class => []
]
): array {
$base = [];
if($includeBase) $base = array_merge_recursive(
ContactAccessControlPolicies::accessPolicy(),
MielAccessControlPolicies::accessPolicy(),
NewsAccessControlPolicies::accessPolicy(),
UploaderAccessControlPolicies::accessPolicy(),
UsersAccessControlPolicies::accessPolicy(),
GeneralAccessControlPolicies::accessPolicy(),
[ ValidToken::class => [] ]
);
return array_merge_recursive($templateArray,$base,$policies);
}
/**
* @param array $policies
* @param bool $includeBase
* @param array $template
* @return array [HookClass => params ]
*/
public static function hooksPolicy(
array $policies = [],
bool $includeBase = true,
array $template = [
NotFoundHook::class => []
]
): array {
$base = [];
if($includeBase) $base = array_merge_recursive(
UsersAccessControlPolicies::hooksPolicy(),
GeneralAccessControlPolicies::hooksPolicy()
);
return array_merge($template,$base,$policies);
}
}
\ No newline at end of file
......@@ -5,8 +5,6 @@ namespace wfw\engine\package\contact\command;
* Archive le(s) prises de contacts spécifiées
*/
final class ArchiveContacts extends ContactCommand {
/** @var string $_userId */
private $_userId;
/** @var string[] $_ids */
private $_ids;
......@@ -16,19 +14,11 @@ final class ArchiveContacts extends ContactCommand {
* @param string $userId Utilisateur a l'origine de la demande d'archivage
* @param string ...$ids Liste des identifiants
*/
public function __construct(string $userId, string... $ids) {
parent::__construct();
$this->_userId = $userId;
public function __construct(?string $userId=null, string... $ids) {
parent::__construct($userId);
$this->_ids =$ids;
}
/**
* @return string
*/
public function getUserId(): string {
return $this->_userId;
}
/**
* @return string[]
*/
......
......@@ -12,21 +12,18 @@ final class CreateContact extends ContactCommand {
private $_label;
/** @var IContactInfos $_infos */
private $_infos;
/** @var string $_userId */
private $_userId;
/**
* CreateContact constructor.
*
* @param ContactLabel $label
* @param IContactInfos $infos
* @param string $userId Identifiant de l'utilisateur à l'o
* @param string $userId Identifiant de l'utilisateur à l'origi
*/
public function __construct(ContactLabel $label, IContactInfos $infos, ?string $userId=null) {
parent::__construct();
parent::__construct($userId);
$this->_label = $label;
$this->_infos = $infos;
$this->_userId = $userId;
}
/**
......@@ -42,11 +39,4 @@ final class CreateContact extends ContactCommand {
public function getInfos(): IContactInfos {
return $this->_infos;
}
/**
* @return null|string
*/
public function getUserId(): ?string {
return $this->_userId;
}
}
\ No newline at end of file
......@@ -5,8 +5,6 @@ namespace wfw\engine\package\contact\command;
* Marque une liste de prises de contact comme lue
*/
final class MarkContactsAsRead extends ContactCommand {
/** @var string $_userId */
private $_userId;
/** @var string[] $_ids */
private $_ids;
......@@ -16,19 +14,11 @@ final class MarkContactsAsRead extends ContactCommand {
* @param string $userId Identifiant de l'utilisateur à l'origine de la demande
* @param string ...$ids Identifiants des prises de contacts à marquer comme lu
*/
public function __construct(string $userId, string... $ids) {
parent::__construct();
$this->_userId = $userId;
public function __construct(?string $userId=null, string... $ids) {
parent::__construct($userId);
$this->_ids = $ids;
}