Merge branch 'v2.0' into findUser

This commit is contained in:
Christophe Maudoux 2020-12-21 21:11:55 +01:00
commit bfcdd370df
35 changed files with 908 additions and 84 deletions

1
debian/control vendored
View File

@ -52,6 +52,7 @@ Build-Depends-Indep: libapache-session-perl <!nocheck>,
libxml-libxml-perl <!nocheck>,
libxml-libxslt-perl <!nocheck>,
libxml-simple-perl <!nocheck>,
libtest-leaktrace-perl <!nocheck>,
python3-sphinx,
python3-sphinx-bootstrap-theme,
perl

View File

@ -1472,7 +1472,7 @@
<div class="tab-content">
<div class="tab-pane active" id="examples-Casapp-addcasapp-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X POST "/api/v1/api/v1/providers/cas/app"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X POST "https://manager-api.example.com/api/v1/providers/cas/app"</code></pre>
</div>
<div class="tab-pane" id="examples-Casapp-addcasapp-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -1828,7 +1828,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Casapp-deleteCasApp-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X DELETE "/api/v1/api/v1/providers/cas/app/{confKey}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X DELETE "https://manager-api.example.com/api/v1/providers/cas/app/{confKey}"</code></pre>
</div>
<div class="tab-pane" id="examples-Casapp-deleteCasApp-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -2169,7 +2169,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Casapp-findCasAppByConfKey-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X GET "/api/v1/api/v1/providers/cas/app/findByConfKey?pattern="</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X GET "https://manager-api.example.com/api/v1/providers/cas/app/findByConfKey?pattern="</code></pre>
</div>
<div class="tab-pane" id="examples-Casapp-findCasAppByConfKey-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -2515,7 +2515,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Casapp-findCasAppByServiceUrl-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X GET "/api/v1/api/v1/providers/cas/app/findByServiceUrl?serviceUrl="</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X GET "https://manager-api.example.com/api/v1/providers/cas/app/findByServiceUrl?serviceUrl="</code></pre>
</div>
<div class="tab-pane" id="examples-Casapp-findCasAppByServiceUrl-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -2905,7 +2905,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Casapp-getCasAppByConfKey-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X GET "/api/v1/api/v1/providers/cas/app/{confKey}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X GET "https://manager-api.example.com/api/v1/providers/cas/app/{confKey}"</code></pre>
</div>
<div class="tab-pane" id="examples-Casapp-getCasAppByConfKey-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -3295,7 +3295,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Casapp-replaceCasApp-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X PUT "/api/v1/api/v1/providers/cas/app/{confKey}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X PUT "https://manager-api.example.com/api/v1/providers/cas/app/{confKey}"</code></pre>
</div>
<div class="tab-pane" id="examples-Casapp-replaceCasApp-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -3736,7 +3736,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Casapp-updateCasApp-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X PATCH "/api/v1/api/v1/providers/cas/app/{confKey}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X PATCH "https://manager-api.example.com/api/v1/providers/cas/app/{confKey}"</code></pre>
</div>
<div class="tab-pane" id="examples-Casapp-updateCasApp-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -4180,7 +4180,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Class2fa-deleteSecondFactors-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X DELETE "/api/v1/api/v1/secondFactor/{uid}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X DELETE "https://manager-api.example.com/api/v1/secondFactor/{uid}"</code></pre>
</div>
<div class="tab-pane" id="examples-Class2fa-deleteSecondFactors-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -4470,7 +4470,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Class2fa-deleteSecondFactorsById-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X DELETE "/api/v1/api/v1/secondFactor/{uid}/id/{id}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X DELETE "https://manager-api.example.com/api/v1/secondFactor/{uid}/id/{id}"</code></pre>
</div>
<div class="tab-pane" id="examples-Class2fa-deleteSecondFactorsById-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -4788,7 +4788,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Class2fa-deleteSecondFactorsByType-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X DELETE "/api/v1/api/v1/secondFactor/{uid}/type/{type}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X DELETE "https://manager-api.example.com/api/v1/secondFactor/{uid}/type/{type}"</code></pre>
</div>
<div class="tab-pane" id="examples-Class2fa-deleteSecondFactorsByType-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -5106,7 +5106,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Class2fa-getSecondFactors-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X GET "/api/v1/api/v1/secondFactor/{uid}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X GET "https://manager-api.example.com/api/v1/secondFactor/{uid}"</code></pre>
</div>
<div class="tab-pane" id="examples-Class2fa-getSecondFactors-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -5445,7 +5445,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Class2fa-getSecondFactorsById-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X GET "/api/v1/api/v1/secondFactor/{uid}/id/{id}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X GET "https://manager-api.example.com/api/v1/secondFactor/{uid}/id/{id}"</code></pre>
</div>
<div class="tab-pane" id="examples-Class2fa-getSecondFactorsById-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -5812,7 +5812,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Class2fa-getSecondFactorsByType-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X GET "/api/v1/api/v1/secondFactor/{uid}/type/{type}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X GET "https://manager-api.example.com/api/v1/secondFactor/{uid}/type/{type}"</code></pre>
</div>
<div class="tab-pane" id="examples-Class2fa-getSecondFactorsByType-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -6182,7 +6182,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Default-status-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X GET "/api/v1/api/v1/status"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X GET "https://manager-api.example.com/api/v1/status"</code></pre>
</div>
<div class="tab-pane" id="examples-Default-status-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -6489,7 +6489,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Menuapp-addMenuApp-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X POST "/api/v1/api/v1/menu/app/{cat}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X POST "https://manager-api.example.com/api/v1/menu/app/{cat}"</code></pre>
</div>
<div class="tab-pane" id="examples-Menuapp-addMenuApp-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -6931,7 +6931,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Menuapp-deleteMenuApp-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X DELETE "/api/v1/api/v1/menu/app/{cat}/{confKey}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X DELETE "https://manager-api.example.com/api/v1/menu/app/{cat}/{confKey}"</code></pre>
</div>
<div class="tab-pane" id="examples-Menuapp-deleteMenuApp-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -7303,7 +7303,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Menuapp-findMenuAppByConfKey-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X GET "/api/v1/api/v1/menu/app/{cat}/findByConfKey?pattern="</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X GET "https://manager-api.example.com/api/v1/menu/app/{cat}/findByConfKey?pattern="</code></pre>
</div>
<div class="tab-pane" id="examples-Menuapp-findMenuAppByConfKey-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -7687,7 +7687,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Menuapp-getMenuAppByConfKey-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X GET "/api/v1/api/v1/menu/app/{cat}/{confKey}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X GET "https://manager-api.example.com/api/v1/menu/app/{cat}/{confKey}"</code></pre>
</div>
<div class="tab-pane" id="examples-Menuapp-getMenuAppByConfKey-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -8108,7 +8108,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Menuapp-getMenuApps-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X GET "/api/v1/api/v1/menu/app/{cat}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X GET "https://manager-api.example.com/api/v1/menu/app/{cat}"</code></pre>
</div>
<div class="tab-pane" id="examples-Menuapp-getMenuApps-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -8502,7 +8502,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Menuapp-replaceMenuApp-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X PUT "/api/v1/api/v1/menu/app/{cat}/{confKey}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X PUT "https://manager-api.example.com/api/v1/menu/app/{cat}/{confKey}"</code></pre>
</div>
<div class="tab-pane" id="examples-Menuapp-replaceMenuApp-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -8974,7 +8974,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Menuapp-updateMenuApp-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X PATCH "/api/v1/api/v1/menu/app/{cat}/{confKey}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X PATCH "https://manager-api.example.com/api/v1/menu/app/{cat}/{confKey}"</code></pre>
</div>
<div class="tab-pane" id="examples-Menuapp-updateMenuApp-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -9449,7 +9449,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Menucat-addMenuCat-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X POST "/api/v1/api/v1/menu/cat"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X POST "https://manager-api.example.com/api/v1/menu/cat"</code></pre>
</div>
<div class="tab-pane" id="examples-Menucat-addMenuCat-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -9805,7 +9805,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Menucat-deleteMenuCat-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X DELETE "/api/v1/api/v1/menu/cat/{confKey}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X DELETE "https://manager-api.example.com/api/v1/menu/cat/{confKey}"</code></pre>
</div>
<div class="tab-pane" id="examples-Menucat-deleteMenuCat-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -10146,7 +10146,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Menucat-findMenuCatByConfKey-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X GET "/api/v1/api/v1/menu/cat/findByConfKey?pattern="</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X GET "https://manager-api.example.com/api/v1/menu/cat/findByConfKey?pattern="</code></pre>
</div>
<div class="tab-pane" id="examples-Menucat-findMenuCatByConfKey-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -10492,7 +10492,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Menucat-getMenuCatByConfKey-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X GET "/api/v1/api/v1/menu/cat/{confKey}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X GET "https://manager-api.example.com/api/v1/menu/cat/{confKey}"</code></pre>
</div>
<div class="tab-pane" id="examples-Menucat-getMenuCatByConfKey-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -10882,7 +10882,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Menucat-replaceMenuCat-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X PUT "/api/v1/api/v1/menu/cat/{confKey}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X PUT "https://manager-api.example.com/api/v1/menu/cat/{confKey}"</code></pre>
</div>
<div class="tab-pane" id="examples-Menucat-replaceMenuCat-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -11323,7 +11323,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Menucat-updateMenuCat-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X PATCH "/api/v1/api/v1/menu/cat/{confKey}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X PATCH "https://manager-api.example.com/api/v1/menu/cat/{confKey}"</code></pre>
</div>
<div class="tab-pane" id="examples-Menucat-updateMenuCat-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -11767,7 +11767,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Oidcrp-addoidcrp-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X POST "/api/v1/api/v1/providers/oidc/rp"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X POST "https://manager-api.example.com/api/v1/providers/oidc/rp"</code></pre>
</div>
<div class="tab-pane" id="examples-Oidcrp-addoidcrp-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -12123,7 +12123,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Oidcrp-deleteOidcRp-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X DELETE "/api/v1/api/v1/providers/oidc/rp/{confKey}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X DELETE "https://manager-api.example.com/api/v1/providers/oidc/rp/{confKey}"</code></pre>
</div>
<div class="tab-pane" id="examples-Oidcrp-deleteOidcRp-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -12464,7 +12464,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Oidcrp-findOidcRpByClientId-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X GET "/api/v1/api/v1/providers/oidc/rp/findByClientId?clientId="</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X GET "https://manager-api.example.com/api/v1/providers/oidc/rp/findByClientId?clientId="</code></pre>
</div>
<div class="tab-pane" id="examples-Oidcrp-findOidcRpByClientId-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -12854,7 +12854,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Oidcrp-findOidcRpByConfKey-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X GET "/api/v1/api/v1/providers/oidc/rp/findByConfKey?pattern="</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X GET "https://manager-api.example.com/api/v1/providers/oidc/rp/findByConfKey?pattern="</code></pre>
</div>
<div class="tab-pane" id="examples-Oidcrp-findOidcRpByConfKey-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -13200,7 +13200,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Oidcrp-getOidcRpByConfKey-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X GET "/api/v1/api/v1/providers/oidc/rp/{confKey}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X GET "https://manager-api.example.com/api/v1/providers/oidc/rp/{confKey}"</code></pre>
</div>
<div class="tab-pane" id="examples-Oidcrp-getOidcRpByConfKey-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -13590,7 +13590,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Oidcrp-replaceOidcRp-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X PUT "/api/v1/api/v1/providers/oidc/rp/{confKey}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X PUT "https://manager-api.example.com/api/v1/providers/oidc/rp/{confKey}"</code></pre>
</div>
<div class="tab-pane" id="examples-Oidcrp-replaceOidcRp-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -14031,7 +14031,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Oidcrp-updateOidcRp-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X PATCH "/api/v1/api/v1/providers/oidc/rp/{confKey}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X PATCH "https://manager-api.example.com/api/v1/providers/oidc/rp/{confKey}"</code></pre>
</div>
<div class="tab-pane" id="examples-Oidcrp-updateOidcRp-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -14475,7 +14475,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Samlsp-addsamlsp-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X POST "/api/v1/api/v1/providers/saml/sp"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X POST "https://manager-api.example.com/api/v1/providers/saml/sp"</code></pre>
</div>
<div class="tab-pane" id="examples-Samlsp-addsamlsp-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -14831,7 +14831,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Samlsp-deleteSamlSp-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X DELETE "/api/v1/api/v1/providers/saml/sp/{confKey}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X DELETE "https://manager-api.example.com/api/v1/providers/saml/sp/{confKey}"</code></pre>
</div>
<div class="tab-pane" id="examples-Samlsp-deleteSamlSp-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -15172,7 +15172,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Samlsp-findSamlSpByConfKey-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X GET "/api/v1/api/v1/providers/saml/sp/findByConfKey?pattern="</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X GET "https://manager-api.example.com/api/v1/providers/saml/sp/findByConfKey?pattern="</code></pre>
</div>
<div class="tab-pane" id="examples-Samlsp-findSamlSpByConfKey-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -15518,7 +15518,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Samlsp-findSamlSpByEntityId-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X GET "/api/v1/api/v1/providers/saml/sp/findByEntityId?entityId="</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X GET "https://manager-api.example.com/api/v1/providers/saml/sp/findByEntityId?entityId="</code></pre>
</div>
<div class="tab-pane" id="examples-Samlsp-findSamlSpByEntityId-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -15908,7 +15908,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Samlsp-getSamlSpByConfKey-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X GET "/api/v1/api/v1/providers/saml/sp/{confKey}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X GET "https://manager-api.example.com/api/v1/providers/saml/sp/{confKey}"</code></pre>
</div>
<div class="tab-pane" id="examples-Samlsp-getSamlSpByConfKey-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -16298,7 +16298,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Samlsp-replaceSamlSp-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X PUT "/api/v1/api/v1/providers/saml/sp/{confKey}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X PUT "https://manager-api.example.com/api/v1/providers/saml/sp/{confKey}"</code></pre>
</div>
<div class="tab-pane" id="examples-Samlsp-replaceSamlSp-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;
@ -16739,7 +16739,7 @@ except ApiException as e:
<div class="tab-content">
<div class="tab-pane active" id="examples-Samlsp-updateSamlSp-0-curl">
<pre class="prettyprint"><code class="language-bsh">curl -X PATCH "/api/v1/api/v1/providers/saml/sp/{confKey}"</code></pre>
<pre class="prettyprint"><code class="language-bsh">curl -X PATCH "https://manager-api.example.com/api/v1/providers/saml/sp/{confKey}"</code></pre>
</div>
<div class="tab-pane" id="examples-Samlsp-updateSamlSp-0-java">
<pre class="prettyprint"><code class="language-java">import io.swagger.client.*;

