diff --git a/.ctags b/.ctags new file mode 100644 index 000000000..2f0cd7daa --- /dev/null +++ b/.ctags @@ -0,0 +1,2 @@ +--exclude=*/blib/* +--exclude=*.js diff --git a/.gitignore b/.gitignore index e8c967efd..5b71b86a1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .gitignore .vstags +tags doc/sources/admin/_build/ doc/pages/documentation/ e2e-tests/conf diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d9ae605ba..a7e8bab7c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,15 +1,26 @@ -.build_job: &job_build +.build_job: stage: build - script: - - apt-get update && apt-get -y dist-upgrade - # Workaround for bionic - - DEBIAN_FRONTEND=noninteractive apt-get -y install tzdata - - ci-build-pkg + retry: 1 artifacts: expire_in: 1 hour paths: - result/* + except: + variables: + - $SONARJOB == "1" +.debian_build_job: + extends: .build_job + script: + - apt-get update && apt-get -y dist-upgrade + - DEBIAN_FRONTEND=noninteractive apt-get -y install tzdata + - ci-build-pkg + before_script: + - env | grep ^CI_ + # Converting to native package... + - sed -i "1{s/-1) /$suffix) /}" debian/changelog + - sed -i "1{s/-2) /$suffix) /}" debian/changelog + - sed -i 's/3.0 (quilt)/3.0 (native)/' debian/source/format stages: - build @@ -18,10 +29,6 @@ stages: before_script: - env | grep ^CI_ - # Converting to native package... - - sed -i "1{s/-1) /$suffix) /}" debian/changelog - - sed -i "1{s/-2) /$suffix) /}" debian/changelog - - sed -i 's/3.0 (quilt)/3.0 (native)/' debian/source/format autopkgtest: stage: build @@ -34,47 +41,40 @@ autopkgtest: - make -j8 autopkgtest build_stretch: + extends: .debian_build_job image: buildpkg/debian:stretch - <<: *job_build build_buster: + extends: .debian_build_job image: buildpkg/debian:buster - <<: *job_build -build_disco: - image: buildpkg/ubuntu:disco - <<: *job_build +#build_xenial: +# extends: .debian_build_job +# image: buildpkg/ubuntu:xenial +# <<: *job_build build_bionic: + extends: .debian_build_job image: buildpkg/ubuntu:bionic - <<: *job_build build_centos_7: + extends: .build_job image: buildpkg/centos:7 - stage: build script: - rm -f /etc/yum.repos.d/CentOS-Sources.repo - yum -y install epel-release - make rpm-dist - ci-build-pkg - artifacts: - expire_in: 1 day - paths: - - result/* build_centos_8: + extends: .build_job image: buildpkg/centos:8 - stage: build script: - yum-config-manager --enable PowerTools - yum-config-manager --enable AppStream - yum -y install epel-release - make rpm-dist - ci-build-pkg - artifacts: - expire_in: 1 day - paths: - - result/* sign: image: buildpkg/debian:stretch @@ -123,3 +123,25 @@ pages: only: - master +sonar-inspect: + image: buildpkg/debian:buster + stage: build + script: + - scripts/sonar + artifacts: + expire_in: 1 hour + paths: + - lemonldap-ng-*/cover_db/sonar_generic.xml + - lemonldap-ng-*/perlcritic_report.txt + only: + variables: + - $SONARJOB == "1" + +sonar-upload: + stage: deploy + image: sonarsource/sonar-scanner-cli + script: + - sonar-scanner + only: + variables: + - $SONARJOB == "1" diff --git a/AUTHORS b/AUTHORS index de8f94141..6de0afac1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,6 +1,5 @@ LemonLDAP::NG Core team: * Maxime BESSON - * David COUTADEUR * Xavier GUIMARD * Christophe MAUDOUX * Clément OUDOT @@ -10,9 +9,10 @@ Past and present contributors: * Casimir ANTUNES * Sébastien BAHLOUL * Oliver BOIREAU - * Jean-Thomas CHECCO * Sandro CAZZANIGA + * Jean-Thomas CHECCO * Thomas CHEMINEAU + * David COUTADEUR * François-Xavier DELTOMBE * Sebastien DIAZ * Soisik FROGIER diff --git a/RELEASE b/RELEASE index d06e877b8..dab1c808e 100644 --- a/RELEASE +++ b/RELEASE @@ -5,7 +5,7 @@ The version ----------- - The release version should be updated in the following location: -* changelog (add a changelog from GitLab for the target version) +* changelog: change version in scripts/generate-changelog.pl and run it * Main modules (Common.pm/Handler.pm/Portal.pm/Manager.pm) * Makefile.PL for cross-dependencies @@ -90,18 +90,21 @@ Upload dist and bundles on sftp://release-up.ow2.org/projects/lemonldap - RPM: see rpm/REDAME - DEB: -The DEB repository is hosted on http://lemonldap-ng.org/deb +The DEB repository is hosted on https://lemonldap-ng.org/deb Copy all generated files (*.deb): -$ scp *.deb lemonldapng@lemonldap-ng.org:incoming/ +$ scp *.deb lemonldap-ng@lemonldap-ng.org:incoming/ -Then connect on the server and launch reprepro: -$ ssh lemonldapng@lemonldap-ng.org -lemonldapng@lemonldap-ng.org$ cd deb/ -lemonldapng@sd-22107:~/deb$ reprepro --ask-passphrase -Vb . includedeb stable ../incoming/*VERSION*deb -lemonldapng@sd-22107:~/deb$ reprepro --ask-passphrase -Vb . includedeb 2.0 ../incoming/*VERSION*deb +On the server, add packages is the appropriate repository, for example: +$ aptly repo add 2.0 incoming/* +$ aptly repo add stable incoming/* -See also reprepro configuration file: 'distributions' +Publish repositories: +$ aptly publish update -gpg-key="57144D2148DD706967DBFF7C548B17BF81F18E7A" 2.0 +$ aptly publish update -gpg-key="57144D2148DD706967DBFF7C548B17BF81F18E7A" stable + +Remove files from incoming directory: +$ rm incoming/* - Docker: Build a new image from https://github.com/LemonLDAPNG/lemonldap-ng-docker @@ -120,7 +123,7 @@ Spread the word - News on OW2 projects page: https://projects.ow2.org/bin/view/lemonldap-ng/ - Twitter account / Facebook page -- IRC channel subject and Mattermost +- IRC channel subject and OW2 Rocket.Chat channel - Mailing lists: lemonldap-ng-users / lemonldap-ng-announces - Optional: blogs and news sites (LinuxFR, etc.) diff --git a/_example/etc/nginx-lua-headers.conf b/_example/etc/nginx-lua-headers.conf index 1869dfdc4..bd378b3d5 100644 --- a/_example/etc/nginx-lua-headers.conf +++ b/_example/etc/nginx-lua-headers.conf @@ -28,6 +28,21 @@ auth_request_set $headervalue14 $upstream_http_headervalue14; auth_request_set $headername15 $upstream_http_headername15; auth_request_set $headervalue15 $upstream_http_headervalue15; + auth_request_set $deleteheader1 $upstream_http_deleteheader1; + auth_request_set $deleteheader2 $upstream_http_deleteheader2; + auth_request_set $deleteheader3 $upstream_http_deleteheader3; + auth_request_set $deleteheader4 $upstream_http_deleteheader4; + auth_request_set $deleteheader5 $upstream_http_deleteheader5; + auth_request_set $deleteheader6 $upstream_http_deleteheader6; + auth_request_set $deleteheader7 $upstream_http_deleteheader7; + auth_request_set $deleteheader8 $upstream_http_deleteheader8; + auth_request_set $deleteheader9 $upstream_http_deleteheader9; + auth_request_set $deleteheader10 $upstream_http_deleteheader10; + auth_request_set $deleteheader11 $upstream_http_deleteheader11; + auth_request_set $deleteheader12 $upstream_http_deleteheader12; + auth_request_set $deleteheader13 $upstream_http_deleteheader13; + auth_request_set $deleteheader14 $upstream_http_deleteheader14; + auth_request_set $deleteheader15 $upstream_http_deleteheader15; auth_request_set $lmcookie $upstream_http_cookie; access_by_lua ' local i = 1 @@ -38,7 +53,16 @@ else break end - i = i +1 + i = i + 1 + end + i = 1 + while true do + if ngx.var["deleteheader"..i] ~= nil then + ngx.req.clear_header(ngx.var["deleteheader"..i]) + else + break + end + i = i + 1 end '; diff --git a/changelog b/changelog index 7cf874f1c..389ae15aa 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,132 @@ +lemonldap-ng (2.0.11) focal; urgency=medium + + * Bugs: + * #2445: lmAuth param sent to protected application + * #2446: Incorrect MIME type on /psgi.js + * #2448: Adaptative Authentication rule triggered several times + * #2449: SAML SLO using Redirect/POST binding does not work with multiple SP + + * New features: + * #1987: add grant_type=client_credentials in OIDC + + * Improvements: + * #2397: OAuth2 handler should make client_id and scopes of the access token available to rules and headers + * #2436: CheckUser displays headers as they have been defined in conf intead of how they are sent + * #2444: set oidcServiceKeyIdSig by default + + -- Clément Sat, 30 Jan 2021 18:33:37 +0100 + +lemonldap-ng (2.0.10) stable; urgency=medium + + * Bugs: + * #1978: can't configure variables to post in virtual host's form replay with lemonldap-cli + * #2245: Manager API does not call reloadUrls + * #2262: SAML: SP-initiated logout does not propagate to external authentication modules + * #2267: LDAP timeout does not apply to search/bind/etc + * #2293: LL:NG 2.0.8 Manager test for external/working SMTP fails @ SSL handshake, terminates connections + * #2304: Error when using SMTP over SSL in CentOS 7 + * #2310: Misspelled parameter in call to ldap->search() + * #2315: CheckUser plugin: option rules rely on checked user rather than connected user + * #2318: Manager API: translate JSON booleans to int + * #2332: [security:low] removal of registrable 2F does not test the current authn level + * #2340: lemonldap-ng-cli restore does not work if the config backend is empty + * #2342: Calling logout page for unauthenticated user forces login + * #2344: Enable keepalive on LDAP connections + * #2347: [Manager API] postLogoutRedirectUris should be an array + * #2348: [Manager API] Bad URL in documentation + * #2352: skipRenewConfirmation and skipUpgradeConfirmation options do not work + * #2354: Lemonldap::NG::Common::Conf::msg is never reset and grows indefinitely + * #2355: Password policy checker broken in password reset by mail template + * #2357: CDA query parameter not parsed when query params are reordered + * #2361: Cannot remove OIDC consent from session explorer + * #2364: llngconnexion cookie in the StayConnected-Plugin rejected + * #2365: Check my last logins option does not work with StayConnected plugin + * #2366: StayConnected plugin does not work with 2FA + * #2367: skip rule doesn't work with DevOps handler + * #2369: Memory leak in Issuer::_redirect + * #2373: Remove spaces from generated login when user register account + * #2374: Missing form-check-input class in form groups + * #2375: Refresh session plugin: refresh result is not checked before returning JSON answer + * #2377: Reset expired password process does not work without _whatToTrace macro or if old password is not required + * #2378: Error in inGroup expansion + * #2383: Vhost with wildcard with % sign, configuration not loaded in manager + * #2387: logout does not clear handler cache + * #2399: Local password policy check should be disabled when clicking on "generate password" checkbox + * #2401: Selinux policy blocks cache after restorecon + * #2403: Missing Ldap attribute in CAS ticket if equals 0 + * #2410: LDAP connectivity issues on startup cause fatal initialization error when passwordDB=LDAP + * #2411: Javascript error when local password policy configured and password tab disabled in menu + * #2413: checkstate returns error 500 with user parameter + * #2417: Error in cookie name used by lemonldap regexp + * #2420: Auth::SAML should handle missing NameID + * #2425: "Configuration error: xxx SAML metadata has no EntityID" when updating SAML sp in manager API + * #2426: twitter auth fails when coming from oidc/saml/cas service + * #2429: SAML sessions fill up with logout sessions that do not expire + * #2430: Password not updated in session after password change + * #2440: OIDC api: redirect URI not handled at top level during get/update operations + + * New features: + * #2336: Adaptative Authentication Plugin + * #2391: Add extended function to test for registered second factor + * #2408: Add Chinese (Taiwan) translation + + * Improvements: + * #714: Make password change compatible with Combination + * #716: Make password reset work with Combination + * #2232: lmAttrOrMacro test in Manager is too restrictive + * #2266: local password policy conflicts with LDAP password policy + * #2301: password reset page(s) CSS issues + * #2309: Unintialized $app in CAS Issuer during test + * #2314: CheckUser plugin: Append an option to display computed sessions data + * #2316: "New keys" in saml security configuration should generate a certificate + * #2317: Combination and fail2ban logs + * #2319: Allow the SAML signature alg to be set per-provider + * #2321: Can't save configuration with 2 CAS applications sharing the same hostname + * #2322: Support for SHA384 and SHA512 saml signatures + * #2329: Display a warning if password module is enabled without password backend + * #2330: Allow to configure OIDC claims type + * #2331: Warning in default Nginx configuration + * #2334: GlobalLogout plugin can sometimes found some non-SSO or corrupted sessions + * #2335: apache handler: allow users to override the port/scheme for redirections + * #2339: Plugins refactoring + * #2341: Make SHA256 the default signature method for SAML + * #2345: RGAA recommand alt tags to be empty for decoration images + * #2350: [security:low] Hiding session ids from the manager + * #2356: RGAA 5.4 requires arrays to have defined captions + * #2359: plugin engine for issuers + * #2360: Avoid assignment in expressions + * #2368: StayConnected-Plugin: when user-agent changes login is only possible after deleting cookies + * #2372: Add a domain whitelist to Auth::Kerberos + * #2380: CORS headers not sent by sendError + * #2381: Append a hook to be able to overwrite access log + * #2386: CheckUser does not resolve vhost aliases + * #2388: Allow custom SSL logos when using choice + * #2393: All messages printed in userLogger should use whatToTrace value to log user name + * #2398: CheckUser: Append an option to hide specific headers value depending on tested VHost + * #2404: Force deletion of corrupted sessions in DBI and LDAP backends + * #2406: Possibility to use a different mail for 2FA and password reset + * #2409: Update Spanish translation + * #2414: Manager evaluates macros with Safe Jail whereas useSafeJail has been disabled + * #2422: Missing alt attributes in mail HTML templates + * #2427: Make AssertionConsumerServiceURL available to SAML rules + * #2438: Add a confirmation when deleting second factor + + * Templates: + * #2301: password reset page(s) CSS issues + * #2355: Password policy checker broken in password reset by mail template + * #2356: RGAA 5.4 requires arrays to have defined captions + * #2365: Check my last logins option does not work with StayConnected plugin + * #2366: StayConnected plugin does not work with 2FA + * #2374: Missing form-check-input class in form groups + * #2422: Missing alt attributes in mail HTML templates + * #2438: Add a confirmation when deleting second factor + + * WebServer Confs: + * #2331: Warning in default Nginx configuration + * #2434: [security:medium] Headers are not deleted for unprotected or skip locations with nginx handler + + -- Clément Sun, 17 Jan 2021 16:52:38 +0100 + lemonldap-ng (2.0.9) stable; urgency=medium * Bugs: diff --git a/debian/changelog b/debian/changelog index f97ecb552..32a241ab7 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,17 @@ +lemonldap-ng (2.0.11-1) unstable; urgency=medium + + * New release. See changes on our website: + https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng + + -- Clement OUDOT Sat, 30 Jan 2021 22:00:00 +0100 + +lemonldap-ng (2.0.10-1) unstable; urgency=medium + + * New release. See changes on our website: + https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng + + -- Clement OUDOT Sun, 17 Jan 2021 22:00:00 +0100 + lemonldap-ng (2.0.9-1) unstable; urgency=medium * New release. See changes on our website: diff --git a/debian/control b/debian/control index a1535966e..7c2130415 100644 --- a/debian/control +++ b/debian/control @@ -5,7 +5,8 @@ Section: perl Priority: optional Build-Depends: debhelper (>= 10), po-debconf -Build-Depends-Indep: libapache-session-perl , +Build-Depends-Indep: gsfonts , + libapache-session-perl , libauth-yubikey-webclient-perl , libauthen-oath-perl , libcache-cache-perl , @@ -30,6 +31,7 @@ Build-Depends-Indep: libapache-session-perl , libio-string-perl , libipc-run-perl , libjson-perl , + libjson-xs-perl , liblasso-perl , libmime-tools-perl , libmouse-perl , @@ -52,10 +54,12 @@ Build-Depends-Indep: libapache-session-perl , libwww-perl , libxml-libxml-perl , libxml-libxslt-perl , + libxml-simple-perl , + libtest-leaktrace-perl , python3-sphinx, python3-sphinx-bootstrap-theme, perl -Standards-Version: 4.5.0 +Standards-Version: 4.5.1 Vcs-Browser: https://salsa.debian.org/perl-team/modules/packages/lemonldap-ng Vcs-Git: https://salsa.debian.org/perl-team/modules/packages/lemonldap-ng.git Homepage: https://lemonldap-ng.org/ @@ -134,10 +138,10 @@ Architecture: all Section: web Depends: ${misc:Depends}, ${perl:Depends}, - liblemonldap-ng-handler-perl (= ${binary:Version}), - uwsgi-plugin-psgi + liblemonldap-ng-handler-perl (= ${binary:Version}) Recommends: libhttp-parser-xs-perl, - nginx-extras | nginx + uwsgi-plugin-psgi +Suggests: nginx-extras | nginx Description: Lemonldap::NG uWSGI server Lemonldap::NG is a complete Web-SSO system that can run with reverse-proxies or directly on application webservers. It can be used in conjunction with @@ -160,8 +164,8 @@ Recommends: lemonldap-ng-fastcgi-server (= ${binary:Version}) | lemonldap-ng-uws Suggests: libcache-memcached-perl, libdigest-hmac-perl, libsoap-lite-perl -Breaks: liblemonldap-ng-handler-perl (<< 1.9.1-2~) -Replaces: liblemonldap-ng-handler-perl (<< 1.9.1-2~) +Breaks: liblemonldap-ng-handler-perl (<< 1.9.1-2~), lemonldap-ng-fastcgi-server (<< 2.0.5~) +Replaces: liblemonldap-ng-handler-perl (<< 1.9.1-2~), lemonldap-ng-fastcgi-server (<< 2.0.5~) Description: Lemonldap::NG handler part Lemonldap::NG is a complete Web-SSO system that can run with reverse-proxies or directly on application webservers. It can be used in conjunction with @@ -208,6 +212,7 @@ Depends: ${misc:Depends}, libcrypt-rijndael-perl, libhtml-template-perl, libjson-perl, + libjson-xs-perl, libmouse-perl, libplack-perl, liburi-perl, @@ -217,7 +222,6 @@ Recommends: libapache-session-browseable-perl, libcookie-baker-xs-perl, libdbi-perl, libhttp-parser-xs-perl, - libjson-xs-perl, liblwp-protocol-https-perl, libstring-random-perl Suggests: libconvert-base32-perl, @@ -278,7 +282,8 @@ Depends: ${misc:Depends}, libtext-unidecode-perl, libregexp-assemble-perl, libemail-date-format-perl -Recommends: libcrypt-openssl-bignum-perl, +Recommends: gsfonts, + libcrypt-openssl-bignum-perl, libconvert-base32-perl, libio-string-perl, libipc-run-perl, @@ -315,3 +320,7 @@ Description: Lemonldap::NG authentication portal part the requested URL and the rule calculates if the user is authorized. . Lemonldap::NG::Portal provides the authentication portal. + . + You may have to install some suggested packages depending on plugins you + enabled. For example, libgd-securityimage-perl and gsfonts are needed if you + want to use Captcha, libcrypt-u2f-server-perl for U2F features,... diff --git a/debian/tests/control b/debian/tests/control index 8b2e272ff..e28f21686 100644 --- a/debian/tests/control +++ b/debian/tests/control @@ -1,32 +1,40 @@ # debian/tests/runner launch pkg-perl-autopkgtest tests for each library Test-Command: ./debian/tests/runner build-deps lemonldap-ng-common Depends: liblemonldap-ng-common-perl, @builddeps@, pkg-perl-autopkgtest +Features: test-name=common-test Test-Command: ./debian/tests/runner build-deps lemonldap-ng-handler Depends: liblemonldap-ng-handler-perl, @builddeps@, pkg-perl-autopkgtest +Features: test-name=handler-test Test-Command: ./debian/tests/runner build-deps lemonldap-ng-portal Depends: liblemonldap-ng-portal-perl, @builddeps@, pkg-perl-autopkgtest +Features: test-name=portal-test Test-Command: ./debian/tests/runner build-deps lemonldap-ng-manager Depends: liblemonldap-ng-manager-perl, @builddeps@, pkg-perl-autopkgtest +Features: test-name=manager-test Test-Command: ./debian/tests/runner runtime-deps lemonldap-ng-common Depends: liblemonldap-ng-common-perl, pkg-perl-autopkgtest, libmouse-perl Restrictions: superficial, skippable +Features: test-name=runtime-deps-common # Disable this one: skipped #Test-Command: ./debian/tests/runner runtime-deps lemonldap-ng-handler #Depends: liblemonldap-ng-handler-perl, pkg-perl-autopkgtest, libmouse-perl #Restrictions: superficial, skippable +#Features: test-name=runtime-deps-handler Test-Command: ./debian/tests/runner runtime-deps lemonldap-ng-portal Depends: liblemonldap-ng-portal-perl, pkg-perl-autopkgtest, libmouse-perl Restrictions: superficial, skippable +Features: test-name=runtime-deps-portal Test-Command: ./debian/tests/runner runtime-deps lemonldap-ng-manager Depends: liblemonldap-ng-manager-perl, pkg-perl-autopkgtest, libmouse-perl Restrictions: superficial, skippable +Features: test-name=runtime-deps-manager # Use pkg-perl-autopkgtest test for runtime-deps-and-recommends # Some portal suggested dependencies are added here @@ -35,7 +43,10 @@ Depends: @, @builddeps@, pkg-perl-autopkgtest , libyaml-perl, liblog-log4perl-perl , libauthen-pam-perl, libauthen-radius-perl , libweb-id-perl, libio-socket-timeout-perl + , libdatetime-format-rfc3339-perl Restrictions: superficial +Features: test-name=runtime-deps-and-recommends #Test-Command: ./debian/tests/runner heavy-deps #Depends: @, pkg-perl-autopkgtest, pkg-perl-autopkgtest-heavy, libmouse-perl +#Features: test-name=heavy-deps diff --git a/doc/pages/manager-api/index.html b/doc/pages/manager-api/index.html index ea1a30646..81e7823c5 100644 --- a/doc/pages/manager-api/index.html +++ b/doc/pages/manager-api/index.html @@ -890,6 +890,17 @@ "allowOffline" : { "type" : "boolean" }, + "accessTokenSignAlg" : { + "type" : "string", + "default" : "HS512", + "enum" : [ "none", "RS256", "RS384", "RS512" ] + }, + "accessTokenJWT" : { + "type" : "bool" + }, + "accessTokenClaims" : { + "type" : "bool" + }, "authnLevel" : { "type" : "integer" }, @@ -908,7 +919,10 @@ "type" : "boolean" }, "postLogoutRedirectUris" : { - "type" : "string" + "type" : "array", + "items" : { + "type" : "string" + } }, "logoutType" : { "type" : "string", @@ -921,6 +935,12 @@ "IDTokenForceClaims" : { "type" : "boolean" }, + "additionalAudiences" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, "requirePKCE" : { "type" : "boolean" }, @@ -996,6 +1016,12 @@ }, "options" : { "$ref" : "#/components/schemas/OidcOptions" + }, + "scopeRules" : { + "type" : "object", + "example" : { + "write" : "requested and inGroup('writers')" + } } } }; @@ -1028,6 +1054,12 @@ }, "options" : { "$ref" : "#/components/schemas/OidcOptions" + }, + "scopeRules" : { + "type" : "object", + "example" : { + "write" : "requested and inGroup('writers')" + } } } }; @@ -1059,6 +1091,12 @@ }, "options" : { "$ref" : "#/components/schemas/OidcOptions" + }, + "scopeRules" : { + "type" : "object", + "example" : { + "write" : "requested and inGroup('writers')" + } } } }; @@ -1472,7 +1510,7 @@
-
curl -X POST "/api/v1/api/v1/providers/cas/app"
+
curl -X POST "https://manager-api.example.com/api/v1/providers/cas/app"
import io.swagger.client.*;
@@ -1828,7 +1866,7 @@ except ApiException as e:
 
                         
-
curl -X DELETE "/api/v1/api/v1/providers/cas/app/{confKey}"
+
curl -X DELETE "https://manager-api.example.com/api/v1/providers/cas/app/{confKey}"
import io.swagger.client.*;
@@ -2169,7 +2207,7 @@ except ApiException as e:
 
                         
-
curl -X GET "/api/v1/api/v1/providers/cas/app/findByConfKey?pattern="
+
curl -X GET "https://manager-api.example.com/api/v1/providers/cas/app/findByConfKey?pattern="
import io.swagger.client.*;
@@ -2515,7 +2553,7 @@ except ApiException as e:
 
                         
-
curl -X GET "/api/v1/api/v1/providers/cas/app/findByServiceUrl?serviceUrl="
+
curl -X GET "https://manager-api.example.com/api/v1/providers/cas/app/findByServiceUrl?serviceUrl="
import io.swagger.client.*;
@@ -2905,7 +2943,7 @@ except ApiException as e:
 
                         
-
curl -X GET "/api/v1/api/v1/providers/cas/app/{confKey}"
+
curl -X GET "https://manager-api.example.com/api/v1/providers/cas/app/{confKey}"
import io.swagger.client.*;
@@ -3295,7 +3333,7 @@ except ApiException as e:
 
                         
-
curl -X PUT "/api/v1/api/v1/providers/cas/app/{confKey}"
+
curl -X PUT "https://manager-api.example.com/api/v1/providers/cas/app/{confKey}"
import io.swagger.client.*;
@@ -3736,7 +3774,7 @@ except ApiException as e:
 
                         
-
curl -X PATCH "/api/v1/api/v1/providers/cas/app/{confKey}"
+
curl -X PATCH "https://manager-api.example.com/api/v1/providers/cas/app/{confKey}"
import io.swagger.client.*;
@@ -4180,7 +4218,7 @@ except ApiException as e:
 
                         
-
curl -X DELETE "/api/v1/api/v1/secondFactor/{uid}"
+
curl -X DELETE "https://manager-api.example.com/api/v1/secondFactor/{uid}"
import io.swagger.client.*;
@@ -4470,7 +4508,7 @@ except ApiException as e:
 
                         
-
curl -X DELETE "/api/v1/api/v1/secondFactor/{uid}/id/{id}"
+
curl -X DELETE "https://manager-api.example.com/api/v1/secondFactor/{uid}/id/{id}"
import io.swagger.client.*;
@@ -4788,7 +4826,7 @@ except ApiException as e:
 
                         
-
curl -X DELETE "/api/v1/api/v1/secondFactor/{uid}/type/{type}"
+
curl -X DELETE "https://manager-api.example.com/api/v1/secondFactor/{uid}/type/{type}"
import io.swagger.client.*;
@@ -5106,7 +5144,7 @@ except ApiException as e:
 
                         
-
curl -X GET "/api/v1/api/v1/secondFactor/{uid}"
+
curl -X GET "https://manager-api.example.com/api/v1/secondFactor/{uid}"
import io.swagger.client.*;
@@ -5445,7 +5483,7 @@ except ApiException as e:
 
                         
-
curl -X GET "/api/v1/api/v1/secondFactor/{uid}/id/{id}"
+
curl -X GET "https://manager-api.example.com/api/v1/secondFactor/{uid}/id/{id}"
import io.swagger.client.*;
@@ -5812,7 +5850,7 @@ except ApiException as e:
 
                         
-
curl -X GET "/api/v1/api/v1/secondFactor/{uid}/type/{type}"
+
curl -X GET "https://manager-api.example.com/api/v1/secondFactor/{uid}/type/{type}"
import io.swagger.client.*;
@@ -6182,7 +6220,7 @@ except ApiException as e:
 
                         
-
curl -X GET "/api/v1/api/v1/status"
+
curl -X GET "https://manager-api.example.com/api/v1/status"
import io.swagger.client.*;
@@ -6489,7 +6527,7 @@ except ApiException as e:
 
                         
-
curl -X POST "/api/v1/api/v1/menu/app/{cat}"
+
curl -X POST "https://manager-api.example.com/api/v1/menu/app/{cat}"
import io.swagger.client.*;
@@ -6931,7 +6969,7 @@ except ApiException as e:
 
                         
-
curl -X DELETE "/api/v1/api/v1/menu/app/{cat}/{confKey}"
+
curl -X DELETE "https://manager-api.example.com/api/v1/menu/app/{cat}/{confKey}"
import io.swagger.client.*;
@@ -7303,7 +7341,7 @@ except ApiException as e:
 
                         
-
curl -X GET "/api/v1/api/v1/menu/app/{cat}/findByConfKey?pattern="
+
curl -X GET "https://manager-api.example.com/api/v1/menu/app/{cat}/findByConfKey?pattern="
import io.swagger.client.*;
@@ -7687,7 +7725,7 @@ except ApiException as e:
 
                         
-
curl -X GET "/api/v1/api/v1/menu/app/{cat}/{confKey}"
+
curl -X GET "https://manager-api.example.com/api/v1/menu/app/{cat}/{confKey}"
import io.swagger.client.*;
@@ -8108,7 +8146,7 @@ except ApiException as e:
 
                         
-
curl -X GET "/api/v1/api/v1/menu/app/{cat}"
+
curl -X GET "https://manager-api.example.com/api/v1/menu/app/{cat}"
import io.swagger.client.*;
@@ -8502,7 +8540,7 @@ except ApiException as e:
 
                         
-
curl -X PUT "/api/v1/api/v1/menu/app/{cat}/{confKey}"
+
curl -X PUT "https://manager-api.example.com/api/v1/menu/app/{cat}/{confKey}"
import io.swagger.client.*;
@@ -8974,7 +9012,7 @@ except ApiException as e:
 
                         
-
curl -X PATCH "/api/v1/api/v1/menu/app/{cat}/{confKey}"
+
curl -X PATCH "https://manager-api.example.com/api/v1/menu/app/{cat}/{confKey}"
import io.swagger.client.*;
@@ -9449,7 +9487,7 @@ except ApiException as e:
 
                         
-
curl -X POST "/api/v1/api/v1/menu/cat"
+
curl -X POST "https://manager-api.example.com/api/v1/menu/cat"
import io.swagger.client.*;
@@ -9805,7 +9843,7 @@ except ApiException as e:
 
                         
-
curl -X DELETE "/api/v1/api/v1/menu/cat/{confKey}"
+
curl -X DELETE "https://manager-api.example.com/api/v1/menu/cat/{confKey}"
import io.swagger.client.*;
@@ -10146,7 +10184,7 @@ except ApiException as e:
 
                         
-
curl -X GET "/api/v1/api/v1/menu/cat/findByConfKey?pattern="
+
curl -X GET "https://manager-api.example.com/api/v1/menu/cat/findByConfKey?pattern="
import io.swagger.client.*;
@@ -10492,7 +10530,7 @@ except ApiException as e:
 
                         
-
curl -X GET "/api/v1/api/v1/menu/cat/{confKey}"
+
curl -X GET "https://manager-api.example.com/api/v1/menu/cat/{confKey}"
import io.swagger.client.*;
@@ -10882,7 +10920,7 @@ except ApiException as e:
 
                         
-
curl -X PUT "/api/v1/api/v1/menu/cat/{confKey}"
+
curl -X PUT "https://manager-api.example.com/api/v1/menu/cat/{confKey}"
import io.swagger.client.*;
@@ -11323,7 +11361,7 @@ except ApiException as e:
 
                         
-
curl -X PATCH "/api/v1/api/v1/menu/cat/{confKey}"
+
curl -X PATCH "https://manager-api.example.com/api/v1/menu/cat/{confKey}"
import io.swagger.client.*;
@@ -11767,7 +11805,7 @@ except ApiException as e:
 
                         
-
curl -X POST "/api/v1/api/v1/providers/oidc/rp"
+
curl -X POST "https://manager-api.example.com/api/v1/providers/oidc/rp"
import io.swagger.client.*;
@@ -12123,7 +12161,7 @@ except ApiException as e:
 
                         
-
curl -X DELETE "/api/v1/api/v1/providers/oidc/rp/{confKey}"
+
curl -X DELETE "https://manager-api.example.com/api/v1/providers/oidc/rp/{confKey}"
import io.swagger.client.*;
@@ -12464,7 +12502,7 @@ except ApiException as e:
 
                         
-
curl -X GET "/api/v1/api/v1/providers/oidc/rp/findByClientId?clientId="
+
curl -X GET "https://manager-api.example.com/api/v1/providers/oidc/rp/findByClientId?clientId="
import io.swagger.client.*;
@@ -12854,7 +12892,7 @@ except ApiException as e:
 
                         
-
curl -X GET "/api/v1/api/v1/providers/oidc/rp/findByConfKey?pattern="
+
curl -X GET "https://manager-api.example.com/api/v1/providers/oidc/rp/findByConfKey?pattern="
import io.swagger.client.*;
@@ -13200,7 +13238,7 @@ except ApiException as e:
 
                         
-
curl -X GET "/api/v1/api/v1/providers/oidc/rp/{confKey}"
+
curl -X GET "https://manager-api.example.com/api/v1/providers/oidc/rp/{confKey}"
import io.swagger.client.*;
@@ -13590,7 +13628,7 @@ except ApiException as e:
 
                         
-
curl -X PUT "/api/v1/api/v1/providers/oidc/rp/{confKey}"
+
curl -X PUT "https://manager-api.example.com/api/v1/providers/oidc/rp/{confKey}"
import io.swagger.client.*;
@@ -14031,7 +14069,7 @@ except ApiException as e:
 
                         
-
curl -X PATCH "/api/v1/api/v1/providers/oidc/rp/{confKey}"
+
curl -X PATCH "https://manager-api.example.com/api/v1/providers/oidc/rp/{confKey}"
import io.swagger.client.*;
@@ -14475,7 +14513,7 @@ except ApiException as e:
 
                         
-
curl -X POST "/api/v1/api/v1/providers/saml/sp"
+
curl -X POST "https://manager-api.example.com/api/v1/providers/saml/sp"
import io.swagger.client.*;
@@ -14831,7 +14869,7 @@ except ApiException as e:
 
                         
-
curl -X DELETE "/api/v1/api/v1/providers/saml/sp/{confKey}"
+
curl -X DELETE "https://manager-api.example.com/api/v1/providers/saml/sp/{confKey}"
import io.swagger.client.*;
@@ -15172,7 +15210,7 @@ except ApiException as e:
 
                         
-
curl -X GET "/api/v1/api/v1/providers/saml/sp/findByConfKey?pattern="
+
curl -X GET "https://manager-api.example.com/api/v1/providers/saml/sp/findByConfKey?pattern="
import io.swagger.client.*;
@@ -15518,7 +15556,7 @@ except ApiException as e:
 
                         
-
curl -X GET "/api/v1/api/v1/providers/saml/sp/findByEntityId?entityId="
+
curl -X GET "https://manager-api.example.com/api/v1/providers/saml/sp/findByEntityId?entityId="
import io.swagger.client.*;
@@ -15908,7 +15946,7 @@ except ApiException as e:
 
                         
-
curl -X GET "/api/v1/api/v1/providers/saml/sp/{confKey}"
+
curl -X GET "https://manager-api.example.com/api/v1/providers/saml/sp/{confKey}"
import io.swagger.client.*;
@@ -16298,7 +16336,7 @@ except ApiException as e:
 
                         
-
curl -X PUT "/api/v1/api/v1/providers/saml/sp/{confKey}"
+
curl -X PUT "https://manager-api.example.com/api/v1/providers/saml/sp/{confKey}"
import io.swagger.client.*;
@@ -16739,7 +16777,7 @@ except ApiException as e:
 
                         
-
curl -X PATCH "/api/v1/api/v1/providers/saml/sp/{confKey}"
+
curl -X PATCH "https://manager-api.example.com/api/v1/providers/saml/sp/{confKey}"
import io.swagger.client.*;
diff --git a/doc/sources/admin/applications.rst b/doc/sources/admin/applications.rst
index ac8384031..26299a3ae 100644
--- a/doc/sources/admin/applications.rst
+++ b/doc/sources/admin/applications.rst
@@ -9,6 +9,7 @@ Applications
    applications/aws
    applications/awx
    applications/bugzilla
