[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