[java-identity-provider] branch master updated: IDP-1211 - Interceptor for controlled impersonation to services
Scott Cantor
cantor.2 at osu.edu
Fri Oct 6 20:32:03 EDT 2017
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch master
in repository java-identity-provider.
View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=2404526613cf5ac29e62e72bd9225cfef575bdeb
The following commit(s) were added to refs/heads/master by this push:
new 2404526 IDP-1211 - Interceptor for controlled impersonation to services
2404526 is described below
commit 2404526613cf5ac29e62e72bd9225cfef575bdeb
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Fri Oct 6 20:31:58 2017 -0400
IDP-1211 - Interceptor for controlled impersonation to services
https://issues.shibboleth.net/jira/browse/IDP-1211
---
idp-conf/src/main/resources/conf/errors.xml | 1 +
.../intercept/impersonate-intercept-config.xml | 26 ++++++
.../resources/conf/intercept/profile-intercept.xml | 2 +
.../main/resources/system/conf/webflow-config.xml | 1 +
.../system/flows/intercept/impersonate-beans.xml | 56 +++++++++++++
.../system/flows/intercept/impersonate-flow.xml | 66 +++++++++++++++
.../flows/intercept/intercept-abstract-flow.xml | 2 +
.../resources/system/messages/messages.properties | 10 +++
.../main/resources/views/intercept/impersonate.vm | 97 ++++++++++++++++++++++
.../net/shibboleth/idp/profile/IdPAuditFields.java | 3 +
10 files changed, 264 insertions(+)
diff --git a/idp-conf/src/main/resources/conf/errors.xml b/idp-conf/src/main/resources/conf/errors.xml
index 6a2d0a2..a9f4074 100644
--- a/idp-conf/src/main/resources/conf/errors.xml
+++ b/idp-conf/src/main/resources/conf/errors.xml
@@ -23,6 +23,7 @@
-->
<util:map id="shibboleth.LocalEventMap">
<entry key="ContextCheckDenied" value="true" />
+ <entry key="ImpersonationViolation" value="true" />
<entry key="AttributeReleaseRejected" value="true" />
<entry key="TermsRejected" value="true" />
<entry key="RuntimeException" value="false" />
diff --git a/idp-conf/src/main/resources/conf/intercept/impersonate-intercept-config.xml b/idp-conf/src/main/resources/conf/intercept/impersonate-intercept-config.xml
new file mode 100644
index 0000000..85af2c7
--- /dev/null
+++ b/idp-conf/src/main/resources/conf/intercept/impersonate-intercept-config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:p="http://www.springframework.org/schema/p"
+ xmlns:c="http://www.springframework.org/schema/c"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+
+ default-init-method="initialize"
+ default-destroy-method="destroy">
+
+ <!-- Function returns the list of principal names the user can impersonate, defaults to null. -->
+
+ <bean id="shibboleth.impersonate.PrincipalFunction" class="com.google.common.base.Functions" factory-method="constant">
+ <constructor-arg>
+ <list>
+ <value>foo</value>
+ <value>bar</value>
+ </list>
+ </constructor-arg>
+ </bean>
+
+</beans>
diff --git a/idp-conf/src/main/resources/conf/intercept/profile-intercept.xml b/idp-conf/src/main/resources/conf/intercept/profile-intercept.xml
index 4040a10..7b4c8aa 100644
--- a/idp-conf/src/main/resources/conf/intercept/profile-intercept.xml
+++ b/idp-conf/src/main/resources/conf/intercept/profile-intercept.xml
@@ -31,6 +31,8 @@
<bean id="intercept/terms-of-use" parent="shibboleth.consent.TermsOfUseFlow" />
<bean id="intercept/attribute-release" parent="shibboleth.consent.AttributeReleaseFlow" />
+
+ <bean id="intercept/impersonate" parent="shibboleth.InterceptFlow" />
</list>
</property>
</bean>
diff --git a/idp-conf/src/main/resources/system/conf/webflow-config.xml b/idp-conf/src/main/resources/system/conf/webflow-config.xml
index c6a72da..38a7ac5 100644
--- a/idp-conf/src/main/resources/system/conf/webflow-config.xml
+++ b/idp-conf/src/main/resources/system/conf/webflow-config.xml
@@ -116,6 +116,7 @@
<entry key="intercept/expiring-password" value="../system/flows/intercept/expiring-password-flow.xml" />
<entry key="intercept/terms-of-use" value="../system/flows/intercept/terms-of-use-flow.xml" />
<entry key="intercept/attribute-release" value="../system/flows/intercept/attribute-release-flow.xml" />
+ <entry key="intercept/impersonate" value="../system/flows/intercept/impersonate-flow.xml" />
<!-- Inbound security processing of SAML messages, implemented as intercept flows. -->
<entry key="security-policy.abstract" value="../system/flows/saml/security-abstract-flow.xml" />
diff --git a/idp-conf/src/main/resources/system/flows/intercept/impersonate-beans.xml b/idp-conf/src/main/resources/system/flows/intercept/impersonate-beans.xml
new file mode 100644
index 0000000..41b39b1
--- /dev/null
+++ b/idp-conf/src/main/resources/system/flows/intercept/impersonate-beans.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:c="http://www.springframework.org/schema/c"
+ xmlns:p="http://www.springframework.org/schema/p"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+
+ default-init-method="initialize"
+ default-destroy-method="destroy">
+
+ <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"
+ p:placeholderPrefix="%{" p:placeholderSuffix="}" />
+
+ <bean class="net.shibboleth.ext.spring.config.IdentifiableBeanPostProcessor" />
+ <bean class="net.shibboleth.idp.profile.impl.ProfileActionBeanPostProcessor" />
+
+ <import resource="../../../conf/intercept/impersonate-intercept-config.xml" />
+
+ <!-- Simplifies flow definition expressions. -->
+ <alias name="shibboleth.impersonate.PrincipalFunction" alias="PrincipalFunction"/>
+
+ <bean id="ResolveAttributes" class="net.shibboleth.idp.profile.impl.ResolveAttributes" scope="prototype"
+ c:resolverService-ref="shibboleth.AttributeResolverService"
+ p:maskFailures="%{idp.service.attribute.resolver.maskFailures:true}"
+ p:principalNameLookupStrategy-ref="FlowScopePrincipalLookup" />
+
+ <bean id="FilterAttributes" class="net.shibboleth.idp.profile.impl.FilterAttributes" scope="prototype"
+ c:filterService-ref="shibboleth.AttributeFilterService"
+ p:maskFailures="%{idp.service.attribute.filter.maskFailures:true}"
+ p:metadataResolver-ref="shibboleth.MetadataResolver"
+ p:principalNameLookupStrategy-ref="FlowScopePrincipalLookup" />
+
+ <bean id="FlowScopePrincipalLookup" parent="shibboleth.ContextFunctions.Expression"
+ c:outputType="#{T(java.lang.String)}"
+ c:expression="#input.getSubcontext(T(net.shibboleth.idp.profile.context.SpringRequestContext)).getRequestContext().getFlowScope().get('principalToSpoof')" />
+
+ <import resource="../../conf/audit-system.xml" />
+
+ <bean id="PopulateAuditContext" parent="shibboleth.AbstractPopulateAuditContext" scope="prototype">
+ <property name="fieldExtractors">
+ <map>
+ <entry>
+ <key>
+ <util:constant static-field="net.shibboleth.idp.profile.IdPAuditFields.IMPERSONATED_USERNAME"/>
+ </key>
+ <ref bean="FlowScopePrincipalLookup" />
+ </entry>
+ </map>
+ </property>
+ </bean>
+
+</beans>
diff --git a/idp-conf/src/main/resources/system/flows/intercept/impersonate-flow.xml b/idp-conf/src/main/resources/system/flows/intercept/impersonate-flow.xml
new file mode 100644
index 0000000..f9752cd
--- /dev/null
+++ b/idp-conf/src/main/resources/system/flows/intercept/impersonate-flow.xml
@@ -0,0 +1,66 @@
+ <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"
+ parent="intercept.abstract">
+
+ <!-- Rudimentary impediment to direct execution of subflow. -->
+ <input name="calledAsSubflow" type="boolean" required="true" />
+
+ <on-start>
+ <evaluate expression="PrincipalFunction.apply(opensamlProfileRequestContext)" result="flowScope.principalCollection" />
+ </on-start>
+
+ <decision-state id="CanImpersonate">
+ <if test="principalCollection != null and !principalCollection.isEmpty()"
+ then="ImpersonateView" else="proceed" />
+ </decision-state>
+
+ <view-state id="ImpersonateView" view="#{flowRequestContext.activeFlow.id}">
+ <on-render>
+ <evaluate expression="environment" result="viewScope.environment" />
+ <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="opensamlProfileRequestContext" result="viewScope.profileRequestContext" />
+ <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext))" result="viewScope.authenticationContext" />
+ <evaluate expression="authenticationContext.getSubcontext(T(net.shibboleth.idp.ui.context.RelyingPartyUIContext))" result="viewScope.rpUIContext" />
+ <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().containsBean('shibboleth.CustomViewContext') ? flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.CustomViewContext') : null" result="viewScope.custom" />
+ </on-render>
+
+ <transition on="proceed" to="CheckForImpersonation" />
+ </view-state>
+
+ <decision-state id="CheckForImpersonation">
+ <on-entry>
+ <evaluate expression="flowRequestContext.getExternalContext().getNativeRequest().getParameter('principal')" result="flowScope.principalToSpoof" />
+ </on-entry>
+ <if test="principalToSpoof == null or principalToSpoof.length() == 0"
+ then="proceed" else="CheckAllowed" />
+ </decision-state>
+
+ <decision-state id="CheckAllowed">
+ <if test="principalCollection.contains(principalToSpoof)"
+ then="DoImpersonate" else="ImpersonationViolation" />
+ </decision-state>
+
+ <action-state id="DoImpersonate">
+ <on-entry>
+ <!-- Clear existing attribute state. -->
+ <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.profile.context.RelyingPartyContext)).removeSubcontext(T(net.shibboleth.idp.attribute.context.AttributeContext))" />
+ </on-entry>
+
+ <evaluate expression="PopulateAuditContext" />
+ <evaluate expression="ResolveAttributes" />
+ <evaluate expression="FilterAttributes" />
+ <evaluate expression="'proceed'" />
+
+ <transition on="proceed" to="proceed" />
+ </action-state>
+
+ <end-state id="ImpersonationViolation" />
+
+ <end-state id="proceed" />
+
+ <bean-import resource="../../../system/flows/intercept/impersonate-beans.xml" />
+
+</flow>
diff --git a/idp-conf/src/main/resources/system/flows/intercept/intercept-abstract-flow.xml b/idp-conf/src/main/resources/system/flows/intercept/intercept-abstract-flow.xml
index e30a00a..eef4a1f 100644
--- a/idp-conf/src/main/resources/system/flows/intercept/intercept-abstract-flow.xml
+++ b/idp-conf/src/main/resources/system/flows/intercept/intercept-abstract-flow.xml
@@ -20,6 +20,7 @@
<end-state id="AttributeReleaseRejected" />
<end-state id="TermsRejected" />
<end-state id="ContextCheckDenied" />
+ <end-state id="ImpersonationViolation" />
<end-state id="RestartAuthentication" />
<end-state id="RuntimeException" />
@@ -37,6 +38,7 @@
<transition on="AttributeReleaseRejected" to="AttributeReleaseRejected" />
<transition on="TermsRejected" to="TermsRejected" />
<transition on="ContextCheckDenied" to="ContextCheckDenied" />
+ <transition on="ImpersonationViolation" to="ImpersonationViolation" />
<transition on="RestartAuthentication" to="RestartAuthentication" />
<transition on="RuntimeException" to="RuntimeException" />
</global-transitions>
diff --git a/idp-conf/src/main/resources/system/messages/messages.properties b/idp-conf/src/main/resources/system/messages/messages.properties
index af19e0f..ca20b9b 100644
--- a/idp-conf/src/main/resources/system/messages/messages.properties
+++ b/idp-conf/src/main/resources/system/messages/messages.properties
@@ -14,6 +14,7 @@
AccessDenied = access
ContextCheckDenied = context-check-denied
EndpointResolutionFailed = endpoint
+ImpersonationViolation = impersonation-violation
InvalidProfileConfiguration = relying-party
InvalidSecurityConfiguration = security-cfg
MessageAuthenticationError = security-msg
@@ -169,6 +170,12 @@ idp.terms-of-use.submit = Submit
idp.terms-of-use.reject = Refuse
idp.terms-of-use.required = Please check this box if you want to proceed.
+# General messages related to impersonation interceptor
+
+idp.impersonate.header = Account Impersonation
+idp.impersonate.login-as = Login as
+idp.impersonate.proceed = Proceed
+
# Triples consisting of a TOU key, and a title and text for each set of terms.
# The default implementation uses the SP name as the key, but this can be overriden.
@@ -209,6 +216,9 @@ access.message = You do not have access to the requested resource.
context-check-denied.title = Access Denied
context-check-denied.message = You are not eligible for the service requested.
+impersonation-violation.title = Access Denied
+impersonation-violation.message = You do not have permission to impersonate the selected account.
+
no-release.title = Release of Information Prevented
no-release.message = At your request, the release of your information has been blocked. If you wish to \
change your decision, you may access the service again and approve the release in the \
diff --git a/idp-conf/src/main/resources/views/intercept/impersonate.vm b/idp-conf/src/main/resources/views/intercept/impersonate.vm
new file mode 100644
index 0000000..ef3f54a
--- /dev/null
+++ b/idp-conf/src/main/resources/views/intercept/impersonate.vm
@@ -0,0 +1,97 @@
+##
+## Velocity Template for expiring password view
+##
+## 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
+## rpUIContext - the context with SP UI information from the metadata
+## encoder - HTMLEncoder class
+## request - HttpServletRequest
+## response - HttpServletResponse
+## environment - Spring Environment object for property resolution
+## custom - arbitrary object injected by deployer
+## principalCollection - Collection of impersonateable principal names (yes, this is re-validated)
+##
+#set ($rpContext = $profileRequestContext.getSubcontext('net.shibboleth.idp.profile.context.RelyingPartyContext'))
+#set ($subjectContext = $profileRequestContext.getSubcontext('net.shibboleth.idp.authn.context.SubjectContext'))
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width,initial-scale=1.0">
+ <title>#springMessageText("idp.title", "Web Login Service")</title>
+ <link rel="stylesheet" type="text/css" href="$request.getContextPath()/css/main.css">
+ </head>
+
+ <body>
+ <div class="wrapper">
+ <div class="container">
+ <header>
+ <img src="$request.getContextPath()#springMessage("idp.logo")" alt="#springMessageText("idp.logo.alt-text", "logo")">
+ <h3>#springMessageText("idp.impersonate.header", "Account Impersonation")</h3>
+ </header>
+
+ <div class="content">
+
+ <form action="$flowExecutionUrl" method="post">
+
+ #set ($serviceName = $rpUIContext.serviceName)
+ #if ($serviceName && !$rpContext.getRelyingPartyId().contains($serviceName))
+ <legend>
+ $encoder.encodeForHTML($serviceName)
+ </legend>
+ #end
+
+ <div class="form-element-wrapper">
+ <label for="impersonation">#springMessageText("idp.impersonate.login-as", "Login as")</label>
+ <select class="form-element form-field" id="impersonation" name="principal">
+ <option value="">$encoder.encodeForHTML($subjectContext.getPrincipalName())</option>
+ #foreach ($principal in $principalCollection)
+ <option value="$encoder.encodeForHTML($principal)">$encoder.encodeForHTML($principal)</option>
+ #end
+ </select>
+ </div>
+
+ <div class="form-element-wrapper">
+ <button class="form-element form-button" type="submit" name="_eventId_proceed"
+ >#springMessageText("idp.impersonate.proceed", "Proceed")</button>
+ </div>
+
+ </form>
+
+ #*
+ //
+ // 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 src= "$encoder.encodeForHTMLAttribute($logo)"
+ alt="$encoder.encodeForHTMLAttribute($serviceName)">
+ #end
+ #set ($desc = $rpUIContext.getServiceDescription())
+ #if ($desc)
+ $encoder.encodeForHTML($desc)
+ #end
+
+ </div>
+ </div>
+
+ <footer>
+ <div class="container container-footer">
+ <p class="footer-text">#springMessageText("idp.footer", "Insert your footer text here.")</p>
+ </div>
+ </footer>
+
+ </div>
+ </body>
+</html>
\ No newline at end of file
diff --git a/idp-profile-api/src/main/java/net/shibboleth/idp/profile/IdPAuditFields.java b/idp-profile-api/src/main/java/net/shibboleth/idp/profile/IdPAuditFields.java
index 895cb5b..4639d64 100644
--- a/idp-profile-api/src/main/java/net/shibboleth/idp/profile/IdPAuditFields.java
+++ b/idp-profile-api/src/main/java/net/shibboleth/idp/profile/IdPAuditFields.java
@@ -56,6 +56,9 @@ public final class IdPAuditFields {
/** Hashed username field. */
@Nonnull @NotEmpty public static final String HASHED_USERNAME = "HASHEDu";
+ /** Impersonating username field. */
+ @Nonnull @NotEmpty public static final String IMPERSONATED_USERNAME = "IMPu";
+
/** Attributes field. */
@Nonnull @NotEmpty public static final String ATTRIBUTES = "attr";
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list