抜粋 | |||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Shibboleth IdP 3.3より導入されたMultiFactor認証フロー(MFA)の認証設定についてのドキュメントです。本ドキュメントはSAML 2.0で認証の切り替えを行うことを目的としており、SAML1は対象外です(LevelXを用いた認証要求はできません)。
MultiFactor認証フローは、シンプルないし複雑な認証シーケンスを作るために複数の認証フローを組み合わせるスクリプト記述可能な方法を提供します。 モジュールの有効化 (Enabling Module)MultiFactor認証フローは、「IdPモジュール」と呼ばれる構成管理補助機能で管理され、MultiFactor認証フローのIdPモジュールは「idp.auth.MFA」となります。 Shibboleth IdP V5を新規インストールした場合、デフォルトで「idp.auth.MFA」は無効となっているため、有効化します。
MultiFactor認証フローの設定は、 また、
Shibboleth IdP V5では初期状態ではJavaScriptが実行できないため、MultiFactor認証フロー内でスクリプトを定義する場合は必要に応じてJavaScriptエンジンをプラグインとしてインストールします。本ページの記述例がNashornですのでそれに合わせたインストールコマンドを示しています。すでにRhinoをインストールしている場合は重複してインストールせず、JavaScriptの記述を修正してご利用ください。
直接的なフロー選択 (Directly Selecting Flows)もっと簡単なルールは、最初の認証フローが成功した場合に、次に実行する認証フローを指定します。 下記の例は、最初にPassword認証フローによる認証を行い、Password認証フローの認証が成功した場合にX509認証フローの認証が行われます。X509認証フローの認証が成功すると認証成功となります。
プログラムでのフロー選択 (Programmatically Selecting Flows)より複雑なルールを実現するには、Script、Spring Expression、もしくはJavaで記述した関数を実行します。 下記の例は、以下の認証シーケンスを実現しています。
なお、
SPのセキュリティレベルに応じたフロー選択 (Selecting Flows By SP Security Level)プログラムでのフロー選択では、SPが要求する認証コンテクストに従って認証方式を制御することが可能です。 下記の例では認証フローの階層化と同様の動作をMFAフローを使用して実現します。 本ドキュメントで使用する認証コンテクストと認証フローの関係を下記に示します。
| |||||||||||||||||||||||||||||||||||||||||||||
認証コンテクスト | 略称 | 認証フロー | |||||||||||||||||||||||||||||||||||||||||||
urn:mace:gakunin.jp:idprivacy:ac:classes:Level1 | Level1 | Password | |||||||||||||||||||||||||||||||||||||||||||
urn:mace:gakunin.jp:idprivacy:ac:classes:Level2 | Level2 | RemoteUser | |||||||||||||||||||||||||||||||||||||||||||
urn:mace:gakunin.jp:idprivacy:ac:classes:Level3 | Level3 | X509 |
コード ブロック | ||||||
---|---|---|---|---|---|---|
| ||||||
# Default settings for most authentication methods.
#idp.authn.defaultLifetime = PT1H
@@ -81,10 +82,11 @@
# Unset if using customized Principals per validator
#idp.authn.Password.addDefaultPrincipals = true
# The Principal collection below is the typical default if not otherwise noted.
-#idp.authn.Password.supportedPrincipals = \
-# saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport, \
-# saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:Password, \
-# saml1/urn:oasis:names:tc:SAML:1.0:am:password
+idp.authn.Password.supportedPrincipals = \
+ saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport, \
+ saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:Password, \
+ saml2/urn:mace:gakunin.jp:idprivacy:ac:classes:Level1, \
+ saml1/urn:oasis:names:tc:SAML:1.0:am:password
# Validators are controlled in password-authn-config.xml
#### Password Backends ####
@@ -126,6 +128,12 @@
#idp.authn.RemoteUser.subjectAttribute =
#idp.authn.RemoteUser.authnMethodHeader =
#idp.authn.RemoteUser.authnAuthorityHeader =
+idp.authn.RemoteUser.supportedPrincipals = \
+ saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport, \
+ saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:Password, \
+ saml2/urn:mace:gakunin.jp:idprivacy:ac:classes:Level2, \
+ saml2/urn:mace:gakunin.jp:idprivacy:ac:classes:Level1, \
+ saml1/urn:oasis:names:tc:SAML:1.0:am:password
#### RemoteUserInternal ####
@@ -166,6 +174,9 @@
idp.authn.X509.supportedPrincipals = \
saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:X509, \
saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:TLSClient, \
+ saml2/urn:mace:gakunin.jp:idprivacy:ac:classes:Level3, \
+ saml2/urn:mace:gakunin.jp:idprivacy:ac:classes:Level2, \
+ saml2/urn:mace:gakunin.jp:idprivacy:ac:classes:Level1, \
saml1/urn:ietf:rfc:2246
#### X509Internal ####
@@ -232,5 +243,8 @@
saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocol, \
saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport, \
saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:Password, \
+ saml2/urn:mace:gakunin.jp:idprivacy:ac:classes:Level3, \
+ saml2/urn:mace:gakunin.jp:idprivacy:ac:classes:Level2, \
+ saml2/urn:mace:gakunin.jp:idprivacy:ac:classes:Level1, \
saml1/urn:oasis:names:tc:SAML:1.0:am:password
# Most actual setup via mfa-authn-config.xml |
conf/relying-party.xml
のshibboleth.DefaultRelyingParty
内のSAML2.SSO
にdefaultAuthenticationMethods
プロパティを設定します。
コード ブロック | ||||||
---|---|---|---|---|---|---|
| ||||||
<bean id="shibboleth.DefaultRelyingParty" parent="RelyingParty">
<property name="profileConfigurations">
<list>
<!-- SAML 1.1 and SAML 2.0 AttributeQuery are disabled by default. -->
<!--
<ref bean="Shibboleth.SSO" />
<ref bean="SAML1.AttributeQuery" />
<ref bean="SAML1.ArtifactResolution" />
-->
- <bean parent="SAML2.SSO" p:postAuthenticationFlows="attribute-release" />
+ <bean parent="SAML2.SSO" p:postAuthenticationFlows="attribute-release">
+ <property name="defaultAuthenticationMethods">
+ <list>
+ <bean parent="shibboleth.SAML2AuthnContextClassRef"
+ c:classRef="urn:mace:gakunin.jp:idprivacy:ac:classes:Level1" />
+ </list>
+ </property>
+ </bean>
<ref bean="SAML2.ECP" />
<ref bean="SAML2.Logout" />
<!--
<ref bean="SAML2.AttributeQuery" />
-->
<ref bean="SAML2.ArtifactResolution" />
</list>
</property>
</bean> |
必要であればRelyingPartyOverridesのほうのSAML2.SSOにも同様の設定を追加してください。
conf/authn/mfa-authn-config.xmlに
認証設定を行います。コード ブロック | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
<util:map id="shibboleth.authn.MFA.TransitionMap">
<entry key="">
<bean parent="shibboleth.authn.MFA.Transition" p:nextFlowStrategy-ref="checkAuthnFactor" />
</entry>
</util:map>
<bean id="checkAuthnFactor" parent="shibboleth.ContextFunctions.Scripted" factory-method="inlineScript">
<constructor-arg>
<value>
<![CDATA[
nextFlow = "authn/Password";
authnContextClassPrincipal = Java.type("net.shibboleth.idp.saml.authn.principal.AuthnContextClassRefPrincipal");
level2 = new authnContextClassPrincipal("urn:mace:gakunin.jp:idprivacy:ac:classes:Level2");
level3 = new authnContextClassPrincipal("urn:mace:gakunin.jp:idprivacy:ac:classes:Level3");
rpid = input.getSubcontext("net.shibboleth.idp.profile.context.RelyingPartyContext").relyingPartyId;
authCtx = input.getSubcontext("net.shibboleth.idp.authn.context.AuthenticationContext");
rprinCtx = authCtx.getSubcontext("net.shibboleth.idp.authn.context.RequestedPrincipalContext");
rpLevel = rprinCtx.getRequestedPrincipals();
if(rpLevel.contains(level2)) {
nextFlow = "authn/RemoteUser";
} else if(rpLevel.contains(level3)) {
nextFlow = "authn/X509";
}
nextFlow;
]]>
</value>
</constructor-arg>
</bean> |
挙動
挙動の説明で使用するSPについて下記に示します。
なし
urn:mace:gakunin.jp:idprivacy:ac:classes:Level1
urn:mace:gakunin.jp:idprivacy:ac:classes:Level2
urn:mace:gakunin.jp:idprivacy:ac:classes:Level3
未認証の場合
- SPaもしくはSPbからIdPにリダイレクトされると、
Level1
のPassword認証フローのログインページが表示されます。 - SPcからIdPにリダイレクトされると、
Level2
のRemoteUser認証フローのためのログインページやダイアログが表示されます。 - SPdからIdPにリダイレクトされると、
Level3
のX509認証フローのログインページが表示されます。
Level1
が認証済みの場合
- SPaもしくはSPbからIdPにリダイレクトされると、
Level1
のPassword認証フローが認証済みのためユーザ同意画面もしくは認証後のSPの画面が表示されます。 - SPcからIdPにリダイレクトされると、
Level2
のRemoteUser認証フローのためのログインページやダイアログが表示されます。 - SPdからIdPにリダイレクトされると、
Level3
のX509認証フローのログインページが表示されます。
Level2
が認証済みの場合
- SPaもしくはSPbからIdPにリダイレクトされると、
Level2
のRemoteUser認証フローが認証済みのためユーザ同意画面もしくは認証後のSPの画面が表示されます。 - SPcからIdPにリダイレクトされると、
Level2
のRemoteUser認証フローが認証済みのためユーザ同意画面もしくはSPの画面が表示されます。 - SPdからIdPにリダイレクトされると、
Level3
のX509認証フローのログインページが表示されます。
Level3
が認証済みの場合
- SPaもしくはSPbからIdPにリダイレクトされると、
Level3
のX509認証フローが認証済みのためユーザ同意画面もしくは認証後のSPの画面が表示されます。 - SPcからIdPにリダイレクトされると、
Level3
のX509認証フローが認証済みのためユーザ同意画面もしくは認証後のSPの画面が表示されます。 - SPdからIdPにリダイレクトされると、
Level3
のX509認証フローが認証済みのためユーザ同意画面もしくは認証後のSPの画面が表示されます。
ユーザによる認証方式が選択できる設定 (Selecting Flows By Login User)
Spring WebFlowイベントに基づいた遷移の制御を使用し、SPのセキュリティレベルに応じたフロー選択にログイン時に対象SPが使用できる認証方式をユーザが選択してログインする動作を追加します。
設定
SPのセキュリティレベルに応じたフロー選択の設定を行ってください。
conf/authn/mfa-authn-config.xml
で各Levelに応じた認証設定を行います。コード ブロック | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
<util:map id="shibboleth.authn.MFA.TransitionMap">
<!-- First rule calls a flow to display a view to select a method to run. -->
<entry key="">
<bean parent="shibboleth.authn.MFA.Transition" p:nextFlow="methodChooser" />
</entry>
<!-- Second rule decides what to call based on event signaled by the view. -->
<entry key="methodChooser">
<bean parent="shibboleth.authn.MFA.Transition">
<property name="nextFlowStrategyMap">
<map>
<!-- Maps event to a flow -->
<entry key="ChoosePassword" value="authn/Password" />
<entry key="ChooseRemoteUser" value="authn/RemoteUser" />
<entry key="ChooseX509" value="authn/X509" />
</map>
</property>
</bean>
</entry>
<!-- An implicit final rule will return whatever the final flow returns. -->
</util:map> |
flows/methodChooser/
ディレクトリを作成します。コード ブロック | ||
---|---|---|
| ||
$ mkdir /opt/shibboleth-idp/flows/methodChooser |
flows/methodChooser/methodChooser-flow.xml
ファイルを作成して以下のように設定します。
コード ブロック | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd">
<!-- This is chooser of login flows. -->
<view-state id="DisplayChooserWebView" view="chooser">
<on-render>
<evaluate expression="environment" result="viewScope.environment" />
<evaluate expression="opensamlProfileRequestContext" result="viewScope.profileRequestContext" />
<evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext))" result="viewScope.authenticationContext" />
<evaluate expression="authenticationContext.getAvailableFlows().values().?[id matches 'authn/(Password|RemoteUser|X509)']" result="viewScope.availableAuthenticationFlows" />
<evaluate expression="authenticationContext.getSubcontext(T(net.shibboleth.idp.ui.context.RelyingPartyUIContext))" result="viewScope.rpUIContext" />
<evaluate expression="T(net.shibboleth.utilities.java.support.codec.HTMLEncoder)" result="viewScope.encoder" />
<evaluate expression="flowRequestContext.getExternalContext().getNativeRequest()" result="viewScope.request" />
<evaluate expression="flowRequestContext.getExternalContext().getNativeResponse()" result="viewScope.response" />
<evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().containsBean('shibboleth.CustomViewContext') ? flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.CustomViewContext') : null" result="viewScope.custom" />
</on-render>
<transition on="ChoosePassword" to="ChoosePassword" />
<transition on="ChooseRemoteUser" to="ChooseRemoteUser" />
<transition on="ChooseX509" to="ChooseX509" />
</view-state>
<end-state id="ChoosePassword" />
<end-state id="ChooseRemoteUser" />
<end-state id="ChooseX509" />
</flow> |
views/chooser.vm
ファイルを作成して以下のように設定します。コード ブロック | ||||||
---|---|---|---|---|---|---|
| ||||||
##
## Velocity Template for DisplayUsernamePasswordPage view-state
##
## Velocity context will contain the following properties
## flowExecutionUrl - the form action location
## flowRequestContext - the Spring Web Flow RequestContext
## flowExecutionKey - the SWF execution key (this is built into the flowExecutionUrl)
## profileRequestContext - root of context tree
## authenticationContext - context with authentication request information
## authenticationErrorContext - context with login error state
## authenticationWarningContext - context with login warning state
## ldapResponseContext - context with LDAP state (if using native LDAP)
## username - username from previous rendering of form
## errorMessageFunction - function to produce error message for form
## rpContext - the context with information about the relying party (SP)
## rpUIContext - the context with SP UI information from the metadata
## encoder - HTMLEncoder class
## cspDigester - Calculates base64-encoded SHA-2 hashes (call apply)
## cspNonce - Calculates secure nonces (call generateIdentifier)
## request - HttpServletRequest
## response - HttpServletResponse
## environment - Spring Environment object for property resolution
## custom - arbitrary object injected by deployer
##
#set ($rpContext = $profileRequestContext.getSubcontext('net.shibboleth.profile.context.RelyingPartyContext'))
##
<!DOCTYPE html>
<html>
<head>
<title>#springMessageText("idp.title", "Web Login Service")</title>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0">
<link rel="stylesheet" type="text/css" href="$request.getContextPath()#springMessageText("idp.css", "/css/placeholder.css")">
</head>
<body>
<main class="main">
<header>
<img class="main-logo" src="$request.getContextPath()#springMessageText("idp.logo", "/images/placeholder-logo.png")" alt="#springMessageText("idp.logo.alt-text", "logo")" />
#set ($serviceName = $rpUIContext.serviceName)
#if ($serviceName && !$rpContext.getRelyingPartyId().contains($serviceName))
<h1>#springMessageText("idp.login.loginTo", "Login to") $encoder.encodeForHTML($serviceName)</h1>
#end
</header>
<section>
<form action="$flowExecutionUrl" method="post">
#parse("csrf/csrf.vm")
#*
//
// SP Description & Logo (optional)
// These idpui lines will display added information (if available
// in the metadata) about the Service Provider (SP) that requested
// authentication. These idpui lines are "active" in this example
// (not commented out) - this extra SP info will be displayed.
// Remove or comment out these lines to stop the display of the
// added SP information.
//
*#
#set ($logo = $rpUIContext.getLogo())
#if ($logo)
<img class="service-logo" src= "$encoder.encodeForHTMLAttribute($logo)" alt="$encoder.encodeForHTMLAttribute($serviceName)">
#end
#set ($desc = $rpUIContext.getServiceDescription())
#if ($desc)
<p>$encoder.encodeForHTML($desc)</p>
#end
#set ($errorMessage = $errorMessageFunction.apply($profileRequestContext))
#if ($errorMessage)
<p class="output-message output--error">$encoder.encodeForHTML($errorMessage)</p>
#end
<p><big>#springMessageText("idp.login.chooser", "Select Authentication.")</big></p>
<div class="grid">
<div class="grid-item">
#foreach ($authnFlow in $availableAuthenticationFlows)
#if ($authenticationContext.isAcceptable($authnFlow) and $authnFlow.test(profileRequestContext))
<button type="submit" name="_eventId_Choose$authnFlow.getId().replace('authn/','')">
#springMessageText("idp.login.$authnFlow.getId().replace('authn/','')", $authnFlow.getId().replace('authn/',''))
</button>
</div>
#end
#end
</div>
</div>
</form>
<ul>
<li><a href="#springMessageText("idp.url.helpdesk", '#')">#springMessageText("idp.login.needHelp", "Need Help?")</a></li>
</ul>
</section>
</main>
<footer class="footer">
<div class="cc">
<p>#springMessageText("idp.footer", "Insert your footer text here.")</p>
</div>
</footer>
</body>
</html> |
挙動
SPからIdPにリダイレクトされた時に最初に表示されるページで認証フローを選択します。SP毎の選択できる認証フローを以下に示します。
パスワード
RemoteUser
X509
パスワード
RemoteUser
X509
RemoteUser
X509
遷移の完全なコントロール (Full Control Over Transitions)
最も複雑なルールを実現するために、Spring WebFlowイベントに基づいて完全に遷移を制御できます。
下記の例ではShibboleth IdPバージョン2向けにNIIと金沢大学で共同開発したGUARDプラグインと同様な認証シーケンスを実現します。GUARDプラグインについては、2015年2月に金沢大学の松平様が発表された『大学統合認証基盤における多要素認証について』の12ページをご参照ください。下記の例での認証コンテクストと認証フローの関係を以下に示します。
認証コンテクスト | 略称 | 認証フロー |
---|---|---|
urn:mace:gakunin.jp:idprivacy:ac:classes:Level1 | Level1 | パスワード |
urn:mace:gakunin.jp:idprivacy:ac:classes:Level2 | Level2 | (機関内) パスワード OR RemoteUser OR X509 (機関外) RemoteUser OR X509 |
urn:mace:gakunin.jp:idprivacy:ac:classes:Level3 | Level3 | RemoteUser AND X509 |
制限事項
- Level1のパスワード認証後に、Level2のパスワード認証はSSOされません。
- Level2のRemoteUser認証後に、Level3のRemoteUser認証はSSOされません。(ただし、BASIC認証に関して言えばブラウザが自動的にユーザー名とパスワードを送信するためエンドユーザーがユーザー名やパスワードを再度入力する必要はありません)
設定
conf/authn/mfa-authn-config.xml
で各Levelに応じた認証設定を行います。コード ブロック language xml title conf/authn/mfa-authn-config.xml linenumbers true collapse true <util:map id="shibboleth.authn.MFA.TransitionMap"> <!-- First rule calls a flow to display a view to select a method to run. --> <entry key=""> <bean parent="shibboleth.authn.MFA.Transition" p:nextFlow="methodChooser" /> </entry> <!-- Second rule decides what to call based on event signaled by the view. --> <entry key="custom/methodChooser"> <bean parent="shibboleth.authn.MFA.Transition"> <property name="nextFlowStrategyMap"> <map> <!-- Maps event to a flow --> <!-- Level1 --> <entry key="ChooseLevel1" value="authn/Level1" /> <!-- Level2 --> <entry key="ChoosePassword" value="authn/Password" /> <entry key="ChooseRemoteUser" value="authn/RemoteUser" /> <entry key="ChooseX509" value="authn/X509" /> <!-- Level3 --> <entry key="ChooseLevel3" value="authn/RemoteUser4Level3" /> </map> </property> </bean> </entry> <!-- Level3 --> <entry key="authn/RemoteUser4Level3"> <bean parent="shibboleth.authn.MFA.Transition" p:nextFlow="authn/X509" /> </entry> <!-- An implicit final rule will return whatever the final flow returns. --> </util:map>
Level2, Level3で異なる認証フローを使用したい場合は16行目から23行目、および31行目の
<entry>
を変更します。下記ファイルをダウンロードして配置します。
ファイル名 配置先ディレクトリ methodChooser-flow.xml flow/methodChooser/
chooser.vm views/
Level2, Level3で異なる認証フローを使用したい場合は、
methodChooser-flow.xml
の30行目のPassword|RemoteUser|X509 の部分と
、37行目から40行目の<transition>
、および47行目から49行目の<end-state>
を変更します。コード ブロック language xml firstline 25 title methodChooser-flow.xml linenumbers true collapse true <view-state id="DisplayChooserWebViewForLevel2" view="chooser"> <on-render> <evaluate expression="environment" result="viewScope.environment" /> <evaluate expression="opensamlProfileRequestContext" result="viewScope.profileRequestContext" /> <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext))" result="viewScope.authenticationContext" /> <evaluate expression="authenticationContext.getAvailableFlows().values().?[id matches 'authn/(Password|RemoteUser|X509)']" result="viewScope.availableAuthenticationFlows" /> <evaluate expression="authenticationContext.getSubcontext(T(net.shibboleth.idp.ui.context.RelyingPartyUIContext))" result="viewScope.rpUIContext" /> <evaluate expression="T(net.shibboleth.utilities.java.support.codec.HTMLEncoder)" result="viewScope.encoder" /> <evaluate expression="flowRequestContext.getExternalContext().getNativeRequest()" result="viewScope.request" /> <evaluate expression="flowRequestContext.getExternalContext().getNativeResponse()" result="viewScope.response" /> <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().containsBean('shibboleth.CustomViewContext') ? flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.CustomViewContext') : null" result="viewScope.custom" /> </on-render> <transition on="ChoosePassword" to="ChoosePassword" /> <transition on="ChooseRemoteUser" to="ChooseRemoteUser" /> <transition on="ChooseX509" to="ChooseX509" /> </view-state> <!-- Level1 --> <end-state id="ChooseLevel1" /> <!-- Level2 --> <end-state id="ChoosePassword" /> <end-state id="ChooseRemoteUser" /> <end-state id="ChooseX509" /> <!-- Level3 --> <end-state id="ChooseLevel3" />
Level1のパスワード認証を用意します。
書式設定済み # mkdir -p flows/authn/Level1 # cp system/flows/authn/password-authn-flow.xml Level1-flow.xml # cp system/flows/authn/password-authn-beans.xml Level1-beans.xml # sed -i 's/password-authn-beans.xml/Level1-beans.xml/' Level1-flow.xml
Level3のRemoteUser認証を用意します。
書式設定済み # mkdir -p flows/authn/RemoteUser4Level3 # cp system/flows/authn/remoteuser-authn-flow.xml flows/authn/RemoteUser4Level3/RemoteUser4Level3-flow.xml # cp system/flows/authn/remoteuser-authn-beans.xml flows/authn/RemoteUser4Level3/RemoteUser4Level3-beans.xml # sed -i 's/remoteuser-authn-beans.xml/RemoteUser4Level3-beans.xml/' flows/authn/RemoteUser4Level3/RemoteUser4Level3-flow.xml # sed -i 's/remoteuser-authn-config.xml/RemoteUser4Level3-config.xml/' flows/authn/RemoteUser4Level3/RemoteUser4Level3-beans.xml # cp conf/authn/remoteuser-authn-config.xml conf/authn/RemoteUser4Level3-config.xml # sed -i 's,Authn/RemoteUser,Authn/RemoteUser4Level3,' conf/authn/remoteuser-authn-config.xml # cp webapp/WEB-INF/web.xml edit-webapp/WEB-INF/web.xml
コード ブロック language diff title edit-webapp/WEB-INF/web.xml collapse true @@ -113,4 +113,15 @@ </servlet-mapping> + <!-- Servlet protected by container used for RemoteUser authentication (Level3) --> + <servlet> + <servlet-name>RemoteUser4Level3AuthHandler</servlet-name> + <servlet-class>net.shibboleth.idp.authn.impl.RemoteUserAuthServlet</servlet-class> + <load-on-startup>2</load-on-startup> + </servlet> + <servlet-mapping> + <servlet-name>RemoteUser4Level3AuthHandler</servlet-name> + <url-pattern>/Authn/RemoteUser4Level3</url-pattern> + </servlet-mapping> + <!-- Servlet protected by container used for X.509 authentication --> <servlet>
書式設定済み # bin/build.sh
conf/authn/general-authn.xml
の設定をします。コード ブロック language diff title conf/authn/general-authn.xml collapse true @@ -54,21 +54,89 @@ <bean id="authn/External" parent="shibboleth.AuthenticationFlow" p:nonBrowserSupported="false" /> + <bean id="authn/Level1" parent="shibboleth.AuthenticationFlow"> + <property name="supportedPrincipals"> + <list> + <bean parent="shibboleth.SAML2AuthnContextClassRef" + c:classRef="urn:mace:gakunin.jp:idprivacy:ac:classes:Level1" /> + </list> + </property> + </bean> + + <bean id="authn/Level2" parent="shibboleth.AuthenticationFlow"> + <property name="supportedPrincipals"> + <list> + <bean parent="shibboleth.SAML2AuthnContextClassRef" + c:classRef="urn:mace:gakunin.jp:idprivacy:ac:classes:Level2" /> + <bean parent="shibboleth.SAML2AuthnContextClassRef" + c:classRef="urn:mace:gakunin.jp:idprivacy:ac:classes:Level1" /> + </list> + </property> + </bean> + + <bean id="authn/Level3" parent="shibboleth.AuthenticationFlow"> + <property name="supportedPrincipals"> + <list> + <bean parent="shibboleth.SAML2AuthnContextClassRef" + c:classRef="urn:mace:gakunin.jp:idprivacy:ac:classes:Level3" /> + <bean parent="shibboleth.SAML2AuthnContextClassRef" + c:classRef="urn:mace:gakunin.jp:idprivacy:ac:classes:Level2" /> + <bean parent="shibboleth.SAML2AuthnContextClassRef" + c:classRef="urn:mace:gakunin.jp:idprivacy:ac:classes:Level1" /> + </list> + </property> + </bean> + + <bean id="authn/Password" parent="shibboleth.AuthenticationFlow" + p:passiveAuthenticationSupported="true" + p:forcedAuthenticationSupported="true"> + <property name="supportedPrincipals"> + <list> + <bean parent="shibboleth.SAML2AuthnContextClassRef" + c:classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" /> + <bean parent="shibboleth.SAML2AuthnContextClassRef" + c:classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:Password" /> + <bean parent="shibboleth.SAML1AuthenticationMethod" + c:method="urn:oasis:names:tc:SAML:1.0:am:password" /> + <!-- GUARD --> + <bean parent="shibboleth.SAML2AuthnContextClassRef" + c:classRef="urn:mace:gakunin.jp:idprivacy:ac:classes:Level2" /> + <bean parent="shibboleth.SAML2AuthnContextClassRef" + c:classRef="urn:mace:gakunin.jp:idprivacy:ac:classes:Level1" /> + </list> + </property> + </bean> + <bean id="authn/RemoteUser" parent="shibboleth.AuthenticationFlow" - p:nonBrowserSupported="false" /> + p:nonBrowserSupported="false"> + <property name="supportedPrincipals"> + <list> + <!-- GUARD --> + <bean parent="shibboleth.SAML2AuthnContextClassRef" + c:classRef="urn:mace:gakunin.jp:idprivacy:ac:classes:Level2" /> + <bean parent="shibboleth.SAML2AuthnContextClassRef" + c:classRef="urn:mace:gakunin.jp:idprivacy:ac:classes:Level1" /> + </list> + </property> + </bean> <bean id="authn/RemoteUserInternal" parent="shibboleth.AuthenticationFlow" /> <bean id="authn/X509" parent="shibboleth.AuthenticationFlow" p:nonBrowserSupported="false"> <property name="supportedPrincipals"> <list> <bean parent="shibboleth.SAML2AuthnContextClassRef" c:classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:X509" /> <bean parent="shibboleth.SAML2AuthnContextClassRef" c:classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:TLSClient" /> <bean parent="shibboleth.SAML1AuthenticationMethod" c:method="urn:ietf:rfc:2246" /> + <!-- GUARD --> + <bean parent="shibboleth.SAML2AuthnContextClassRef" + c:classRef="urn:mace:gakunin.jp:idprivacy:ac:classes:Level2" /> + <bean parent="shibboleth.SAML2AuthnContextClassRef" + c:classRef="urn:mace:gakunin.jp:idprivacy:ac:classes:Level1" /> </list> </property> </bean> @@ -89,3 +157,14 @@ - <bean id="authn/Password" parent="shibboleth.AuthenticationFlow" - p:passiveAuthenticationSupported="true" - p:forcedAuthenticationSupported="true" /> + <bean id="authn/RemoteUser4Level3" parent="shibboleth.AuthenticationFlow" + p:nonBrowserSupported="false"> + <property name="supportedPrincipals"> + <list> + <!-- GUARD --> + <bean parent="shibboleth.SAML2AuthnContextClassRef" + c:classRef="urn:mace:gakunin.jp:idprivacy:ac:classes:Level3" /> + <bean parent="shibboleth.SAML2AuthnContextClassRef" + c:classRef="urn:mace:gakunin.jp:idprivacy:ac:classes:Level2" /> + <bean parent="shibboleth.SAML2AuthnContextClassRef" + c:classRef="urn:mace:gakunin.jp:idprivacy:ac:classes:Level1" /> + </list> + </property> + </bean> @@ -112,22 +191,29 @@ <bean id="authn/MFA" parent="shibboleth.AuthenticationFlow" p:passiveAuthenticationSupported="true" p:forcedAuthenticationSupported="true"> <!-- The list below almost certainly requires changes, and should generally be the union of any of the separate factors you combine in your particular MFA flow rules. The example corresponds to the example in mfa-authn-config.xml that combines IPAddress with Password. --> <property name="supportedPrincipals"> <list> <bean parent="shibboleth.SAML2AuthnContextClassRef" c:classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocol" /> <bean parent="shibboleth.SAML2AuthnContextClassRef" c:classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" /> <bean parent="shibboleth.SAML2AuthnContextClassRef" c:classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:Password" /> <bean parent="shibboleth.SAML1AuthenticationMethod" c:method="urn:oasis:names:tc:SAML:1.0:am:password" /> + <!-- GUARD --> + <bean parent="shibboleth.SAML2AuthnContextClassRef" + c:classRef="urn:mace:gakunin.jp:idprivacy:ac:classes:Level1" /> + <bean parent="shibboleth.SAML2AuthnContextClassRef" + c:classRef="urn:mace:gakunin.jp:idprivacy:ac:classes:Level2" /> + <bean parent="shibboleth.SAML2AuthnContextClassRef" + c:classRef="urn:mace:gakunin.jp:idprivacy:ac:classes:Level3" /> </list> </property> </bean>
Level2のPassword認証フローに機関のIPアドレスレンジ(下記例では203.0.113.0/24)を設定します。
コード ブロック language diff title conf/authn/general-authn.xml collapse true @@ -90,3 +90,4 @@ <bean id="authn/Password" parent="shibboleth.AuthenticationFlow" + p:activationCondition-ref="authn.PasswordActivationCondition" p:passiveAuthenticationSupported="true" p:forcedAuthenticationSupported="true"> @@ -241,1 +242,9 @@ + + <!-- + Activation Condition + --> + <bean id="authn.PasswordActivationCondition" class="org.opensaml.profile.logic.IPRangePredicate" + p:httpServletRequest-ref="shibboleth.HttpServletRequest" + p:ranges="#{ '203.0.113.0/24' }" > + </bean> </beans>
参考
...