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.
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/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}
+
+
+
+ | {@idpadmin~default.table.th.name@} |
+ {@idpadmin~default.table.th.status@} |
+
+
+
+ {foreach $idps as $idp}
+ {assign $ctlName = 'chck_'.$idp[0]}
+
+ |
+ {$idp[0]}
+ |
+
+ {ctrl_control $ctlName}
+ |
+
+ {/foreach}
+
+
+
+{/formcontrols}
+
+
+{/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}
+
+
+
+
+ | {@idpadmin~default.table.th.name@} |
+ {@idpadmin~default.table.th.status@} |
+
+
+
+
+ {foreach $idps as $idp}
+ {assign $ctlName = 'chck_'.$idp[0]}
+
+ |
+ {$idp[0]}
+ |
+
+ {ctrl_value $ctlName}
+ |
+
+ {/foreach}
+
+
+
+{/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 @@
+
+
+
+
+
+
+
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
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 @@
+
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']);
+ }
+}