+   applications/bigbluebutton
    applications/cornerstone
    applications/discourse
    applications/django
@@ -83,6 +84,7 @@ Application                                                       Configuration
 .. image:: applications/alfresco_logo.png                         :doc:`Alfresco`               ✔                                 ✔
 .. image:: applications/logo_amazon_web_services.jpg              :doc:`Amazon Web Services`                                           ✔
 .. image:: applications/logo-awx.png                              :doc:`AWX (Ansible Tower)`                                           ✔
+.. image:: applications/bigbluebutton-logo.png                    :doc:`BigBlueButton`                                            ✔
 .. image:: applications/bugzilla_logo.png                         :doc:`Bugzilla`               ✔
 .. image:: applications/csod_logo.png                             :doc:`Cornerstone`                                           ✔
 .. image:: applications/discourse.jpg                             :doc:`Discourse`                                               ✔    ✔
diff --git a/doc/sources/admin/applications/awx.rst b/doc/sources/admin/applications/awx.rst
index e64b1ea78..806a872d6 100644
--- a/doc/sources/admin/applications/awx.rst
+++ b/doc/sources/admin/applications/awx.rst
@@ -163,6 +163,16 @@ This is the configuration of the IdP :
    inside AWX
 -  "url": "https://auth.example.com/saml/singleSignOn" SAML SSO Url
 