View File

@ -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.0 # to change branch
git fetch upstream
*import version branch* *on linux station :*
Import version branch on linux station:
::
@ -106,8 +106,7 @@ Import Project and using Git
git fetch upstream
git rebase upstream/v2.0 # to align to parent project remote branch
*on gitlab, create working branch, one per thematic* *on linux station
:*
On gitlab, create working branch, one per thematic on linux station:
::
@ -141,6 +140,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
^^^^^^^^^^

181
doc/sources/admin/hooks.rst Normal file
View File

@ -0,0 +1,181 @@
Available plugin hooks
======================
OpenID Connect Issuer hooks
---------------------------
oidcGotRequest
~~~~~~~~~~~~~~~
.. versionadded:: 2.0.10
This hook is triggered when LemonLDAP::NG received an authorization request on the `/oauth2/authorize` endpoint.
The hook's parameter is a hash containing the authorization request parameters.
Sample code::
use constant hook => {
oidcGotRequest => 'addScopeToRequest',
};
sub addScopeToRequest {
my ( $self, $req, $oidc_request ) = @_;
$oidc_request->{scope} = $oidc_request->{scope} . " my_hooked_scope";
return PE_OK;
}
oidcGenerateUserInfoResponse
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 2.0.10
This hook is triggered when LemonLDAP::NG is about to send a UserInfo response to a relying party on the `/oauth2/userinfo` endpoint.
The hook's parameter is a hash containing all the claims that are about to be released.
Sample code::
use constant hook => {
oidcGenerateUserInfoResponse => 'addClaimToUserInfo',
};
sub addClaimToUserInfo {
my ( $self, $req, $userinfo ) = @_;
$userinfo->{"userinfo_hook"} = 1;
return PE_OK;
}
oidcGenerateIDToken
~~~~~~~~~~~~~~~~~~~
.. versionadded:: 2.0.10
This hook is triggered when LemonLDAP::NG is generating an ID Token.
The hook's parameters are:
* A hash of the claims to be contained in the ID Token
* the configuration key of the relying party which will receive the token
Sample code::
use constant hook => {
oidcGenerateIDToken => 'addClaimToIDToken',
};
sub addClaimToIDToken {
my ( $self, $req, $payload, $rp ) = @_;
$payload->{"id_token_hook"} = 1;
return PE_OK;
}
SAML Issuer hooks
-----------------
samlGotAuthnRequest
~~~~~~~~~~~~~~~~~~~
.. versionadded:: 2.0.10
This hook is triggered when LemonLDAP::NG has received a SAML login request
The hook's parameter is the Lasso::Login object
Sample code::
use constant hook => {
samlGotAuthnRequest => 'gotRequest',
};
sub gotRequest {
my ( $self, $res, $login ) = @_;
# Your code here
}
samlBuildAuthnResponse
~~~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 2.0.10
This hook is triggered when LemonLDAP::NG is about to build a response to the SAML login request
The hook's parameter is the Lasso::Login object
Sample code::
use constant hook => {
samlBuildAuthnResponse => 'buildResponse',
};
sub buildResponse {
my ( $self, $res, $login ) = @_;
# Your code here
}
samlGotLogoutRequest
~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 2.0.10
This hook is triggered when LemonLDAP::NG has received a SAML logout request
The hook's parameter is the Lasso::Logout object
Sample code::
use constant hook => {
samlGotLogoutRequest => 'gotLogout',
};
sub gotLogout {
my ( $self, $res, $logout ) = @_;
# Your code here
}
samlGotLogoutResponse
~~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 2.0.10
This hook is triggered when LemonLDAP::NG has received a SAML logout response
The hook's parameter is the Lasso::Logout object
Sample code::
use constant hook => {
samlGotLogoutResponse => 'gotLogoutResponse',
};
sub gotLogoutResponse {
my ( $self, $res, $logout ) = @_;
# Your code here
}
samlBuildLogoutResponse
~~~~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 2.0.10
This hook is triggered when LemonLDAP::NG is about to generate a SAML logout response
The hook's parameter is the Lasso::Logout object
Sample code::
use constant hook => {
samlBuildLogoutResponse => 'buildLogoutResponse',
};
sub buildLogoutResponse {
my ( $self, $res, $logout ) = @_;
# Your code here
}

