Principal-Id Attribute

Registered CAS applications are given the ability to allow for configuration of a username attribute provider, which controls what should be the designated user identifier that is returned to the application. The user identifier by default is the authenticated CAS principal id, yet it optionally may be based off of an existing attribute that is available and resolved for the principal already. More practically, this component determines what should be placed inside the <cas:user> tag in the final CAS validation payload that is returned to the application.

Principal Id As Attribute

You may also return the authenticated principal id as an extra attribute in the final CAS payload. See this guide to learn more.

A number of providers are able to perform canonicalization on the final user id returned to transform it into uppercase/lowercase. This is noted by the canonicalizationMode whose allowed values are UPPER, LOWER or NONE.

Default

The default configuration which need not explicitly be defined, simply returns the resolved principal id as the username for this service.

1
2
3
4
5
6
7
8
9
10
11
{
  "@class" : "org.apereo.cas.services.RegexRegisteredService",
  "serviceId" : "sample",
  "name" : "sample",
  "id" : 100,
  "description" : "sample",
  "usernameAttributeProvider" : {
    "@class" : "org.apereo.cas.services.DefaultRegisteredServiceUsernameProvider",
    "canonicalizationMode" : "NONE"
  }
}

If you do not need to adjust the behavior of this provider (i.e. to modify the canonicalization mode), then you can leave out this block entirely.

Encrypted

Most if not all providers are able to encrypt the resolved username, assuming the service definition is given a public key.

The key can be generated via the following commands:

1
2
3
openssl genrsa -out private.key 1024
openssl rsa -pubout -in private.key -out public.key -inform PEM -outform DER
openssl pkcs8 -topk8 -inform PER -outform DER -nocrypt -in private.key -out private.p8

The public key is then configured for a service definition in CAS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
  "@class" : "org.apereo.cas.services.RegexRegisteredService",
  "serviceId" : "sample",
  "name" : "sample",
  "id" : 100,
  "description" : "sample",
  "usernameAttributeProvider" : {
    "@class" : "org.apereo.cas.services.DefaultRegisteredServiceUsernameProvider",
    "encryptUsername" : "true"
  },
  "publicKey" : {
    "@class" : "org.apereo.cas.services.RegisteredServicePublicKeyImpl",
    "location" : "classpath:public.key",
    "algorithm" : "RSA"
  }
}

The configuration of the public key component qualifies to use the Spring Expression Language syntax.

The application can then proceed to decrypt the username using its own private key. The following sample code demonstrates how that might be done in Java:

1
2
3
4
5
6
7
final String casUsername = ...
final PrivateKey privateKey = ...
final Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
final byte[] cred64 = decodeBase64(encodedPsw);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
final byte[] cipherData = cipher.doFinal(casUsername);
return new String(cipherData);

Attribute

Returns an attribute that is already resolved for the principal as the username for this service. If the attribute is not available, the default principal id will be used.

1
2
3
4
5
6
7
8
9
10
11
12
{
  "@class" : "org.apereo.cas.services.RegexRegisteredService",
  "serviceId" : "sample",
  "name" : "sample",
  "id" : 600,
  "description" : "sample",
  "usernameAttributeProvider" : {
    "@class" : "org.apereo.cas.services.PrincipalAttributeRegisteredServiceUsernameProvider",
    "usernameAttribute" : "cn",
    "canonicalizationMode" : "UPPER"
  }
}

Javascript/Python/Groovy Script

Usage

This feature is deprecated and is scheduled to be removed in the future.

Let an external javascript, groovy or python script decide how the principal id attribute should be determined. This approach takes advantage of scripting functionality built into the Java platform. While Javascript and Groovy should be natively supported by CAS, Python scripts may need to massage the CAS configuration to include the Python modules.

Scripts will receive and have access to the following variable bindings:

  • id: The existing identifier for the authenticated principal.
  • attributes: A map of attributes currently resolved for the principal.
  • logger: A logger object, able to provide logger.info() operations, etc.
1
2
3
4
5
6
7
8
9
10
11
12
{
  "@class" : "org.apereo.cas.services.RegexRegisteredService",
  "serviceId" : "sample",
  "name" : "sample",
  "id" : 500,
  "description" : "sample",
  "usernameAttributeProvider" : {
    "@class" : "org.apereo.cas.services.ScriptedRegisteredServiceUsernameProvider",
    "script" : "file:///etc/cas/sampleService.[groovy|js|.py]",
    "canonicalizationMode" : "UPPER"
  }
}