+SAML Security Config
+^^^^^^^^^^^^^^^^^^^^
+
+::
+
+   {
+      "requestedAuthnContext": false,
+      "authnRequestsSigned": true
+   }
+
 Save your configuration.
 
 LemonLDAP:NG
diff --git a/doc/sources/admin/applications/bigbluebutton-logo.png b/doc/sources/admin/applications/bigbluebutton-logo.png
new file mode 100644
index 000000000..7cc26c8a7
Binary files /dev/null and b/doc/sources/admin/applications/bigbluebutton-logo.png differ
diff --git a/doc/sources/admin/applications/bigbluebutton.rst b/doc/sources/admin/applications/bigbluebutton.rst
new file mode 100644
index 000000000..9d5ce2a52
--- /dev/null
+++ b/doc/sources/admin/applications/bigbluebutton.rst
@@ -0,0 +1,64 @@
+BigBlueButton
+=============
+
+|logo|
+
+Presentation
+------------
+
+`BigBlueButton `__ is a web conferencing system
+designed for online learning. It offers audio/video sharing, presentations with
+extended whiteboard capabilities - such as a pointer, zooming and drawing -
+public and private chat, breakout rooms, screen sharing, integrated VoIP using
+FreeSWITCH, and support for presentation of PDF documents and Microsoft Office
+documents.
+
+Its user-facing interface, *Greenlight*, can be configured to authenticate users with :doc:`OpenID Connect <../idpopenidconnect>` since version 2.7.17.
+
+Configuration
+--------------
+
+LL:NG
+~~~~~
+
+Make sure you have already
+:doc:`enabled OpenID Connect<../idpopenidconnect>` on your LemonLDAP::NG
+server
+
+Make sure you have generated a set of signing keys in
+``OpenID Connect Service`` » ``Security`` » ``Keys``
+
+You also need to set a Signing key ID to a non-empty value of your choice.
+
+Then, add a Relaying Party with the following configuration
+
+- Options » Authentification » Client ID : choose a client ID, such as ``my_client_id``
+- Options » Authentification » Client Secret : choose a client secret, such as ``my_client_secret``
+- Options » Allowed redirection address : ``https://my_greenlight_server/b/auth/openid_connect/callback``
+- Options » ID Token Signature Algorithm : ``RS256``
+- Adjust your Exported Attributes to send the correct session variables in the ``email`` and ``name`` claims.
+
+Greenlight
+~~~~~~~~~~
+
+Configure the following environment variables in your greenlight `.env` file ::
+
+   OPENID_CONNECT_CLIENT_ID=my_client_id
+   OPENID_CONNECT_CLIENT_SECRET=my_client_secret
+   OPENID_CONNECT_ISSUER=https://auth.example.com
+   OPENID_CONNECT_UID_FIELD=sub
+   OAUTH2_REDIRECT=https://my_greenlight_server/b/
+
+
+Notes
+~~~~~
+
+* Your ID Token Signature Algorithm has to be RSxxx, symmetric algorithms seem broken as of Greenlight 2.7.17
+* ``OAUTH2_REDIRECT`` must match the URL you use to access Greenlight. the
+  ``auth/openid_connect/callback`` suffix must be omitted
+* Greenlight will not work if your LemonLDAP::NG server is not served over HTTPS using a publically recognized certificate authority (such as Let's Encrypt)
+
+.. |logo| image:: /applications/bigbluebutton-logo.png
+   :class: align-center
+
+
diff --git a/doc/sources/admin/applications/gerrit.rst b/doc/sources/admin/applications/gerrit.rst
index 0a9816221..f95cf624b 100644
--- a/doc/sources/admin/applications/gerrit.rst
+++ b/doc/sources/admin/applications/gerrit.rst
@@ -16,12 +16,7 @@ Configuration
 Gerrit
 ------
 
-`Install `__ the OAuth Provider plugin.
-
-.. tip::
-
-   The LemonLDAP::NG support was added on February 23, 2020.
-   If you can't find a prebuilt package, you can use this `dockerfile `__ to build your own.
+`Install `__ the OAuth Provider plugin. A prebuilt package of the plugin can be found on the `Gerrit CI `__.
 
 Then, configure Gerrit:
 
diff --git a/doc/sources/admin/applications/mattermost.rst b/doc/sources/admin/applications/mattermost.rst
index c2c48875c..a90b4277b 100644
--- a/doc/sources/admin/applications/mattermost.rst
+++ b/doc/sources/admin/applications/mattermost.rst
@@ -101,15 +101,15 @@ with the following parameters:
    * ``username``: set it to the session attribute containing the user login
    * ``name``: session attribute containing the user's full name
    * ``email``: session attribute containing the user's email
-   * ``id``: session attribute containing the user's numeric ID
-
+   * ``id``: session attribute containing the user's numeric ID. You must set
+     this claim type to *Integer*
 
 .. danger::
 
-    Mattermost absolutely needs to receive a numerical value
-    in the ``id`` claim. If you are using a LDAP server, you could use the
-    ``uidNumber`` LDAP attribute. If you use something else, you will have
-    to find a trick to assign a unique numeric ID to each Mattermost user.
+    Mattermost absolutely needs to receive a numerical value in the ``id``
+    claim. If you are using a LDAP server, you could use the ``uidNumber`` LDAP
+    attribute. If you use something else, you will have to find a way to
+    assign a unique numeric ID to each Mattermost user.
 
     The ``id`` attribute has to be different for each user, since this is
     the field Mattermost will use internally to map Gitlab identities to
@@ -123,10 +123,10 @@ in ``(*GitLabUser).IsValid(...)`` , it probably means that you are not
 exporting the correct attributes, but it can also mean that ``id`` is
 exported as a JSON string.
 
-If this case, it can help to create a macro, for example
-``uidNumber_n``, with a value of ``$uidNumber + 0`` to force conversion
-to a numeric value. You must then export it as the ``id`` field in the
-Relaying Party configuration.
+.. note::
+   An issue in version 2.0.9 prevented the ``id`` field from being sent correctly.
+   Upgrade your LemonLDAP-NG installation to at least 2.0.10 and :ref:`set the claim
+   type ` to *Integer*
 
 .. |image0| image:: /applications/mattermost_logo.png
    :class: align-center
diff --git a/doc/sources/admin/applications/nextcloud.rst b/doc/sources/admin/applications/nextcloud.rst
index 4603f9ae5..e0b7cfe39 100644
--- a/doc/sources/admin/applications/nextcloud.rst
+++ b/doc/sources/admin/applications/nextcloud.rst
@@ -30,14 +30,13 @@ software  'nextcloud.example.com',
-
-
+       'overwriteprotocol' => 'https',
 
 You also need to enable the "SAML authentication" plugin in your
 NextCloud.  + Apps -> Not enabled -> SAML authentication
diff --git a/doc/sources/admin/authchoice.rst b/doc/sources/admin/authchoice.rst
index 70e4a273f..608b9a0f3 100644
--- a/doc/sources/admin/authchoice.rst
+++ b/doc/sources/admin/authchoice.rst
@@ -49,9 +49,11 @@ Then, go in ``Choice Parameters``:
 
 -  **URL parameter**: parameter name used to set choice value (default:
    ``lmAuth``)
--  **AuthBasic handler parameter**: authentication module used by
-   AuthBasic handler
 -  **Allowed modules**: click on ``New chain`` to add a choice.
+-  **AuthBasic handler parameter**: authentication module called by
+   AuthBasic handler (:doc:`AuthBasic handler`)
+-  **FindUser plugin parameter**: authentication module called by
+   Find user plugin (:doc:`Find user plugin`)
 
 |image0|
 
@@ -75,6 +77,12 @@ Define here:
    $env->{urldc} =~ /test1\.example\.com/
 
 
+.. note::
+
+    Federated authentication need pdata cookie.
+    SameSite cookie value must be set to "Lax" or "None".
+    See :doc:`SSO cookie parameters`
+
 .. note::
 
     Authentication request to an another URL than Portal URL can lead
@@ -100,7 +108,7 @@ Define here:
 .. tip::
 
     You can also override some LLNG parameters for each chain. See
-    :doc:`Parameter list` to have the key names to use
+    :doc:`Parameters list` to have the key names to use
 
 .. |image0| image:: /documentation/manager-choice.png
    :class: align-center
diff --git a/doc/sources/admin/authcombination.rst b/doc/sources/admin/authcombination.rst
index 06b7c8553..addef86a7 100644
--- a/doc/sources/admin/authcombination.rst
+++ b/doc/sources/admin/authcombination.rst
@@ -1,11 +1,11 @@
 Combination of authentication schemes
 =====================================
 
-============== ===== ========
+============== ===== ==================
 Authentication Users Password
-============== ===== ========
-✔              ✔
-============== ===== ========
+============== ===== ==================
+✔              ✔     ✔ (since *2.0.10*)
+============== ===== ==================
 
 Presentation
 ------------
@@ -184,6 +184,47 @@ lemonldap-ng.ini. Example:
    [portal]
    combinationForms = standardform, openidform
 
+
+Password management
+-------------------
+
+.. versionadded:: 2.0.10
+
+Not all configurations of the Combination module allow password management. 
+
+If your combination looks like this ::
+
+   [Kerberos, LDAP] or [LDAP]
+
+Then you can simply set ``LDAP`` as the password module, and password changes
+and reset will work as expected.
+
+If your combination looks like this ::
+
+   [LDAP1] or [LDAP2]
+
+Then you can configure the ``Combination`` password module to automatically
+send password changes to the LDAP server which was used during authentication.
+This module also enables password reset.
+
+.. note::
+
+   You can set the ``_cmbPasswordDB`` session variable to manually select which
+   backend will be called when changing the password. This is useful when using
+   SASL delegation
+
+Limitations
+~~~~~~~~~~~
+
+* When using password reset with a combination of 2 or more LDAP servers, you
+  need to make sure that there is no duplication of email addresses between all
+  your servers. If an email exists in more than one server, the password will
+  be reset on the first LDAP server that contains this email address
+* Combinations using the ``and`` boolean expression will not cause passwords to
+  be changed in both backends for now
+* Forcing the user to reset their password on next login is not currently
+  supported by the combination module
+
 Known problems
 --------------
 
diff --git a/doc/sources/admin/authldap.rst b/doc/sources/admin/authldap.rst
index c7d7dc248..adaa96914 100644
--- a/doc/sources/admin/authldap.rst
+++ b/doc/sources/admin/authldap.rst
@@ -205,7 +205,11 @@ Password
    reset (default: TRUE).
 -  **Allow a user to reset his expired password**: if activated, the
    user will be prompted to change password if his password is expired
-   (default: 0)
+   (default: disabled)
+- **Search for user before password change**: this option forces the password
+   change module to search for the user again, refreshing its DN. This feature
+   is only useful in rare cases when you use LDAP as the password module, but
+   not as the UserDB module. (default: enabled)
 -  **IBM Tivoli DS support**: enable this option if you use ITDS. LL::NG
    will then scan error message to return a more precise error to the
    user.
diff --git a/doc/sources/admin/authopenidconnect.rst b/doc/sources/admin/authopenidconnect.rst
index 9e9f10a2f..ae8d03e82 100644
--- a/doc/sources/admin/authopenidconnect.rst
+++ b/doc/sources/admin/authopenidconnect.rst
@@ -56,7 +56,7 @@ Google          France Connect
 
 .. attention::
 
-    OpenID-Connect specification isn't finished for logout
+    OpenID-Connect specification is not finished for logout
     propagation. So logout initiated by relaying-party will be forward to
     OpenID-Connect provider but logout initiated by the provider (or another
     RP) will not be propagated. LLNG will implement this when spec will be
@@ -127,7 +127,9 @@ parameter, for example:
 .. attention::
 
     If you use the :doc:`choice backend`, you
-    need to add the choice parameter in redirect URL
+    need to add the choice parameter in redirect URL or
+    set SameSite cookie value to "Lax" or "None".
+    See :doc:`SSO cookie parameters`
 
 After registration, the OP must give you a client ID and a client
 secret, that will be used to configure the OP in LL::NG.
@@ -148,6 +150,8 @@ The OP should publish its metadata in a JSON file (see for example
 `Google
 metadata `__).
 Copy the content of this file in the textarea.
+Portal discovery document can be found here:
+https://#portal#/.well-known/openid-configuration
 
 If no metadata is available, you need to write them in the textarea.
 Mandatory fields are:
@@ -217,7 +221,7 @@ Options
    -  **Client ID**: Client ID given by OP
    -  **Client secret**: Client secret given by OP
    -  **Store ID token**: Allows one to store the ID token (JWT) inside
-      user session. Don't enable it unless you need to replay this token
+      user session. Do not enable it unless you need to replay this token
       on an application, or if you need the id_token_hint parameter when
       using logout.
 
diff --git a/doc/sources/admin/browseableldapsessionbackend.rst b/doc/sources/admin/browseableldapsessionbackend.rst
new file mode 100644
index 000000000..057bb2471
--- /dev/null
+++ b/doc/sources/admin/browseableldapsessionbackend.rst
@@ -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)
+======================== ================================= ===============================
diff --git a/doc/sources/admin/browseablemysqlsessionbackend.rst b/doc/sources/admin/browseablemysqlsessionbackend.rst
new file mode 100644
index 000000000..0fa732ad5
--- /dev/null
+++ b/doc/sources/admin/browseablemysqlsessionbackend.rst
@@ -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 `__ 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`
+=================== ================================================= =============================================================
diff --git a/doc/sources/admin/browseablesessionbackend.rst b/doc/sources/admin/browseablesessionbackend.rst
index 80c670478..f123de171 100644
--- a/doc/sources/admin/browseablesessionbackend.rst
+++ b/doc/sources/admin/browseablesessionbackend.rst
@@ -7,250 +7,53 @@ Presentation
 Browseable session backend
 (`Apache::Session::Browseable `__)
 works exactly like Apache::Session::\* corresponding module but add
