Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion modules/core/docs/authproc_attributevaluemap.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ Filter that creates a target attribute based on one or more value(s) in source a
Besides the mapping of source values to target values, the filter has the following options:

* `%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.
* `%keep` can be used to keep the source attribute, otherwise it will be removed (regardless of whether there is a match or not).
* `%regex` can be used to evaluate the values as regular expressions instead of plain strings.

**Examples**:

Expand Down Expand Up @@ -84,3 +85,25 @@ Replace any existing `affiliation` attribute values and keep the `groups` attrib
],
],
],

## Regular expressions

Will add eduPersonAffiliation containing value `student` if the `memberOf` attribute contains
some value matching `/^cn=student,o=[a-z]+,o=organization,dc=org`
(eg. `cn=student,o=some,o=organization,dc=org`).
The `memberOf` attribute will be removed (use `%keep`, to keep it) and existing values in
`eduPersonAffiliation` will be merged (use `%replace` to replace them).

'authproc' => [
50 => [
'class' => 'core:AttributeValueMap',
'sourceattribute' => 'memberOf',
'targetattribute' => 'eduPersonAffiliation',
'%regex',
'values' => [
'student' => [
'/^cn=student,o=[a-z]+,o=organization,dc=org$/',
],
],
],
],
22 changes: 21 additions & 1 deletion modules/core/src/Auth/Process/AttributeValueMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ class AttributeValueMap extends Auth\ProcessingFilter
*/
private bool $replace = false;

/**
* Whether $sourceattribute values should be treated as regular expressions or not.
* @var bool
*/
private bool $regex = false;


/**
* Initialize the filter.
Expand All @@ -66,6 +72,8 @@ public function __construct(array &$config, $reserved)
$this->replace = true;
} elseif ($value === '%keep') {
$this->keep = true;
} elseif ($value === '%regex') {
$this->regex = true;
} else {
// unknown configuration option, log it and ignore the error
Logger::warning(
Expand Down Expand Up @@ -129,7 +137,19 @@ public function process(array &$state): void
if (!is_array($values)) {
$values = [$values];
}
if (count(array_intersect($values, $sourceattribute)) > 0) {
if ($this->regex) {
foreach ($sourceattribute as $sourcevalue) {
foreach ($values as $pattern) {
if (preg_match($pattern, $sourcevalue) === 1) {
Logger::debug("AttributeValueMap: regex match for '$value'");
$targetvalues[] = $value;
// no need to check other patterns for this sourceattribute value
break 2;
}
}
}
continue;
} elseif (count(array_intersect($values, $sourceattribute)) > 0) {
Logger::debug("AttributeValueMap: intersect match for '$value'");
$targetvalues[] = $value;
}
Expand Down
87 changes: 87 additions & 0 deletions tests/modules/core/src/Auth/Process/AttributeValueMapTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,34 @@ public function testBasic(): void
}


/**
* The most basic test of no match.
*
* @throws \SimpleSAML\Error\Exception
*/
public function testBasicNoMatch(): void
{
$config = [
'sourceattribute' => 'memberOf',
'targetattribute' => 'eduPersonAffiliation',
'values' => [
'member' => [
'nomatch',
],
],
];
$request = [
'Attributes' => [
'memberOf' => ['theGroup'],
],
];
$result = self::processFilter($config, $request);
$attributes = $result['Attributes'];
$this->assertArrayNotHasKey('memberOf', $attributes);
$this->assertArrayNotHasKey('eduPersonAffiliation', $attributes);
}


/**
* Test basic functionality, remove duplicates
*
Expand Down Expand Up @@ -225,4 +253,63 @@ public function testMissingTargetAttribute(): void
];
self::processFilter($config, $request);
}


/**
* Basic regular expression functionality test.
*
* @throws \SimpleSAML\Error\Exception
*/
public function testBasicRegex(): void
{
$config = [
'sourceattribute' => 'memberOf',
'targetattribute' => 'eduPersonAffiliation',
'%regex',
'values' => [
'member' => [
'/^the/',
],
],
];
$request = [
'Attributes' => [
'memberOf' => ['theGroup'],
],
];
$result = self::processFilter($config, $request);
$attributes = $result['Attributes'];
$this->assertArrayNotHasKey('memberOf', $attributes);
$this->assertArrayHasKey('eduPersonAffiliation', $attributes);
$this->assertEquals($attributes['eduPersonAffiliation'], ['member']);
}


/**
* Basic regular expression functionality test of a "no match".
*
* @throws \SimpleSAML\Error\Exception
*/
public function testBasicRegexNoMatch(): void
{
$config = [
'sourceattribute' => 'memberOf',
'targetattribute' => 'eduPersonAffiliation',
'%regex',
'values' => [
'member' => [
'/^nomatch$/',
],
],
];
$request = [
'Attributes' => [
'memberOf' => ['theGroup'],
],
];
$result = self::processFilter($config, $request);
$attributes = $result['Attributes'];
$this->assertArrayNotHasKey('memberOf', $attributes);
$this->assertArrayNotHasKey('eduPersonAffiliation', $attributes);
}
}
Loading