Skip to content
This repository was archived by the owner on Sep 19, 2022. It is now read-only.

Commit db69b7a

Browse files
committed
allow multiple filters in ProxyFilter
1 parent a6be1a8 commit db69b7a

File tree

2 files changed

+134
-20
lines changed

2 files changed

+134
-20
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file.
1010
- Use object `Configuration` for getting base module configuration
1111
- Add possibility to select mode(whitelist/blacklist) in ProxyFilter.php
1212
* The default option is blacklist
13+
- Allow call multiple ProcessFilter in one ProxyFilter module
1314

1415
#### Fixed
1516
- Fixed the width of showed tagged idps in case the count of idps is equal to (x * 3) + 1

lib/Auth/Process/ProxyFilter.php

Lines changed: 133 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@
99
/**
1010
* Class sspmod_perun_Auth_Process_ProxyFilter
1111
*
12-
* This filter allows to disable/enable nested filter for particular SP
12+
* This filter allows to disable/enable nested filters for particular SP
1313
* or for users with one of (black/white)listed attribute values.
14-
* Based on the mode of operation, the nested filter
15-
* IS (whitelist) or IS NOT (blacklist) run when any of the attribute values matches.
14+
* Based on the mode of operation, the nested filters
15+
* ARE (whitelist) or ARE NOT (blacklist) run when any of the attribute values matches.
1616
* SPs are defined by theirs entityID in property 'filterSPs'.
1717
* User attributes are defined as a map 'attrName'=>['value1','value2']
1818
* in property 'filterAttributes'.
19-
* Nested filter is defined in property config as regular filter.
19+
* Nested filters are defined in the authproc property in the same format as in config.
20+
* If only one filter is needed, it can be specified in the config property.
2021
*
2122
* example usage:
2223
*
@@ -36,9 +37,15 @@
3637
* 'class' => 'perun:ProxyFilter',
3738
* 'mode' => 'whitelist',
3839
* 'filterSPs' => ['enableSpEntityId01', 'enableSpEntityId02'],
39-
* 'config' => [
40-
* 'class' => 'perun:NestedFilter',
41-
* // ...
40+
* 'authproc' => [
41+
* [
42+
* 'class' => 'perun:NestedFilter1',
43+
* // ...
44+
* ],
45+
* [
46+
* 'class' => 'perun:NestedFilter2',
47+
* // ...
48+
* ],
4249
* ],
4350
* ],
4451
*
@@ -53,8 +60,8 @@ class ProxyFilter extends \SimpleSAML\Auth\ProcessingFilter
5360
self::MODE_WHITELIST,
5461
];
5562

56-
private $config;
57-
private $nestedClass;
63+
private $authproc;
64+
private $nestedClasses;
5865
private $filterSPs;
5966
private $filterAttributes;
6067
private $mode;
@@ -65,13 +72,20 @@ public function __construct($config, $reserved)
6572
parent::__construct($config, $reserved);
6673

6774
$conf = Configuration::loadFromArray($config);
68-
$this->config = $conf->getArray('config', []);
69-
$this->nestedClass = Configuration::loadFromArray($this->config)->getString('class');
70-
unset($this->config['class']);
7175
$this->filterSPs = $conf->getArray('filterSPs', []);
7276
$this->filterAttributes = $conf->getArray('filterAttributes', []);
7377
$this->mode = $conf->getValueValidate('mode', self::MODES, self::MODE_BLACKLIST);
7478

79+
$this->authproc = $conf->getArray('authproc', []);
80+
$this->authproc[] = $conf->getArray('config', []);
81+
$this->authproc = array_filter($this->authproc);
82+
$this->nestedClasses = implode(',', array_map(
83+
function ($config) {
84+
return is_string($config) ? $config : $config['class'];
85+
},
86+
$this->authproc
87+
));
88+
7589
$this->reserved = (array)$reserved;
7690
}
7791

@@ -86,7 +100,15 @@ public function process(&$request)
86100
}
87101

88102
if ($shouldRun) {
89-
$this->runAuthProcFilter($request);
103+
$this->processState($request);
104+
} elseif ($this->mode === self::MODE_WHITELIST) {
105+
Logger::info(
106+
sprintf(
107+
'perun.ProxyFilter: Not running filter %s for SP %s',
108+
$this->nestedClasses,
109+
$request['Destination']['entityid']
110+
)
111+
);
90112
}
91113
}
92114

@@ -99,7 +121,7 @@ private function shouldRunForSP($currentSp, $default)
99121
sprintf(
100122
'perun.ProxyFilter: %s filter %s for SP %s',
101123
$shouldRun ? 'Running' : 'Filtering out',
102-
$this->nestedClass,
124+
$this->nestedClasses,
103125
$currentSp
104126
)
105127
);
@@ -120,7 +142,7 @@ private function shouldRunForAttribute($attributes, $default)
120142
sprintf(
121143
'perun.ProxyFilter: %s filter %s because %s contains %s',
122144
$shouldRun ? 'Running' : 'Filtering out',
123-
$this->nestedClass,
145+
$this->nestedClasses,
124146
$attr,
125147
$value
126148
)
@@ -133,11 +155,102 @@ private function shouldRunForAttribute($attributes, $default)
133155
return $default;
134156
}
135157

136-
private function runAuthProcFilter(&$request)
158+
/**
159+
* Parse an array of authentication processing filters.
160+
* @see https://github.com/simplesamlphp/simplesamlphp/blob/simplesamlphp-1.17/lib/SimpleSAML/Auth/ProcessingChain.php
161+
*
162+
* @param array $filterSrc Array with filter configuration.
163+
* @return array Array of ProcessingFilter objects.
164+
*/
165+
private static function parseFilterList($filterSrc)
166+
{
167+
assert(is_array($filterSrc));
168+
169+
$parsedFilters = [];
170+
171+
foreach ($filterSrc as $priority => $filter) {
172+
if (is_string($filter)) {
173+
$filter = ['class' => $filter];
174+
}
175+
176+
if (!is_array($filter)) {
177+
throw new \Exception('Invalid authentication processing filter configuration: '.
178+
'One of the filters wasn\'t a string or an array.');
179+
}
180+
181+
$parsedFilters[] = self::parseFilter($filter, $priority);
182+
}
183+
184+
return $parsedFilters;
185+
}
186+
187+
/**
188+
* Parse an authentication processing filter.
189+
* @see https://github.com/simplesamlphp/simplesamlphp/blob/simplesamlphp-1.17/lib/SimpleSAML/Auth/ProcessingChain.php
190+
*
191+
* @param array $config Array with the authentication processing filter configuration.
192+
* @param int $priority The priority of the current filter, (not included in the filter
193+
* definition.)
194+
* @return ProcessingFilter The parsed filter.
195+
*/
196+
private static function parseFilter($config, $priority)
197+
{
198+
assert(is_array($config));
199+
200+
if (!array_key_exists('class', $config)) {
201+
throw new \Exception('Authentication processing filter without name given.');
202+
}
203+
204+
$className = \SimpleSAML\Module::resolveClass(
205+
$config['class'],
206+
'Auth\Process',
207+
'\SimpleSAML\Auth\ProcessingFilter'
208+
);
209+
$config['%priority'] = $priority;
210+
unset($config['class']);
211+
return new $className($config, null);
212+
}
213+
214+
/**
215+
* Process the given state.
216+
* @see https://github.com/simplesamlphp/simplesamlphp/blob/simplesamlphp-1.17/lib/SimpleSAML/Auth/ProcessingChain.php
217+
*
218+
* This function will only return if processing completes. If processing requires showing
219+
* a page to the user, we will not be able to return from this function. There are two ways
220+
* this can be handled:
221+
* - Redirect to a URL: We will redirect to the URL set in $state['ReturnURL'].
222+
* - Call a function: We will call the function set in $state['ReturnCall'].
223+
*
224+
* If an exception is thrown during processing, it should be handled by the caller of
225+
* this function. If the user has redirected to a different page, the exception will be
226+
* returned through the exception handler defined on the state array. See
227+
* State for more information.
228+
*
229+
* @see State
230+
* @see State::EXCEPTION_HANDLER_URL
231+
* @see State::EXCEPTION_HANDLER_FUNC
232+
*
233+
* @param array &$state The state we are processing.
234+
*/
235+
private function processState(&$state)
137236
{
138-
list($module, $simpleClass) = explode(':', $this->nestedClass);
139-
$className = '\SimpleSAML\Module\\' . $module . '\Auth\Process\\' . $simpleClass;
140-
$authFilter = new $className($this->config, $this->reserved);
141-
$authFilter->process($request);
237+
$filters = self::parseFilterList($this->authproc);
238+
try {
239+
while (count($filters) > 0) {
240+
$filter = array_shift($filters);
241+
$filter->process($state);
242+
}
243+
} catch (\SimpleSAML\Error\Exception $e) {
244+
// No need to convert the exception
245+
throw $e;
246+
} catch (\Exception $e) {
247+
/*
248+
* To be consistent with the exception we return after an redirect,
249+
* we convert this exception before returning it.
250+
*/
251+
throw new \SimpleSAML\Error\UnserializableException($e);
252+
}
253+
254+
// Completed
142255
}
143256
}

0 commit comments

Comments
 (0)