-index that increase :ref:`session explorer` and
-:ref:`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`.
-
-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 `__.
-
-Prepare database
-~~~~~~~~~~~~~~~~
-
-Database must be prepared exactly like in
-:ref:`SQL session backend`
-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` 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 `__
-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 `__ 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`.
-
-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) 
+   browseablemysqlsessionbackend
+   browseableldapsessionbackend
+   nosqlsessionbackend
diff --git a/doc/sources/admin/changesessionbackend.rst b/doc/sources/admin/changesessionbackend.rst
index 8be1d7753..9c6501145 100644
--- a/doc/sources/admin/changesessionbackend.rst
+++ b/doc/sources/admin/changesessionbackend.rst
@@ -19,25 +19,25 @@ configuration file with the following content:
    # This example migrates psessions from the default File backend to a PostgreSQL database
    [sessions_from]
    storageModule = Apache::Session::File
-   storageModuleOptions = {    \\
-         'Directory' => '/var/lib/lemonldap-ng/psessions',     \\
-         'LockDirectory' => '/var/lib/lemonldap-ng/psessions/lock', \\
+   storageModuleOptions = {    \
+         'Directory' => '/var/lib/lemonldap-ng/psessions',     \
+         'LockDirectory' => '/var/lib/lemonldap-ng/psessions/lock', \
    }
    # Only convert some session types
    # sessionKind = Persistent, SSO
 
    [sessions_to]
    storageModule = Apache::Session::Browseable::Postgres
-   storageModuleOptions = {    \\
-       'DataSource' => 'DBI:Pg:database=lemonldapdb;host=pg.example.com', \\
-       'UserName' => 'lemonldaplogin', \\
-       'Password' => 'lemonldappw', \\
-       'Commit' => 1, \\
-       'Index' => 'ipAddr _whatToTrace user', \\
-       'TableName' => 'psessions', \\
+   storageModuleOptions = {    \
+       'DataSource' => 'DBI:Pg:database=lemonldapdb;host=pg.example.com', \
+       'UserName' => 'lemonldaplogin', \
+       'Password' => 'lemonldappw', \
+       'Commit' => 1, \
+       'Index' => 'ipAddr _whatToTrace user', \
+       'TableName' => 'psessions', \
    }
 
-Invokation
+Invocation
 ----------
 
 ``convertSessions -c job.ini``
diff --git a/doc/sources/admin/checkdevops.rst b/doc/sources/admin/checkdevops.rst
new file mode 100644
index 000000000..668bc8174
--- /dev/null
+++ b/doc/sources/admin/checkdevops.rst
@@ -0,0 +1,55 @@
+Check DevOps plugin
+===================
+
+This plugin can be used to check the :doc:`DevOps` file.
+
+Configuration
+-------------
+
+Just enable it in the manager (section “plugins”).
+
+-  **Parameters**:
+
+   -  **Activation**: Enable / Disable this plugin
+   -  **Download file**: Allow users to download DevOps file from a remote server by
+      providing an URL (By example: http://myapp.example.com:8080). Plugin will
+      try to retrieve remote file by sending a request (i.e.
+      http://myapp.example.com:8080/rules.json)
+
+Usage
+-----
+When enabled, ``/checkdevops`` URL path is handled by this plugin.
+Then, you can paste a file to test your rules and headers.
+
+Example
+~~~~~~~
+DevOps handler requires a rules.json file to define
+access rules and headers:
+
+.. code-block:: json
+
+   {
+     "rules": {
+       "^/admin": "$uid eq 'admin'",
+       "default": "accept"
+     },
+     "headers": {
+       "Auth-User": "$uid"
+     }
+   }
+
+.. note::
+
+    This plugin displays ALL user session attributes except
+    the hidden ones.
+
+    You have to restrict access to specific users like DevOps teams
+    by setting an access rule like other VirtualHosts.
+
+    By example: ``$groups =~ /\bdevops\b/``
+
+.. attention::
+
+    Be careful to not display secret attributes.
+
+    checkDevOps plugin uses hidden attributes option.
diff --git a/doc/sources/admin/checkuser.rst b/doc/sources/admin/checkuser.rst
index 980851bd6..65c10208b 100644
--- a/doc/sources/admin/checkuser.rst
+++ b/doc/sources/admin/checkuser.rst
@@ -22,17 +22,19 @@ Just enable it in the manager (section “plugins”).
    -  **Attributes used for searching sessions**: User's attributes used
       for searching sessions in backend if ``whatToTrace`` fails. Useful
       to look for sessions by mail or givenName. Let it blank to search
-      by ``whatToTrace`` only.
+      by ``whatToTrace`` only
    -  **Display computed sessions**: Rule to define which users can display a
       computed session if no SSO session is found
    -  **Display empty headers**: Rule to define which users can display ALL headers
       appended by LemonLDAP::NG including empty ones
+   -  **Display normalized headers**: Rule to define which users see headers name sent by
+      the web server (see RFC3875)
    -  **Display empty values**: Rule to define which users can display ALL attributes
       even empty ones
    -  **Display persistent session data**: Rule to define which users can display
       persistent session data
    -  **Hidden headers**: Sent headers whose value is masked except for unrestricted users.
-      Key is a Virtualhost name and value represents a headers list.
+      Key is a Virtualhost name and value represents a space-separated headers list.
       A blank value obfuscates ALL relative Virtualhost sent headers.
       Note that just valued hearders are masked.
 
diff --git a/doc/sources/admin/cli_examples.rst b/doc/sources/admin/cli_examples.rst
index 26c8fe98c..b00cc02cc 100644
--- a/doc/sources/admin/cli_examples.rst
+++ b/doc/sources/admin/cli_examples.rst
@@ -190,6 +190,33 @@ In this example we have:
            'exportedHeaders/test.example.com' 'Auth-User' '$uid' \
            'exportedHeaders/test.example.com' 'Auth-Mail' '$mail'
 
+Configure form replay
+---------------------
+
+To add form replay on a host, you need to set the catched URI and
+the variables to post.
+
+In this example we have:
+
+- Host: test.example.com
+- Catched URI: /login.php
+- jQuery URL: default
+
+- Variables:
+   - login: $uid
+   - password: $_password
+
+::
+
+   /usr/share/lemonldap-ng/bin/lemonldap-ng-cli -yes 1 -sep , \
+       addKey \
+           post,test.example.com,'/login.php' jqueryUrl default
+
+   /usr/share/lemonldap-ng/bin/lemonldap-ng-cli -yes 1 -sep , \
+       addPostVars \
+           post,test.example.com,'/login.php' login '$uid' \
+           post,test.example.com,'/login.php' password '$_password'
+
 Configure LDAP authentication backend
 -------------------------------------
 
@@ -331,8 +358,7 @@ URL):
 
    /usr/share/lemonldap-ng/bin/lemonldap-ng-cli -yes 1 \
        set \
-           issuerDBOpenIDConnectActivation 1 \
-           oidcServiceMetaDataIssuer http://auth.example.com
+           issuerDBOpenIDConnectActivation 1
 
 Generate keys:
 
@@ -457,6 +483,7 @@ To update the master encryption key:
            key 'xxxxxxxxxxxxxxx'
 
 
+.. _cli-sessions:
 
 Sessions Management
 -------------------
@@ -480,6 +507,15 @@ Modify session ::
    lemonldap-ng-sessions setKey 9684dd2a6489bf2be2fbdd799a8028e3 \
       authenticationLevel 1
 
+
+.. versionadded:: 2.0.10
+   Delete all sessions by username
+
+::
+
+   lemonldap-ng-sessions delete --where uid=dwho
+
+
 Second Factors management
 -------------------------
 
diff --git a/doc/sources/admin/conf.py b/doc/sources/admin/conf.py
index 234f601d0..779221733 100644
--- a/doc/sources/admin/conf.py
+++ b/doc/sources/admin/conf.py
@@ -50,7 +50,7 @@ master_doc = 'start'
 
 # General information about the project.
 project = u'LemonLDAP::NG'
-copyright = u'2020, LemonLDAP::NG'
+copyright = u'2021, LemonLDAP::NG'
 author = u'LemonLDAP::NG'
 
 # The version info for the project you're documenting, acts as replacement for
diff --git a/doc/sources/admin/configvhost.rst b/doc/sources/admin/configvhost.rst
index bf4e56468..90e80ecf5 100644
--- a/doc/sources/admin/configvhost.rst
+++ b/doc/sources/admin/configvhost.rst
@@ -497,24 +497,24 @@ Options
 
 Some options are available:
 
--  Port: used to build redirection URL *(when user is not logged, or for
+-  **Port**: used to build redirection URL *(when user is not logged, or for
    CDA requests)*
--  HTTPS: used to build redirection URL
--  Maintenance mode: reject all requests with a maintenance message
--  Aliases: list of aliases for this virtual host *(avoid to rewrite
+-  **HTTPS**: used to build redirection URL
+-  **Maintenance mode**: reject all requests with a maintenance message
+-  **Aliases**: list of aliases for this virtual host *(avoid to rewrite
    rules,...)*
--  Access to trace: can be used for overwriting REMOTE_CUSTOM with a custom function.
+-  **Access to trace**: can be used for overwriting REMOTE_CUSTOM with a custom function.
    Provide a comma separated parameters list with custom function path and args.
    By example: My::accessToTrace, Doctor, Who
--  Type: handler type (normal,
+-  **Type**: handler type (normal,
    :doc:`ServiceToken Handler`,
    :doc:`DevOps Handler`,...)
--  Required authentication level: this option avoids to reject user with
+-  **Required authentication level**: this option avoids to reject user with
    a rule based on ``$_authenticationLevel``. When user has not got the
    required level, he is redirected to an upgrade page in the portal.
    This default level is required for ALL locations relative to this virtual host.
    It can be overrided for each locations.
--  ServiceToken timeout: by default, ServiceToken is just valid during 30
+-  **ServiceToken timeout**: by default, ServiceToken is just valid during 30
    seconds. This TTL can be customized for each virtual host.
 
 
diff --git a/doc/sources/admin/contribute.rst b/doc/sources/admin/contribute.rst
index 363a78ec3..c6ab5ba36 100644
--- a/doc/sources/admin/contribute.rst
+++ b/doc/sources/admin/contribute.rst
@@ -37,14 +37,14 @@ Go to your gitlab account : https://gitlab.ow2.org/profile/keys
 
    cat ~/.ssh/id_rsa.pub
 
-copy id_rsa.pub content to key section and enter a name into "Title"
-tans "Add key" button Test ssh connexion :
+Copy id_rsa.pub content to key section and enter a name into "Title" and click "Add key" button. 
+Test ssh connexion :
 
 ::
 
    ssh -T git@gitlab.com
 
-accept messages
+Accept messages
 
 Install basic tools
 -------------------
@@ -52,13 +52,13 @@ Install basic tools
 Debian
 ^^^^^^
 
-*root :*
+As *root :*
 
 ::
 
    apt install aptitude
    aptitude install vim make devscripts yui-compressor git git-gui libjs-uglify coffeescript cpanminus autopkgtest pkg-perl-autopkgtest
-   aptitude install libauth-yubikey-webclient-perl libnet-smtp-server-perl
+   aptitude install libauth-yubikey-webclient-perl libnet-smtp-server-perl libtime-fake-perl libtest-output-perl libtest-pod-perl libtest-leaktrace-perl
 
    cpanm Authen::U2F Authen::U2F::Tester Crypt::U2F::Server::Simple
 
@@ -71,7 +71,7 @@ Debian
 Configure Git
 ^^^^^^^^^^^^^
 
-*user :*
+As *user :*
 
 ::
 
@@ -85,7 +85,7 @@ Configure Git
 Import Project and using Git
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-*user :* create directory in directory :
+As *user*, create directory in directory:
 
 ::
 
@@ -98,7 +98,7 @@ Import Project and using Git
    git checkout v2.1 # to change branch
    git fetch upstream
 
-*import version branch* *on linux station :*
+Import version branch on linux station:
 
 ::
 
@@ -107,8 +107,7 @@ Import Project and using Git
    git rebase upstream/v2.1 # to align to parent project remote branch
    git push # to push to working remote branch
 
-*on gitlab, create working branch, one per thematic* *on linux station
-:*
+On gitlab, create working branch, one per thematic on linux station:
 
 ::
 
@@ -142,6 +141,12 @@ For SAML:
 Working Project
 ---------------
 
+Configure hosts file
+^^^^^^^^^^^^^^^^^^^^
+::
+
+     echo '127.0.0.1       auth.example.com manager.example.com test1.example.com test2.example.com' >> /etc/hosts
+
 Unit tests
 ^^^^^^^^^^
 
diff --git a/doc/sources/admin/crowdsec.rst b/doc/sources/admin/crowdsec.rst
new file mode 100644
index 000000000..a9c48d616
--- /dev/null
+++ b/doc/sources/admin/crowdsec.rst
@@ -0,0 +1,28 @@
+CrowdSec
+========
+
+Presentation
+------------
+
+`CrowdSec `__ is a free and open-source security
+automation tool leveraging local IP behavior detection and a
+community-powered IP reputation system.
+
+LL::NG provides a **CrowdSec** bouncer that can reject Crowdsec banned-IP
+requests or just provide an environment variable that can be used in
+another plugin rule. For example, a second factor may be required if user's
+IP is CrowdSec-banned.
+
+Configuration
+-------------
+
+To configure bouncer plugin, go in ``General Parameters`` > ``Plugins`` >
+``CrowdSec``.
+
+You can then configure:
+
+- **Activation**: enable this plugin *(default: disabled)*
+- **Action**: reject **or** warn and set ``$env->{CROWDSEC_REJECT} = 1``
+- **Base URL of local API**: base URL of CrowdSec local API
+  *(default: http://localhost:8080)*
+- **API key**: API key, usually given by ``cscli bouncers add mylemon``
diff --git a/doc/sources/admin/documentation.rst b/doc/sources/admin/documentation.rst
new file mode 100644
index 000000000..be9171ea5
--- /dev/null
+++ b/doc/sources/admin/documentation.rst
@@ -0,0 +1,168 @@
+Documentation
+=============
+
+Presentation
+------------
+
+|image0|
+
+-  :doc:`How it works `
+-  :doc:`Main features `
+-  :doc:`Quick start tutorial `
+
+Workshops
+---------
+
+-  LDAPCon 2019: `Connect LL::NG to OpenLDAP and use 2FA, configure SSO
+   on Fusion Directory and
+   Dokuwiki `__
+-  Pass the SALT 2019: `Connect LL::NG to OpenLDAP and use 2FA,
+   configure SSO on Fusion
+   Directory `__
+
+Installation and configuration
+------------------------------
+
+|image1|
+
+-  Maintained versions:
+
+   -  `Version 3.0 `__ (dev)
+   -  `Version 2.0 `__ (stable)
+   -  `Version 1.9 `__ (oldstable)
+
+-  Archived versions (unmaintained by `LLNG Team `__ )
+
+   -  `Version 1.4 `__
+   -  `Version 1.3 `__
+   -  `Version 1.2 `__
+   -  `Version 1.1 `__
+   -  `Version 1.0 `__
+
+Packaged versions
+~~~~~~~~~~~~~~~~~
+
+These versions are maintained under distribution umbrella following
+their policy.
+
+Debian
+^^^^^^
+
+Following Debian Policy, LLNG packages are never upgraded in
+published distributions. However, security patches are backported by
+maintenance teams *(except minor ones)*.
+
+=========== ======================== ======================================== ===================================================== ============================================================ =============================== =============================================================
+Debian dist                          LLNG version                             Secured                                               Maintenance                                                  LTS Limit                       `Extended LTS `__ Limit
+=========== ======================== ======================================== ===================================================== ============================================================ =============================== =============================================================
+*6*         *Squeeze*                *0.9.4.1*                                |maybe| No known vulnerability                        *None*                                                       *February 2016*                 *April 2019*
+**7**       Wheezy                   `1.1.2 `__          |maybe| No known vulnerability                        **None**  [1]_                                               May 2018                        Probably 2021
+**8**       Jessie                   `1.3.3 `__          |clean| CVE-2019-19791 tagged as minor                **None**  [1]_                                               June 2020                       Probably 2023
+**9**       Stretch                  `1.9.7 `__          |clean| CVE-2019-19791 tagged as minor                `Debian LTS Team `__            June 2022                      
+\           *Stretch-backports*      `2.0.2 `__          |bad| CVE-2019-12046, CVE-2019-13031, CVE-2019-15941  *None*                                                       *June 2019*                    
+\           Stretch-backports-sloppy `2.0.11 `__         |clean|                                               `LLNG Team `__, "best effort"  [3]_                   Until Debian 11 release  [4]_
+**10**      Buster                   `2.0.2 `__          |clean| CVE-2019-19791 tagged as minor                `Debian Security Team `__ Probably July 2024             
+\           Buster-backports         `2.0.11 `__         |clean|                                               `LLNG Team `__                                        Until Debian 11 release  [4]_
+**Next**    Testing                  Latest  [5]_                             |clean|                                               `LLNG Team `__                                                                       
+=========== ======================== ======================================== ===================================================== ============================================================ =============================== =============================================================
+
+See `Debian Security
+Tracker `__
+and `Debian Package
+Tracker `__ for more.
+
+Ubuntu
+^^^^^^
+
+Ubuntu version are included in "universe" branch [8]_, so
+not really security maintained. Prefer to use our repositories or Debian
+ones
+
+=========== ============= ================================ ==================================================================== ===========
+Ubuntu dist               LLNG version                     Secured                                                              Maintenance
+=========== ============= ================================ ==================================================================== ===========
+12.04       Precise       `1.1.2 `__  |maybe| No known vulnerability                                       None
+14.04       Trusty        `1.2.5 `__  |maybe| No known vulnerability                                       None
+16.04       Xenial  [9]_  `1.4.6 `__  |bad| CVE-2019-12046, CVE-2019-13031                                 None
+18.04       Bionic  [9]_  `1.9.16 `__ |bad| CVE-2019-12046, CVE-2019-13031, CVE-2020-24660                 None
+18.10       Cosmic        `1.9.17 `__ |bad| CVE-2019-12046, CVE-2019-13031, CVE-2020-24660                 None
+19.04       Disco         `2.0.2 `__  |bad| CVE-2019-12046, CVE-2019-13031, CVE-2019-15941, CVE-2020-24660 None
+19.10       Eoan          `2.0.5 `__  |bad| CVE-2019-15941, CVE-2020-24660                                 None
+20.04       Focal  [9]_   `2.0.7 `__  |bad| CVE-2020-24660                                                 None
+20.10       Groovy        `2.0.8 `__  |bad| CVE-2020-24660                                                 None
+=========== ============= ================================ ==================================================================== ===========
+
+Bug report
+----------
+
+See :doc:`Reporting a bug `.
+
+Development
+-----------
+
+|image13|
+
+-  `Bugtracker `__
+-  `Source
+   code `__
+-  `Nightly trunk builds `__
+   *(for Debian or Ubuntu,*\ **really unstable**\ *)*
+-  Git access:
+
+::
+
+   git clone https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng.git
+
+-  CPAN test reports:
+
+   -  `LemonLDAP::NG
+      Common `__
+   -  `LemonLDAP::NG
+      Handler `__
+   -  `LemonLDAP::NG
+      Portal `__
+   -  `LemonLDAP::NG
+      Manager `__
+
+Other
+-----
+
+|image14|
+
+-  `Conferences `__
+-  `References `__
+-  `Press `__
+
+.. [1]
+   Possible `Extended LTS `__
+
+.. [3]
+   updated by `LLNG Team `__ until dependencies are compatible
+
+.. [4]
+   around June 2021
+
+.. [5]
+   few days after release
+
+.. [8]
+   Ubuntu universe/multiverse branches are community maintained *(so not
+   maintained by Canonical)*, but in fact nobody considers LLNG security
+   issues. See `this
+   issue `__
+   for example
+
+.. [9]
+   LTS
+
+.. |clean| image:: /icons/clean.png
+   :width: 20px
+.. |bad| image:: /icons/bad.png
+   :width: 20px
+.. |maybe| image:: /icons/maybe.png
+   :width: 20px
+.. |image0| image:: /icons/tutorials.png
+.. |image1| image:: /icons/windowlist.png
+.. |image13| image:: /icons/terminal.png
+.. |image14| image:: /icons/wizard.png
+
diff --git a/doc/sources/admin/download.rst b/doc/sources/admin/download.rst
index 99e2fcac2..624ea799c 100644
--- a/doc/sources/admin/download.rst
+++ b/doc/sources/admin/download.rst
@@ -5,7 +5,7 @@ Release notes
 -------------
 
 Release notes for latest version:
-https://projects.ow2.org/view/lemonldap-ng/lemonldap-ng-2-0-8-is-out/
+https://projects.ow2.org/view/lemonldap-ng/lemonldap-ng-2-0-9-is-out
 
 Go on https://projects.ow2.org/bin/view/lemonldap-ng/ for older
 versions.
@@ -15,13 +15,13 @@ See also :doc:`upgrade notes`.
 Packages and archives
 ---------------------
 
-Stable version (2.0.8)
+Stable version (2.0.9)
 ~~~~~~~~~~~~~~~~~~~~~~
 
 Tarball
 ^^^^^^^
 
--  `Tarball `__
+-  `Tarball `__
 
 RPM
 ^^^
@@ -38,17 +38,17 @@ RHEL/CentOS 7
 '''''''''''''
 
 -  `RPM
