[java-identity-provider] branch master updated: IDP-1211 - Interceptor for controlled impersonation to services
Scott Cantor
cantor.2 at osu.edu
Wed Aug 15 17:10:39 EDT 2018
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=fca608c2ef61bbe6171bd5e1050072b3a5a148cd
The following commit(s) were added to refs/heads/master by this push:
new fca608c IDP-1211 - Interceptor for controlled impersonation to services
fca608c is described below
commit fca608c2ef61bbe6171bd5e1050072b3a5a148cd
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Aug 15 17:10:37 2018 -0400
IDP-1211 - Interceptor for controlled impersonation to services
https://issues.shibboleth.net/jira/browse/IDP-1211
Redesigned flow.
---
.../idp/authn/context/SubjectContext.java | 33 +++++++++++++++++
...ontextImpersonatingPrincipalLookupFunction.java | 40 +++++++++++++++++++++
.../intercept/impersonate-intercept-config.xml | 14 ++++----
.../system/flows/intercept/impersonate-beans.xml | 41 +++++++++++++++-------
.../system/flows/intercept/impersonate-flow.xml | 40 ++++++++++++---------
.../resources/system/messages/messages.properties | 5 ++-
.../main/resources/views/intercept/impersonate.vm | 41 ++++++----------------
.../net/shibboleth/idp/profile/IdPAuditFields.java | 2 +-
8 files changed, 150 insertions(+), 66 deletions(-)
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/SubjectContext.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/SubjectContext.java
index cc4c2e3..4c1b73a 100644
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/SubjectContext.java
+++ b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/SubjectContext.java
@@ -45,11 +45,18 @@ import com.google.common.collect.ImmutableList;
*
* <p>This is the ultimate product of a successful authentication process.</p>
*
+ * <p>A second field is available to store an identity that is impersonating the effective
+ * subject identity. Profiles should operate on the effective subject unless they need to
+ * be aware of both identities.</p>
+ *
* @parent {@link org.opensaml.profile.context.ProfileRequestContext}
* @added After the subject of a request is determined
*/
public class SubjectContext extends BaseContext {
+ /** Canonical principal name of an impersonating identity. */
+ @Nullable private String impersonatingPrincipalName;
+
/** Canonical principal name of subject. */
@Nullable private String principalName;
@@ -84,6 +91,32 @@ public class SubjectContext extends BaseContext {
}
/**
+ * Get the canonical principal name of an identity that is impersonating the subject.
+ *
+ * @return the canonical principal name of an impersonating identity
+ *
+ * @since 3.4.0
+ */
+ @Nullable public String getImpersonatingPrincipalName() {
+ return impersonatingPrincipalName;
+ }
+
+ /**
+ * Set the canonical principal name of an identity that is impersonating the subject.
+ *
+ * @param name the canonical principal name of an impersonating identity
+ *
+ * @return this context
+ *
+ * @since 3.4.0
+ */
+ @Nonnull public SubjectContext setImpersonatingPrincipalName(@Nullable final String name) {
+ impersonatingPrincipalName = name;
+
+ return this;
+ }
+
+ /**
* Get a mutable map of authentication flow IDs to authentication results.
*
* @return mutable map of authentication flow IDs to authentication results
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/navigate/SubjectContextImpersonatingPrincipalLookupFunction.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/navigate/SubjectContextImpersonatingPrincipalLookupFunction.java
new file mode 100644
index 0000000..d089071
--- /dev/null
+++ b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/context/navigate/SubjectContextImpersonatingPrincipalLookupFunction.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.authn.context.navigate;
+
+import javax.annotation.Nullable;
+
+import net.shibboleth.idp.authn.context.SubjectContext;
+
+import org.opensaml.messaging.context.navigate.ContextDataLookupFunction;
+
+/** A function that returns the impersonating principal name from a {@link SubjectContext}. */
+public class SubjectContextImpersonatingPrincipalLookupFunction
+ implements ContextDataLookupFunction<SubjectContext,String> {
+
+ /** {@inheritDoc} */
+ @Override
+ @Nullable public String apply(@Nullable final SubjectContext input) {
+
+ if (input != null) {
+ return input.getImpersonatingPrincipalName();
+ }
+ return null;
+ }
+
+}
\ No newline at end of file
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
index 90f3b49..7dfda2b 100644
--- a/idp-conf/src/main/resources/conf/intercept/impersonate-intercept-config.xml
+++ b/idp-conf/src/main/resources/conf/intercept/impersonate-intercept-config.xml
@@ -12,12 +12,14 @@
default-init-method="initialize"
default-destroy-method="destroy">
- <!-- Function returns the list of principal names the user can impersonate, defaults to null. -->
+ <!--
+ Names of access control policies defined in access-control.xml to control impersonation.
+ The general policy runs first and determines whether to offer the impersonation option.
+ The specific policy runs second and determines whether to allow the requested impersonation.
+ -->
- <bean id="shibboleth.impersonate.PrincipalFunction" class="com.google.common.base.Functions" factory-method="constant">
- <constructor-arg>
- <null/>
- </constructor-arg>
- </bean>
+ <bean id="shibboleth.impersonate.GeneralPolicy" class="java.lang.String" c:_0="GeneralImpersonationPolicy" />
+
+ <bean id="shibboleth.impersonate.SpecificPolicy" class="java.lang.String" c:_0="SpecificImpersonationPolicy" />
</beans>
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
index 00441a9..7328135 100644
--- a/idp-conf/src/main/resources/system/flows/intercept/impersonate-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/intercept/impersonate-beans.xml
@@ -20,24 +20,34 @@
<import resource="../../../conf/intercept/impersonate-intercept-config.xml" />
- <!-- Simplifies flow definition expressions. -->
- <alias name="shibboleth.impersonate.PrincipalFunction" alias="PrincipalFunction"/>
+ <bean id="CheckGeneralAccess"
+ class="org.opensaml.profile.action.impl.CheckAccess" scope="prototype"
+ p:httpServletRequest-ref="shibboleth.HttpServletRequest"
+ p:accessControlService-ref="shibboleth.AccessControlService"
+ p:policyName-ref="shibboleth.impersonate.GeneralPolicy"
+ p:operation="impersonate" />
+
+ <bean id="CheckSpecificAccess"
+ class="org.opensaml.profile.action.impl.CheckAccess" scope="prototype"
+ p:httpServletRequest-ref="shibboleth.HttpServletRequest"
+ p:accessControlService-ref="shibboleth.AccessControlService"
+ p:policyName-ref="shibboleth.impersonate.SpecificPolicy"
+ p:operation="impersonate"
+ p:resourceLookupStrategy-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().getRequestScope().get('principalToSpoof')" />
<bean id="ResolveAttributes" class="net.shibboleth.idp.profile.impl.ResolveAttributes" scope="prototype"
c:resolverService-ref="shibboleth.AttributeResolverService"
p:resolutionLabel="intercept/impersonate"
- p:maskFailures="%{idp.service.attribute.resolver.maskFailures:true}"
- p:principalNameLookupStrategy-ref="FlowScopePrincipalLookup" />
+ p:maskFailures="%{idp.service.attribute.resolver.maskFailures:true}" />
<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().getRequestScope().get('principalToSpoof')" />
+ p:metadataResolver-ref="shibboleth.MetadataResolver" />
<import resource="../../conf/audit-system.xml" />
@@ -46,9 +56,16 @@
<map>
<entry>
<key>
- <util:constant static-field="net.shibboleth.idp.profile.IdPAuditFields.IMPERSONATED_USERNAME"/>
+ <util:constant static-field="net.shibboleth.idp.profile.IdPAuditFields.IMPERSONATING_USERNAME"/>
</key>
- <ref bean="FlowScopePrincipalLookup" />
+ <bean class="com.google.common.base.Functions" factory-method="compose">
+ <constructor-arg name="g">
+ <bean class="net.shibboleth.idp.authn.context.navigate.SubjectContextImpersonatingPrincipalLookupFunction" />
+ </constructor-arg>
+ <constructor-arg name="f">
+ <ref bean="shibboleth.ChildLookup.SubjectContext" />
+ </constructor-arg>
+ </bean>
</entry>
</map>
</property>
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
index 9e87437..05a2916 100644
--- a/idp-conf/src/main/resources/system/flows/intercept/impersonate-flow.xml
+++ b/idp-conf/src/main/resources/system/flows/intercept/impersonate-flow.xml
@@ -5,15 +5,18 @@
<!-- Rudimentary impediment to direct execution of subflow. -->
<input name="calledAsSubflow" type="boolean" required="true" />
-
+
<on-start>
- <evaluate expression="PrincipalFunction.apply(opensamlProfileRequestContext)" result="flowScope.principalCollection" />
+ <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.SubjectContext))" result="flowScope.subjectContext" />
</on-start>
- <decision-state id="CanImpersonate">
- <if test="principalCollection != null and !principalCollection.isEmpty()"
- then="ImpersonateView" else="proceed" />
- </decision-state>
+ <action-state id="CheckGeneralAccess">
+ <evaluate expression="CheckGeneralAccess" />
+ <evaluate expression="'proceed'" />
+
+ <transition on="proceed" to="ImpersonateView" />
+ <transition to="proceed" />
+ </action-state>
<view-state id="ImpersonateView" view="#{flowRequestContext.activeFlow.id}">
<on-render>
@@ -26,7 +29,8 @@
<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" />
+ <transition on="impersonate" to="CheckForImpersonation" />
+ <transition to="proceed" />
</view-state>
<decision-state id="CheckForImpersonation">
@@ -34,13 +38,16 @@
<evaluate expression="flowRequestContext.getExternalContext().getNativeRequest().getParameter('principal')" result="requestScope.principalToSpoof" />
</on-entry>
<if test="principalToSpoof == null or principalToSpoof.length() == 0"
- then="proceed" else="CheckAllowed" />
+ then="proceed" else="CheckSpecificAccess" />
</decision-state>
- <decision-state id="CheckAllowed">
- <if test="principalCollection.contains(principalToSpoof)"
- then="DoImpersonate" else="ImpersonationViolation" />
- </decision-state>
+ <action-state id="CheckSpecificAccess">
+ <evaluate expression="CheckSpecificAccess" />
+ <evaluate expression="'proceed'" />
+
+ <transition on="proceed" to="DoImpersonate" />
+ <transition to="ImpersonationViolation" />
+ </action-state>
<action-state id="DoImpersonate">
<on-entry>
@@ -48,8 +55,10 @@
<evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.profile.context.RelyingPartyContext)).removeSubcontext(T(net.shibboleth.idp.attribute.context.AttributeContext))" />
<!-- Log the impersonation. -->
<evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.profile.context.RelyingPartyContext)).getRelyingPartyId()" result="requestScope.relyingPartyId" />
- <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.SubjectContext)).getPrincipalName()" result="requestScope.principalSpoofer" />
- <evaluate expression="T(org.slf4j.LoggerFactory).getLogger('net.shibboleth.idp.profile.interceptor').info('Impersonation by principal ''{}'' as ''{}'' to relying party ''{}''', new Object[]{principalSpoofer, principalToSpoof, relyingPartyId})" />
+ <evaluate expression="T(org.slf4j.LoggerFactory).getLogger('net.shibboleth.idp.profile.interceptor').info('Impersonation by principal ''{}'' as ''{}'' to relying party ''{}''', new Object[]{subjectContext.getPrincipalName(), principalToSpoof, relyingPartyId})" />
+ <!-- Swap identities. -->
+ <evaluate expression="subjectContext.setImpersonatingPrincipalName(subjectContext.getPrincipalName())" />
+ <evaluate expression="subjectContext.setPrincipalName(principalToSpoof)" />
</on-entry>
<evaluate expression="PopulateAuditContext" />
@@ -64,8 +73,7 @@
<on-entry>
<!-- Log violation. -->
<evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.profile.context.RelyingPartyContext)).getRelyingPartyId()" result="requestScope.relyingPartyId" />
- <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.SubjectContext)).getPrincipalName()" result="requestScope.principalSpoofer" />
- <evaluate expression="T(org.slf4j.LoggerFactory).getLogger('net.shibboleth.idp.profile.interceptor').warn('Unauthorized impersonation attempt by principal ''{}'' as ''{}'' to relying party ''{}''', new Object[]{principalSpoofer, principalToSpoof, relyingPartyId})" />
+ <evaluate expression="T(org.slf4j.LoggerFactory).getLogger('net.shibboleth.idp.profile.interceptor').warn('Unauthorized impersonation attempt by principal ''{}'' as ''{}'' to relying party ''{}''', new Object[]{subjectContext.getPrincipalName(), principalToSpoof, relyingPartyId})" />
</on-entry>
</end-state>
diff --git a/idp-conf/src/main/resources/system/messages/messages.properties b/idp-conf/src/main/resources/system/messages/messages.properties
index 29eae36..3f71609 100644
--- a/idp-conf/src/main/resources/system/messages/messages.properties
+++ b/idp-conf/src/main/resources/system/messages/messages.properties
@@ -22,6 +22,7 @@ MessageReplay = stale
MessageExpired = stale
UnableToDecode = stale
AccountError = authn
+AccountLocked = authn
AuthenticationException = authn
InvalidCredentials = authn
NoCredentials = authn
@@ -174,8 +175,10 @@ 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.text = Enter an account name to impersonate to this service or continue normally.
idp.impersonate.login-as = Login as
-idp.impersonate.proceed = Proceed
+idp.impersonate.impersonate = Impersonate
+idp.impersonate.continue = Continue Normally
# General messages related to key-unlocking admin flow
diff --git a/idp-conf/src/main/resources/views/intercept/impersonate.vm b/idp-conf/src/main/resources/views/intercept/impersonate.vm
index 4fa4e93..4895677 100644
--- a/idp-conf/src/main/resources/views/intercept/impersonate.vm
+++ b/idp-conf/src/main/resources/views/intercept/impersonate.vm
@@ -12,10 +12,8 @@
## 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>
@@ -44,44 +42,27 @@
</legend>
#end
+ <legend>
+ #springMessageText("idp.impersonate.text", "Enter an account name to impersonate to this service or continue normally.")
+ </legend>
+
<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>
+ <input class="form-element form-field" id="impersonation" name="principal" type="text">
+ </div>
+
+ <div class="form-element-wrapper">
+ <button class="form-element form-button" type="submit" name="_eventId_impersonate"
+ >#springMessageText("idp.impersonate.impersonate", "Impersonate")</button>
</div>
<div class="form-element-wrapper">
<button class="form-element form-button" type="submit" name="_eventId_proceed"
- >#springMessageText("idp.impersonate.proceed", "Proceed")</button>
+ >#springMessageText("idp.impersonate.continue", "Continue Normally")</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>
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 4639d64..fbaa587 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
@@ -57,7 +57,7 @@ public final class IdPAuditFields {
@Nonnull @NotEmpty public static final String HASHED_USERNAME = "HASHEDu";
/** Impersonating username field. */
- @Nonnull @NotEmpty public static final String IMPERSONATED_USERNAME = "IMPu";
+ @Nonnull @NotEmpty public static final String IMPERSONATING_USERNAME = "uu";
/** 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