-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathFilterAttributes.php
More file actions
179 lines (164 loc) · 6.88 KB
/
FilterAttributes.php
File metadata and controls
179 lines (164 loc) · 6.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
<?php
/**
* Filter to remove
* * all attributes if there is no `shibmd:Scope` value for the IdP
* * attribute values which are not properly scoped
* * configured scopeAttribute if it doesn't match against a value from `shibmd:Scope`.
*
* Note:
* * regexp in scope values are not supported.
* * Configured attribute names MUST match with names in attributemaps. It is case-sensitive.
*
* @author Adam Lantos NIIF / Hungarnet
* @author Gyula Szabo NIIF / Hungarnet
* @author Tamas Frank NIIF / Hungarnet
*/
class sspmod_attributescope_Auth_Process_FilterAttributes extends SimpleSAML_Auth_ProcessingFilter
{
private $attributesWithScope = array(
'eduPersonPrincipalName',
'eduPersonScopedAffiliation',
);
private $scopeAttributes = array(
'schacHomeOrganization',
);
private $ignoreCheckForEntities = array();
private $attributesWithScopeSuffix = array();
private $ignoreCase = false;
public function __construct($config, $reserved)
{
parent::__construct($config, $reserved);
if (array_key_exists('attributesWithScope', $config)) {
$this->attributesWithScope = $config['attributesWithScope'];
}
if (array_key_exists('scopeAttributes', $config)) {
$this->scopeAttributes = $config['scopeAttributes'];
}
if (array_key_exists('ignoreCheckForEntities', $config)) {
$this->ignoreCheckForEntities = $config['ignoreCheckForEntities'];
}
if (array_key_exists('attributesWithScopeSuffix', $config)) {
$this->attributesWithScopeSuffix = $config['attributesWithScopeSuffix'];
}
if (array_key_exists('ignoreCase', $config)) {
$this->ignoreCase = $config['ignoreCase'];
}
}
/**
* Apply filter.
*
* @param array &$request the current request
*/
public function process(&$request)
{
$src = $request['Source'];
if (isset($src['entityid']) && in_array($src['entityid'], $this->ignoreCheckForEntities, true)) {
SimpleSAML_Logger::debug('Ignoring scope checking for assertions from ' . $src['entityid']);
return;
}
$noscope = false;
if (!isset($src['scope']) ||
!is_array($src['scope']) ||
!count($src['scope'])) {
SimpleSAML_Logger::warning('No scope extension in IdP metadata, all scoped attributes are filtered out!');
$noscope = true;
}
$scopes = $noscope ? array() : $src['scope'];
foreach ($this->attributesWithScope as $attributesWithScope) {
if (!isset($request['Attributes'][$attributesWithScope])) {
continue;
}
if ($noscope) {
SimpleSAML_Logger::info('Attribute '.$attributesWithScope.' is filtered out due to missing scope information in IdP metadata.');
unset($request['Attributes'][$attributesWithScope]);
continue;
}
$values = $request['Attributes'][$attributesWithScope];
$newValues = array();
foreach ($values as $value) {
if ($this->isProperlyScoped($value, $scopes)) {
$newValues[] = $value;
} else {
SimpleSAML_Logger::warning('Attribute value ('.$value.') is removed by attributescope check.');
}
}
if (count($newValues)) {
$request['Attributes'][$attributesWithScope] = $newValues;
} else {
unset($request['Attributes'][$attributesWithScope]);
}
}
// Filter out scopeAttributes if the value does not match any scope values
foreach ($this->scopeAttributes as $scopeAttribute) {
if (array_key_exists($scopeAttribute, $request['Attributes'])) {
if (count($request['Attributes'][$scopeAttribute]) != 1) {
SimpleSAML_Logger::warning('$scopeAttribute (' . $scopeAttribute . ') must be single valued. Filtering out.');
unset($request['Attributes'][$scopeAttribute]);
} elseif (!in_array($request['Attributes'][$scopeAttribute][0], $scopes)) {
SimpleSAML_Logger::warning('Scope attribute (' . $scopeAttribute . ') does not match metadata. Filtering out.');
unset($request['Attributes'][$scopeAttribute]);
}
}
}
foreach ($this->attributesWithScopeSuffix as $attributeWithSuffix) {
if (!isset($request['Attributes'][$attributeWithSuffix])) {
continue;
}
if ($noscope) {
SimpleSAML_Logger::info('Attribute '.$attributeWithSuffix.' is filtered out due to missing scope information in IdP metadata.');
unset($request['Attributes'][$attributeWithSuffix]);
continue;
}
$values = $request['Attributes'][$attributeWithSuffix];
$newValues = array();
foreach ($values as $value) {
if ($this->isProperlySuffixed($value, $scopes)) {
$newValues[] = $value;
} else {
SimpleSAML_Logger::warning('Attribute value ('.$value.') is removed by attributeWithScopeSuffix check.');
}
}
if (count($newValues)) {
$request['Attributes'][$attributeWithSuffix] = $newValues;
} else {
unset($request['Attributes'][$attributeWithSuffix]);
}
}
}
/**
* Determines whether an attribute value is properly scoped.
*
* @param string $value The attribute value to check
* @param array $scopes The array of scopes for the Idp
*
* @return bool true if properly scoped
*/
private function isProperlyScoped($value, $scopes)
{
foreach ($scopes as $scope) {
$preg = '/^[^@]+@'.preg_quote($scope).'$/' . ($this->ignoreCase ? 'i' : '');
if (preg_match($preg, $value) == 1) {
return true;
}
}
}
/**
* Determines whether an attribute value is properly suffixed with the scope.
* @ and (literal) . are used for suffix boundries
*
* @param string $value The attribute value to check
* @param array $scopes The array of scopes for the IdP
*
* @return bool true if attribute is suffixed with a scope
*/
private function isProperlySuffixed($value, $scopes)
{
foreach ($scopes as $scope) {
$scopeRegex = '/^[^@]+@(.*\.)?'.preg_quote($scope).'$/' . ($this->ignoreCase ? 'i' : '');
$subdomainRegex = '/^([^@]*\.)?'.preg_quote($scope).'$/' . ($this->ignoreCase ? 'i' : '');
if (preg_match($subdomainRegex, $value) === 1 || preg_match($scopeRegex, $value) === 1) {
return true;
}
}
}
}