-   bundle `__
+   bundle `__
 -  `Source
-   RPM `__
+   RPM `__
 
 RHEL/CentOS 8
 '''''''''''''
 
 -  `RPM
-   bundle `__
+   bundle `__
 -  `Source
-   RPM `__
+   RPM `__
 
 Debian
 ^^^^^^
@@ -67,7 +67,7 @@ Debian
 
 
 -  `DEB
-   bundle `__
+   bundle `__
 
 Docker
 ^^^^^^
diff --git a/doc/sources/admin/extendedfunctions.rst b/doc/sources/admin/extendedfunctions.rst
index c1e09075e..a5ca72824 100644
--- a/doc/sources/admin/extendedfunctions.rst
+++ b/doc/sources/admin/extendedfunctions.rst
@@ -29,6 +29,7 @@ Inside this jail, you can access to:
   * checkDate_
   * checkLogonHours_
   * date_
+  * dateToTime_ (|new| in version 2.0.12)
   * encrypt_
   * groupMatch_
   * has2f_ (|new| in version 2.0.10)
@@ -58,12 +59,43 @@ date
 Returns the date, in format YYYYMMDDHHMMSS, local time by default, GMT
 by calling ``date(1)``
 
+::
+
+    For example: date(1) lt '19551018080000'
+  
+dateToTime
+~~~~~~~~~~
+
+.. versionadded:: 2.0.12
+
+Converts a string date into epoch time.
+
+The date format is the LDAP date syntax, for example for the 1st March
+2009 (GMT):
+
+::
+
+    20090301000000Z
+
+The date may end with a differential timezone that is interpreted to 
+adjust the epoch time, for example for the 1st March 2009 (+0100):
+
+::
+
+    20090301000000+0100
+
+Simple usage example:
+
+::
+
+    dateToTime($ssoStartDate) lt dateToTime(date(1))
+
 checkLogonHours
 ~~~~~~~~~~~~~~~
 
 This function will check the day and the hour of current request, and
 compare it to allowed days and hours. It returns 1 if this match, 0
