summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--modules/core/docs/authproc_attributevaluemap.txt83
-rw-r--r--modules/core/lib/Auth/Process/AttributeValueMap.php120
-rw-r--r--tests/modules/core/lib/Auth/Process/AttributeValueMapTest.php196
3 files changed, 399 insertions, 0 deletions
diff --git a/modules/core/docs/authproc_attributevaluemap.txt b/modules/core/docs/authproc_attributevaluemap.txt
new file mode 100644
index 0000000..cf7468a
--- /dev/null
+++ b/modules/core/docs/authproc_attributevaluemap.txt
@@ -0,0 +1,83 @@
+`core:AttributeValueMap`
+===================
+
+Filter that creates a target attribute based on one or more value(s) in source attribute.
+
+%replace can be used to replace all existing values in target with new ones (any existing values will be lost)
+%keep can be used to keep the source attribute, otherwise it will be removed.
+
+Examples
+--------
+
+Add student affiliation based on LDAP groupmembership.
+Will add eduPersonAffiliation containing value "student" if memberOf attribute contains
+either 'cn=student,o=some,o=organization,dc=org' or 'cn=student,o=other,o=organization,dc=org'.
+'memberOf' attribute will be removed (use %keep, to keep it) and existing values in
+'eduPersonAffiliation' will be merged (use %replace to replace them).
+
+ 'authproc' => array(
+ 50 => array(
+ 'class' => 'core:AttributeValueMap',
+ 'sourceattribute' => 'memberOf',
+ 'targetattribute' => 'eduPersonAffiliation',
+ 'values' => array(
+ 'student' => array(
+ 'cn=student,o=some,o=organization,dc=org',
+ 'cn=student,o=other,o=organization,dc=org',
+ ),
+ ),
+ ),
+ )
+
+Multiple assignments.
+Add student, employee and both affiliation based on LDAP groupmembership in memberOf attribute.
+
+ 'authproc' => array(
+ 50 => array(
+ 'class' => 'core:AttributeValueMap',
+ 'sourceattribute' => 'memberOf',
+ 'targetattribute' => 'eduPersonAffiliation',
+ 'values' => array(
+ 'student' => array(
+ 'cn=student,o=some,o=organization,dc=org',
+ 'cn=student,o=other,o=organization,dc=org',
+ ),
+ 'employee' => array(
+ 'cn=employees,o=some,o=organization,dc=org',
+ 'cn=employee,o=other,o=organization,dc=org',
+ 'cn=workers,o=any,o=organization,dc=org',
+ ),
+ 'both' => array(
+ 'cn=student,o=some,o=organization,dc=org',
+ 'cn=student,o=other,o=organization,dc=org',
+ 'cn=employees,o=some,o=organization,dc=org',
+ 'cn=employee,o=other,o=organization,dc=org',
+ 'cn=workers,o=any,o=organization,dc=org',
+ ),
+ ),
+ ),
+ )
+
+Replace and Keep.
+Replace any existing 'affiliation' attribute values and keep 'groups' attribute.
+
+ 'authproc' => array(
+ 50 => array(
+ 'class' => 'core:AttributeValueMap',
+ 'sourceattribute' => 'groups',
+ 'targetattribute' => 'affiliation',
+ '%replace',
+ '%keep',
+ 'values' => array(
+ 'student' => array(
+ 'cn=student,o=some,o=organization,dc=org',
+ 'cn=student,o=other,o=organization,dc=org',
+ ),
+ 'employee' => array(
+ 'cn=employees,o=some,o=organization,dc=org',
+ 'cn=employee,o=other,o=organization,dc=org',
+ 'cn=workers,o=any,o=organization,dc=org',
+ ),
+ ),
+ ),
+ )
diff --git a/modules/core/lib/Auth/Process/AttributeValueMap.php b/modules/core/lib/Auth/Process/AttributeValueMap.php
new file mode 100644
index 0000000..231f2d3
--- /dev/null
+++ b/modules/core/lib/Auth/Process/AttributeValueMap.php
@@ -0,0 +1,120 @@
+<?php
+
+/**
+ * Filter to create target attribute based on value(s) in source attribute
+ *
+ * @author Martin van Es, m7
+ * @package simpleSAMLphp
+ */
+class sspmod_core_Auth_Process_AttributeValueMap extends SimpleSAML_Auth_ProcessingFilter {
+
+ /**
+ * The attributename we should assign values to (ie target)
+ */
+ private $targetattribute;
+
+ /**
+ * The attributename we should create values from
+ */
+ private $sourceattribute;
+
+ /**
+ * The required $sourceattribute values and target affiliations
+ */
+ private $values = array();
+
+ /**
+ * Wether $sourceattribute should be kept
+ */
+ private $keep = false;
+
+ /**
+ * Wether $target attribute values should be replaced by new values
+ */
+ private $replace = false;
+
+ /**
+ * Initialize this filter.
+ *
+ * @param array $config Configuration information about this filter.
+ * @param mixed $reserved For future use.
+ */
+ public function __construct($config, $reserved) {
+ parent::__construct($config, $reserved);
+
+ assert('is_array($config)');
+
+ /* Validate configuration. */
+ foreach ($config as $name => $value) {
+ if (is_int($name)) {
+ // check if this is an option
+ if ($value === '%replace') {
+ $this->replace = true;
+ } elseif ($value === '%keep') {
+ $this->keep = true;
+ } else {
+ throw new SimpleSAML_Error_Exception('Unknown flag : ' . var_export($value, true));
+ }
+ continue;
+ }
+
+ // Set targetattribute
+ if ($name === 'targetattribute') {
+ $this->targetattribute = $value;
+ }
+
+ // Set sourceattribute
+ if ($name === 'sourceattribute') {
+ $this->sourceattribute = $value;
+ }
+
+ // Set values
+ if ($name === 'values') {
+ $this->values = $value;
+ }
+ }
+ }
+
+
+ /**
+ * Apply filter to add groups attribute.
+ *
+ * @param array &$request The current request
+ */
+ public function process(&$request) {
+ SimpleSAML_Logger::debug('AttributeValueMap - process');
+
+ assert('is_array($request)');
+ assert('array_key_exists("Attributes", $request)');
+ $attributes =& $request['Attributes'];
+
+ // Make sure sourceattribute exists
+ assert('array_key_exists($this->sourceattribute, $attributes)');
+ // Make sure the targetattribute is set
+ assert('is_string($this->targetattribute)');
+
+ $sourceattribute = $attributes[$this->sourceattribute];
+ $targetvalues = array();
+
+ if (is_array($sourceattribute)) {
+ foreach ($this->values as $value => $require) {
+ if (count(array_intersect($require, $sourceattribute)) > 0) {
+ SimpleSAML_Logger::debug('AttributeValueMap - intersect match for ' . $value);
+ $targetvalues[] = $value;
+ }
+ }
+ }
+
+ if (count($targetvalues) > 0) {
+ if ($this->replace or !@is_array($attributes[$this->targetattribute])) {
+ $attributes[$this->targetattribute] = $targetvalues;
+ } else {
+ $attributes[$this->targetattribute] = array_unique(array_merge($attributes[$this->targetattribute], $targetvalues));
+ }
+ }
+
+ if (!$this->keep) {
+ unset($attributes[$this->sourceattribute]);
+ }
+ }
+}
diff --git a/tests/modules/core/lib/Auth/Process/AttributeValueMapTest.php b/tests/modules/core/lib/Auth/Process/AttributeValueMapTest.php
new file mode 100644
index 0000000..426f2ba
--- /dev/null
+++ b/tests/modules/core/lib/Auth/Process/AttributeValueMapTest.php
@@ -0,0 +1,196 @@
+<?php
+
+/**
+ * Test for the core:AttributeValueMap filter.
+ */
+class Test_Core_Auth_Process_AttributeValueMap extends PHPUnit_Framework_TestCase
+{
+
+ /**
+ * Helper function to run the filter with a given configuration.
+ *
+ * @param array $config The filter configuration.
+ * @param array $request The request state.
+ * @return array The state array after processing.
+ */
+ private static function processFilter(array $config, array $request) {
+ $filter = new sspmod_core_Auth_Process_AttributeValueMap($config, null);
+ $filter->process($request);
+ return $request;
+ }
+
+ /**
+ * Test the most basic functionality.
+ */
+ public function testBasic() {
+ $config = array(
+ 'sourceattribute' => 'memberOf',
+ 'targetattribute' => 'eduPersonAffiliation',
+ 'values' => array(
+ 'member' => array(
+ 'theGroup',
+ 'otherGroup',
+ ),
+ ),
+ );
+ $request = array(
+ 'Attributes' => array(
+ 'memberOf' => array('theGroup'),
+ ),
+ );
+ $result = self::processFilter($config, $request);
+ $attributes = $result['Attributes'];
+ $this->assertArrayNotHasKey('memberOf', $attributes);
+ $this->assertArrayHasKey('eduPersonAffiliation', $attributes);
+ $this->assertEquals($attributes['eduPersonAffiliation'], array('member'));
+ }
+
+ /**
+ * Test basic functionality, remove duplicates
+ */
+ public function testNoDuplicates() {
+ $config = array(
+ 'sourceattribute' => 'memberOf',
+ 'targetattribute' => 'eduPersonAffiliation',
+ 'values' => array(
+ 'member' => array(
+ 'theGroup',
+ 'otherGroup',
+ ),
+ ),
+ );
+ $request = array(
+ 'Attributes' => array(
+ 'memberOf' => array('theGroup', 'otherGroup'),
+ 'eduPersonAffiliation' => array('member', 'someValue'),
+ ),
+ );
+ $result = self::processFilter($config, $request);
+ $attributes = $result['Attributes'];
+ $this->assertArrayNotHasKey('memberOf', $attributes);
+ $this->assertArrayHasKey('eduPersonAffiliation', $attributes);
+ $this->assertEquals($attributes['eduPersonAffiliation'], array('member', 'someValue'));
+ }
+
+ /**
+ * Test the %replace functionality.
+ */
+ public function testReplace() {
+ $config = array(
+ 'sourceattribute' => 'memberOf',
+ 'targetattribute' => 'eduPersonAffiliation',
+ '%replace',
+ 'values' => array(
+ 'member' => array(
+ 'theGroup',
+ 'otherGroup',
+ ),
+ ),
+ );
+ $request = array(
+ 'Attributes' => array(
+ 'memberOf' => array('theGroup'),
+ 'eduPersonAffiliation' => array('someValue'),
+ ),
+ );
+ $result = self::processFilter($config, $request);
+ $attributes = $result['Attributes'];
+ $this->assertArrayNotHasKey('memberOf', $attributes);
+ $this->assertArrayHasKey('eduPersonAffiliation', $attributes);
+ $this->assertEquals($attributes['eduPersonAffiliation'], array('member'));
+ }
+
+ /**
+ * Test the %keep functionality.
+ */
+ public function testKeep() {
+ $config = array(
+ 'sourceattribute' => 'memberOf',
+ 'targetattribute' => 'eduPersonAffiliation',
+ '%keep',
+ 'values' => array(
+ 'member' => array(
+ 'theGroup',
+ 'otherGroup',
+ ),
+ ),
+ );
+ $request = array(
+ 'Attributes' => array(
+ 'memberOf' => array('theGroup'),
+ 'eduPersonAffiliation' => array('someValue'),
+ ),
+ );
+ $result = self::processFilter($config, $request);
+ $attributes = $result['Attributes'];
+ $this->assertArrayHasKey('memberOf', $attributes);
+ $this->assertArrayHasKey('eduPersonAffiliation', $attributes);
+ $this->assertEquals($attributes['eduPersonAffiliation'], array('someValue','member'));
+ }
+
+ /**
+ * Test unknown flag Exception
+ *
+ * @expectedException Exception
+ */
+ public function testUnknownFlag() {
+ $config = array(
+ '%test',
+ 'values' => array(
+ 'member' => array(
+ 'theGroup',
+ ),
+ ),
+ );
+ $request = array(
+ 'Attributes' => array(
+ 'memberOf' => array('theGroup'),
+ ),
+ );
+ $result = self::processFilter($config, $request);
+ }
+
+ /**
+ * Test missing Source attribute
+ *
+ * @expectedException Exception
+ */
+ public function testMissingSourceAttribute() {
+ $config = array(
+ 'targetattribute' => 'affiliation',
+ 'values' => array(
+ 'member' => array(
+ 'theGroup',
+ ),
+ ),
+ );
+ $request = array(
+ 'Attributes' => array(
+ 'memberOf' => array('theGroup'),
+ ),
+ );
+ $result = self::processFilter($config, $request);
+ }
+
+ /**
+ * Test missing Target attribute
+ *
+ * @expectedException Exception
+ */
+ public function testMissingTargetAttribute() {
+ $config = array(
+ 'sourceattribute' => 'memberOf',
+ 'values' => array(
+ 'member' => array(
+ 'theGroup',
+ ),
+ ),
+ );
+ $request = array(
+ 'Attributes' => array(
+ 'memberOf' => array('theGroup'),
+ ),
+ );
+ $result = self::processFilter($config, $request);
+ }
+}