Gluu Blog

Follow us:
Back to Blog

Five Interceptions of the OX OP

Mike S. August 20, 2013

Use Jython to Customize Authentication and Authorization policy

We’ve designed the Gluu Server to be very flexible. Using the web based oxTrust application, admins can use Jython scripts to customize behavior.

Jython was chosen because an interpreted language facilitates dynamic creation of business logic, and makes it easier to distribute this logic to a cluster of Gluu Servers. Another advantage of Jython was that developers can use either Java or Python classes. Combined with the option of calling web services from Python or Java, this enables the Gluu Server to support any crazy requirement. Domains can use “interception” scripts to code their own business logic in five areas:

  1. Authentication: Implement adaptive authentication to identify people in one or more steps, and if needed, support SSO workflows other than SAML & OpenID Connect such as CAS or Social Login.
  2. Authorization: Express your policies in Python or Java, or call an external entitlements management system, like XACML or SiteMinder.
  3. Attribute Transformation: Create new attributes, change attribute names, or change the value of existing attributes.
  4. Logout: Make sure you logout of any backend services, such as an external IDP or porotal, or SSO environment.
  5. ID Generation: People don’t see their internal id, but domains may want to use one convention or another to provide a “primary key” value to identify an entity (person, client, etc.) UUID’s are the most common, but also used is IPv6 addresses, DNS style names, or custom schemes.

Below is an example of each.

If you have questions or would like to see a demo, feel free to schedule a call or contact us.

Authentication Script

Interface:

public interface ExternalAuthenticatorType {

	public boolean init(Map configurationAttributes);

	public boolean authenticate(Map configurationAttributes, Map requestParameters, int step);

	public boolean prepareForStep(Map configurationAttributes, Map requestParameters, int step);
	
	public int getCountAuthenticationSteps(Map configurationAttributes);

	public String getPageForStep(Map configurationAttributes, int step);

	public List getExtraParametersForStep(Map configurationAttributes, int step);

}

Sample Script

from org.jboss.seam.security import Identity
from org.xdi.oxauth.service.python.interfaces import ExternalAuthenticatorType
from org.xdi.oxauth.service import UserService
from org.xdi.util import StringHelper

import java

class ExternalAuthenticator(ExternalAuthenticatorType):
    def __init__(self, currentTimeMillis):
        self.currentTimeMillis = currentTimeMillis

    def init(self, configurationAttributes):
        return True   

    def authenticate(self, configurationAttributes, requestParameters, step):
        if (step == 1):
            print "Basic authenticate for step 1"

            credentials = Identity.instance().getCredentials()
            user_name = credentials.getUsername()
            user_password = credentials.getPassword()

            logged_in = False
            if (StringHelper.isNotEmptyString(user_name) and StringHelper.isNotEmptyString(user_password)):
                userService = UserService.instance()
                logged_in = userService.authenticate(user_name, user_password)

            if (not logged_in):
                return False
# Commented out becuase we do the same in AuthenticationService.authenticate method
#
#            user = userService.getUser(user_name)
#            if (user == None):
#                print "Basic authenticate for step 1. Failed to find user in local LDAP"
#                return False
#
#            # Store user to allow use this module for web services 
#            credentials.setUser(user);

            return True
        else:
            return False

    def prepareForStep(self, configurationAttributes, requestParameters, step):
        if (step == 1):
            print "Basic prepare for Step 1"
            return True
        else:
            return False

    def getExtraParametersForStep(self, configurationAttributes, step):
        return None

    def getCountAuthenticationSteps(self, configurationAttributes):
        return 1

    def getPageForStep(self, configurationAttributes, step):
        return ""

Attribute Transformation Script

Interfaces:

public interface EntryInterceptorType {

	public boolean updateAttributes(GluuCustomPerson person);

}

Sample Attribute Transformation Script

# Import of Java classes
from org.gluu.site.ldap.cache.service.intercept.interfaces import EntryInterceptorType
from org.gluu.site.util import StringHelper
from org.gluu.site.model import GluuCustomPerson
from org.gluu.site.model import GluuCustomAttribute

import java

