Skip to content

Commit 33ee3ae

Browse files
Add regular expression support to AttributeValueMap authproc filter (#2558)
1 parent 5c1986a commit 33ee3ae

3 files changed

Lines changed: 132 additions & 2 deletions

File tree

modules/core/docs/authproc_attributevaluemap.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ Filter that creates a target attribute based on one or more value(s) in source a
55
Besides the mapping of source values to target values, the filter has the following options:
66

77
* `%replace` can be used to replace all existing values in target with new ones (any existing values will be lost)
8-
* `%keep` can be used to keep the source attribute, otherwise it will be removed.
8+
* `%keep` can be used to keep the source attribute, otherwise it will be removed (regardless of whether there is a match or not).
9+
* `%regex` can be used to evaluate the values as regular expressions instead of plain strings.
910

1011
**Examples**:
1112

@@ -84,3 +85,25 @@ Replace any existing `affiliation` attribute values and keep the `groups` attrib
8485
],
8586
],
8687
],
88+
89+
## Regular expressions
90+
91+
Will add eduPersonAffiliation containing value `student` if the `memberOf` attribute contains
92+
some value matching `/^cn=student,o=[a-z]+,o=organization,dc=org`
93+
(eg. `cn=student,o=some,o=organization,dc=org`).
94+
The `memberOf` attribute will be removed (use `%keep`, to keep it) and existing values in
95+
`eduPersonAffiliation` will be merged (use `%replace` to replace them).
96+
97+
'authproc' => [
98+
50 => [
99+
'class' => 'core:AttributeValueMap',
100+
'sourceattribute' => 'memberOf',
101+
'targetattribute' => 'eduPersonAffiliation',
102+
'%regex',
103+
'values' => [
104+
'student' => [
105+
'/^cn=student,o=[a-z]+,o=organization,dc=org$/',
106+
],
107+
],
108+
],
109+
],

modules/core/src/Auth/Process/AttributeValueMap.php

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ class AttributeValueMap extends Auth\ProcessingFilter
4646
*/
4747
private bool $replace = false;
4848

49+
/**
50+
* Whether $sourceattribute values should be treated as regular expressions or not.
51+
* @var bool
52+
*/
53+
private bool $regex = false;
54+
4955

5056
/**
5157
* Initialize the filter.
@@ -66,6 +72,8 @@ public function __construct(array &$config, $reserved)
6672
$this->replace = true;
6773
} elseif ($value === '%keep') {
6874
$this->keep = true;
75+
} elseif ($value === '%regex') {
76+
$this->regex = true;
6977
} else {
7078
// unknown configuration option, log it and ignore the error
7179
Logger::warning(
@@ -129,7 +137,19 @@ public function process(array &$state): void
129137
if (!is_array($values)) {
130138
$values = [$values];
131139
}
132-
if (count(array_intersect($values, $sourceattribute)) > 0) {
140+
if ($this->regex) {
141+
foreach ($sourceattribute as $sourcevalue) {
142+
foreach ($values as $pattern) {
143+
if (preg_match($pattern, $sourcevalue) === 1) {
144+
Logger::debug("AttributeValueMap: regex match for '$value'");
145+
$targetvalues[] = $value;
146+
// no need to check other patterns for this sourceattribute value
147+
break 2;
148+
}
149+
}
150+
}
151+
continue;
152+
} elseif (count(array_intersect($values, $sourceattribute)) > 0) {
133153
Logger::debug("AttributeValueMap: intersect match for '$value'");
134154
$targetvalues[] = $value;
135155
}

tests/modules/core/src/Auth/Process/AttributeValueMapTest.php

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,34 @@ public function testBasic(): void
5959
}
6060

6161

62+
/**
63+
* The most basic test of no match.
64+
*
65+
* @throws \SimpleSAML\Error\Exception
66+
*/
67+
public function testBasicNoMatch(): void
68+
{
69+
$config = [
70+
'sourceattribute' => 'memberOf',
71+
'targetattribute' => 'eduPersonAffiliation',
72+
'values' => [
73+
'member' => [
74+
'nomatch',
75+
],
76+
],
77+
];
78+
$request = [
79+
'Attributes' => [
80+
'memberOf' => ['theGroup'],
81+
],
82+
];
83+
$result = self::processFilter($config, $request);
84+
$attributes = $result['Attributes'];
85+
$this->assertArrayNotHasKey('memberOf', $attributes);
86+
$this->assertArrayNotHasKey('eduPersonAffiliation', $attributes);
87+
}
88+
89+
6290
/**
6391
* Test basic functionality, remove duplicates
6492
*
@@ -225,4 +253,63 @@ public function testMissingTargetAttribute(): void
225253
];
226254
self::processFilter($config, $request);
227255
}
256+
257+
258+
/**
259+
* Basic regular expression functionality test.
260+
*
261+
* @throws \SimpleSAML\Error\Exception
262+
*/
263+
public function testBasicRegex(): void
264+
{
265+
$config = [
266+
'sourceattribute' => 'memberOf',
267+
'targetattribute' => 'eduPersonAffiliation',
268+
'%regex',
269+
'values' => [
270+
'member' => [
271+
'/^the/',
272+
],
273+
],
274+
];
275+
$request = [
276+
'Attributes' => [
277+
'memberOf' => ['theGroup'],
278+
],
279+
];
280+
$result = self::processFilter($config, $request);
281+
$attributes = $result['Attributes'];
282+
$this->assertArrayNotHasKey('memberOf', $attributes);
283+
$this->assertArrayHasKey('eduPersonAffiliation', $attributes);
284+
$this->assertEquals($attributes['eduPersonAffiliation'], ['member']);
285+
}
286+
287+
288+
/**
289+
* Basic regular expression functionality test of a "no match".
290+
*
291+
* @throws \SimpleSAML\Error\Exception
292+
*/
293+
public function testBasicRegexNoMatch(): void
294+
{
295+
$config = [
296+
'sourceattribute' => 'memberOf',
297+
'targetattribute' => 'eduPersonAffiliation',
298+
'%regex',
299+
'values' => [
300+
'member' => [
301+
'/^nomatch$/',
302+
],
303+
],
304+
];
305+
$request = [
306+
'Attributes' => [
307+
'memberOf' => ['theGroup'],
308+
],
309+
];
310+
$result = self::processFilter($config, $request);
311+
$attributes = $result['Attributes'];
312+
$this->assertArrayNotHasKey('memberOf', $attributes);
313+
$this->assertArrayNotHasKey('eduPersonAffiliation', $attributes);
314+
}
228315
}

0 commit comments

Comments
 (0)