From 812611befafab1a8defe4fa51556cc429aebbd0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Martin?= Date: Fri, 12 Sep 2025 11:52:49 +0200 Subject: [PATCH 1/5] feat : add idpadmin module --- .../idpadmin/controllers/default.classic.php | 109 ++++++++++++++++++ modules/idpadmin/events.xml | 5 + modules/idpadmin/forms/idp.form.xml | 6 + modules/idpadmin/install/install.php | 19 +++ modules/idpadmin/lib/AdminUiEventListener.php | 31 +++++ modules/idpadmin/lib/IdpFinder.php | 30 +++++ .../locales/en_US/default.UTF-8.properties | 9 ++ .../locales/fr_FR/default.UTF-8.properties | 9 ++ modules/idpadmin/module.xml | 19 +++ modules/idpadmin/templates/idp.edit.tpl | 37 ++++++ modules/idpadmin/templates/idp.list.tpl | 36 ++++++ modules/idpadmin/urls.xml | 7 ++ 12 files changed, 317 insertions(+) create mode 100644 modules/idpadmin/controllers/default.classic.php create mode 100644 modules/idpadmin/events.xml create mode 100644 modules/idpadmin/forms/idp.form.xml create mode 100644 modules/idpadmin/install/install.php create mode 100644 modules/idpadmin/lib/AdminUiEventListener.php create mode 100644 modules/idpadmin/lib/IdpFinder.php create mode 100644 modules/idpadmin/locales/en_US/default.UTF-8.properties create mode 100644 modules/idpadmin/locales/fr_FR/default.UTF-8.properties create mode 100644 modules/idpadmin/module.xml create mode 100644 modules/idpadmin/templates/idp.edit.tpl create mode 100644 modules/idpadmin/templates/idp.list.tpl create mode 100644 modules/idpadmin/urls.xml diff --git a/modules/idpadmin/controllers/default.classic.php b/modules/idpadmin/controllers/default.classic.php new file mode 100644 index 0000000..6f7ae66 --- /dev/null +++ b/modules/idpadmin/controllers/default.classic.php @@ -0,0 +1,109 @@ + array('auth.required' => false, 'jacl2.right' => 'idpadmin.view'), + 'save' => array('jacl2.right' => 'idpadmin.edit'), + ]; + + private $idpList ; + + public function __construct(jRequest $req) + { + $idpFinder = new IdpFinder(); + $this->idpList = $idpFinder->findAllIDP(); + parent::__construct($req); + } + + public function index() + { + $resp = $this->getResponse('html'); + // build form + $form = jForms::create('idpadmin~idp'); + $this->buildForm($form); + foreach($this->idpList as $idpInfo) { + $form->setData('chck_'.$idpInfo[0], $idpInfo[1]); + } + $tpl = new jTpl(); + $tpl->assign('idps', $this->idpList); + $tpl->assign('form', $form); + $resp->body->assign('MAIN', $tpl->fetch('idp.list')); + + return $resp; + } + + public function prepareEdit() + { + $form = jForms::create('idpadmin~idp'); + $this->buildForm($form); + foreach($this->idpList as $idpInfo) { + $form->setData('chck_'.$idpInfo[0], $idpInfo[1]); + } + return $this->redirect('idpadmin~default:showEdit'); + } + + public function showEdit() + { + $form = jForms::get('idpadmin~idp'); + if(is_null($form)) { + return $this->redirect('idpadmin~default:prepareEdit'); + } + $this->buildForm($form); + $resp = $this->getResponse('html'); + + $tpl = new jTpl(); + $tpl->assign('idps', $this->idpList); + $tpl->assign('form', $form); + $resp->body->assign('MAIN', $tpl->fetch('idp.edit')); + + return $resp; + } + + public function save() + { + $modif = new IniModifier(jApp::appPath('app/system/mainconfig.ini.php')); + + // build form + $form = jForms::get('idpadmin~idp'); + $this->buildForm($form); + $form->initFromRequest(); + + if(!$form->check()) { + return $this->redirect('idpadmin~default:showEdit'); + } + + $enabledIdp = []; + foreach($this->idpList as $idp) { + $name = $idp[0]; + if ($form->getData('chck_'.$name) == 1) { + $enabledIdp[] = $name; + } + } + $sessionIdp = jAuthentication::session()->getIdentityProviderId(); + if (!in_array($sessionIdp, $enabledIdp)) { + $form->setErrorOn('chck_'.$name, jLocale::get('default.form.error.session.idp.disabling.forbidden')); + return $this->redirect('idpadmin~default:showEdit'); + } + $modif->setValues(['idp' => $enabledIdp], 'authentication'); + $modif->save(); + jForms::destroy('idpadmin~idp'); + + return $this->redirect('idpadmin~default:index'); + } + + protected function buildForm(jFormsBase $form) + { + foreach($this->idpList as $idpInfo) { + $name = $idpInfo[0]; + $ctrlStatus = new jFormsControlCheckbox('chck_'.$name); + $ctrlStatus->label = $name; + $ctrlStatus->valueLabelOnCheck = jLocale::get('jelix~ui.buttons.enabled'); + $ctrlStatus->valueLabelOnUncheck = jLocale::get('jelix~ui.buttons.disabled'); + $form->addControl($ctrlStatus); + } + } +} diff --git a/modules/idpadmin/events.xml b/modules/idpadmin/events.xml new file mode 100644 index 0000000..598b746 --- /dev/null +++ b/modules/idpadmin/events.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/modules/idpadmin/forms/idp.form.xml b/modules/idpadmin/forms/idp.form.xml new file mode 100644 index 0000000..8bcbf97 --- /dev/null +++ b/modules/idpadmin/forms/idp.form.xml @@ -0,0 +1,6 @@ + +
+ + +
diff --git a/modules/idpadmin/install/install.php b/modules/idpadmin/install/install.php new file mode 100644 index 0000000..b6d95c8 --- /dev/null +++ b/modules/idpadmin/install/install.php @@ -0,0 +1,19 @@ + 'onAdminUILoading', + ); + + /** + * @param jEvent $event + */ + public function onAdminUILoading($event) + { + if(jAcl2::check('idpadmin.view')) { + /** @var \Jelix\AdminUI\UIManager $uim */ + $uim = $event->uiManager; + + $adminMenu = new SubMenu('admin', 'Administration', 10); + $adminMenu->addJelixLinkItem(jLocale::get('idpadmin~default.navigation.menu.idp'), 'idpadmin~default:index', array(), 'address-book'); + $uim->sidebar()->addMenuItem($adminMenu); + } + + } +} diff --git a/modules/idpadmin/lib/IdpFinder.php b/modules/idpadmin/lib/IdpFinder.php new file mode 100644 index 0000000..cef3e71 --- /dev/null +++ b/modules/idpadmin/lib/IdpFinder.php @@ -0,0 +1,30 @@ +getResponse(); + $authManager = jAuthentication::manager(); + $enabledIdps = $authManager->getIdpList(); + $enabledIdpNames = array_map(function (IdentityProviderInterface $idp) {return $idp->getId();}, $enabledIdps); + $allIdpName = []; + foreach($allIdpResponse as $idpInfo) { + if (array_key_exists('pluginName', $idpInfo)) { + $pluginName = $idpInfo['pluginName']; + $idpEnabled = (false !== array_search($pluginName, $enabledIdpNames)); + $allIdpName[] = [$pluginName, $idpEnabled]; + } + + } + + return $allIdpName; + } +} diff --git a/modules/idpadmin/locales/en_US/default.UTF-8.properties b/modules/idpadmin/locales/en_US/default.UTF-8.properties new file mode 100644 index 0000000..aff4794 --- /dev/null +++ b/modules/idpadmin/locales/en_US/default.UTF-8.properties @@ -0,0 +1,9 @@ +rights.group.name=Identity providers +idp.view=View Identity providers +idp.edit=Edit Identity providers +navigation.menu.idp=Identity providers +idp.list.page.title=Installed identity providers +idp.edit.page.title=Edit identity providers +form.error.session.idp.disabling.forbidden=You can't disable the idp uses by current session +table.th.name=Nom +table.th.status=Status diff --git a/modules/idpadmin/locales/fr_FR/default.UTF-8.properties b/modules/idpadmin/locales/fr_FR/default.UTF-8.properties new file mode 100644 index 0000000..db25043 --- /dev/null +++ b/modules/idpadmin/locales/fr_FR/default.UTF-8.properties @@ -0,0 +1,9 @@ +rights.group.name=Fournisseurs d'identité +idp.view=Voir les fournisseurs d'identité +idp.edit=Modifier les fournisseurs d'identité +navigation.menu.idp=Fournisseurs d'identité +idp.list.page.title=Fournisseurs d'identité installés +idp.edit.page.title=Modifier les fournisseurs d'identité +form.error.session.idp.disabling.forbidden=Vous ne pouvez pas désactiver le fournisseur utilisé par la session en cours +table.th.name=Nom +table.th.status=Status diff --git a/modules/idpadmin/module.xml b/modules/idpadmin/module.xml new file mode 100644 index 0000000..f574359 --- /dev/null +++ b/modules/idpadmin/module.xml @@ -0,0 +1,19 @@ + + + + 0.0.1 + + Module adding administration page for IDP + MIT + 2019-2025 Laurent Jouanneau + + https://jelix.org + + + + + + + + + diff --git a/modules/idpadmin/templates/idp.edit.tpl b/modules/idpadmin/templates/idp.edit.tpl new file mode 100644 index 0000000..9671d3d --- /dev/null +++ b/modules/idpadmin/templates/idp.edit.tpl @@ -0,0 +1,37 @@ +

