Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Expand
titlegrouperImpersonation.js
Code Block
importClass(Packages.java.util.ArrayList);
importClass(Packages.net.shibboleth.shared.httpclient.HttpClientSupport);
importPackage(Packages.net.shibboleth.idp.attribute);

var body = HttpClientSupport.toString(response.getEntity(), "UTF-8", 65536);
var result = JSON.parse(body);

if (result != null
        && result.WsGetGroupsLiteResult != null
        && result.WsGetGroupsLiteResult.resultMetadata != null
        && result.WsGetGroupsLiteResult.resultMetadata.resultCode == "SUCCESS") {
        var groups = result.WsGetGroupsLiteResult.wsGroups;
        if (groups != null) {
                var values = new ArrayList();
                for (var i=0; i<groups.length; i++) {

                        // Format is OSU:WebLoginService:SHA1(entityID):impersonate:username
                        var ctx= custom.get("servletRequestSupplier").get().getAttribute("opensamlProfileRequestContext").getSubcontext("net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext");
                        if (ctx != null && ctx.getAttributeRecipientID() != null) {
                            var prefix = "OSU:WebLoginService:" + custom.get("digester").apply(ctx.getAttributeRecipientID()) + ":impersonate:";
                            if (groups[i].name.indexOf(prefix) != 0) {
                                values.clear();
                                break;
                            }
                        }

                        var username = groups[i].name.substring(73);
                        values.add(new StringAttributeValue(username));
                }

                if (!values.isEmpty()) {
                        var usernames = new IdPAttribute("impersonatableUsernames");
                        usernames.setValues(values);
                        connectorResults.add(usernames);
                }
        }
}

Most of this is just parsing the Grouper result, but Grouper has a critical, unfixed bug that causes a request for a non-existent stem to return every group membership the requesting account can see. To prevent this, the code checks that the stem prefix is as expected, and if not, that the data should be cleared and nothing returned.

Apart from that, the username suffix of each group membership is stripped off and populated as a value of an array list. Provided at least one exists, the array list is then converted into values of a resolver attribute named “impersonatableUsernames“.

Impersonation Policies

TBD - the access control policies used by the flow to control the feature based on the resolved attributeWith all this machinery in place, the connection back to the impersonation process involves definiing the to access control policies that the feature relies on.

The “general” policy which controls whether the impersonation flow runs at all is simply a check for whether the “impersonatableUsernames” IdPAttribute computed via the script above based on Grouper data has any values at all. That is, if it has a value, then at least one impersonatable identity was authorized for the user, so they have the option to select one to use or proceed as themselves.

The “specific” policy which controls whether the specific identity chosen is acceptable or not is then evaluated against the values present in the IdPAttribute indirectly using a special condition defined for this use case. In English, what this does is define a function to run to obtain the value to check for in the values of the IdPAttribute. The function in this case is a Spring Expression that extracts the “resource” the access control policy is being run against, which for impersonation checking is the actual identity to impersonate.

Expand
titleaccess-control.xml
Code Block
        <entry key="GeneralImpersonationPolicy">
            <bean parent="shibboleth.PredicateAccessControl">
                <constructor-arg>
                    <bean class="net.shibboleth.idp.profile.logic.SimpleAttributePredicate"
                            p:useUnfilteredAttributes="true">
                        <property name="attributeValueMap">
                            <map>
                                <entry key="impersonatableUsernames">
                                    <list>
                                        <value>*</value>
                                    </list>
                                </entry>
                            </map>
                        </property>
                    </bean>
                </constructor-arg>
            </bean>
        </entry>

        <entry key="SpecificImpersonationPolicy">
            <bean parent="shibboleth.PredicateAccessControl">
                <constructor-arg>
                    <bean parent="shibboleth.Conditions.DynamicAttribute">
                        <property name="attributeFunctionMap">
                            <map>
                                <entry key="impersonatableUsernames">
                                    <list>
                                        <bean parent="shibboleth.ContextFunctions.Expression"
                                            c:expression="#input.getSubcontext(T(org.opensaml.profile.context.AccessControlContext)).getResource()" />
                                    </list>
                                </entry>
                            </map>
                        </property>
                    </bean>
                </constructor-arg>
            </bean>
        </entry>