View File

@ -25,6 +25,7 @@ LemonLDAP::NG provides packages for Red Hat/Centos 7:
- lemonldap-ng-fastcgi-server: FastCGI server needed to use Nginx
- lemonldap-ng-nginx: contains Nginx configuration and dependencies
- lemonldap-ng-uwsgi-app: contains Uwsgi application
- lemonldap-ng-selinux: contains the SELinux policy for httpd
- perl-Lemonldap-NG-Common: CPAN - Shared modules
- perl-Lemonldap-NG-Handler: CPAN - Handler modules
- perl-Lemonldap-NG-Manager: CPAN - Manager modules
@ -124,6 +125,9 @@ If the packages are stored in a yum repository:
yum install lemonldap-ng
# If you use SELinux
yum install lemonldap-ng-selinux
You can also use yum on local RPMs file:
::

View File

@ -4,6 +4,9 @@ Write a custom plugin
Presentation
------------
Standard entry points
~~~~~~~~~~~~~~~~~~~~~
You can now write a custom portal plugin that will hook in the
authentication process:
@ -18,6 +21,9 @@ authentication process:
- ``forAuthUser``: method called for already authenticated users
- ``beforeLogout``: method called before logout
Extended entry points
~~~~~~~~~~~~~~~~~~~~~
If you need to call a method just after any standard method in
authentication process, then use ``afterSub``, for example:
@ -48,6 +54,23 @@ authentication process, then use ``aroundSub``, for example:
return $ret;
}
Hooks
~~~~~
.. versionadded:: 2.0.10
Your plugin can also register itself to be called at some points of interest
within the main LemonLDAP::NG code.
.. toctree::
:maxdepth: 1
hooks
Routes
~~~~~~
The plugin can also define new routes and call actions on them.
See also ``Lemonldap::NG::Portal::Main::Plugin`` man page.

