Merge branch 'v2.0' into findUser
This commit is contained in:
commit
a1700369c5
|
@ -908,7 +908,10 @@
|
|||
"type" : "boolean"
|
||||
},
|
||||
"postLogoutRedirectUris" : {
|
||||
"type" : "string"
|
||||
"type" : "array",
|
||||
"items" : {
|
||||
"type" : "string"
|
||||
}
|
||||
},
|
||||
"logoutType" : {
|
||||
"type" : "string",
|
||||
|
|
35
doc/sources/admin/browseableldapsessionbackend.rst
Normal file
35
doc/sources/admin/browseableldapsessionbackend.rst
Normal file
|
@ -0,0 +1,35 @@
|
|||
Browseable LDAP session backend
|
||||
===============================
|
||||
|
||||
LemonLDAP::NG configuration
|
||||
---------------------------
|
||||
|
||||
Go in the Manager and set the session module to ``Apache::Session::Browseable::LDAP`` for each session type you intend to use:
|
||||
|
||||
* ``General parameters`` » ``Sessions`` » ``Session storage`` » ``Apache::Session module``
|
||||
* ``General parameters`` » ``Sessions`` » ``Persistent sessions`` » ``Apache::Session module``
|
||||
* ``CAS Service`` » ``CAS sessions module name``
|
||||
* ``OpenID Connect Service`` » ``Sessions`` » ``Sessions module name``
|
||||
* ``SAML2 Service`` » ``Advanced`` » ``SAML sessions module name``
|
||||
|
||||
The fill out the corresponding module parameters:
|
||||
|
||||
======================== ================================= ===============================
|
||||
Required parameters
|
||||
======================== ================================= ===============================
|
||||
Name Comment Example
|
||||
**ldapServer** URI of the server ldap://localhost
|
||||
**ldapConfBase** DN of sessions branch ou=sessions,dc=example,dc=com
|
||||
**ldapBindDN** Connection login cn=admin,dc=example,dc=password
|
||||
**ldapBindPassword** Connection password secret
|
||||
**Index** Fields to index refer to :ref:`fieldstoindex`
|
||||
Optional parameters
|
||||
Name Comment Default value
|
||||
**ldapObjectClass** Objectclass of the entry applicationProcess
|
||||
**ldapAttributeId** Attribute storing session ID cn
|
||||
**ldapAttributeContent** Attribute storing session content description
|
||||
**ldapAttributeIndex** Attribute storing index ou
|
||||
**ldapVerify** Perform certificate validation require (use none to disable)
|
||||
**ldapCAFile** Path of CA file bundle (system CA bundle)
|
||||
**ldapCAPath** Perform CA directory (system CA bundle)
|
||||
======================== ================================= ===============================
|
120
doc/sources/admin/browseablemysqlsessionbackend.rst
Normal file
120
doc/sources/admin/browseablemysqlsessionbackend.rst
Normal file
|
@ -0,0 +1,120 @@
|
|||
Browseable MySQL session backend
|
||||
================================
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
First, make sure you have installed the ``DBD::mysql`` perl module.
|
||||
|
||||
On Debian-based distributions ::
|
||||
|
||||
apt install libdbd-mysql-perl
|
||||
|
||||
On Fedora-based distributions ::
|
||||
|
||||
yum install 'perl(DBD::mysql)'
|
||||
|
||||
Create database schema
|
||||
----------------------
|
||||
|
||||
Create the following tables. You may skip the session types you are not going to use, but you need at least ``sessions`` and ``psessions``
|
||||
|
||||
::
|
||||
|
||||
CREATE TABLE sessions (
|
||||
id varchar(64) not null primary key,
|
||||
a_session text,
|
||||
_whatToTrace varchar(64),
|
||||
_session_kind varchar(15),
|
||||
ipAddr varchar(64),
|
||||
_utime bigint,
|
||||
_httpSessionType varchar(64),
|
||||
user varchar(64)
|
||||
) DEFAULT CHARSET utf8;
|
||||
CREATE INDEX i_s__whatToTrace ON sessions (_whatToTrace);
|
||||
CREATE INDEX i_s__session_kind ON sessions (_session_kind);
|
||||
CREATE INDEX i_s__utime ON sessions (_utime);
|
||||
CREATE INDEX i_s_ipAddr ON sessions (ipAddr);
|
||||
CREATE INDEX i_s__httpSessionType ON sessions (_httpSessionType);
|
||||
CREATE INDEX i_s_user ON sessions (user);
|
||||
|
||||
CREATE TABLE psessions (
|
||||
id varchar(64) not null primary key,
|
||||
a_session text,
|
||||
_session_kind varchar(15),
|
||||
_httpSessionType varchar(64),
|
||||
_whatToTrace varchar(64),
|
||||
ipAddr varchar(64),
|
||||
_session_uid varchar(64)
|
||||
) DEFAULT CHARSET utf8;
|
||||
CREATE INDEX i_p__session_kind ON psessions (_session_kind);
|
||||
CREATE INDEX i_p__httpSessionType ON psessions (_httpSessionType);
|
||||
CREATE INDEX i_p__session_uid ON psessions (_session_uid);
|
||||
CREATE INDEX i_p_ipAddr ON psessions (ipAddr);
|
||||
CREATE INDEX i_p__whatToTrace ON psessions (_whatToTrace);
|
||||
|
||||
CREATE TABLE samlsessions (
|
||||
id varchar(64) not null primary key,
|
||||
a_session text,
|
||||
_session_kind varchar(15),
|
||||
_utime bigint,
|
||||
ProxyID varchar(64),
|
||||
_nameID varchar(128),
|
||||
_assert_id varchar(64),
|
||||
_art_id varchar(64),
|
||||
_saml_id varchar(64)
|
||||
) DEFAULT CHARSET utf8;
|
||||
CREATE INDEX i_a__session_kind ON samlsessions (_session_kind);
|
||||
CREATE INDEX i_a__utime ON samlsessions (_utime);
|
||||
CREATE INDEX i_a_ProxyID ON samlsessions (ProxyID);
|
||||
CREATE INDEX i_a__nameID ON samlsessions (_nameID);
|
||||
CREATE INDEX i_a__assert_id ON samlsessions (_assert_id);
|
||||
CREATE INDEX i_a__art_id ON samlsessions (_art_id);
|
||||
CREATE INDEX i_a__saml_id ON samlsessions (_saml_id);
|
||||
|
||||
CREATE TABLE oidcsessions (
|
||||
id varchar(64) not null primary key,
|
||||
a_session text,
|
||||
_session_kind varchar(15),
|
||||
_utime bigint
|
||||
) DEFAULT CHARSET utf8;
|
||||
CREATE INDEX i_o__session_kind ON oidcsessions (_session_kind);
|
||||
CREATE INDEX i_o__utime ON oidcsessions (_utime);
|
||||
|
||||
|
||||
CREATE TABLE cassessions (
|
||||
id varchar(64) not null primary key,
|
||||
a_session text,
|
||||
_session_kind varchar(15),
|
||||
_utime bigint,
|
||||
_cas_id varchar(128),
|
||||
pgtIou varchar(128)
|
||||
) DEFAULT CHARSET utf8
|
||||
CREATE INDEX i_c__session_kind ON cassessions (_session_kind);
|
||||
CREATE INDEX i_c__utime ON cassessions (_utime);
|
||||
CREATE INDEX i_c__cas_id ON cassessions (_cas_id);
|
||||
CREATE INDEX i_c_pgtIou ON cassessions (pgtIou);
|
||||
|
||||
LemonLDAP::NG configuration
|
||||
---------------------------
|
||||
|
||||
Go in the Manager and set the session module to ``Apache::Session::Browseable::PgJSON`` for each session type you intend to use:
|
||||
|
||||
* ``General parameters`` » ``Sessions`` » ``Session storage`` » ``Apache::Session module``
|
||||
* ``General parameters`` » ``Sessions`` » ``Persistent sessions`` » ``Apache::Session module``
|
||||
* ``CAS Service`` » ``CAS sessions module name``
|
||||
* ``OpenID Connect Service`` » ``Sessions`` » ``Sessions module name``
|
||||
* ``SAML2 Service`` » ``Advanced`` » ``SAML sessions module name``
|
||||
|
||||
Then, set the following module options:
|
||||
|
||||
=================== ================================================= =============================================================
|
||||
Required parameters
|
||||
=================== ================================================= =============================================================
|
||||
Name Comment Example
|
||||
**DataSource** The `DBI <https://metacpan.org/pod/DBI>`__ string dbi:mysql:database=lemonldap-ng
|
||||
**UserName** The database username lemonldapng
|
||||
**Password** The database password mysuperpassword
|
||||
**TableName** Table name (optional) sessions
|
||||
**Index** Fields to index refer to :ref:`fieldstoindex`
|
||||
=================== ================================================= =============================================================
|
|
@ -7,250 +7,53 @@ Presentation
|
|||
Browseable session backend
|
||||
(`Apache::Session::Browseable <https://metacpan.org/pod/Apache::Session::Browseable>`__)
|
||||
works exactly like Apache::Session::\* corresponding module but add
|
||||
index that increase :ref:`session explorer<session-explorer>` and
|
||||
:ref:`session restrictions<session-restrictions>` performances.
|
||||
|
||||
If you use features like SAML (authentication and issuer), CAS (issuer)
|
||||
and password reset self-service, you also need to index some fields.
|
||||
|
||||
index that increase the speed of some operations. It is recommended in production deployments.
|
||||
|
||||
.. note::
|
||||
|
||||
Without index, LL::NG will have to retrieve all sessions stored in
|
||||
backend and parse them to find the needed sessions. With index, LL::NG
|
||||
wil be able to get only wanted sessions from the backend.
|
||||
backend and deserialize then filter each of them.
|
||||
|
||||
The following table list fields to index depending on the feature you
|
||||
want to increase performance:
|
||||
The following table list fields to index for each session type:
|
||||
|
||||
====================================== ============= ===================================================================
|
||||
Feature Session Type Fields to index
|
||||
====================================== ============= ===================================================================
|
||||
Database cleanup *(cron)* All \_session_kind \_utime
|
||||
Session explorer Global \_session_kind ipAddr \_httpSessionType *WHATTOTRACE*
|
||||
Session explorer Persistent \_session_kind \_session_uid ipAddr \_httpSessionType *WHATTOTRACE*
|
||||
Session restrictions Global \_session_kind ipAddr *WHATTOTRACE*
|
||||
Password reset by email Global user
|
||||
SAML Session SAML \_saml_id ProxyID \_nameID \_assert_id \_art_id
|
||||
====================================== ============= ===================================================================
|
||||
|
||||
.. _fieldstoindex:
|
||||
|
||||
List of fields to index by session type
|
||||
---------------------------------------
|
||||
|
||||
========================== ======================================================================
|
||||
Session Type Fields to index
|
||||
========================== ======================================================================
|
||||
Sessions (global) \_whatToTrace \_session_kind \_utime ipAddr \_httpSessionType user
|
||||
Persistent sessions \_session\_kind \_httpSessionType \_session\_uid ipAddr \_whatToTrace
|
||||
CAS sessions \_cas\_id pgtIou
|
||||
SAML sessions \_session_kind \_utime \_saml_id ProxyID \_nameID \_assert_id \_art_id
|
||||
OpenID Connect sessions \_session_kind \_utime
|
||||
========================== ======================================================================
|
||||
|
||||
.. note::
|
||||
|
||||
If you have configured LemonLDAP::NG to use something other than
|
||||
`_whatToTrace` as the main session identifier, you must replace
|
||||
`_whatToTrace` with the new session field in the previous list
|
||||
|
||||
See Apache::Session::Browseable man page to see how use indexes.
|
||||
|
||||
|
||||
.. attention::
|
||||
|
||||
\ *WHATTOTRACE* must be replaced by the attribute or
|
||||
macro configured in the What To Trace parameter (REMOTE_USER). By
|
||||
default: **\_whatToTrace**\
|
||||
|
||||
|
||||
.. tip::
|
||||
|
||||
It is advised to use separate session backends for standard
|
||||
sessions, SAML sessions and CAS sessions, in order to manage index
|
||||
separately.
|
||||
It is advised to use separate session backends for standard sessions, SAML
|
||||
sessions and CAS sessions, in order to avoid unused indexes.
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
Documentation below explains how set index on ipAddr and
|
||||
\_whatToTrace. Adapt it to configure the index you need.
|
||||
Available backends
|
||||
------------------
|
||||
|
||||
Browseable NoSQL
|
||||
----------------
|
||||
|
||||
You can use Redis and set up the database like explained in
|
||||
:doc:`Redis session backend<nosqlsessionbackend>`.
|
||||
|
||||
You then just have to add the ``Index`` parameter in
|
||||
``General parameters`` » ``Sessions`` » ``Session storage`` »
|
||||
``Apache::Session module`` :
|
||||
|
||||
=================== ============ ====================
|
||||
Required parameters
|
||||
=================== ============ ====================
|
||||
Name Comment Example
|
||||
**server** Redis server 127.0.0.1:6379
|
||||
**Index** Index \_whatToTrace ipAddr
|
||||
=================== ============ ====================
|
||||
|
||||
Browseable SQL
|
||||
--------------
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
This documentation concerns PostgreSQL. Some adaptations are
|
||||
needed with other databases. When using
|
||||
Apache::Session::Browseable::Postgres, it
|
||||
is strongly recommended to use version 1.3.1 at least. See `bug
|
||||
1732 <https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/issues/1732>`__.
|
||||
|
||||
Prepare database
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Database must be prepared exactly like in
|
||||
:ref:`SQL session backend<sqlsessionbackend-prepare-the-database>`
|
||||
except that a field must be added for each data to index.
|
||||
|
||||
|
||||
.. attention::
|
||||
|
||||
Data written to UNLOGGED tables is not written to the
|
||||
WAL, which makes them considerably faster than ordinary tables. However,
|
||||
they are not crash-safe: an unlogged table is automatically truncated
|
||||
after a crash or unclean shutdown. The contents of an unlogged table are
|
||||
also not replicated to standby servers. Any indexes created on an
|
||||
unlogged table are automatically unlogged as well.
|
||||
|
||||
Apache::Session::Browseable::Postgres
|
||||
example:
|
||||
|
||||
::
|
||||
|
||||
CREATE UNLOGGED TABLE sessions (
|
||||
id varchar(64) not null primary key,
|
||||
a_session text,
|
||||
_whatToTrace text,
|
||||
_session_kind text,
|
||||
_utime bigint,
|
||||
_httpSessionType text,
|
||||
ipAddr text
|
||||
);
|
||||
CREATE INDEX uid1 ON sessions USING BTREE (_whatToTrace text_pattern_ops);
|
||||
CREATE INDEX s1 ON sessions (_session_kind);
|
||||
CREATE INDEX u1 ON sessions (_utime);
|
||||
CREATE INDEX ip1 ON sessions USING BTREE (ipAddr);
|
||||
CREATE INDEX h1 ON sessions (_httpSessionType);
|
||||
|
||||
|
||||
.. attention::
|
||||
|
||||
For Session Explorer and one-off sessions, it is
|
||||
recommended to use BTREE or any index method that indexes partial
|
||||
content.
|
||||
|
||||
"id" fieds is set to ``varchar(64)`` (instead of char(32)) to use the
|
||||
now recommended SHA256 hash algorithm. See
|
||||
:doc:`Sessions<sessions>` for more details.
|
||||
|
||||
|
||||
.. tip::
|
||||
|
||||
With new
|
||||
Apache::Session::Browseable::PgHstore
|
||||
and **PgJSON**, you don't need to declare indexes in ``CREATE TABLE``
|
||||
since "json" and "hstore" type are browseable. You should anyway add
|
||||
some indexes *(see manpage)*.
|
||||
|
||||
Manager
|
||||
~~~~~~~
|
||||
|
||||
Go in the Manager and set the session module
|
||||
(`Apache::Session::Browseable::MySQL <https://metacpan.org/pod/Apache::Session::Browseable::MySQL>`__
|
||||
for MySQL) in ``General parameters`` » ``Sessions`` »
|
||||
``Session storage`` » ``Apache::Session module`` and add the following
|
||||
parameters (case sensitive):
|
||||
|
||||
=================== ================================================= =============================================================
|
||||
Required parameters
|
||||
=================== ================================================= =============================================================
|
||||
Name Comment Example
|
||||
**DataSource** The `DBI <https://metacpan.org/pod/DBI>`__ string dbi:Pg:database=lemonldap-ng
|
||||
**UserName** The database username lemonldapng
|
||||
**Password** The database password mysuperpassword
|
||||
**Index** Index \_whatToTrace ipAddr \_session_kind \_utime \_httpSessionType
|
||||
**TableName** Table name (optional) sessions
|
||||
=================== ================================================= =============================================================
|
||||
|
||||
|
||||
.. tip::
|
||||
|
||||
Apache::Session::Browseable::MySQL doesn't use locks so performances are
|
||||
keeped.
|
||||
|
||||
For databases like PostgreSQL, don't forget to add "Commit" with a value
|
||||
of 1
|
||||
|
||||
Browseable LDAP
|
||||
---------------
|
||||
|
||||
Go in the Manager and set the session module to
|
||||
``Apache::Session::Browseable::LDAP``. Then configure the options like
|
||||
in :doc:`LDAP session backend<ldapsessionbackend>`.
|
||||
|
||||
You need to add the ``Index`` field and can also configure the
|
||||
``ldapAttributeIndex`` field to set the attribute name where index
|
||||
values will be stored.
|
||||
|
||||
======================== ================================= ===============================
|
||||
Required parameters
|
||||
======================== ================================= ===============================
|
||||
Name Comment Example
|
||||
**ldapServer** URI of the server ldap://localhost
|
||||
**ldapConfBase** DN of sessions branch ou=sessions,dc=example,dc=com
|
||||
**ldapBindDN** Connection login cn=admin,dc=example,dc=password
|
||||
**ldapBindPassword** Connection password secret
|
||||
**Index** Index list \_whatToTrace ipAddr
|
||||
Optional parameters
|
||||
Name Comment Default value
|
||||
**ldapObjectClass** Objectclass of the entry applicationProcess
|
||||
**ldapAttributeId** Attribute storing session ID cn
|
||||
**ldapAttributeContent** Attribute storing session content description
|
||||
**ldapAttributeIndex** Attribute storing index ou
|
||||
**ldapVerify** Perform certificate validation require (use none to disable)
|
||||
**ldapCAFile** Path of CA file bundle (system CA bundle)
|
||||
**ldapCAPath** Perform CA directory (system CA bundle)
|
||||
======================== ================================= ===============================
|
||||
|
||||
Security
|
||||
--------
|
||||
|
||||
Restrict network access to the backend.
|
||||
|
||||
You can also use different user/password for your servers by overriding
|
||||
parameters ``globalStorage`` and ``globalStorageOptions`` in
|
||||
lemonldap-ng.ini file.
|
||||
|
||||
Performances
|
||||
------------
|
||||
|
||||
Here are some recommended configurations:
|
||||
|
||||
**Browseable::Postgres**:
|
||||
|
||||
::
|
||||
|
||||
CREATE UNLOGGED TABLE sessions (
|
||||
id varchar(64) not null primary key,
|
||||
a_session text,
|
||||
_whatToTrace text,
|
||||
_session_kind text,
|
||||
_utime bigint,
|
||||
_httpSessionType text,
|
||||
ipAddr text
|
||||
);
|
||||
CREATE INDEX uid1 ON sessions USING BTREE (_whatToTrace text_pattern_ops);
|
||||
CREATE INDEX s1 ON sessions (_session_kind);
|
||||
CREATE INDEX u1 ON sessions (_utime);
|
||||
CREATE INDEX ip1 ON sessions USING BTREE (ipAddr);
|
||||
CREATE INDEX h1 ON sessions (_httpSessionType);
|
||||
|
||||
**Browseable::MySQL**:
|
||||
|
||||
::
|
||||
|
||||
CREATE TABLE sessions (
|
||||
id varchar(64) not null primary key,
|
||||
a_session text,
|
||||
_whatToTrace varchar(64),
|
||||
_session_kind varchar(15),
|
||||
user text,
|
||||
_utime bigint,
|
||||
ipAddr varchar(64)
|
||||
);
|
||||
CREATE INDEX uid1 ON sessions (_whatToTrace) USING BTREE;
|
||||
CREATE INDEX _s1 ON sessions (_session_kind);
|
||||
CREATE INDEX _u1 ON sessions (_utime);
|
||||
CREATE INDEX ip1 ON sessions (ipAddr) USING BTREE;
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
PgJSON Backend (recommended) <pgjsonsessionbackend>
|
||||
browseablemysqlsessionbackend
|
||||
browseableldapsessionbackend
|
||||
nosqlsessionbackend
|
||||
|
|
|
@ -170,21 +170,32 @@ Security
|
|||
- **Authentication Level**: required authentication level to access this SP
|
||||
- **Access Rule**: lets you specify a :doc:`Perl rule<rules_examples>` to restrict access to this SP
|
||||
|
||||
Extra variables
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
.. tip::
|
||||
The following environment variables are available in SAML access rules and macros:
|
||||
|
||||
The IDP Initiated URL is the SSO SAML URL with GET
|
||||
parameters:
|
||||
* ``$env->{llng_saml_sp}`` : entityID of the SAML service
|
||||
* ``$env->{llng_saml_spconfkey}`` : configuration key of the SAML service
|
||||
|
||||
- IDPInitiated: 1
|
||||
- One of:
|
||||
.. versionadded:: 2.0.10
|
||||
|
||||
- sp: SP entity ID
|
||||
- spConfKey: SP configuration key
|
||||
* ``$env->{llng_saml_acs}`` : AssertionConsumerServiceURL, if specified in the AuthnRequest
|
||||
|
||||
For example:
|
||||
http://auth.example.com/saml/singleSignOn?IDPInitiated=1&spConfKey=simplesamlphp
|
||||
IDP Initiated mode
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The IDP Initiated URL is the SSO SAML URL with GET
|
||||
parameters:
|
||||
|
||||
- IDPInitiated: 1
|
||||
- One of:
|
||||
|
||||
- sp: SP entity ID
|
||||
- spConfKey: SP configuration key
|
||||
|
||||
For example:
|
||||
http://auth.example.com/saml/singleSignOn?IDPInitiated=1&spConfKey=simplesamlphp
|
||||
|
||||
Macros
|
||||
^^^^^^
|
||||
|
|
|
@ -7,4 +7,5 @@ Development
|
|||
bugreport
|
||||
contribute
|
||||
handlerarch
|
||||
plugincustom
|
||||
customhandlers
|
||||
|
|
|
@ -12,7 +12,6 @@ Plugins
|
|||
checkuser
|
||||
viewer
|
||||
contextswitching
|
||||
plugincustom
|
||||
decryptvalue
|
||||
loginhistory
|
||||
forcereauthn
|
||||
|
|
|
@ -6,7 +6,6 @@ Sessions database
|
|||
|
||||
changesessionbackend
|
||||
filesessionbackend
|
||||
restsessionbackend
|
||||
sqlsessionbackend
|
||||
ldapsessionbackend
|
||||
nosqlsessionbackend
|
||||
|
|
|
@ -30,9 +30,9 @@ Name Comment Example
|
|||
**sentinels** Redis sentinels list 127.0.0.1:26379,127.0.0.2:26379,127.0.0.3:26379
|
||||
**password** password (== requirepass) ChangeMe
|
||||
**select** Redis DB 1
|
||||
**Index** Fields to index refer to :ref:`fieldstoindex`
|
||||
============= =========================== ===============================================
|
||||
|
||||
|
||||
Security
|
||||
--------
|
||||
|
||||
|
|
110
doc/sources/admin/pgjsonsessionbackend.rst
Normal file
110
doc/sources/admin/pgjsonsessionbackend.rst
Normal file
|
@ -0,0 +1,110 @@
|
|||
PgJSON session backend
|
||||
======================
|
||||
|
||||
This backend is the recommended one for production installations of LemonLDAP::NG.
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
First, make sure you have installed the ``DBD::Pg`` perl module.
|
||||
|
||||
On Debian-based distributions ::
|
||||
|
||||
apt install libdbd-pg-perl
|
||||
|
||||
On Fedora-based distributions ::
|
||||
|
||||
yum install 'perl(DBD::Pg)'
|
||||
|
||||
|
||||
The minimum required version of PostgreSQL is 9.3 with `support for JSON column types <https://www.postgresql.org/docs/9.3/functions-json.html>`__
|
||||
|
||||
Make sure you are using at least version 1.2.9 of ``Apache::Session::Browseable``, this might require installing it from Debian Backports or CPAN.
|
||||
|
||||
Create database schema
|
||||
----------------------
|
||||
|
||||
Create the following tables. You may skip the session types you are not going to use, but you need at least ``sessions`` and ``psessions``
|
||||
|
||||
::
|
||||
|
||||
CREATE TABLE sessions (
|
||||
id varchar(64) not null primary key,
|
||||
a_session jsonb
|
||||
);
|
||||
|
||||
CREATE INDEX i_s__whatToTrace ON sessions ((a_session ->> '_whatToTrace'));
|
||||
CREATE INDEX i_s__session_kind ON sessions ((a_session ->> '_session_kind'));
|
||||
CREATE INDEX i_s__utime ON sessions ((cast (a_session ->> '_utime' as bigint)));
|
||||
CREATE INDEX i_s_ipAddr ON sessions ((a_session ->> 'ipAddr'));
|
||||
CREATE INDEX i_s__httpSessionType ON sessions ((a_session ->> '_httpSessionType'));
|
||||
CREATE INDEX i_s_user ON sessions ((a_session ->> 'user'));
|
||||
|
||||
|
||||
CREATE TABLE psessions (
|
||||
id varchar(64) not null primary key,
|
||||
a_session jsonb
|
||||
);
|
||||
CREATE INDEX i_p__session_kind ON psessions ((a_session ->> '_session_kind'));
|
||||
CREATE INDEX i_p__httpSessionType ON psessions ((a_session ->> '_httpSessionType'));
|
||||
CREATE INDEX i_p__session_uid ON psessions ((a_session ->> '_session_uid'));
|
||||
CREATE INDEX i_p_ipAddr ON psessions ((a_session ->> 'ipAddr'));
|
||||
CREATE INDEX i_p__whatToTrace ON psessions ((a_session ->> '_whatToTrace'));
|
||||
|
||||
|
||||
CREATE TABLE samlsessions (
|
||||
id varchar(64) not null primary key,
|
||||
a_session jsonb
|
||||
);
|
||||
CREATE INDEX i_a__session_kind ON samlsessions ((a_session ->> '_session_kind'));
|
||||
CREATE INDEX i_a__utime ON samlsessions ((cast(a_session ->> '_utime' as bigint)));
|
||||
CREATE INDEX i_a_ProxyID ON samlsessions ((a_session ->> 'ProxyID'));
|
||||
CREATE INDEX i_a__nameID ON samlsessions ((a_session ->> '_nameID'));
|
||||
CREATE INDEX i_a__assert_id ON samlsessions ((a_session ->> '_assert_id'));
|
||||
CREATE INDEX i_a__art_id ON samlsessions ((a_session ->> '_art_id'));
|
||||
CREATE INDEX i_a__saml_id ON samlsessions ((a_session ->> '_saml_id'));
|
||||
|
||||
CREATE TABLE oidcsessions (
|
||||
id varchar(64) not null primary key,
|
||||
a_session jsonb
|
||||
);
|
||||
CREATE INDEX i_o__session_kind ON oidcsessions ((a_session ->> '_session_kind'));
|
||||
CREATE INDEX i_o__utime ON oidcsessions ((cast(a_session ->> '_utime' as bigint )));
|
||||
|
||||
CREATE TABLE cassessions (
|
||||
id varchar(64) not null primary key,
|
||||
a_session jsonb
|
||||
);
|
||||
CREATE INDEX i_c__session_kind ON cassessions ((a_session ->> '_session_kind'));
|
||||
CREATE INDEX i_c__utime ON cassessions ((cast(a_session ->> '_utime' as bigint)));
|
||||
CREATE INDEX i_c__cas_id ON cassessions ((a_session ->> '_cas_id'));
|
||||
CREATE INDEX i_c_pgtIou ON cassessions ((a_session ->> 'pgtIou'));
|
||||
|
||||
LemonLDAP::NG configuration
|
||||
---------------------------
|
||||
|
||||
Go in the Manager and set the session module to ``Apache::Session::Browseable::PgJSON`` for each session type you intend to use:
|
||||
|
||||
* ``General parameters`` » ``Sessions`` » ``Session storage`` » ``Apache::Session module``
|
||||
* ``General parameters`` » ``Sessions`` » ``Persistent sessions`` » ``Apache::Session module``
|
||||
* ``CAS Service`` » ``CAS sessions module name``
|
||||
* ``OpenID Connect Service`` » ``Sessions`` » ``Sessions module name``
|
||||
* ``SAML2 Service`` » ``Advanced`` » ``SAML sessions module name``
|
||||
|
||||
Then, set the following module options:
|
||||
|
||||
=================== ================================================= =============================================================
|
||||
Required parameters
|
||||
=================== ================================================= =============================================================
|
||||
Name Comment Example
|
||||
**DataSource** The `DBI <https://metacpan.org/pod/DBI>`__ string dbi:Pg:database=lemonldap-ng
|
||||
**UserName** The database username lemonldapng
|
||||
**Password** The database password mysuperpassword
|
||||
**TableName** Table name (optional) sessions
|
||||
**Commit** 1 This setting is mandatory for PostgreSQL to work
|
||||
=================== ================================================= =============================================================
|
||||
|
||||
|
||||
.. tip::
|
||||
|
||||
Unlike other browseable modules, Pg::JSON does not require an ``Index`` parameter
|
|
@ -383,16 +383,16 @@ style modules are usable except for some features.
|
|||
Backend Shareable :ref:`Session explorer<session-explorer>` :ref:`Session restrictions<session-restrictions>` Session expiration Comment
|
||||
================================================================ ========= ================================================ ==================================================== ================== =================================================================================================================================================================================================
|
||||
:doc:`File<filesessionbackend>` ✔ ✔ ✔ Not shareable between servers except if used in conjunction with :doc:`REST session backend<restsessionbackend>` or with a shared file system (NFS,...). Selected by default during installation.
|
||||
:doc:`SQL<sqlsessionbackend>` ✔ ✔ ✔ ✔ Unoptimized for :doc:`session explorer<features>` and :doc:`single session<features>` features.
|
||||
:doc:`LDAP<ldapsessionbackend>` ✔ ✔ ✔ ✔
|
||||
:doc:`PgJSON<pgjsonsessionbackend>` ✔ ✔ ✔ ✔ Recommended backend for production installations
|
||||
:doc:`Browseable MySQL<browseablemysqlsessionbackend>` ✔ ✔ ✔ ✔ Recommended for those who prefer MySQL
|
||||
:doc:`Browseable LDAP<browseableldapsessionbackend>` ✔ ✔ ✔ ✔
|
||||
:doc:`Redis<nosqlsessionbackend>` ✔ ✔ ✔ ✔ The fastest. Must be secured by network access control.
|
||||
:doc:`MongoDB<mongodbsessionbackend>` ✔ ✔ ✔ ✔ Must be secured by network access control.
|
||||
:doc:`Browseable (SQL, Redis or LDAP)<browseablesessionbackend>` ✔ ✔ ✔ ✔ **Optimized** for :doc:`session explorer<features>` and :doc:`single session<features>` features.
|
||||
:doc:`SQL<sqlsessionbackend>` ✔ ✔ ✔ ✔ Unoptimized for :doc:`session explorer<features>` and :doc:`single session<features>` features.
|
||||
:doc:`REST<restsessionbackend>` |new| ✔ ✔ ✔ ✔ Proxy backend to be used in conjunction with another session backend.
|
||||
:doc:`SOAP<soapsessionbackend>` |deprecated| ✔ ✔ ✔ ✔ Proxy backend to be used in conjunction with another session backend.
|
||||
================================================================ ========= ================================================ ==================================================== ================== =================================================================================================================================================================================================
|
||||
|
||||
|
||||
.. tip::
|
||||
|
||||
You can migrate from one session backend to another using the
|
||||
|
|
|
@ -26,6 +26,7 @@ backups and a rollback plan ready!
|
|||
- RHEL/CentOS SELinux users should install the new ``lemonldap-ng-selinux`` package to fix `an issue with the new default cache directory <https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/issues/2401>`__
|
||||
- If you use :doc:`applications/mattermost` with OpenID Connect, you need to set the ``id`` claim type to *Integer*
|
||||
- BruteForceProtection plugin now prevents authentication on backend if an account is locked
|
||||
- In the Manager API, postLogoutRedirectUri is now `returned and consumed as an array <https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/issues/2347>`__
|
||||
|
||||
2.0.9
|
||||
-----
|
||||
|
|
|
@ -1221,7 +1221,9 @@ components:
|
|||
public:
|
||||
type: boolean
|
||||
postLogoutRedirectUris:
|
||||
type: string
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
logoutType:
|
||||
type: string
|
||||
enum:
|
||||
|
|
|
@ -30,6 +30,8 @@ use constant defaultRoute => 'api.html';
|
|||
sub init {
|
||||
my ( $self, $conf ) = @_;
|
||||
|
||||
$self->ua( Lemonldap::NG::Common::UserAgent->new($conf) );
|
||||
|
||||
# HTML template
|
||||
$self->addRoute( 'api.html', undef, ['GET'] )
|
||||
|
||||
|
|
|
@ -99,6 +99,45 @@ sub _translateOptionConfToApi {
|
|||
return $optionName;
|
||||
}
|
||||
|
||||
sub _translateValueConfToApi {
|
||||
my ( $self, $optionName, $optionValue ) = @_;
|
||||
if ( $optionName eq "oidcRPMetaDataOptionsRedirectUris" ) {
|
||||
return [ split( /\s+/, $optionValue, ) ];
|
||||
}
|
||||
elsif ( $optionName eq "oidcRPMetaDataOptionsPostLogoutRedirectUris" ) {
|
||||
return [ split( /\s+/, $optionValue, ) ];
|
||||
}
|
||||
else {
|
||||
return $optionValue;
|
||||
}
|
||||
}
|
||||
|
||||
sub _translateValueApiToConf {
|
||||
my ( $self, $optionName, $optionValue ) = @_;
|
||||
|
||||
# redirectUris is handled as an array
|
||||
if ( $optionName eq 'redirectUris' ) {
|
||||
die "redirectUris is not an array\n"
|
||||
unless ( ref($optionValue) eq "ARRAY" );
|
||||
return join( ' ', @{$optionValue} );
|
||||
}
|
||||
|
||||
# postLogoutRedirectUris is handled as an array
|
||||
elsif ( $optionName eq 'postLogoutRedirectUris' ) {
|
||||
die "postLogoutRedirectUris is not an array\n"
|
||||
unless ( ref($optionValue) eq "ARRAY" );
|
||||
return join( ' ', @{$optionValue} );
|
||||
}
|
||||
|
||||
# Translate JSON booleans to integers
|
||||
elsif ( JSON::is_bool($optionValue) ) {
|
||||
return $optionValue ? 1 : 0;
|
||||
}
|
||||
else {
|
||||
return $optionValue;
|
||||
}
|
||||
}
|
||||
|
||||
sub _getRegexpFromPattern {
|
||||
my ( $self, $pattern ) = @_;
|
||||
return unless ( $pattern =~ /[\w\.\-\*]+/ );
|
||||
|
@ -127,25 +166,10 @@ sub _getSSOMod {
|
|||
return $mod;
|
||||
}
|
||||
|
||||
sub _fix_bool {
|
||||
my $h = shift;
|
||||
if ( ref($h) eq "HASH" ) {
|
||||
for my $k ( keys %{$h} ) {
|
||||
if ( JSON::is_bool( $h->{$k} ) ) {
|
||||
$h->{$k} = $h->{$k} ? 1 : 0;
|
||||
}
|
||||
else {
|
||||
_fix_bool( $h->{$k} );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub getJsonBody {
|
||||
my ( $self, $req ) = @_;
|
||||
my $obj = $req->jsonBodyToObj;
|
||||
_fix_bool($obj);
|
||||
return $obj;
|
||||
sub _saveApplyConf {
|
||||
my ( $self, $conf ) = @_;
|
||||
$self->_confAcc->saveConf($conf);
|
||||
$self->applyConf($conf);
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -117,7 +117,7 @@ sub findMenuAppByConfKey {
|
|||
|
||||
sub addMenuApp {
|
||||
my ( $self, $req ) = @_;
|
||||
my $add = $self->getJsonBody($req);
|
||||
my $add = $req->jsonBodyToObj;
|
||||
|
||||
my $catConfKey = $req->params('confKey')
|
||||
or return $self->sendError( $req, 'Category confKey is missing', 400 );
|
||||
|
@ -185,7 +185,7 @@ sub updateMenuApp {
|
|||
my $appConfKey = $req->params('appConfKey')
|
||||
or return $self->sendError( $req, 'Application confKey is missing', 400 );
|
||||
|
||||
my $update = $self->getJsonBody($req);
|
||||
my $update = $req->jsonBodyToObj;
|
||||
|
||||
return $self->sendError( $req, "Invalid input: " . $req->error, 400 )
|
||||
unless ($update);
|
||||
|
@ -229,7 +229,7 @@ sub replaceMenuApp {
|
|||
my $appConfKey = $req->params('appConfKey')
|
||||
or return $self->sendError( $req, 'Application confKey is missing', 400 );
|
||||
|
||||
my $replace = $self->getJsonBody($req);
|
||||
my $replace = $req->jsonBodyToObj;
|
||||
|
||||
return $self->sendError( $req, "Invalid input: " . $req->error, 400 )
|
||||
unless ($replace);
|
||||
|
@ -313,7 +313,7 @@ sub deleteMenuApp {
|
|||
delete $conf->{applicationList}->{$catConfKey}->{$appConfKey};
|
||||
|
||||
# Save configuration
|
||||
$self->_confAcc->saveConf($conf);
|
||||
$self->_saveApplyConf($conf);
|
||||
|
||||
return $self->sendJSONresponse( $req, undef, code => 204 );
|
||||
}
|
||||
|
@ -399,7 +399,7 @@ sub _pushMenuApp {
|
|||
}
|
||||
|
||||
# Save configuration
|
||||
$self->_confAcc->saveConf($conf);
|
||||
$self->_saveApplyConf($conf);
|
||||
|
||||
return { res => 'ok' };
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ sub findMenuCatByConfKey {
|
|||
|
||||
sub addMenuCat {
|
||||
my ( $self, $req ) = @_;
|
||||
my $add = $self->getJsonBody($req);
|
||||
my $add = $req->jsonBodyToObj;
|
||||
|
||||
return $self->sendError( $req, "Invalid input: " . $req->error, 400 )
|
||||
unless ($add);
|
||||
|
@ -116,7 +116,7 @@ sub updateMenuCat {
|
|||
my $confKey = $req->params('confKey')
|
||||
or return $self->sendError( $req, 'confKey is missing', 400 );
|
||||
|
||||
my $update = $self->getJsonBody($req);
|
||||
my $update = $req->jsonBodyToObj;
|
||||
|
||||
return $self->sendError( $req, "Invalid input: " . $req->error, 400 )
|
||||
unless ($update);
|
||||
|
@ -147,7 +147,7 @@ sub replaceMenuCat {
|
|||
my $confKey = $req->params('confKey')
|
||||
or return $self->sendError( $req, 'confKey is missing', 400 );
|
||||
|
||||
my $replace = $self->getJsonBody($req);
|
||||
my $replace = $req->jsonBodyToObj;
|
||||
|
||||
return $self->sendError( $req, "Invalid input: " . $req->error, 400 )
|
||||
unless ($replace);
|
||||
|
@ -209,7 +209,7 @@ sub deleteMenuCat {
|
|||
delete $conf->{applicationList}->{$confKey};
|
||||
|
||||
# Save configuration
|
||||
$self->_confAcc->saveConf($conf);
|
||||
$self->_saveApplyConf($conf);
|
||||
|
||||
return $self->sendJSONresponse( $req, undef, code => 204 );
|
||||
}
|
||||
|
@ -262,7 +262,7 @@ sub _pushMenuCat {
|
|||
}
|
||||
|
||||
# Save configuration
|
||||
$self->_confAcc->saveConf($conf);
|
||||
$self->_saveApplyConf($conf);
|
||||
|
||||
return { res => 'ok' };
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ sub _getSessionDBState {
|
|||
|
||||
# Handle DBI-type session stores
|
||||
if ( $fakeobj->{object_store}->isa("Apache::Session::Store::DBI") ) {
|
||||
|
||||
|
||||
# The 'connection' method will fail if the DB is unreachable
|
||||
# this is good enough a test for now
|
||||
eval { $fakeobj->{object_store}->connection($fakeobj) };
|
||||
|
|
|
@ -91,7 +91,7 @@ sub findCasAppsByServiceUrl {
|
|||
|
||||
sub addCasApp {
|
||||
my ( $self, $req ) = @_;
|
||||
my $add = $self->getJsonBody($req);
|
||||
my $add = $req->jsonBodyToObj;
|
||||
|
||||
return $self->sendError( $req, "Invalid input: " . $req->error, 400 )
|
||||
unless ($add);
|
||||
|
@ -153,7 +153,7 @@ sub updateCasApp {
|
|||
my $confKey = $req->params('confKey')
|
||||
or return $self->sendError( $req, 'confKey is missing', 400 );
|
||||
|
||||
my $update = $self->getJsonBody($req);
|
||||
my $update = $req->jsonBodyToObj;
|
||||
|
||||
return $self->sendError( $req, "Invalid input: " . $req->error, 400 )
|
||||
unless ($update);
|
||||
|
@ -190,7 +190,7 @@ sub replaceCasApp {
|
|||
my $confKey = $req->params('confKey')
|
||||
or return $self->sendError( $req, 'confKey is missing', 400 );
|
||||
|
||||
my $replace = $self->getJsonBody($req);
|
||||
my $replace = $req->jsonBodyToObj;
|
||||
|
||||
return $self->sendError( $req, "Invalid input: " . $req->error, 400 )
|
||||
unless ($replace);
|
||||
|
@ -241,7 +241,7 @@ sub deleteCasApp {
|
|||
delete $conf->{casAppMetaDataMacros}->{$confKey};
|
||||
|
||||
# Save configuration
|
||||
$self->_confAcc->saveConf($conf);
|
||||
$self->_saveApplyConf($conf);
|
||||
|
||||
return $self->sendJSONresponse( $req, undef, code => 204 );
|
||||
}
|
||||
|
@ -266,8 +266,10 @@ sub _getCasAppByConfKey {
|
|||
for
|
||||
my $configOption ( keys %{ $conf->{casAppMetaDataOptions}->{$confKey} } )
|
||||
{
|
||||
$options->{ $self->_translateOptionConfToApi($configOption) } =
|
||||
$conf->{casAppMetaDataOptions}->{$confKey}->{$configOption};
|
||||
my $optionName = $self->_translateOptionConfToApi($configOption);
|
||||
my $optionValue = $self->_translateValueConfToApi( $configOption,
|
||||
$conf->{casAppMetaDataOptions}->{$confKey}->{$configOption} );
|
||||
$options->{$optionName} = $optionValue;
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -326,8 +328,11 @@ sub _pushCasApp {
|
|||
if ( defined $push->{options} ) {
|
||||
|
||||
foreach ( keys %{ $push->{options} } ) {
|
||||
$translatedOptions->{ $self->_translateOptionApiToConf( $_,
|
||||
'casApp' ) } = $push->{options}->{$_};
|
||||
my $optionName = $self->_translateOptionApiToConf( $_, 'casApp' );
|
||||
my $optionValue =
|
||||
$self->_translateValueApiToConf( $_, $push->{options}->{$_} );
|
||||
|
||||
$translatedOptions->{$optionName} = $optionValue;
|
||||
}
|
||||
|
||||
my $res = $self->_hasAllowedAttributes( $translatedOptions,
|
||||
|
@ -390,7 +395,7 @@ sub _pushCasApp {
|
|||
}
|
||||
|
||||
# Save configuration
|
||||
$self->_confAcc->saveConf($conf);
|
||||
$self->_saveApplyConf($conf);
|
||||
|
||||
return { res => 'ok' };
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ sub findOidcRpByClientId {
|
|||
|
||||
sub addOidcRp {
|
||||
my ( $self, $req ) = @_;
|
||||
my $add = $self->getJsonBody($req);
|
||||
my $add = $req->jsonBodyToObj;
|
||||
|
||||
return $self->sendError( $req, "Invalid input: " . $req->error, 400 )
|
||||
unless ($add);
|
||||
|
@ -161,7 +161,7 @@ sub updateOidcRp {
|
|||
my $confKey = $req->params('confKey')
|
||||
or return $self->sendError( $req, 'confKey is missing', 400 );
|
||||
|
||||
my $update = $self->getJsonBody($req);
|
||||
my $update = $req->jsonBodyToObj;
|
||||
|
||||
return $self->sendError( $req, "Invalid input: " . $req->error, 400 )
|
||||
unless ($update);
|
||||
|
@ -199,7 +199,7 @@ sub replaceOidcRp {
|
|||
my $confKey = $req->params('confKey')
|
||||
or return $self->sendError( $req, 'confKey is missing', 400 );
|
||||
|
||||
my $replace = $self->getJsonBody($req);
|
||||
my $replace = $req->jsonBodyToObj;
|
||||
|
||||
return $self->sendError( $req, "Invalid input: " . $req->error, 400 )
|
||||
unless ($replace);
|
||||
|
@ -269,7 +269,7 @@ sub deleteOidcRp {
|
|||
delete $conf->{oidcRPMetaDataMacros}->{$confKey};
|
||||
|
||||
# Save configuration
|
||||
$self->_confAcc->saveConf($conf);
|
||||
$self->_saveApplyConf($conf);
|
||||
|
||||
return $self->sendJSONresponse( $req, undef, code => 204 );
|
||||
}
|
||||
|
@ -301,22 +301,10 @@ sub _getOidcRpByConfKey {
|
|||
for
|
||||
my $configOption ( keys %{ $conf->{oidcRPMetaDataOptions}->{$confKey} } )
|
||||
{
|
||||
# redirectUris is handled as an array
|
||||
if ( $configOption eq "oidcRPMetaDataOptionsRedirectUris" ) {
|
||||
$redirectUris = [
|
||||
split(
|
||||
/\s+/,
|
||||
$conf->{oidcRPMetaDataOptions}->{$confKey}->{$configOption}
|
||||
)
|
||||
];
|
||||
$options->{ $self->_translateOptionConfToApi($configOption) } =
|
||||
$redirectUris;
|
||||
|
||||
}
|
||||
else {
|
||||
$options->{ $self->_translateOptionConfToApi($configOption) } =
|
||||
$conf->{oidcRPMetaDataOptions}->{$confKey}->{$configOption};
|
||||
}
|
||||
my $apiName = $self->_translateOptionConfToApi($configOption);
|
||||
my $apiValue = $self->_translateValueConfToApi( $configOption,
|
||||
$conf->{oidcRPMetaDataOptions}->{$confKey}->{$configOption} );
|
||||
$options->{$apiName} = $apiValue;
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -377,21 +365,17 @@ sub _pushOidcRp {
|
|||
|
||||
foreach ( keys %{ $push->{options} } ) {
|
||||
|
||||
# redirectUris is handled as an array
|
||||
if ( $_ eq 'redirectUris' ) {
|
||||
my $option = $push->{options}->{$_};
|
||||
my $optionName = $self->_translateOptionApiToConf( $_, 'oidcRP' );
|
||||
eval {
|
||||
my $optionValue =
|
||||
$self->_translateValueApiToConf( $_, $push->{options}->{$_} );
|
||||
$translatedOptions->{$optionName} = $optionValue;
|
||||
};
|
||||
if ($@) {
|
||||
return {
|
||||
res => 'ko',
|
||||
msg => "Invalid input: redirectUris is not an array"
|
||||
}
|
||||
unless ( ref($option) eq "ARRAY" );
|
||||
|
||||
$translatedOptions->{ $self->_translateOptionApiToConf( $_,
|
||||
'oidcRP' ) } = join( ' ', @{ $push->{options}->{$_} } );
|
||||
}
|
||||
else {
|
||||
$translatedOptions->{ $self->_translateOptionApiToConf( $_,
|
||||
'oidcRP' ) } = $push->{options}->{$_};
|
||||
msg => "Invalid input: $@",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -475,7 +459,7 @@ sub _pushOidcRp {
|
|||
}
|
||||
|
||||
# Save configuration
|
||||
$self->_confAcc->saveConf($conf);
|
||||
$self->_saveApplyConf($conf);
|
||||
|
||||
return { res => 'ok' };
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ sub findSamlSpByEntityId {
|
|||
|
||||
sub addSamlSp {
|
||||
my ( $self, $req ) = @_;
|
||||
my $add = $self->getJsonBody($req);
|
||||
my $add = $req->jsonBodyToObj;
|
||||
|
||||
return $self->sendError( $req, "Invalid input: " . $req->error, 400 )
|
||||
unless ($add);
|
||||
|
@ -140,7 +140,7 @@ sub replaceSamlSp {
|
|||
my $confKey = $req->params('confKey')
|
||||
or return $self->sendError( $req, 'confKey is missing', 400 );
|
||||
|
||||
my $replace = $self->getJsonBody($req);
|
||||
my $replace = $req->jsonBodyToObj;
|
||||
|
||||
return $self->sendError( $req, "Invalid input: " . $req->error, 400 )
|
||||
unless ($replace);
|
||||
|
@ -180,7 +180,7 @@ sub updateSamlSp {
|
|||
my $confKey = $req->params('confKey')
|
||||
or return $self->sendError( $req, 'confKey is missing', 400 );
|
||||
|
||||
my $update = $self->getJsonBody($req);
|
||||
my $update = $req->jsonBodyToObj;
|
||||
|
||||
return $self->sendError( $req, "Invalid input: " . $req->error, 400 )
|
||||
unless ($update);
|
||||
|
@ -237,7 +237,7 @@ sub deleteSamlSp {
|
|||
delete $conf->{samlSPMetaDataMacros}->{$confKey};
|
||||
|
||||
# Save configuration
|
||||
$self->_confAcc->saveConf($conf);
|
||||
$self->_saveApplyConf($conf);
|
||||
|
||||
return $self->sendJSONresponse( $req, undef, code => 204 );
|
||||
}
|
||||
|
@ -255,8 +255,11 @@ sub _getSamlSpByConfKey {
|
|||
my $options = {};
|
||||
for my $confOption ( keys %{ $conf->{samlSPMetaDataOptions}->{$confKey} } )
|
||||
{
|
||||
$options->{ $self->_translateOptionConfToApi($confOption) } =
|
||||
$conf->{samlSPMetaDataOptions}->{$confKey}->{$confOption};
|
||||
my $optionName = $self->_translateOptionConfToApi($confOption);
|
||||
my $optionValue = $self->_translateValueConfToApi( $confOption,
|
||||
$conf->{samlSPMetaDataOptions}->{$confKey}->{$confOption} );
|
||||
|
||||
$options->{$optionName} = $optionValue;
|
||||
}
|
||||
|
||||
# Get macros
|
||||
|
@ -372,13 +375,16 @@ sub _pushSamlSp {
|
|||
}
|
||||
|
||||
$conf->{samlSPMetaDataXML}->{$confKey}->{samlSPMetaDataXML} =
|
||||
$push->{metadata};
|
||||
$push->{metadata}
|
||||
if defined $push->{metadata};
|
||||
|
||||
if ( defined $push->{options} ) {
|
||||
|
||||
foreach ( keys %{ $push->{options} } ) {
|
||||
$translatedOptions->{ $self->_translateOptionApiToConf( $_,
|
||||
'samlSP' ) } = $push->{options}->{$_};
|
||||
my $optionName = $self->_translateOptionApiToConf( $_, 'samlSP' );
|
||||
my $optionValue =
|
||||
$self->_translateValueApiToConf( $_, $push->{options}->{$_} );
|
||||
$translatedOptions->{$optionName} = $optionValue;
|
||||
}
|
||||
|
||||
my $res = $self->_hasAllowedAttributes( $translatedOptions,
|
||||
|
@ -434,7 +440,7 @@ sub _pushSamlSp {
|
|||
}
|
||||
|
||||
# Save configuration
|
||||
$self->_confAcc->saveConf($conf);
|
||||
$self->_saveApplyConf($conf);
|
||||
|
||||
return { res => 'ok' };
|
||||
}
|
||||
|
|
|
@ -30,8 +30,6 @@ our $VERSION = '2.0.10';
|
|||
|
||||
use constant defaultRoute => 'manager.html';
|
||||
|
||||
has ua => ( is => 'rw' );
|
||||
|
||||
sub init {
|
||||
my ( $self, $conf ) = @_;
|
||||
$self->ua( Lemonldap::NG::Common::UserAgent->new($conf) );
|
||||
|
@ -306,12 +304,10 @@ sub prx {
|
|||
# IV. Upload methods #
|
||||
######################
|
||||
|
||||
# In this section, 4 methods:
|
||||
# In this section, 3 methods:
|
||||
# - getConfByNum: override SUPER method to be able to use Zero
|
||||
# - newConf()
|
||||
# - newConf(), load a new configuration and invokes reloadUrls
|
||||
# - newRawConf(): restore a saved conf
|
||||
# - applyConf(): called by the 2 previous to inform other servers that a new
|
||||
# configuration is available
|
||||
|
||||
sub getConfByNum {
|
||||
my ( $self, $cfgNum, @args ) = @_;
|
||||
|
@ -479,65 +475,6 @@ sub newRawConf {
|
|||
return $self->sendJSONresponse( $req, $res );
|
||||
}
|
||||
|
||||
## @method private applyConf()
|
||||
# Try to inform other servers declared in `reloadUrls` that a new
|
||||
# configuration is available.
|
||||
#
|
||||
#@return reload status as boolean
|
||||
sub applyConf {
|
||||
my ( $self, $newConf ) = @_;
|
||||
my $status;
|
||||
|
||||
# 1 Apply conf locally
|
||||
$self->p->api->checkConf();
|
||||
|
||||
# Get apply section values
|
||||
my %reloadUrls =
|
||||
%{ $self->confAcc->getLocalConf( APPLYSECTION, undef, 0 ) };
|
||||
if ( !%reloadUrls && $newConf->{reloadUrls} ) {
|
||||
%reloadUrls = %{ $newConf->{reloadUrls} };
|
||||
}
|
||||
return {} unless (%reloadUrls);
|
||||
|
||||
$self->ua->timeout( $newConf->{reloadTimeout} );
|
||||
|
||||
# Parse apply values
|
||||
while ( my ( $host, $request ) = each %reloadUrls ) {
|
||||
my $r = HTTP::Request->new( 'GET', "http://$host$request" );
|
||||
$self->logger->debug("Sending reload request to $host");
|
||||
if ( $request =~ /^https?:\/\/[^\/]+.*$/ ) {
|
||||
my $url = URI::URL->new($request);
|
||||
my $targetUrl = $url->scheme . "://" . $host;
|
||||
$targetUrl .= ":" . $url->port if defined( $url->port );
|
||||
$targetUrl .= $url->full_path;
|
||||
$r =
|
||||
HTTP::Request->new( 'GET', $targetUrl,
|
||||
HTTP::Headers->new( Host => $url->host ) );
|
||||
if ( defined $url->userinfo
|
||||
&& $url->userinfo =~ /^([^:]+):(.*)$/ )
|
||||
{
|
||||
$r->authorization_basic( $1, $2 );
|
||||
}
|
||||
}
|
||||
|
||||
my $response = $self->ua->request($r);
|
||||
if ( $response->code != 200 ) {
|
||||
$status->{$host} =
|
||||
"Error " . $response->code . " (" . $response->message . ")";
|
||||
$self->logger->error( "Apply configuration for $host: error "
|
||||
. $response->code . " ("
|
||||
. $response->message
|
||||
. ")" );
|
||||
}
|
||||
else {
|
||||
$status->{$host} = "OK";
|
||||
$self->logger->notice("Apply configuration for $host: ok");
|
||||
}
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
sub diff {
|
||||
my ( $self, $req, @path ) = @_;
|
||||
return $self->sendError( $req, 'to many arguments in path info', 400 )
|
||||
|
|
|
@ -2,6 +2,9 @@ package Lemonldap::NG::Manager::Plugin;
|
|||
|
||||
use strict;
|
||||
use Mouse;
|
||||
use Lemonldap::NG::Common::UserAgent;
|
||||
use Lemonldap::NG::Common::Conf::Constants;
|
||||
use URI::URL;
|
||||
|
||||
our $VERSION = '2.0.10';
|
||||
|
||||
|
@ -13,6 +16,14 @@ has _confAcc => (
|
|||
default => sub { return $_[0]->p->{_confAcc} },
|
||||
);
|
||||
|
||||
has ua => (
|
||||
is => 'rw',
|
||||
lazy => 1,
|
||||
builder => sub {
|
||||
Lemonldap::NG::Common::UserAgent->new( $_[0]->{conf} );
|
||||
}
|
||||
);
|
||||
|
||||
sub sendError {
|
||||
my $self = shift;
|
||||
return $self->p->sendError(@_);
|
||||
|
@ -49,4 +60,63 @@ sub loadTemplate {
|
|||
return $self->p->loadTemplate(@_);
|
||||
}
|
||||
|
||||
## @method private applyConf()
|
||||
# Try to inform other servers declared in `reloadUrls` that a new
|
||||
# configuration is available.
|
||||
#
|
||||
#@return reload status as boolean
|
||||
sub applyConf {
|
||||
my ( $self, $newConf ) = @_;
|
||||
my $status;
|
||||
|
||||
# 1 Apply conf locally
|
||||
$self->p->api->checkConf();
|
||||
|
||||
# Get apply section values
|
||||
my %reloadUrls =
|
||||
%{ $self->confAcc->getLocalConf( APPLYSECTION, undef, 0 ) };
|
||||
if ( !%reloadUrls && $newConf->{reloadUrls} ) {
|
||||
%reloadUrls = %{ $newConf->{reloadUrls} };
|
||||
}
|
||||
return {} unless (%reloadUrls);
|
||||
|
||||
$self->ua->timeout( $newConf->{reloadTimeout} );
|
||||
|
||||
# Parse apply values
|
||||
while ( my ( $host, $request ) = each %reloadUrls ) {
|
||||
my $r = HTTP::Request->new( 'GET', "http://$host$request" );
|
||||
$self->logger->debug("Sending reload request to $host");
|
||||
if ( $request =~ /^https?:\/\/[^\/]+.*$/ ) {
|
||||
my $url = URI::URL->new($request);
|
||||
my $targetUrl = $url->scheme . "://" . $host;
|
||||
$targetUrl .= ":" . $url->port if defined( $url->port );
|
||||
$targetUrl .= $url->full_path;
|
||||
$r =
|
||||
HTTP::Request->new( 'GET', $targetUrl,
|
||||
HTTP::Headers->new( Host => $url->host ) );
|
||||
if ( defined $url->userinfo
|
||||
&& $url->userinfo =~ /^([^:]+):(.*)$/ )
|
||||
{
|
||||
$r->authorization_basic( $1, $2 );
|
||||
}
|
||||
}
|
||||
|
||||
my $response = $self->ua->request($r);
|
||||
if ( $response->code != 200 ) {
|
||||
$status->{$host} =
|
||||
"Error " . $response->code . " (" . $response->message . ")";
|
||||
$self->logger->error( "Apply configuration for $host: error "
|
||||
. $response->code . " ("
|
||||
. $response->message
|
||||
. ")" );
|
||||
}
|
||||
else {
|
||||
$status->{$host} = "OK";
|
||||
$self->logger->notice("Apply configuration for $host: ok");
|
||||
}
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -243,7 +243,7 @@ sub checkFindByConfKey {
|
|||
my ( $test, $type, $confKey, $expectedHits ) = splice @_;
|
||||
my $res = findByConfKey( $test, $type, $confKey );
|
||||
check200( $test, $res );
|
||||
my $hits = from_json( $res->[2]->[0] );
|
||||
my $hits = from_json( $res->[2]->[0] );
|
||||
my $counter = @{$hits};
|
||||
ok(
|
||||
$counter eq $expectedHits,
|
||||
|
@ -276,7 +276,7 @@ sub checkFindByProviderId {
|
|||
($gotProviderId) = $result->{metadata} =~ m/entityID=['"](.+?)['"]/i;
|
||||
}
|
||||
elsif ( $providerIdName eq 'serviceUrl' ) {
|
||||
$gotProviderId = $result->{options}->{service};
|
||||
$gotProviderId = $result->{options}->{service};
|
||||
}
|
||||
else {
|
||||
$gotProviderId = $result->{$providerIdName};
|
||||
|
@ -338,7 +338,9 @@ my $oidcRp = {
|
|||
},
|
||||
options => {
|
||||
clientSecret => 'secret',
|
||||
icon => 'web.png'
|
||||
icon => 'web.png',
|
||||
postLogoutRedirectUris =>
|
||||
[ "http://url/logout1", "http://url/logout2" ],
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -378,6 +380,8 @@ checkGet( $test, 'oidc/rp', 'myOidcRp1', 'extraClaims/phone',
|
|||
'telephoneNumber' );
|
||||
checkGet( $test, 'oidc/rp', 'myOidcRp1', 'options/redirectUris/1',
|
||||
'http://url/2' );
|
||||
checkGet( $test, 'oidc/rp', 'myOidcRp1', 'options/postLogoutRedirectUris/1',
|
||||
'http://url/logout2' );
|
||||
|
||||
$test = "OidcRp - Update should fail on non existing options";
|
||||
$oidcRp->{options}->{playingPossum} = 'elephant';
|
||||
|
@ -411,6 +415,7 @@ $test = "OidcRp - Replace should succeed";
|
|||
$oidcRp->{confKey} = 'myOidcRp2';
|
||||
$oidcRp->{clientId} = 'myOidcClient2';
|
||||
$oidcRp->{redirectUris} = ["http://url/3"];
|
||||
$oidcRp->{options}->{postLogoutRedirectUris} = [];
|
||||
delete $oidcRp->{options}->{icon};
|
||||
delete $oidcRp->{options}->{IDTokenSignAlg};
|
||||
checkReplace( $test, 'oidc/rp', 'myOidcRp2', $oidcRp );
|
||||
|
@ -419,6 +424,8 @@ $test = "OidcRp - Check attribute default value was set after replace";
|
|||
checkGet( $test, 'oidc/rp', 'myOidcRp2', 'options/IDTokenSignAlg', 'HS512' );
|
||||
checkGet( $test, 'oidc/rp', 'myOidcRp2', 'options/redirectUris/0',
|
||||
'http://url/3' );
|
||||
checkGet( $test, 'oidc/rp', 'myOidcRp2', 'options/postLogoutRedirectUris/0',
|
||||
'' );
|
||||
|
||||
$test = "OidcRp - Replace should fail on non existing or invalid options";
|
||||
$oidcRp->{options}->{playingPossum} = 'elephant';
|
||||
|
@ -631,9 +638,9 @@ checkDeleteNotFound( $test, 'saml/sp', 'mySamlSp1' );
|
|||
my $casApp = {
|
||||
confKey => 'myCasApp1',
|
||||
exportedVars => {
|
||||
"cn" => "cn",
|
||||
"uid" => "uid",
|
||||
"mail" => "mail"
|
||||
"cn" => "cn",
|
||||
"uid" => "uid",
|
||||
"mail" => "mail"
|
||||
},
|
||||
macros => {
|
||||
given_name => '$firstName',
|
||||
|
@ -647,7 +654,8 @@ my $casApp = {
|
|||
|
||||
$test = "CasApp - Add should succeed";
|
||||
checkAdd( $test, 'cas/app', $casApp );
|
||||
checkGet( $test, 'cas/app', 'myCasApp1', 'options/service', 'http://mycasapp.example.com' );
|
||||
checkGet( $test, 'cas/app', 'myCasApp1', 'options/service',
|
||||
'http://mycasapp.example.com' );
|
||||
checkGet( $test, 'cas/app', 'myCasApp1', 'options/userAttribute', 'uid' );
|
||||
checkGet( $test, 'cas/app', 'myCasApp1', 'options/rule', '$uid eq \'dwho\'' );
|
||||
|
||||
|
@ -656,18 +664,19 @@ checkAddFailsIfExists( $test, 'cas/app', $casApp );
|
|||
|
||||
$test = "CasApp - Update should succeed and keep existing values";
|
||||
$casApp->{options}->{service} = 'http://mycasapp.acme.com';
|
||||
$casApp->{options}->{userAttribute} = 'cn';
|
||||
$casApp->{options}->{userAttribute} = 'cn';
|
||||
delete $casApp->{options}->{rule};
|
||||
delete $casApp->{macros};
|
||||
delete $casApp->{exportedVars};
|
||||
$casApp->{macros}->{given_name} = '$givenName';
|
||||
$casApp->{exportedVars}->{cn} = 'uid';
|
||||
checkUpdate( $test, 'cas/app', 'myCasApp1', $casApp );
|
||||
checkGet( $test, 'cas/app', 'myCasApp1', 'options/service', 'http://mycasapp.acme.com' );
|
||||
checkGet( $test, 'cas/app', 'myCasApp1', 'options/service',
|
||||
'http://mycasapp.acme.com' );
|
||||
checkGet( $test, 'cas/app', 'myCasApp1', 'options/userAttribute', 'cn' );
|
||||
checkGet( $test, 'cas/app', 'myCasApp1', 'options/rule', '$uid eq \'dwho\'' );
|
||||
checkGet( $test, 'cas/app', 'myCasApp1', 'exportedVars/cn', 'uid' );
|
||||
checkGet( $test, 'cas/app', 'myCasApp1', 'exportedVars/uid', 'uid' );
|
||||
checkGet( $test, 'cas/app', 'myCasApp1', 'exportedVars/cn', 'uid' );
|
||||
checkGet( $test, 'cas/app', 'myCasApp1', 'exportedVars/uid', 'uid' );
|
||||
checkGet( $test, 'cas/app', 'myCasApp1', 'macros/given_name', '$givenName' );
|
||||
|
||||
$test = "CasApp - Update should fail on non existing options";
|
||||
|
@ -675,14 +684,14 @@ $casApp->{options}->{playingPossum} = 'elephant';
|
|||
checkUpdateWithUnknownAttributes( $test, 'cas/app', 'myCasApp1', $casApp );
|
||||
delete $casApp->{options}->{playingPossum};
|
||||
|
||||
$test = "CasApp - Add should fail on non existing options";
|
||||
$casApp->{confKey} = 'myCasApp2';
|
||||
$casApp->{options}->{service} = 'http://mycasapp.skynet.com';
|
||||
$test = "CasApp - Add should fail on non existing options";
|
||||
$casApp->{confKey} = 'myCasApp2';
|
||||
$casApp->{options}->{service} = 'http://mycasapp.skynet.com';
|
||||
$casApp->{options}->{playingPossum} = 'ElephantInTheRoom';
|
||||
checkAddWithUnknownAttributes( $test, 'cas/app', $casApp );
|
||||
delete $casApp->{options}->{playingPossum};
|
||||
|
||||
$test = "CasApp - Add should fail because service host already exists";
|
||||
$test = "CasApp - Add should fail because service host already exists";
|
||||
$casApp->{options}->{service} = 'http://mycasapp.acme.com/ignoredbyissuer';
|
||||
checkAddFailsIfExists( $test, 'cas/app', $casApp );
|
||||
|
||||
|
@ -694,10 +703,10 @@ $test = "CasApp - Update should fail if confKey not found";
|
|||
$casApp->{confKey} = 'myCasApp3';
|
||||
checkUpdateNotFound( $test, 'cas/app', 'myCasApp3', $casApp );
|
||||
|
||||
$test = "CasApp - Replace should succeed";
|
||||
$casApp->{confKey} = 'myCasApp2';
|
||||
$test = "CasApp - Replace should succeed";
|
||||
$casApp->{confKey} = 'myCasApp2';
|
||||
checkGet( $test, 'cas/app', 'myCasApp2', 'options/userAttribute', 'cn' );
|
||||
$casApp->{options}->{userAttribute} = 'uid';
|
||||
$casApp->{options}->{userAttribute} = 'uid';
|
||||
checkReplace( $test, 'cas/app', 'myCasApp2', $casApp );
|
||||
checkGet( $test, 'cas/app', 'myCasApp2', 'options/userAttribute', 'uid' );
|
||||
|
||||
|
|
|
@ -200,6 +200,10 @@ sub storeEnv {
|
|||
$req->pdata->{targetAuthnLevel} = $targetAuthnLevel
|
||||
if $targetAuthnLevel;
|
||||
}
|
||||
if ( $login->request ) {
|
||||
my $acs = $login->request->AssertionConsumerServiceURL;
|
||||
$req->env->{llng_saml_acs} = $acs if $acs;
|
||||
}
|
||||
}
|
||||
return PE_OK;
|
||||
}
|
||||
|
@ -407,6 +411,15 @@ sub run {
|
|||
$self->logger->debug("$sp match $spConfKey SP in configuration");
|
||||
$req->env->{llng_saml_spconfkey} = $spConfKey;
|
||||
|
||||
if ( $login->request ) {
|
||||
my $acs = $login->request->AssertionConsumerServiceURL;
|
||||
if ($acs) {
|
||||
$req->env->{llng_saml_acs} = $acs;
|
||||
$self->logger->debug(
|
||||
"Using AssertionConsumerServiceURL $acs");
|
||||
}
|
||||
}
|
||||
|
||||
# Check access rule
|
||||
if ( my $rule = $self->spRules->{$spConfKey} ) {
|
||||
unless ( $rule->( $req, $req->sessionInfo ) ) {
|
||||
|
|
|
@ -476,10 +476,10 @@ $(window).on 'load', () ->
|
|||
|
||||
if result
|
||||
$('.ppolicy').removeClass('border-danger').addClass 'border-success'
|
||||
$('#newpassword')[0].setCustomValidity('')
|
||||
$('#newpassword').get(0)?.setCustomValidity('')
|
||||
else
|
||||
$('.ppolicy').removeClass('border-success').addClass 'border-danger'
|
||||
$('#newpassword')[0].setCustomValidity(translate('PE28'))
|
||||
$('#newpassword').get(0)?.setCustomValidity(translate('PE28'))
|
||||
return
|
||||
|
||||
if window.datas.ppolicy? and $('#newpassword').length
|
||||
|
@ -490,16 +490,30 @@ $(window).on 'load', () ->
|
|||
checkpassword e.target.value
|
||||
return
|
||||
|
||||
# If generating password, disable policy check
|
||||
togglecheckpassword = (e) ->
|
||||
if e.target.checked
|
||||
$('#newpassword').off('keyup')
|
||||
$('#newpassword').get(0)?.setCustomValidity('')
|
||||
# Restore check
|
||||
else
|
||||
$('#newpassword').keyup (e) ->
|
||||
checkpassword e.target.value
|
||||
return
|
||||
checkpassword ''
|
||||
|
||||
checksamepass = () ->
|
||||
if $('#confirmpassword')[0].value == $('#newpassword')[0].value
|
||||
$('#confirmpassword')[0].setCustomValidity('')
|
||||
if $('#confirmpassword').get(0)?.value == $('#newpassword').get(0)?.value
|
||||
$('#confirmpassword').get(0)?.setCustomValidity('')
|
||||
return true
|
||||
else
|
||||
$('#confirmpassword')[0].setCustomValidity(translate('PE34'))
|
||||
$('#confirmpassword').get(0)?.setCustomValidity(translate('PE34'))
|
||||
return false
|
||||
|
||||
$('#newpassword').change checksamepass
|
||||
$('#confirmpassword').change checksamepass
|
||||
if window.datas.ppolicy? and $('#newpassword').length
|
||||
$('#reset').change togglecheckpassword
|
||||
|
||||
# Ping if asked
|
||||
if datas['pingInterval'] and datas['pingInterval'] > 0
|
||||
|
|
|
@ -233,7 +233,7 @@ LemonLDAP::NG Portal jQuery scripts
|
|||
datas = {};
|
||||
|
||||
$(window).on('load', function() {
|
||||
var action, al, authMenuIndex, authMenuTabs, back_url, checkpassword, checksamepass, hiddenParams, isAlphaNumeric, l, lang, langdiv, langs, langs2, len1, len2, len3, len4, link, m, menuIndex, menuTabs, method, n, nl, nlangs, o, queryLang, re, ref, ref1, ref2, setCookieLang;
|
||||
var action, al, authMenuIndex, authMenuTabs, back_url, checkpassword, checksamepass, hiddenParams, isAlphaNumeric, l, lang, langdiv, langs, langs2, len1, len2, len3, len4, link, m, menuIndex, menuTabs, method, n, nl, nlangs, o, queryLang, re, ref, ref1, ref2, setCookieLang, togglecheckpassword;
|
||||
datas = getValues();
|
||||
if ("datas" in window && "choicetab" in window.datas) {
|
||||
datas.choicetab = window.datas.choicetab;
|
||||
|
@ -408,7 +408,7 @@ LemonLDAP::NG Portal jQuery scripts
|
|||
return false;
|
||||
};
|
||||
checkpassword = function(password) {
|
||||
var digit, hasforbidden, i, len, lower, nonwhitespechar, numspechar, result, upper;
|
||||
var digit, hasforbidden, i, len, lower, nonwhitespechar, numspechar, ref3, ref4, result, upper;
|
||||
result = true;
|
||||
if (window.datas.ppolicy.minsize > 0) {
|
||||
if (password.length >= window.datas.ppolicy.minsize) {
|
||||
|
@ -514,10 +514,14 @@ LemonLDAP::NG Portal jQuery scripts
|
|||
}
|
||||
if (result) {
|
||||
$('.ppolicy').removeClass('border-danger').addClass('border-success');
|
||||
$('#newpassword')[0].setCustomValidity('');
|
||||
if ((ref3 = $('#newpassword').get(0)) != null) {
|
||||
ref3.setCustomValidity('');
|
||||
}
|
||||
} else {
|
||||
$('.ppolicy').removeClass('border-success').addClass('border-danger');
|
||||
$('#newpassword')[0].setCustomValidity(translate('PE28'));
|
||||
if ((ref4 = $('#newpassword').get(0)) != null) {
|
||||
ref4.setCustomValidity(translate('PE28'));
|
||||
}
|
||||
}
|
||||
};
|
||||
if ((window.datas.ppolicy != null) && $('#newpassword').length) {
|
||||
|
@ -526,17 +530,37 @@ LemonLDAP::NG Portal jQuery scripts
|
|||
checkpassword(e.target.value);
|
||||
});
|
||||
}
|
||||
togglecheckpassword = function(e) {
|
||||
var ref3;
|
||||
if (e.target.checked) {
|
||||
$('#newpassword').off('keyup');
|
||||
return (ref3 = $('#newpassword').get(0)) != null ? ref3.setCustomValidity('') : void 0;
|
||||
} else {
|
||||
$('#newpassword').keyup(function(e) {
|
||||
checkpassword(e.target.value);
|
||||
});
|
||||
return checkpassword('');
|
||||
}
|
||||
};
|
||||
checksamepass = function() {
|
||||
if ($('#confirmpassword')[0].value === $('#newpassword')[0].value) {
|
||||
$('#confirmpassword')[0].setCustomValidity('');
|
||||
var ref3, ref4, ref5, ref6;
|
||||
if (((ref3 = $('#confirmpassword').get(0)) != null ? ref3.value : void 0) === ((ref4 = $('#newpassword').get(0)) != null ? ref4.value : void 0)) {
|
||||
if ((ref5 = $('#confirmpassword').get(0)) != null) {
|
||||
ref5.setCustomValidity('');
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
$('#confirmpassword')[0].setCustomValidity(translate('PE34'));
|
||||
if ((ref6 = $('#confirmpassword').get(0)) != null) {
|
||||
ref6.setCustomValidity(translate('PE34'));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
$('#newpassword').change(checksamepass);
|
||||
$('#confirmpassword').change(checksamepass);
|
||||
if ((window.datas.ppolicy != null) && $('#newpassword').length) {
|
||||
$('#reset').change(togglecheckpassword);
|
||||
}
|
||||
if (datas['pingInterval'] && datas['pingInterval'] > 0) {
|
||||
window.setTimeout(ping, datas['pingInterval']);
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user