Sample Groovy script follows:

1
2
3
4
5
6
7
def run(Object[] args) {
    def attributes = args[0]
    def id = args[1]
    def logger = args[2]
    logger.info("Testing username attribute")
    return "test"
}

Sample javascript function follows:

1
2
3
function run(uid, logger) {
   return "test"
}

Groovy

Returns a username attribute value as the final result of a groovy script’s execution. Groovy scripts whether inlined or external will receive and have access to the following variable bindings:

  • id: The existing identifier for the authenticated principal.
  • attributes: A map of attributes currently resolved for the principal.
  • service: The service object that is matched by the registered service definition.
  • logger: A logger object, able to provide logger.info(...) operations, etc.

Inline

Embed the groovy script directly inside the service configuration.

1
2
3
4
5
6
7
8
9
10
11
12
{
  "@class" : "org.apereo.cas.services.RegexRegisteredService",
  "serviceId" : "sample",
  "name" : "sample",
  "id" : 600,
  "description" : "sample",
  "usernameAttributeProvider" : {
    "@class" : "org.apereo.cas.services.GroovyRegisteredServiceUsernameProvider",
    "groovyScript" : "groovy { return attributes['uid'][0] + '123456789' }",
    "canonicalizationMode" : "UPPER"
  }
}

Note that the uid attribute in the above example is resolved internally as a multivalued attribute, as should all attributes when fetched by CAS. So the above example uses the [0] syntax to fetch the first value of the attribute.

External

Reference the groovy script as an external resource outside the service configuration. The script must return a single String value.

1
2
3
4
5
6
7
8
9
10
11
12
{
  "@class" : "org.apereo.cas.services.RegexRegisteredService",
  "serviceId" : "sample",
  "name" : "sample",
  "id" : 600,
  "description" : "sample",
  "usernameAttributeProvider" : {
    "@class" : "org.apereo.cas.services.GroovyRegisteredServiceUsernameProvider",
    "groovyScript" : "file:///etc/cas/sampleService.groovy",
    "canonicalizationMode" : "UPPER"
  }
}

Sample Groovy script follows:

1
2
logger.info("Choosing username attribute out of attributes $attributes")
return "newPrincipalId"

The configuration of this component qualifies to use the Spring Expression Language syntax.

Anonymous / Transient

Provides an opaque identifier for the username.

1
2
3
4
5
6
7
8
9
10
{
  "@class" : "org.apereo.cas.services.RegexRegisteredService",
  "serviceId" : "sample",
  "name" : "sample",
  "id" : 500,
  "description" : "sample",
  "usernameAttributeProvider" : {
    "@class" : "org.apereo.cas.services.AnonymousRegisteredServiceUsernameAttributeProvider"
  }
}

Anonymous / Persistent

Provides an opaque identifier for the username. The opaque identifier by default conforms to the requirements of the eduPersonTargetedID attribute. The generated id may be based off of an existing principal attribute. If left unspecified or attribute not found, the authenticated principal id is used.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
  "@class" : "org.apereo.cas.services.RegexRegisteredService",
  "serviceId" : "sample",
  "name" : "sample",
  "id" : 500,
  "description" : "sample",
  "usernameAttributeProvider" : {
    "@class" : "org.apereo.cas.services.AnonymousRegisteredServiceUsernameAttributeProvider",
    "persistentIdGenerator" : {
      "@class" : "org.apereo.cas.authentication.principal.ShibbolethCompatiblePersistentIdGenerator",
      "salt" : "aGVsbG93b3JsZA==",
      "attribute": ""
    }
  }
}

To simulate the behavior, you may also try the following command:

1
2
3
perl -e 'use Digest::SHA qw(sha1_base64); \
    $digest = sha1_base64("$SERVICE!$USER!$SALT"); \
    $eqn = length($digest) % 4; print $digest; print "=" x (4-$eqn) . "\n"' 

Replace $SERVICE (the url of the application under test), $USER and $SALT with the appropriate values for the test.