View File

@ -26,7 +26,7 @@ Main components
`CAS <http://en.wikipedia.org/wiki/Central_Authentication_Service>`__).
Futhermore, Portal affordes many other features (see
:doc:`portal<portal>` for more)
- **Handler**: used to protect applications which can read HTTP headers
- :doc:`Handler<index_handler>`: used to protect applications which can read HTTP headers
or environment variables to get user information
Databases
@ -36,7 +36,7 @@ Databases
.. attention::
We call "database" a backend where we can read or write a data.
This can be a file, an LDAP directory, ...
This can be a file, an LDAP directory, etc.
We split databases in two categories:
@ -130,12 +130,13 @@ Session expiration
~~~~~~~~~~~~~~~~~~
The session expires after 20 hours by default.
This duration can be set in the manager's Configuration tab (General Parameters > Sessions > Sessions Timeout).
.. attention::
- Handlers have a session cache, with a default lifetime of 10 minutes.
So for Handlers on different physical servers than the Portal, a user
with an expired session can still be authorized till the cache
So for Handlers located on different physical servers than the Portal, a user
with an expired session can still be authorized until the cache
expires.
- Sessions are deleted by a scheduled task. Don't forget to install
cron files !

View File

@ -42,6 +42,8 @@ CentOS / RHEL
yum update
yum install epel-release
yum install lemonldap-ng
# If you use SELinux
yum install lemonldap-ng-selinux
SSO domain configuration
------------------------

View File

@ -4,11 +4,27 @@ SELinux
To make LemonLDAP::NG work with SELinux, you may need to set up some
options.
SELinux policy package
----------------------
If you are using a RPM distribution and Apache as the web server, you need to
install the ``lemonldap-ng-selinux`` package to configure SELinux context correctly ::
yum install lemonldap-ng-selinux
.. note::
On CentOS 8 and Fedora, this is done automatically
This package will not configure SELinux booleans, please read the next sections to see which booleans you need to enable manually
Disk cache (sessions an configuration)
--------------------------------------
You need to set the correct context on the cache directory
.. deprecated:: 2.0.10
this is now done by the ``lemonldap-ng-selinux`` package
::
semanage fcontext --add -t httpd_cache_t -f a '/var/cache/lemonldap-ng(/.*)?'

View File

@ -23,6 +23,7 @@ backups and a rollback plan ready!
- New dependency: IO::Socket::Timeout
- TOTP check tolerates forward AND backward clock drift (totp2fRange)
- Avoid assignment in expressions option is disabled by default
- RHEL/CentOS SELinux users should install the new ``lemonldap-ng-selinux`` package to fix `an issue with the new default cache directory <https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/-/issues/2401>`__
2.0.9
-----

View File

