Merge branch 'v2.0' into findUser
This commit is contained in:
commit
bfcdd370df
1
debian/control
vendored
1
debian/control
vendored
|
@ -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
|
||||
|
|
|
@ -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.*;
|
||||
|
|
|
@ -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
181
doc/sources/admin/hooks.rst
Normal 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
|
||||
}
|
|
@ -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:
|
||||
|
||||
::
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 !
|
||||
|
|
|
@ -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
|
||||
------------------------
|
||||
|
|
|
@ -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(/.*)?'
|
||||
|
|
|
@ -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
|
||||
-----
|
||||
|
|
|
@ -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,...).
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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} >=
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" )
|
||||
|
|
|
@ -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 ('
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;">
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
185
lemonldap-ng-portal/t/30-Auth-and-issuer-SAML-POST-Hook.t
Normal file
185
lemonldap-ng-portal/t/30-Auth-and-issuer-SAML-POST-Hook.t
Normal 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,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
175
lemonldap-ng-portal/t/32-OIDC-Hooks.t
Normal file
175
lemonldap-ng-portal/t/32-OIDC-Hooks.t
Normal 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();
|
||||
|
41
lemonldap-ng-portal/t/OidcHookPlugin.pm
Normal file
41
lemonldap-ng-portal/t/OidcHookPlugin.pm
Normal 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;
|
||||
|
21
lemonldap-ng-portal/t/SamlHookPlugin.pm
Normal file
21
lemonldap-ng-portal/t/SamlHookPlugin.pm
Normal 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
1
rpm/lemonldap-ng.fc
Normal file
|
@ -0,0 +1 @@
|
|||
/var/cache/lemonldap-ng(/.*)? system_u:object_r:httpd_cache_t:s0
|
|
@ -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 it’s 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
1
rpm/lemonldap-ng.te
Normal file
|
@ -0,0 +1 @@
|
|||
policy_module(lemonldap-ng,1.0)
|
Loading…
Reference in New Issue
Block a user