{@idpadmin~default.idp.edit.page.title@}

+ +
+
+{form $form, 'idpadmin~default:save', [], 'adminlte'} + +{formcontrols} + + + + + + + + + {foreach $idps as $idp} + {assign $ctlName = 'chck_'.$idp[0]} + + + + + {/foreach} + +
{@idpadmin~default.table.th.name@}{@idpadmin~default.table.th.status@}
+ {$idp[0]} + + {ctrl_control $ctlName} +
+ +{/formcontrols} + +
+
{formsubmit}
+
+{/form} +
+
diff --git a/modules/idpadmin/templates/idp.list.tpl b/modules/idpadmin/templates/idp.list.tpl new file mode 100644 index 0000000..f7803da --- /dev/null +++ b/modules/idpadmin/templates/idp.list.tpl @@ -0,0 +1,36 @@ + +

{@idpadmin~default.idp.list.page.title@}

+
+
+ +{formcontrols $form} + + + + + + + + + + + {foreach $idps as $idp} + {assign $ctlName = 'chck_'.$idp[0]} + + + + + {/foreach} + +
{@idpadmin~default.table.th.name@}{@idpadmin~default.table.th.status@}
+ {$idp[0]} + + {ctrl_value $ctlName} +
+ +{/formcontrols} +{ifacl2 'idpadmin.edit'} +{@jelix~ui.buttons.update@} +{/ifacl2} +
+
diff --git a/modules/idpadmin/urls.xml b/modules/idpadmin/urls.xml new file mode 100644 index 0000000..c7942d0 --- /dev/null +++ b/modules/idpadmin/urls.xml @@ -0,0 +1,7 @@ + + + + + + + From d298d8db2cad6ce8a90e429eb42959a7e8088557 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Martin?= Date: Fri, 12 Sep 2025 12:09:23 +0200 Subject: [PATCH 2/5] feat(testapp) : add idpadmin to testapp --- test/testapp/app/system/mainconfig.ini.php | 1 + test/testapp/app/system/urls.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/test/testapp/app/system/mainconfig.ini.php b/test/testapp/app/system/mainconfig.ini.php index 3565965..c769cc2 100644 --- a/test/testapp/app/system/mainconfig.ini.php +++ b/test/testapp/app/system/mainconfig.ini.php @@ -32,6 +32,7 @@ account.enabled=on accountadmin.enabled=on +idpadmin.enabled=on [coordplugins] sessionauth=on jacl2=1 diff --git a/test/testapp/app/system/urls.xml b/test/testapp/app/system/urls.xml index 20d8f56..b5c4c65 100644 --- a/test/testapp/app/system/urls.xml +++ b/test/testapp/app/system/urls.xml @@ -7,6 +7,7 @@ + From ee2679b1c1ce8c905e8ecac627d7c13c22a6c51d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Martin?= Date: Fri, 12 Sep 2025 12:09:53 +0200 Subject: [PATCH 3/5] feat(testapp) add command to declare 1st user --- test/appctl | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/appctl b/test/appctl index 7335902..1ff81e8 100755 --- a/test/appctl +++ b/test/appctl @@ -5,7 +5,7 @@ shift if [ "$COMMAND" == "" ]; then echo "Error: command is missing" - echo "Possible commands: clean_tmp, reset, install, rights, composer-install, composer-update" + echo "Possible commands: clean_tmp, reset, install, rights, composer-install, composer-update, install-admin" echo " unit-tests, shell, shellroot" echo " ldapreset, ldapusers" exit 1; @@ -45,6 +45,15 @@ case $COMMAND in ldapshell) docker exec -it ${CONTAINER_PREFIX}_ldap /bin/bash -l ;; + install-admin) + docker exec -t -i ${CONTAINER_PREFIX}_php /bin/bash -c 'php test/testapp/console.php account:create admin admin@example.com' + docker exec -t -i ${CONTAINER_PREFIX}_php /bin/bash -c 'php test/testapp/console.php account:login:create -b daotablesqlite admin -p adminpass' + docker exec -t -i ${CONTAINER_PREFIX}_php /bin/bash -c 'php test/testapp/console.php acl2user:register admin' + docker exec -t -i ${CONTAINER_PREFIX}_php /bin/bash -c 'php test/testapp/console.php acl2user:addgroup admin admins' + docker exec -t -i ${CONTAINER_PREFIX}_php /bin/bash -c 'php test/testapp/console.php acl2:add admins idpadmin.view' + docker exec -t -i ${CONTAINER_PREFIX}_php /bin/bash -c 'php test/testapp/console.php acl2:add admins idpadmin.edit' + echo "admin account created, login admin, password adminpass" + ;; *) echo "wrong command" exit 2 From 165b89ced4467cf5cbe3d33757aa5dac084031c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Martin?= Date: Fri, 12 Sep 2025 12:10:47 +0200 Subject: [PATCH 4/5] feat IDP are now declared using events --- modules/authloginpass/events.xml | 5 +++++ .../authloginpass/lib/AuthCoreEventListener.php | 16 ++++++++++++++++ test/testapp/modules/test/events.xml | 3 +++ .../modules/test/lib/AuthCoreEventListener.php | 16 ++++++++++++++++ 4 files changed, 40 insertions(+) create mode 100644 modules/authloginpass/events.xml create mode 100644 modules/authloginpass/lib/AuthCoreEventListener.php create mode 100644 test/testapp/modules/test/lib/AuthCoreEventListener.php diff --git a/modules/authloginpass/events.xml b/modules/authloginpass/events.xml new file mode 100644 index 0000000..dadcce2 --- /dev/null +++ b/modules/authloginpass/events.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/modules/authloginpass/lib/AuthCoreEventListener.php b/modules/authloginpass/lib/AuthCoreEventListener.php new file mode 100644 index 0000000..d2ef447 --- /dev/null +++ b/modules/authloginpass/lib/AuthCoreEventListener.php @@ -0,0 +1,16 @@ +add(['pluginName' => 'loginpass']); + } +} diff --git a/test/testapp/modules/test/events.xml b/test/testapp/modules/test/events.xml index 02c704e..de9be08 100644 --- a/test/testapp/modules/test/events.xml +++ b/test/testapp/modules/test/events.xml @@ -3,4 +3,7 @@ + + + diff --git a/test/testapp/modules/test/lib/AuthCoreEventListener.php b/test/testapp/modules/test/lib/AuthCoreEventListener.php new file mode 100644 index 0000000..7993c5b --- /dev/null +++ b/test/testapp/modules/test/lib/AuthCoreEventListener.php @@ -0,0 +1,16 @@ +add(['pluginName' => 'alwadysyes']); + } +} From 86ff901aa3fa6da86c6e15ed78a9c488686cc887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Martin?= Date: Fri, 12 Sep 2025 12:27:59 +0200 Subject: [PATCH 5/5] docs: explain declareIdpPlugin --- docs/en/events.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/en/events.md b/docs/en/events.md index 1e65b63..dcc7bb3 100644 --- a/docs/en/events.md +++ b/docs/en/events.md @@ -102,3 +102,9 @@ See the `Jelix\Authentication\LoginPass\AuthLPCanResetPasswordEvent` class. +`declareIDPlugin` +----------------- + +This event is sent to build the IDP list (existing regardless the `[authentication]` configuration). + +Use the event when you create a module so that it is known by idpadmin module.