In the Shibboleth IdP it's possible to enable the impersonation intercept to enable help desk to impersonate users.
The impersonation is an amazing feature very well designed.
You can enable it in three steps:
- enable the module;
- add a p:postAuthenticationFlows="#{ {'impersonate'} }" attr to the SAML2 property to the relying party list involved (relying-party.xml);
- configure it on conf/access-control.xml (legacy configuration file in conf/intercept is useless).
The documentation proposes a workflow where a help desk operator has to be authorized to impersonate johndoe user by adding the johndoe value to a certain operator's attribute.
If your requirements allow any help desk operator to impersonate any user, the configuration can be simply:
<?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"> | |
<!-- Limits who can impersonate based on group membership. --> | |
<entry key="GeneralImpersonationPolicy"> | |
<bean parent="shibboleth.PredicateAccessControl"> | |
<constructor-arg> | |
<bean class="net.shibboleth.idp.profile.logic.SimpleAttributePredicate"> | |
<property name="attributeValueMap"> | |
<map> | |
<entry key="isMemberOf"> | |
<list> | |
<value>example:impersonate</value> | |
</list> | |
</entry> | |
</map> | |
</property> | |
</bean> | |
</constructor-arg> | |
</bean> | |
</entry> | |
<!-- Controls the impersonation scenarios to allow. --> | |
<!-- Impersonate any user on the https://sp.example.org/shibboleth SP --> | |
<entry key="SpecificImpersonationPolicy"> | |
<bean parent="shibboleth.PredicateAccessControl"> | |
<constructor-arg> | |
<bean parent="shibboleth.Conditions.AND"> | |
<constructor-arg> | |
<bean parent="shibboleth.Conditions.TRUE" /> | |
</constructor-arg> | |
<constructor-arg> | |
<bean parent="shibboleth.Conditions.RelyingPartyId" | |
c:candidates="#{{'https://sp.example.org/shibboleth'}}" /> | |
</constructor-arg> | |
</bean> | |
</constructor-arg> | |
</bean> | |
</entry> | |
<!-- | |
your other beans here | |
--> | |
</util:map> | |
</beans> |
The impersonation event is logged on the IdP. But if you want to let the SP to know about it, you can leverage the "populate the impersonated principal name into the attached SubjectContext" phase of the module. What you need is a acript that picks the added Subject and turns it to an attribute:
import org.slf4j.* | |
import net.shibboleth.idp.attribute.* | |
import net.shibboleth.idp.authn.context.SubjectContext | |
logger = LoggerFactory.getLogger("org.example.idp.scripted.groovy.impersonatingPrincipalName") | |
subjectContext = profileContext.getSubcontext(SubjectContext.class) | |
impersonating_principal_name = subjectContext.impersonatingPrincipalName | |
logger.debug("impersonating_principal_name: {}", impersonating_principal_name) | |
impersonatingPrincipalName.addValue(impersonating_principal_name) |
Add it to attribute-resolver.xml:
<AttributeDefinition id="impersonatingPrincipalName" xsi:type="ScriptedAttribute" language="groovy"> | |
<ScriptFile>%{idp.home}/script/impersonatingPrincipalName.groovy</ScriptFile> | |
</AttributeDefinition> |
Of course the attribute format can be embedded in the declaration or be pulled in from a property file in conf/attributes/custom:
# impersonatingPrincipalName | |
id=impersonatingPrincipalName | |
transcoder=SAML2StringTranscoder | |
displayName.en=impersonatingPrincipalName | |
# displayName.bacedifo=if you want bacedifo speakers understand it | |
description.en=original impersonating user name | |
# description.bacedifo=same as above | |
saml2.name=urn:mace:example.org:attribute-def:impersonatingPrincipalName | |
saml1.encodeType=false |