Skip to content
Open
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
26 changes: 26 additions & 0 deletions modules/core/docs/authproc_attributeadd.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ Filter that adds attributes to the user.
If the attribute already exists, the values added will be merged into a multi-valued attribute.
If you instead want to replace the existing attribute, you may add the `%replace` option.

If you want to only add the attribute if another attribute (or attributes) already exist, you can
specify the optional `%if_attr_exists` (for plain strings) or `%if_attr_regex_matches`
(for regular expressions). Both can be specified as either a single value or an array of values.

Examples
--------

Expand Down Expand Up @@ -46,3 +50,25 @@ Replace an existing attributes:
'uid' => ['guest'],
],
],

Add a single-valued attribute if at least one of two existing attributes exist:

'authproc' => [
50 => [
'class' => 'core:AttributeAdd',
'%if_attr_exists' => ['studentId', 'staffId'],
'internalUser' => ['true'],
],
],

Add a single-valued attribute if a regular expression matches an existing attribute. In this case,
if there is an existing attribute where the attribute name starts with "graduateOf", then add a
new "hasGraduated" attribute:

'authproc' => [
50 => [
'class' => 'core:AttributeAdd',
'%if_attr_regex_matches' => '/^graduateOf/',
'hasGraduated' => ['true'],
],
],
48 changes: 48 additions & 0 deletions modules/core/src/Auth/Process/AttributeAdd.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ class AttributeAdd extends Auth\ProcessingFilter
*/
private bool $replace = false;

/**
* Flag which indicates only to add the new attribute if one of this list of attributes already exists.
* @var array
*/
private array $if_attr_exists = [];

/**
* Flag which indicates only to add the new attribute if one of the regular expressions in this list
* matches one of the existing attributes.
* @var array
*/
private array $if_attr_regex_matches = [];

/**
* Attributes which should be added/appended.
*
Expand Down Expand Up @@ -50,6 +63,21 @@ public function __construct(array &$config, $reserved)
throw new Exception('Unknown flag: ' . var_export($values, true));
}
continue;
} elseif (str_starts_with($name, "%")) {
if ($name === '%if_attr_exists') {
if (!is_array($values)) {
$values = [$values];
}
$this->if_attr_exists = $values;
} elseif ($name === '%if_attr_regex_matches') {
if (!is_array($values)) {
$values = [$values];
}
$this->if_attr_regex_matches = $values;
} else {
throw new Exception('Unknown option: ' . var_export($name, true));
}
continue;
}

if (!is_array($values)) {
Expand Down Expand Up @@ -81,6 +109,26 @@ public function process(array &$state): void

$attributes = &$state['Attributes'];

$shouldAdd = empty($this->if_attr_exists) && empty($this->if_attr_regex_matches);
foreach ($this->if_attr_exists as $attrName) {
if (array_key_exists($attrName, $attributes)) {
$shouldAdd = true;
break;
}
}
foreach ($this->if_attr_regex_matches as $regex) {
foreach (array_keys($attributes) as $attrName) {
if (preg_match($regex, $attrName) === 1) {
$shouldAdd = true;
break 2;
}
}
}

if (!$shouldAdd) {
return;
}

foreach ($this->attributes as $name => $values) {
if ($this->replace === true || !array_key_exists($name, $attributes)) {
$attributes[$name] = $values;
Expand Down
90 changes: 90 additions & 0 deletions tests/modules/core/src/Auth/Process/AttributeAddTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -187,4 +187,94 @@ public function testWrongAttributeValue(): void
];
self::processFilter($config, $request);
}


/**
* Test the most basic functionality.
*/
public function testBasicAttrExists(): void
{
$config = [
'%if_attr_exists' => ['memberOf'],
'test' => ['value1', 'value2'],
];
$request = [
'Attributes' => [
'memberOf' => ['theGroup'],
],
];
$result = self::processFilter($config, $request);
$attributes = $result['Attributes'];
$this->assertArrayHasKey('test', $attributes);
$this->assertArrayHasKey('memberOf', $attributes);
$this->assertEquals($attributes['test'], ['value1', 'value2']);
$this->assertEquals($attributes['memberOf'], ['theGroup']);
}


/**
* Test the most basic functionality.
*/
public function testBasicAttrExistsFail(): void
{
$config = [
'%if_attr_exists' => ['someOtherAttr'],
'test' => ['value1', 'value2'],
];
$request = [
'Attributes' => [
'memberOf' => ['theGroup'],
],
];
$result = self::processFilter($config, $request);
$attributes = $result['Attributes'];
$this->assertArrayNotHasKey('test', $attributes);
$this->assertArrayHasKey('memberOf', $attributes);
$this->assertEquals($attributes['memberOf'], ['theGroup']);
}


/**
* Test the most basic functionality.
*/
public function testBasicRegexMatches(): void
{
$config = [
'%if_attr_regex_matches' => ['/^member/'],
'test' => ['value1', 'value2'],
];
$request = [
'Attributes' => [
'memberOf' => ['theGroup'],
],
];
$result = self::processFilter($config, $request);
$attributes = $result['Attributes'];
$this->assertArrayHasKey('test', $attributes);
$this->assertArrayHasKey('memberOf', $attributes);
$this->assertEquals($attributes['test'], ['value1', 'value2']);
$this->assertEquals($attributes['memberOf'], ['theGroup']);
}


/**
* Test the most basic functionality.
*/
public function testBasicRegexMatchesFail(): void
{
$config = [
'%if_attr_regex_matches' => ['/^someOther/'],
'test' => ['value1', 'value2'],
];
$request = [
'Attributes' => [
'memberOf' => ['theGroup'],
],
];
$result = self::processFilter($config, $request);
$attributes = $result['Attributes'];
$this->assertArrayNotHasKey('test', $attributes);
$this->assertArrayHasKey('memberOf', $attributes);
$this->assertEquals($attributes['memberOf'], ['theGroup']);
}
}
Loading