diff --git a/modules/authorize/docs/authorize.md b/modules/authorize/docs/authorize.md index 36ac30c90b9a776e19459706c221bea9cb6bf1a5..4521e3015e06255ea42a45d106f1b3c5d4579a49 100644 --- a/modules/authorize/docs/authorize.md +++ b/modules/authorize/docs/authorize.md @@ -50,14 +50,17 @@ Note: If regex is enabled, you must use the preg_match format, i.e. you have to (as far as I know), you have to close the browser. ### Examples ### -To use this filter configure it in `config/config.php`: +To use this filter configure it in `config/config.php`. +For unstructured attributes use `^` and `$` to anchor your regex as necessary: + ```php 'authproc.sp' => [ 60 => [ 'class' => 'authorize:Authorize', 'uid' => [ - '/.*@example.com/', - '/(user1|user2|user3)@example.edu/', + '/^.*@example.com$/', + // Use anchors to prevent matching 'wronguser1@exampled.edu.attacker.com' + '/^(user1|user2|user3)@example.edu$/', ], 'schacUserStatus' => '@urn:mace:terena.org:userStatus:' . 'example.org:service:active.*@', @@ -72,10 +75,10 @@ An alternate way of using this filter is to deny certain users. Or even use mult 'authproc.sp' => [ 60 => array[ 'class' => 'authorize:Authorize', - 'deny' => TRUE, + 'deny' => true, 'uid' => [ - '/.*@students.example.edu/', - '/(stu1|stu2|stu3)@example.edu/', + '/.*@students.example.edu$/', + '/^(stu1|stu2|stu3)@example.edu$/', ] ] ] @@ -89,7 +92,7 @@ Additionally, some helpful instructions are shown. 'authproc.sp' => [ 60 => [ 'class' => 'authorize:Authorize', - 'regex' => FALSE, + 'regex' => false, 'group' => [ 'CN=SimpleSAML Students,CN=Users,DC=example,DC=edu', 'CN=All Teachers,OU=Staff,DC=example,DC=edu', diff --git a/tests/modules/authorize/lib/Auth/Process/AuthorizeTest.php b/tests/modules/authorize/lib/Auth/Process/AuthorizeTest.php new file mode 100644 index 0000000000000000000000000000000000000000..62b8644bf7a0ae9b81114a86c12fc193310ac340 --- /dev/null +++ b/tests/modules/authorize/lib/Auth/Process/AuthorizeTest.php @@ -0,0 +1,175 @@ +<?php +/** + * Test for the authorize:Authorize authproc filter. + */ + +namespace SimpleSAML\Test\Module\consent\Auth\Process; + +use PHPUnit\Framework\TestCase; +use SimpleSAML\Module\authorize\Auth\Process\Authorize; +use SimpleSAML\Utils\Attributes; + +class AuthorizeTest extends TestCase +{ + + /** + * Helper function to run the filter with a given configuration. + * + * @param array $config The filter configuration. + * @param array $request The request state. + * @return array The state array after processing. + */ + private function processFilter(array $config, array $request) + { + $filter = new TestableAuthorize($config, null); + $filter->process($request); + return $request; + } + + /** + * Test that having a matching attribute grants access + * @dataProvider allowScenarioProvider + * @param array $userAttributes The attributes to test + * @param bool $isAuthorized Should the user be authorized + */ + public function testAllowScenarios($userAttributes, $isAuthorized) + { + $userAttributes = Attributes::normalizeAttributesArray($userAttributes); + $config = [ + 'uid' => [ + '/^.*@example.com$/', + '/^(user1|user2|user3)@example.edu$/', + ], + 'schacUserStatus' => '@urn:mace:terena.org:userStatus:example.org:service:active.*@', + ]; + + $resultState = $this->processFilter($config, ['Attributes' => $userAttributes]); + + $resultAuthorized = isset($resultState['NOT_AUTHORIZED']) ? false : true; + $this->assertEquals($isAuthorized, $resultAuthorized); + } + + public function allowScenarioProvider() + { + return [ + // Should be allowed + [['uid' => 'anything@example.com'], true], + [['uid' => 'user2@example.edu'], true], + [['schacUserStatus' => 'urn:mace:terena.org:userStatus:example.org:service:active.my.service'], true], + [ + [ + 'uid' => ['wrongValue', 'user2@example.edu', 'wrongValue2'], + 'schacUserStatus' => 'incorrectstatus' + ], + true + ], + + //Should be denied + [['wrongAttributes' => ['abc']], false], + [ + [ + 'uid' => [ + 'anything@example.com.wrong', + 'wronguser@example.edu', + 'user2@example.edu.wrong', + 'prefixuser2@example.edu' + ] + ], + false + ], + ]; + } + + /** + * Test that having a matching attribute prevents access + * @dataProvider invertScenarioProvider + * @param array $userAttributes The attributes to test + * @param bool $isAuthorized Should the user be authorized + */ + public function testInvertAllowScenarios($userAttributes, $isAuthorized) + { + $userAttributes = Attributes::normalizeAttributesArray($userAttributes); + $config = [ + 'deny' => true, + 'uid' => [ + '/.*@students.example.edu$/', + '/^(stu1|stu2|stu3)@example.edu$/', + ], + 'schacUserStatus' => '@urn:mace:terena.org:userStatus:example.org:service:blocked.*@', + ]; + + $resultState = $this->processFilter($config, ['Attributes' => $userAttributes]); + + $resultAuthorized = isset($resultState['NOT_AUTHORIZED']) ? false : true; + $this->assertEquals($isAuthorized, $resultAuthorized); + } + + public function invertScenarioProvider() + { + return [ + // Should be allowed + [['noMatch' => 'abc'], true], + [['uid' => 'anything@example.edu'], true], + + //Should be denied + [['uid' => 'anything@students.example.edu'], false], + [['uid' => 'stu3@example.edu'], false], + [['schacUserStatus' => 'urn:mace:terena.org:userStatus:example.org:service:blocked'], false], + // Matching any of the attributes results in denial + [ + [ + 'uid' => ['noMatch', 'abc@students.example.edu', 'noMatch2'], + 'schacUserStatus' => 'noMatch' + ], + false + ], + ]; + } + + /** + * Test that having a matching attribute prevents access + * @dataProvider noregexScenarioProvider + * @param array $userAttributes The attributes to test + * @param bool $isAuthorized Should the user be authorized + */ + public function testDisableRegex($userAttributes, $isAuthorized) + { + $userAttributes = Attributes::normalizeAttributesArray($userAttributes); + $config = [ + 'regex' => false, + 'group' => [ + 'CN=SimpleSAML Students,CN=Users,DC=example,DC=edu', + 'CN=All Teachers,OU=Staff,DC=example,DC=edu', + ], + ]; + + $resultState = $this->processFilter($config, ['Attributes' => $userAttributes]); + + $resultAuthorized = isset($resultState['NOT_AUTHORIZED']) ? false : true; + $this->assertEquals($isAuthorized, $resultAuthorized); + } + + public function noregexScenarioProvider() + { + return [ + // Should be allowed + [['group' => 'CN=SimpleSAML Students,CN=Users,DC=example,DC=edu'], true], + + //Should be denied + [['wrongAttribute' => 'CN=SimpleSAML Students,CN=Users,DC=example,DC=edu'], false], + [['group' => 'CN=wrongCN=SimpleSAML Students,CN=Users,DC=example,DC=edu'], false], + ]; + } +} +// phpcs:ignore +class TestableAuthorize extends Authorize +{ + /** + * Override the redirect behavior since its difficult to test + * @param array $request the state + */ + protected function unauthorized(&$request) + { + $request['NOT_AUTHORIZED'] = true; + } +}