class EntryInterceptor(EntryInterceptorType):
    def __init__(self, currentTimeMillis):
	self.currentTimeMillis = currentTimeMillis

    def updateAttributes(self, person):

        # Assign the current entry's attributes to an array
	attributes = person.getCustomAttributes()

	# Create and set the attribute eduPersonPrincipalName
	uidValue = person.getAttribute('uid')
	attrEPPN = GluuCustomAttribute('edupersonprincipalname', uidValue + '@example.edu')
	attributes.add(attrEPPN)

	# Loop through each attribute in the current entry
	for attribute in attributes:
	    attrName = attribute.getName()

	    # The mapping in the Cache Refresh configuration page set employeeType to
            # eduPersonScopedAffiliation(EPSA). This means that EPSA currently has an
            # integer value instead of the value expected by Educause. It needs to be
            # set to the correct value.
	    if ("edupersonscopedaffiliation" == StringHelper.toLowerCase(attrName)):
		attrValue = attribute.getValue()
		newEPSAValue = []

	        if (attrValue==1):
                    newEPSAValue.append('student@example.edu')
		elif (attrValue==2):
		    newEPSAValue.append('faculty@example.edu')
		elif (attrValue==3):
		    newEPSAValue.append('student@example.edu')
		    newEPSAValue.append('faculty@example.edu')
		elif (attrValue==4):
		    newEPSAValue.append('staff@example.edu')
		elif (attrValue==5):
		    newEPSAValue.append('staff@example.edu')
		    newEPSAValue.append('faculty@example.edu')

                # Remove current attribute which is EPSA with an integer value
                attributes.remove(attribute)
                epsa = GluuAttribute('eduPersonScopedAffiliation', newEPSAValue)
                attributes.add(epsa)
        return True                                                        

Authorization Script

Interfaces:

public interface IPolicyExternalAuthorization {
    public boolean authorize(AuthorizationContext p_authorizationContext);
}

public interface IAuthorizationContext {
    String getClientClaim(String claimName);
    String getUserClaim(String claimName);
    String getUserClaimByLdapName(String ldapName);
    Integer getAuthLevel();
    String getAuthMode();
    String getIpAddress();
    boolean isInNetwork(String cidrNotation);
    RequesterPermissionToken getRPT();
    ResourceSetPermission getPermission();
    AuthorizationGrant getGrant(); // here return unmodifiable version of grant, e.g. to avoid new token creation
    HttpServletRequest getHttpRequest();
}

Python sample authorization script (authorize only if user location claim equals to Austin)

from org.xdi.oxauth.service.uma.authorization import IPolicyExternalAuthorization
from org.xdi.util import StringHelper

class PythonExternalAuthorization(IPolicyExternalAuthorization):

    def authorize(self, authorizationContext):

        print "authorizing..."

        if StringHelper.equalsIgnoreCase(authorizationContext.getUserClaim("locality"), "Austin"):
            print "authorized"
            return True

        return False
 

Logout

Interface:

public interface CustomLogoutScript{

    /**
     * Provides an interception to insert domain specific logic when a person logs out,
     * such as logout to backend IDPs.
     *
     * @param p_idType   id type : use to specify entity type, i.e. person, client
     * @return int result code
     */
    public String logout(String message);
}

Sample Script:

import myIDP

class ExampleDomainLogout(CustomLogoutScript):
    def logout(self, message):
        print "Executing %s" % message
        try:
            myIDP.logout()
        except Exception, err:
            print Exception, err            
            return 1
        return 0 

GenerateID

Interface:

public interface IdGenerator {

    /**
     * Generates id.
     *
     * @param p_idType   id type : use to specify entity type, i.e. person, client
     * @param p_idPrefix id prefix : If you want to prefix all ids, use this, otherwise pass ""
     * @return generated id as string
     */
    public String generateId(String p_idType, String p_idPrefix);
}

Sample Script:

import uuid

class ExampleDomainUniqueIdPolicyClass(IdGenerator):
    def generateId(self, entityType, prefix):
        print "Generating %s ID for prefix %s" % entityType, prefix
        # make a UUID using an MD5 hash of a namespace UUID and a name
        id = uuid.uuid3(uuid.NAMESPACE_DNS, 'example.co')
        return "%s%s" % (prefix, id)
 

Be sure to subscibe to
our RSS Feed

Mike Schwartz

Mike has been an entrepreneur and identity specialist for more than two decades. He is the technical and business visionary behind Gluu. Mike is an application security expert and has been a featured speaker at RSA Conference, Gartner Catalyst, Cloud Identity Summity (now "Identiverse") and many other security conferences around the world.