@ -1,7 +1,7 @@
Writing rules and headers
=========================
Lemonldap::NG manage applications by their hostname (Apache's
Lemonldap::NG manages applications by their hostname (Apache's
virtualHosts). Rules are used to protect applications, headers are HTTP
headers added to the request to give datas to the application (for logs,
profiles,...).

View File

@ -4,7 +4,7 @@ info:
description: The Manager API allows an administrator to modify the LemonLDAP::NG configuration programmatically. It is not meant to be accessed by end users. The client libraries mentionned in examples can be generated from doc/sources/manager-api/openapi-spec.yaml
version: 2.0.9
servers:
- url: /api/v1
- url: https://manager-api.example.com
tags:
- name: samlsp
description: SAML Service Providers

View File

@ -14,7 +14,7 @@ use lib dirname( abs_path $0 );
#########################
# Insert your test code below, the Test::More module is used here so read
# its man page ( perldoc Test::More ) for help writing this test script.
# its man page (perldoc Test::More) for help writing this test script.
my $h;
$h = 'Lemonldap::NG::Handler::Test';
$ENV{SERVER_NAME} = "test1.example.com";

View File

@ -692,24 +692,24 @@ sub tests {
}
grep { /\d+/ }
split /\s*,\s*/, $conf->{bruteForceProtectionLockTimes};
$conf->{bruteForceProtectionLockTimes} = join ', ', @lockTimes;
$conf->{bruteForceProtectionLockTimes} = join ', ', @lockTimes if scalar @lockTimes;
return 1 unless ( $conf->{bruteForceProtection} );
return ( 1,
return ( 0,
'"History" plugin is required to enable "BruteForceProtection" plugin'
) unless ( $conf->{loginHistoryEnabled} );
return ( 1,
return ( 0,
'Number of failed logins must be higher than 1 to enable "BruteForceProtection" plugin'
) unless ( $conf->{failedLoginNumber} > 1 );
return ( 1,
return ( 0,
'Number of allowed failed logins must be higher than 0 to enable "BruteForceProtection" plugin'
) unless ( $conf->{bruteForceProtectionMaxFailed} > 0 );
return ( 1,
return ( 0,
'Number of failed logins history must be higher or equal than allowed failed logins plus lock time values'
)
if ( $conf->{bruteForceProtectionIncrementalTempo}
&& $conf->{failedLoginNumber} <
$conf->{bruteForceProtectionMaxFailed} + scalar @lockTimes );
return ( 1,
return ( 0,
'Number of failed logins history must be higher or equal than allowed failed logins'
)
unless ( $conf->{failedLoginNumber} >=

View File

@ -297,9 +297,13 @@ sub extractFormInfo {
}
# Get NameID
my $nameid = $login->nameIdentifier;
my $nameid_content = $nameid->content;
my $nameid = $login->nameIdentifier;
unless ($nameid) {
$self->userLogger->error("No NameID element found");
return PE_SAML_SSO_ERROR;
}
my $nameid_content = $nameid->content;
unless ($nameid_content) {
$self->userLogger->error("No NameID value found");
return PE_SAML_SSO_ERROR;

View File

@ -178,6 +178,10 @@ sub run {
}
}
my $h =
$self->p->processHook( $req, 'oidcGotRequest', $oidc_request );
return PE_ERROR if ( $h != PE_OK );
# Detect requested flow
my $response_type = $oidc_request->{'response_type'};
my $flow = $self->getFlowType($response_type);
@ -1364,7 +1368,7 @@ sub _handleAuthorizationCodeGrant {
}
# Create ID Token
my $id_token = $self->createIDToken( $id_token_payload_hash, $rp );
my $id_token = $self->createIDToken( $req, $id_token_payload_hash, $rp );
unless ($id_token) {
$self->logger->error(
@ -1582,7 +1586,7 @@ sub _handleRefreshTokenGrant {
}
# Create ID Token
my $id_token = $self->createIDToken( $id_token_payload_hash, $rp );
my $id_token = $self->createIDToken( $req, $id_token_payload_hash, $rp );
unless ($id_token) {
$self->logger->error(
@ -2298,7 +2302,7 @@ sub _generateIDToken {
}
# Create ID Token
return $self->createIDToken( $id_token_payload_hash, $rp );
return $self->createIDToken( $req, $id_token_payload_hash, $rp );
}
sub _redirectToUrl {

View File

@ -385,6 +385,10 @@ sub run {
return PE_SAML_SSO_ERROR;
}
my $h =
$self->p->processHook( $req, 'samlGotAuthnRequest', $login );
return $h if ( $h != PE_OK );
# Get SP entityID
my $sp = $request ? $login->remote_providerID() : $idp_initiated_sp;
@ -847,6 +851,10 @@ sub run {
"SAML authentication response sent to SAML SP $spConfKey for $user$nameIDLog"
);
$h =
$self->p->processHook( $req, 'samlBuildAuthnResponse', $login );
return $h if ( $h != PE_OK );
# Build SAML response
$protocolProfile = $login->protocolProfile();
@ -1124,6 +1132,12 @@ sub soapSloServer {
$self->logger->debug("SLO: Logout request is valid");
my $h = $self->p->processHook( $req, 'samlGotLogoutRequest', $logout );
if ( $h != PE_OK ) {
return $self->p->sendError( $req,
"SLO: samlGotLogoutRequest hook returned error", 400 );
}
# We accept only SOAP here
unless ( $method eq $self->getHttpMethod('soap') ) {
return $self->p->sendError( $req,
@ -1283,6 +1297,13 @@ sub soapSloServer {
"SLO response signature according to metadata");
}
$h =
$self->p->processHook( $req, 'samlBuildLogoutResponse', $logout );
if ( $h != PE_OK ) {
return $self->p->sendError( $req,
"SLO: samlBuildLogoutResponse hook returned error", 400 );
}
# Send logout response
unless ( $self->buildLogoutResponseMsg($logout) ) {
$self->logger->error("Unable to build SLO response");
@ -1614,8 +1635,8 @@ sub sloResume {
$req->setInfo( $logoutContextSession->data->{info} )
if $logoutContextSession->data->{info};
return $self->_finishSlo( $req, $logout, $method, $spConfKey, $provider_nb,
$relayID );
return $self->_finishSlo( $req, $logout, $method, $spConfKey,
$provider_nb, $relayID );
}
sub _finishSlo {
@ -1679,6 +1700,12 @@ sub sloServer {
$self->logger->debug("SLO: Logout request is valid");
my $h = $self->p->processHook( $req, 'samlGotLogoutRequest', $logout );
if ( $h != PE_OK ) {
return $self->p->sendError( $req,
"SLO: samlGotLogoutRequest hook returned error", 400 );
}
# Get SP entityID
my $sp = $logout->remote_providerID();
$req->env->{llng_saml_sp} = $sp;
@ -1901,6 +1928,9 @@ sub sloServer {
$self->logger->debug("Logout response is valid");
my $h = $self->p->processHook( $req, 'samlGotLogoutResponse', $logout );
$self->imgnok($req) if ( $h != PE_OK );
# Check Destination
$self->imgnok($req)
unless ( $self->checkDestination( $logout->response, $url ) );
@ -2018,8 +2048,8 @@ sub attributeServer {
my $name_id = $query->nameIdentifier();
unless ($name_id) {
$self->p->sendError( $req, "Fail to get NameID from attribute request",
400 );
$self->p->sendError( $req,
"Fail to get NameID from attribute request", 400 );
}
my $user = $name_id->content();
@ -2077,8 +2107,8 @@ sub attributeServer {
eval { @requested_attributes = $query->request()->Attribute(); };
if ($@) {
$self->checkLassoError($@);
return $self->p->sendError( $req, "Unable to get requested attributes",
400 );
return $self->p->sendError( $req,
"Unable to get requested attributes", 400 );
}
# Returned attributes

View File

@ -1394,6 +1394,10 @@ sub buildUserInfoResponse {
}
}
my $h = $self->p->processHook( $req, 'oidcGenerateUserInfoResponse',
$userinfo_response );
return {} if ( $h != PE_OK );
return $userinfo_response;
}
@ -1577,13 +1581,16 @@ sub createJWT {
# @param rp Internal Relying Party identifier
# @return String id_token ID Token as JWT
sub createIDToken {
my ( $self, $payload, $rp ) = @_;
my ( $self, $req, $payload, $rp ) = @_;
# Get signature algorithm
my $alg = $self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsIDTokenSignAlg};
$self->logger->debug("ID Token signature algorithm: $alg");
my $h = $self->p->processHook( $req, 'oidcGenerateIDToken', $payload, $rp );
return undef if ( $h != PE_OK );
return $self->createJWT( $payload, $alg, $rp );
}

View File

@ -74,6 +74,9 @@ BEGIN {
has 'afterSub' => ( is => 'rw', default => sub { {} } );
has 'aroundSub' => ( is => 'rw', default => sub { {} } );
# Issuer hooks
has 'hook' => ( is => 'rw', default => sub { {} } );
has spRules => (
is => 'rw',
default => sub { {} }
@ -496,6 +499,25 @@ sub findEP {
}
}
}
if ( $obj->can('hook') ) {
$self->logger->debug("Found hook in $plugin");
my $h = $obj->hook;
unless ( ref $h and ref($h) eq 'HASH' ) {
$self->logger->error('"hook" endpoint must be a hashref, skipped');
}
else {
foreach my $hookname ( keys %$h ) {
my $callback = $h->{$hookname};
push @{ $self->hook->{$hookname} }, sub {
eval {
$obj->logger->debug(
"Launching ${plugin}::$callback on hook $hookname");
};
$obj->$callback(@_);
};
}
}
}
$self->logger->debug("Plugin $plugin initializated");
# Rules for menu

View File

@ -320,6 +320,19 @@ method. Example:
Do not launch "getUser" but use the given C<$sub>. This permits multiple
plugins to use "aroundSub" in the same time.
=item C<hook>: hash ref that gives methods to call when a hook is triggered in the
LemonLDAP::NG code. Example:
use constant hook => {
oidcGenerateIDToken => 'addClaimToIDToken'
};
sub addClaimToIDToken {
my ( $self, $req, $payload, $rp ) = @_;
$payload->{"id_token_hook"} = 1;
return PE_OK;
}
=back
=head1 LOGGING

View File

@ -44,6 +44,22 @@ sub process {
return $err;
}
sub processHook {
my ( $self, $req, $hookName, @args ) = @_;
$self->logger->debug("Calling hook $hookName");
my $err = PE_OK;
for my $sub ( @{ $self->hook->{$hookName} } ) {
if ( ref $sub eq 'CODE' ) {
last if ( $err = $sub->( $req, @args ) );
}
else {
$self->logger->debug("Not a code ref: $sub");
}
}
return $err;
}
sub _formatProcessResult {
my ( $self, $err ) = @_;
return ( ( $err > 0 ? "error" : "status" )

View File

@ -47,7 +47,7 @@ sub init {
return 0;
}
unless ( $self->conf->{failedLoginNumber} > $self->maxFailed ) {
unless ( $self->conf->{failedLoginNumber} >= $self->maxFailed ) {
$self->logger->error( 'Number of failed logins history ('
. $self->conf->{failedLoginNumber}
. ') must be higher than allowed failed logins attempt ('

View File

@ -3,7 +3,7 @@
<p>
<span trspan="hello">Hello</span> $cn,<br />
<br />
<span><img src="cid:arrow:../common/bullet_go.png" /></span>
<span><img src="cid:arrow:../common/bullet_go.png" alt="go"/></span>
<a href="$url" style="text-decoration:none;color:orange;">
<span trspan="click2ResetCertificate">Click here to reset your certificate</span>
</a>

View File

@ -3,7 +3,7 @@
<p>
<span trspan="hello">Hello</span> $cn,<br />
<br />
<span><img src="cid:arrow:../common/bullet_go.png" /></span>
<span><img src="cid:arrow:../common/bullet_go.png" alt="go"/></span>
<a href="$url" style="text-decoration:none;color:orange;">
<span trspan="click2Reset">Click here to reset your password</span>
</a>

View File

@ -3,6 +3,6 @@
<div id="page" style="font-family:sans-serif;font-size:12pt;padding:5px 20px;">
<div id="header" style="text-align:center;"><img src="cid:logo:../../htdocs/static/<TMPL_VAR NAME="MAIN_LOGO">" style="max-width:150px;" /></div>
<div id="header" style="text-align:center;"><img src="cid:logo:../../htdocs/static/<TMPL_VAR NAME="MAIN_LOGO">" style="max-width:150px;" alt="logo"/></div>
<div id="content" style="padding:5px 10px;margin:10px 0;">

View File

@ -5,7 +5,7 @@
<br />
<TMPL_IF NAME="RESET">
<span trspan="newPwdIs">Your new password is</span>
<span><img src="cid:key:../common/key.png" /></span>
<span><img src="cid:key:../common/key.png" alt="key"/></span>
<b>$password</b>
<TMPL_ELSE>
<span trspan="pwdChanged">Your password has been successfully changed!</span>

View File

@ -3,7 +3,7 @@
<p>
<span trspan="hello">Hello</span> $firstname $lastname,<br />
<br />
<span><img src="cid:arrow:../common/bullet_go.png" /></span>
<span><img src="cid:arrow:../common/bullet_go.png" alt="go"/></span>
<a href="$url" style="text-decoration:none;color:orange;">
<span trspan="click2Register">Click here to confirm your account registration</span>
</a>

View File

@ -7,11 +7,11 @@
<br />
<br />
<span trspan="yourLoginIs">Your login is</span>
<span><img src="cid:key:../common/bullet_go.png" /></span>
<span><img src="cid:key:../common/bullet_go.png" alt="go"/></span>
<b>$login</b>
<br />
<span trspan="pwdIs">Your password is</span>
<span><img src="cid:key:../common/key.png" /></span>
<span><img src="cid:key:../common/key.png" alt="key"/></span>
<b>$password</b>
</p>
<p><a href="$url"><span trspan="goToPortal">Click here to access to portal</span></a></p>

View File

@ -0,0 +1,185 @@
use lib 'inc';
use Test::More;
use strict;
use IO::String;
use LWP::UserAgent;
use LWP::Protocol::PSGI;
use MIME::Base64;
BEGIN {
require 't/test-lib.pm';
require 't/saml-lib.pm';
}
my $maintests = 3;
my $debug = 'error';
my ( $issuer, $sp, $res );
# Redefine LWP methods for tests
LWP::Protocol::PSGI->register(
sub {
my $req = Plack::Request->new(@_);
fail('POST should not launch SOAP requests');
count(1);
return [ 500, [], [] ];
}
);
SKIP: {
eval "use Lasso";
if ($@) {
skip 'Lasso not found', $maintests;
}
# Initialization
$issuer = register( 'issuer', \&issuer );
$sp = register( 'sp', \&sp );
my ( $url, $s, $pdata, $host );
# Simple SP access
ok(
$res = $sp->_get(
'/', accept => 'text/html',
),
'Unauth SP request'
);
expectOK($res);
( $host, $url, $s ) =
expectAutoPost( $res, 'auth.idp.com', '/saml/singleSignOn',
'SAMLRequest' );
# Push SAML request to IdP
ok(
$res = $issuer->_post(
$url,
IO::String->new($s),
accept => 'text/html',
length => length($s)
),
'Post SAML request to IdP'
);
expectOK($res);
$pdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
# Try to authenticate with an authorized user to IdP
$s = "user=french&password=french&$s";
ok(
$res = $issuer->_post(
$url,
IO::String->new($s),
accept => 'text/html',
cookie => $pdata,
length => length($s),
),
'Post authentication'
);
my $idpId = expectCookie($res);
# Expect failure triggered by the hook
expectPortalError( $res, -999 );
}
count($maintests);
clean_sessions();
done_testing( count() );
sub issuer {
return LLNG::Manager::Test->new( {
ini => {
logLevel => $debug,
domain => 'idp.com',
portal => 'http://auth.idp.com',
authentication => 'Demo',
userDB => 'Same',
issuerDBSAMLActivation => 1,
issuerDBSAMLRule => '$uid eq "french"',
samlSPMetaDataOptions => {
'sp.com' => {
samlSPMetaDataOptionsEncryptionMode => 'none',
samlSPMetaDataOptionsSignSSOMessage => 1,
samlSPMetaDataOptionsSignSLOMessage => 1,
samlSPMetaDataOptionsCheckSSOMessageSignature => 1,
samlSPMetaDataOptionsCheckSLOMessageSignature => 1,
}
},
samlSPMetaDataExportedAttributes => {
'sp.com' => {
cn =>
'1;cn;urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
uid =>
'1;uid;urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
}
},
samlOrganizationDisplayName => "IDP",
samlOrganizationName => "IDP",
samlOrganizationURL => "http://www.idp.com/",
samlServicePrivateKeyEnc => saml_key_idp_private_enc,
samlServicePrivateKeySig => saml_key_idp_private_sig,
samlServicePublicKeyEnc => saml_key_idp_public_enc,
samlServicePublicKeySig => saml_key_idp_public_sig,
samlSPMetaDataXML => {
"sp.com" => {
samlSPMetaDataXML =>
samlSPMetaDataXML( 'sp', 'HTTP-POST' )
},
},
customPlugins => 't::SamlHookPlugin',
}
}
);
}
sub sp {
return LLNG::Manager::Test->new( {
ini => {
logLevel => $debug,
domain => 'sp.com',
portal => 'http://auth.sp.com',
authentication => 'SAML',
userDB => 'Same',
issuerDBSAMLActivation => 0,
restSessionServer => 1,
samlIDPMetaDataExportedAttributes => {
idp => {
mail => "0;mail;;",
uid => "1;uid",
cn => "0;cn"
}
},
samlIDPMetaDataOptions => {
idp => {
samlIDPMetaDataOptionsEncryptionMode => 'none',
samlIDPMetaDataOptionsSSOBinding => 'post',
samlIDPMetaDataOptionsSLOBinding => 'post',
samlIDPMetaDataOptionsSignSSOMessage => 1,
samlIDPMetaDataOptionsSignSLOMessage => 1,
samlIDPMetaDataOptionsCheckSSOMessageSignature => 1,
samlIDPMetaDataOptionsCheckSLOMessageSignature => 1,
samlIDPMetaDataOptionsForceUTF8 => 1,
}
},
samlIDPMetaDataExportedAttributes => {
idp => {
"uid" => "0;uid;;",
"cn" => "1;cn;;",
},
},
samlIDPMetaDataXML => {
idp => {
samlIDPMetaDataXML =>
samlIDPMetaDataXML( 'idp', 'HTTP-POST' )
}
},
samlOrganizationDisplayName => "SP",
samlOrganizationName => "SP",
samlOrganizationURL => "http://www.sp.com",
samlServicePublicKeySig => saml_key_sp_public_sig,
samlServicePrivateKeyEnc => saml_key_sp_private_enc,
samlServicePrivateKeySig => saml_key_sp_private_sig,
samlServicePublicKeyEnc => saml_key_sp_public_enc,
samlSPSSODescriptorAuthnRequestsSigned => 1,
},
}
);
}

View File

@ -0,0 +1,175 @@
use lib 'inc';
use Test::More;
use strict;
use IO::String;
use LWP::UserAgent;
use LWP::Protocol::PSGI;
use MIME::Base64;
use JSON;
BEGIN {
require 't/test-lib.pm';
require 't/oidc-lib.pm';
}
my $debug = 'error';
# Initialization
my $op = LLNG::Manager::Test->new( {
ini => {
logLevel => $debug,
domain => 'idp.com',
portal => 'http://auth.op.com',
authentication => 'Demo',
userDB => 'Same',
issuerDBOpenIDConnectActivation => 1,
issuerDBOpenIDConnectRule => '$uid eq "french"',
oidcRPMetaDataExportedVars => {
rp => {
email => "mail",
family_name => "cn",
name => "cn"
},
rp2 => {
email => "mail",
family_name => "cn",
name => "cn"
}
},
oidcServiceMetaDataAuthorizeURI => "authorize",
oidcServiceMetaDataCheckSessionURI => "checksession.html",
oidcServiceMetaDataJWKSURI => "jwks",
oidcServiceMetaDataEndSessionURI => "logout",
oidcServiceMetaDataRegistrationURI => "register",
oidcServiceMetaDataTokenURI => "token",
oidcServiceMetaDataUserInfoURI => "userinfo",
oidcServiceAllowHybridFlow => 1,
oidcServiceAllowImplicitFlow => 1,
oidcServiceAllowDynamicRegistration => 1,
oidcServiceAllowAuthorizationCodeFlow => 1,
oidcRPMetaDataOptions => {
rp => {
oidcRPMetaDataOptionsDisplayName => "RP",
oidcRPMetaDataOptionsIDTokenExpiration => 3600,
oidcRPMetaDataOptionsClientID => "rpid",
oidcRPMetaDataOptionsIDTokenSignAlg => "HS512",
oidcRPMetaDataOptionsClientSecret => "rpsecret",
oidcRPMetaDataOptionsUserIDAttr => "",
oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
oidcRPMetaDataOptionsBypassConsent => 1,
},
oauth => {
oidcRPMetaDataOptionsDisplayName => "oauth",
oidcRPMetaDataOptionsClientID => "oauth",
oidcRPMetaDataOptionsClientSecret => "service",
oidcRPMetaDataOptionsUserIDAttr => "",
}
},
oidcOPMetaDataOptions => {},
oidcOPMetaDataJSON => {},
oidcOPMetaDataJWKS => {},
oidcServiceMetaDataAuthnContext => {
'loa-4' => 4,
'loa-1' => 1,
'loa-5' => 5,
'loa-2' => 2,
'loa-3' => 3
},
oidcServicePrivateKeySig => oidc_key_op_private_sig,
oidcServicePublicKeySig => oidc_key_op_public_sig,
customPlugins => 't::OidcHookPlugin',
}
}
);
my $res;
# Authenticate to LLNG
my $url = "/";
my $query = "user=french&password=french";
ok(
$res = $op->_post(
"/",
IO::String->new($query),
accept => 'text/html',
length => length($query),
),
"Post authentication"
);
my $idpId = expectCookie($res);
# Get code for RP1
$query =
"response_type=code&scope=openid%20profile%20email&client_id=rpid&state=af0ifjsldkj&redirect_uri=http%3A%2F%2Frp2.com%2F";
ok(
$res = $op->_get(
"/oauth2/authorize",
query => "$query",
accept => 'text/html',
cookie => "lemonldap=$idpId",
),
"Get authorization code"
);
my ($code) = expectRedirection( $res, qr#http://rp2\.com/.*code=([^\&]*)# );
# Exchange code for AT
$query =
"grant_type=authorization_code&code=$code&redirect_uri=http%3A%2F%2Frp2.com%2F";
ok(
$res = $op->_post(
"/oauth2/token",
IO::String->new($query),
accept => 'text/html',
length => length($query),
custom => {
HTTP_AUTHORIZATION => "Basic " . encode_base64("rpid:rpsecret"),
},
),
"Post token"
);
my $json = from_json( $res->[2]->[0] );
my $token = $json->{access_token};
ok( $token, 'Access token present' );
my $id_token = $json->{id_token};
ok( $id_token, 'ID token present' );
my $id_token_payload = id_token_payload($id_token);
is ($id_token_payload->{id_token_hook}, 1, "Found hooked claim in ID token");
# Get userinfo
$res = $op->_post(
"/oauth2/userinfo",
IO::String->new(''),
accept => 'application/json',
length => 0,
custom => {
HTTP_AUTHORIZATION => "Bearer " . $token,
},
);
$json = expectJSON($res);
is ($json->{userinfo_hook}, 1, "Found hooked claim in Userinfo token");
# Introspect to find scopes
$query = "token=$token";
ok(
$res = $op->_post(
"/oauth2/introspect",
IO::String->new($query),
accept => 'text/html',
length => length $query,
custom => {
HTTP_AUTHORIZATION => "Basic " . encode_base64("oauth:service"),
},
),
"Post introspection"
);
expectOK($res);
$json = from_json( $res->[2]->[0] );
like($json->{scope}, qr/\bmy_hooked_scope\b/, "Found hook defined scope");
clean_sessions();
done_testing();

View File

@ -0,0 +1,41 @@
package t::OidcHookPlugin;
use Mouse;
extends 'Lemonldap::NG::Portal::Main::Plugin';
use Lemonldap::NG::Portal::Main::Constants qw(PE_OK);
use Data::Dumper;
use Test::More;
use constant hook => {
oidcGenerateIDToken => 'addClaimToIDToken',
oidcGenerateUserInfoResponse => 'addClaimToUserInfo',
oidcGotRequest => 'addScopeToRequest',
};
sub init {
my ($self) = @_;
return 1;
}
sub addClaimToIDToken {
my ( $self, $req, $payload, $rp ) = @_;
$payload->{"id_token_hook"} = 1;
return PE_OK;
}
sub addClaimToUserInfo {
my ( $self, $req, $userinfo ) = @_;
$userinfo->{"userinfo_hook"} = 1;
return PE_OK;
}
sub addScopeToRequest {
my ( $self, $req, $oidc_request ) = @_;
$oidc_request->{scope} = $oidc_request->{scope} . " my_hooked_scope";
return PE_OK;
}
1;

View File

@ -0,0 +1,21 @@
package t::SamlHookPlugin;
use Mouse;
extends 'Lemonldap::NG::Portal::Main::Plugin';
use constant hook => { samlGotAuthnRequest => 'gotRequest', };
sub init {
my ($self) = @_;
return 1;
}
sub gotRequest {
my ( $self, $res, $login ) = @_;
# Return a weird
return -999;
}
1;

1
rpm/lemonldap-ng.fc Normal file
View File

@ -0,0 +1 @@
/var/cache/lemonldap-ng(/.*)? system_u:object_r:httpd_cache_t:s0

View File

@ -23,6 +23,11 @@
%global lm_dnsdomain example.com
# SELinux
%global with_selinux 1
%global modulename lemonldap-ng
%global selinuxtype targeted
#global pre_release beta1
#==============================================================================
@ -194,6 +199,14 @@ Requires: lemonldap-ng-manager = %{version}-%{release}
Requires: lemonldap-ng-portal = %{version}-%{release}
Requires: lemonldap-ng-test = %{version}-%{release}
%if 0%{?with_selinux} && 0%{?fedora}%{?el8}
# ! Not available in Centos7, you need to install lemonldap-ng-selinux manually
# This ensures that the *-selinux package and all its dependencies are not pulled
# into containers and other systems that do not use SELinux
Requires: (%{name}-selinux if selinux-policy-%{selinuxtype})
%endif
# Setup requires filtering
%{?perl_default_filter}
%{?el7:%global __requires_exclude perl\\(Lasso|perl\\(Web::ID|perl\\(Sentry::Raven}
@ -355,6 +368,22 @@ Summary: LemonLDAP-NG Portal Modules
%description -n perl-Lemonldap-NG-Portal
This package installs the authentication portal.
#==============================================================================
# SELinux policy package
#==============================================================================
%if 0%{?with_selinux}
%package selinux
Summary: LemonLDAP-NG SELinux policy
BuildArch: noarch
Requires: selinux-policy-%{selinuxtype}
Requires(post): selinux-policy-%{selinuxtype}
BuildRequires: selinux-policy-devel
%{?selinux_requires}
%description selinux
Custom SELinux policy module
%endif
#==============================================================================
# Source preparation
#==============================================================================
@ -373,6 +402,17 @@ make %{?_smp_mflags} configure \
PERLOPTIONS="INSTALLDIRS=vendor"
make %{?_smp_mflags}
%if 0%{?with_selinux}
# SELinux policy (originally from selinux-policy-contrib)
# this policy module will override the production module
mkdir selinux
cp -p rpm/lemonldap-ng.fc selinux/
cp -p rpm/lemonldap-ng.te selinux/
make -f %{_datadir}/selinux/devel/Makefile %{modulename}.pp
bzip2 -9 %{modulename}.pp
%endif
#==============================================================================
# Installation
#============================================================================
@ -500,6 +540,11 @@ sed -i -e '1i#!/usr/bin/plackup' \
%{buildroot}/usr/share/lemonldap-ng/examples/llngapp.psgi
chmod 644 %{buildroot}/usr/share/lemonldap-ng/test/cas.php
# Install SELinux policy
%if 0%{?with_selinux}
install -D -m 0644 %{modulename}.pp.bz2 %{buildroot}%{_datadir}/selinux/packages/%{selinuxtype}/%{modulename}.pp.bz2
%endif
#==============================================================================
# Run test
#==============================================================================
@ -553,6 +598,25 @@ fi
%postun fastcgi-server
%systemd_postun_with_restart llng-fastcgi-server.service
%if 0%{?with_selinux}
# SELinux contexts are saved so that only affected files can be
# relabeled after the policy module installation
%pre selinux
%selinux_relabel_pre -s %{selinuxtype}
%post selinux
%selinux_modules_install -s %{selinuxtype} %{_datadir}/selinux/packages/%{selinuxtype}/%{modulename}.pp.bz2
%postun selinux
if [ $1 -eq 0 ]; then
%selinux_modules_uninstall -s %{selinuxtype} %{modulename}
fi
%posttrans selinux
%selinux_relabel_post -s %{selinuxtype}
# if with_selinux
%endif
%files
%files conf
@ -670,6 +734,12 @@ fi
%{perl_vendorlib}/Lemonldap/NG/Portal.pm
%{perl_vendorlib}/Lemonldap/NG/Portal/
%if 0%{?with_selinux}
%files selinux
%{_datadir}/selinux/packages/%{selinuxtype}/%{modulename}.pp.*
%ghost %{_sharedstatedir}/selinux/%{selinuxtype}/active/modules/200/%{modulename}
%endif
#==============================================================================
# Changelog
#==============================================================================

1
rpm/lemonldap-ng.te Normal file
View File

@ -0,0 +1 @@
policy_module(lemonldap-ng,1.0)