-else. All e By default, the allowed days and hours is an hexadecimal
+else. By default, the allowed days and hours is an hexadecimal
 value, representing each hour of the week. A day has 24 hours, and a
 week 7 days, so the value contains 168 bits, converted into 42
 hexadecimal characters. Sunday is the first day.
@@ -98,7 +130,6 @@ If you use the binary value (Active Directory), use this:
 
 ::
 
-   All e
    checkLogonHours($ssoLogonHours, 'octetstring')
 
 You can also configure jetlag (if all of your users use the same
@@ -125,6 +156,7 @@ rejected. You can allow these users instead of reject them:
 
    checkLogonHours($ssoLogonHours, '', '', '1')
 
+
 checkDate
 ~~~~~~~~~
 
@@ -132,17 +164,27 @@ This function will check the date of current request, and compare it to
 a start date and an end date. It returns 1 if this match, 0 else.
 
 
-The date format is the LDAP date syntax, for example for the 1st March
-2009:
+The date format is the LDAP date syntax, for example for the 1st of March
+2009 (GMT)
 
 ::
 
    20090301000000Z
 
+|new| Since version 2.0.12, the date may end with a differential timezone, 
+for example for the 1st of March 2009 (+0100):
+
+::
+
+    20090301000000+0100
+
+
 Functions parameters:
 
--  **start**: Start date (GMT)
--  **end**: End date (GMT)
+-  **start**: Start date (GMT unless, |new| since version 2.0.12, a
+   differential timezone is included)
+-  **end**: End date (GMT unless, |new| since version 2.0.12, a
+   differential timezone is included)
 -  **default_access** (optional): what result to return if **start** and
    **end** are empty
 
@@ -152,10 +194,10 @@ Simple usage example:
 
    checkDate($ssoStartDate, $ssoEndDate)
 
+
 basic
 ~~~~~
 
-
 .. attention::
 
     This function is not compliant with
@@ -177,6 +219,7 @@ Simple usage example:
 
    basic($uid,$_password)
 
+
 unicode2iso
 ~~~~~~~~~~~
 
diff --git a/doc/sources/admin/features.rst b/doc/sources/admin/features.rst
index e17706d99..5427e5410 100644
--- a/doc/sources/admin/features.rst
+++ b/doc/sources/admin/features.rst
@@ -14,13 +14,13 @@ LL::NG is designed using `Model–View–Controller software
 architecture `__,
 so you just have to
 :doc:`change HTML/CSS files` to
-custom portal.
+customize the portal.
 
 Easy to integrate
 -----------------
 
 :doc:`Integrating applications` in
-LL::NG is easy since its dialog with applications is based on
+LL::NG is easy since its dialogue with applications is based on
 :ref:`customizable HTTP headers`.
 
 Unifying authentications (Identity Federation)
@@ -32,7 +32,7 @@ heterogeneous architecture. LL:NG can be set as Identity provider,
 Service Provider or Protocol Proxy
 (:doc:`LL::NG as federation protocol proxy`).
 
-Its SOAP API can also be used to dialog directly with your custom
+Its SOAP API can also be used to dialogue directly with your custom
 applications.
 
 Sessions
@@ -48,9 +48,9 @@ opened sessions:
 
 -  by users
 -  by IP *(IPv4 and IPv6)*
--  by date
 -  by double IP (sessions opened by the same user from multiple
    computers)
+-  by date
 
 It can be used to delete a session
 
@@ -61,7 +61,7 @@ Session restrictions
 
 By default, a user can open several
 :doc:`sessions`. LL::NG can restrict
-this:
+the following:
 
 -  Allow only one session per user
 -  Allow only one IP address per user
@@ -78,11 +78,11 @@ LL::NG can be configured to provides
 -  one secured (SSL only) for sensitive applications
 -  one unsecured for other applications
 
-So if the http cookie is stolen, sensitive applications stay secured.
+So that if the http cookie is stolen, sensitive applications remain secured.
 
 Notifications
 -------------
 
-LL::NG can be used to prompt users with a message. This can be used to
-notify right changes,... See
-:doc:`notifications` for more.
+LL::NG can be used to notify users with a message when authenticating. This can be used to
+inform of a change in access rights, the publication of a new IT charter, etc. (See
+:doc:`notifications` for more details)
diff --git a/doc/sources/admin/finduser.rst b/doc/sources/admin/finduser.rst
new file mode 100644
index 000000000..8eb271ccf
--- /dev/null
+++ b/doc/sources/admin/finduser.rst
@@ -0,0 +1,57 @@
+|image0|
+
+Find user plugin
+================
+
+This plugin allows unauthenticated users to search for an user account to impersonate. This may be useful to randomly provide an
+identifier depending on allowed searching attributes and excluded values.
+
+.. attention::
+
+    FindUser plugin works only if :doc:`Impersonation plugin` is enabled.
+
+Configuration
+-------------
+
+Just enable it in the Manager (section “plugins”). Then, set searching attributes used for selecting accounts and randomly suggest one of them in login form. Excluding attributes can also be defined to exclude some user accounts and avoid to provide them.
+
+-  **Parameters**:
+
+   -  **Activation**: Enable / Disable this plugin
+   -  **Character used as wildcard**: Character that can be used by users as wildcard. An empty value disable wildcarded search requests
+   -  **Parameters control**: Regular expression used for checking searching values syntax
+   -  **Searching attributes**: For each attribute, you have to set a key (attribute as defined in UserBD) and a value that will be display in login form (placeholder). A value can be a multivalued list separated by multiValuesSeparator parameter (General Parameters > Advanced parameters > Separator). See note below.
+   -  **Excluding attributes**: You can defined here attributes used for excluding accounts. Set keys corresponding to UserBD attributes and values to exclude. A value can be a multivalued list separated by multiValuesSeparator parameter (General Parameters > Advanced parameters > Separator)
+
+.. note::
+
+   You can provide a 'multiValuesSeparator' separated list of allowed searching values that will be displayed as an HTML ",
-	options: {
-		appendTo: null,
-		autoFocus: false,
-		delay: 300,
-		minLength: 1,
-		position: {
-			my: "left top",
-			at: "left bottom",
-			collision: "none"
-		},
-		source: null,
-
-		// Callbacks
-		change: null,
-		close: null,
-		focus: null,
-		open: null,
-		response: null,
-		search: null,
-		select: null
-	},
-
-	requestIndex: 0,
-	pending: 0,
-
-	_create: function() {
-
-		// Some browsers only repeat keydown events, not keypress events,
-		// so we use the suppressKeyPress flag to determine if we've already
-		// handled the keydown event. #7269
-		// Unfortunately the code for & in keypress is the same as the up arrow,
-		// so we use the suppressKeyPressRepeat flag to avoid handling keypress
-		// events when we know the keydown event was used to modify the
-		// search term. #7799
-		var suppressKeyPress, suppressKeyPressRepeat, suppressInput,
-			nodeName = this.element[ 0 ].nodeName.toLowerCase(),
-			isTextarea = nodeName === "textarea",
-			isInput = nodeName === "input";
-
-		// Textareas are always multi-line
-		// Inputs are always single-line, even if inside a contentEditable element
-		// IE also treats inputs as contentEditable
-		// All other element types are determined by whether or not they're contentEditable
-		this.isMultiLine = isTextarea || !isInput && this._isContentEditable( this.element );
-
-		this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ];
-		this.isNewMenu = true;
-
-		this._addClass( "ui-autocomplete-input" );
-		this.element.attr( "autocomplete", "off" );
-
-		this._on( this.element, {
-			keydown: function( event ) {
-				if ( this.element.prop( "readOnly" ) ) {
-					suppressKeyPress = true;
-					suppressInput = true;
-					suppressKeyPressRepeat = true;
-					return;
-				}
-
-				suppressKeyPress = false;
-				suppressInput = false;
-				suppressKeyPressRepeat = false;
-				var keyCode = $.ui.keyCode;
-				switch ( event.keyCode ) {
-				case keyCode.PAGE_UP:
-					suppressKeyPress = true;
-					this._move( "previousPage", event );
-					break;
-				case keyCode.PAGE_DOWN:
-					suppressKeyPress = true;
-					this._move( "nextPage", event );
-					break;
-				case keyCode.UP:
-					suppressKeyPress = true;
-					this._keyEvent( "previous", event );
-					break;
-				case keyCode.DOWN:
-					suppressKeyPress = true;
-					this._keyEvent( "next", event );
-					break;
-				case keyCode.ENTER:
-
-					// when menu is open and has focus
-					if ( this.menu.active ) {
-
-						// #6055 - Opera still allows the keypress to occur
-						// which causes forms to submit
-						suppressKeyPress = true;
-						event.preventDefault();
-						this.menu.select( event );
-					}
-					break;
-				case keyCode.TAB:
-					if ( this.menu.active ) {
-						this.menu.select( event );
-					}
-					break;
-				case keyCode.ESCAPE:
-					if ( this.menu.element.is( ":visible" ) ) {
-						if ( !this.isMultiLine ) {
-							this._value( this.term );
-						}
-						this.close( event );
-
-						// Different browsers have different default behavior for escape
-						// Single press can mean undo or clear
-						// Double press in IE means clear the whole form
-						event.preventDefault();
-					}
-					break;
-				default:
-					suppressKeyPressRepeat = true;
-
-					// search timeout should be triggered before the input value is changed
-					this._searchTimeout( event );
-					break;
-				}
-			},
-			keypress: function( event ) {
-				if ( suppressKeyPress ) {
-					suppressKeyPress = false;
-					if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
-						event.preventDefault();
-					}
-					return;
-				}
-				if ( suppressKeyPressRepeat ) {
-					return;
-				}
-
-				// Replicate some key handlers to allow them to repeat in Firefox and Opera
-				var keyCode = $.ui.keyCode;
-				switch ( event.keyCode ) {
-				case keyCode.PAGE_UP:
-					this._move( "previousPage", event );
-					break;
-				case keyCode.PAGE_DOWN:
-					this._move( "nextPage", event );
-					break;
-				case keyCode.UP:
-					this._keyEvent( "previous", event );
-					break;
-				case keyCode.DOWN:
-					this._keyEvent( "next", event );
-					break;
-				}
-			},
-			input: function( event ) {
-				if ( suppressInput ) {
-					suppressInput = false;
-					event.preventDefault();
-					return;
-				}
-				this._searchTimeout( event );
-			},
-			focus: function() {
-				this.selectedItem = null;
-				this.previous = this._value();
-			},
-			blur: function( event ) {
-				if ( this.cancelBlur ) {
-					delete this.cancelBlur;
-					return;
-				}
-
-				clearTimeout( this.searching );
-				this.close( event );
-				this._change( event );
-			}
-		} );
-
-		this._initSource();
-		this.menu = $( "