diff --git a/.travis.yml b/.travis.yml index 4f67b010417a5eafb731118788272afe99ed384c..eadcb2accb218b4367130cf2777ed67e3d816694 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ php: - 5.6 - 7.0 - 7.1 +- 7.2 - hhvm matrix: allow_failures: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8a16e72314880d702ba0368ed4cfd9bb09d23881..d16eec4696968af1e18c93acdf4a5227584b1667 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,15 +1,11 @@ # Contribution guidelines +**SimpleSAMLphp welcomes all contributions**. It is impossible to make a product like this without the efforts of many people, so please don't be shy and share your help with us. Even the tiniest contribution can make a difference! -**SimpleSAMLphp welcomes all contributions**. It is impossible to make a product like this without the efforts of many -people, so please don't be shy and share your help with us. Even the tiniest contribution can make a difference! - -This guidelines briefly explain how to contribute to SimpleSAMLphp in an effective manner, making sure to keep high -quality standards and making it easier for your contributions to make through. +These guidelines briefly explain how to contribute to SimpleSAMLphp effectively and consistently, making sure to keep high quality standards and making it easier for you to contribute. <!-- {{TOC}} --> ## Team members - Currently, the core team members are: * Jaime Pérez Crespo, *main developer and release manager*, UNINETT <jaime.perez@uninett.no> @@ -17,83 +13,64 @@ Currently, the core team members are: * Andreas Åkre Solberg, *architect and original developer*, UNINETT <andreas.solberg@uninett.no> * Hanne Moa, *developer*, UNINETT <hanne.moa@uninett.no> -We've been lucky to have the help of many people through the years. SimpleSAMLphp wouldn't have reached so far without -them, and we want to thank them from here. Unfortunately, they are so many it is nearly impossible to mention all of -them. [Github can offer a good summary on who has contributed to the project](https://github.com/simplesamlphp/simplesamlphp/graphs/contributors?from=2007-09-09&to=2015-09-06&type=c). -Big thanks to you all! +We have been lucky enough to have so many people help us through the years. SimpleSAMLphp wouldn't have reached so far without them. We want to thank them from here, but unfortunately they are so many it is nearly impossible to mention all of them. [Here is a Github page that summarizes everyone's contributions](https://github.com/simplesamlphp/simplesamlphp/graphs/contributors?from=2007-09-09&to=2015-09-06&type=c). -## First things first +***Big thanks to you all!*** -Before embarking yourself in a contribution, please make sure you are familiar with the way SimpleSAMLphp is written, -the way it works, and what is required or not. +## First things first +Before embarking yourself in a contribution, please make sure you are familiar with the way SimpleSAMLphp is written, the way it works, and what is required or not. -* Make sure to read [the documentation](https://simplesamlphp.org/docs/stable/). If you use the search engine in the -website, please verify that you are reading the latest stable version. If you want to develop something, check [the -development branch of the documentation](https://simplesamlphp.org/docs/development/). +* Make sure to read [the documentation](https://simplesamlphp.org/docs/stable/). If you use the search engine in the website, please verify that you are reading the latest stable version. If you want to make a change, check [the development branch of the documentation](https://simplesamlphp.org/docs/development/). * If you have a question about **using SimpleSAMLphp**, please use [the mailing list](http://groups.google.com/group/simplesamlphp). * If you have a question about **developing SimpleSAMLphp**, please ask in the [development mailing list](http://groups.google.com/group/simplesamlphp-dev). -* If you think you have discovered a bug, please check the [issue tracker](https://github.com/simplesamlphp/simplesamlphp/issues) -and the [pull requests](https://github.com/simplesamlphp/simplesamlphp/pulls) to verify it hasn't been reported before. +* If you think you have discovered a bug, please check the [issue tracker](https://github.com/simplesamlphp/simplesamlphp/issues) and the [pull requests](https://github.com/simplesamlphp/simplesamlphp/pulls) to verify it hasn't been reported already. ## Contributing code - -New features are always welcome provided they will be useful to someone apart from yourself. Please take a look at the -[list of issues](https://github.com/simplesamlphp/simplesamlphp/issues) to see what people is demanding. Our -[roadmap](https://simplesamlphp.org/releaseplan) might also be a good place to start if you don't know exactly what -you can contribute with. - -When contributing your code, please make sure to: - -* Respect the coding standards. We try to comply with PHP's [PSR-2](http://www.php-fig.org/psr/psr-2/). Pay special -attention to: - * Lines should not be longer than 80 characters. - * Use **4 spaces** instead of tabs. - * Keep the keywords in **lowercase**, including `true`, `false` and `null`. - * Make sure your classes work with *autoloading*. - * Never include a trailing `?>` in your files. - * The first line of every file must be `<?php`. - * Use namespaces if you are adding new files. -* Do not include many changes in every commit. Commits should be focused and address one single problem or feature. By -having **multiple, small commits** instead of few large ones, it is easier to track what you are doing, revert changes -in case of an error and help you out if needed. +New features are always welcome, provided they will be useful to someone apart from yourself. Please take a look at the [list of issues](https://github.com/simplesamlphp/simplesamlphp/issues) to see what people are asking for. Our [roadmap](https://simplesamlphp.org/releaseplan) might also be a good place to start if you do not know exactly how you can contribute. + +When submitting a pull request, please make sure to account for: + +### Coding standards +* Respect the coding standards. We try to comply with PHP's [PSR-2](http://www.php-fig.org/psr/psr-2/). Pay special attention to the rules below: + * Lines should not be longer than 80 characters. + * Use **4 spaces** instead of tabs. + * Keep the keywords in **lowercase**, including `true`, `false` and `null`. + * Make sure your classes work with *autoloading*. + * Never include a trailing `?>` in your files. + * The first line of every file must be `<?php`. + * Use namespaces if you are adding new files. +* Do not include too many changes in every commit. Commits should be focused and address one single problem or feature. By having **multiple, small commits** instead of fewer large ones, it is easier to track what you are doing, revert changes in case of an error and help you make changes if needed. * Try to write clean **commit messages**, largely based on the [seven rules](http://chris.beams.io/posts/git-commit/): - * Write a **short** subject line, followed by a blank line and a longer explanation. - * Prefix the subject line with the component(s) changed, e.g. "docs: Update foo", or "SAML: Don't log bar twice", - or "tests: Add tests for quux". - * Explain **what and why** in the commit message, not just _how_. Things obvious now might become quite confusing - when rediscovered years later. -* **Be explicit**. Add comments. Use strict comparison operators like `===` and check for specific values when testing -conditions. -* **Keep things simple**. Avoid big functions, long nested loops or `if` statements. -* Include complete **phpdoc** documentation for every property and method you add. If you change a method or property, -make sure to update the existing *phpdoc* accordingly. Do not forget to document all parameters, returned values and -exceptions thrown. -* Try to keep **backwards-compatibility**. Code that breaks current configurations and installations is difficult to -deploy, and therefore we try to avoid it. -* Add **unit tests** to verify that your code not only works but also keeps working over time. When adding tests, keep -the same directory structure used for regular classes. Try to cover **all your code** with tests. The bigger the test -coverage, the more reliable and better our library is. Read our [guidelines](TESTING.md) to learn more about tests. -* Add proper **documentation** explaining your how to use your new feature or how your code changes things. -* Submit your code as a **pull request** in github, from a branch with a descriptive name in your own fork of the -repository. Add a meaningful, short title, and explain in detail what you did and why in the description of the *PR*. -Add instructions on how to test your code. We appreciate branch names like `feature/whatever-new-feature` for new -features and `bug/something-not-working` for bug fixes, but this is not carved in stone. - -Sometimes it can take a long time until we are able to process your pull requests. Don't get discouraged, we'll -eventually reach up to you. And remember that following this guidelines you are making it easier for us to analyze your -request so the process will be smoother and faster. We really appreciate you helping us out, not only with your code, -but also by following this guidelines. + * Write a **short** subject line, followed by a blank line and a longer explanation. + * Prefix the subject line with the component(s) changed, e.g. "docs: Update foo", or "SAML: Don't log bar twice", or "tests: Add tests for quux". + * Explain **what and why** in the commit message, not just _how_. Things that seem obvious now might become quite confusing when rediscovered years later. -## Reporting bugs +### Comments, comparisons, and simplicity +* Add comments that describe why/how your code works. +* Include complete **phpdoc** documentation for every property and method you add. If you change a method or property, make sure to update the existing *phpdoc* accordingly. Do not forget to document all parameters, returned values and exceptions thrown. +* Use strict comparison operators like `===` and check for specific values when writing tests. +* Avoid big functions, long nested loops or `if` statements. +* Try to keep **backwards-compatibility**. Code that breaks current configurations and installations is difficult to deploy, and therefore we try to avoid that as much as possible. + +### Unit tests +Add **unit tests** to verify that your code not only works but also keeps working over time. When adding tests, keep the same directory structure used for regular classes. Try to cover **all your code** with tests. The bigger the test coverage, the more reliable and better our library is. Read our [guidelines](TESTING.md) to learn more about tests. + +### Documentation +In order to keep this library user-friendly, we ask that you add proper **documentation** explaining how to use your new feature or how your code changes things. -Before reporting a bug, please make sure it is indeed a bug. Check [the documentation](https://simplesamlphp.org/docs/stable/) -to verify what the intended behaviour is. Review the [list of issues](https://github.com/simplesamlphp/simplesamlphp/issues) -and the [pull requests](https://github.com/simplesamlphp/simplesamlphp/pulls) to see if someone has already reported the -same issue. +### Pull requests +Please follow all instructions below: -Pull requests are definitely more appreciated than plain issue reports, as they are easier and faster to address, but -please, do not hesitate to open an issue if you don't have coding skills or just can't find the bug. It's better to have -just an issue report than nothing! +1. Submit your code as a **pull request** in github from a branch with a descriptive name in your own fork of the repository. +2. Add a meaningful, short title, and explain in detail what you did and why in the description of the *PR*. +3. Add instructions on how to test your code. We appreciate branch names like `feature/whatever-new-feature` for new features and `bug/something-not-working` for bug fixes, but this is not required. + +Sometimes it can take a long time before we are able to process your pull requests. Do not get discouraged, we will eventually reach your change. Remember that by following these guidelines, you are making it easier for us to analyze your request so the process will be smooth and fast. We really appreciate you helping us out, not only with your code, but also by following these guidelines. + +## Reporting bugs +Before reporting a bug, please make sure it is indeed a bug. Check [the documentation](https://simplesamlphp.org/docs/stable/) to verify what the intended behaviour is. Review the [issue tracker](https://github.com/simplesamlphp/simplesamlphp/issues) and the [pull requests](https://github.com/simplesamlphp/simplesamlphp/pulls) to see if someone has already reported the same issue. + +If you are able, a pull request is much more appreciated than just a new issue. If not, please do not hesitate to open one. It is better to have just an issue report than nothing! You can help us diagnose and fix bugs by asking and providing answers to the following questions: @@ -102,18 +79,12 @@ You can help us diagnose and fix bugs by asking and providing answers to the fol * Are the steps to reproduce the bug clear? If not, can you describe how you might reproduce it? * What tags should the bug have? * How critical is this bug? Does it impact a large amount of users? -* Is this a security issue? If so, how severe is it? How can an attacker exploit it? Read more about security issues in -the next section. +* Is this a security issue? If so, how severe is it? How can an attacker exploit it? Read more about security issues in the next section. ## Reporting vulnerabilities +In case you find a vulnerability in SimpleSAMLphp, or you want to confirm a possible security issue in the software, please get in touch with us through [UNINETT's CERT team](https://www.uninett.no/cert). Please use our PGP public key to encrypt any possibly sensitive data that you may need to submit. We will get back to you as soon as possible according to our working hours in Central European Time. -In case you find a vulnerability in SimpleSAMLphp, or you want to confirm a possible security issue in the software, please -get in touch with us through [UNINETT's CERT team](https://www.uninett.no/cert). Please use our PGP public key to encrypt -any possible sensitive data that you may need to submit. We will get back to you as soon as possible according to our -working hours in Central European Time. - -When reporting a security issue, please add as much information as possible to help us identify, confirm, replicate and -fix the problem. In particular, remember to include the following information in your report: +When reporting a security issue, please add as much information as possible to help us identify, confirm, replicate and fix the problem. In particular, remember to include the following information in your report: * The version or versions of SimpleSAMLphp affected. * An exact version that can be used to replicate the issue. @@ -125,42 +96,23 @@ fix the problem. In particular, remember to include the following information in * Context on how you discovered the issue. * Your own name and whether you want to be credited for the discovery or not. -Please **DO NOT** report security incidents related to systems that use SimpleSAMLphp, where this software is not the -cause of the incident. Issues related to the use (or misuse) of infrastructure, misconfiguration of the software, -malfunction of a particular system or user-related errors should not be reported either. If you are using SimpleSAMLphp -to authenticate or login to services, but you don't know what SimpleSAMLphp is or you are not sure about the nature of -the issue, please contact the organization running the service for you. +Please **DO NOT** report security incidents related to systems that use SimpleSAMLphp, where this software is not the cause of the incident. Issues related to the use (or misuse) of infrastructure, misconfiguration of the software, malfunction of a particular system or user-related errors should not be reported either. If you are using SimpleSAMLphp to authenticate or login to services, but you don't know what SimpleSAMLphp is or you are not sure about the nature of the issue, please contact the organization running the service for you. -Finally, be reasonable. We'll do our best to resolve the issue according to our principles of security and transparency. -Every confirmed vulnerability will be published and resolved in a timely manner. All we ask in return is that you -contact us privately first in order to avoid any potential damage to those using the software. +Finally, be reasonable. We'll do our best to resolve the issue according to our principles of security and transparency. Every confirmed vulnerability will be published and resolved in a timely manner. All we ask in return is that you contact us privately first in order to avoid any potential damage to those using the software. You can find the list of security advisories we have published [here](https://simplesamlphp.org/security). ## Translations +SimpleSAMLphp is translated to many languages, though it needs constant updates from translators, as well as new translations to other languages. For the moment, translations can be contributed as **pull requests**. We are looking at better ways to translate the software that would make your life easier, so stay tuned! You can also join the [translators mailing list](http://groups.google.com/group/simplesamlphp-translation) to keep up to date on the latest news. -SimpleSAMLphp is translated to many languages, though it needs constant updates from translators, as well as new -translations to other languages. For the moment, translations can be contributed as **pull requests**. We are looking -at better ways to translate the software that would make your life easier, so stay tuned! You can also join the -[translators mailing list](http://groups.google.com/group/simplesamlphp-translation) to keep up to date on the -latest news. - -Before starting a new translation, decide what style you want to use, whether you want to address the user in a polite -manner or not, etc. Be coherent and keep that style through all your translations. If there is already a translation and -you want to complete it, make sure to keep the same style used by your fellow translators. +Before starting a new translation, decide what style you want to use, whether you want to address the user in a polite manner or not, etc. Be coherent and keep that style through all your translations. If there is already a translation and you want to complete it, make sure to keep the same style used by your fellow translators. ## Documentation +Did you find a typo in the documentation? Does something make no sense? Did we use poor english? Tell us! -Did you find a typo in the documentation? Does something make no sense? Did we use very poor english? Tell us! - -Documentation is included in our own repository in *markdown* format. You can submit pull requests with fixes. If you -encounter some feature that's not documented, or the documentation does not reflect the real behaviour of the library, -please do not hesitate to open an issue. +Documentation is included in our own repository in *markdown* format. You can submit pull requests with fixes. If you encounter some feature that is not documented, or the documentation does not reflect the real behaviour of the library, please do not hesitate to open an issue. Good documentation is key to make things easier for our users! ## Community - -You don't feel capable of contributing with your code, but are using SimpleSAMLphp and can share your knowledge and -experience? Please, do so! Join our [users mailing list](http://groups.google.com/group/simplesamlphp) and help other -users when you can. Your experience might be valuable for many! +You do not feel capable of contributing with your code, but are using SimpleSAMLphp and can share your knowledge and experience? Please, do so! Join our [users mailing list](http://groups.google.com/group/simplesamlphp) and help other users when you can. Your experience might be valuable for many! diff --git a/composer.json b/composer.json index 3a06bf0dad4889394b7f5b6b2282f5cf149b2159..a837f10218ea42537d30ca92852e9ab6042e7e62 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "type": "project", "keywords": [ "saml2", "shibboleth","oauth","ws-federation","sp","idp" ], "homepage": "http://simplesamlphp.org", - "license": "LGPL-2.1", + "license": "LGPL-2.1-or-later", "authors": [ { "name": "Andreas Åkre Solberg", @@ -36,7 +36,7 @@ "ext-hash": "*", "ext-json": "*", "ext-mbstring": "*", - "simplesamlphp/saml2": "~3.1", + "simplesamlphp/saml2": "~3.1.4", "robrichards/xmlseclibs": "~3.0", "whitehat101/apr1-md5": "~1.0", "twig/twig": "~1.0", @@ -44,7 +44,6 @@ "jaimeperez/twig-configurable-i18n": "^1.2" }, "require-dev": { - "ext-pdo_sqlite": "*", "phpunit/phpunit": "~4.8.35", "mikey179/vfsStream": "~1.6", "friendsofphp/php-cs-fixer": "^2.2" diff --git a/composer.lock b/composer.lock index cb059121c63298c6a7723032466f8ecc4f588f92..b70eae7c36c3dd1d5b6ef88f0e564f55e3c33d8e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "a7ac3f6a5e037f467876e690ee591e59", + "content-hash": "cce632a654df08dacb6e48fca10476da", "packages": [ { "name": "gettext/gettext", @@ -260,16 +260,16 @@ }, { "name": "simplesamlphp/saml2", - "version": "v3.1.0", + "version": "v3.1.5", "source": { "type": "git", "url": "https://github.com/simplesamlphp/saml2.git", - "reference": "1b93d1fa49abaf99ffa78aef5358224e3a6b29b2" + "reference": "a07885a55fe5d3335ef0913ee9e95a75b34bfc52" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/simplesamlphp/saml2/zipball/1b93d1fa49abaf99ffa78aef5358224e3a6b29b2", - "reference": "1b93d1fa49abaf99ffa78aef5358224e3a6b29b2", + "url": "https://api.github.com/repos/simplesamlphp/saml2/zipball/a07885a55fe5d3335ef0913ee9e95a75b34bfc52", + "reference": "a07885a55fe5d3335ef0913ee9e95a75b34bfc52", "shasum": "" }, "require": { @@ -283,7 +283,7 @@ "require-dev": { "mockery/mockery": "~0.9", "phpmd/phpmd": "~1.5", - "phpunit/phpunit": "~3.7", + "phpunit/phpunit": "~4", "sebastian/phpcpd": "~1.4", "sensiolabs/security-checker": "~1.1", "squizlabs/php_codesniffer": "~1.4" @@ -291,7 +291,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "v3.0.x-dev" + "dev-master": "v3.1.x-dev" } }, "autoload": { @@ -304,7 +304,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "LGPL-2.1" + "LGPL-2.1-or-later" ], "authors": [ { @@ -313,7 +313,7 @@ } ], "description": "SAML2 PHP library from SimpleSAMLphp", - "time": "2017-12-06T15:57:23+00:00" + "time": "2018-04-18T04:43:35+00:00" }, { "name": "twig/extensions", @@ -373,16 +373,16 @@ }, { "name": "twig/twig", - "version": "v1.34.4", + "version": "v1.35.2", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "f878bab48edb66ad9c6ed626bf817f60c6c096ee" + "reference": "9c24f2cd39dc1906b76879e099970b7e53724601" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/f878bab48edb66ad9c6ed626bf817f60c6c096ee", - "reference": "f878bab48edb66ad9c6ed626bf817f60c6c096ee", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/9c24f2cd39dc1906b76879e099970b7e53724601", + "reference": "9c24f2cd39dc1906b76879e099970b7e53724601", "shasum": "" }, "require": { @@ -396,7 +396,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.34-dev" + "dev-master": "1.35-dev" } }, "autoload": { @@ -434,7 +434,7 @@ "keywords": [ "templating" ], - "time": "2017-07-04T13:19:31+00:00" + "time": "2018-03-03T16:21:29+00:00" }, { "name": "whitehat101/apr1-md5", @@ -546,35 +546,35 @@ }, { "name": "doctrine/annotations", - "version": "v1.2.7", + "version": "v1.4.0", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535" + "reference": "54cacc9b81758b14e3ce750f205a393d52339e97" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/f25c8aab83e0c3e976fd7d19875f198ccf2f7535", - "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/54cacc9b81758b14e3ce750f205a393d52339e97", + "reference": "54cacc9b81758b14e3ce750f205a393d52339e97", "shasum": "" }, "require": { "doctrine/lexer": "1.*", - "php": ">=5.3.2" + "php": "^5.6 || ^7.0" }, "require-dev": { "doctrine/cache": "1.*", - "phpunit/phpunit": "4.*" + "phpunit/phpunit": "^5.7" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3.x-dev" + "dev-master": "1.4.x-dev" } }, "autoload": { - "psr-0": { - "Doctrine\\Common\\Annotations\\": "lib/" + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" } }, "notification-url": "https://packagist.org/downloads/", @@ -610,7 +610,7 @@ "docblock", "parser" ], - "time": "2015-08-31T12:32:49+00:00" + "time": "2017-02-24T16:22:25+00:00" }, { "name": "doctrine/instantiator", @@ -722,16 +722,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v2.2.8", + "version": "v2.10.3", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "aca23e791784eade7b377d578d6dfc6fcf1398d2" + "reference": "1634a2c250bf4640f1c5c963f63b413c2d966c8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/aca23e791784eade7b377d578d6dfc6fcf1398d2", - "reference": "aca23e791784eade7b377d578d6dfc6fcf1398d2", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/1634a2c250bf4640f1c5c963f63b413c2d966c8a", + "reference": "1634a2c250bf4640f1c5c963f63b413c2d966c8a", "shasum": "" }, "require": { @@ -739,30 +739,31 @@ "doctrine/annotations": "^1.2", "ext-json": "*", "ext-tokenizer": "*", - "gecko-packages/gecko-php-unit": "^2.0", - "php": "^5.3.6 || >=7.0 <7.3", - "sebastian/diff": "^1.4", - "symfony/console": "^2.4 || ^3.0", - "symfony/event-dispatcher": "^2.1 || ^3.0", - "symfony/filesystem": "^2.4 || ^3.0", - "symfony/finder": "^2.2 || ^3.0", - "symfony/options-resolver": "^2.6 || ^3.0", - "symfony/polyfill-php54": "^1.0", - "symfony/polyfill-php55": "^1.3", + "php": "^5.6 || >=7.0 <7.3", + "php-cs-fixer/diff": "^1.2", + "symfony/console": "^3.2 || ^4.0", + "symfony/event-dispatcher": "^3.0 || ^4.0", + "symfony/filesystem": "^3.0 || ^4.0", + "symfony/finder": "^3.0 || ^4.0", + "symfony/options-resolver": "^3.0 || ^4.0", "symfony/polyfill-php70": "^1.0", "symfony/polyfill-php72": "^1.4", - "symfony/process": "^2.3 || ^3.0", - "symfony/stopwatch": "^2.5 || ^3.0" + "symfony/process": "^3.0 || ^4.0", + "symfony/stopwatch": "^3.0 || ^4.0" }, "conflict": { - "hhvm": "<3.18" + "hhvm": "*" }, "require-dev": { - "johnkary/phpunit-speedtrap": "^1.0.1", + "johnkary/phpunit-speedtrap": "^1.1 || ^2.0@dev", "justinrainbow/json-schema": "^5.0", - "phpunit/phpunit": "^4.8.35 || ^5.4.3", - "satooshi/php-coveralls": "^1.0", - "symfony/phpunit-bridge": "^3.2.2" + "keradus/cli-executor": "^1.0", + "mikey179/vfsstream": "^1.6", + "php-coveralls/php-coveralls": "^2.0", + "php-cs-fixer/accessible-object": "^1.0", + "phpunit/phpunit": "^5.7.23 || ^6.4.3", + "phpunitgoodpractices/traits": "^1.0", + "symfony/phpunit-bridge": "^3.2.2 || ^4.0" }, "suggest": { "ext-mbstring": "For handling non-UTF8 characters in cache signature.", @@ -772,20 +773,21 @@ "php-cs-fixer" ], "type": "application", - "extra": { - "branch-alias": { - "dev-master": "2.2-dev" - } - }, "autoload": { "psr-4": { "PhpCsFixer\\": "src/" }, "classmap": [ "tests/Test/AbstractFixerTestCase.php", + "tests/Test/AbstractIntegrationCaseFactory.php", "tests/Test/AbstractIntegrationTestCase.php", + "tests/Test/Assert/AssertTokensTrait.php", + "tests/Test/Constraint/SameStringsConstraint.php", "tests/Test/IntegrationCase.php", - "tests/Test/IntegrationCaseFactory.php" + "tests/Test/IntegrationCaseFactory.php", + "tests/Test/IntegrationCaseFactoryInterface.php", + "tests/Test/InternalIntegrationCaseFactory.php", + "tests/TestCase.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -803,93 +805,7 @@ } ], "description": "A tool to automatically fix PHP code style", - "time": "2017-09-29T15:07:49+00:00" - }, - { - "name": "gecko-packages/gecko-php-unit", - "version": "v2.2", - "source": { - "type": "git", - "url": "https://github.com/GeckoPackages/GeckoPHPUnit.git", - "reference": "ab525fac9a9ffea219687f261b02008b18ebf2d1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/GeckoPackages/GeckoPHPUnit/zipball/ab525fac9a9ffea219687f261b02008b18ebf2d1", - "reference": "ab525fac9a9ffea219687f261b02008b18ebf2d1", - "shasum": "" - }, - "require": { - "php": "^5.3.6 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.4.3" - }, - "suggest": { - "ext-dom": "When testing with xml.", - "ext-libxml": "When testing with xml.", - "phpunit/phpunit": "This is an extension for it so make sure you have it some way." - }, - "type": "library", - "autoload": { - "psr-4": { - "GeckoPackages\\PHPUnit\\": "src/PHPUnit" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Additional PHPUnit asserts and constraints.", - "homepage": "https://github.com/GeckoPackages", - "keywords": [ - "extension", - "filesystem", - "phpunit" - ], - "time": "2017-08-23T07:39:54+00:00" - }, - { - "name": "ircmaxell/password-compat", - "version": "v1.0.4", - "source": { - "type": "git", - "url": "https://github.com/ircmaxell/password_compat.git", - "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ircmaxell/password_compat/zipball/5c5cde8822a69545767f7c7f3058cb15ff84614c", - "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c", - "shasum": "" - }, - "require-dev": { - "phpunit/phpunit": "4.*" - }, - "type": "library", - "autoload": { - "files": [ - "lib/password.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Anthony Ferrara", - "email": "ircmaxell@php.net", - "homepage": "http://blog.ircmaxell.com" - } - ], - "description": "A compatibility library for the proposed simplified password hashing algorithm: https://wiki.php.net/rfc/password_hash", - "homepage": "https://github.com/ircmaxell/password_compat", - "keywords": [ - "hashing", - "password" - ], - "time": "2014-11-20T16:49:30+00:00" + "time": "2018-02-22T16:49:33+00:00" }, { "name": "mikey179/vfsStream", @@ -985,18 +901,69 @@ ], "time": "2017-09-27T21:40:39+00:00" }, + { + "name": "php-cs-fixer/diff", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/diff.git", + "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/78bb099e9c16361126c86ce82ec4405ebab8e756", + "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7.23 || ^6.4.3", + "symfony/process": "^3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "SpacePossum" + } + ], + "description": "sebastian/diff v2 backport support for PHP5.6", + "homepage": "https://github.com/PHP-CS-Fixer", + "keywords": [ + "diff" + ], + "time": "2018-02-15T16:58:55+00:00" + }, { "name": "phpdocumentor/reflection-common", - "version": "1.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", - "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", "shasum": "" }, "require": { @@ -1037,25 +1004,25 @@ "reflection", "static analysis" ], - "time": "2015-12-27T11:43:31+00:00" + "time": "2017-09-11T18:02:19+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "3.2.0", + "version": "3.3.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "46f7e8bb075036c92695b15a1ddb6971c751e585" + "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/46f7e8bb075036c92695b15a1ddb6971c751e585", - "reference": "46f7e8bb075036c92695b15a1ddb6971c751e585", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bf329f6c1aadea3299f08ee804682b7c45b326a2", + "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2", "shasum": "" }, "require": { - "php": ">=5.5", - "phpdocumentor/reflection-common": "^1.0@dev", + "php": "^5.6 || ^7.0", + "phpdocumentor/reflection-common": "^1.0.0", "phpdocumentor/type-resolver": "^0.4.0", "webmozart/assert": "^1.0" }, @@ -1082,7 +1049,7 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-07-15T11:38:20+00:00" + "time": "2017-11-10T14:09:06+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -1133,33 +1100,33 @@ }, { "name": "phpspec/prophecy", - "version": "v1.7.0", + "version": "1.7.5", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "93d39f1f7f9326d746203c7c056f300f7f126073" + "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/93d39f1f7f9326d746203c7c056f300f7f126073", - "reference": "93d39f1f7f9326d746203c7c056f300f7f126073", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/dfd6be44111a7c41c2e884a336cc4f461b3b2401", + "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", "sebastian/comparator": "^1.1|^2.0", "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, "require-dev": { "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8 || ^5.6.5" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.6.x-dev" + "dev-master": "1.7.x-dev" } }, "autoload": { @@ -1192,7 +1159,7 @@ "spy", "stub" ], - "time": "2017-03-02T20:05:34+00:00" + "time": "2018-02-19T10:16:54+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1258,16 +1225,16 @@ }, { "name": "phpunit/php-file-iterator", - "version": "1.4.2", + "version": "1.4.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5" + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5", - "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", "shasum": "" }, "require": { @@ -1301,7 +1268,7 @@ "filesystem", "iterator" ], - "time": "2016-10-03T07:40:28+00:00" + "time": "2017-11-27T13:52:08+00:00" }, { "name": "phpunit/php-text-template", @@ -1395,16 +1362,16 @@ }, { "name": "phpunit/php-token-stream", - "version": "1.4.11", + "version": "1.4.12", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7" + "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7", - "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/1ce90ba27c42e4e44e6d8458241466380b51fa16", + "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16", "shasum": "" }, "require": { @@ -1440,7 +1407,7 @@ "keywords": [ "tokenizer" ], - "time": "2017-02-27T10:12:30+00:00" + "time": "2017-12-04T08:55:13+00:00" }, { "name": "phpunit/phpunit", @@ -1944,44 +1911,45 @@ }, { "name": "symfony/console", - "version": "v3.3.10", + "version": "v3.4.5", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "116bc56e45a8e5572e51eb43ab58c769a352366c" + "reference": "067339e9b8ec30d5f19f5950208893ff026b94f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/116bc56e45a8e5572e51eb43ab58c769a352366c", - "reference": "116bc56e45a8e5572e51eb43ab58c769a352366c", + "url": "https://api.github.com/repos/symfony/console/zipball/067339e9b8ec30d5f19f5950208893ff026b94f7", + "reference": "067339e9b8ec30d5f19f5950208893ff026b94f7", "shasum": "" }, "require": { "php": "^5.5.9|>=7.0.8", - "symfony/debug": "~2.8|~3.0", + "symfony/debug": "~2.8|~3.0|~4.0", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/dependency-injection": "<3.3" + "symfony/dependency-injection": "<3.4", + "symfony/process": "<3.3" }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "~3.3", - "symfony/dependency-injection": "~3.3", - "symfony/event-dispatcher": "~2.8|~3.0", - "symfony/filesystem": "~2.8|~3.0", - "symfony/process": "~2.8|~3.0" + "symfony/config": "~3.3|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~2.8|~3.0|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/process": "~3.3|~4.0" }, "suggest": { "psr/log": "For using the console logger", "symfony/event-dispatcher": "", - "symfony/filesystem": "", + "symfony/lock": "", "symfony/process": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -2008,20 +1976,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2017-10-02T06:42:24+00:00" + "time": "2018-02-26T15:46:28+00:00" }, { "name": "symfony/debug", - "version": "v3.3.10", + "version": "v3.4.5", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "eb95d9ce8f18dcc1b3dfff00cb624c402be78ffd" + "reference": "9b1071f86e79e1999b3d3675d2e0e7684268b9bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/eb95d9ce8f18dcc1b3dfff00cb624c402be78ffd", - "reference": "eb95d9ce8f18dcc1b3dfff00cb624c402be78ffd", + "url": "https://api.github.com/repos/symfony/debug/zipball/9b1071f86e79e1999b3d3675d2e0e7684268b9bc", + "reference": "9b1071f86e79e1999b3d3675d2e0e7684268b9bc", "shasum": "" }, "require": { @@ -2032,12 +2000,12 @@ "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" }, "require-dev": { - "symfony/http-kernel": "~2.8|~3.0" + "symfony/http-kernel": "~2.8|~3.0|~4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -2064,20 +2032,20 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2017-10-02T06:42:24+00:00" + "time": "2018-02-28T21:49:22+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v3.3.10", + "version": "v3.4.5", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "d7ba037e4b8221956ab1e221c73c9e27e05dd423" + "reference": "58990682ac3fdc1f563b7e705452921372aad11d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d7ba037e4b8221956ab1e221c73c9e27e05dd423", - "reference": "d7ba037e4b8221956ab1e221c73c9e27e05dd423", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/58990682ac3fdc1f563b7e705452921372aad11d", + "reference": "58990682ac3fdc1f563b7e705452921372aad11d", "shasum": "" }, "require": { @@ -2088,10 +2056,10 @@ }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "~2.8|~3.0", - "symfony/dependency-injection": "~3.3", - "symfony/expression-language": "~2.8|~3.0", - "symfony/stopwatch": "~2.8|~3.0" + "symfony/config": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0" }, "suggest": { "symfony/dependency-injection": "", @@ -2100,7 +2068,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -2127,20 +2095,20 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2017-10-02T06:42:24+00:00" + "time": "2018-02-14T10:03:57+00:00" }, { "name": "symfony/filesystem", - "version": "v3.3.10", + "version": "v3.4.5", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "90bc45abf02ae6b7deb43895c1052cb0038506f1" + "reference": "253a4490b528597aa14d2bf5aeded6f5e5e4a541" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/90bc45abf02ae6b7deb43895c1052cb0038506f1", - "reference": "90bc45abf02ae6b7deb43895c1052cb0038506f1", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/253a4490b528597aa14d2bf5aeded6f5e5e4a541", + "reference": "253a4490b528597aa14d2bf5aeded6f5e5e4a541", "shasum": "" }, "require": { @@ -2149,7 +2117,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -2176,20 +2144,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2017-10-03T13:33:10+00:00" + "time": "2018-02-22T10:48:49+00:00" }, { "name": "symfony/finder", - "version": "v3.3.10", + "version": "v3.4.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "773e19a491d97926f236942484cb541560ce862d" + "reference": "6a615613745cef820d807443f32076bb9f5d0a38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/773e19a491d97926f236942484cb541560ce862d", - "reference": "773e19a491d97926f236942484cb541560ce862d", + "url": "https://api.github.com/repos/symfony/finder/zipball/6a615613745cef820d807443f32076bb9f5d0a38", + "reference": "6a615613745cef820d807443f32076bb9f5d0a38", "shasum": "" }, "require": { @@ -2198,7 +2166,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -2225,20 +2193,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2017-10-02T06:42:24+00:00" + "time": "2018-02-11T17:15:12+00:00" }, { "name": "symfony/options-resolver", - "version": "v3.3.10", + "version": "v3.4.5", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "ee4e22978fe885b54ee5da8c7964f0a5301abfb6" + "reference": "f3109a6aedd20e35c3a33190e932c2b063b7b50e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/ee4e22978fe885b54ee5da8c7964f0a5301abfb6", - "reference": "ee4e22978fe885b54ee5da8c7964f0a5301abfb6", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/f3109a6aedd20e35c3a33190e932c2b063b7b50e", + "reference": "f3109a6aedd20e35c3a33190e932c2b063b7b50e", "shasum": "" }, "require": { @@ -2247,7 +2215,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -2279,20 +2247,20 @@ "configuration", "options" ], - "time": "2017-07-29T21:54:42+00:00" + "time": "2018-01-11T07:56:07+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.6.0", + "version": "v1.7.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296" + "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296", - "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b", + "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b", "shasum": "" }, "require": { @@ -2304,7 +2272,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.6-dev" + "dev-master": "1.7-dev" } }, "autoload": { @@ -2338,134 +2306,20 @@ "portable", "shim" ], - "time": "2017-10-11T12:05:26+00:00" - }, - { - "name": "symfony/polyfill-php54", - "version": "v1.6.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php54.git", - "reference": "d7810a14b2c6c1aff415e1bb755f611b3d5327bc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/d7810a14b2c6c1aff415e1bb755f611b3d5327bc", - "reference": "d7810a14b2c6c1aff415e1bb755f611b3d5327bc", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.6-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php54\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 5.4+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "time": "2017-10-11T12:05:26+00:00" - }, - { - "name": "symfony/polyfill-php55", - "version": "v1.6.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php55.git", - "reference": "b64e7f0c37ecf144ecc16668936eef94e628fbfd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/b64e7f0c37ecf144ecc16668936eef94e628fbfd", - "reference": "b64e7f0c37ecf144ecc16668936eef94e628fbfd", - "shasum": "" - }, - "require": { - "ircmaxell/password-compat": "~1.0", - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.6-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php55\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 5.5+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "time": "2017-10-11T12:05:26+00:00" + "time": "2018-01-30T19:27:44+00:00" }, { "name": "symfony/polyfill-php70", - "version": "v1.6.0", + "version": "v1.7.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "0442b9c0596610bd24ae7b5f0a6cdbbc16d9fcff" + "reference": "3532bfcd8f933a7816f3a0a59682fc404776600f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/0442b9c0596610bd24ae7b5f0a6cdbbc16d9fcff", - "reference": "0442b9c0596610bd24ae7b5f0a6cdbbc16d9fcff", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/3532bfcd8f933a7816f3a0a59682fc404776600f", + "reference": "3532bfcd8f933a7816f3a0a59682fc404776600f", "shasum": "" }, "require": { @@ -2475,7 +2329,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.6-dev" + "dev-master": "1.7-dev" } }, "autoload": { @@ -2511,20 +2365,20 @@ "portable", "shim" ], - "time": "2017-10-11T12:05:26+00:00" + "time": "2018-01-30T19:27:44+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.6.0", + "version": "v1.7.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "6de4f4884b97abbbed9f0a84a95ff2ff77254254" + "reference": "8eca20c8a369e069d4f4c2ac9895144112867422" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/6de4f4884b97abbbed9f0a84a95ff2ff77254254", - "reference": "6de4f4884b97abbbed9f0a84a95ff2ff77254254", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/8eca20c8a369e069d4f4c2ac9895144112867422", + "reference": "8eca20c8a369e069d4f4c2ac9895144112867422", "shasum": "" }, "require": { @@ -2533,7 +2387,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.6-dev" + "dev-master": "1.7-dev" } }, "autoload": { @@ -2566,20 +2420,20 @@ "portable", "shim" ], - "time": "2017-10-11T12:05:26+00:00" + "time": "2018-01-31T17:43:24+00:00" }, { "name": "symfony/process", - "version": "v3.3.10", + "version": "v3.4.5", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "fdf89e57a723a29baf536e288d6e232c059697b1" + "reference": "cc4aea21f619116aaf1c58016a944e4821c8e8af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/fdf89e57a723a29baf536e288d6e232c059697b1", - "reference": "fdf89e57a723a29baf536e288d6e232c059697b1", + "url": "https://api.github.com/repos/symfony/process/zipball/cc4aea21f619116aaf1c58016a944e4821c8e8af", + "reference": "cc4aea21f619116aaf1c58016a944e4821c8e8af", "shasum": "" }, "require": { @@ -2588,7 +2442,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -2615,20 +2469,20 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2017-10-02T06:42:24+00:00" + "time": "2018-02-12T17:55:00+00:00" }, { "name": "symfony/stopwatch", - "version": "v3.3.10", + "version": "v3.4.5", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "170edf8b3247d7b6779eb6fa7428f342702ca184" + "reference": "eb17cfa072cab26537ac37e9c4ece6c0361369af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/170edf8b3247d7b6779eb6fa7428f342702ca184", - "reference": "170edf8b3247d7b6779eb6fa7428f342702ca184", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/eb17cfa072cab26537ac37e9c4ece6c0361369af", + "reference": "eb17cfa072cab26537ac37e9c4ece6c0361369af", "shasum": "" }, "require": { @@ -2637,7 +2491,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -2664,27 +2518,30 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2017-10-02T06:42:24+00:00" + "time": "2018-02-17T14:55:25+00:00" }, { "name": "symfony/yaml", - "version": "v3.3.10", + "version": "v3.4.5", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "8c7bf1e7d5d6b05a690b715729cb4cd0c0a99c46" + "reference": "6af42631dcf89e9c616242c900d6c52bd53bd1bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/8c7bf1e7d5d6b05a690b715729cb4cd0c0a99c46", - "reference": "8c7bf1e7d5d6b05a690b715729cb4cd0c0a99c46", + "url": "https://api.github.com/repos/symfony/yaml/zipball/6af42631dcf89e9c616242c900d6c52bd53bd1bb", + "reference": "6af42631dcf89e9c616242c900d6c52bd53bd1bb", "shasum": "" }, "require": { "php": "^5.5.9|>=7.0.8" }, + "conflict": { + "symfony/console": "<3.4" + }, "require-dev": { - "symfony/console": "~2.8|~3.0" + "symfony/console": "~3.4|~4.0" }, "suggest": { "symfony/console": "For validating YAML files using the lint command" @@ -2692,7 +2549,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -2719,20 +2576,20 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2017-10-05T14:43:42+00:00" + "time": "2018-02-16T09:50:28+00:00" }, { "name": "webmozart/assert", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" + "reference": "0df1908962e7a3071564e857d86874dad1ef204a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", + "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", + "reference": "0df1908962e7a3071564e857d86874dad1ef204a", "shasum": "" }, "require": { @@ -2769,7 +2626,7 @@ "check", "validate" ], - "time": "2016-11-23T20:04:58+00:00" + "time": "2018-01-29T19:49:41+00:00" } ], "aliases": [], diff --git a/config-templates/authmemcookie.php b/config-templates/authmemcookie.php index b8e8df8f98a32f021dc994a48dbbbe4f885cba22..6fa872061d9e7580efadba42b56a67d36b11755e 100644 --- a/config-templates/authmemcookie.php +++ b/config-templates/authmemcookie.php @@ -33,7 +33,7 @@ $config = array( * Default: * No default value. */ - 'username' => NULL, + 'username' => null, /* * This option specifies the name of the attribute which contains the groups of the user. Set this option to @@ -43,9 +43,9 @@ $config = array( * 'groups' => 'edupersonaffiliation', * * Default: - * 'groups' => NULL, + * 'groups' => null, */ - 'groups' => NULL, + 'groups' => null, /* * This option contains the hostnames or IP addresses of the memcache servers where we should store the diff --git a/config-templates/authsources.php b/config-templates/authsources.php index 6afe417811d36c5248af6fa115dc3001d2bf1456..bccb964fd09fa0960027602eba874bacb699ddbe 100644 --- a/config-templates/authsources.php +++ b/config-templates/authsources.php @@ -148,53 +148,6 @@ $config = array( ), */ - /* - 'openid' => array( - 'openid:OpenIDConsumer', - 'attributes.required' => array('nickname'), - 'attributes.optional' => array('fullname', 'email',), - // 'sreg.validate' => FALSE, - 'attributes.ax_required' => array('http://axschema.org/namePerson/friendly'), - 'attributes.ax_optional' => array('http://axschema.org/namePerson','http://axschema.org/contact/email'), - // Prefer HTTP redirect over POST - // 'prefer_http_redirect' => FALSE, - ), - */ - - /* - // Example of an authsource that authenticates against Google. - // See: http://code.google.com/apis/accounts/docs/OpenID.html - 'google' => array( - 'openid:OpenIDConsumer', - // Googles OpenID endpoint. - 'target' => 'https://www.google.com/accounts/o8/id', - // Custom realm - // 'realm' => 'http://*.example.org', - // Attributes that google can supply. - 'attributes.ax_required' => array( - //'http://axschema.org/namePerson/first', - //'http://axschema.org/namePerson/last', - //'http://axschema.org/contact/email', - //'http://axschema.org/contact/country/home', - //'http://axschema.org/pref/language', - ), - // custom extension arguments - 'extension.args' => array( - //'http://specs.openid.net/extensions/ui/1.0' => array( - // 'mode' => 'popup', - // 'icon' => 'true', - //), - ), - ), - */ - - /* - 'papi' => array( - 'authpapi:PAPI', - ), - */ - - /* 'facebook' => array( 'authfacebook:Facebook', diff --git a/config-templates/config.php b/config-templates/config.php index ec3f97d3fe870db10189e8623af2da76679b2b32..a147f2e5fe34c386c077d5659a3dc5daf9bca5d4 100644 --- a/config-templates/config.php +++ b/config-templates/config.php @@ -1,7 +1,7 @@ <?php -/* +/* * The configuration of SimpleSAMLphp - * + * */ $config = array( @@ -19,7 +19,6 @@ $config = array( * baseurlpath is a *URL path* (not a filesystem path). * A valid format for 'baseurlpath' is: * [(http|https)://(hostname|fqdn)[:port]]/[path/to/simplesaml/] - * (note that it must end with a '/') * * The full url format is useful if your SimpleSAMLphp setup is hosted behind * a reverse proxy. In that case you can specify the external url here. @@ -49,7 +48,7 @@ $config = array( * need to compute the right URLs yourself and pass them dynamically * to SimpleSAMLphp's API. */ - //'baseURL' => 'https://example.com' + //'baseURL' => 'https://example.com', //), /* @@ -61,7 +60,7 @@ $config = array( * - 'temdir': Saving temporary files. SimpleSAMLphp will attempt to create * this directory if it doesn't exist. * When specified as a relative path, this is relative to the SimpleSAMLphp - * root directory. + * root directory. */ 'certdir' => 'cert/', 'loggingdir' => 'log/', @@ -104,7 +103,7 @@ $config = array( * 'secretsalt' can be any valid string of any length. * * A possible way to generate a random salt is by running the following command from a unix shell: - * tr -c -d '0123456789abcdefghijklmnopqrstuvwxyz' </dev/urandom | dd bs=32 count=1 2>/dev/null;echo + * LC_CTYPE=C tr -c -d '0123456789abcdefghijklmnopqrstuvwxyz' </dev/urandom | dd bs=32 count=1 2>/dev/null;echo */ 'secretsalt' => 'defaultsecretsalt', @@ -378,6 +377,7 @@ $config = array( */ 'database.username' => 'simplesamlphp', 'database.password' => 'secret', + 'database.options' => array(), /* * (Optional) Table prefix diff --git a/docs/simplesamlphp-authproc.md b/docs/simplesamlphp-authproc.md index d8523d10ddad98831e784c7b48f32dd633ab6f40..3b5408396591d39cd06adecee368ee4d610d9848 100644 --- a/docs/simplesamlphp-authproc.md +++ b/docs/simplesamlphp-authproc.md @@ -151,6 +151,8 @@ The following filters are included in the SimpleSAMLphp distribution: - [`saml:TransientNameID`](./saml:nameid): Generate transient NameID. - [`smartattributes:SmartID`](./smartattributes:smartattributes): Generate user ID attribute based on several attributes. +See the [Third-party modules](https://simplesamlphp.org/modules) page on the SimpleSAMLphp website +for externally hosted modules that may provide a processing filter. Writing your own Auth Proc Filter diff --git a/docs/simplesamlphp-changelog.md b/docs/simplesamlphp-changelog.md index 303740fcb98b280192acd52eba517b2a2fc2fc0b..c4c035a35df7a7945e3ae682ee270444afa149c3 100644 --- a/docs/simplesamlphp-changelog.md +++ b/docs/simplesamlphp-changelog.md @@ -10,15 +10,24 @@ See the upgrade notes for specific information about upgrading. Released TBD +### Changes + * Renamed class `SimpleSAML_Error_BadUserInnput` to `SimpleSAML_Error_BadUserInput` + ### New features * Added support for SAML "Enhanced Client or Proxy" (ECP) protocol, IdP side with HTTP Basic Authentcation as authentication method. See the [ECP IdP documentation](./simplesamlphp-ecp-idp) for details. * New option `sendmail_from`, the from address for email sent by SSP. + * New option `options` for PDO database connections, e.g. for TLS setup. + * New option `search.scope` for LDAP authsources + +### consent + * Sort attribute values for consent. ### core * StatisticsWithAttribute: add `passive-` prefix when logging passive requests, set new option `skipPassive` to skip logging these altogether. + * Replace deprecated create_function with an anonymous function. ### Oauth * Make module HTTP proxy-aware. @@ -27,6 +36,50 @@ Released TBD ### Sqlauth * Changed from default-enabled to default-disabled. +## Version 1.15.4 + +Released 2018-03-02 + + * Resolved a security issue related to signature validation in the SAML2 library. See [SSPSA 201803-01](https://simplesamlphp.org/security/201803-01). + +## Version 1.15.3 + +Released 2018-02-27 + + * Resolved a security issue related to signature validation in the SAML2 library. See [SSPSA 201802-01](https://simplesamlphp.org/security/201802-01). + * Fixed edge-case scenario where an application uses one of the known LoggingHandlers' name as a defined class + * Fixed issue #793 in the PHP logging handler. + +## Version 1.15.2 + +Released 2018-01-31 + + * Resolved a Denial of Service security issue when validating timestamps in the SAML2 library. See [SSPSA 201801-01](https://simplesamlphp.org/security/201801-01). + * Resolved a security issue with the open redirect protection mechanism. See [SSPSA 201801-02](https://simplesamlphp.org/security/201801-02). + * Fix _undefined method_ error when using memcacheD. + +### `authfacebook` + * Fix compatibility with Facebook strict URI match. + +### `consent` + * Fix statistics not being gathered. + +### `sqlauth` + * Prevented a security issue with the connection charset used for MySQL backends. See [SSPSA 201801-03](https://simplesamlphp.org/security/201801-03). + +## Version 1.15.1 + +Released 2018-01-12 + +### Bug fixes + * AuthX509 error messages were broken. + * Properly calculate supported protocols based on config. + * NameIDAttribute filter: update to use SAML2\XML\saml\NameID. + * Replace remaining uses of SimpleSAML_Logger with namespace version. + * Statistics: prevent mixed content errors. + * Add 'no-store' to the cache-control header to avoid Chrome + caching redirects. + ## Version 1.15.0 Released 2017-11-20 @@ -155,6 +208,12 @@ Released 2017-11-20 ### `sqlauth` * Fixed SQL schema for usergroups table. +## Version 1.14.17 + +Released 2017-10-25 + + * Resolved a security issue with the SAML 1.1 Service Provider. See [SSPSA 201710-01](https://simplesamlphp.org/security/201710-01). + ## Version 1.14.16 Released 2017-09-04 diff --git a/docs/simplesamlphp-customauth.md b/docs/simplesamlphp-customauth.md index 8f7c30f9fd5e3826307cb70d758ac80933438377..8238ba695b3dfbfafd23a356e6420ed3084cd43a 100644 --- a/docs/simplesamlphp-customauth.md +++ b/docs/simplesamlphp-customauth.md @@ -253,9 +253,10 @@ The class follows: */ private $dsn; - /* The database username & password. */ + /* The database username, password & options. */ private $username; private $password; + private $options; public function __construct($info, $config) { parent::__construct($info, $config); @@ -272,6 +273,12 @@ The class follows: throw new Exception('Missing or invalid password option in config.'); } $this->password = $config['password']; + if (isset($config['options']) { + if (!is_array($config['options])) { + throw new Exception('Missing or invalid options option in config.'); + } + $this->options = $config['options']; + } } /** @@ -294,7 +301,7 @@ The class follows: protected function login($username, $password) { /* Connect to the database. */ - $db = new PDO($this->dsn, $this->username, $this->password); + $db = new PDO($this->dsn, $this->username, $this->password, $this->options); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); /* Ensure that we are operating with UTF-8 encoding. diff --git a/docs/simplesamlphp-ecp-idp.txt b/docs/simplesamlphp-ecp-idp.md similarity index 100% rename from docs/simplesamlphp-ecp-idp.txt rename to docs/simplesamlphp-ecp-idp.md diff --git a/docs/simplesamlphp-install.md b/docs/simplesamlphp-install.md index 0c4ba9c4859687b7954f1228e4e023abf71a5777..f1e6499814d2f4bb3f1a64e94df0a074d288c6d6 100644 --- a/docs/simplesamlphp-install.md +++ b/docs/simplesamlphp-install.md @@ -24,7 +24,7 @@ Development version -------------------- This document is about the latest stable version of SimpleSAMLphp. -If you want to install the development version, look at the instructions for [installing SimpleSAMLphp from the repository](simplesamlphp-install-repo). +If you want to install the development version, look at the instructions for [installing SimpleSAMLphp from the repository](simplesamlphp-install-repo.md). Prerequisites @@ -151,9 +151,43 @@ This works only for the `config` directory. If you need your metadata to be in a This is just the basic configuration to get things working. For a checklist further completing your documentation, please see -[Maintenance and configuration: Apache](simplesamlphp-maintenance#section_4). +[Maintenance and configuration: Apache](simplesamlphp-maintenance.md#apache-configuration). +Configuring Ngynx +------------------ + +Examples below assume that SimpleSAMLphp is installed in the default location, `/var/simplesamlphp`. You may choose another location, but this requires a path update in a few files. See Appendix for details ‹Installing SimpleSAMLphp in alternative locations›. + +The only subdirectory of `SimpleSAMLphp` that needs to be accessible from the web is `www`. There are several ways of exposing SimpleSAMLphp depending on the way web sites are structured on your Ngynx web server. The following is just one possible configuration. + +Find the Ngynx configuration file for the virtual hosts where you want to run SimpleSAMLphp. The configuration may look like this: + + server { + listen 443 ssl; + server_name idp.example.com; + + ssl_certificate /etc/pki/tls/certs/idp.example.com.crt; + ssl_certificate_key /etc/pki/tls/private/idp.example.com.key; + ssl_protocols TLSv1.1 TLSv1.2; + ssl_ciphers HIGH:!aNULL:!MD5; + + location / { + root /var/simplesamlphp/www; + index index.php; + } + + location ~ \.php$ { + root /var/simplesamlphp/www; + fastcgi_pass 127.0.0.1:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_split_path_info ^(.+?\.php)(/.*)$; + fastcgi_param PATH_INFO $fastcgi_path_info; + include fastcgi_params; + } + } + SimpleSAMLphp configuration: config.php --------------------------------------- @@ -164,7 +198,7 @@ file, `config.php`, right away: 'auth.adminpassword' => 'setnewpasswordhere', - Hashed passwords can also be used here. See the [`authcrypt`](./authcrypt:authcrypt) documentation for more information. + Hashed passwords can also be used here. See the [`authcrypt`](../modules/authcrypt/docs/authcrypt.md) documentation for more information. - Set a secret salt. This should be a random string. Some parts of the SimpleSAMLphp needs this salt to generate cryptographically secure hashes. SimpleSAMLphp will give an error if the salt is not changed from the default value. The command below can help you to generated a random string on (some) unix systems: @@ -254,7 +288,6 @@ At the bottom of the installation page are some green lights. simpleSAML runs so You have now successfully installed SimpleSAMLphp, and the next steps depends on whether you want to setup a service provider, to protect a website by authentication or if you want to setup an identity provider and connect it to a user catalog. Documentation on bridging between federation protocols is found in a separate document. * [Using SimpleSAMLphp as a SAML Service Provider](simplesamlphp-sp) - * [Hosted SP Configuration Reference](./saml:sp) * [IdP remote reference](simplesamlphp-reference-idp-remote) * [Connecting SimpleSAMLphp as a SP to UK Access Federation or InCommon](simplesamlphp-ukaccess) * [Upgrading - migration to use the SAML authentication source](simplesamlphp-sp-migration) diff --git a/docs/simplesamlphp-metadata-extensions-ui.md b/docs/simplesamlphp-metadata-extensions-ui.md index 94a855d8451f8149db0e5e986a88b415b5895ffd..f9627b93e6206bc0c53e26c5e091058335c640ef 100644 --- a/docs/simplesamlphp-metadata-extensions-ui.md +++ b/docs/simplesamlphp-metadata-extensions-ui.md @@ -83,7 +83,7 @@ And for an SP it could look like this: ), 'Description' => array( 'en' => 'English description', - 'es' => 'Descripción en Español + 'es' => 'Descripción en Español' ), ), /* ... */ diff --git a/docs/simplesamlphp-reference-idp-remote.md b/docs/simplesamlphp-reference-idp-remote.md index 89526fdfdea27bad7f6f260bd733681ce05139b6..cfa993104526ef1865ab9297b6fed033965a9182 100644 --- a/docs/simplesamlphp-reference-idp-remote.md +++ b/docs/simplesamlphp-reference-idp-remote.md @@ -120,6 +120,9 @@ The following SAML 2.0 options are available: `hide.from.discovery` : Whether to hide hide this IdP from the local discovery or not. Set to true to hide it. Defaults to false. +`IDPList` +: The IdP is allowed to respond to an `AuthNRequest` originally sent to entityIDs in this list. + `nameid.encryption` : Whether NameIDs sent to this IdP should be encrypted. The default value is `FALSE`. diff --git a/docs/simplesamlphp-theming.md b/docs/simplesamlphp-theming.md index 0c66fbde91ea3f948e96767901fa788113294a3d..7d0ea7310c5f47e2c31f9009dfa96a15c79f9b89 100644 --- a/docs/simplesamlphp-theming.md +++ b/docs/simplesamlphp-theming.md @@ -107,10 +107,29 @@ modules Reference these resources in your custom PHP templates under `themes/fancytheme` by using a generator for the URL: ```php -<?php echo SimpleSAML_Module::getModuleURL('mymodule/logo.png'); ?> +<?php echo SimpleSAML\Module::getModuleURL('mymodule/logo.png'); ?> ``` Example for a custom CSS stylesheet file: ```html -<link rel="stylesheet" type="text/css" href="<?php echo SimpleSAML_Module::getModuleURL('mymodule/style.css'); ?>" /> +<link rel="stylesheet" type="text/css" href="<?php echo SimpleSAML\Module::getModuleURL('mymodule/style.css'); ?>" /> ``` + +Migrating to Twig templates +--------------------------- + +In version 1.15, a new templating system based on [Twig](https://twig.symfony.com/) was introduced. As modules migrate, it will become necessary for themes to include both the old templating style described above and new Twig-based templates. + +Twig works by extending a base template, which can itself include other partial templates. Some of the content of the old `includes/header.php` template is now located in a separate `_header.twig` file. This can be customized by copying it from the base template: + + cp templates/_header.twig modules/mymodule/themes/fancytheme/default/ + +If you need to make more extensive customizations to the base template, you should copy it from the base theme: + + cp templates/base.twig modules/mymodule/themes/fancytheme/default/ + +Any references to `$this->data['baseurlpath']` in old-style templates can be replaced with `{{baseurlpath}}` in Twig templates. Likewise, references to `SimpleSAML_Module::getModuleURL()` can be replaced with `{{baseurlpath}}module.php/mymodule/...` + +See the [Twig documentation](https://twig.symfony.com/doc/1.x/templates.html) for more information on using variables and expressions in Twig templates, and the SimpleSAMLphp wiki for [our conventions](https://github.com/simplesamlphp/simplesamlphp/wiki/Twig-conventions). + +The wiki also includes some information on [migrating translations](https://github.com/simplesamlphp/simplesamlphp/wiki/Migrating-translation-in-Twig) and [migrating templates](https://github.com/simplesamlphp/simplesamlphp/wiki/Twig:-Migrating-templates). diff --git a/docs/simplesamlphp-upgrade-notes-1.15.md b/docs/simplesamlphp-upgrade-notes-1.15.md index 19523af595a2397051f980bc29990f2fdcecba0d..a4ffd2483d3c320b80ac1e00607b8c154b033cda 100644 --- a/docs/simplesamlphp-upgrade-notes-1.15.md +++ b/docs/simplesamlphp-upgrade-notes-1.15.md @@ -5,7 +5,9 @@ The minimum required PHP version is now 5.4. The dependency on mcrypt has been dropped. A new templating system based on Twig has been introduced. The old templating -system is still available but should be considered deprecated. +system is still available but should be considered deprecated. Custom themes +may need to be updated to include Twig-style templates as well. See the +[theming documentation](simplesamlphp-theming). A new internationalization system based on Gettext has been introduced. While old templates can use either the old or the new system (refer to the diff --git a/lib/SimpleSAML/Auth/LDAP.php b/lib/SimpleSAML/Auth/LDAP.php index cc7cf38bccab084105aaf8cde04410850594ad11..f5d47f5fe469b55457284236197b7c888bfd5928 100644 --- a/lib/SimpleSAML/Auth/LDAP.php +++ b/lib/SimpleSAML/Auth/LDAP.php @@ -56,12 +56,12 @@ class SimpleSAML_Auth_LDAP public function __construct($hostname, $enable_tls = true, $debug = false, $timeout = 0, $port = 389, $referrals = true) { // Debug - SimpleSAML\Logger::debug('Library - LDAP __construct(): Setup LDAP with ' . - 'host=\'' . $hostname . - '\', tls=' . var_export($enable_tls, true) . - ', debug=' . var_export($debug, true) . - ', timeout=' . var_export($timeout, true) . - ', referrals=' . var_export($referrals, true)); + SimpleSAML\Logger::debug('Library - LDAP __construct(): Setup LDAP with '. + 'host=\''.$hostname. + '\', tls='.var_export($enable_tls, true). + ', debug='.var_export($debug, true). + ', timeout='.var_export($timeout, true). + ', referrals='.var_export($referrals, true)); /* * Set debug level before calling connect. Note that this passes @@ -79,7 +79,7 @@ class SimpleSAML_Auth_LDAP */ $this->ldap = @ldap_connect($hostname, $port); if ($this->ldap === false) { - throw $this->makeException('Library - LDAP __construct(): Unable to connect to \'' . $hostname . '\'', ERR_INTERNAL); + throw $this->makeException('Library - LDAP __construct(): Unable to connect to \''.$hostname.'\'', ERR_INTERNAL); } // Enable LDAP protocol version 3 @@ -97,10 +97,10 @@ class SimpleSAML_Auth_LDAP $this->timeout = $timeout; if ($timeout > 0) { if (!@ldap_set_option($this->ldap, LDAP_OPT_NETWORK_TIMEOUT, $timeout)) { - SimpleSAML\Logger::warning('Library - LDAP __construct(): Unable to set timeouts (LDAP_OPT_NETWORK_TIMEOUT) to ' . $timeout); + SimpleSAML\Logger::warning('Library - LDAP __construct(): Unable to set timeouts (LDAP_OPT_NETWORK_TIMEOUT) to '.$timeout); } if (!@ldap_set_option($this->ldap, LDAP_OPT_TIMELIMIT, $timeout)) { - SimpleSAML\Logger::warning('Library - LDAP __construct(): Unable to set timeouts (LDAP_OPT_TIMELIMIT) to ' . $timeout); + SimpleSAML\Logger::warning('Library - LDAP __construct(): Unable to set timeouts (LDAP_OPT_TIMELIMIT) to '.$timeout); } } @@ -136,7 +136,7 @@ class SimpleSAML_Auth_LDAP if ($type) { if ($errNo !== 0) { // Only log real LDAP errors; not success - SimpleSAML\Logger::error($description . '; cause: \'' . ldap_error($this->ldap) . '\' (0x' . dechex($errNo) . ')'); + SimpleSAML\Logger::error($description.'; cause: \''.ldap_error($this->ldap).'\' (0x'.dechex($errNo).')'); } else { SimpleSAML\Logger::error($description); } @@ -155,9 +155,9 @@ class SimpleSAML_Auth_LDAP } } else { if ($errNo !== 0) { - $description .= '; cause: \'' . ldap_error($this->ldap) . '\' (0x' . dechex($errNo) . ')'; + $description .= '; cause: \''.ldap_error($this->ldap).'\' (0x'.dechex($errNo).')'; if (@ldap_get_option($this->ldap, LDAP_OPT_DIAGNOSTIC_MESSAGE, $extendedError) && !empty($extendedError)) { - $description .= '; additional: \'' . $extendedError . '\''; + $description .= '; additional: \''.$extendedError.'\''; } } switch ($errNo) { @@ -187,6 +187,10 @@ class SimpleSAML_Auth_LDAP * The attribute name(s) to search for. * @param string $value * The attribute value to search for. + * Additional search filter + * @param string|null $searchFilter + * The scope of the search + * @param string $scope * @return string * The DN of the resulting found element. * @throws SimpleSAML_Error_Exception if: @@ -201,16 +205,16 @@ class SimpleSAML_Auth_LDAP * @throws SimpleSAML_Error_UserNotFound if: * - Zero entries were found */ - private function search($base, $attribute, $value, $searchFilter = null) + private function search($base, $attribute, $value, $searchFilter = null, $scope = "subtree") { // Create the search filter $attribute = self::escape_filter_value($attribute, false); $value = self::escape_filter_value($value, true); $filter = ''; foreach ($attribute as $attr) { - $filter .= '(' . $attr . '=' . $value. ')'; + $filter .= '('.$attr.'='.$value.')'; } - $filter = '(|' . $filter . ')'; + $filter = '(|'.$filter.')'; // Append LDAP filters if defined if ($searchFilter != null) { @@ -218,10 +222,17 @@ class SimpleSAML_Auth_LDAP } // Search using generated filter - SimpleSAML\Logger::debug('Library - LDAP search(): Searching base \'' . $base . '\' for \'' . $filter . '\''); - $result = @ldap_search($this->ldap, $base, $filter, array(), 0, 0, $this->timeout, LDAP_DEREF_NEVER); + SimpleSAML\Logger::debug('Library - LDAP search(): Searching base ('.$scope.') \''.$base.'\' for \''.$filter.'\''); + if ($scope === 'base') { + $result = @ldap_read($this->ldap, $base, $filter, array(), 0, 0, $this->timeout, LDAP_DEREF_NEVER); + } else if ($scope === 'onelevel') { + $result = @ldap_list($this->ldap, $base, $filter, array(), 0, 0, $this->timeout, LDAP_DEREF_NEVER); + } else { + $result = @ldap_search($this->ldap, $base, $filter, array(), 0, 0, $this->timeout, LDAP_DEREF_NEVER); + } + if ($result === false) { - throw $this->makeException('Library - LDAP search(): Failed search on base \'' . $base . '\' for \'' . $filter . '\''); + throw $this->makeException('Library - LDAP search(): Failed search on base \''.$base.'\' for \''.$filter.'\''); } // Sanity checks on search results @@ -230,21 +241,21 @@ class SimpleSAML_Auth_LDAP throw $this->makeException('Library - LDAP search(): Failed to get number of entries returned'); } elseif ($count > 1) { // More than one entry is found. External error - throw $this->makeException('Library - LDAP search(): Found ' . $count . ' entries searching base \'' . $base . '\' for \'' . $filter . '\'', ERR_AS_DATA_INCONSIST); + throw $this->makeException('Library - LDAP search(): Found '.$count.' entries searching base \''.$base.'\' for \''.$filter.'\'', ERR_AS_DATA_INCONSIST); } elseif ($count === 0) { // No entry is fond => wrong username is given (or not registered in the catalogue). User error - throw $this->makeException('Library - LDAP search(): Found no entries searching base \'' . $base . '\' for \'' . $filter . '\'', ERR_NO_USER); + throw $this->makeException('Library - LDAP search(): Found no entries searching base \''.$base.'\' for \''.$filter.'\'', ERR_NO_USER); } // Resolve the DN from the search result $entry = @ldap_first_entry($this->ldap, $result); if ($entry === false) { - throw $this->makeException('Library - LDAP search(): Unable to retrieve result after searching base \'' . $base . '\' for \'' . $filter . '\''); + throw $this->makeException('Library - LDAP search(): Unable to retrieve result after searching base \''.$base.'\' for \''.$filter.'\''); } $dn = @ldap_get_dn($this->ldap, $entry); if ($dn === false) { - throw $this->makeException('Library - LDAP search(): Unable to get DN after searching base \'' . $base . '\' for \'' . $filter . '\''); + throw $this->makeException('Library - LDAP search(): Unable to get DN after searching base \''.$base.'\' for \''.$filter.'\''); } return $dn; } @@ -264,6 +275,8 @@ class SimpleSAML_Auth_LDAP * Defaults to FALSE. * @param string|null $searchFilter * Additional searchFilter to be added to the (attribute=value) filter + * @param string $scope + * The scope of the search * @return string * The DN of the matching element, if found. If no element was found and * $allowZeroHits is set to FALSE, an exception will be thrown; otherwise @@ -275,14 +288,14 @@ class SimpleSAML_Auth_LDAP * - $allowZeroHits is FALSE and no result is found * */ - public function searchfordn($base, $attribute, $value, $allowZeroHits = false, $searchFilter = null) + public function searchfordn($base, $attribute, $value, $allowZeroHits = false, $searchFilter = null, $scope = 'subtree') { // Traverse all search bases, returning DN if found $bases = SimpleSAML\Utils\Arrays::arrayize($base); foreach ($bases as $current) { try { // Single base search - $result = $this->search($current, $attribute, $value, $searchFilter); + $result = $this->search($current, $attribute, $value, $searchFilter, $scope); // We don't hawe to look any futher if user is found if (!empty($result)) { @@ -300,8 +313,8 @@ class SimpleSAML_Auth_LDAP return null; } else { // Zero hits not allowed - throw $this->makeException('Library - LDAP searchfordn(): LDAP search returned zero entries for filter \'(' . - join(' | ', $attribute) . ' = ' . $value . ')\' on base(s) \'(' . join(' & ', $bases) . ')\'', 2); + throw $this->makeException('Library - LDAP searchfordn(): LDAP search returned zero entries for filter \'('. + join(' | ', $attribute).' = '.$value.')\' on base(s) \'('.join(' & ', $bases).')\'', 2); } } @@ -316,9 +329,10 @@ class SimpleSAML_Auth_LDAP * @param string|array $attributes Array of attributes requested from LDAP * @param bool $and If multiple filters defined, then either bind them with & or | * @param bool $escape Weather to escape the filter values or not + * @param string $scope The scope of the search * @return array */ - public function searchformultiple($bases, $filters, $attributes = array(), $and = true, $escape = true) + public function searchformultiple($bases, $filters, $attributes = array(), $and = true, $escape = true, $scope = 'subtree') { // Escape the filter values, if requested if ($escape) { @@ -332,7 +346,7 @@ class SimpleSAML_Auth_LDAP $filter .= "($attribute=$value)"; } if (count($filters) > 1) { - $filter = ($and ? '(&' : '(|') . $filter . ')'; + $filter = ($and ? '(&' : '(|').$filter.')'; } } elseif (is_string($filters)) { $filter = $filters; @@ -352,7 +366,14 @@ class SimpleSAML_Auth_LDAP // Search each base until result is found $result = false; foreach ($bases as $base) { - $result = @ldap_search($this->ldap, $base, $filter, $attributes, 0, 0, $this->timeout); + if ($scope === 'base') { + $result = @ldap_read($this->ldap, $base, $filter, $attributes, 0, 0, $this->timeout); + } else if ($scope === 'onelevel') { + $result = @ldap_list($this->ldap, $base, $filter, $attributes, 0, 0, $this->timeout); + } else { + $result = @ldap_search($this->ldap, $base, $filter, $attributes, 0, 0, $this->timeout); + } + if ($result !== false && @ldap_count_entries($this->ldap, $result) > 0) { break; } @@ -361,14 +382,14 @@ class SimpleSAML_Auth_LDAP // Verify that a result was found in one of the bases if ($result === false) { throw $this->makeException( - 'ldap:LdapConnection->search_manual : Failed to search LDAP using base(s) [' . - implode('; ', $bases) . '] with filter [' . $filter . ']. LDAP error [' . - ldap_error($this->ldap) . ']' + 'ldap:LdapConnection->search_manual : Failed to search LDAP using base(s) ['. + implode('; ', $bases).'] with filter ['.$filter.']. LDAP error ['. + ldap_error($this->ldap).']' ); } elseif (@ldap_count_entries($this->ldap, $result) < 1) { throw $this->makeException( - 'ldap:LdapConnection->search_manual : No entries found in LDAP using base(s) [' . - implode('; ', $bases) . '] with filter [' . $filter . ']', + 'ldap:LdapConnection->search_manual : No entries found in LDAP using base(s) ['. + implode('; ', $bases).'] with filter ['.$filter.']', ERR_NO_USER ); } @@ -451,7 +472,7 @@ class SimpleSAML_Auth_LDAP if ($error === true) { // Good $this->authz_id = $authz_id; - SimpleSAML\Logger::debug('Library - LDAP bind(): Bind successful with DN \'' . $dn . '\''); + SimpleSAML\Logger::debug('Library - LDAP bind(): Bind successful with DN \''.$dn.'\''); return true; } @@ -474,7 +495,7 @@ class SimpleSAML_Auth_LDAP } // Bad - throw $this->makeException('Library - LDAP bind(): Bind failed with DN \'' . $dn . '\''); + throw $this->makeException('Library - LDAP bind(): Bind failed with DN \''.$dn.'\''); } @@ -491,16 +512,16 @@ class SimpleSAML_Auth_LDAP // Attempt to set the LDAP option if (!@ldap_set_option($this->ldap, $option, $value)) { throw $this->makeException( - 'ldap:LdapConnection->setOption : Failed to set LDAP option [' . - $option . '] with the value [' . $value . '] error: ' . ldap_error($this->ldap), + 'ldap:LdapConnection->setOption : Failed to set LDAP option ['. + $option.'] with the value ['.$value.'] error: '.ldap_error($this->ldap), ERR_INTERNAL ); } // Log debug message SimpleSAML\Logger::debug( - 'ldap:LdapConnection->setOption : Set the LDAP option [' . - $option . '] with the value [' . $value . ']' + 'ldap:LdapConnection->setOption : Set the LDAP option ['. + $option.'] with the value ['.$value.']' ); } @@ -526,31 +547,31 @@ class SimpleSAML_Auth_LDAP // Preparations, including a pretty debug message... $description = 'all attributes'; if (is_array($attributes)) { - $description = '\'' . join(',', $attributes) . '\''; + $description = '\''.join(',', $attributes).'\''; } else { // Get all attributes... // TODO: Verify that this originally was the intended behaviour. Could $attributes be a string? $attributes = array(); } - SimpleSAML\Logger::debug('Library - LDAP getAttributes(): Getting ' . $description . ' from DN \'' . $dn . '\''); + SimpleSAML\Logger::debug('Library - LDAP getAttributes(): Getting '.$description.' from DN \''.$dn.'\''); // Attempt to get attributes // TODO: Should aliases be dereferenced? $result = @ldap_read($this->ldap, $dn, 'objectClass=*', $attributes, 0, 0, $this->timeout); if ($result === false) { - throw $this->makeException('Library - LDAP getAttributes(): Failed to get attributes from DN \'' . $dn . '\''); + throw $this->makeException('Library - LDAP getAttributes(): Failed to get attributes from DN \''.$dn.'\''); } $entry = @ldap_first_entry($this->ldap, $result); if ($entry === false) { - throw $this->makeException('Library - LDAP getAttributes(): Could not get first entry from DN \'' . $dn . '\''); + throw $this->makeException('Library - LDAP getAttributes(): Could not get first entry from DN \''.$dn.'\''); } - $attributes = @ldap_get_attributes($this->ldap, $entry); // Recycling $attributes... Possibly bad practice. + $attributes = @ldap_get_attributes($this->ldap, $entry); // Recycling $attributes... Possibly bad practice. if ($attributes === false) { - throw $this->makeException('Library - LDAP getAttributes(): Could not get attributes of first entry from DN \'' . $dn . '\''); + throw $this->makeException('Library - LDAP getAttributes(): Could not get attributes of first entry from DN \''.$dn.'\''); } // Parsing each found attribute into our result set - $result = array(); // Recycling $result... Possibly bad practice. + $result = array(); // Recycling $result... Possibly bad practice. for ($i = 0; $i < $attributes['count']; $i++) { // Ignore attributes that exceed the maximum allowed size $name = $attributes[$i]; @@ -561,15 +582,15 @@ class SimpleSAML_Auth_LDAP for ($j = 0; $j < $attribute['count']; $j++) { $value = $attribute[$j]; - if (!empty($maxsize) && strlen($value) >= $maxsize) { + if (!empty($maxsize) && strlen($value) > $maxsize) { // Ignoring and warning - SimpleSAML\Logger::warning('Library - LDAP getAttributes(): Attribute \'' . - $name . '\' exceeded maximum allowed size by ' + ($maxsize - strlen($value))); + SimpleSAML\Logger::warning('Library - LDAP getAttributes(): Attribute \''. + $name.'\' exceeded maximum allowed size by '.(strlen($value) - $maxsize)); continue; } // Base64 encode binary attributes - if (strtolower($name) === 'jpegphoto' || strtolower($name) === 'objectguid') { + if (strtolower($name) === 'jpegphoto' || strtolower($name) === 'objectguid' || strtolower($name) === 'ms-ds-consistencyguid') { $values[] = base64_encode($value); } else { $values[] = $value; @@ -581,7 +602,7 @@ class SimpleSAML_Auth_LDAP } // We're done - SimpleSAML\Logger::debug('Library - LDAP getAttributes(): Found attributes \'(' . join(',', array_keys($result)) . ')\''); + SimpleSAML\Logger::debug('Library - LDAP getAttributes(): Found attributes \'('.join(',', array_keys($result)).')\''); return $result; } @@ -616,7 +637,7 @@ class SimpleSAML_Auth_LDAP // escape characters with a special meaning, also in the password $password = addcslashes($password, ',+"\\<>;*'); if (!$this->bind($dn, $password)) { - SimpleSAML\Logger::info('Library - LDAP validate(): Failed to authenticate \''. $username . '\' using DN \'' . $dn . '\''); + SimpleSAML\Logger::info('Library - LDAP validate(): Failed to authenticate \''.$username.'\' using DN \''.$dn.'\''); return false; } } @@ -658,7 +679,7 @@ class SimpleSAML_Auth_LDAP $val = self::asc2hex32($val); if (null === $val) { - $val = '\0'; // apply escaped "null" if string is empty + $val = '\0'; // apply escaped "null" if string is empty } $values[$key] = $val; diff --git a/lib/SimpleSAML/Auth/TimeLimitedToken.php b/lib/SimpleSAML/Auth/TimeLimitedToken.php index 0fd02dd4f05ffe6ccf686490c9ae1e18edb487df..920dd2d1269d0ae452f5910fa358ba77e23c8ab7 100644 --- a/lib/SimpleSAML/Auth/TimeLimitedToken.php +++ b/lib/SimpleSAML/Auth/TimeLimitedToken.php @@ -130,7 +130,7 @@ class TimeLimitedToken if (count($splittoken) !== 2) { return false; } - $offset = hexdec($splittoken[0]); + $offset = intval(hexdec($splittoken[0])); $value = $splittoken[1]; return ($this->calculateTokenValue($offset) === $value); } diff --git a/lib/SimpleSAML/AuthMemCookie.php b/lib/SimpleSAML/AuthMemCookie.php index 33944677e48399d63c2bbc9863e5dd9e42fea77a..21e3e4485a10136028aad4dc2630914bf4f704ec 100644 --- a/lib/SimpleSAML/AuthMemCookie.php +++ b/lib/SimpleSAML/AuthMemCookie.php @@ -117,7 +117,7 @@ class SimpleSAML_AuthMemCookie $memcacheHost = $this->amcConfig->getString('memcache.host', '127.0.0.1'); $memcachePort = $this->amcConfig->getInteger('memcache.port', 11211); - $class = class_exists('Memcache') ? 'Memcache' : (class_exists('Memcached') ? 'Memcached' : FALSE); + $class = class_exists('Memcache') ? 'Memcache' : (class_exists('Memcached') ? 'Memcached' : false); if (!$class) { throw new Exception('Missing Memcached implementation. You must install either the Memcache or Memcached extension.'); } diff --git a/lib/SimpleSAML/Configuration.php b/lib/SimpleSAML/Configuration.php index 71e618ae9492fb7335122bfceddae19716a62503..7461a2ba3152721af884158880c9e6b0bb34b79a 100644 --- a/lib/SimpleSAML/Configuration.php +++ b/lib/SimpleSAML/Configuration.php @@ -118,7 +118,7 @@ class SimpleSAML_Configuration implements \SimpleSAML\Utils\ClearableState // the file initializes a variable named '$config' ob_start(); - if (interface_exists('Throwable')) { + if (interface_exists('Throwable', false)) { try { require($filename); } catch (ParseError $e) { @@ -194,6 +194,35 @@ class SimpleSAML_Configuration implements \SimpleSAML\Utils\ClearableState self::$configDirs[$configSet] = $path; } + /** + * Store a pre-initialized configuration. + * + * Allows consumers to create configuration objects without having them + * loaded from a file. + * + * @param SimpleSAML_Configuration $config The configuration object to store + * @param string $filename The name of the configuration file. + * @param string $configSet The configuration set. Optional, defaults to 'simplesaml'. + */ + public static function setPreLoadedConfig(SimpleSAML_Configuration $config, $filename = 'config.php', $configSet = 'simplesaml') + { + assert(is_string($filename)); + assert(is_string($configSet)); + + if (!array_key_exists($configSet, self::$configDirs)) { + if ($configSet !== 'simplesaml') { + throw new Exception('Configuration set \'' . $configSet . '\' not initialized.'); + } else { + self::$configDirs['simplesaml'] = dirname(dirname(dirname(__FILE__))) . '/config'; + } + } + + $dir = self::$configDirs[$configSet]; + $filePath = $dir . '/' . $filename; + + self::$loadedConfigs[$filePath] = $config; + } + /** * Load a configuration file from a configuration set. diff --git a/lib/SimpleSAML/Database.php b/lib/SimpleSAML/Database.php index 85e19134ebbdc505adecf1f84c990fe5a2a213a3..8a189386dfb12ea8ae109d65cf8ae7feef0565ce 100644 --- a/lib/SimpleSAML/Database.php +++ b/lib/SimpleSAML/Database.php @@ -90,18 +90,16 @@ class Database // connect to any configured slaves $slaves = $config->getArray('database.slaves', array()); - if (count($slaves >= 1)) { - foreach ($slaves as $slave) { - array_push( - $this->dbSlaves, - $this->connect( - $slave['dsn'], - $slave['username'], - $slave['password'], - $driverOptions - ) - ); - } + foreach ($slaves as $slave) { + array_push( + $this->dbSlaves, + $this->connect( + $slave['dsn'], + $slave['username'], + $slave['password'], + $driverOptions + ) + ); } $this->tablePrefix = $config->getString('database.prefix', ''); diff --git a/lib/SimpleSAML/Error/Assertion.php b/lib/SimpleSAML/Error/Assertion.php index 3497d32b2f4adce9331e1c71f4971e7760abee79..d9a607a97e36164e84acdaf53caa6ebcdc94f12a 100644 --- a/lib/SimpleSAML/Error/Assertion.php +++ b/lib/SimpleSAML/Error/Assertion.php @@ -6,76 +6,80 @@ * @author Olav Morken, UNINETT AS. * @package SimpleSAMLphp */ -class SimpleSAML_Error_Assertion extends SimpleSAML_Error_Exception { - - - /** - * The assertion which failed, or NULL if only an expression was passed to the - * assert-function. - */ - private $assertion; - - - /** - * Constructor for the assertion exception. - * - * Should only be called from the onAssertion handler. - * - * @param string|NULL $assertion The assertion which failed, or NULL if the assert-function was - * given an expression. - */ - public function __construct($assertion = NULL) { - assert($assertion === null || is_string($assertion)); - - $msg = 'Assertion failed: ' . var_export($assertion, TRUE); - parent::__construct($msg); - - $this->assertion = $assertion; - } - - - /** - * Retrieve the assertion which failed. - * - * @return string|NULL The assertion which failed, or NULL if the assert-function was called with an expression. - */ - public function getAssertion() { - return $this->assertion; - } - - - /** - * Install this assertion handler. - * - * This function will register this assertion handler. If will not enable assertions if they are - * disabled. - */ - public static function installHandler() { - - assert_options(ASSERT_WARNING, 0); - assert_options(ASSERT_QUIET_EVAL, 0); - assert_options(ASSERT_CALLBACK, array('SimpleSAML_Error_Assertion', 'onAssertion')); - } - - - /** - * Handle assertion. - * - * This function handles an assertion. - * - * @param string $file The file assert was called from. - * @param int $line The line assert was called from. - * @param mixed $message The expression which was passed to the assert-function. - */ - public static function onAssertion($file, $line, $message) { - - if(!empty($message)) { - $exception = new self($message); - } else { - $exception = new self(); - } - - $exception->logError(); - } - +class SimpleSAML_Error_Assertion extends SimpleSAML_Error_Exception +{ + + + /** + * The assertion which failed, or null if only an expression was passed to the + * assert-function. + */ + private $assertion; + + + /** + * Constructor for the assertion exception. + * + * Should only be called from the onAssertion handler. + * + * @param string|null $assertion The assertion which failed, or null if the assert-function was + * given an expression. + */ + public function __construct($assertion = null) + { + assert($assertion === null || is_string($assertion)); + + $msg = 'Assertion failed: ' . var_export($assertion, true); + parent::__construct($msg); + + $this->assertion = $assertion; + } + + + /** + * Retrieve the assertion which failed. + * + * @return string|null The assertion which failed, or null if the assert-function was called with an expression. + */ + public function getAssertion() + { + return $this->assertion; + } + + + /** + * Install this assertion handler. + * + * This function will register this assertion handler. If will not enable assertions if they are + * disabled. + */ + public static function installHandler() + { + + assert_options(ASSERT_WARNING, 0); + assert_options(ASSERT_QUIET_EVAL, 0); + assert_options(ASSERT_CALLBACK, array('SimpleSAML_Error_Assertion', 'onAssertion')); + } + + + /** + * Handle assertion. + * + * This function handles an assertion. + * + * @param string $file The file assert was called from. + * @param int $line The line assert was called from. + * @param mixed $message The expression which was passed to the assert-function. + */ + public static function onAssertion($file, $line, $message) + { + + if (!empty($message)) { + $exception = new self($message); + } else { + $exception = new self(); + } + + $exception->logError(); + } } diff --git a/lib/SimpleSAML/Error/AuthSource.php b/lib/SimpleSAML/Error/AuthSource.php index 119ffc4b38740d61c86f5f1be96f015dad42fd62..107083eae66b2fe3153c48eb08d0b249475717af 100644 --- a/lib/SimpleSAML/Error/AuthSource.php +++ b/lib/SimpleSAML/Error/AuthSource.php @@ -1,68 +1,70 @@ <?php /** * Baseclass for auth source exceptions. - * + * * @package SimpleSAMLphp_base * */ -class SimpleSAML_Error_AuthSource extends SimpleSAML_Error_Error { - +class SimpleSAML_Error_AuthSource extends SimpleSAML_Error_Error +{ - /** - * Authsource module name. - */ - private $authsource; + /** + * Authsource module name. + */ + private $authsource; - /** - * Reason why this request was invalid. - */ - private $reason; + /** + * Reason why this request was invalid. + */ + private $reason; - /** - * Create a new AuthSource error. - * - * @param string $authsource Authsource module name from where this error was thrown. - * @param string $reason Description of the error. - */ - public function __construct($authsource, $reason, $cause = NULL) { - assert(is_string($authsource)); - assert(is_string($reason)); - $this->authsource = $authsource; - $this->reason = $reason; - parent::__construct( - array( - 'AUTHSOURCEERROR', - '%AUTHSOURCE%' => htmlspecialchars(var_export($this->authsource, TRUE)), - '%REASON%' => htmlspecialchars(var_export($this->reason, TRUE)) - ), - $cause - ); + /** + * Create a new AuthSource error. + * + * @param string $authsource Authsource module name from where this error was thrown. + * @param string $reason Description of the error. + */ + public function __construct($authsource, $reason, $cause = null) + { + assert(is_string($authsource)); + assert(is_string($reason)); - $this->message = "Error with authentication source '$authsource': $reason"; - } + $this->authsource = $authsource; + $this->reason = $reason; + parent::__construct( + array( + 'AUTHSOURCEERROR', + '%AUTHSOURCE%' => htmlspecialchars(var_export($this->authsource, true)), + '%REASON%' => htmlspecialchars(var_export($this->reason, true)) + ), + $cause + ); + $this->message = "Error with authentication source '$authsource': $reason"; + } - /** - * Retrieve the authsource module name from where this error was thrown. - * - * @return string Authsource module name. - */ - public function getAuthSource() { - return $this->authsource; - } + /** + * Retrieve the authsource module name from where this error was thrown. + * + * @return string Authsource module name. + */ + public function getAuthSource() + { + return $this->authsource; + } - /** - * Retrieve the reason why the request was invalid. - * - * @return string The reason why the request was invalid. - */ - public function getReason() { - return $this->reason; - } - + /** + * Retrieve the reason why the request was invalid. + * + * @return string The reason why the request was invalid. + */ + public function getReason() + { + return $this->reason; + } } diff --git a/lib/SimpleSAML/Error/BadRequest.php b/lib/SimpleSAML/Error/BadRequest.php index 21b57296b53ca363d58d59f2f0f690ab8e0082a5..79710cce3cb365652f18060502814bfda0066a57 100644 --- a/lib/SimpleSAML/Error/BadRequest.php +++ b/lib/SimpleSAML/Error/BadRequest.php @@ -9,36 +9,38 @@ * @author Olav Morken, UNINETT AS. * @package SimpleSAMLphp */ -class SimpleSAML_Error_BadRequest extends SimpleSAML_Error_Error { - - - /** - * Reason why this request was invalid. - */ - private $reason; - - - /** - * Create a new BadRequest error. - * - * @param string $reason Description of why the request was unacceptable. - */ - public function __construct($reason) { - assert(is_string($reason)); - - $this->reason = $reason; - parent::__construct(array('BADREQUEST', '%REASON%' => $this->reason)); - $this->httpCode = 400; - } - - - /** - * Retrieve the reason why the request was invalid. - * - * @return string The reason why the request was invalid. - */ - public function getReason() { - return $this->reason; - } - +class SimpleSAML_Error_BadRequest extends SimpleSAML_Error_Error +{ + + + /** + * Reason why this request was invalid. + */ + private $reason; + + + /** + * Create a new BadRequest error. + * + * @param string $reason Description of why the request was unacceptable. + */ + public function __construct($reason) + { + assert(is_string($reason)); + + $this->reason = $reason; + parent::__construct(array('BADREQUEST', '%REASON%' => $this->reason)); + $this->httpCode = 400; + } + + + /** + * Retrieve the reason why the request was invalid. + * + * @return string The reason why the request was invalid. + */ + public function getReason() + { + return $this->reason; + } } diff --git a/lib/SimpleSAML/Error/BadUserInnput.php b/lib/SimpleSAML/Error/BadUserInput.php similarity index 66% rename from lib/SimpleSAML/Error/BadUserInnput.php rename to lib/SimpleSAML/Error/BadUserInput.php index 48386c16ecea80a3275363f7058f8006aafaf122..0e4d721dab73acf903a8e9c4a6c72eb5acfdbec6 100644 --- a/lib/SimpleSAML/Error/BadUserInnput.php +++ b/lib/SimpleSAML/Error/BadUserInput.php @@ -1,11 +1,12 @@ <?php /** * Exception indicating illegal innput from user. - * + * * @author Thomas Graff <thomas.graff@uninett.no> * @package SimpleSAMLphp_base * */ -class SimpleSAML_Error_BadUserInnput extends SimpleSAML_Error_User{ - +class SimpleSAML_Error_BadUserInput extends SimpleSAML_Error_User +{ + } diff --git a/lib/SimpleSAML/Error/CannotSetCookie.php b/lib/SimpleSAML/Error/CannotSetCookie.php index 31a25b0c535bb1006856f8e6f6741f25aac16556..c4e7fee2a18001f61f7a4c08f6dca77cb21395d7 100644 --- a/lib/SimpleSAML/Error/CannotSetCookie.php +++ b/lib/SimpleSAML/Error/CannotSetCookie.php @@ -8,7 +8,6 @@ namespace SimpleSAML\Error; - class CannotSetCookie extends \SimpleSAML_Error_Exception { diff --git a/lib/SimpleSAML/Error/ConfigurationError.php b/lib/SimpleSAML/Error/ConfigurationError.php index 15eb30f366ee918b0226f1345587598c52cb4b2d..574ef62843c9a19f8f7f650c9e5752fb43392848 100644 --- a/lib/SimpleSAML/Error/ConfigurationError.php +++ b/lib/SimpleSAML/Error/ConfigurationError.php @@ -8,7 +8,6 @@ namespace SimpleSAML\Error; - class ConfigurationError extends \SimpleSAML_Error_Error { diff --git a/lib/SimpleSAML/Error/CriticalConfigurationError.php b/lib/SimpleSAML/Error/CriticalConfigurationError.php index 76af87ff8acbcb33fd404e6ff96aabbdc1c50c2a..507fd2bf2222de9e33d5d7157ebf9269707238ec 100644 --- a/lib/SimpleSAML/Error/CriticalConfigurationError.php +++ b/lib/SimpleSAML/Error/CriticalConfigurationError.php @@ -20,7 +20,6 @@ namespace SimpleSAML\Error; - class CriticalConfigurationError extends ConfigurationError { diff --git a/lib/SimpleSAML/Error/Exception.php b/lib/SimpleSAML/Error/Exception.php index 48d74cd2f6ef3159cbc3d8395e4611d5570b3586..6d7efcc736704d611b8c0d7141140d93aa5193bc 100644 --- a/lib/SimpleSAML/Error/Exception.php +++ b/lib/SimpleSAML/Error/Exception.php @@ -87,7 +87,6 @@ class SimpleSAML_Error_Exception extends Exception $pos = $exception->getFile().':'.$exception->getLine(); foreach ($exception->getTrace() as $t) { - $function = $t['function']; if (array_key_exists('class', $t)) { $function = $t['class'].'::'.$function; diff --git a/lib/SimpleSAML/Error/InvalidCredential.php b/lib/SimpleSAML/Error/InvalidCredential.php index 5a3f7d8a4f15fd823a8a67159555d6bc81a6b1bc..d0bbff14588c458f5d2a7eec6b15f131647acb6d 100644 --- a/lib/SimpleSAML/Error/InvalidCredential.php +++ b/lib/SimpleSAML/Error/InvalidCredential.php @@ -1,11 +1,12 @@ <?php /** * Exception indicating wrong password given by user. - * + * * @author Thomas Graff <thomas.graff@uninett.no> * @package SimpleSAMLphp_base * */ -class SimpleSAML_Error_InvalidCredential extends SimpleSAML_Error_User{ - +class SimpleSAML_Error_InvalidCredential extends SimpleSAML_Error_User +{ + } diff --git a/lib/SimpleSAML/Error/MetadataNotFound.php b/lib/SimpleSAML/Error/MetadataNotFound.php index 9fe5a498a6e7eb93812d908d8b9b4f9a98234c10..3aef36584ec1463ff13ad61e9261ca2f71f3fb0a 100644 --- a/lib/SimpleSAML/Error/MetadataNotFound.php +++ b/lib/SimpleSAML/Error/MetadataNotFound.php @@ -5,22 +5,23 @@ * * @package SimpleSAMLphp */ -class SimpleSAML_Error_MetadataNotFound extends SimpleSAML_Error_Error { +class SimpleSAML_Error_MetadataNotFound extends SimpleSAML_Error_Error +{ - /** - * Create the error - * - * @param string $entityId The entityID we were unable to locate. - */ - public function __construct($entityId) { - assert(is_string($entityId)); - - $this->includeTemplate = 'core:no_metadata.tpl.php'; - parent::__construct(array( - 'METADATANOTFOUND', - '%ENTITYID%' => htmlspecialchars(var_export($entityId, TRUE)) - )); - } + /** + * Create the error + * + * @param string $entityId The entityID we were unable to locate. + */ + public function __construct($entityId) + { + assert(is_string($entityId)); + $this->includeTemplate = 'core:no_metadata.tpl.php'; + parent::__construct(array( + 'METADATANOTFOUND', + '%ENTITYID%' => htmlspecialchars(var_export($entityId, true)) + )); + } } diff --git a/lib/SimpleSAML/Error/NoPassive.php b/lib/SimpleSAML/Error/NoPassive.php index 8966dc8b527497999edea34fc9db2cc47451fa1e..2f53433450aedc158088a4de9cfcad9023753af5 100644 --- a/lib/SimpleSAML/Error/NoPassive.php +++ b/lib/SimpleSAML/Error/NoPassive.php @@ -9,6 +9,7 @@ * * @see \SimpleSAML\Module\saml\Error\NoPassive */ -class SimpleSAML_Error_NoPassive extends SimpleSAML_Error_Exception { +class SimpleSAML_Error_NoPassive extends SimpleSAML_Error_Exception +{ } diff --git a/lib/SimpleSAML/Error/NoState.php b/lib/SimpleSAML/Error/NoState.php index 1c92da92728a3d8086f484fb8f9f383903e58294..7b4adb196a490daf3be0bba00648b04ef714ca35 100644 --- a/lib/SimpleSAML/Error/NoState.php +++ b/lib/SimpleSAML/Error/NoState.php @@ -6,15 +6,16 @@ * * @package SimpleSAMLphp */ -class SimpleSAML_Error_NoState extends SimpleSAML_Error_Error { +class SimpleSAML_Error_NoState extends SimpleSAML_Error_Error +{ - /** - * Create the error - */ - public function __construct() { - $this->includeTemplate = 'core:no_state.tpl.php'; - parent::__construct('NOSTATE'); - } - + /** + * Create the error + */ + public function __construct() + { + $this->includeTemplate = 'core:no_state.tpl.php'; + parent::__construct('NOSTATE'); + } } diff --git a/lib/SimpleSAML/Error/NotFound.php b/lib/SimpleSAML/Error/NotFound.php index 0c1f460a3367d6f03c2d45b2d0a0bc40f07cb59a..7a64ec31ff844083d806c0e2b8640b2b21d675ad 100644 --- a/lib/SimpleSAML/Error/NotFound.php +++ b/lib/SimpleSAML/Error/NotFound.php @@ -9,60 +9,64 @@ * @author Olav Morken, UNINETT AS. * @package SimpleSAMLphp */ -class SimpleSAML_Error_NotFound extends SimpleSAML_Error_Error { +class SimpleSAML_Error_NotFound extends SimpleSAML_Error_Error +{ - /** - * Reason why the given page could not be found. - */ - private $reason; + /** + * Reason why the given page could not be found. + */ + private $reason; - /** - * Create a new NotFound error - * - * @param string $reason Optional description of why the given page could not be found. - */ - public function __construct($reason = NULL) { + /** + * Create a new NotFound error + * + * @param string $reason Optional description of why the given page could not be found. + */ + public function __construct($reason = null) + { - assert($reason === null || is_string($reason)); + assert($reason === null || is_string($reason)); - $url = \SimpleSAML\Utils\HTTP::getSelfURL(); + $url = \SimpleSAML\Utils\HTTP::getSelfURL(); - if($reason === NULL) { - parent::__construct(array('NOTFOUND', '%URL%' => $url)); - $this->message = "The requested page '$url' could not be found."; - } else { - parent::__construct(array('NOTFOUNDREASON', '%URL%' => $url, '%REASON%' => $reason)); - $this->message = "The requested page '$url' could not be found. ".$reason; - } + if ($reason === null) { + parent::__construct(array('NOTFOUND', '%URL%' => $url)); + $this->message = "The requested page '$url' could not be found."; + } else { + parent::__construct(array('NOTFOUNDREASON', '%URL%' => $url, '%REASON%' => $reason)); + $this->message = "The requested page '$url' could not be found. ".$reason; + } - $this->reason = $reason; - $this->httpCode = 404; - } + $this->reason = $reason; + $this->httpCode = 404; + } - /** - * Retrieve the reason why the given page could not be found. - * - * @return string|NULL The reason why the page could not be found. - */ - public function getReason() { - return $this->reason; - } + /** + * Retrieve the reason why the given page could not be found. + * + * @return string|null The reason why the page could not be found. + */ + public function getReason() + { + return $this->reason; + } - /** - * NotFound exceptions don't need to display a backtrace, as they are very simple and the trace is usually trivial, - * so just log the message without any backtrace at all. - * - * @param bool $anonymize Whether to anonymize the trace or not. - * - * @return array - */ - public function format($anonymize = false) { - return array( - $this->getClass().': '.$this->getMessage(), - ); - } + /** + * NotFound exceptions don't need to display a backtrace, as they are very simple and the trace is usually trivial, + * so just log the message without any backtrace at all. + * + * @param bool $anonymize Whether to anonymize the trace or not. + * + * @return array + */ + public function format($anonymize = false) + { + return array( + $this->getClass().': '.$this->getMessage(), + ); + } } diff --git a/lib/SimpleSAML/Error/ProxyCountExceeded.php b/lib/SimpleSAML/Error/ProxyCountExceeded.php index 0af64d51e740db277e523d89284290d6b16a3122..1462d371aae5334a62d8344972e4ced143729e01 100644 --- a/lib/SimpleSAML/Error/ProxyCountExceeded.php +++ b/lib/SimpleSAML/Error/ProxyCountExceeded.php @@ -9,6 +9,7 @@ * * @see \SimpleSAML\Module\saml\Error\ProxyCountExceeded */ -class SimpleSAML_Error_ProxyCountExceeded extends SimpleSAML_Error_Exception { +class SimpleSAML_Error_ProxyCountExceeded extends SimpleSAML_Error_Exception +{ } diff --git a/lib/SimpleSAML/Error/UnserializableException.php b/lib/SimpleSAML/Error/UnserializableException.php index 6c55bece1381dcc74eac1ada769fcf65d5333002..c134939530e863075aae867d7652d14ea9921258 100644 --- a/lib/SimpleSAML/Error/UnserializableException.php +++ b/lib/SimpleSAML/Error/UnserializableException.php @@ -12,44 +12,46 @@ * * @package SimpleSAMLphp */ -class SimpleSAML_Error_UnserializableException extends SimpleSAML_Error_Exception { - - /** - * The classname of the original exception. - * - * @var string - */ - private $class; - - - /** - * Create a serializable exception representing an unserializable exception. - * - * @param Exception $original The original exception. - */ - public function __construct(Exception $original) { - - $this->class = get_class($original); - $msg = $original->getMessage(); - $code = $original->getCode(); - - if (!is_int($code)) { - // PDOException uses a string as the code. Filter it out here. - $code = -1; - } - - parent::__construct($msg, $code); - $this->initBacktrace($original); - } - - - /** - * Retrieve the class of this exception. - * - * @return string The classname. - */ - public function getClass() { - return $this->class; - } - +class SimpleSAML_Error_UnserializableException extends SimpleSAML_Error_Exception +{ + + /** + * The classname of the original exception. + * + * @var string + */ + private $class; + + + /** + * Create a serializable exception representing an unserializable exception. + * + * @param Exception $original The original exception. + */ + public function __construct(Exception $original) + { + + $this->class = get_class($original); + $msg = $original->getMessage(); + $code = $original->getCode(); + + if (!is_int($code)) { + // PDOException uses a string as the code. Filter it out here. + $code = -1; + } + + parent::__construct($msg, $code); + $this->initBacktrace($original); + } + + + /** + * Retrieve the class of this exception. + * + * @return string The classname. + */ + public function getClass() + { + return $this->class; + } } diff --git a/lib/SimpleSAML/Error/User.php b/lib/SimpleSAML/Error/User.php index 2f674be18f97d6f59063810f5cde54820974a9dd..4e70db01feae19bda4d6b00f1b02391a3f465a16 100644 --- a/lib/SimpleSAML/Error/User.php +++ b/lib/SimpleSAML/Error/User.php @@ -2,12 +2,13 @@ /** * Baseclass for user error exceptions - * - * + * + * * @author Thomas Graff <thomas.graff@uninett.no> * @package SimpleSAMLphp_base * */ -class SimpleSAML_Error_User extends SimpleSAML_Error_Exception{ - +class SimpleSAML_Error_User extends SimpleSAML_Error_Exception +{ + } diff --git a/lib/SimpleSAML/Error/UserAborted.php b/lib/SimpleSAML/Error/UserAborted.php index 6bd2762afae00d4b0aad0630092cb550683af820..9ddcb0fac5ddf2d179da7a63d6e69a933d3b52b1 100644 --- a/lib/SimpleSAML/Error/UserAborted.php +++ b/lib/SimpleSAML/Error/UserAborted.php @@ -5,15 +5,16 @@ * * @package SimpleSAMLphp */ -class SimpleSAML_Error_UserAborted extends SimpleSAML_Error_Error { - - /** - * Create the error - * - * @param Exception|NULL $cause The exception that caused this error. - */ - public function __construct(Exception $cause = NULL) { - parent::__construct('USERABORTED', $cause); - } +class SimpleSAML_Error_UserAborted extends SimpleSAML_Error_Error +{ + /** + * Create the error + * + * @param Exception|null $cause The exception that caused this error. + */ + public function __construct(Exception $cause = null) + { + parent::__construct('USERABORTED', $cause); + } } diff --git a/lib/SimpleSAML/Error/UserNotFound.php b/lib/SimpleSAML/Error/UserNotFound.php index c536f724301e96596e805ed63dcf828f488f2335..cc782dd79591b995705932a9156449cc802059c6 100644 --- a/lib/SimpleSAML/Error/UserNotFound.php +++ b/lib/SimpleSAML/Error/UserNotFound.php @@ -2,11 +2,12 @@ /** * Exception indicating user not found by authsource. - * + * * @author Thomas Graff <thomas.graff@uninett.no> * @package SimpleSAMLphp_base * */ -class SimpleSAML_Error_UserNotFound extends SimpleSAML_Error_User{ - +class SimpleSAML_Error_UserNotFound extends SimpleSAML_Error_User +{ + } diff --git a/lib/SimpleSAML/IdP/IFrameLogoutHandler.php b/lib/SimpleSAML/IdP/IFrameLogoutHandler.php index 23a1daa85e8e7f506dd3f67a18ee67ff18f959f4..b08440343920f0929870efbf1729d07d79290c77 100644 --- a/lib/SimpleSAML/IdP/IFrameLogoutHandler.php +++ b/lib/SimpleSAML/IdP/IFrameLogoutHandler.php @@ -5,7 +5,6 @@ namespace SimpleSAML\IdP; use SimpleSAML\Module; use SimpleSAML\Utils\HTTP; - /** * Class that handles iframe logout. * diff --git a/lib/SimpleSAML/IdP/LogoutHandlerInterface.php b/lib/SimpleSAML/IdP/LogoutHandlerInterface.php index 11589f3d0f56478e114f30b6fbac115fc67a13bb..011dd63c66ae2b5d6296c0888952ea3e613b9d8f 100644 --- a/lib/SimpleSAML/IdP/LogoutHandlerInterface.php +++ b/lib/SimpleSAML/IdP/LogoutHandlerInterface.php @@ -2,7 +2,6 @@ namespace SimpleSAML\IdP; - /** * Interface that all logout handlers must implement. * diff --git a/lib/SimpleSAML/IdP/TraditionalLogoutHandler.php b/lib/SimpleSAML/IdP/TraditionalLogoutHandler.php index 61756f32e55e55513b8938c9a68940653533e8d6..105ac2796ecc77d8911daddf7cc2f81295f19da5 100644 --- a/lib/SimpleSAML/IdP/TraditionalLogoutHandler.php +++ b/lib/SimpleSAML/IdP/TraditionalLogoutHandler.php @@ -5,7 +5,6 @@ namespace SimpleSAML\IdP; use SimpleSAML\Logger; use SimpleSAML\Utils\HTTP; - /** * Class that handles traditional logout. * diff --git a/lib/SimpleSAML/Locale/Localization.php b/lib/SimpleSAML/Locale/Localization.php index cbfc42c304b4dddd03e79622f88e0f2a6f4b538d..9a023f9b4cf68bec4e170e77606c5e4d9f9001eb 100644 --- a/lib/SimpleSAML/Locale/Localization.php +++ b/lib/SimpleSAML/Locale/Localization.php @@ -262,5 +262,4 @@ class Localization { return $this->localeDomainMap; } - } diff --git a/lib/SimpleSAML/Locale/Translate.php b/lib/SimpleSAML/Locale/Translate.php index 9cb0000bd8ebe9835a5ff2985e163a46aed44c5a..00ac6e5ee72c0aa2ff1ed671e75140e0c33fe07e 100644 --- a/lib/SimpleSAML/Locale/Translate.php +++ b/lib/SimpleSAML/Locale/Translate.php @@ -259,9 +259,12 @@ class Translate public function t( $tag, $replacements = array(), - $fallbackdefault = true, // TODO: remove this for 2.0. Assume true - $oldreplacements = array(), // TODO: remove this for 2.0 - $striptags = false // TODO: remove this for 2.0 + // TODO: remove this for 2.0. Assume true + $fallbackdefault = true, + // TODO: remove this for 2.0 + $oldreplacements = array(), + // TODO: remove this for 2.0 + $striptags = false ) { $backtrace = debug_backtrace(); $where = $backtrace[0]['file'].':'.$backtrace[0]['line']; diff --git a/lib/SimpleSAML/Logger.php b/lib/SimpleSAML/Logger.php index a383addbd76782bde5972fc6c32ed9895d2bdd3f..4f6b7a9c7ec13ec2c26399fd0f56d5167c726aa2 100644 --- a/lib/SimpleSAML/Logger.php +++ b/lib/SimpleSAML/Logger.php @@ -2,7 +2,6 @@ namespace SimpleSAML; - /** * The main logger class for SimpleSAMLphp. * @@ -10,7 +9,6 @@ namespace SimpleSAML; * @author Andreas Åkre Solberg, UNINETT AS. <andreas.solberg@uninett.no> * @author Jaime Pérez Crespo, UNINETT AS <jaime.perez@uninett.no> * @package SimpleSAMLphp - * @version $ID$ */ class Logger { diff --git a/lib/SimpleSAML/Memcache.php b/lib/SimpleSAML/Memcache.php index 65f2cba99d36fdf87b827836a1c2b89c0bd2a80d..3f4bba004c37db9e9ef4a34a0ac6ff8da6751197 100644 --- a/lib/SimpleSAML/Memcache.php +++ b/lib/SimpleSAML/Memcache.php @@ -164,8 +164,7 @@ class SimpleSAML_Memcache foreach (self::getMemcacheServers() as $server) { if (self::$extension === 'memcached') { $server->set($key, $savedInfoSerialized, $expire); - } - else { + } else { $server->set($key, $savedInfoSerialized, 0, $expire); } } @@ -310,7 +309,7 @@ class SimpleSAML_Memcache */ private static function loadMemcacheServerGroup(array $group) { - $class = class_exists('Memcache') ? 'Memcache' : (class_exists('Memcached') ? 'Memcached' : FALSE); + $class = class_exists('Memcache') ? 'Memcache' : (class_exists('Memcached') ? 'Memcached' : false); if (!$class) { throw new Exception('Missing Memcached implementation. You must install either the Memcache or Memcached extension.'); } @@ -457,7 +456,7 @@ class SimpleSAML_Memcache $ret = array(); foreach (self::getMemcacheServers() as $sg) { - $stats = $sg->getExtendedStats(); + $stats = method_exists($sg, 'getExtendedStats') ? $sg->getExtendedStats() : $sg->getStats(); foreach ($stats as $server => $data) { if ($data === false) { throw new Exception('Failed to get memcache server status.'); @@ -484,11 +483,10 @@ class SimpleSAML_Memcache $ret = array(); foreach (self::getMemcacheServers() as $sg) { - $stats = $sg->getExtendedStats(); + $stats = method_exists($sg, 'getExtendedStats') ? $sg->getExtendedStats() : $sg->getStats(); $ret[] = $stats; } return $ret; } - } diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php index 95f23244914728ffa8d9ab4ea5b801c108d0ba6f..6091470223ec016ae98580076ced9d2423815540 100644 --- a/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php +++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php @@ -280,7 +280,6 @@ class SimpleSAML_Metadata_MetaDataStorageHandler $metadata = $source->getMetaData($index, $set); if ($metadata !== null) { - if (array_key_exists('expire', $metadata)) { if ($metadata['expire'] < time()) { throw new Exception( @@ -347,7 +346,6 @@ class SimpleSAML_Metadata_MetaDataStorageHandler $result = array_merge($srcList, $result); } foreach ($result as $remote_provider) { - if (sha1($remote_provider['entityid']) == $sha1) { $remote_provider['metadata-set'] = $set; diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerPdo.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerPdo.php index 0e3720e8b0911d33962d097f2e70ff9ab5f263f5..f617c7e28ff7ec8e8f69681eb893651cd665cd01 100644 --- a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerPdo.php +++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerPdo.php @@ -261,5 +261,4 @@ class SimpleSAML_Metadata_MetaDataStorageHandlerPdo extends SimpleSAML_Metadata_ } return $stmt; } - } diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSerialize.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSerialize.php index a9ec6f3c41ae02382db0fcd54cd1218c7fc68b5b..686791152b54c80a592acfbacf4ab0a8cef878ae 100644 --- a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSerialize.php +++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSerialize.php @@ -84,7 +84,6 @@ class SimpleSAML_Metadata_MetaDataStorageHandlerSerialize extends SimpleSAML_Met } while (($entry = readdir($dh)) !== false) { - if ($entry[0] === '.') { // skip '..', '.' and hidden files continue; diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerXML.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerXML.php index 44dba1fcfe357bf20979a1e7b341983e5778ecf3..7e2b50509163583d6ac58101b49d1730b8e9e34b 100644 --- a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerXML.php +++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerXML.php @@ -32,15 +32,17 @@ class SimpleSAML_Metadata_MetaDataStorageHandlerXML extends SimpleSAML_Metadata_ */ protected function __construct($config) { - // get the configuration - $globalConfig = SimpleSAML_Configuration::getInstance(); - + $src = $srcXml = null; if (array_key_exists('file', $config)) { + // get the configuration + $globalConfig = SimpleSAML_Configuration::getInstance(); $src = $globalConfig->resolvePath($config['file']); } elseif (array_key_exists('url', $config)) { $src = $config['url']; + } elseif (array_key_exists('xml', $config)) { + $srcXml = $config['xml']; } else { - throw new Exception("Missing either 'file' or 'url' in XML metadata source configuration."); + throw new Exception("Missing one of 'file', 'url' and 'xml' in XML metadata source configuration."); } @@ -50,9 +52,14 @@ class SimpleSAML_Metadata_MetaDataStorageHandlerXML extends SimpleSAML_Metadata_ $IdP20 = array(); $AAD = array(); - $entities = SimpleSAML_Metadata_SAMLParser::parseDescriptorsFile($src); + if(isset($src)) { + $entities = SimpleSAML_Metadata_SAMLParser::parseDescriptorsFile($src); + } elseif(isset($srcXml)) { + $entities = SimpleSAML_Metadata_SAMLParser::parseDescriptorsString($srcXml); + } else { + throw new Exception("Neither source file path/URI nor string data provided"); + } foreach ($entities as $entityId => $entity) { - $md = $entity->getMetadata1xSP(); if ($md !== null) { $SP1x[$entityId] = $md; diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageSource.php b/lib/SimpleSAML/Metadata/MetaDataStorageSource.php index bc96feeec691e4dd4339ea455ab0f876931064ca..c4e7161b4d840fb54d4db0fffaf0216aed2ffbd4 100644 --- a/lib/SimpleSAML/Metadata/MetaDataStorageSource.php +++ b/lib/SimpleSAML/Metadata/MetaDataStorageSource.php @@ -139,7 +139,6 @@ abstract class SimpleSAML_Metadata_MetaDataStorageSource } foreach ($metadataSet as $index => $entry) { - if (!array_key_exists('host', $entry)) { continue; } @@ -177,7 +176,6 @@ abstract class SimpleSAML_Metadata_MetaDataStorageSource $metadataSet = $this->getMetadataSet($set); foreach ($metadataSet as $index => $entry) { - if (!array_key_exists('hint.cidr', $entry)) { continue; } @@ -262,5 +260,4 @@ abstract class SimpleSAML_Metadata_MetaDataStorageSource return null; } - } diff --git a/lib/SimpleSAML/Metadata/SAMLBuilder.php b/lib/SimpleSAML/Metadata/SAMLBuilder.php index c21df33a5dbfd1bcfc526cdfa6dab355cd642805..a868d5988ba4c912f2d3fa479e82cb6f64691e0a 100644 --- a/lib/SimpleSAML/Metadata/SAMLBuilder.php +++ b/lib/SimpleSAML/Metadata/SAMLBuilder.php @@ -299,8 +299,7 @@ class SimpleSAML_Metadata_SAMLBuilder */ public function addOrganizationInfo(array $metadata) { - if ( - empty($metadata['OrganizationName']) || + if (empty($metadata['OrganizationName']) || empty($metadata['OrganizationDisplayName']) || empty($metadata['OrganizationURL']) ) { diff --git a/lib/SimpleSAML/Metadata/SAMLParser.php b/lib/SimpleSAML/Metadata/SAMLParser.php index 4ee7cdda0735571fae7b5b8bb0e6b6fecfeb2aac..280ca923d207149ed7b6a09c5170b8122b3d3cfd 100644 --- a/lib/SimpleSAML/Metadata/SAMLParser.php +++ b/lib/SimpleSAML/Metadata/SAMLParser.php @@ -211,7 +211,7 @@ class SimpleSAML_Metadata_SAMLParser try { $doc = \SAML2\DOMDocumentFactory::fromString($data); - } catch(\Exception $e) { + } catch (\Exception $e) { throw new Exception('Failed to read XML from file: '.$file); } @@ -231,7 +231,7 @@ class SimpleSAML_Metadata_SAMLParser { try { $doc = \SAML2\DOMDocumentFactory::fromString($metadata); - } catch(\Exception $e) { + } catch (\Exception $e) { throw new Exception('Failed to parse XML string.'); } @@ -292,7 +292,7 @@ class SimpleSAML_Metadata_SAMLParser try { $doc = \SAML2\DOMDocumentFactory::fromString($data); - } catch(\Exception $e) { + } catch (\Exception $e) { throw new Exception('Failed to read XML from file: '.$file); } @@ -319,7 +319,7 @@ class SimpleSAML_Metadata_SAMLParser { try { $doc = \SAML2\DOMDocumentFactory::fromString($string); - } catch(\Exception $e) { + } catch (\Exception $e) { throw new Exception('Failed to parse XML string.'); } @@ -1007,7 +1007,6 @@ class SimpleSAML_Metadata_SAMLParser } foreach ($element->Extensions as $e) { - if ($e instanceof \SAML2\XML\shibmd\Scope) { $ret['scope'][] = $e->scope; continue; @@ -1016,8 +1015,6 @@ class SimpleSAML_Metadata_SAMLParser // Entity Attributes are only allowed at entity level extensions and not at RoleDescriptor level if ($element instanceof \SAML2\XML\md\EntityDescriptor || $element instanceof \SAML2\XML\md\EntitiesDescriptor) { - - if ($e instanceof \SAML2\XML\mdrpi\RegistrationInfo) { // Registration Authority cannot be overridden (warn only if override attempts to change the value) if (isset($ret['RegistrationInfo']['registrationAuthority']) @@ -1059,7 +1056,6 @@ class SimpleSAML_Metadata_SAMLParser // UIInfo elements are only allowed at RoleDescriptor level extensions if ($element instanceof \SAML2\XML\md\RoleDescriptor) { if ($e instanceof \SAML2\XML\mdui\UIInfo) { - $ret['UIInfo']['DisplayName'] = $e->DisplayName; $ret['UIInfo']['Description'] = $e->Description; $ret['UIInfo']['InformationURL'] = $e->InformationURL; @@ -1097,7 +1093,6 @@ class SimpleSAML_Metadata_SAMLParser // DiscoHints elements are only allowed at IDPSSODescriptor level extensions if ($element instanceof \SAML2\XML\md\IDPSSODescriptor) { - if ($e instanceof \SAML2\XML\mdui\DiscoHints) { $ret['DiscoHints']['IPHint'] = $e->IPHint; $ret['DiscoHints']['DomainHint'] = $e->DomainHint; @@ -1464,7 +1459,6 @@ class SimpleSAML_Metadata_SAMLParser $candidates = array(); foreach ($this->validators as $validator) { foreach ($validator->getValidatingCertificates() as $cert) { - $fp = strtolower(sha1(base64_decode($cert))); $candidates[] = $fp; if ($fp === $fingerprint) { diff --git a/lib/SimpleSAML/Metadata/Signer.php b/lib/SimpleSAML/Metadata/Signer.php index 3d3f2eaf9601f7b4235029799076ac5d78afc76e..6397a75da7511da53f18966bc234ebbf7026ed29 100644 --- a/lib/SimpleSAML/Metadata/Signer.php +++ b/lib/SimpleSAML/Metadata/Signer.php @@ -28,11 +28,9 @@ class SimpleSAML_Metadata_Signer if (array_key_exists('metadata.sign.privatekey', $entityMetadata) || array_key_exists('metadata.sign.certificate', $entityMetadata) ) { - if (!array_key_exists('metadata.sign.privatekey', $entityMetadata) || !array_key_exists('metadata.sign.certificate', $entityMetadata) ) { - throw new Exception( 'Missing either the "metadata.sign.privatekey" or the'. ' "metadata.sign.certificate" configuration option in the metadata for'. @@ -79,7 +77,6 @@ class SimpleSAML_Metadata_Signer if (array_key_exists('privatekey', $entityMetadata) || array_key_exists('certificate', $entityMetadata) ) { - if (!array_key_exists('privatekey', $entityMetadata) || !array_key_exists('certificate', $entityMetadata) ) { @@ -246,7 +243,7 @@ class SimpleSAML_Metadata_Signer // convert the metadata to a DOM tree try { $xml = \SAML2\DOMDocumentFactory::fromString($metadataString); - } catch(Exception $e) { + } catch (Exception $e) { throw new Exception('Error parsing self-generated metadata.'); } diff --git a/lib/SimpleSAML/Session.php b/lib/SimpleSAML/Session.php index f7bb281fb39fda37305476d8e132b2c0f03b5969..17e035732c08600ce3dccfd0179f873c89d09a20 100644 --- a/lib/SimpleSAML/Session.php +++ b/lib/SimpleSAML/Session.php @@ -631,7 +631,6 @@ class SimpleSAML_Session implements Serializable if (!$this->transient && (!empty($data['RememberMe']) || $this->rememberMeExpire) && $globalConfig->getBoolean('session.rememberme.enable', false) ) { - $this->setRememberMeExpire(); } else { try { @@ -753,12 +752,16 @@ class SimpleSAML_Session implements Serializable */ public function updateSessionCookies($params = null) { + assert(is_null($params) || is_array($params)); + $sessionHandler = \SimpleSAML\SessionHandler::getSessionHandler(); if ($this->sessionId !== null) { $sessionHandler->setCookie($sessionHandler->getSessionCookieName(), $this->sessionId, $params); } + $params = array_merge($sessionHandler->getCookieParams(), is_array($params) ? $params : array()); + if ($this->authToken !== null) { $globalConfig = SimpleSAML_Configuration::getInstance(); \SimpleSAML\Utils\HTTP::setCookie( diff --git a/lib/SimpleSAML/SessionHandler.php b/lib/SimpleSAML/SessionHandler.php index 7c18ac210c4afbe0b1da6118e442877b38e8e317..a8d4ad0efb00042a03ebd54aa084f4afa4d14d43 100644 --- a/lib/SimpleSAML/SessionHandler.php +++ b/lib/SimpleSAML/SessionHandler.php @@ -97,6 +97,16 @@ abstract class SessionHandler abstract public function loadSession($sessionId = null); + /** + * Check whether the session cookie is set. + * + * This function will only return false if is is certain that the cookie isn't set. + * + * @return bool True if it was set, false if not. + */ + abstract public function hasSessionCookie(); + + /** * Set a session cookie. * @@ -129,19 +139,6 @@ abstract class SessionHandler } - /** - * Check whether the session cookie is set. - * - * This function will only return false if is is certain that the cookie isn't set. - * - * @return bool True if it was set, false if not. - */ - public function hasSessionCookie() - { - return true; - } - - /** * Get the cookie parameters that should be used for session cookies. * diff --git a/lib/SimpleSAML/SessionHandlerPHP.php b/lib/SimpleSAML/SessionHandlerPHP.php index 067c384d050d683bea83ace4144558eedd76f4ba..30f3d909ab56b9c0688bc118401f239937394295 100644 --- a/lib/SimpleSAML/SessionHandlerPHP.php +++ b/lib/SimpleSAML/SessionHandlerPHP.php @@ -76,13 +76,15 @@ class SessionHandlerPHP extends SessionHandler $params = $this->getCookieParams(); - session_set_cookie_params( - $params['lifetime'], - $params['path'], - $params['domain'], - $params['secure'], - $params['httponly'] - ); + if (!headers_sent()) { + session_set_cookie_params( + $params['lifetime'], + $params['path'], + $params['domain'], + $params['secure'], + $params['httponly'] + ); + } $savepath = $config->getString('session.phpsession.savepath', null); if (!empty($savepath)) { @@ -112,8 +114,8 @@ class SessionHandlerPHP extends SessionHandler */ session_cache_limiter(''); } - @session_start(); session_cache_limiter($cacheLimiter); + @session_start(); } diff --git a/lib/SimpleSAML/Stats.php b/lib/SimpleSAML/Stats.php index e710a373ef1c31e59a49bf3d1a8d7919e76d1712..36b6f66267a6e0d72522cdc0f7afe78d4499af11 100644 --- a/lib/SimpleSAML/Stats.php +++ b/lib/SimpleSAML/Stats.php @@ -97,5 +97,4 @@ class SimpleSAML_Stats $out->emit($data); } } - } diff --git a/lib/SimpleSAML/Store/Redis.php b/lib/SimpleSAML/Store/Redis.php index 2f568e3600544953edfd2c43384ffe1be654850e..d102b01fd5d955fa2f7a880ba32cc7c320412b7e 100644 --- a/lib/SimpleSAML/Store/Redis.php +++ b/lib/SimpleSAML/Store/Redis.php @@ -98,7 +98,8 @@ class Redis extends Store if ($expire === null) { $this->redis->set("{$type}.{$key}", $serialized); } else { - $this->redis->setex("{$type}.{$key}", $expire, $serialized); + // setex expire time is in seconds (not unix timestamp) + $this->redis->setex("{$type}.{$key}", $expire - time(), $serialized); } } diff --git a/lib/SimpleSAML/Store/SQL.php b/lib/SimpleSAML/Store/SQL.php old mode 100755 new mode 100644 index 614a7f7b8840cfb0b79174a5a16b9ef577e7b587..adfa6b56e2e372039339daedd8d2034a7249b2a6 --- a/lib/SimpleSAML/Store/SQL.php +++ b/lib/SimpleSAML/Store/SQL.php @@ -55,9 +55,13 @@ class SQL extends Store $dsn = $config->getString('store.sql.dsn'); $username = $config->getString('store.sql.username', null); $password = $config->getString('store.sql.password', null); + $options = $config->getArray('store.sql.options', null); $this->prefix = $config->getString('store.sql.prefix', 'simpleSAMLphp'); - - $this->pdo = new \PDO($dsn, $username, $password); + try { + $this->pdo = new \PDO($dsn, $username, $password, $options); + } catch (\PDOException $e) { + throw new \Exception("Database error: " . $e->getMessage()); + } $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME); diff --git a/lib/SimpleSAML/Utils/Attributes.php b/lib/SimpleSAML/Utils/Attributes.php index 00e934f5e192e3332be5078cc7ce493bd8fdef02..1f49413625ec602f12d450d2964d120e8d6a7ad9 100644 --- a/lib/SimpleSAML/Utils/Attributes.php +++ b/lib/SimpleSAML/Utils/Attributes.php @@ -48,7 +48,6 @@ class Attributes if (count($attribute) === 0) { throw new \SimpleSAML_Error_Exception("Empty attribute '".$expected."'.'"); - } elseif (count($attribute) > 1) { if ($allow_multiple === false) { throw new \SimpleSAML_Error_Exception( diff --git a/lib/SimpleSAML/Utils/Auth.php b/lib/SimpleSAML/Utils/Auth.php index 5360f74da0082e23911da5a27261a989a8892a78..cd2dbc29e38c484b05a7d05718b0380964e1bd4e 100644 --- a/lib/SimpleSAML/Utils/Auth.php +++ b/lib/SimpleSAML/Utils/Auth.php @@ -73,4 +73,4 @@ class Auth ); } } -} \ No newline at end of file +} diff --git a/lib/SimpleSAML/Utils/Config/Metadata.php b/lib/SimpleSAML/Utils/Config/Metadata.php index 632ec04cca8156bb31e1c7ee0ea452a48466d30e..d1974308f4987cfbe431bffe1e1f261a1f980f7c 100644 --- a/lib/SimpleSAML/Utils/Config/Metadata.php +++ b/lib/SimpleSAML/Utils/Config/Metadata.php @@ -117,8 +117,8 @@ class Metadata // check attributes is an associative array if (isset($contact['attributes'])) { - if (empty($contact['attributes']) - || !is_array($contact['attributes']) + if (empty($contact['attributes']) + || !is_array($contact['attributes']) || count(array_filter(array_keys($contact['attributes']), 'is_string')) === 0 ) { throw new \InvalidArgumentException('"attributes" must be an array and cannot be empty.'); diff --git a/lib/SimpleSAML/Utils/HTTP.php b/lib/SimpleSAML/Utils/HTTP.php index 96cfc62ecc5146a83a5bafdeb4fdc877272e65dd..7233c9cc19841f6c381976d7a852565503c3eaf6 100644 --- a/lib/SimpleSAML/Utils/HTTP.php +++ b/lib/SimpleSAML/Utils/HTTP.php @@ -68,7 +68,7 @@ class HTTP if (!is_numeric($port)) { array_push($decomposed, $port); } - $current = implode($decomposed, ":"); + $current = implode(":", $decomposed); } return $current; } @@ -108,15 +108,14 @@ class HTTP */ public static function getServerPort() { - $port = (isset($_SERVER['SERVER_PORT'])) ? $_SERVER['SERVER_PORT'] : '80'; - if (self::getServerHTTPS()) { - if ($port !== '443') { - return ':'.$port; - } - } else { - if ($port !== '80') { - return ':'.$port; - } + $default_port = self::getServerHTTPS() ? '443' : '80'; + $port = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : $default_port; + + // Take care of edge-case where SERVER_PORT is an integer + $port = strval($port); + + if ($port !== $default_port) { + return ':'.$port; } return ''; } @@ -341,6 +340,13 @@ class HTTP $components = parse_url($url); $hostname = $components['host']; + // check for userinfo + if ((isset($components['user']) && strpos($components['user'], '\\') !== false) || + (isset($components['pass']) && strpos($components['pass'], '\\') !== false) + ) { + throw new \SimpleSAML_Error_Exception('Invalid URL: '.$url); + } + // allow URLs with standard ports specified (non-standard ports must then be allowed explicitly) if (isset($components['port']) && (($components['scheme'] === 'http' && $components['port'] !== 80) || @@ -414,7 +420,7 @@ class HTTP } $proxy_auth = $config->getString('proxy.auth', false); if ($proxy_auth !== false) { - $context['http']['header'] = "Proxy-Authorization: Basic".base64_encode($proxy_auth); + $context['http']['header'] = "Proxy-Authorization: Basic ".base64_encode($proxy_auth); } if (!isset($context['http']['request_fulluri'])) { $context['http']['request_fulluri'] = true; @@ -596,8 +602,7 @@ class HTTP if (preg_match('#^https?://.*/?$#D', $baseURL, $matches)) { // full URL in baseurlpath, override local server values return rtrim($baseURL, '/').'/'; - } elseif ( - (preg_match('#^/?([^/]?.*/)$#D', $baseURL, $matches)) || + } elseif ((preg_match('#^/?([^/]?.*/)$#D', $baseURL, $matches)) || (preg_match('#^\*(.*)/$#D', $baseURL, $matches)) || ($baseURL === '') ) { @@ -793,7 +798,6 @@ class HTTP $hostname = parse_url($appurl, PHP_URL_HOST); $port = parse_url($appurl, PHP_URL_PORT); $port = !empty($port) ? ':'.$port : ''; - } else { // no base URL specified for app, just use the current URL $protocol = 'http'; $protocol .= (self::getServerHTTPS()) ? 's' : ''; @@ -965,7 +969,7 @@ class HTTP /** * This function redirects to the specified URL after performing the appropriate security checks on it. - * Particularly, it will make sure that the provided URL is allowed by the 'redirect.trustedsites' directive in the + * Particularly, it will make sure that the provided URL is allowed by the 'trusted.url.domains' directive in the * configuration. * * If the aforementioned option is not set or the URL does correspond to a trusted site, it performs a redirection diff --git a/lib/SimpleSAML/Utils/Random.php b/lib/SimpleSAML/Utils/Random.php index b53ace2e5a5f40e1af4632c40db0a3a4421018ac..86937294e2b5c6ed3ca907bbc51ad5705a092fc7 100644 --- a/lib/SimpleSAML/Utils/Random.php +++ b/lib/SimpleSAML/Utils/Random.php @@ -27,4 +27,4 @@ class Random { return '_'.bin2hex(openssl_random_pseudo_bytes((int)((self::ID_LENGTH - 1)/2))); } -} \ No newline at end of file +} diff --git a/lib/SimpleSAML/Utils/Time.php b/lib/SimpleSAML/Utils/Time.php index be8d47b2d9986a55c12d849bc490a42e59dcaef9..21c005e50784fd16bbf707e7ac80bafe3585b064 100644 --- a/lib/SimpleSAML/Utils/Time.php +++ b/lib/SimpleSAML/Utils/Time.php @@ -9,7 +9,6 @@ namespace SimpleSAML\Utils; use SimpleSAML\Logger; - class Time { diff --git a/lib/SimpleSAML/XHTML/EMail.php b/lib/SimpleSAML/XHTML/EMail.php index 9638920e940b5f8dd1cedb190f15f49fc0fc7272..5796ec1c028f7cf6b745465a8259dd984e4a65f8 100644 --- a/lib/SimpleSAML/XHTML/EMail.php +++ b/lib/SimpleSAML/XHTML/EMail.php @@ -8,43 +8,44 @@ */ class SimpleSAML_XHTML_EMail { - private $to = null; - private $cc = null; - private $body = null; - private $from = null; - private $replyto = null; - private $subject = null; - private $headers = array(); - - - /** - * Constructor - */ - public function __construct($to, $subject, $from = null, $cc = null, $replyto = null) + private $to = null; + private $cc = null; + private $body = null; + private $from = null; + private $replyto = null; + private $subject = null; + private $headers = array(); + + + /** + * Constructor + */ + public function __construct($to, $subject, $from = null, $cc = null, $replyto = null) { - $this->to = $to; - $this->cc = $cc; - $this->from = $from; - $this->replyto = $replyto; - $this->subject = $subject; - } - - /* + $this->to = $to; + $this->cc = $cc; + $this->from = $from; + $this->replyto = $replyto; + $this->subject = $subject; + } + + /* * @param string $body * @return void */ public function setBody($body) { - $this->body = $body; - } + $this->body = $body; + } - /* + /* * @param string $body * @return void */ - private function getHTML($body) { - return '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + private function getHTML($body) + { + return '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> @@ -68,32 +69,34 @@ pre { </div> </body> </html>'; - } + } /* * @return void */ - public function send() + public function send() { - if ($this->to === null) { + if ($this->to === null) { throw new Exception('EMail field [to] is required and not set.'); } elseif ($this->subject === null) { throw new Exception('EMail field [subject] is required and not set.'); } elseif ($this->body === null) { throw new Exception('EMail field [body] is required and not set.'); } - - $random_hash = bin2hex(openssl_random_pseudo_bytes(16)); - - if (isset($this->from)) - $this->headers[]= 'From: ' . $this->from; - if (isset($this->replyto)) - $this->headers[]= 'Reply-To: ' . $this->replyto; - - $this->headers[] = 'Content-Type: multipart/alternative; boundary="simplesamlphp-' . $random_hash . '"'; - - $message = ' + + $random_hash = bin2hex(openssl_random_pseudo_bytes(16)); + + if (isset($this->from)) { + $this->headers[]= 'From: ' . $this->from; + } + if (isset($this->replyto)) { + $this->headers[]= 'Reply-To: ' . $this->replyto; + } + + $this->headers[] = 'Content-Type: multipart/alternative; boundary="simplesamlphp-' . $random_hash . '"'; + + $message = ' --simplesamlphp-' . $random_hash . ' Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 8bit @@ -108,12 +111,12 @@ Content-Transfer-Encoding: 8bit --simplesamlphp-' . $random_hash . '-- '; - $headers = implode("\n", $this->headers); + $headers = implode("\n", $this->headers); - $mail_sent = @mail($this->to, $this->subject, $message, $headers); - SimpleSAML\Logger::debug('Email: Sending e-mail to [' . $this->to . '] : ' . ($mail_sent ? 'OK' : 'Failed')); - if (!$mail_sent) { + $mail_sent = @mail($this->to, $this->subject, $message, $headers); + SimpleSAML\Logger::debug('Email: Sending e-mail to [' . $this->to . '] : ' . ($mail_sent ? 'OK' : 'Failed')); + if (!$mail_sent) { throw new Exception('Error when sending e-mail'); } - } + } } diff --git a/lib/SimpleSAML/XHTML/Template.php b/lib/SimpleSAML/XHTML/Template.php index 844ed4cc1514ce96d4a68a531c7a51aecdf12adb..d23c95f43e34de68d899b2b292f197a354d8c628 100644 --- a/lib/SimpleSAML/XHTML/Template.php +++ b/lib/SimpleSAML/XHTML/Template.php @@ -278,7 +278,7 @@ class SimpleSAML_XHTML_Template $subdirs = scandir($themeDir); if (empty($subdirs)) { // no subdirectories in the theme directory, nothing to do here // this is probably wrong, log a message - \SimpleSAML\Logger::warning('Emtpy theme directory for theme "'.$this->theme['name'].'".'); + \SimpleSAML\Logger::warning('Empty theme directory for theme "'.$this->theme['name'].'".'); return array(); } @@ -313,7 +313,6 @@ class SimpleSAML_XHTML_Template $templatedir = $moduledir.'/templates'; if (!is_dir($templatedir)) { throw new InvalidArgumentException('The module \''.$module.'\' has no templates directory.'); - } return $templatedir; } diff --git a/lib/SimpleSAML/XHTML/TemplateControllerInterface.php b/lib/SimpleSAML/XHTML/TemplateControllerInterface.php index bd54907c48a9e5d4152062e36a19add335ad83ee..8d6f4e733ee95f3f082f8cda06fe4f631f74622e 100644 --- a/lib/SimpleSAML/XHTML/TemplateControllerInterface.php +++ b/lib/SimpleSAML/XHTML/TemplateControllerInterface.php @@ -7,7 +7,8 @@ namespace SimpleSAML\XHTML; * * @package SimpleSAMLphp */ -interface TemplateControllerInterface { +interface TemplateControllerInterface +{ /** * Implement to modify the twig environment after its initialization (e.g. add filters or extensions). diff --git a/lib/SimpleSAML/XML/Validator.php b/lib/SimpleSAML/XML/Validator.php index becf3516e74c6ca9ffc4e19619d09a6cce597e42..6d8a4f32a6acfb65e1e5be7f0da24f909b42af2e 100644 --- a/lib/SimpleSAML/XML/Validator.php +++ b/lib/SimpleSAML/XML/Validator.php @@ -325,7 +325,8 @@ class Validator assert(is_string($caFile)); // Clear openssl errors - while (openssl_error_string() !== false); + while (openssl_error_string() !== false) { + } $res = openssl_x509_checkpurpose($certificate, X509_PURPOSE_ANY, array($caFile)); diff --git a/modules/adfs/lib/IdP/ADFS.php b/modules/adfs/lib/IdP/ADFS.php index 3a28b9243497602ca75a5a8c9524e4ef6c0be8ee..f609c7fc18be23a8c0eeb9d331d1e8ec1f33c4fa 100644 --- a/modules/adfs/lib/IdP/ADFS.php +++ b/modules/adfs/lib/IdP/ADFS.php @@ -1,5 +1,8 @@ <?php +use RobRichards\XMLSecLibs\XMLSecurityDSig; +use RobRichards\XMLSecLibs\XMLSecurityKey; + class sspmod_adfs_IdP_ADFS { public static function receiveAuthnRequest(SimpleSAML_IdP $idp) @@ -9,11 +12,12 @@ class sspmod_adfs_IdP_ADFS $requestid = $query['wctx']; $issuer = $query['wtrealm']; + $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); $spMetadata = $metadata->getMetaDataConfig($issuer, 'adfs-sp-remote'); SimpleSAML\Logger::info('ADFS - IdP.prp: Incoming Authentication request: '.$issuer.' id '.$requestid); - } catch(Exception $exception) { + } catch (Exception $exception) { throw new SimpleSAML_Error_Error('PROCESSAUTHNREQUEST', $exception); } @@ -23,16 +27,21 @@ class sspmod_adfs_IdP_ADFS 'ForceAuthn' => false, 'isPassive' => false, 'adfs:wctx' => $requestid, + 'adfs:wreply' => false ); + if (isset($query['wreply']) && !empty($query['wreply'])) { + $state['adfs:wreply'] = SimpleSAML\Utils\HTTP::checkURLAllowed($query['wreply']); + } + $idp->handleAuthenticationRequest($state); } - private static function generateResponse($issuer, $target, $nameid, $attributes) + private static function generateResponse($issuer, $target, $nameid, $attributes, $assertionLifetime) { $issueInstant = SimpleSAML\Utils\Time::generateTimestamp(); $notBefore = SimpleSAML\Utils\Time::generateTimestamp(time() - 30); - $assertionExpire = SimpleSAML\Utils\Time::generateTimestamp(time() + 60 * 5); + $assertionExpire = SimpleSAML\Utils\Time::generateTimestamp(time() + $assertionLifetime); $assertionID = SimpleSAML\Utils\Random::generateID(); $nameidFormat = 'http://schemas.xmlsoap.org/claims/UPN'; $nameid = htmlspecialchars($nameid); @@ -93,19 +102,20 @@ MSG; return $result; } - private static function signResponse($response, $key, $cert) + private static function signResponse($response, $key, $cert, $algo) { $objXMLSecDSig = new XMLSecurityDSig(); $objXMLSecDSig->idKeys = array('AssertionID'); $objXMLSecDSig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N); - $responsedom = \SAML2\DOMDocumentFactory::fromString(str_replace ("\r", "", $response)); + $responsedom = \SAML2\DOMDocumentFactory::fromString(str_replace("\r", "", $response)); $firstassertionroot = $responsedom->getElementsByTagName('Assertion')->item(0); $objXMLSecDSig->addReferenceList( array($firstassertionroot), XMLSecurityDSig::SHA1, array('http://www.w3.org/2000/09/xmldsig#enveloped-signature', XMLSecurityDSig::EXC_C14N), array('id_name' => 'AssertionID') ); - $objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type'=>'private')); + + $objKey = new XMLSecurityKey($algo, array('type' => 'private')); $objKey->loadKey($key, true); $objXMLSecDSig->sign($objKey); if ($cert) { @@ -143,8 +153,7 @@ MSG; { $spMetadata = $state["SPMetadata"]; $spEntityId = $spMetadata['entityid']; - $spMetadata = SimpleSAML_Configuration::loadFromArray($spMetadata, - '$metadata[' . var_export($spEntityId, true) . ']'); + $spMetadata = SimpleSAML_Configuration::loadFromArray($spMetadata, '$metadata['.var_export($spEntityId, true).']'); $attributes = $state['Attributes']; @@ -163,19 +172,40 @@ MSG; $idpEntityId = $idpMetadata->getString('entityid'); $idp->addAssociation(array( - 'id' => 'adfs:' . $spEntityId, + 'id' => 'adfs:'.$spEntityId, 'Handler' => 'sspmod_adfs_IdP_ADFS', 'adfs:entityID' => $spEntityId, )); - $response = sspmod_adfs_IdP_ADFS::generateResponse($idpEntityId, $spEntityId, $nameid, $attributes); + $assertionLifetime = $spMetadata->getInteger('assertion.lifetime', null); + if ($assertionLifetime === null) { + $assertionLifetime = $idpMetadata->getInteger('assertion.lifetime', 300); + } + + $response = sspmod_adfs_IdP_ADFS::generateResponse($idpEntityId, $spEntityId, $nameid, $attributes, $assertionLifetime); $privateKeyFile = \SimpleSAML\Utils\Config::getCertPath($idpMetadata->getString('privatekey')); $certificateFile = \SimpleSAML\Utils\Config::getCertPath($idpMetadata->getString('certificate')); - $wresult = sspmod_adfs_IdP_ADFS::signResponse($response, $privateKeyFile, $certificateFile); + + $algo = $spMetadata->getString('signature.algorithm', null); + if ($algo === null) { + /* + * In the NIST Special Publication 800-131A, SHA-1 became deprecated for generating + * new digital signatures in 2011, and will be explicitly disallowed starting the 1st + * of January, 2014. We'll keep this as a default for the next release and mark it + * as deprecated, as part of the transition to SHA-256. + * + * See http://csrc.nist.gov/publications/nistpubs/800-131A/sp800-131A.pdf for more info. + * + * TODO: change default to XMLSecurityKey::RSA_SHA256. + */ + $algo = $idpMetadata->getString('signature.algorithm', XMLSecurityKey::RSA_SHA1); + } + $wresult = sspmod_adfs_IdP_ADFS::signResponse($response, $privateKeyFile, $certificateFile, $algo); $wctx = $state['adfs:wctx']; - sspmod_adfs_IdP_ADFS::postResponse($spMetadata->getValue('prp'), $wresult, $wctx); + $wreply = $state['adfs:wreply'] ? : $spMetadata->getValue('prp'); + sspmod_adfs_IdP_ADFS::postResponse($wreply, $wresult, $wctx); } public static function sendLogoutResponse(SimpleSAML_IdP $idp, array $state) @@ -209,7 +239,7 @@ MSG; { $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); $spMetadata = $metadata->getMetaDataConfig($association['adfs:entityID'], 'adfs-sp-remote'); - $returnTo = SimpleSAML\Module::getModuleURL('adfs/idp/prp.php?assocId=' . urlencode($association["id"]) . '&relayState=' . urlencode($relayState)); - return $spMetadata->getValue('prp') . '?' . 'wa=wsignoutcleanup1.0&wreply=' . urlencode($returnTo); + $returnTo = SimpleSAML\Module::getModuleURL('adfs/idp/prp.php?assocId='.urlencode($association["id"]).'&relayState='.urlencode($relayState)); + return $spMetadata->getValue('prp').'?wa=wsignoutcleanup1.0&wreply='.urlencode($returnTo); } } diff --git a/modules/authYubiKey/libextinc/Yubico.php b/modules/authYubiKey/libextinc/Yubico.php index ca7f2690c9da0def07a2d4fa5b22eda5e9510db4..a0f420aebffa632b069449f67be97500be99d17c 100644 --- a/modules/authYubiKey/libextinc/Yubico.php +++ b/modules/authYubiKey/libextinc/Yubico.php @@ -137,7 +137,7 @@ class Auth_Yubico if ($this->_key <> "") { $rows = explode("\r\n", $responseMsg); $response = array(); - while (list(, $val) = each($rows)) { + foreach ($rows as $val) { // = is also used in BASE64 encoding so we only replace the first = by # which is not used in BASE64 $val = preg_replace('/=/', '#', $val, 1); $row = explode("#", $val); diff --git a/modules/authYubiKey/www/resources/logo.jpg b/modules/authYubiKey/www/resources/logo.jpg index 3b7e7f360ddb176fd383a8bea0d6b500fff95cf5..e2fded1a931f2d0ad604be69c7722002f9bb71fe 100644 Binary files a/modules/authYubiKey/www/resources/logo.jpg and b/modules/authYubiKey/www/resources/logo.jpg differ diff --git a/modules/authfacebook/extlibinc/base_facebook.php b/modules/authfacebook/extlibinc/base_facebook.php index a5fd3e9043db0edc0fc683e6d5f596e06f9da660..cd4536db1a59b5b48fe8b396a78aa356b229defd 100644 --- a/modules/authfacebook/extlibinc/base_facebook.php +++ b/modules/authfacebook/extlibinc/base_facebook.php @@ -698,7 +698,7 @@ abstract class BaseFacebook $this->clearPersistentData('state'); return $_REQUEST['code']; } else { - self::errorLog('CSRF state token does not match one provided.'); + self::errorLog('CSRF state token does not match one provided. ' . $this->state . '!=' . $_REQUEST['state']); return false; } } diff --git a/modules/authfacebook/lib/Auth/Source/Facebook.php b/modules/authfacebook/lib/Auth/Source/Facebook.php index 1a85e2bcded8e15813aaef2bed74a4394d787918..865e152c38db35e7569b7678e3ea4ce627c76c56 100644 --- a/modules/authfacebook/lib/Auth/Source/Facebook.php +++ b/modules/authfacebook/lib/Auth/Source/Facebook.php @@ -86,12 +86,12 @@ class sspmod_authfacebook_Auth_Source_Facebook extends SimpleSAML_Auth_Source { // We are going to need the authId in order to retrieve this authentication source later $state[self::AUTHID] = $this->authId; - $stateID = SimpleSAML_Auth_State::saveState($state, self::STAGE_INIT); + SimpleSAML_Auth_State::saveState($state, self::STAGE_INIT); $facebook = new sspmod_authfacebook_Facebook(array('appId' => $this->api_key, 'secret' => $this->secret), $state); $facebook->destroySession(); - $linkback = SimpleSAML\Module::getModuleURL('authfacebook/linkback.php', array('AuthState' => $stateID)); + $linkback = SimpleSAML\Module::getModuleURL('authfacebook/linkback.php'); $url = $facebook->getLoginUrl(array('redirect_uri' => $linkback, 'scope' => $this->req_perms)); SimpleSAML_Auth_State::saveState($state, self::STAGE_INIT); diff --git a/modules/authfacebook/lib/Facebook.php b/modules/authfacebook/lib/Facebook.php index 530853fbf9ad58fff79d499b9403374a9514643f..42153933c2fb2a23ca526067514a7ecbb1ceedf6 100644 --- a/modules/authfacebook/lib/Facebook.php +++ b/modules/authfacebook/lib/Facebook.php @@ -146,4 +146,11 @@ class sspmod_authfacebook_Facebook extends BaseFacebook } return implode('_', $parts); } + + protected function establishCSRFTokenState() { + if ($this->state === null) { + $this->state = SimpleSAML_Auth_State::getStateId($this->ssp_state); + $this->setPersistentData('state', $this->state); + } + } } diff --git a/modules/authfacebook/www/linkback.php b/modules/authfacebook/www/linkback.php index 6ca8855bcc9c5d22dacb518933970861dd28412f..94adb167289de6490ed6cc6083121b194aad2f18 100644 --- a/modules/authfacebook/www/linkback.php +++ b/modules/authfacebook/www/linkback.php @@ -3,11 +3,15 @@ /** * Handle linkback() response from Facebook. */ - -if (!array_key_exists('AuthState', $_REQUEST) || empty($_REQUEST['AuthState'])) { - throw new SimpleSAML_Error_BadRequest('Missing state parameter on facebook linkback endpoint.'); + +// For backwards compatability look for AuthState first +if (array_key_exists('AuthState', $_REQUEST) && !empty($_REQUEST['AuthState'])) { + $state = SimpleSAML_Auth_State::loadState($_REQUEST['AuthState'], sspmod_authfacebook_Auth_Source_Facebook::STAGE_INIT); +} elseif (array_key_exists('state', $_REQUEST) && !empty($_REQUEST['state'])) { + $state = SimpleSAML_Auth_State::loadState($_REQUEST['state'], sspmod_authfacebook_Auth_Source_Facebook::STAGE_INIT); +} else { + throw new SimpleSAML_Error_BadRequest('Missing state parameter on facebook linkback endpoint.'); } -$state = SimpleSAML_Auth_State::loadState($_REQUEST['AuthState'], sspmod_authfacebook_Auth_Source_Facebook::STAGE_INIT); // Find authentication source if (!array_key_exists(sspmod_authfacebook_Auth_Source_Facebook::AUTHID, $state)) { diff --git a/modules/casserver/www/serviceValidate.php b/modules/casserver/www/serviceValidate.php index cf4bd0af22c7d00bf416c458369cfb2d32bf1ea4..fd9ebc65a974ffeb2dd2348e438494e954027583 100644 --- a/modules/casserver/www/serviceValidate.php +++ b/modules/casserver/www/serviceValidate.php @@ -1,8 +1,10 @@ <?php -require 'tickets.php'; +require('tickets.php'); -# set manually if called directly - ie not included from validate.php or cas.php -if (!$function) $function = 'serviceValidate'; +// set manually if called directly - ie not included from validate.php or cas.php +if (!$function) { + $function = 'serviceValidate'; +} /* * Incoming parameters: @@ -11,89 +13,95 @@ if (!$function) $function = 'serviceValidate'; * ticket * */ - if (array_key_exists('service', $_GET)) { - $service = $_GET['service']; - $ticket = $_GET['ticket']; - $forceAuthn = isset($_GET['renew']) && $_GET['renew']; + $service = $_GET['service']; + $ticket = $_GET['ticket']; + $forceAuthn = isset($_GET['renew']) && $_GET['renew']; } else { - throw new Exception('Required URL query parameter [service] not provided. (CAS Server)'); + throw new Exception('Required URL query parameter [service] not provided. (CAS Server)'); } try { -// Load SimpleSAMLphp, configuration and metadata - $casconfig = SimpleSAML_Configuration::getConfig('module_casserver.php'); - - $path = $casconfig->resolvePath($casconfig->getValue('ticketcache', 'ticketcache')); - $ticketcontent = retrieveTicket($ticket, $path); - - $usernamefield = $casconfig->getValue('attrname', 'eduPersonPrincipalName'); - $dosendattributes = $casconfig->getValue('attributes', FALSE); - - $attributes = $ticketcontent['attributes']; + // Load SimpleSAMLphp, configuration and metadata + $casconfig = SimpleSAML_Configuration::getConfig('module_casserver.php'); + + $path = $casconfig->resolvePath($casconfig->getValue('ticketcache', 'ticketcache')); + $ticketcontent = retrieveTicket($ticket, $path); + + $usernamefield = $casconfig->getValue('attrname', 'eduPersonPrincipalName'); + $dosendattributes = $casconfig->getValue('attributes', false); + + $attributes = $ticketcontent['attributes']; + + $pgtiouxml = ""; - $pgtiouxml = ""; + if ($ticketcontent['service'] == $service + && $ticketcontent['forceAuthn'] == $forceAuthn + && array_key_exists($usernamefield, $attributes) + && $ticketcontent['validbefore'] > time()) { - if ($ticketcontent['service'] == $service - && $ticketcontent['forceAuthn'] == $forceAuthn - && array_key_exists($usernamefield, $attributes) - && $ticketcontent['validbefore'] > time()) { - - if (isset($_GET['pgtUrl'])) { - $pgtUrl = $_GET['pgtUrl']; - $pgtiou = str_replace( '_', 'PGTIOU-', SimpleSAML\Utils\Random::generateID()); - $pgt = str_replace( '_', 'PGT-', SimpleSAML\Utils\Random::generateID()); - $content = array( - 'attributes' => $attributes, - 'forceAuthn' => false, - 'proxies' => array_merge(array($service), $ticketcontent['proxies']), - 'validbefore' => time() + 60); - \SimpleSAML\Utils\HTTP::fetch($pgtUrl . '?pgtIou=' . $pgtiou . '&pgtId=' . $pgt); - storeTicket($pgt, $path, $content); - $pgtiouxml = "\n<cas:proxyGrantingTicket>$pgtiou</cas:proxyGrantingTicket>\n"; - } - - $proxiesxml = join("\n", array_map(create_function('$a', 'return "<cas:proxy>$a</cas:proxy>";'), $ticketcontent['proxies'])); - if ($proxiesxml) $proxiesxml = "<cas:proxies>\n$proxiesxml\n</cas:proxies>\n"; - returnResponse('YES', $function, $attributes[$usernamefield][0], $dosendattributes ? $attributes : array(), $pgtiouxml.$proxiesxml); - } else { - returnResponse('NO', $function); + if (isset($_GET['pgtUrl'])) { + $pgtUrl = $_GET['pgtUrl']; + $pgtiou = str_replace( '_', 'PGTIOU-', SimpleSAML\Utils\Random::generateID()); + $pgt = str_replace( '_', 'PGT-', SimpleSAML\Utils\Random::generateID()); + $content = array( + 'attributes' => $attributes, + 'forceAuthn' => false, + 'proxies' => array_merge(array($service), $ticketcontent['proxies']), + 'validbefore' => time() + 60); + \SimpleSAML\Utils\HTTP::fetch($pgtUrl . '?pgtIou=' . $pgtiou . '&pgtId=' . $pgt); + storeTicket($pgt, $path, $content); + $pgtiouxml = "\n<cas:proxyGrantingTicket>$pgtiou</cas:proxyGrantingTicket>\n"; + } + + $proxiesxml = join("\n", array_map( + function($a) { return "<cas:proxy>$a</cas:proxy>"; }, + $ticketcontent['proxies'])); + if ($proxiesxml) { + $proxiesxml = "<cas:proxies>\n$proxiesxml\n</cas:proxies>\n"; } + returnResponse('YES', $function, $attributes[$usernamefield][0], + $dosendattributes ? $attributes : array(), + $pgtiouxml.$proxiesxml); + } else { + returnResponse('NO', $function); + } + } catch (Exception $e) { - returnResponse('NO', $function, $e->getMessage()); + returnResponse('NO', $function, $e->getMessage()); } -function returnResponse($value, $function, $usrname = '', $attributes = array(), $xtraxml = "") { - if ($value === 'YES') { - if ($function != 'validate') { - $attributesxml = ""; - foreach ($attributes as $attributename => $attributelist) { - $attr = htmlspecialchars($attributename); - foreach ($attributelist as $attributevalue) { - $attributesxml .= "<cas:$attr>" . htmlspecialchars($attributevalue) . "</cas:$attr>\n"; - } - } - if (sizeof($attributes)) $attributesxml = "<cas:attributes>\n" . $attributesxml . "</cas:attributes>\n"; - echo '<cas:serviceResponse xmlns:cas="http://www.yale.edu/tp/cas"> +function returnResponse($value, $function, $usrname = '', $attributes = array(), $xtraxml = "") +{ + if ($value === 'YES') { + if ($function != 'validate') { + $attributesxml = ""; + foreach ($attributes as $attributename => $attributelist) { + $attr = htmlspecialchars($attributename); + foreach ($attributelist as $attributevalue) { + $attributesxml .= "<cas:$attr>" . htmlspecialchars($attributevalue) . "</cas:$attr>\n"; + } + } + if (sizeof($attributes)) $attributesxml = "<cas:attributes>\n" . $attributesxml . "</cas:attributes>\n"; + echo '<cas:serviceResponse xmlns:cas="http://www.yale.edu/tp/cas"> <cas:authenticationSuccess> <cas:user>' . htmlspecialchars($usrname) . '</cas:user>' . - $xtraxml . - $attributesxml . - '</cas:authenticationSuccess> + $xtraxml . + $attributesxml . + '</cas:authenticationSuccess> </cas:serviceResponse>'; - } else { - echo 'yes' . "\n" . $usrname; - } - } else { - if ($function != 'validate') { - echo '<cas:serviceResponse xmlns:cas="http://www.yale.edu/tp/cas"> + } else { + echo 'yes' . "\n" . $usrname; + } + } else { + if ($function != 'validate') { + echo '<cas:serviceResponse xmlns:cas="http://www.yale.edu/tp/cas"> <cas:authenticationFailure code=""> </cas:authenticationFailure> </cas:serviceResponse>'; - } else { - echo 'no'; - - } - } -} \ No newline at end of file + } else { + echo 'no'; + } + } +} diff --git a/modules/consent/dictionaries/consent.definition.json b/modules/consent/dictionaries/consent.definition.json index d36f9e70f8d2396c191a6e12d236efe8d3a75aa9..6c445161046037f3264377dd668270ad384332b8 100644 --- a/modules/consent/dictionaries/consent.definition.json +++ b/modules/consent/dictionaries/consent.definition.json @@ -1,68 +1,68 @@ { - "yes": { - "en": "Yes, continue" - }, - "no": { - "en": "No, cancel" - }, - "remember": { - "en": "Remember" - }, - "consent_accept": { - "en": "SPNAME requires that the information below is transferred." - }, - "login": { - "en": "login" - }, - "service_providers_for": { - "en": "Service Providers for" - }, - "service_provider_header": { - "en": "Service Provider" - }, - "status_header": { - "en": "Consent status" - }, - "show_hide_attributes": { - "en": "show\/hide attributes" - }, - "consent_privacypolicy": { - "en": "Privacypolicy for the service" - }, - "noconsent_title": { - "en": "No consent given" - }, - "noconsent_text": { - "en": "You did not give consent for transfering your attributes to SPNAME." - }, - "noconsent_return": { - "en": "Return to consent page" - }, - "consent_header": { - "en": "Consent about releasing personal information" - }, - "consent_attributes_header": { - "en": "Information that will be sent to SPNAME" - }, - "show_attributes": { - "en": "Show attributes" - }, - "noconsent_goto_about": { - "en": "Go to information page for the service" - }, - "consent_purpose": { - "en": "The purpose of SPNAME is SPDESC" - }, - "table_caption": { - "en": "User information" - }, - "table_summary": { - "en": "List the information about you that is about to be transmitted to the service you are going to login to" - }, - "show_attribute": { - "en": "Show content" - }, - "abort": { - "en": "Abort login to SPNAME" - } + "yes": { + "en": "Yes, continue" + }, + "no": { + "en": "No, cancel" + }, + "remember": { + "en": "Remember" + }, + "consent_accept": { + "en": "SPNAME requires that the information below is transferred." + }, + "login": { + "en": "login" + }, + "service_providers_for": { + "en": "Service Providers for" + }, + "service_provider_header": { + "en": "Service Provider" + }, + "status_header": { + "en": "Consent status" + }, + "show_hide_attributes": { + "en": "show\/hide attributes" + }, + "consent_privacypolicy": { + "en": "Privacy policy for the service" + }, + "noconsent_title": { + "en": "No consent given" + }, + "noconsent_text": { + "en": "You did not give consent for transfering your attributes to SPNAME." + }, + "noconsent_return": { + "en": "Return to consent page" + }, + "consent_header": { + "en": "Consent about releasing personal information" + }, + "consent_attributes_header": { + "en": "Information that will be sent to SPNAME" + }, + "show_attributes": { + "en": "Show attributes" + }, + "noconsent_goto_about": { + "en": "Go to information page for the service" + }, + "consent_purpose": { + "en": "The purpose of SPNAME is SPDESC" + }, + "table_caption": { + "en": "User information" + }, + "table_summary": { + "en": "List the information about you that is about to be transmitted to the service you are going to login to" + }, + "show_attribute": { + "en": "Show content" + }, + "abort": { + "en": "Abort login to SPNAME" + } } diff --git a/modules/consent/dictionaries/consent.php b/modules/consent/dictionaries/consent.php index cdf77fec0aa3ba12d75f9498822cb3ad504b62c4..dfb70c73e6d676845982aa58100d0b3a75b7b398 100644 --- a/modules/consent/dictionaries/consent.php +++ b/modules/consent/dictionaries/consent.php @@ -1,379 +1,379 @@ <?php $lang = array( - 'yes' => array ( - 'no' => 'Ja, fortsett', - 'nn' => 'Ja, fortsett', - 'da' => 'Ja, jeg accepterer', - 'en' => 'Yes, continue', - 'de' => 'Ja, ich stimmte zu', - 'sv' => 'Ja', - 'fi' => 'Kyllä', - 'es' => 'Sí', - 'fr' => 'Oui', - 'nl' => 'Ja, ik ga akkoord', - 'lb' => 'Jo', - 'sl' => 'Da, nadaljuj', - 'hr' => 'Da, prihvaćam', - 'hu' => 'Igen, elfogadom', - 'pl' => 'Tak, akceptuję', - 'pt' => 'Sim, Aceito', - 'pt-br' => 'Sim, Aceito', - 'tr' => 'Evet, devam et', - ), - 'no' => array ( - 'no' => 'Nei, avbryt', - 'nn' => 'Nei, avbryt', - 'da' => 'Nej, jeg accepterer ikke', - 'en' => 'No, cancel', - 'de' => 'Nein, ich stimmte nicht zu', - 'sv' => 'Nej', - 'fi' => 'ei', - 'es' => 'No', - 'fr' => 'Non', - 'nl' => 'Nee, ik weiger', - 'lb' => 'Nee', - 'sl' => 'Ne, prekliči', - 'hr' => 'Ne privaćam', - 'hu' => 'Nem, nem fogadom el', - 'pl' => 'Nie, nie akceptuję', - 'pt' => 'Não aceito', - 'pt-br' => 'Não, não aceito', - 'tr' => 'Hayır, iptal et', - ), - 'remember' => array ( - 'no' => 'Godta også for fremtiden', - 'nn' => 'Godta også for framtida', - 'da' => 'Husk samtykke', - 'en' => 'Remember', - 'de' => 'Zustimmung merken', - 'sv' => 'Spara samtycke', - 'fi' => 'Muista', - 'es' => 'Recordar el consentimiento', - 'fr' => 'Se souvenir du consentement', - 'nl' => 'Bewaar toestemming', - 'lb' => 'Zoustëmmung verhalen', - 'sl' => 'Zapomni si privolitev.', - 'hr' => 'Zapamti dozvole', - 'hu' => 'Emlékezzen a hozzájárulásra', - 'pl' => 'Pamiętaj moją zgodę', - 'pt' => 'Lembrar a minha escolha', - 'pt-br' => 'Lembrar Consentimento', - 'tr' => 'Hatırla', - ), - 'consent_header' => array ( - 'no' => 'Samtykke om overføring av personinformasjon', - 'nn' => 'Samtykke til overføring av personinformasjon', - 'da' => 'Samtykke til at frigive personlige oplysninger', - 'en' => 'Consent about releasing personal information', - 'de' => 'Zustimmung zur Weitergabe persönlicher Daten ', - 'sv' => 'Samtycke gällande överföring av personinformation', - 'fi' => 'Henkilötietojen luovutuksen hyväksyntä', - 'es' => 'Consentimiento para la liberación de información personal', - 'nl' => 'Toestemming voor het vrijgeven van persoonsgegevens', - 'sl' => 'Odločitev o privolitvi posredovanja vaših osebnih podatkov', - 'hr' => 'Dozvola za isporuku osobnih podataka', - 'hu' => 'Hozzájárulás személyes adatok kiadásához', - 'pl' => 'Zgoda na wysłanie danych osobistych', - 'pt' => 'Consentimento do envio de informação pessoal', - 'tr' => 'Kişisel bilgilerin verilmesi hakkında onay', - ), - 'consent_accept' => array ( - 'no' => 'For å fullføre innloggingen må du godta at opplysningene nedenfor sendes til SPNAME.', - 'nn' => 'For å fullføra innlogginga må du godta at opplysningane under blir sende til SPNAME', - 'da' => 'SPNAME kræver at nedenstående oplysninger overføres fra IDPNAME. Vil du acceptere dette?', - 'en' => 'SPNAME requires that the information below is transferred.', - 'de' => 'SPNAME erfordert die Übertragung untenstehender Information von IDPNAME. Akzeptieren Sie das?', - 'sv' => 'Du är på väg att logga in i tjänsten SPNAME. Tjänsten kräver att informationen nedan skickas från IDPNAME. Är detta okej?', - 'fi' => 'Olet kirjautumassa palveluun SPNAME. Identiteetilähteesi henkilötietojasi palvelun tarjoajalle. Hyväksytkö tietojen siirron?', - 'es' => 'Está a punto de acceder al servicio SPNAME. El servicio requiere que la información que se muestra a continuación sea transferida desde IDPNAME. ¿Acepta esto? ', - 'fr' => 'Vous êtes sur le point de vous connecter au service SPNAME. Lors de l\'ouverture de session, le fournisseur d\'identité enverra des informations sur votre identité à ce service. Acceptez-vous cela ?', - 'nl' => 'U gaat inloggen bij een dienst SPNAME. Tijdens het loginproces stuurt de identity provider zgn. attributen met daarin informatie over uw identiteit voor deze dienst. Bent u het daarmee eens?', - 'lb' => 'Daer sid dobai aerch um service unzemellen SPNAME. Waerend dem Login Prozess schéckt den Identity Provider Attributer, déi Informatiounen iwert aer Identitéit enthaalen. Akzeptéier daer daat?', - 'sl' => 'Pravkar se nameravate prijaviti v storitev SPNAME. Med postopkom prijave bo IdP tej storitvi posredoval atribute, ki vsebujejo informacije o vaši identiteti. Ali se s tem strinjate? ', - 'hr' => 'U tijeku je proces prijave za pristup servisu SPNAME. Servis zahtjeva da IDPNAME isporuči dolje navedene podatke. Slažete li se s time?', - 'hu' => 'Ön azonosítja magát ehhez a szolgáltatáshoz SPNAME. Az azonosítás során IDPNAME az alábbi adatokat fogja küldeni a szolgáltatásnak. Engedélyezi?', - 'pl' => 'SPNAME wymaga aby poniższa informacja została przesłana.', - 'pt' => 'O serviço SPNAME necessita que a informação apresentada em baixo seja transferida.', - 'pt-br' => 'Você está prestes a acessar o serviço SPNAME. O serviço exige que as informações a seguir sejam transferidas do IDPNAME. Você aceita isso?', - 'tr' => 'SPNAME aşağıdaki bilgilerin gönderilmesine ihtiyaç duyuyor.', - ), - 'consent_purpose' => array ( - 'no' => 'Formålet med SPNAME er SPDESC', - 'nn' => 'Hensikta med SPNAME er SPDESC', - 'da' => 'SPNAME har til formål at SPDESC', - 'en' => 'The purpose of SPNAME is SPDESC', - 'de' => 'Der Zweck von SPNAME ist SPDESC', - 'sv' => 'Syftet med SPNAME är SPDESC', - 'fi' => 'Palvelun SPNAME käyttötarkoitus on SPDESC', - 'es' => 'El propósito de SPNAME es SPDESC', - 'nl' => 'Het doel van SPNAME is SPDESC', - 'sl' => 'Namen %SPNAME%: %SPDESC%', - 'hr' => 'Svrha SPNAME je SPDESC', - 'hu' => 'A szolgáltatás (SPNAME) ezt a célt szolgálja: SPDESC', - 'pl' => 'Celem SPNAME jest SPDESC', - 'pt' => 'O propósito de SPNAME é SPDESC', - 'tr' => 'SPNAME\'in amacı SPDESC\'tir', - ), - 'consent_privacypolicy' => array ( - 'no' => 'Personvern for tjenesten', - 'nn' => 'Personvern for tenesta', - 'da' => 'Tjenestens politik vedrørende personoplysninger', - 'en' => 'Privacypolicy for the service', - 'de' => 'Datenschutzrichtlinie des Dienstes', - 'sv' => 'Tjänstens policy för personlig integritet', - 'fi' => 'Tietosuojaseloste palvelulle', - 'es' => 'Política de privacidad para el servicio', - 'nl' => 'Privacybeleid voor de dienst', - 'sl' => 'Politika zasebnosti za ta SP', - 'hr' => 'Politika zaštite privatnosti', - 'hu' => 'A szolgáltatás adatvádelmi nyilatkozata', - 'pl' => 'Polityka prywatności dla serwisu', - 'pt' => 'Política de privacidade do serviço', - 'pt-br' => 'Política de Privacidade deste serviço', - 'tr' => 'Servis için gizlilik politikası', - ), - 'consent_attributes_header' => array ( - 'no' => 'Opplysninger som vil bli sendt til SPNAME', - 'nn' => 'Opplysningar som blir sende til SPNAME', - 'da' => 'Attributter som bliver sendt til SPNAME', - 'en' => 'Information that will be sent to SPNAME', - 'de' => 'Informationen, die an SPNAME gesandt werden', - 'sv' => 'Attribut som kommer att skickas till tjänsten', - 'fi' => 'Tiedot lähetetään palvelulle SPNAME', - 'es' => 'Atributos que serán enviados al servicio', - 'nl' => 'Informatie die naar SPNAME zal worden gestuurd', - 'sl' => 'Atributi, ki bodo poslani SPju', - 'hr' => 'Atributi koji će biti poslani servisu', - 'hu' => 'A(z) SPNAME szolgáltatónak küldött adatok', - 'pl' => 'Atrybuty, które zostaną przesłane do serwisu', - 'pt' => 'Informação que irá ser enviada para SPNAME', - 'tr' => 'SPNAME\'e gönderilecek bilgiler', - ), - 'show_attributes' => array ( - 'no' => 'Vis opplysninger', - 'nn' => 'Vis opplysingar', - 'da' => 'Vis attributter', - 'en' => 'Show attributes', - 'de' => 'Attribute zeigen', - 'sv' => 'Visa attribut', - 'fi' => 'Näytä henkilötiedot', - 'es' => 'Mostrar atributos', - 'nl' => 'Toon attributen', - 'sl' => 'Prikaži atribute', - 'hr' => 'Prikaži atribute', - 'hu' => 'Mutasd az attribútumokat', - 'pl' => 'Wyświetl atrybuty', - 'pt' => 'Mostrar atributos', - 'tr' => 'Özellikleri göster', - ), - 'show_attribute' => array ( - 'no' => 'Vis innhold', - 'nn' => 'Vis innhald', - 'da' => 'Vis indhold', - 'en' => 'Show content', - 'sv' => 'Visa samtycke', - 'es' => 'Mostrart consentimiento', - 'nl' => 'Toon inhoud', - 'sl' => 'Prikaži vsebino', - ), - 'login' => array ( - 'no' => 'innlogging', - 'nn' => 'Logg inn', - 'da' => 'login', - 'en' => 'login', - 'de' => 'anmelden', - 'sv' => 'Logga in', - 'fi' => 'Tunnus', - 'es' => 'login', - 'fr' => 'ouvrir une session', - 'nl' => 'Login', - 'lb' => 'anloggen', - 'sl' => 'Prijava', - 'hr' => 'prijava', - 'hu' => 'bejelentkezés', - 'pl' => 'login', - 'pt' => 'Entrar', - 'pt-br' => 'login', - 'tr' => 'Giriş', - ), - 'service_providers_for' => array ( - 'no' => 'Tjenesteleverandør for', - 'nn' => 'Tenesteleverandørar for', - 'da' => 'Tjenesteudbyder for', - 'en' => 'Service Providers for', - 'de' => 'Service-Provider für', - 'sv' => 'Tjänsteleverantörer för', - 'fi' => 'Palveluntarjoaja', - 'es' => 'Proveedores de servicio para', - 'fr' => 'Fournisseurs de services pour', - 'nl' => 'Service Providers voor', - 'lb' => 'Service Provider fir', - 'sl' => 'SP za', - 'hr' => 'Davatelji usluge za', - 'hu' => 'Alkalmazásszolgáltatók a következő számára', - 'pl' => 'Dostawca Serwisu dla', - 'pt' => 'Fornecedores de Serviço (SP) para', - 'pt-br' => 'Provedor de serviços para', - 'tr' => 'için Servis Sağlayıcılar', - ), - 'service_provider_header' => array ( - 'no' => 'Tjenesteleverandør', - 'nn' => 'Tenesteleverandør', - 'da' => 'Tjenesteudbyder', - 'en' => 'Service Provider', - 'de' => 'Service-Provider', - 'sv' => 'Tjänsteleverantör', - 'fi' => 'Palveluntarjoaja', - 'es' => 'Proveedor de servicio', - 'fr' => 'Fournisseur de service', - 'nl' => 'Service Provider', - 'lb' => 'Service Provider', - 'sl' => 'SP', - 'hr' => 'Davatelj usluge', - 'hu' => 'Alkalmazásszolgáltató', - 'pl' => 'Dostawca serwisu', - 'pt' => 'Fornecedor de Serviço (SP)', - 'pt-br' => 'Provedor de Serviços', - 'tr' => 'Servis Sağlayıcı', - ), - 'status_header' => array ( - 'no' => 'Samtykke-status', - 'nn' => 'Samtykkestatus', - 'da' => 'Samtykke status', - 'en' => 'Consent status', - 'de' => 'Zustimmungsstatus', - 'sv' => 'Status för samtycke', - 'fi' => 'Hyväksynnään tila', - 'es' => 'Estado del consentimiento', - 'fr' => 'État des consentements', - 'nl' => 'Toestemming status', - 'lb' => 'Zoustëmmungsstatus', - 'sl' => 'Stanje privolitve', - 'hr' => 'Status dozvole', - 'hu' => 'Hozzájárulás állapota', - 'pl' => 'Status zgody', - 'pt' => 'Consentimento', - 'pt-br' => 'Status do Consentimento', - 'tr' => 'Onay durumu', - ), - 'show_hide_attributes' => array ( - 'no' => 'Vis/skjul opplysninger', - 'nn' => 'Vis/skjul opplysningar', - 'da' => 'vis/skjul attributter', - 'en' => 'show/hide attributes', - 'de' => 'zeige/verstecke Eigenschaften', - 'sv' => 'visa/göm attribut', - 'fi' => 'Näytä/piilota attribuutteja', - 'es' => 'Mostrar/ocultar atributos', - 'fr' => 'montrer/cacher les attributs', - 'nl' => 'toon/verberg attributen', - 'lb' => 'Attributer weisen/verstoppen', - 'sl' => 'prikaži/skrij atribute', - 'hr' => 'prikaži/sakrij atribute', - 'hu' => 'attribútumok mutatása/elrejtése', - 'pl' => 'pokaż/ukryj atrybuty', - 'pt' => 'Mostrar/Ocultar atributos', - 'pt-br' => 'mostra/esconder Atributos', - 'tr' => 'bilgileri göster/gizle ', - ), - 'noconsent_title' => array ( - 'no' => 'Ikke akseptert overføring av informasjon', - 'nn' => 'Ikkje akseptert overføring av informasjon', - 'da' => 'Manglende samtykke', - 'en' => 'No consent given', - 'de' => 'Zustimmung verweigert', - 'sv' => 'Inget samtycket givet', - 'fi' => 'Lupaa ei annettu', - 'es' => 'No se dió el consentimiento', - 'nl' => 'Geen toestemming gegeven', - 'sl' => 'Privolitev ni bila dana.', - 'hr' => 'Nema pristanka', - 'hu' => 'Nincs hozzájárulás', - 'pl' => 'Nie wyrażono zgody', - 'pt' => 'Consentimento negado', - 'pt-br' => 'Nenhum consentimento dado', - 'tr' => 'Onay verilmemiş', - ), - 'noconsent_text' => array ( - 'no' => 'Du har ikke akseptert å overlevere opplysninger til tjenesteleverandøren.', - 'nn' => 'Du har ikkje akseptert til at dine opplysningar kan sendast til tenesteleverandøren', - 'da' => 'Du har ikke givet samtykke til overleveringen af oplysninger til tjenesten', - 'en' => 'You did not give consent for transfering your attributes to the service provider.', - 'de' => 'Sie haben der Weitergabe ihrer Daten an den Service Provider nicht zugestimmt.', - 'sv' => 'Du gav inte samtycke för att överföra dina attribut till tjänsteleverantören.', - 'fi' => 'Et antanut lupaa siirtää henkilötietojasi palveluntarjoajalle', - 'es' => 'No ha dado su consentimiento para tranferir sus atributos al proveedor de servicio.', - 'nl' => 'U heeft geen toestemming gegeven om uw attributen naar de Service Provider te versturen', - 'sl' => 'Niste podali privolitve za posredovanje atributov SP-ju.', - 'hr' => 'Niste dali pristanak da se vaši podaci isporuče davatelju usluge.', - 'hu' => 'Nem adta hozzájárulását, hogy adatait továbbadjuk a szolgáltatónak.', - 'pl' => 'Nie wyraziłeś zgody na przesłanie Twoich atrybutów do Dostawcy Serwisu.', - 'pt' => 'Negou o consentimento para a transferência dos seus atributos para o fornecedor de serviço.', - 'pt-br' => 'Você não deu o consentimento para a transferência de seus atributos para o provedor de serviços.', - 'tr' => 'Bilgilerinizin servis sağlayıcıya gönderilmesi için onay vermediniz.', - ), - 'noconsent_return' => array ( - 'no' => 'Gå tilbake til aksept-siden med opplysninger', - 'nn' => 'Gå tilbake til aksept-sida for overføring av opplysningar', - 'da' => 'Gå tilbage', - 'en' => 'Return to consent page', - 'de' => 'Zurück', - 'sv' => 'Åter till sidan för samtycke', - 'fi' => 'Palaa hyväksyntäsivulle', - 'es' => 'Volver a la página de consentimiento', - 'nl' => 'Keer terug naar de toestemmingspagina', - 'sl' => 'Vrnitev na privolitveno stran', - 'hr' => 'Povratak na stranicu s dozvolama', - 'hu' => 'Vissza az hozzájárulás-kezelő oldalra', - 'pl' => 'Powrót do strony wydania zezwolenia.', - 'pt' => 'Voltar à página de consentimento', - 'pt-br' => 'Retornar a página de consentimento', - 'tr' => 'Onay sayfasına geri dön', - ), - 'noconsent_goto_about' => array ( - 'no' => 'Gå til informasjonsside om tjenesten', - 'nn' => 'Gå til informasjonssida for tenesta', - 'da' => 'Gå til side med information om tjenesten', - 'en' => 'Go to information page for the service', - 'de' => 'Gehe zur Informationsseite dieses Dienstes', - 'sv' => 'Gå till tjänstens informationssida', - 'fi' => 'Siirry palvelun tiedot -sivulle', - 'es' => 'Ir a la página de información del servicio', - 'nl' => 'Ga naar de informatiepagina voor de service', - 'sl' => 'Pojdi na spletno stran z informacijami o storitvi', - 'hr' => 'Idi na stranicu s informacijama o servisu', - 'hu' => 'A szolgáltatás információs oldalára', - 'pl' => 'Przejdź do strony informacyjnej dla tego serwisu', - 'pt' => 'Ir para página de informação do serviço', - 'tr' => 'Servis için bilgi sayfasına git', - ), - 'table_caption' => array ( - 'no' => 'Bruker innformasjon', - 'da' => 'Bruger information', - 'en' => 'User information', - 'de' => 'Benutzerdaten', - 'sv' => 'Användarinformation', - 'fi' => 'Käyttäjätiedot', - 'es' => 'Información del usuario', - 'nl' => 'Gerbuikersinformatie', - 'sl' => 'Podatki o uporabniku', - 'hu' => 'Felhasználói információk', - 'pt' => 'Informação do utilizador', - 'tr' => 'Kullanıcı bilgisi', - ), - 'table_summary' => array ( - 'no' => 'Her listes den innformasjonen om deg som blir send til den tjenesten du er i ferd med å logge på', - 'da' => 'Informationer som vil blive sendt til den service du er ved at logge in på', - 'en' => 'List the information about you that is about to be transmitted to the service you are going to login to', - 'sv' => 'Visa den nformation om din användare som kommer att skickas till tjänsten som du är på väg att logga in i', - 'fi' => 'Näytä tietosi, joita ollaan siirtämässä palveluun', - 'es' => 'Muestra que información relativa a usted va a ser transmitida al servicio en el que se va a identificar', - 'nl' => 'De informatie die over jou bekend is en naar de service waarop je wilt inloggen verstuurd zal worden', - 'sl' => 'Seznam podatkov o vas, ki bodo posredovani storitvi, v katero se nameravate prijaviti', - 'hu' => 'Ezeket az adatokat fogjuk elküldeni Önről annak a szolgáltatásnak, ahová be kíván jelentkezni', - 'pt' => 'Listar a informação acerca de si que será enviada para o serviço no qual se está autenticar', - 'tr' => 'Girmek istediğiniz servise gönderilecek bilginizi listeleyin', - ), + 'yes' => array ( + 'no' => 'Ja, fortsett', + 'nn' => 'Ja, fortsett', + 'da' => 'Ja, jeg accepterer', + 'en' => 'Yes, continue', + 'de' => 'Ja, ich stimmte zu', + 'sv' => 'Ja', + 'fi' => 'Kyllä', + 'es' => 'Sí', + 'fr' => 'Oui', + 'nl' => 'Ja, ik ga akkoord', + 'lb' => 'Jo', + 'sl' => 'Da, nadaljuj', + 'hr' => 'Da, prihvaćam', + 'hu' => 'Igen, elfogadom', + 'pl' => 'Tak, akceptuję', + 'pt' => 'Sim, Aceito', + 'pt-br' => 'Sim, Aceito', + 'tr' => 'Evet, devam et', + ), + 'no' => array ( + 'no' => 'Nei, avbryt', + 'nn' => 'Nei, avbryt', + 'da' => 'Nej, jeg accepterer ikke', + 'en' => 'No, cancel', + 'de' => 'Nein, ich stimmte nicht zu', + 'sv' => 'Nej', + 'fi' => 'ei', + 'es' => 'No', + 'fr' => 'Non', + 'nl' => 'Nee, ik weiger', + 'lb' => 'Nee', + 'sl' => 'Ne, prekliči', + 'hr' => 'Ne privaćam', + 'hu' => 'Nem, nem fogadom el', + 'pl' => 'Nie, nie akceptuję', + 'pt' => 'Não aceito', + 'pt-br' => 'Não, não aceito', + 'tr' => 'Hayır, iptal et', + ), + 'remember' => array ( + 'no' => 'Godta også for fremtiden', + 'nn' => 'Godta også for framtida', + 'da' => 'Husk samtykke', + 'en' => 'Remember', + 'de' => 'Zustimmung merken', + 'sv' => 'Spara samtycke', + 'fi' => 'Muista', + 'es' => 'Recordar el consentimiento', + 'fr' => 'Se souvenir du consentement', + 'nl' => 'Bewaar toestemming', + 'lb' => 'Zoustëmmung verhalen', + 'sl' => 'Zapomni si privolitev.', + 'hr' => 'Zapamti dozvole', + 'hu' => 'Emlékezzen a hozzájárulásra', + 'pl' => 'Pamiętaj moją zgodę', + 'pt' => 'Lembrar a minha escolha', + 'pt-br' => 'Lembrar Consentimento', + 'tr' => 'Hatırla', + ), + 'consent_header' => array ( + 'no' => 'Samtykke om overføring av personinformasjon', + 'nn' => 'Samtykke til overføring av personinformasjon', + 'da' => 'Samtykke til at frigive personlige oplysninger', + 'en' => 'Consent about releasing personal information', + 'de' => 'Zustimmung zur Weitergabe persönlicher Daten ', + 'sv' => 'Samtycke gällande överföring av personinformation', + 'fi' => 'Henkilötietojen luovutuksen hyväksyntä', + 'es' => 'Consentimiento para la liberación de información personal', + 'nl' => 'Toestemming voor het vrijgeven van persoonsgegevens', + 'sl' => 'Odločitev o privolitvi posredovanja vaših osebnih podatkov', + 'hr' => 'Dozvola za isporuku osobnih podataka', + 'hu' => 'Hozzájárulás személyes adatok kiadásához', + 'pl' => 'Zgoda na wysłanie danych osobistych', + 'pt' => 'Consentimento do envio de informação pessoal', + 'tr' => 'Kişisel bilgilerin verilmesi hakkında onay', + ), + 'consent_accept' => array ( + 'no' => 'For å fullføre innloggingen må du godta at opplysningene nedenfor sendes til SPNAME.', + 'nn' => 'For å fullføra innlogginga må du godta at opplysningane under blir sende til SPNAME', + 'da' => 'SPNAME kræver at nedenstående oplysninger overføres fra IDPNAME. Vil du acceptere dette?', + 'en' => 'SPNAME requires that the information below is transferred.', + 'de' => 'SPNAME erfordert die Übertragung untenstehender Information von IDPNAME. Akzeptieren Sie das?', + 'sv' => 'Du är på väg att logga in i tjänsten SPNAME. Tjänsten kräver att informationen nedan skickas från IDPNAME. Är detta okej?', + 'fi' => 'Olet kirjautumassa palveluun SPNAME. Identiteetilähteesi henkilötietojasi palvelun tarjoajalle. Hyväksytkö tietojen siirron?', + 'es' => 'Está a punto de acceder al servicio SPNAME. El servicio requiere que la información que se muestra a continuación sea transferida desde IDPNAME. ¿Acepta esto? ', + 'fr' => 'Vous êtes sur le point de vous connecter au service SPNAME. Lors de l\'ouverture de session, le fournisseur d\'identité enverra des informations sur votre identité à ce service. Acceptez-vous cela ?', + 'nl' => 'U gaat inloggen bij een dienst SPNAME. Tijdens het loginproces stuurt de identity provider zgn. attributen met daarin informatie over uw identiteit voor deze dienst. Bent u het daarmee eens?', + 'lb' => 'Daer sid dobai aerch um service unzemellen SPNAME. Waerend dem Login Prozess schéckt den Identity Provider Attributer, déi Informatiounen iwert aer Identitéit enthaalen. Akzeptéier daer daat?', + 'sl' => 'Pravkar se nameravate prijaviti v storitev SPNAME. Med postopkom prijave bo IdP tej storitvi posredoval atribute, ki vsebujejo informacije o vaši identiteti. Ali se s tem strinjate? ', + 'hr' => 'U tijeku je proces prijave za pristup servisu SPNAME. Servis zahtjeva da IDPNAME isporuči dolje navedene podatke. Slažete li se s time?', + 'hu' => 'Ön azonosítja magát ehhez a szolgáltatáshoz SPNAME. Az azonosítás során IDPNAME az alábbi adatokat fogja küldeni a szolgáltatásnak. Engedélyezi?', + 'pl' => 'SPNAME wymaga aby poniższa informacja została przesłana.', + 'pt' => 'O serviço SPNAME necessita que a informação apresentada em baixo seja transferida.', + 'pt-br' => 'Você está prestes a acessar o serviço SPNAME. O serviço exige que as informações a seguir sejam transferidas do IDPNAME. Você aceita isso?', + 'tr' => 'SPNAME aşağıdaki bilgilerin gönderilmesine ihtiyaç duyuyor.', + ), + 'consent_purpose' => array ( + 'no' => 'Formålet med SPNAME er SPDESC', + 'nn' => 'Hensikta med SPNAME er SPDESC', + 'da' => 'SPNAME har til formål at SPDESC', + 'en' => 'The purpose of SPNAME is SPDESC', + 'de' => 'Der Zweck von SPNAME ist SPDESC', + 'sv' => 'Syftet med SPNAME är SPDESC', + 'fi' => 'Palvelun SPNAME käyttötarkoitus on SPDESC', + 'es' => 'El propósito de SPNAME es SPDESC', + 'nl' => 'Het doel van SPNAME is SPDESC', + 'sl' => 'Namen %SPNAME%: %SPDESC%', + 'hr' => 'Svrha SPNAME je SPDESC', + 'hu' => 'A szolgáltatás (SPNAME) ezt a célt szolgálja: SPDESC', + 'pl' => 'Celem SPNAME jest SPDESC', + 'pt' => 'O propósito de SPNAME é SPDESC', + 'tr' => 'SPNAME\'in amacı SPDESC\'tir', + ), + 'consent_privacypolicy' => array ( + 'no' => 'Personvern for tjenesten', + 'nn' => 'Personvern for tenesta', + 'da' => 'Tjenestens politik vedrørende personoplysninger', + 'en' => 'Privacy policy for the service', + 'de' => 'Datenschutzrichtlinie des Dienstes', + 'sv' => 'Tjänstens policy för personlig integritet', + 'fi' => 'Tietosuojaseloste palvelulle', + 'es' => 'Política de privacidad para el servicio', + 'nl' => 'Privacybeleid voor de dienst', + 'sl' => 'Politika zasebnosti za ta SP', + 'hr' => 'Politika zaštite privatnosti', + 'hu' => 'A szolgáltatás adatvádelmi nyilatkozata', + 'pl' => 'Polityka prywatności dla serwisu', + 'pt' => 'Política de privacidade do serviço', + 'pt-br' => 'Política de Privacidade deste serviço', + 'tr' => 'Servis için gizlilik politikası', + ), + 'consent_attributes_header' => array ( + 'no' => 'Opplysninger som vil bli sendt til SPNAME', + 'nn' => 'Opplysningar som blir sende til SPNAME', + 'da' => 'Attributter som bliver sendt til SPNAME', + 'en' => 'Information that will be sent to SPNAME', + 'de' => 'Informationen, die an SPNAME gesandt werden', + 'sv' => 'Attribut som kommer att skickas till tjänsten', + 'fi' => 'Tiedot lähetetään palvelulle SPNAME', + 'es' => 'Atributos que serán enviados al servicio', + 'nl' => 'Informatie die naar SPNAME zal worden gestuurd', + 'sl' => 'Atributi, ki bodo poslani SPju', + 'hr' => 'Atributi koji će biti poslani servisu', + 'hu' => 'A(z) SPNAME szolgáltatónak küldött adatok', + 'pl' => 'Atrybuty, które zostaną przesłane do serwisu', + 'pt' => 'Informação que irá ser enviada para SPNAME', + 'tr' => 'SPNAME\'e gönderilecek bilgiler', + ), + 'show_attributes' => array ( + 'no' => 'Vis opplysninger', + 'nn' => 'Vis opplysingar', + 'da' => 'Vis attributter', + 'en' => 'Show attributes', + 'de' => 'Attribute zeigen', + 'sv' => 'Visa attribut', + 'fi' => 'Näytä henkilötiedot', + 'es' => 'Mostrar atributos', + 'nl' => 'Toon attributen', + 'sl' => 'Prikaži atribute', + 'hr' => 'Prikaži atribute', + 'hu' => 'Mutasd az attribútumokat', + 'pl' => 'Wyświetl atrybuty', + 'pt' => 'Mostrar atributos', + 'tr' => 'Özellikleri göster', + ), + 'show_attribute' => array ( + 'no' => 'Vis innhold', + 'nn' => 'Vis innhald', + 'da' => 'Vis indhold', + 'en' => 'Show content', + 'sv' => 'Visa samtycke', + 'es' => 'Mostrart consentimiento', + 'nl' => 'Toon inhoud', + 'sl' => 'Prikaži vsebino', + ), + 'login' => array ( + 'no' => 'innlogging', + 'nn' => 'Logg inn', + 'da' => 'login', + 'en' => 'login', + 'de' => 'anmelden', + 'sv' => 'Logga in', + 'fi' => 'Tunnus', + 'es' => 'login', + 'fr' => 'ouvrir une session', + 'nl' => 'Login', + 'lb' => 'anloggen', + 'sl' => 'Prijava', + 'hr' => 'prijava', + 'hu' => 'bejelentkezés', + 'pl' => 'login', + 'pt' => 'Entrar', + 'pt-br' => 'login', + 'tr' => 'Giriş', + ), + 'service_providers_for' => array ( + 'no' => 'Tjenesteleverandør for', + 'nn' => 'Tenesteleverandørar for', + 'da' => 'Tjenesteudbyder for', + 'en' => 'Service Providers for', + 'de' => 'Service-Provider für', + 'sv' => 'Tjänsteleverantörer för', + 'fi' => 'Palveluntarjoaja', + 'es' => 'Proveedores de servicio para', + 'fr' => 'Fournisseurs de services pour', + 'nl' => 'Service Providers voor', + 'lb' => 'Service Provider fir', + 'sl' => 'SP za', + 'hr' => 'Davatelji usluge za', + 'hu' => 'Alkalmazásszolgáltatók a következő számára', + 'pl' => 'Dostawca Serwisu dla', + 'pt' => 'Fornecedores de Serviço (SP) para', + 'pt-br' => 'Provedor de serviços para', + 'tr' => 'için Servis Sağlayıcılar', + ), + 'service_provider_header' => array ( + 'no' => 'Tjenesteleverandør', + 'nn' => 'Tenesteleverandør', + 'da' => 'Tjenesteudbyder', + 'en' => 'Service Provider', + 'de' => 'Service-Provider', + 'sv' => 'Tjänsteleverantör', + 'fi' => 'Palveluntarjoaja', + 'es' => 'Proveedor de servicio', + 'fr' => 'Fournisseur de service', + 'nl' => 'Service Provider', + 'lb' => 'Service Provider', + 'sl' => 'SP', + 'hr' => 'Davatelj usluge', + 'hu' => 'Alkalmazásszolgáltató', + 'pl' => 'Dostawca serwisu', + 'pt' => 'Fornecedor de Serviço (SP)', + 'pt-br' => 'Provedor de Serviços', + 'tr' => 'Servis Sağlayıcı', + ), + 'status_header' => array ( + 'no' => 'Samtykke-status', + 'nn' => 'Samtykkestatus', + 'da' => 'Samtykke status', + 'en' => 'Consent status', + 'de' => 'Zustimmungsstatus', + 'sv' => 'Status för samtycke', + 'fi' => 'Hyväksynnään tila', + 'es' => 'Estado del consentimiento', + 'fr' => 'État des consentements', + 'nl' => 'Toestemming status', + 'lb' => 'Zoustëmmungsstatus', + 'sl' => 'Stanje privolitve', + 'hr' => 'Status dozvole', + 'hu' => 'Hozzájárulás állapota', + 'pl' => 'Status zgody', + 'pt' => 'Consentimento', + 'pt-br' => 'Status do Consentimento', + 'tr' => 'Onay durumu', + ), + 'show_hide_attributes' => array ( + 'no' => 'Vis/skjul opplysninger', + 'nn' => 'Vis/skjul opplysningar', + 'da' => 'vis/skjul attributter', + 'en' => 'show/hide attributes', + 'de' => 'zeige/verstecke Eigenschaften', + 'sv' => 'visa/göm attribut', + 'fi' => 'Näytä/piilota attribuutteja', + 'es' => 'Mostrar/ocultar atributos', + 'fr' => 'montrer/cacher les attributs', + 'nl' => 'toon/verberg attributen', + 'lb' => 'Attributer weisen/verstoppen', + 'sl' => 'prikaži/skrij atribute', + 'hr' => 'prikaži/sakrij atribute', + 'hu' => 'attribútumok mutatása/elrejtése', + 'pl' => 'pokaż/ukryj atrybuty', + 'pt' => 'Mostrar/Ocultar atributos', + 'pt-br' => 'mostra/esconder Atributos', + 'tr' => 'bilgileri göster/gizle ', + ), + 'noconsent_title' => array ( + 'no' => 'Ikke akseptert overføring av informasjon', + 'nn' => 'Ikkje akseptert overføring av informasjon', + 'da' => 'Manglende samtykke', + 'en' => 'No consent given', + 'de' => 'Zustimmung verweigert', + 'sv' => 'Inget samtycket givet', + 'fi' => 'Lupaa ei annettu', + 'es' => 'No se dió el consentimiento', + 'nl' => 'Geen toestemming gegeven', + 'sl' => 'Privolitev ni bila dana.', + 'hr' => 'Nema pristanka', + 'hu' => 'Nincs hozzájárulás', + 'pl' => 'Nie wyrażono zgody', + 'pt' => 'Consentimento negado', + 'pt-br' => 'Nenhum consentimento dado', + 'tr' => 'Onay verilmemiş', + ), + 'noconsent_text' => array ( + 'no' => 'Du har ikke akseptert å overlevere opplysninger til tjenesteleverandøren.', + 'nn' => 'Du har ikkje akseptert til at dine opplysningar kan sendast til tenesteleverandøren', + 'da' => 'Du har ikke givet samtykke til overleveringen af oplysninger til tjenesten', + 'en' => 'You did not give consent for transfering your attributes to the service provider.', + 'de' => 'Sie haben der Weitergabe ihrer Daten an den Service Provider nicht zugestimmt.', + 'sv' => 'Du gav inte samtycke för att överföra dina attribut till tjänsteleverantören.', + 'fi' => 'Et antanut lupaa siirtää henkilötietojasi palveluntarjoajalle', + 'es' => 'No ha dado su consentimiento para tranferir sus atributos al proveedor de servicio.', + 'nl' => 'U heeft geen toestemming gegeven om uw attributen naar de Service Provider te versturen', + 'sl' => 'Niste podali privolitve za posredovanje atributov SP-ju.', + 'hr' => 'Niste dali pristanak da se vaši podaci isporuče davatelju usluge.', + 'hu' => 'Nem adta hozzájárulását, hogy adatait továbbadjuk a szolgáltatónak.', + 'pl' => 'Nie wyraziłeś zgody na przesłanie Twoich atrybutów do Dostawcy Serwisu.', + 'pt' => 'Negou o consentimento para a transferência dos seus atributos para o fornecedor de serviço.', + 'pt-br' => 'Você não deu o consentimento para a transferência de seus atributos para o provedor de serviços.', + 'tr' => 'Bilgilerinizin servis sağlayıcıya gönderilmesi için onay vermediniz.', + ), + 'noconsent_return' => array ( + 'no' => 'Gå tilbake til aksept-siden med opplysninger', + 'nn' => 'Gå tilbake til aksept-sida for overføring av opplysningar', + 'da' => 'Gå tilbage', + 'en' => 'Return to consent page', + 'de' => 'Zurück', + 'sv' => 'Åter till sidan för samtycke', + 'fi' => 'Palaa hyväksyntäsivulle', + 'es' => 'Volver a la página de consentimiento', + 'nl' => 'Keer terug naar de toestemmingspagina', + 'sl' => 'Vrnitev na privolitveno stran', + 'hr' => 'Povratak na stranicu s dozvolama', + 'hu' => 'Vissza az hozzájárulás-kezelő oldalra', + 'pl' => 'Powrót do strony wydania zezwolenia.', + 'pt' => 'Voltar à página de consentimento', + 'pt-br' => 'Retornar a página de consentimento', + 'tr' => 'Onay sayfasına geri dön', + ), + 'noconsent_goto_about' => array ( + 'no' => 'Gå til informasjonsside om tjenesten', + 'nn' => 'Gå til informasjonssida for tenesta', + 'da' => 'Gå til side med information om tjenesten', + 'en' => 'Go to information page for the service', + 'de' => 'Gehe zur Informationsseite dieses Dienstes', + 'sv' => 'Gå till tjänstens informationssida', + 'fi' => 'Siirry palvelun tiedot -sivulle', + 'es' => 'Ir a la página de información del servicio', + 'nl' => 'Ga naar de informatiepagina voor de service', + 'sl' => 'Pojdi na spletno stran z informacijami o storitvi', + 'hr' => 'Idi na stranicu s informacijama o servisu', + 'hu' => 'A szolgáltatás információs oldalára', + 'pl' => 'Przejdź do strony informacyjnej dla tego serwisu', + 'pt' => 'Ir para página de informação do serviço', + 'tr' => 'Servis için bilgi sayfasına git', + ), + 'table_caption' => array ( + 'no' => 'Bruker innformasjon', + 'da' => 'Bruger information', + 'en' => 'User information', + 'de' => 'Benutzerdaten', + 'sv' => 'Användarinformation', + 'fi' => 'Käyttäjätiedot', + 'es' => 'Información del usuario', + 'nl' => 'Gerbuikersinformatie', + 'sl' => 'Podatki o uporabniku', + 'hu' => 'Felhasználói információk', + 'pt' => 'Informação do utilizador', + 'tr' => 'Kullanıcı bilgisi', + ), + 'table_summary' => array ( + 'no' => 'Her listes den innformasjonen om deg som blir send til den tjenesten du er i ferd med å logge på', + 'da' => 'Informationer som vil blive sendt til den service du er ved at logge in på', + 'en' => 'List the information about you that is about to be transmitted to the service you are going to login to', + 'sv' => 'Visa den nformation om din användare som kommer att skickas till tjänsten som du är på väg att logga in i', + 'fi' => 'Näytä tietosi, joita ollaan siirtämässä palveluun', + 'es' => 'Muestra que información relativa a usted va a ser transmitida al servicio en el que se va a identificar', + 'nl' => 'De informatie die over jou bekend is en naar de service waarop je wilt inloggen verstuurd zal worden', + 'sl' => 'Seznam podatkov o vas, ki bodo posredovani storitvi, v katero se nameravate prijaviti', + 'hu' => 'Ezeket az adatokat fogjuk elküldeni Önről annak a szolgáltatásnak, ahová be kíván jelentkezni', + 'pt' => 'Listar a informação acerca de si que será enviada para o serviço no qual se está autenticar', + 'tr' => 'Girmek istediğiniz servise gönderilecek bilginizi listeleyin', + ), ); diff --git a/modules/consent/docs/consent.md b/modules/consent/docs/consent.md index 886d6fcbd197b12c04c5b23975f73fcf920ae145..80d12b1c4562982afee1f313d76e7976ade39a92 100644 --- a/modules/consent/docs/consent.md +++ b/modules/consent/docs/consent.md @@ -72,8 +72,8 @@ database. Here is the initialization SQL script: CREATE TABLE consent ( - consent_date TIMESTAMP NOT NULL, - usage_date TIMESTAMP NOT NULL, + consent_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + usage_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, hashed_user_id VARCHAR(80) NOT NULL, service_id VARCHAR(255) NOT NULL, attribute VARCHAR(80) NOT NULL, diff --git a/modules/consent/lib/Auth/Process/Consent.php b/modules/consent/lib/Auth/Process/Consent.php index 2319c36c053e23365f746d142285a17e2ce09e6a..0647ac84f194f457b48e5d83139f23cd30067ddf 100644 --- a/modules/consent/lib/Auth/Process/Consent.php +++ b/modules/consent/lib/Auth/Process/Consent.php @@ -288,12 +288,12 @@ class sspmod_consent_Auth_Process_Consent extends SimpleSAML_Auth_ProcessingFilt try { if ($this->_store->hasConsent($userId, $targetedId, $attributeSet)) { // Consent already given - SimpleSAML\Logger::stats('Consent: Consent found'); + SimpleSAML\Logger::stats('consent found'); SimpleSAML_Stats::log('consent:found', $statsData); return; } - SimpleSAML\Logger::stats('Consent: Consent notfound'); + SimpleSAML\Logger::stats('consent notfound'); SimpleSAML_Stats::log('consent:notfound', $statsData); $state['consent:store'] = $this->_store; @@ -302,11 +302,11 @@ class sspmod_consent_Auth_Process_Consent extends SimpleSAML_Auth_ProcessingFilt $state['consent:store.attributeSet'] = $attributeSet; } catch (Exception $e) { SimpleSAML\Logger::error('Consent: Error reading from storage: '.$e->getMessage()); - SimpleSAML\Logger::stats('Consent: Failed'); + SimpleSAML\Logger::stats('Ccnsent failed'); SimpleSAML_Stats::log('consent:failed', $statsData); } } else { - SimpleSAML\Logger::stats('Consent: No storage'); + SimpleSAML\Logger::stats('consent nostorage'); SimpleSAML_Stats::log('consent:nostorage', $statsData); } @@ -375,6 +375,9 @@ class sspmod_consent_Auth_Process_Consent extends SimpleSAML_Auth_ProcessingFilt public static function getAttributeHash($attributes, $includeValues = false) { if ($includeValues) { + foreach ($attributes as &$values) { + sort($values); + } ksort($attributes); $hashBase = serialize($attributes); } else { diff --git a/modules/consent/lib/Consent/Store/Database.php b/modules/consent/lib/Consent/Store/Database.php index d23f2c6a14bed23e9ac350d4fb8245986306591e..82017a2157f48aea11bc5049c34449dde4f26693 100644 --- a/modules/consent/lib/Consent/Store/Database.php +++ b/modules/consent/lib/Consent/Store/Database.php @@ -37,6 +37,11 @@ class sspmod_consent_Consent_Store_Database extends sspmod_consent_Store */ private $_password; + /** + * Options for the database; + */ + private $_options; + /** * Table with consent. */ @@ -98,6 +103,14 @@ class sspmod_consent_Consent_Store_Database extends sspmod_consent_Store $this->_password = null; } + if (array_key_exists('options', $config)) { + if (!is_array($config['options'])) { + throw new Exception('consent:Database - \'options\' is supposed to be an array.'); + } + $this->_options = $config['options']; + } else { + $this->_options = null; + } if (array_key_exists('table', $config)) { if (!is_string($config['table'])) { throw new Exception('consent:Database - \'table\' is supposed to be a string.'); @@ -380,7 +393,7 @@ class sspmod_consent_Consent_Store_Database extends sspmod_consent_Store // Get total number of consents $st = $this->_execute('SELECT COUNT(*) AS no FROM '.$this->_table, array()); - + if ($st === false) { return array(); } @@ -395,7 +408,7 @@ class sspmod_consent_Consent_Store_Database extends sspmod_consent_Store 'FROM (SELECT DISTINCT hashed_user_id FROM '.$this->_table.' ) AS foo', array() ); - + if ($st === false) { return array(); } @@ -409,7 +422,7 @@ class sspmod_consent_Consent_Store_Database extends sspmod_consent_Store 'SELECT COUNT(*) AS no FROM (SELECT DISTINCT service_id FROM '.$this->_table.') AS foo', array() ); - + if ($st === false) { return array(); } @@ -437,8 +450,13 @@ class sspmod_consent_Consent_Store_Database extends sspmod_consent_Store if (isset($this->_timeout)) { $driver_options[PDO::ATTR_TIMEOUT] = $this->_timeout; } + if (isset($this->_options)) { + $this->_options = array_merge($driver_options, $this->_options); + } else { + $this->_options = $driver_options; + } - $this->_db = new PDO($this->_dsn, $this->_username, $this->_password, $driver_options); + $this->_db = new PDO($this->_dsn, $this->_username, $this->_password, $this->_options); return $this->_db; } diff --git a/modules/core/dictionaries/cardinality.definition.json b/modules/core/dictionaries/cardinality.definition.json new file mode 100644 index 0000000000000000000000000000000000000000..b9059cb93a389ad4422be193ae8b17371ed426cf --- /dev/null +++ b/modules/core/dictionaries/cardinality.definition.json @@ -0,0 +1,14 @@ +{ + "cardinality_header": { + "en": "Incorrect Attributes" + }, + "cardinality_text": { + "en": "One or more of the attributes supplied by your identity provider did not contain the expected number of values." + }, + "problematic_attributes": { + "en": "The problematic attribute(s) are:" + }, + "got_want": { + "en": "got %GOT% values, want %WANT%" + } +} diff --git a/modules/core/dictionaries/cardinality.translation.json b/modules/core/dictionaries/cardinality.translation.json new file mode 100644 index 0000000000000000000000000000000000000000..d54ab371fbefc1cec8a11e65efae46e0e1996980 --- /dev/null +++ b/modules/core/dictionaries/cardinality.translation.json @@ -0,0 +1,15 @@ +{ + "cardinality_header": { + "af": "Onjuiste Eienskappe", + "nl": "Niet de juiste attributen" + }, + "cardinality_text": { + "nl": "Één of meer door de Identity Provider geleverde attributen bevat niet het vereiste aantal attributen." + }, + "problematic_attributes": { + "nl": "De onjuiste attributen zijn:" + }, + "got_want": { + "nl": "%GOT% ontvangen waarden, %WANT% vereist" + } +} diff --git a/modules/core/docs/authproc_cardinality.md b/modules/core/docs/authproc_cardinality.md new file mode 100644 index 0000000000000000000000000000000000000000..0902bf27863dad94991aacb54d15a7a407750f63 --- /dev/null +++ b/modules/core/docs/authproc_cardinality.md @@ -0,0 +1,47 @@ +`core:Cardinality` +================== + +Ensure the number of attribute values is within the specified multiplicity. + +This filter should contain a set of attribute name => rule pairs describing the multiplicity rules for an attribute. + +The special parameter `%ignoreEntities` can be used to give an array of entity IDs that should be ignored for testing, etc purposes. + +Specifying Rules +---------------- + +Multiplicity rules are specified as an associative array containing one or more of the following parameters: + +`min` +: The minimum number of values (participation) this attribute should have. Defaults to `zero`. + +`max` +: The maximum number of values (cardinality) this attribute should have. Defaults to no upper bound. + +`warn` +: Log a warning rather than generating an error. Defaults to `false`. + +For convenience, minimum and maximum values can also be specified using a shorthand list notation. + +Examples +-------- + +Require at least one `givenName`, no more than two email addresses, and between two and four values for `eduPersonScopedAffiliation`. + + 'authproc' => array( + 50 => array( + 'class' => 'core:Cardinality', + 'givenName' => array('min' => 1), + 'mail' => array('max' => 2), + 'eduPersonScopedAffiliation' => array('min' => 2, 'max' => 4), + ), + ), + +Use the shorthand notation for min, max: + + 'authproc' => array( + 50 => array( + 'class' => 'core:Cardinality', + 'mail' => array(0, 2), + ), + ), diff --git a/modules/core/docs/authproc_cardinalitysingle.md b/modules/core/docs/authproc_cardinalitysingle.md new file mode 100644 index 0000000000000000000000000000000000000000..4a61bdbc5aaca4579729fc0bafec6b6976a2efea --- /dev/null +++ b/modules/core/docs/authproc_cardinalitysingle.md @@ -0,0 +1,88 @@ +`core:CardinalitySingle` +======================== + +Ensure the correct cardinality of single-valued attributes. This filter is a special case +of the more generic [core:Cardinality] filter that allows for optional corrective measures +when multi-valued attributes are received where single-valued ones are expected. + +Parameters +---------- + +This filter implements a number of optional parameters: + +`singleValued` +: array of attribute names that *must* be single-valued, or a 403 error is generated. + +`firstValue` +: array of attribute names where only the first value of a multi-valued assertion should be returned. + +`flatten` +: array of attribute names where a multi-valued assertion is flattened into a single delimited string. + +`flattenWith` +: the delimiter for `flatten`. Defaults to ";". + +`ignoreEntities` +: array of entity IDs that should be ignored for testing, etc purposes. + +When the same attribute name appears in multiple stanzas, they are processed in the order above. + +Examples +-------- + +Abort with an error if any attribute defined as single-valued in the eduPerson or SCHAC schemas exists and has more than one value: + + 'authproc' => array( + 50 => array( + 'class' => 'core:CardinalitySingle', + 'singleValued' => array( + /* from eduPerson (internet2-mace-dir-eduperson-201602) */ + 'eduPersonOrgDN', 'eduPersonPrimaryAffiliation', 'eduPersonPrimaryOrgUnitDN', + 'eduPersonPrincipalName', 'eduPersonUniqueId', + /* from inetOrgPerson (RFC2798), referenced by internet2-mace-dir-eduperson-201602 */ + 'displayName', 'preferredLanguage', + /* from SCHAC-IAD Version 1.3.0 */ + 'schacMotherTongue', 'schacGender', 'schacDateOfBirth', 'schacPlaceOfBirth', + 'schacPersonalTitle', 'schacHomeOrganization', 'schacHomeOrganizationType', + 'schacExpiryDate', + ), + ), + ), + +Abort if multiple values are received for `eduPersonPrincipalName`, but take the first value for `eduPersonPrimaryAffiliation`: + + 'authproc' => array( + 50 => array( + 'class' => 'core:CardinalitySingle', + 'singleValued' => array('eduPersonPrincipalName'), + 'firstValue' => array('eduPersonPrimaryAffiliation'), + ), + ), + ), + +Construct `eduPersonPrimaryAffiliation` using the first value in `eduPersonAffiliation`: + + 'authproc' => array( + 50 => array( + 'class' => 'core:AttributeCopy', + 'eduPersonAffiliation' => 'eduPersonPrimaryAffiliation', + ), + 51 => array( + 'class' => 'core:CardinalitySingle', + 'firstValue' => array('eduPersonPrimaryAffiliation'), + ), + ), + +Construct a single, comma-separated value version of `eduPersonAffiliation`: + + 'authproc' => array( + 50 => array( + 'class' => 'core:AttributeCopy', + 'eduPersonAffiliation' => 'eduPersonAffiliationWithCommas', + ), + 51 => array( + 'class' => 'core:CardinalitySingle', + 'flatten' => array('eduPersonAffiliationWithCommas'), + 'flattenWith' => ',', + ), + ), diff --git a/modules/core/docs/authproc_scopefromattribute.md b/modules/core/docs/authproc_scopefromattribute.md index 0b7f92b9882897918cc1fd66fd4566140aa3659c..5eba851b9f8702fbbb322bf6c28ee48d0bbf895b 100644 --- a/modules/core/docs/authproc_scopefromattribute.md +++ b/modules/core/docs/authproc_scopefromattribute.md @@ -25,7 +25,7 @@ Set the `scope` attribute to the scope from the `eduPersonPrincipalName` attribu 'authproc' => array( 50 => array( 'class' => 'core:ScopeFromAttribute', - 'sourceAttribute' => 'eduPersonPrincipal' - 'targetAttribute' => 'scope' + 'sourceAttribute' => 'eduPersonPrincipalName', + 'targetAttribute' => 'scope', ), ), diff --git a/modules/core/lib/Auth/Process/Cardinality.php b/modules/core/lib/Auth/Process/Cardinality.php new file mode 100644 index 0000000000000000000000000000000000000000..74d330f00c7f1f7526eb2ad45ffa61df1171e223 --- /dev/null +++ b/modules/core/lib/Auth/Process/Cardinality.php @@ -0,0 +1,164 @@ +<?php + +/** + * Filter to ensure correct cardinality of attributes + * + * @author Guy Halse, http://orcid.org/0000-0002-9388-8592 + * @package SimpleSAMLphp + */ +class sspmod_core_Auth_Process_Cardinality extends SimpleSAML_Auth_ProcessingFilter +{ + /** @var array Associative array with the mappings of attribute names. */ + private $cardinality = array(); + + /** @var array Entities that should be ignored */ + private $ignoreEntities = array(); + + /** + * Initialize this filter, parse configuration. + * + * @param array $config Configuration information about this filter. + * @param mixed $reserved For future use. + * @throws SimpleSAML_Error_Exception + */ + public function __construct($config, $reserved) + { + parent::__construct($config, $reserved); + assert(is_array($config)); + + foreach ($config as $attribute => $rules) { + if ($attribute === '%ignoreEntities') { + $this->ignoreEntities = $config['%ignoreEntities']; + continue; + } + + if (!is_string($attribute)) { + throw new SimpleSAML_Error_Exception('Invalid attribute name: '.var_export($attribute, true)); + } + $this->cardinality[$attribute] = array('warn' => false); + + /* allow either positional or name-based parameters */ + if (isset($rules[0])) { + $this->cardinality[$attribute]['min'] = $rules[0]; + } elseif (isset($rules['min'])) { + $this->cardinality[$attribute]['min'] = $rules['min']; + } + if (isset($rules[1])) { + $this->cardinality[$attribute]['max'] = $rules[1]; + } elseif (isset($rules['max'])) { + $this->cardinality[$attribute]['max'] = $rules['max']; + } + if (array_key_exists('warn', $rules)) { + $this->cardinality[$attribute]['warn'] = (bool) $rules['warn']; + } + + /* sanity check the rules */ + if (!array_key_exists('min', $this->cardinality[$attribute])) { + $this->cardinality[$attribute]['min'] = 0; + } elseif (!is_int($this->cardinality[$attribute]['min']) || + $this->cardinality[$attribute]['min'] < 0 + ) { + throw new SimpleSAML_Error_Exception('Minimum cardinality must be a positive integer: '. + var_export($attribute, true)); + } + if (array_key_exists('max', $this->cardinality[$attribute]) && + !is_int($this->cardinality[$attribute]['max']) + ) { + throw new SimpleSAML_Error_Exception('Maximum cardinality must be a positive integer: '. + var_export($attribute, true)); + } + if (array_key_exists('min', $this->cardinality[$attribute]) && + array_key_exists('max', $this->cardinality[$attribute]) && + $this->cardinality[$attribute]['min'] > $this->cardinality[$attribute]['max'] + ) { + throw new SimpleSAML_Error_Exception('Minimum cardinality must be less than maximium: '. + var_export($attribute, true)); + } + + /* generate a display expression */ + $this->cardinality[$attribute]['_expr'] = sprintf('%d ≤ n', $this->cardinality[$attribute]['min']); + if (array_key_exists('max', $this->cardinality[$attribute])) { + $this->cardinality[$attribute]['_expr'] .= sprintf(' ≤ %d', $this->cardinality[$attribute]['max']); + } + } + } + + /** + * Process this filter + * + * @param array &$request The current request + */ + public function process(&$request) + { + assert(is_array($request)); + assert(array_key_exists("Attributes", $request)); + + $entityid = false; + if (array_key_exists('Source', $request) && array_key_exists('entityid', $request['Source'])) { + $entityid = $request['Source']['entityid']; + } + if (in_array($entityid, $this->ignoreEntities)) { + SimpleSAML\Logger::debug('Cardinality: Ignoring assertions from '.$entityid); + return; + } + + foreach ($request['Attributes'] as $k => $v) { + + if (!array_key_exists($k, $this->cardinality)) { + continue; + } + if (!is_array($v)) { + $v = array($v); + } + + /* minimum cardinality */ + if (count($v) < $this->cardinality[$k]['min']) { + if ($this->cardinality[$k]['warn']) { + SimpleSAML\Logger::warning(sprintf( + 'Cardinality: attribute %s from %s does not meet minimum cardinality of %d (%d)', + $k, $entityid, $this->cardinality[$k]['min'], count($v) + )); + } else { + $request['core:cardinality:errorAttributes'][$k] = array(count($v), $this->cardinality[$k]['_expr']); + } + continue; + } + + /* maximum cardinality */ + if (array_key_exists('max', $this->cardinality[$k]) && count($v) > $this->cardinality[$k]['max']) { + if ($this->cardinality[$k]['warn']) { + SimpleSAML\Logger::warning(sprintf( + 'Cardinality: attribute %s from %s does not meet maximum cardinality of %d (%d)', + $k, $entityid, $this->cardinality[$k]['max'], count($v) + )); + } else { + $request['core:cardinality:errorAttributes'][$k] = array(count($v), $this->cardinality[$k]['_expr']); + } + continue; + } + } + + /* check for missing attributes with a minimum cardinality */ + foreach ($this->cardinality as $k => $v) { + if (!$this->cardinality[$k]['min'] || array_key_exists($k, $request['Attributes'])) { + continue; + } + if ($this->cardinality[$k]['warn']) { + SimpleSAML\Logger::warning(sprintf( + 'Cardinality: attribute %s from %s is missing', + $k, $entityid + )); + } else { + $request['core:cardinality:errorAttributes'][$k] = array(0, $this->cardinality[$k]['_expr']); + } + } + + /* abort if we found a problematic attribute */ + if (array_key_exists('core:cardinality:errorAttributes', $request)) { + $id = SimpleSAML_Auth_State::saveState($request, 'core:cardinality'); + $url = SimpleSAML\Module::getModuleURL('core/cardinality_error.php'); + \SimpleSAML\Utils\HTTP::redirectTrustedURL($url, array('StateId' => $id)); + return; + } + } +} diff --git a/modules/core/lib/Auth/Process/CardinalitySingle.php b/modules/core/lib/Auth/Process/CardinalitySingle.php new file mode 100644 index 0000000000000000000000000000000000000000..459d57d8ba162893bcd20fa4f5e1585d43ac5731 --- /dev/null +++ b/modules/core/lib/Auth/Process/CardinalitySingle.php @@ -0,0 +1,109 @@ +<?php + +/** + * Filter to ensure correct cardinality of single-valued attributes + * + * This filter implements a special case of the core:Cardinality filter, and + * allows for optional corrections to be made when cardinality errors are encountered. + * + * @author Guy Halse, http://orcid.org/0000-0002-9388-8592 + * @package SimpleSAMLphp + */ +class sspmod_core_Auth_Process_CardinalitySingle extends SimpleSAML_Auth_ProcessingFilter +{ + /** @var array Attributes that should be single-valued or we generate an error */ + private $singleValued = array(); + + /** @var array Attributes for which the first value should be taken */ + private $firstValue = array(); + + /** @var array Attributes that can be flattened to a single value */ + private $flatten = array(); + + /** @var string Separator for flattened value */ + private $flattenWith = ';'; + + /** @var array Entities that should be ignored */ + private $ignoreEntities = array(); + + /** + * Initialize this filter, parse configuration. + * + * @param array $config Configuration information about this filter. + * @param mixed $reserved For future use. + */ + public function __construct($config, $reserved) + { + parent::__construct($config, $reserved); + assert(is_array($config)); + + if (array_key_exists('singleValued', $config)) { + $this->singleValued = $config['singleValued']; + } + if (array_key_exists('firstValue', $config)) { + $this->firstValue = $config['firstValue']; + } + if (array_key_exists('flattenWith', $config)) { + $this->flattenWith = is_array($config['flattenWith']) ? array_shift($config['flattenWith']) : $config['flattenWith']; + } + if (array_key_exists('flatten', $config)) { + $this->flatten = $config['flatten']; + } + if (array_key_exists('ignoreEntities', $config)) { + $this->ignoreEntities = $config['ignoreEntities']; + } + /* for consistency with core:Cardinality */ + if (array_key_exists('%ignoreEntities', $config)) { + $this->ignoreEntities = $config['%ignoreEntities']; + } + } + + /** + * Process this filter + * + * @param array &$request The current request + */ + public function process(&$request) + { + assert(is_array($request)); + assert(array_key_exists("Attributes", $request)); + + if (array_key_exists('Source', $request) && + array_key_exists('entityid', $request['Source']) && + in_array($request['Source']['entityid'], $this->ignoreEntities) + ) { + SimpleSAML\Logger::debug('CardinalitySingle: Ignoring assertions from '.$request['Source']['entityid']); + return; + } + + foreach ($request['Attributes'] as $k => $v) { + if (!is_array($v)) { + continue; + } + if (count($v) <= 1) { + continue; + } + + if (in_array($k, $this->singleValued)) { + $request['core:cardinality:errorAttributes'][$k] = array(count($v), '0 ≤ n ≤ 1'); + continue; + } + if (in_array($k, $this->firstValue)) { + $request['Attributes'][$k] = array(array_shift($v)); + continue; + } + if (in_array($k, $this->flatten)) { + $request['Attributes'][$k] = array(implode($this->flattenWith, $v)); + continue; + } + } + + /* abort if we found a problematic attribute */ + if (array_key_exists('core:cardinality:errorAttributes', $request)) { + $id = SimpleSAML_Auth_State::saveState($request, 'core:cardinality'); + $url = SimpleSAML\Module::getModuleURL('core/cardinality_error.php'); + \SimpleSAML\Utils\HTTP::redirectTrustedURL($url, array('StateId' => $id)); + return; + } + } +} diff --git a/modules/core/lib/Auth/Process/PHP.php b/modules/core/lib/Auth/Process/PHP.php index 48d668c4ee62f99d6d911055cadc3b31ee0a4521..5b7f11711bc8507b2dbc68e55d37eebafbe7ed17 100644 --- a/modules/core/lib/Auth/Process/PHP.php +++ b/modules/core/lib/Auth/Process/PHP.php @@ -48,7 +48,7 @@ class sspmod_core_Auth_Process_PHP extends SimpleSAML_Auth_ProcessingFilter assert(is_array($request)); assert(array_key_exists('Attributes', $request)); - $function = create_function('&$attributes', $this->code); + $function = function(&$attributes) { eval($this->code); }; $function($request['Attributes']); } } diff --git a/modules/core/lib/Auth/UserPassBase.php b/modules/core/lib/Auth/UserPassBase.php index bc05f61967cf976ac80f323a148759dfd215a5be..474d0a03bbfc66a043978f6ded6ed8699f00c469 100644 --- a/modules/core/lib/Auth/UserPassBase.php +++ b/modules/core/lib/Auth/UserPassBase.php @@ -196,7 +196,7 @@ abstract class sspmod_core_Auth_UserPassBase extends SimpleSAML_Auth_Source { } $attributes = $this->login($username, $password); - assert('is_array($attributes)'); + assert(is_array($attributes)); $state['Attributes'] = $attributes; return; diff --git a/modules/core/lib/Storage/SQLPermanentStorage.php b/modules/core/lib/Storage/SQLPermanentStorage.php index 54bb5642bf1b59201b850164f1ac343489d4b64a..57daf749e38d86ea665dab3065796a0c5f267285 100644 --- a/modules/core/lib/Storage/SQLPermanentStorage.php +++ b/modules/core/lib/Storage/SQLPermanentStorage.php @@ -9,198 +9,208 @@ * @author Andreas Åkre Solberg <andreas@uninett.no>, UNINETT AS. * @package SimpleSAMLphp */ -class sspmod_core_Storage_SQLPermanentStorage { - - private $db; - - function __construct($name, $config = NULL) { - if (is_null($config)) - $config = SimpleSAML_Configuration::getInstance(); - - $datadir = $config->getPathValue('datadir', 'data/'); - - if (!is_dir($datadir)) - throw new Exception('Data directory [' . $datadir. '] does not exist'); - if (!is_writable($datadir)) - throw new Exception('Data directory [' . $datadir. '] is not writable'); - - $sqllitedir = $datadir . 'sqllite/'; - if (!is_dir($sqllitedir)) { - mkdir($sqllitedir); - } - - $dbfile = $sqllitedir . $name . '.sqllite'; - - if ($this->db = new SQLiteDatabase($dbfile)) { - $q = @$this->db->query('SELECT key1 FROM data LIMIT 1'); - if ($q === false) { - $this->db->queryExec(' - CREATE TABLE data ( - key1 text, - key2 text, - type text, - value text, - created timestamp, - updated timestamp, - expire timestamp, - PRIMARY KEY (key1,key2,type) - ); - '); - } - } else { - throw new Exception('Error creating SQL lite database [' . $dbfile . '].'); - } - } - - public function set($type, $key1, $key2, $value, $duration = NULL) { - if ($this->exists($type, $key1, $key2)) { - $this->update($type, $key1, $key2, $value, $duration); - } else { - $this->insert($type, $key1, $key2, $value, $duration); - } - } - - private function insert($type, $key1, $key2, $value, $duration = NULL) { - - $setDuration = ''; - if (is_null($duration)) { - $setDuration = 'NULL'; - } else { - $setDuration = "'" . sqlite_escape_string(time() + $duration) . "'"; - } - - $query = "INSERT INTO data (key1,key2,type,created,updated,expire,value) VALUES (" . - "'" . sqlite_escape_string($key1) . "'," . - "'" . sqlite_escape_string($key2) . "'," . - "'" . sqlite_escape_string($type) . "'," . - "'" . sqlite_escape_string(time()) . "'," . - "'" . sqlite_escape_string(time()) . "'," . - $setDuration . "," . - "'" . sqlite_escape_string(serialize($value)) . "')"; - $results = $this->db->queryExec($query); - return $results; - } - - private function update($type, $key1, $key2, $value, $duration = NULL) { - - $setDuration = ''; - if (is_null($duration)) { - $setDuration = ", expire = NULL "; - } else { - $setDuration = ", expire = '" . sqlite_escape_string(time() + $duration) . "' "; - } - - $query = "UPDATE data SET " . - "updated = '" . sqlite_escape_string(time()) . "'," . - "value = '" . sqlite_escape_string(serialize($value)) . "'" . - $setDuration . - "WHERE " . - "key1 = '" . sqlite_escape_string($key1) . "' AND " . - "key2 = '" . sqlite_escape_string($key2) . "' AND " . - "type = '" . sqlite_escape_string($type) . "'"; - $results = $this->db->queryExec($query); - return $results; - } - - public function get($type = NULL, $key1 = NULL, $key2 = NULL) { - - $condition = self::getCondition($type, $key1, $key2); - $query = "SELECT * FROM data WHERE " . $condition; - $results = $this->db->arrayQuery($query, SQLITE_ASSOC); - - if (count($results) !== 1) return NULL; - - $res = $results[0]; - $res['value'] = unserialize($res['value']); - return $res; - } - - /* - * Return the value directly (not in a container) - */ - public function getValue($type = NULL, $key1 = NULL, $key2 = NULL) { - $res = $this->get($type, $key1, $key2); - if ($res === NULL) return NULL; - return $res['value']; - } - - public function exists($type, $key1, $key2) { - $query = "SELECT * FROM data WHERE " . - "key1 = '" . sqlite_escape_string($key1) . "' AND " . - "key2 = '" . sqlite_escape_string($key2) . "' AND " . - "type = '" . sqlite_escape_string($type) . "' LIMIT 1"; - $results = $this->db->arrayQuery($query, SQLITE_ASSOC); - return (count($results) == 1); - } - - public function getList($type = NULL, $key1 = NULL, $key2 = NULL) { - - $condition = self::getCondition($type, $key1, $key2); - $query = "SELECT * FROM data WHERE " . $condition; - $results = $this->db->arrayQuery($query, SQLITE_ASSOC); - if (count($results) == 0) return NULL; - - foreach($results AS $key => $value) { - $results[$key]['value'] = unserialize($results[$key]['value']); - } - return $results; - } - - public function getKeys($type = NULL, $key1 = NULL, $key2 = NULL, $whichKey = 'type') { - - if (!in_array($whichKey, array('key1', 'key2', 'type'), true)) - throw new Exception('Invalid key type'); - - $condition = self::getCondition($type, $key1, $key2); - - $query = "SELECT DISTINCT " . $whichKey . " FROM data WHERE " . $condition; - $results = $this->db->arrayQuery($query, SQLITE_ASSOC); - - if (count($results) == 0) return NULL; - - $resarray = array(); - foreach($results AS $key => $value) { - $resarray[] = $value[$whichKey]; - } - - return $resarray; - } - - - public function remove($type, $key1, $key2) { - $query = "DELETE FROM data WHERE " . - "key1 = '" . sqlite_escape_string($key1) . "' AND " . - "key2 = '" . sqlite_escape_string($key2) . "' AND " . - "type = '" . sqlite_escape_string($type) . "'"; - $results = $this->db->arrayQuery($query, SQLITE_ASSOC); - return (count($results) == 1); - } - - public function removeExpired() { - $query = "DELETE FROM data WHERE expire NOT NULL AND expire < " . time(); - $this->db->arrayQuery($query, SQLITE_ASSOC); - $changes = $this->db->changes(); - return $changes; - } - - - /** - * Create a SQL condition statement based on parameters - */ - private static function getCondition($type = NULL, $key1 = NULL, $key2 = NULL) { - $conditions = array(); - - if (!is_null($type)) $conditions[] = "type = '" . sqlite_escape_string($type) . "'"; - if (!is_null($key1)) $conditions[] = "key1 = '" . sqlite_escape_string($key1) . "'"; - if (!is_null($key2)) $conditions[] = "key2 = '" . sqlite_escape_string($key2) . "'"; - - if (count($conditions) === 0) return '1'; - - $condition = join(' AND ', $conditions); - - return $condition; - } - - +class sspmod_core_Storage_SQLPermanentStorage +{ + private $db; + + public function __construct($name, $config = null) + { + if (is_null($config)) { + $config = SimpleSAML_Configuration::getInstance(); + } + + $datadir = $config->getPathValue('datadir', 'data/'); + + if (!is_dir($datadir)) { + throw new Exception('Data directory ['.$datadir.'] does not exist'); + } else if (!is_writable($datadir)) { + throw new Exception('Data directory ['.$datadir.'] is not writable'); + } + + $sqllitedir = $datadir.'sqllite/'; + if (!is_dir($sqllitedir)) { + mkdir($sqllitedir); + } + + $dbfile = 'sqlite:'.$sqllitedir.$name.'.sqlite'; + if ($this->db = new \PDO($dbfile)) { + $q = @$this->db->query('SELECT key1 FROM data LIMIT 1'); + if ($q === false) { + $this->db->exec(' + CREATE TABLE data ( + key1 text, + key2 text, + type text, + value text, + created timestamp, + updated timestamp, + expire timestamp, + PRIMARY KEY (key1,key2,type) + ); + '); + } + } else { + throw new Exception('Error creating SQL lite database ['.$dbfile.'].'); + } + } + + public function set($type, $key1, $key2, $value, $duration = null) + { + if ($this->exists($type, $key1, $key2)) { + $this->update($type, $key1, $key2, $value, $duration); + } else { + $this->insert($type, $key1, $key2, $value, $duration); + } + } + + private function insert($type, $key1, $key2, $value, $duration = null) + { + $expire = is_null($duration) ? null : (time() + $duration); + + $query = "INSERT INTO data (key1, key2, type, created, updated, expire, value)". + " VALUES(:key1, :key2, :type, :created, :updated, :expire, :value)"; + $prepared = $this->db->prepare($query); + $data = array(':key1' => $key1, ':key2' => $key2, + ':type' => $type, ':created' => time(), + ':updated' => time(), ':expire' => $expire, + ':value' => serialize($value)); + $prepared->execute($data); + $results = $prepared->fetchAll(PDO::FETCH_ASSOC); + return $results; + } + + private function update($type, $key1, $key2, $value, $duration = null) + { + $expire = is_null($duration) ? null : (time() + $duration); + + $query = "UPDATE data SET updated = :updated, value = :value, expire = :expire WHERE key1 = :key1 AND key2 = :key2 AND type = :type"; + $prepared = $this->db->prepare($query); + $data = array(':key1' => $key1, ':key2' => $key2, + ':type' => $type, ':updated' => time(), + ':expire' => $expire, ':value' => serialize($value)); + $prepared->execute($data); + $results = $prepared->fetchAll(PDO::FETCH_ASSOC); + return $results; + } + + public function get($type = null, $key1 = null, $key2 = null) + { + $conditions = self::getCondition($type, $key1, $key2); + $query = 'SELECT * FROM data WHERE '.$conditions; + + $prepared = $this->db->prepare($query); + $prepared->execute(); + $results = $prepared->fetchAll(PDO::FETCH_ASSOC); + if (count($results) !== 1) { + return null; + } + + $res = $results[0]; + $res['value'] = unserialize($res['value']); + return $res; + } + + /* + * Return the value directly (not in a container) + */ + public function getValue($type = null, $key1 = null, $key2 = null) + { + $res = $this->get($type, $key1, $key2); + if ($res === null) { + return null; + } + return $res['value']; + } + + public function exists($type, $key1, $key2) + { + $query = 'SELECT * FROM data WHERE type = :type AND key1 = :key1 AND key2 = :key2 LIMIT 1'; + $prepared = $this->db->prepare($query); + $data = array(':type' => $type, ':key1' => $key1, ':key2' => $key2); + $prepared->execute($data); + $results = $prepared->fetchAll(PDO::FETCH_ASSOC); + return (count($results) == 1); + } + + public function getList($type = null, $key1 = null, $key2 = null) + { + $conditions = self::getCondition($type, $key1, $key2); + $query = 'SELECT * FROM data WHERE '.$conditions; + $prepared = $this->db->prepare($query); + $prepared->execute(); + + $results = $prepared->fetchAll(PDO::FETCH_ASSOC); + if (count($results) == 0) { + return null; + } + + foreach ($results as $key => $value) { + $results[$key]['value'] = unserialize($results[$key]['value']); + } + return $results; + } + + public function getKeys($type = null, $key1 = null, $key2 = null, $whichKey = 'type') + { + if (!in_array($whichKey, array('key1', 'key2', 'type'), true)) { + throw new Exception('Invalid key type'); + } + + $conditions = self::getCondition($type, $key1, $key2); + $query = 'SELECT DISTINCT :whichKey FROM data WHERE '.$conditions; + $prepared = $this->db->prepare($query); + $data = array('whichKey' => $whichKey); + $prepared->execute($data); + $results = $prepared->fetchAll(PDO::FETCH_ASSOC); + + if (count($results) == 0) { + return null; + } + + $resarray = array(); + foreach ($results as $key => $value) { + $resarray[] = $value[$whichKey]; + } + return $resarray; + } + + public function remove($type, $key1, $key2) + { + $query = 'DELETE FROM data WHERE type = :type AND key1 = :key1 AND key2 = :key2'; + $prepared = $this->db->prepare($query); + $data = array(':type' => $type, ':key1' => $key1, ':key2' => $key2); + $prepared->execute($data); + $results = $prepared->fetchAll(PDO::FETCH_ASSOC); + return (count($results) == 1); + } + + public function removeExpired() + { + $query = "DELETE FROM data WHERE expire NOT NULL AND expire < :expire"; + $prepared = $this->db->prepare($query); + $data = array(':expire' => time()); + $prepared->execute($data); + return $prepared->rowCount(); + } + + /** + * Create a SQL condition statement based on parameters + */ + private function getCondition($type = null, $key1 = null, $key2 = null) + { + $conditions = array(); + if (!is_null($type)) { + $conditions[] = "type = ".$this->db->quote($type); + } + if (!is_null($key1)) { + $conditions[] = "key1 = ".$this->db->quote($key1); + } + if (!is_null($key2)) { + $conditions[] = "key2 = ".$this->db->quote($key2); + } + + $conditions[] = "(expire = NULL OR expire >= ".time().")"; + return join(' AND ', $conditions); + } } diff --git a/modules/core/templates/cardinality_error.tpl.php b/modules/core/templates/cardinality_error.tpl.php new file mode 100644 index 0000000000000000000000000000000000000000..dc75c540afc9b7f71a167a5d397188eca3cb3e47 --- /dev/null +++ b/modules/core/templates/cardinality_error.tpl.php @@ -0,0 +1,37 @@ +<?php +/** + * Template which is shown when when an attribute violates a cardinality rule + * + * Parameters: + * - 'target': Target URL. + * - 'params': Parameters which should be included in the request. + * + * @package SimpleSAMLphp + */ + + +$this->data['cardinality_header'] = $this->t('{core:cardinality:cardinality_header}'); +$this->data['cardinality_text'] = $this->t('{core:cardinality:cardinality_text}'); +$this->data['problematic_attributes'] = $this->t('{core:cardinality:problematic_attributes}'); + +$this->includeAtTemplateBase('includes/header.php'); +?> +<h1><?php echo $this->data['cardinality_header']; ?></h1> +<p><?php echo $this->data['cardinality_text']; ?></p> +<h3><?php echo $this->data['problematic_attributes']; ?></h3> +<dl class="cardinalityErrorAttributes"> +<?php foreach ($this->data['cardinalityErrorAttributes'] as $attr => $v) { ?> + <dt><?php echo $attr ?></td> + <dd><?php echo $this->t('{core:cardinality:got_want}', array('%GOT%' => $v[0], '%WANT%' => htmlspecialchars($v[1]))) ?></dd> + </tr> +<?php } ?> +</dl> +<?php +if (isset($this->data['LogoutURL'])) { +?> +<p><a href="<?php echo htmlspecialchars($this->data['LogoutURL']); ?>"><?php echo $this->t('{status:logout}'); ?></a></p> +<?php +} +?> +<?php +$this->includeAtTemplateBase('includes/footer.php'); diff --git a/modules/core/www/cardinality_error.php b/modules/core/www/cardinality_error.php new file mode 100644 index 0000000000000000000000000000000000000000..4b36495fb859baa797ce67b11eb3660aca336b25 --- /dev/null +++ b/modules/core/www/cardinality_error.php @@ -0,0 +1,25 @@ +<?php +/** + * Show a 403 Forbidden page when an attribute violates a cardinality rule + * + * @package SimpleSAMLphp + */ + +if (!array_key_exists('StateId', $_REQUEST)) { + throw new \SimpleSAML_Error_BadRequest('Missing required StateId query parameter.'); +} +$id = $_REQUEST['StateId']; +$state = \SimpleSAML_Auth_State::loadState($id, 'core:cardinality'); +$session = \SimpleSAML_Session::getSessionFromRequest(); + +\SimpleSAML\Logger::stats('core:cardinality:error '.$state['Destination']['entityid'].' '.$state['saml:sp:IdP']. + ' '.implode(',', array_keys($state['core:cardinality:errorAttributes']))); + +$globalConfig = SimpleSAML_Configuration::getInstance(); +$t = new \SimpleSAML_XHTML_Template($globalConfig, 'core:cardinality_error.tpl.php'); +$t->data['cardinalityErrorAttributes'] = $state['core:cardinality:errorAttributes']; +if (isset($state['Source']['auth'])) { + $t->data['LogoutURL'] = \SimpleSAML\Module::getModuleURL('core/authenticate.php', array('as' => $state['Source']['auth']))."&logout"; +} +header('HTTP/1.0 403 Forbidden'); +$t->show(); diff --git a/modules/core/www/frontpage_config.php b/modules/core/www/frontpage_config.php index e1eef9df53d0a85709b7d47a76518e8b41925114..805b23bb43089fa9de41105aca74561800807b35 100644 --- a/modules/core/www/frontpage_config.php +++ b/modules/core/www/frontpage_config.php @@ -1,7 +1,5 @@ <?php - - // Load SimpleSAMLphp, configuration $config = SimpleSAML_Configuration::getInstance(); $session = SimpleSAML_Session::getSessionFromRequest(); @@ -17,48 +15,42 @@ $isadmin = SimpleSAML\Utils\Auth::isAdmin(); $warnings = array(); if (!\SimpleSAML\Utils\HTTP::isHTTPS()) { - $warnings[] = '{core:frontpage:warnings_https}'; + $warnings[] = '{core:frontpage:warnings_https}'; } if ($config->getValue('secretsalt') === 'defaultsecretsalt') { - $warnings[] = '{core:frontpage:warnings_secretsalt}'; + $warnings[] = '{core:frontpage:warnings_secretsalt}'; } if (extension_loaded('suhosin')) { - $suhosinLength = ini_get('suhosin.get.max_value_length'); - if (empty($suhosinLength) || (int)$suhosinLength < 2048) { - $warnings[] = '{core:frontpage:warnings_suhosin_url_length}'; - } + $suhosinLength = ini_get('suhosin.get.max_value_length'); + if (empty($suhosinLength) || (int)$suhosinLength < 2048) { + $warnings[] = '{core:frontpage:warnings_suhosin_url_length}'; + } } - - - - $links = array(); $links_welcome = array(); $links_config = array(); $links_auth = array(); $links_federation = array(); - - $links_config[] = array( - 'href' => \SimpleSAML\Utils\HTTP::getBaseURL() . 'admin/hostnames.php', - 'text' => '{core:frontpage:link_diagnostics}' + 'href' => \SimpleSAML\Utils\HTTP::getBaseURL() . 'admin/hostnames.php', + 'text' => '{core:frontpage:link_diagnostics}' ); $links_config[] = array( - 'href' => \SimpleSAML\Utils\HTTP::getBaseURL() . 'admin/phpinfo.php', - 'text' => '{core:frontpage:link_phpinfo}' + 'href' => \SimpleSAML\Utils\HTTP::getBaseURL() . 'admin/phpinfo.php', + 'text' => '{core:frontpage:link_phpinfo}' ); $allLinks = array( - 'links' => &$links, - 'welcome' => &$links_welcome, - 'config' => &$links_config, - 'auth' => &$links_auth, - 'federation' => &$links_federation, + 'links' => &$links, + 'welcome' => &$links_welcome, + 'config' => &$links_config, + 'auth' => &$links_auth, + 'federation' => &$links_federation, ); SimpleSAML\Module::callHooks('frontpage', $allLinks); @@ -66,68 +58,69 @@ SimpleSAML\Module::callHooks('frontpage', $allLinks); // don't need to fetch it on every access to this page. $current = $config->getVersion(); if ($config->getBoolean('admin.checkforupdates', true) && $current !== 'master') { - $latest = $session->getData("core:latest_simplesamlphp_version", "version"); - - if (!$latest) { - $api_url = 'https://api.github.com/repos/simplesamlphp/simplesamlphp/releases'; - $ch = curl_init($api_url.'/latest'); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_USERAGENT, 'SimpleSAMLphp'); - curl_setopt($ch, CURLOPT_TIMEOUT, 2); + $latest = $session->getData("core:latest_simplesamlphp_version", "version"); + + if (!$latest) { + $api_url = 'https://api.github.com/repos/simplesamlphp/simplesamlphp/releases'; + $ch = curl_init($api_url.'/latest'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_USERAGENT, 'SimpleSAMLphp'); + curl_setopt($ch, CURLOPT_TIMEOUT, 2); curl_setopt($ch, CURLOPT_PROXY, $config->getString('proxy', null)); curl_setopt($ch, CURLOPT_PROXYUSERPWD, $config->getstring('proxy.auth', null)); - $response = curl_exec($ch); - - if (curl_getinfo($ch, CURLINFO_HTTP_CODE) === 200) { - $latest = json_decode($response, true); - $session->setData("core:latest_simplesamlphp_version", "version", $latest); - } - curl_close($ch); - } - - if ($latest && version_compare($current, ltrim($latest['tag_name'], 'v'), 'lt')) { - $outdated = true; - $warnings[] = array( - '{core:frontpage:warnings_outdated}', - array('%LATEST_URL%' => $latest['html_url']) - ); - } + $response = curl_exec($ch); + + if (curl_getinfo($ch, CURLINFO_HTTP_CODE) === 200) { + $latest = json_decode($response, true); + $session->setData("core:latest_simplesamlphp_version", "version", $latest); + } + curl_close($ch); + } + + if ($latest && version_compare($current, ltrim($latest['tag_name'], 'v'), 'lt')) { + $outdated = true; + $warnings[] = array( + '{core:frontpage:warnings_outdated}', + array('%LATEST_URL%' => $latest['html_url']) + ); + } } $enablematrix = array( - 'saml20-idp' => $config->getBoolean('enable.saml20-idp', false), - 'shib13-idp' => $config->getBoolean('enable.shib13-idp', false), + 'saml20-idp' => $config->getBoolean('enable.saml20-idp', false), + 'shib13-idp' => $config->getBoolean('enable.shib13-idp', false), ); $functionchecks = array( - 'time' => array('required', 'Date/Time Extension'), - 'hash' => array('required', 'Hashing function'), - 'gzinflate' => array('required', 'ZLib'), - 'openssl_sign' => array('required', 'OpenSSL'), - 'dom_import_simplexml' => array('required', 'XML DOM'), - 'preg_match' => array('required', 'RegEx support'), - 'json_decode' => array('required', 'JSON support'), - 'class_implements' => array('required', 'Standard PHP Library (SPL)'), - 'mb_strlen' => array('required', 'Multibyte String Extension'), - 'curl_init' => array('optional', 'cURL (required if automatic version checks are used, also by some modules.'), - 'session_start' => array('optional', 'Session Extension (required if PHP sessions are used)'), - 'pdo_drivers' => array('optional', 'PDO Extension (required if a database backend is used)'), + 'time' => array('required', 'Date/Time Extension'), + 'hash' => array('required', 'Hashing function'), + 'gzinflate' => array('required', 'ZLib'), + 'openssl_sign' => array('required', 'OpenSSL'), + 'dom_import_simplexml' => array('required', 'XML DOM'), + 'preg_match' => array('required', 'RegEx support'), + 'json_decode' => array('required', 'JSON support'), + 'class_implements' => array('required', 'Standard PHP Library (SPL)'), + 'mb_strlen' => array('required', 'Multibyte String Extension'), + 'curl_init' => array('optional', 'cURL (required if automatic version checks are used, also by some modules.'), + 'session_start' => array('optional', 'Session Extension (required if PHP sessions are used)'), + 'pdo_drivers' => array('optional', 'PDO Extension (required if a database backend is used)'), ); if (SimpleSAML\Module::isModuleEnabled('ldap')) { - $functionchecks['ldap_bind'] = array('optional', 'LDAP Extension (required if an LDAP backend is used)'); + $functionchecks['ldap_bind'] = array('optional', 'LDAP Extension (required if an LDAP backend is used)'); } if (SimpleSAML\Module::isModuleEnabled('radius')) { - $functionchecks['radius_auth_open'] = array('optional', 'Radius Extension (required if a Radius backend is used)'); + $functionchecks['radius_auth_open'] = array('optional', 'Radius Extension (required if a Radius backend is used)'); } $funcmatrix = array(); $funcmatrix[] = array( - 'required' => 'required', - 'descr' => 'PHP Version >= 5.4. You run: ' . phpversion(), - 'enabled' => version_compare(phpversion(), '5.4', '>=')); -foreach ($functionchecks AS $func => $descr) { - $funcmatrix[] = array('descr' => $descr[1], 'required' => $descr[0], 'enabled' => function_exists($func)); + 'required' => 'required', + 'descr' => 'PHP Version >= 5.4. You run: ' . phpversion(), + 'enabled' => version_compare(phpversion(), '5.4', '>=') +); +foreach ($functionchecks as $func => $descr) { + $funcmatrix[] = array('descr' => $descr[1], 'required' => $descr[0], 'enabled' => function_exists($func)); } $funcmatrix[] = array( @@ -144,25 +137,25 @@ $funcmatrix[] = array( /* Some basic configuration checks */ -if($config->getString('technicalcontact_email', 'na@example.org') === 'na@example.org') { - $mail_ok = FALSE; +if ($config->getString('technicalcontact_email', 'na@example.org') === 'na@example.org') { + $mail_ok = false; } else { - $mail_ok = TRUE; + $mail_ok = true; } $funcmatrix[] = array( - 'required' => 'recommended', - 'descr' => 'technicalcontact_email option set', - 'enabled' => $mail_ok - ); -if($config->getString('auth.adminpassword', '123') === '123') { - $password_ok = FALSE; + 'required' => 'recommended', + 'descr' => 'technicalcontact_email option set', + 'enabled' => $mail_ok +); +if ($config->getString('auth.adminpassword', '123') === '123') { + $password_ok = false; } else { - $password_ok = TRUE; + $password_ok = true; } $funcmatrix[] = array( - 'required' => 'required', - 'descr' => 'auth.adminpassword option set', - 'enabled' => $password_ok + 'required' => 'required', + 'descr' => 'auth.adminpassword option set', + 'enabled' => $password_ok ); $t = new SimpleSAML_XHTML_Template($config, 'core:frontpage_config.tpl.php'); @@ -179,7 +172,6 @@ $t->data['links_auth'] = $links_auth; $t->data['links_federation'] = $links_federation; - $t->data['enablematrix'] = $enablematrix; $t->data['funcmatrix'] = $funcmatrix; $t->data['requiredmap'] = array( @@ -191,5 +183,3 @@ $t->data['version'] = $config->getVersion(); $t->data['directory'] = dirname(dirname(dirname(dirname(__FILE__)))); $t->show(); - - diff --git a/modules/ldap/docs/ldap.md b/modules/ldap/docs/ldap.md index fae1ca1c6ae30d51f946d48a3a8ba4088b38c510..1bad69582b69d30162c4b1b1a7aff9bba7a74919 100644 --- a/modules/ldap/docs/ldap.md +++ b/modules/ldap/docs/ldap.md @@ -63,6 +63,12 @@ authentication source: */ 'search.base' => 'ou=people,dc=example,dc=org', + /* + * The scope of the search. Valid values are 'subtree' and 'onelevel' and 'base', + * first one being the default if no value is set. + */ + 'search.scope' => 'subtree', + /* * The attribute(s) the username should match against. * @@ -94,7 +100,7 @@ You also need to update the `hostname` and `dnpattern` options. The `hostname` should be the hostname of your LDAP server, and the `dnpattern` should be a pattern which can be used to generate the `dn` of a user with a given username. - +- All other options have default values, and are not required. ### Searching for a user ### diff --git a/modules/ldap/lib/Auth/Process/AttributeAddUsersGroups.php b/modules/ldap/lib/Auth/Process/AttributeAddUsersGroups.php index c263db0e66386f1ab3bf3585d5a47331f23d5fce..8fa7c2ccf2a9b9661434ba9917942f11513ecde7 100644 --- a/modules/ldap/lib/Auth/Process/AttributeAddUsersGroups.php +++ b/modules/ldap/lib/Auth/Process/AttributeAddUsersGroups.php @@ -235,6 +235,14 @@ class sspmod_ldap_Auth_Process_AttributeAddUsersGroups extends sspmod_ldap_Auth_ ' Group Type: ' . $this->type_map['group'] ); + // Work out what attributes to get for a group + $use_group_name = FALSE; + $get_attributes = array($map['memberof'], $map['type']); + if (isset($map['name']) && $map['name']) { + $get_attributes[] = $map['name']; + $use_group_name = TRUE; + } + // Check each DN of the passed memberOf foreach ($memberof as $dn) { @@ -249,7 +257,7 @@ class sspmod_ldap_Auth_Process_AttributeAddUsersGroups extends sspmod_ldap_Auth_ // Query LDAP for the attribute values for the DN try { - $attributes = $this->getLdap()->getAttributes($dn, array($map['memberof'], $map['type'])); + $attributes = $this->getLdap()->getAttributes($dn, $get_attributes); } catch (SimpleSAML_Error_AuthSource $e) { continue; // DN must not exist, just continue. Logged by the LDAP object } @@ -260,10 +268,16 @@ class sspmod_ldap_Auth_Process_AttributeAddUsersGroups extends sspmod_ldap_Auth_ } // Add to found groups array - $groups[] = $dn; + if ($use_group_name && isset($attributes[$map['name']]) && is_array($attributes[$map['name']])) { + $groups[] = $attributes[$map['name']][0]; + } else { + $groups[] = $dn; + } // Recursively search "sub" groups - $groups = array_merge($groups, $this->search($attributes[$map['memberof']])); + if (!empty($attributes[$map['memberof']])) { + $groups = array_merge($groups, $this->search($attributes[$map['memberof']])); + } } // Return only the unique group names diff --git a/modules/ldap/lib/Auth/Process/BaseFilter.php b/modules/ldap/lib/Auth/Process/BaseFilter.php index c1da79255aebfd83d0e58a0b047b9013839902e1..d7116a2d835ec3b6c101b2f4d882123631809093 100644 --- a/modules/ldap/lib/Auth/Process/BaseFilter.php +++ b/modules/ldap/lib/Auth/Process/BaseFilter.php @@ -99,11 +99,11 @@ abstract class sspmod_ldap_Auth_Process_BaseFilter extends SimpleSAML_Auth_Proce // This way if the class is extended the proper name is used $classname = get_class($this); $classname = explode('_', $classname); - $this->title = 'ldap:' . end($classname) . ' : '; + $this->title = 'ldap:'.end($classname).' : '; // Log the construction SimpleSAML\Logger::debug( - $this->title . 'Creating and configuring the filter.' + $this->title.'Creating and configuring the filter.' ); // If an authsource was defined (an not empty string)... @@ -111,8 +111,8 @@ abstract class sspmod_ldap_Auth_Process_BaseFilter extends SimpleSAML_Auth_Proce // Log the authsource request SimpleSAML\Logger::debug( - $this->title . 'Attempting to get configuration values from authsource [' . - $config['authsource'] . ']' + $this->title.'Attempting to get configuration values from authsource ['. + $config['authsource'].']' ); // Get the authsources file, which should contain the config @@ -121,7 +121,7 @@ abstract class sspmod_ldap_Auth_Process_BaseFilter extends SimpleSAML_Auth_Proce // Verify that the authsource config exists if (!$authsource->hasValue($config['authsource'])) { throw new SimpleSAML_Error_Exception( - $this->title . 'Authsource [' . $config['authsource'] . + $this->title.'Authsource ['.$config['authsource']. '] defined in filter parameters not found in authsources.php' ); } @@ -134,7 +134,7 @@ abstract class sspmod_ldap_Auth_Process_BaseFilter extends SimpleSAML_Auth_Proce // TODO: Support ldap:LDAPMulti, if possible if (@$authsource[0] != 'ldap:LDAP') { throw new SimpleSAML_Error_Exception( - $this->title . 'Authsource [' . $config['authsource'] . + $this->title.'Authsource ['.$config['authsource']. '] specified in filter parameters is not an ldap:LDAP type' ); } @@ -162,7 +162,10 @@ abstract class sspmod_ldap_Auth_Process_BaseFilter extends SimpleSAML_Auth_Proce // only set when search.enabled = true if (isset($authsource['search.enable']) && $authsource['search.enable']) { if (isset($authsource['search.base'])) { - $authconfig['ldap.basedn'] = $authsource['search.base']; + $authconfig['ldap.basedn'] = $authsource['search.base']; + } + if (isset($authsource['search.scope'])) { + $authconfig['ldap.scope'] = $authsource['search.scope']; } if (isset($authsource['search.username'])) { $authconfig['ldap.username'] = $authsource['search.username']; @@ -192,8 +195,8 @@ abstract class sspmod_ldap_Auth_Process_BaseFilter extends SimpleSAML_Auth_Proce // Authsource complete SimpleSAML\Logger::debug( - $this->title . 'Retrieved authsource [' . $config['authsource'] . - '] configuration values: ' . $this->var_export($authconfig) + $this->title.'Retrieved authsource ['.$config['authsource']. + '] configuration values: '.$this->var_export($authconfig) ); } @@ -213,9 +216,9 @@ abstract class sspmod_ldap_Auth_Process_BaseFilter extends SimpleSAML_Auth_Proce // Log the member values retrieved above SimpleSAML\Logger::debug( - $this->title . 'Configuration values retrieved;' . - ' BaseDN: ' . $this->var_export($this->base_dn) . - ' Product: ' . $this->var_export($this->product) + $this->title.'Configuration values retrieved;'. + ' BaseDN: '.$this->var_export($this->base_dn). + ' Product: '.$this->var_export($this->product) ); // Setup the attribute map which will be used to search LDAP @@ -231,7 +234,7 @@ abstract class sspmod_ldap_Auth_Process_BaseFilter extends SimpleSAML_Auth_Proce // Log the attribute map SimpleSAML\Logger::debug( - $this->title . 'Attribute map created: ' . $this->var_export($this->attribute_map) + $this->title.'Attribute map created: '.$this->var_export($this->attribute_map) ); // Setup the object type map which is used to determine a DNs' type @@ -242,7 +245,7 @@ abstract class sspmod_ldap_Auth_Process_BaseFilter extends SimpleSAML_Auth_Proce // Log the type map SimpleSAML\Logger::debug( - $this->title . 'Type map created: ' . $this->var_export($this->type_map) + $this->title.'Type map created: '.$this->var_export($this->type_map) ); } @@ -272,15 +275,15 @@ abstract class sspmod_ldap_Auth_Process_BaseFilter extends SimpleSAML_Auth_Proce // Log the LDAP connection SimpleSAML\Logger::debug( - $this->title . 'Connecting to LDAP server;' . - ' Hostname: ' . $hostname . - ' Port: ' . $port . - ' Enable TLS: ' . ($enable_tls ? 'Yes' : 'No') . - ' Debug: ' . ($debug ? 'Yes' : 'No') . - ' Referrals: ' . ($referrals ? 'Yes' : 'No') . - ' Timeout: ' . $timeout . - ' Username: ' . $username . - ' Password: ' . (empty($password) ? '' : '********') + $this->title.'Connecting to LDAP server;'. + ' Hostname: '.$hostname. + ' Port: '.$port. + ' Enable TLS: '.($enable_tls ? 'Yes' : 'No'). + ' Debug: '.($debug ? 'Yes' : 'No'). + ' Referrals: '.($referrals ? 'Yes' : 'No'). + ' Timeout: '.$timeout. + ' Username: '.$username. + ' Password: '.(empty($password) ? '' : '********') ); // Connect to the LDAP server to be queried during processing diff --git a/modules/ldap/lib/ConfigHelper.php b/modules/ldap/lib/ConfigHelper.php index d76684bb8026d7721891f8409d527dbbff53595e..c02adf8b02444c15018475f0a26ba1b9e498a380 100644 --- a/modules/ldap/lib/ConfigHelper.php +++ b/modules/ldap/lib/ConfigHelper.php @@ -56,30 +56,31 @@ class sspmod_ldap_ConfigHelper */ private $referrals; - /** * Whether we need to search for the users DN. */ private $searchEnable; - /** * The username we should bind with before we can search for the user. */ private $searchUsername; - /** * The password we should bind with before we can search for the user. */ private $searchPassword; - /** * Array with the base DN(s) for the search. */ private $searchBase; + /** + * The scope of the search. + */ + private $searchScope; + /** * Additional LDAP filter fields for the search */ @@ -90,31 +91,26 @@ class sspmod_ldap_ConfigHelper */ private $searchAttributes; - /** * The DN pattern we should use to create the DN from the username. */ private $dnPattern; - /** * The attributes we should fetch. Can be NULL in which case we will fetch all attributes. */ private $attributes; - /** * The user cannot get all attributes, privileged reader required */ private $privRead; - /** * The DN we should bind with before we can get the attributes. */ private $privUsername; - /** * The password we should bind with before we can get the attributes. */ @@ -153,6 +149,7 @@ class sspmod_ldap_ConfigHelper } $this->searchBase = $config->getArrayizeString('search.base'); + $this->searchScope = $config->getString('search.scope', 'subtree'); $this->searchFilter = $config->getString('search.filter', null); $this->searchAttributes = $config->getArray('search.attributes'); @@ -187,7 +184,7 @@ class sspmod_ldap_ConfigHelper assert(is_string($password)); if (empty($password)) { - SimpleSAML\Logger::info($this->location . ': Login with empty password disallowed.'); + SimpleSAML\Logger::info($this->location.': Login with empty password disallowed.'); throw new SimpleSAML_Error_Error('WRONGUSERPASS'); } @@ -203,16 +200,16 @@ class sspmod_ldap_ConfigHelper } } - $dn = $ldap->searchfordn($this->searchBase, $this->searchAttributes, $username, true, $this->searchFilter); + $dn = $ldap->searchfordn($this->searchBase, $this->searchAttributes, $username, true, $this->searchFilter, $this->searchScope); if ($dn === null) { /* User not found with search. */ - SimpleSAML\Logger::info($this->location . ': Unable to find users DN. username=\'' . $username . '\''); + SimpleSAML\Logger::info($this->location.': Unable to find users DN. username=\''.$username.'\''); throw new SimpleSAML_Error_Error('WRONGUSERPASS'); } } if (!$ldap->bind($dn, $password, $sasl_args)) { - SimpleSAML\Logger::info($this->location . ': '. $username . ' failed to authenticate. DN=' . $dn); + SimpleSAML\Logger::info($this->location.': '.$username.' failed to authenticate. DN='.$dn); throw new SimpleSAML_Error_Error('WRONGUSERPASS'); } @@ -275,7 +272,7 @@ class sspmod_ldap_ConfigHelper } return $ldap->searchfordn($this->searchBase, $attribute, - $value, $allowZeroHits, $this->searchFilter); + $value, $allowZeroHits, $this->searchFilter, $this->searchScope); } public function getAttributes($dn, $attributes = null) diff --git a/modules/memcacheMonitor/dictionaries/memcachestat.definition.json b/modules/memcacheMonitor/dictionaries/memcachestat.definition.json index be9187165db19f388606c385016864c26a606b3c..885b475085a28683868385d48391bb66e755f4f3 100644 --- a/modules/memcacheMonitor/dictionaries/memcachestat.definition.json +++ b/modules/memcacheMonitor/dictionaries/memcachestat.definition.json @@ -41,9 +41,9 @@ "cmd_set": { "en": "Total SET commands" }, - "cmd_flush": { - "en": "Total FLUSH commands" - }, + "cmd_flush": { + "en": "Total FLUSH commands" + }, "get_hits": { "en": "Total GET commands (success)" }, @@ -60,54 +60,87 @@ "en": "Total storage avail" }, "pointer_size": { - "en": "Pointer size (bits)" + "en": "Pointer size (bits)" }, "delete_misses": { - "en": "Total DELETE commands (failed)" + "en": "Total DELETE commands (failed)" }, "delete_hits": { - "en": "Total DELETE commands (success)" + "en": "Total DELETE commands (success)" }, "incr_misses": { - "en": "Total INCR commands (failed)" + "en": "Total INCR commands (failed)" }, "incr_hits": { - "en": "Total INCR commands (success)" + "en": "Total INCR commands (success)" }, "decr_misses": { - "en": "Total DECR commands (failed)" + "en": "Total DECR commands (failed)" }, "decr_hits": { - "en": "Total DECR commands (success)" - }, + "en": "Total DECR commands (success)" + }, "cas_misses": { - "en": "Total CAS commands (failed)" + "en": "Total CAS commands (failed)" }, "cas_hits": { - "en": "Total CAS commands (success)" + "en": "Total CAS commands (success)" }, "cas_badval": { - "en": "Total bad CAS identifiers" + "en": "Total bad CAS identifiers" }, "auth_cmds": { - "en": "Total authentication commands processed" + "en": "Total authentication commands processed" }, "auth_errors": { - "en": "Total authentication commands failed" + "en": "Total authentication commands failed" }, "accepting_conns": { - "en": "Currently accepting new connections" + "en": "Currently accepting new connections" }, "listen_disabled_num": { - "en": "Total number of denied connections (connection limit)" + "en": "Total number of denied connections (connection limit)" }, "threads": { - "en": "Number of available threads" + "en": "Number of available threads" }, "conn_yields": { - "en": "Number of times the request limit was reached" + "en": "Number of times the request limit was reached" }, "evictions": { - "en": "Number of objects removed from cache (memory limit)" + "en": "Number of objects removed from cache (memory limit)" + }, + "libevent": { + "en": "Libevent version" + }, + "reserved_fds": { + "en": "Number of misc fds used internally" + }, + "cmd_touch": { + "en": "Cumulative number of touch reqs" + }, + "touch_hits": { + "en": "Number of keys that have been touched with a new expiration time" + }, + "touch_misses": { + "en": "Number of items that have been touched and not found" + }, + "hash_power_level": { + "en": "Current size multiplier for hash table" + }, + "hash_bytes": { + "en": "Bytes currently used by hash tables" + }, + "hash_is_expanding": { + "en": "Indicates if the hash table is being grown to a new size" + }, + "expired_unfetched": { + "en": "Items pulled from LRU that were never touched before expiring" + }, + "evicted_unfetched": { + "en": "Items pulled from LRU that were never touched" + }, + "reclaimed": { + "en": "Number of times an entry was stored using memory from an expired entry" } } diff --git a/modules/memcacheMonitor/locales/en/LC_MESSAGES/memcacheMonitor.po b/modules/memcacheMonitor/locales/en/LC_MESSAGES/memcacheMonitor.po index a71c1988257e3acad4925ffbcb6269ab81d05144..bab82dd3f380481d57cb1d0c8356a57d9fef53e9 100644 --- a/modules/memcacheMonitor/locales/en/LC_MESSAGES/memcacheMonitor.po +++ b/modules/memcacheMonitor/locales/en/LC_MESSAGES/memcacheMonitor.po @@ -15,6 +15,39 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.3.4\n" +msgid "{memcacheMonitor:memcachestat:libevent}" +msgstr "Libevent version" + +msgid "{memcacheMonitor:memcachestat:reserved_fds}" +msgstr "Number of misc fds used internally" + +msgid "{memcacheMonitor:memcachestat:cmd_touch}" +msgstr "Cumulative number of touch reqs" + +msgid "{memcacheMonitor:memcachestat:touch_hits}" +msgstr "Number of keys that have been touched with a new expiration time" + +msgid "{memcacheMonitor:memcachestat:touch_misses}" +msgstr "Number of items that have been touched and not found" + +msgid "{memcacheMonitor:memcachestat:hash_power_level}" +msgstr "Current size multiplier for hash table" + +msgid "{memcacheMonitor:memcachestat:hash_bytes}" +msgstr "Bytes currently used by hash tables" + +msgid "{memcacheMonitor:memcachestat:hash_is_expanding}" +msgstr "Indicates if the hash table is being grown to a new size" + +msgid "{memcacheMonitor:memcachestat:expired_unfetched}" +msgstr "Items pulled from LRU that were never touched before expiring" + +msgid "{memcacheMonitor:memcachestat:evicted_unfetched}" +msgstr "Items pulled from LRU that were never touched" + +msgid "{memcacheMonitor:memcachestat:reclaimed}" +msgstr "Number of times an entry was stored using memory from an expired entry" + msgid "{memcacheMonitor:memcachestat:delete_misses}" msgstr "Total DELETE commands (failed)" diff --git a/modules/memcacheMonitor/www/memcachestat.php b/modules/memcacheMonitor/www/memcachestat.php index 3d8fde43973780f64d71f0449b4dbe0e566b5647..1410b8b9e5e8ee5a9cb52c493cb7b7c9324e2e7c 100644 --- a/modules/memcacheMonitor/www/memcachestat.php +++ b/modules/memcacheMonitor/www/memcachestat.php @@ -103,42 +103,54 @@ foreach($stats AS $key => &$entry) { $t = new SimpleSAML_XHTML_Template($config, 'memcacheMonitor:memcachestat.tpl.php'); $rowTitles = array( - 'accepting_conns' => $t->noop('{memcacheMonitor:memcachestat:accepting_conns}'), - 'auth_cmds' => $t->noop('{memcacheMonitor:memcachestat:auth_cmds}'), - 'auth_errors' => $t->noop('{memcacheMonitor:memcachestat:auth_errors}'), - 'bytes' => $t->noop('{memcacheMonitor:memcachestat:bytes}'), - 'bytes_read' => $t->noop('{memcacheMonitor:memcachestat:bytes_read}'), - 'bytes_written' => $t->noop('{memcacheMonitor:memcachestat:bytes_written}'), - 'cas_badval' => $t->noop('{memcacheMonitor:memcachestat:cas_badval}'), - 'cas_hits' => $t->noop('{memcacheMonitor:memcachestat:cas_hits}'), - 'cas_misses' => $t->noop('{memcacheMonitor:memcachestat:cas_misses}'), - 'cmd_get' => $t->noop('{memcacheMonitor:memcachestat:cmd_get}'), - 'cmd_set' => $t->noop('{memcacheMonitor:memcachestat:cmd_set}'), - 'connection_structures' => $t->noop('{memcacheMonitor:memcachestat:connection_structures}'), - 'conn_yields' => $t->noop('{memcacheMonitor:memcachestat:conn_yields}'), - 'curr_connections' => $t->noop('{memcacheMonitor:memcachestat:curr_connections}'), - 'curr_items' => $t->noop('{memcacheMonitor:memcachestat:curr_items}'), - 'decr_hits' => $t->noop('{memcacheMonitor:memcachestat:decr_hits}'), - 'decr_misses' => $t->noop('{memcacheMonitor:memcachestat:decr_misses}'), - 'delete_hits' => $t->noop('{memcacheMonitor:memcachestat:delete_hits}'), - 'delete_misses' => $t->noop('{memcacheMonitor:memcachestat:delete_misses}'), - 'evictions' => $t->noop('{memcacheMonitor:memcachestat:evictions}'), - 'get_hits' => $t->noop('{memcacheMonitor:memcachestat:get_hits}'), - 'get_misses' => $t->noop('{memcacheMonitor:memcachestat:get_misses}'), - 'incr_hits' => $t->noop('{memcacheMonitor:memcachestat:incr_hits}'), - 'incr_misses' => $t->noop('{memcacheMonitor:memcachestat:incr_misses}'), - 'limit_maxbytes' => $t->noop('{memcacheMonitor:memcachestat:limit_maxbytes}'), - 'listen_disabled_num' => $t->noop('{memcacheMonitor:memcachestat:listen_disabled_num}'), - 'pid' => $t->noop('{memcacheMonitor:memcachestat:pid}'), - 'pointer_size' => $t->noop('{memcacheMonitor:memcachestat:pointer_size}'), - 'rusage_system' => $t->noop('{memcacheMonitor:memcachestat:rusage_system}'), - 'rusage_user' => $t->noop('{memcacheMonitor:memcachestat:rusage_user}'), - 'threads' => $t->noop('{memcacheMonitor:memcachestat:threads}'), - 'time' => $t->noop('{memcacheMonitor:memcachestat:time}'), - 'total_connections' => $t->noop('{memcacheMonitor:memcachestat:total_connections}'), - 'total_items' => $t->noop('{memcacheMonitor:memcachestat:total_items}'), - 'uptime' => $t->noop('{memcacheMonitor:memcachestat:uptime}'), - 'version' => $t->noop('{memcacheMonitor:memcachestat:version}'), + 'accepting_conns' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:accepting_conns}'), + 'auth_cmds' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:auth_cmds}'), + 'auth_errors' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:auth_errors}'), + 'bytes' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:bytes}'), + 'bytes_read' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:bytes_read}'), + 'bytes_written' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:bytes_written}'), + 'cas_badval' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:cas_badval}'), + 'cas_hits' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:cas_hits}'), + 'cas_misses' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:cas_misses}'), + 'cmd_flush' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:cmd_flush}'), + 'cmd_get' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:cmd_get}'), + 'cmd_set' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:cmd_set}'), + 'cmd_touch' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:cmd_touch}'), + 'connection_structures' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:connection_structures}'), + 'conn_yields' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:conn_yields}'), + 'curr_connections' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:curr_connections}'), + 'curr_items' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:curr_items}'), + 'decr_hits' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:decr_hits}'), + 'decr_misses' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:decr_misses}'), + 'delete_hits' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:delete_hits}'), + 'delete_misses' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:delete_misses}'), + 'expired_unfetched' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:expired_unfetched}'), + 'evicted_unfetched' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:evicted_unfetched}'), + 'evictions' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:evictions}'), + 'get_hits' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:get_hits}'), + 'get_misses' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:get_misses}'), + 'hash_bytes' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:hash_bytes}'), + 'hash_is_expanding' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:hash_is_expanding}'), + 'hash_power_level' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:hash_power_level}'), + 'incr_hits' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:incr_hits}'), + 'incr_misses' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:incr_misses}'), + 'libevent' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:libevent}'), + 'limit_maxbytes' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:limit_maxbytes}'), + 'listen_disabled_num' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:listen_disabled_num}'), + 'pid' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:pid}'), + 'pointer_size' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:pointer_size}'), + 'reclaimed' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:reclaimed}'), + 'reserved_fds' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:reserved_fds}'), + 'rusage_system' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:rusage_system}'), + 'rusage_user' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:rusage_user}'), + 'threads' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:threads}'), + 'time' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:time}'), + 'total_connections' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:total_connections}'), + 'total_items' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:total_items}'), + 'touch_hits' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:touch_hits}'), + 'touch_misses' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:touch_misses}'), + 'uptime' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:uptime}'), + 'version' => \SimpleSAML\Locale\Translate::noop('{memcacheMonitor:memcachestat:version}'), ); $t->data['title'] = 'Memcache stats'; $t->data['rowtitles'] = $rowTitles; diff --git a/modules/metarefresh/dictionaries/metarefresh.definition.json b/modules/metarefresh/dictionaries/metarefresh.definition.json new file mode 100644 index 0000000000000000000000000000000000000000..8bfb51c4af39570be0a83c43531693df4caa64c1 --- /dev/null +++ b/modules/metarefresh/dictionaries/metarefresh.definition.json @@ -0,0 +1,12 @@ +{ + "frontpage_link": { + "en": "Metarefresh: fetch metadata" + }, + "metarefresh_header": { + "en": "Metarefresh fetch" + }, + "no_output": { + "en": "No output from metarefresh." + } +} + diff --git a/modules/metarefresh/dictionaries/metarefresh.translation.json b/modules/metarefresh/dictionaries/metarefresh.translation.json new file mode 100644 index 0000000000000000000000000000000000000000..2023ecba4f39e60385911c6708ac187bf2db6c8e --- /dev/null +++ b/modules/metarefresh/dictionaries/metarefresh.translation.json @@ -0,0 +1,9 @@ +{ + "frontpage_link": { + }, + "metarefresh_header": { + }, + "no_output": { + } +} + diff --git a/modules/metarefresh/hooks/hook_frontpage.php b/modules/metarefresh/hooks/hook_frontpage.php index 60e7aef5e002c1b218ec9c5c5c336d57e7ed1d2f..0c5c2b17529f9e5a10e9e85d9035003f49a2d52f 100644 --- a/modules/metarefresh/hooks/hook_frontpage.php +++ b/modules/metarefresh/hooks/hook_frontpage.php @@ -10,7 +10,7 @@ function metarefresh_hook_frontpage(&$links) { $links['federation'][] = array( 'href' => SimpleSAML\Module::getModuleURL('metarefresh/fetch.php'), - 'text' => array('en' => 'Metarefresh: fetch metadata'), + 'text' => '{metarefresh:metarefresh:frontpage_link}', ); } diff --git a/modules/metarefresh/lib/MetaLoader.php b/modules/metarefresh/lib/MetaLoader.php index 800d6f20301a8c8256651ca8c42de4f2cc696c1e..5554c017f40a4f30fdef5919cb99c889f2b1a053 100644 --- a/modules/metarefresh/lib/MetaLoader.php +++ b/modules/metarefresh/lib/MetaLoader.php @@ -1,482 +1,498 @@ <?php -/* - * @author Andreas Åkre Solberg <andreas.solberg@uninett.no> +/** * @package SimpleSAMLphp + * @author Andreas Åkre Solberg <andreas.solberg@uninett.no> */ class sspmod_metarefresh_MetaLoader { - private $expire; - private $metadata; - private $oldMetadataSrc; - private $stateFile; - private $changed; + private $expire; + private $metadata; + private $oldMetadataSrc; + private $stateFile; + private $changed; private $state; - private $types = array( - 'saml20-idp-remote', - 'saml20-sp-remote', - 'shib13-idp-remote', - 'shib13-sp-remote', - 'attributeauthority-remote' - ); - - - /** - * Constructor - * - * @param - */ - public function __construct($expire = null, $stateFile = null, $oldMetadataSrc = null) + private $types = array( + 'saml20-idp-remote', + 'saml20-sp-remote', + 'shib13-idp-remote', + 'shib13-sp-remote', + 'attributeauthority-remote' + ); + + + /** + * Constructor + * + * @param integer $expire + * @param string $stateFile + * @param object $oldMetadataSrc + */ + public function __construct($expire = null, $stateFile = null, $oldMetadataSrc = null) { - $this->expire = $expire; - $this->metadata = array(); - $this->oldMetadataSrc = $oldMetadataSrc; - $this->stateFile = $stateFile; - $this->changed = false; - - // Read file containing $state from disk - if(is_readable($stateFile)) { - require($stateFile); - } - - $this->state = array(); - - } - - - /** - * Get the types of entities that will be loaded. - * - * @return array The entity types allowed. - */ - public function getTypes() - { - return $this->types; - } - - - /** - * Set the types of entities that will be loaded. - * - * @param string|array $types Either a string with the name of one single type allowed, or an array with a list of - * types. Pass an empty array to reset to all types of entities. - */ - public function setTypes($types) - { - if (!is_array($types)) { - $types = array($types); - } - $this->types = $types; - } - - - /** - * This function processes a SAML metadata file. - * - * @param $source - */ - public function loadSource($source) - { - if (preg_match('@^https?://@i', $source['src'])) { - // Build new HTTP context - $context = $this->createContext($source); - - // GET! - try { - list($data, $responseHeaders) = \SimpleSAML\Utils\HTTP::fetch($source['src'], $context, true); - } catch(Exception $e) { - SimpleSAML\Logger::warning('metarefresh: ' . $e->getMessage()); - } - - // We have response headers, so the request succeeded - if (!isset($responseHeaders)) { - // No response headers, this means the request failed in some way, so re-use old data - SimpleSAML\Logger::debug('No response from ' . $source['src'] . ' - attempting to re-use cached metadata'); - $this->addCachedMetadata($source); - return; - } elseif (preg_match('@^HTTP/1\.[01]\s304\s@', $responseHeaders[0])) { - // 304 response - SimpleSAML\Logger::debug('Received HTTP 304 (Not Modified) - attempting to re-use cached metadata'); - $this->addCachedMetadata($source); - return; - } elseif (!preg_match('@^HTTP/1\.[01]\s200\s@', $responseHeaders[0])) { - // Other error - SimpleSAML\Logger::debug('Error from ' . $source['src'] . ' - attempting to re-use cached metadata'); - $this->addCachedMetadata($source); - return; - } - } else { - // Local file. - $data = file_get_contents($source['src']); - $responseHeaders = null; - } - - // Everything OK. Proceed. - if (isset($source['conditionalGET']) && $source['conditionalGET']) { - // Stale or no metadata, so a fresh copy - SimpleSAML\Logger::debug('Downloaded fresh copy'); - } - - try { - $entities = $this->loadXML($data, $source); - } catch(Exception $e) { - SimpleSAML\Logger::debug('XML parser error when parsing ' . $source['src'] . ' - attempting to re-use cached metadata'); - $this->addCachedMetadata($source); - return; - } - - foreach ($entities as $entity) { - - if (isset($source['blacklist'])) { - if (!empty($source['blacklist']) && in_array($entity->getEntityID(), $source['blacklist'], true)) { - SimpleSAML\Logger::info('Skipping "' . $entity->getEntityID() . '" - blacklisted.' . "\n"); - continue; - } - } - - if (isset($source['whitelist'])) { - if (!empty($source['whitelist']) && !in_array($entity->getEntityID(), $source['whitelist'], true)) { - SimpleSAML\Logger::info('Skipping "' . $entity->getEntityID() . '" - not in the whitelist.' . "\n"); - continue; - } - } - - if (array_key_exists('certificates', $source) && $source['certificates'] !== null) { - if (!$entity->validateSignature($source['certificates'])) { - SimpleSAML\Logger::info('Skipping "' . $entity->getEntityId() . '" - could not verify signature using certificate.' . "\n"); - continue; - } - } - - if (array_key_exists('validateFingerprint', $source) && $source['validateFingerprint'] !== null) { - if (!array_key_exists('certificates', $source) || $source['certificates'] == null) { - if (!$entity->validateFingerprint($source['validateFingerprint'])) { - SimpleSAML\Logger::info('Skipping "' . $entity->getEntityId() . '" - could not verify signature using fingerprint.' . "\n"); - continue; - } - } else { - SimpleSAML\Logger::info('Skipping validation with fingerprint since option certificate is set.' . "\n"); - } - } - - $template = null; - if (array_key_exists('template', $source)) $template = $source['template']; - - $this->addMetadata($source['src'], $entity->getMetadata1xSP(), 'shib13-sp-remote', $template); - $this->addMetadata($source['src'], $entity->getMetadata1xIdP(), 'shib13-idp-remote', $template); - $this->addMetadata($source['src'], $entity->getMetadata20SP(), 'saml20-sp-remote', $template); - $this->addMetadata($source['src'], $entity->getMetadata20IdP(), 'saml20-idp-remote', $template); - $attributeAuthorities = $entity->getAttributeAuthorities(); - if (!empty($attributeAuthorities)) { - $this->addMetadata($source['src'], $attributeAuthorities[0], 'attributeauthority-remote', $template); - } - } - - $this->saveState($source, $responseHeaders); - } - - /** - * Create HTTP context, with any available caches taken into account - */ - private function createContext($source) + $this->expire = $expire; + $this->metadata = array(); + $this->oldMetadataSrc = $oldMetadataSrc; + $this->stateFile = $stateFile; + $this->changed = false; + + // Read file containing $state from disk + if (is_readable($stateFile)) { + include $stateFile; + } + + $this->state = array(); + + } + + + /** + * Get the types of entities that will be loaded. + * + * @return array The entity types allowed. + */ + public function getTypes() { - $config = SimpleSAML_Configuration::getInstance(); - $name = $config->getString('technicalcontact_name', null); - $mail = $config->getString('technicalcontact_email', null); + return $this->types; + } + - $rawheader = "User-Agent: SimpleSAMLphp metarefresh, run by $name <$mail>\r\n"; + /** + * Set the types of entities that will be loaded. + * + * @param string|array $types Either a string with the name of one single type allowed, or an array with a list of + * types. Pass an empty array to reset to all types of entities. + */ + public function setTypes($types) + { + if (!is_array($types)) { + $types = array($types); + } + $this->types = $types; + } + + + /** + * This function processes a SAML metadata file. + * + * @param $source + */ + public function loadSource($source) + { + if (preg_match('@^https?://@i', $source['src'])) { + // Build new HTTP context + $context = $this->createContext($source); + + // GET! + try { + list($data, $responseHeaders) = \SimpleSAML\Utils\HTTP::fetch($source['src'], $context, true); + } catch(Exception $e) { + SimpleSAML\Logger::warning('metarefresh: ' . $e->getMessage()); + } + + // We have response headers, so the request succeeded + if (!isset($responseHeaders)) { + // No response headers, this means the request failed in some way, so re-use old data + SimpleSAML\Logger::debug('No response from ' . $source['src'] . ' - attempting to re-use cached metadata'); + $this->addCachedMetadata($source); + return; + } elseif (preg_match('@^HTTP/1\.[01]\s304\s@', $responseHeaders[0])) { + // 304 response + SimpleSAML\Logger::debug('Received HTTP 304 (Not Modified) - attempting to re-use cached metadata'); + $this->addCachedMetadata($source); + return; + } elseif (!preg_match('@^HTTP/1\.[01]\s200\s@', $responseHeaders[0])) { + // Other error + SimpleSAML\Logger::debug('Error from ' . $source['src'] . ' - attempting to re-use cached metadata'); + $this->addCachedMetadata($source); + return; + } + } else { + // Local file. + $data = file_get_contents($source['src']); + $responseHeaders = null; + } + + // Everything OK. Proceed. + if (isset($source['conditionalGET']) && $source['conditionalGET']) { + // Stale or no metadata, so a fresh copy + SimpleSAML\Logger::debug('Downloaded fresh copy'); + } + + try { + $entities = $this->loadXML($data, $source); + } catch(Exception $e) { + SimpleSAML\Logger::debug('XML parser error when parsing ' . $source['src'] . ' - attempting to re-use cached metadata'); + SimpleSAML\Logger::debug('XML parser returned: ' . $e->getMessage()); + $this->addCachedMetadata($source); + return; + } + + foreach ($entities as $entity) { + + if (isset($source['blacklist'])) { + if (!empty($source['blacklist']) && in_array($entity->getEntityID(), $source['blacklist'], true)) { + SimpleSAML\Logger::info('Skipping "' . $entity->getEntityID() . '" - blacklisted.' . "\n"); + continue; + } + } + + if (isset($source['whitelist'])) { + if (!empty($source['whitelist']) && !in_array($entity->getEntityID(), $source['whitelist'], true)) { + SimpleSAML\Logger::info('Skipping "' . $entity->getEntityID() . '" - not in the whitelist.' . "\n"); + continue; + } + } + + if (array_key_exists('certificates', $source) && $source['certificates'] !== null) { + if (!$entity->validateSignature($source['certificates'])) { + SimpleSAML\Logger::info('Skipping "' . $entity->getEntityId() . '" - could not verify signature using certificate.' . "\n"); + continue; + } + } + + if (array_key_exists('validateFingerprint', $source) && $source['validateFingerprint'] !== null) { + if (!array_key_exists('certificates', $source) || $source['certificates'] == null) { + if (!$entity->validateFingerprint($source['validateFingerprint'])) { + SimpleSAML\Logger::info('Skipping "' . $entity->getEntityId() . '" - could not verify signature using fingerprint.' . "\n"); + continue; + } + } else { + SimpleSAML\Logger::info('Skipping validation with fingerprint since option certificate is set.' . "\n"); + } + } + + $template = null; + if (array_key_exists('template', $source)) { + $template = $source['template']; + } + + if (in_array('shib13-sp-remote', $this->types)) { + $this->addMetadata($source['src'], $entity->getMetadata1xSP(), 'shib13-sp-remote', $template); + } + if (in_array('shib13-idp-remote', $this->types)) { + $this->addMetadata($source['src'], $entity->getMetadata1xIdP(), 'shib13-idp-remote', $template); + } + if (in_array('saml20-sp-remote', $this->types)) { + $this->addMetadata($source['src'], $entity->getMetadata20SP(), 'saml20-sp-remote', $template); + } + if (in_array('saml20-idp-remote', $this->types)) { + $this->addMetadata($source['src'], $entity->getMetadata20IdP(), 'saml20-idp-remote', $template); + } + if (in_array('attributeauthority-remote', $this->types)) { + $attributeAuthorities = $entity->getAttributeAuthorities(); + if (!empty($attributeAuthorities)) { + $this->addMetadata($source['src'], $attributeAuthorities[0], 'attributeauthority-remote', $template); + } + } + } + + $this->saveState($source, $responseHeaders); + } + + /** + * Create HTTP context, with any available caches taken into account + */ + private function createContext($source) + { + $config = SimpleSAML_Configuration::getInstance(); + $name = $config->getString('technicalcontact_name', null); + $mail = $config->getString('technicalcontact_email', null); - if (isset($source['conditionalGET']) && $source['conditionalGET']) { - if (array_key_exists($source['src'], $this->state)) { + $rawheader = "User-Agent: SimpleSAMLphp metarefresh, run by $name <$mail>\r\n"; - $sourceState = $this->state[$source['src']]; + if (isset($source['conditionalGET']) && $source['conditionalGET']) { + if (array_key_exists($source['src'], $this->state)) { + $sourceState = $this->state[$source['src']]; - if (isset($sourceState['last-modified'])) { - $rawheader .= 'If-Modified-Since: ' . $sourceState['last-modified'] . "\r\n"; - } + if (isset($sourceState['last-modified'])) { + $rawheader .= 'If-Modified-Since: ' . $sourceState['last-modified'] . "\r\n"; + } - if (isset($sourceState['etag'])) { - $rawheader .= 'If-None-Match: ' . $sourceState['etag'] . "\r\n"; - } - } - } + if (isset($sourceState['etag'])) { + $rawheader .= 'If-None-Match: ' . $sourceState['etag'] . "\r\n"; + } + } + } - return array('http' => array('header' => $rawheader)); - } + return array('http' => array('header' => $rawheader)); + } - private function addCachedMetadata($source) + private function addCachedMetadata($source) { - if (isset($this->oldMetadataSrc)) { - foreach ($this->types as $type) { - foreach ($this->oldMetadataSrc->getMetadataSet($type) as $entity) { - if (array_key_exists('metarefresh:src', $entity)) { - if ($entity['metarefresh:src'] == $source['src']) { - $this->addMetadata($source['src'], $entity, $type); - } - } - } - } - } - } - - - /** - * Store caching state data for a source - */ - private function saveState($source, $responseHeaders) + if (isset($this->oldMetadataSrc)) { + foreach ($this->types as $type) { + foreach ($this->oldMetadataSrc->getMetadataSet($type) as $entity) { + if (array_key_exists('metarefresh:src', $entity)) { + if ($entity['metarefresh:src'] == $source['src']) { + $this->addMetadata($source['src'], $entity, $type); + } + } + } + } + } + } + + + /** + * Store caching state data for a source + */ + private function saveState($source, $responseHeaders) { - if (isset($source['conditionalGET']) && $source['conditionalGET']) { - // Headers section - $candidates = array('last-modified', 'etag'); - - foreach ($candidates as $candidate) { - if (array_key_exists($candidate, $responseHeaders)) { - $this->state[$source['src']][$candidate] = $responseHeaders[$candidate]; - } - } - - if (!empty($this->state[$source['src']])) { - // Timestamp when this src was requested. - $this->state[$source['src']]['requested_at'] = $this->getTime(); - - $this->changed = true; - } - } - } - - - /** - * Parse XML metadata and return entities - */ - private function loadXML($data, $source) + if (isset($source['conditionalGET']) && $source['conditionalGET']) { + // Headers section + if ($responseHeaders !== null) { + $candidates = array('last-modified', 'etag'); + + foreach ($candidates as $candidate) { + if (array_key_exists($candidate, $responseHeaders)) { + $this->state[$source['src']][$candidate] = $responseHeaders[$candidate]; + } + } + } + + if (!empty($this->state[$source['src']])) { + // Timestamp when this src was requested. + $this->state[$source['src']]['requested_at'] = $this->getTime(); + $this->changed = true; + } + } + } + + /** + * Parse XML metadata and return entities + */ + private function loadXML($data, $source) { - try { - $doc = \SAML2\DOMDocumentFactory::fromString($data); - } catch (Exception $e) { - throw new Exception('Failed to read XML from ' . $source['src']); - } - if ($doc->documentElement === null) { - throw new Exception('Opened file is not an XML document: ' . $source['src']); - } - return SimpleSAML_Metadata_SAMLParser::parseDescriptorsElement($doc->documentElement); - } - - - /** - * This function writes the state array back to disk - */ - public function writeState() + try { + $doc = \SAML2\DOMDocumentFactory::fromString($data); + } catch (Exception $e) { + throw new Exception('Failed to read XML from ' . $source['src']); + } + if ($doc->documentElement === null) { + throw new Exception('Opened file is not an XML document: ' . $source['src']); + } + return SimpleSAML_Metadata_SAMLParser::parseDescriptorsElement($doc->documentElement); + } + + + /** + * This function writes the state array back to disk + */ + public function writeState() { - if ($this->changed) { - SimpleSAML\Logger::debug('Writing: ' . $this->stateFile); + if ($this->changed) { + SimpleSAML\Logger::debug('Writing: ' . $this->stateFile); SimpleSAML\Utils\System::writeFile( - $this->stateFile, - "<?php\n/* This file was generated by the metarefresh module at ".$this->getTime() . ".\n". - " Do not update it manually as it will get overwritten. */\n". - '$state = ' . var_export($this->state, true) . ";\n?>\n", - 0644 - ); - } - } - - - /** - * This function writes the metadata to stdout. - */ - public function dumpMetadataStdOut() + $this->stateFile, + "<?php\n/* This file was generated by the metarefresh module at ".$this->getTime() . ".\n". + " Do not update it manually as it will get overwritten. */\n". + '$state = ' . var_export($this->state, true) . ";\n?>\n", + 0644 + ); + } + } + + + /** + * This function writes the metadata to stdout. + */ + public function dumpMetadataStdOut() { - foreach ($this->metadata as $category => $elements) { - - echo '/* The following data should be added to metadata/' . $category . '.php. */' . "\n"; - - - foreach ($elements as $m) { - $filename = $m['filename']; - $entityID = $m['metadata']['entityid']; - - echo "\n"; - echo '/* The following metadata was generated from ' . $filename . ' on ' . $this->getTime() . '. */' . "\n"; - echo '$metadata[\'' . addslashes($entityID) . '\'] = ' . var_export($m['metadata'], true) . ';' . "\n"; - } - - - echo "\n"; - echo '/* End of data which should be added to metadata/' . $category . '.php. */' . "\n"; - echo "\n"; - } - } - - - /** - * This function adds metadata from the specified file to the list of metadata. - * This function will return without making any changes if $metadata is NULL. - * - * @param $filename The filename the metadata comes from. - * @param $metadata The metadata. - * @param $type The metadata type. - */ - private function addMetadata($filename, $metadata, $type, $template = null) + foreach ($this->metadata as $category => $elements) { + + echo '/* The following data should be added to metadata/' . $category . '.php. */' . "\n"; + + foreach ($elements as $m) { + $filename = $m['filename']; + $entityID = $m['metadata']['entityid']; + + echo "\n"; + echo '/* The following metadata was generated from ' . $filename . ' on ' . $this->getTime() . '. */' . "\n"; + echo '$metadata[\'' . addslashes($entityID) . '\'] = ' . var_export($m['metadata'], true) . ';' . "\n"; + } + + echo "\n"; + echo '/* End of data which should be added to metadata/' . $category . '.php. */' . "\n"; + echo "\n"; + } + } + + + /** + * This function adds metadata from the specified file to the list of metadata. + * This function will return without making any changes if $metadata is NULL. + * + * @param $filename The filename the metadata comes from. + * @param $metadata The metadata. + * @param $type The metadata type. + */ + private function addMetadata($filename, $metadata, $type, $template = null) { - if ($metadata === null) { - return; - } - - if (isset($template)) { - $metadata = array_merge($metadata, $template); - } - - $metadata['metarefresh:src'] = $filename; - if (!array_key_exists($type, $this->metadata)) { - $this->metadata[$type] = array(); - } - - // If expire is defined in constructor... - if (!empty($this->expire)) { - - // If expire is already in metadata - if (array_key_exists('expire', $metadata)) { - - // Override metadata expire with more restrictive global config- - if ($this->expire < $metadata['expire']) - $metadata['expire'] = $this->expire; - - // If expire is not already in metadata use global config - } else { - $metadata['expire'] = $this->expire; - } - } - - - - $this->metadata[$type][] = array('filename' => $filename, 'metadata' => $metadata); - } - - - /** - * This function writes the metadata to an ARP file - */ - public function writeARPfile($config) + if ($metadata === null) { + return; + } + + if (isset($template)) { + $metadata = array_merge($metadata, $template); + } + + $metadata['metarefresh:src'] = $filename; + if (!array_key_exists($type, $this->metadata)) { + $this->metadata[$type] = array(); + } + + // If expire is defined in constructor... + if (!empty($this->expire)) { + // If expire is already in metadata + if (array_key_exists('expire', $metadata)) { + // Override metadata expire with more restrictive global config- + if ($this->expire < $metadata['expire']) { + $metadata['expire'] = $this->expire; + } + + // If expire is not already in metadata use global config + } else { + $metadata['expire'] = $this->expire; + } + } + $this->metadata[$type][] = array('filename' => $filename, 'metadata' => $metadata); + } + + + /** + * This function writes the metadata to an ARP file + */ + public function writeARPfile($config) { - assert($config instanceof SimpleSAML_Configuration); - - $arpfile = $config->getValue('arpfile'); - $types = array('saml20-sp-remote'); - - $md = array(); - foreach ($this->metadata as $category => $elements) { - if (!in_array($category, $types, true)) continue; - $md = array_merge($md, $elements); - } - - // $metadata, $attributemap, $prefix, $suffix - $arp = new sspmod_metarefresh_ARP($md, - $config->getValue('attributemap', ''), - $config->getValue('prefix', ''), - $config->getValue('suffix', '') - ); - - - $arpxml = $arp->getXML(); - - SimpleSAML\Logger::info('Writing ARP file: ' . $arpfile . "\n"); - file_put_contents($arpfile, $arpxml); - } - - - /** - * This function writes the metadata to to separate files in the output directory. - */ - public function writeMetadataFiles($outputDir) + assert($config instanceof SimpleSAML_Configuration); + + $arpfile = $config->getValue('arpfile'); + $types = array('saml20-sp-remote'); + + $md = array(); + foreach ($this->metadata as $category => $elements) { + if (!in_array($category, $types, true)) { + continue; + } + $md = array_merge($md, $elements); + } + + // $metadata, $attributemap, $prefix, $suffix + $arp = new sspmod_metarefresh_ARP($md, + $config->getValue('attributemap', ''), + $config->getValue('prefix', ''), + $config->getValue('suffix', '') + ); + + + $arpxml = $arp->getXML(); + + SimpleSAML\Logger::info('Writing ARP file: ' . $arpfile . "\n"); + file_put_contents($arpfile, $arpxml); + } + + + /** + * This function writes the metadata to to separate files in the output directory. + */ + public function writeMetadataFiles($outputDir) { - while (strlen($outputDir) > 0 && $outputDir[strlen($outputDir) - 1] === '/') { - $outputDir = substr($outputDir, 0, strlen($outputDir) - 1); - } - - if (!file_exists($outputDir)) { - SimpleSAML\Logger::info('Creating directory: ' . $outputDir . "\n"); - $res = @mkdir($outputDir, 0777, true); - if ($res === false) { - throw new Exception('Error creating directory: ' . $outputDir); - } - } - - foreach ($this->types as $type) { - $filename = $outputDir . '/' . $type . '.php'; - - if (array_key_exists($type, $this->metadata)) { - $elements = $this->metadata[$type]; - SimpleSAML\Logger::debug('Writing: ' . $filename); - - $content = '<?php' . "\n" . '/* This file was generated by the metarefresh module at '. $this->getTime() . "\n"; - $content .= ' Do not update it manually as it will get overwritten' . "\n" . '*/' . "\n"; - - foreach ($elements as $m) { - $entityID = $m['metadata']['entityid']; - $content .= "\n"; - $content .= '$metadata[\'' . addslashes($entityID) . '\'] = ' . var_export($m['metadata'], TRUE) . ';' . "\n"; - } - - $content .= "\n" . '?>'; + while (strlen($outputDir) > 0 && $outputDir[strlen($outputDir) - 1] === '/') { + $outputDir = substr($outputDir, 0, strlen($outputDir) - 1); + } + + if (!file_exists($outputDir)) { + SimpleSAML\Logger::info('Creating directory: ' . $outputDir . "\n"); + $res = @mkdir($outputDir, 0777, true); + if ($res === false) { + throw new Exception('Error creating directory: ' . $outputDir); + } + } + + foreach ($this->types as $type) { + $filename = $outputDir . '/' . $type . '.php'; + + if (array_key_exists($type, $this->metadata)) { + $elements = $this->metadata[$type]; + SimpleSAML\Logger::debug('Writing: ' . $filename); + + $content = '<?php' . "\n" . '/* This file was generated by the metarefresh module at '. $this->getTime() . "\n"; + $content .= ' Do not update it manually as it will get overwritten' . "\n" . '*/' . "\n"; + + foreach ($elements as $m) { + $entityID = $m['metadata']['entityid']; + $content .= "\n"; + $content .= '$metadata[\'' . addslashes($entityID) . '\'] = ' . var_export($m['metadata'], true) . ';' . "\n"; + } + + $content .= "\n" . '?>'; SimpleSAML\Utils\System::writeFile($filename, $content, 0644); - } elseif (is_file($filename)) { - if (unlink($filename)) { - SimpleSAML\Logger::debug('Deleting stale metadata file: ' . $filename); - } else { - SimpleSAML\Logger::warning('Could not delete stale metadata file: ' . $filename); - } - } - } - } - - - /** - * Save metadata for loading with the 'serialize' metadata loader. - * - * @param string $outputDir The directory we should save the metadata to. - */ - public function writeMetadataSerialize($outputDir) + } elseif (is_file($filename)) { + if (unlink($filename)) { + SimpleSAML\Logger::debug('Deleting stale metadata file: ' . $filename); + } else { + SimpleSAML\Logger::warning('Could not delete stale metadata file: ' . $filename); + } + } + } + } + + + /** + * Save metadata for loading with the 'serialize' metadata loader. + * + * @param string $outputDir The directory we should save the metadata to. + */ + public function writeMetadataSerialize($outputDir) { - assert(is_string($outputDir)); - - $metaHandler = new SimpleSAML_Metadata_MetaDataStorageHandlerSerialize(array('directory' => $outputDir)); - - /* First we add all the metadata entries to the metadata handler. */ - foreach ($this->metadata as $set => $elements) { - foreach ($elements as $m) { - $entityId = $m['metadata']['entityid']; - - SimpleSAML\Logger::debug('metarefresh: Add metadata entry ' . - var_export($entityId, true) . ' in set ' . var_export($set, true) . '.'); - $metaHandler->saveMetadata($entityId, $set, $m['metadata']); - } - } - - /* Then we delete old entries which should no longer exist. */ - $ct = time(); - foreach ($metaHandler->getMetadataSets() as $set) { - foreach ($metaHandler->getMetadataSet($set) as $entityId => $metadata) { - if (!array_key_exists('expire', $metadata)) { - SimpleSAML\Logger::warning('metarefresh: Metadata entry without expire timestamp: ' . var_export($entityId, true) . - ' in set ' . var_export($set, true) . '.'); - continue; - } - if ($metadata['expire'] > $ct) { - continue; - } - SimpleSAML\Logger::debug('metarefresh: ' . $entityId . ' expired ' . date('l jS \of F Y h:i:s A', $metadata['expire']) ); - SimpleSAML\Logger::debug('metarefresh: Delete expired metadata entry ' . - var_export($entityId, true) . ' in set ' . var_export($set, true) . '. (' . ($ct - $metadata['expire']) . ' sec)'); - $metaHandler->deleteMetadata($entityId, $set); - } - } - } - - - private function getTime() + assert(is_string($outputDir)); + + $metaHandler = new SimpleSAML_Metadata_MetaDataStorageHandlerSerialize(array('directory' => $outputDir)); + + /* First we add all the metadata entries to the metadata handler. */ + foreach ($this->metadata as $set => $elements) { + foreach ($elements as $m) { + $entityId = $m['metadata']['entityid']; + + SimpleSAML\Logger::debug( + 'metarefresh: Add metadata entry ' . + var_export($entityId, true) . ' in set ' . var_export($set, true) . '.' + ); + $metaHandler->saveMetadata($entityId, $set, $m['metadata']); + } + } + + /* Then we delete old entries which should no longer exist. */ + $ct = time(); + foreach ($metaHandler->getMetadataSets() as $set) { + foreach ($metaHandler->getMetadataSet($set) as $entityId => $metadata) { + if (!array_key_exists('expire', $metadata)) { + SimpleSAML\Logger::warning( + 'metarefresh: Metadata entry without expire timestamp: ' . var_export($entityId, true) . + ' in set ' . var_export($set, true) . '.' + ); + continue; + } + if ($metadata['expire'] > $ct) { + continue; + } + SimpleSAML\Logger::debug('metarefresh: ' . $entityId . ' expired ' . date('l jS \of F Y h:i:s A', $metadata['expire'])); + SimpleSAML\Logger::debug( + 'metarefresh: Delete expired metadata entry ' . + var_export($entityId, true) . ' in set ' . var_export($set, true) . '. (' . ($ct - $metadata['expire']) . ' sec)' + ); + $metaHandler->deleteMetadata($entityId, $set); + } + } + } + + + private function getTime() { - /* The current date, as a string. */ - date_default_timezone_set('UTC'); - return date('Y-m-d\\TH:i:s\\Z'); - } + /* The current date, as a string. */ + date_default_timezone_set('UTC'); + return date('Y-m-d\\TH:i:s\\Z'); + } } diff --git a/modules/metarefresh/templates/fetch.tpl.php b/modules/metarefresh/templates/fetch.tpl.php index 512f6f429f3cc6b937ee5e38a062ca66d61d98da..780e98c894c86ca40c45cbddb8c8878c255ce83a 100644 --- a/modules/metarefresh/templates/fetch.tpl.php +++ b/modules/metarefresh/templates/fetch.tpl.php @@ -1,20 +1,20 @@ <?php -$this->data['header'] = $this->t('{aggregator:aggregator:aggregator_header}'); +$this->data['header'] = $this->t('{metarefresh:metarefresh:metarefresh_header}'); $this->includeAtTemplateBase('includes/header.php'); -echo('<h1>Metarefresh fetch</h1>'); +echo('<h1>'.$this->data['header'].'</h1>'); if (!empty($this->data['logentries'])) { - + echo '<pre style="border: 1px solid #aaa; padding: .5em; overflow: scroll">'; foreach($this->data['logentries'] AS $l) { - echo $l . "\n"; + echo $l . "\n"; } echo '</pre>'; - + } else { - echo 'No output from metarefresh.'; + echo $this->t('{metarefresh:metarefresh:no_output}'); } diff --git a/modules/oauth/lib/OAuthStore.php b/modules/oauth/lib/OAuthStore.php index 3b9ec1afac68493a7c66ac10daa7199847e33490..6bc5f1b436d7a5d5a56ec20eb9fa6566f12b1508 100644 --- a/modules/oauth/lib/OAuthStore.php +++ b/modules/oauth/lib/OAuthStore.php @@ -11,170 +11,191 @@ require_once(dirname(dirname(__FILE__)) . '/libextinc/OAuth.php'); * @author Mark Dobrinic, <mdobrinic@cozmanova.com>, Cozmanova bv * @package SimpleSAMLphp */ -class sspmod_oauth_OAuthStore extends OAuthDataStore { - - private $store; - private $config; - private $defaultversion = '1.0'; - - protected $_store_tables = array( - 'consumers' => 'consumer = array with consumer attributes', - 'nonce' => 'nonce+consumer_key = -boolean-', - 'requesttorequest' => 'requestToken.key = array(version,callback,consumerKey,)', - 'authorized' => 'requestToken.key, verifier = array(authenticated-user-attributes)', - 'access' => 'accessToken.key+consumerKey = accestoken', - 'request' => 'requestToken.key+consumerKey = requesttoken', - ); - - function __construct() { - $this->store = new sspmod_core_Storage_SQLPermanentStorage('oauth'); - $this->config = SimpleSAML_Configuration::getOptionalConfig('module_oauth.php'); +class sspmod_oauth_OAuthStore extends OAuthDataStore +{ + private $store; + private $config; + private $defaultversion = '1.0'; + + protected $_store_tables = array( + 'consumers' => 'consumer = array with consumer attributes', + 'nonce' => 'nonce+consumer_key = -boolean-', + 'requesttorequest' => 'requestToken.key = array(version,callback,consumerKey,)', + 'authorized' => 'requestToken.key, verifier = array(authenticated-user-attributes)', + 'access' => 'accessToken.key+consumerKey = accesstoken', + 'request' => 'requestToken.key+consumerKey = requesttoken', + ); + + + public function __construct() + { + $this->store = new sspmod_core_Storage_SQLPermanentStorage('oauth'); + $this->config = SimpleSAML_Configuration::getOptionalConfig('module_oauth.php'); } - - + + /** * Attach the data to the token, and establish the Callback URL and verifier * @param $requestTokenKey RequestToken that was authorized * @param $data Data that is authorized and to be attached to the requestToken * @return array(string:url, string:verifier) ; empty verifier for 1.0-response */ - public function authorize($requestTokenKey, $data) { - $url = null; - - // See whether to remember values from the original requestToken request: - $request_attributes = $this->store->get('requesttorequest', $requestTokenKey, ''); // must be there .. - if ($request_attributes['value']) { - // establish callback to use - if ($request_attributes['value']['callback']) { - $url = $request_attributes['value']['callback']; - } - } - - - // Is there a callback registered? This is leading, even over a supplied oauth_callback-parameter - $oConsumer = $this->lookup_consumer($request_attributes['value']['consumerKey']); - - if ($oConsumer && ($oConsumer->callback_url)) $url = $oConsumer->callback_url; - - $verifier = SimpleSAML\Utils\Random::generateID(); - $url = \SimpleSAML\Utils\HTTP::addURLParameters($url, array("oauth_verifier"=>$verifier)); - - $this->store->set('authorized', $requestTokenKey, $verifier, $data, $this->config->getValue('requestTokenDuration', 60*30) ); - - return array($url, $verifier); - } - - /** - * Perform lookup whether a given token exists in the list of authorized tokens; if a verifier is - * passed as well, the verifier *must* match the verifier that was registered with the token<br/> - * Note that an accessToken should never be stored with a verifier - * @param $requestToken - * @param $verifier - * @return unknown_type - */ - public function isAuthorized($requestToken, $verifier='') { - SimpleSAML\Logger::info('OAuth isAuthorized(' . $requestToken . ')'); - return $this->store->exists('authorized', $requestToken, $verifier); - } - - public function getAuthorizedData($token, $verifier = '') { - SimpleSAML\Logger::info('OAuth getAuthorizedData(' . $token . ')'); - $data = $this->store->get('authorized', $token, $verifier); - return $data['value']; - } - - public function moveAuthorizedData($requestToken, $verifier, $accessTokenKey) { - SimpleSAML\Logger::info('OAuth moveAuthorizedData(' . $requestToken . ', ' . $accessTokenKey . ')'); - - // Retrieve authorizedData from authorized.requestToken (with provider verifier) - $authorizedData = $this->getAuthorizedData($requestToken, $verifier); - - // Remove the requesttoken+verifier from authorized store - $this->store->remove('authorized', $requestToken, $verifier); - - // Add accesstoken with authorizedData to authorized store (with empty verifier) - // accessTokenKey+consumer => accessToken is already registered in 'access'-table - $this->store->set('authorized', $accessTokenKey, '', $authorizedData, $this->config->getValue('accessTokenDuration', 60*60*24)); - } - - public function lookup_consumer($consumer_key) { - SimpleSAML\Logger::info('OAuth lookup_consumer(' . $consumer_key . ')'); - if (! $this->store->exists('consumers', $consumer_key, '')) return NULL; - $consumer = $this->store->get('consumers', $consumer_key, ''); - - $callback = NULL; - if ($consumer['value']['callback_url']) $callback = $consumer['value']['callback_url']; - - if ($consumer['value']['RSAcertificate']) { - return new OAuthConsumer($consumer['value']['key'], $consumer['value']['RSAcertificate'], $callback); - } else { - return new OAuthConsumer($consumer['value']['key'], $consumer['value']['secret'], $callback); - } + public function authorize($requestTokenKey, $data) + { + $url = null; + + // See whether to remember values from the original requestToken request: + $request_attributes = $this->store->get('requesttorequest', $requestTokenKey, ''); + // must be there .. + if ($request_attributes['value']) { + // establish callback to use + if ($request_attributes['value']['callback']) { + $url = $request_attributes['value']['callback']; + } + } + + // Is there a callback registered? This is leading, even over a supplied oauth_callback-parameter + $oConsumer = $this->lookup_consumer($request_attributes['value']['consumerKey']); + + if ($oConsumer && ($oConsumer->callback_url)) { + $url = $oConsumer->callback_url; + } + + $verifier = SimpleSAML\Utils\Random::generateID(); + $url = \SimpleSAML\Utils\HTTP::addURLParameters($url, array("oauth_verifier"=>$verifier)); + + $this->store->set('authorized', $requestTokenKey, $verifier, $data, $this->config->getValue('requestTokenDuration', 60*30)); + + return array($url, $verifier); } - function lookup_token($consumer, $tokenType = 'default', $token) { - SimpleSAML\Logger::info('OAuth lookup_token(' . $consumer->key . ', ' . $tokenType. ',' . $token . ')'); - $data = $this->store->get($tokenType, $token, $consumer->key); - if ($data == NULL) throw new Exception('Could not find token'); - return $data['value']; + /** + * Perform lookup whether a given token exists in the list of authorized tokens; if a verifier is + * passed as well, the verifier *must* match the verifier that was registered with the token<br/> + * Note that an accessToken should never be stored with a verifier + * @param $requestToken + * @param $verifier + * @return unknown_type + */ + public function isAuthorized($requestToken, $verifier = '') + { + SimpleSAML\Logger::info('OAuth isAuthorized(' . $requestToken . ')'); + return $this->store->exists('authorized', $requestToken, $verifier); } - function lookup_nonce($consumer, $token, $nonce, $timestamp) { - SimpleSAML\Logger::info('OAuth lookup_nonce(' . $consumer . ', ' . $token. ',' . $nonce . ')'); - if ($this->store->exists('nonce', $nonce, $consumer->key)) return TRUE; - $this->store->set('nonce', $nonce, $consumer->key, TRUE, $this->config->getValue('nonceCache', 60*60*24*14)); - return FALSE; + public function getAuthorizedData($token, $verifier = '') + { + SimpleSAML\Logger::info('OAuth getAuthorizedData(' . $token . ')'); + $data = $this->store->get('authorized', $token, $verifier); + return $data['value']; } - function new_request_token($consumer, $callback = null, $version = null) { - SimpleSAML\Logger::info('OAuth new_request_token(' . $consumer . ')'); - - $lifetime = $this->config->getValue('requestTokenDuration', 60*30); - - $token = new OAuthToken(SimpleSAML\Utils\Random::generateID(), SimpleSAML\Utils\Random::generateID()); - $token->callback = $callback; // OAuth1.0-RevA - $this->store->set('request', $token->key, $consumer->key, $token, $lifetime); - - // also store in requestToken->key => array('callback'=>CallbackURL, 'version'=>oauth_version - $request_attributes = array( - 'callback' => $callback, - 'version' => ($version?$version:$this->defaultversion), - 'consumerKey' => $consumer->key, - ); - $this->store->set('requesttorequest', $token->key, '', $request_attributes, $lifetime); - - // also store in requestToken->key => Consumer->key (enables consumer-lookup during reqToken-authorization stage) - $this->store->set('requesttoconsumer', $token->key, '', $consumer->key, $lifetime); - + public function moveAuthorizedData($requestToken, $verifier, $accessTokenKey) + { + SimpleSAML\Logger::info('OAuth moveAuthorizedData(' . $requestToken . ', ' . $accessTokenKey . ')'); + + // Retrieve authorizedData from authorized.requestToken (with provider verifier) + $authorizedData = $this->getAuthorizedData($requestToken, $verifier); + + // Remove the requesttoken+verifier from authorized store + $this->store->remove('authorized', $requestToken, $verifier); + + // Add accesstoken with authorizedData to authorized store (with empty verifier) + // accessTokenKey+consumer => accessToken is already registered in 'access'-table + $this->store->set('authorized', $accessTokenKey, '', $authorizedData, $this->config->getValue('accessTokenDuration', 60*60*24)); + } + + public function lookup_consumer($consumer_key) + { + SimpleSAML\Logger::info('OAuth lookup_consumer(' . $consumer_key . ')'); + if (!$this->store->exists('consumers', $consumer_key, '')) { + return null; + } + $consumer = $this->store->get('consumers', $consumer_key, ''); + + $callback = null; + if ($consumer['value']['callback_url']) { + $callback = $consumer['value']['callback_url']; + } + + if ($consumer['value']['RSAcertificate']) { + return new OAuthConsumer($consumer['value']['key'], $consumer['value']['RSAcertificate'], $callback); + } else { + return new OAuthConsumer($consumer['value']['key'], $consumer['value']['secret'], $callback); + } + } + + function lookup_token($consumer, $tokenType = 'default', $token) + { + SimpleSAML\Logger::info('OAuth lookup_token(' . $consumer->key . ', ' . $tokenType. ',' . $token . ')'); + $data = $this->store->get($tokenType, $token, $consumer->key); + if ($data == null) { + throw new Exception('Could not find token'); + } + return $data['value']; + } + + function lookup_nonce($consumer, $token, $nonce, $timestamp) + { + SimpleSAML\Logger::info('OAuth lookup_nonce(' . $consumer . ', ' . $token. ',' . $nonce . ')'); + if ($this->store->exists('nonce', $nonce, $consumer->key)) { + return true; + } + $this->store->set('nonce', $nonce, $consumer->key, true, $this->config->getValue('nonceCache', 60*60*24*14)); + return false; + } + + function new_request_token($consumer, $callback = null, $version = null) + { + SimpleSAML\Logger::info('OAuth new_request_token(' . $consumer . ')'); + + $lifetime = $this->config->getValue('requestTokenDuration', 60*30); + + $token = new OAuthToken(SimpleSAML\Utils\Random::generateID(), SimpleSAML\Utils\Random::generateID()); + $token->callback = $callback; // OAuth1.0-RevA + $this->store->set('request', $token->key, $consumer->key, $token, $lifetime); + + // also store in requestToken->key => array('callback'=>CallbackURL, 'version'=>oauth_version + $request_attributes = array( + 'callback' => $callback, + 'version' => ($version?$version:$this->defaultversion), + 'consumerKey' => $consumer->key, + ); + $this->store->set('requesttorequest', $token->key, '', $request_attributes, $lifetime); + + // also store in requestToken->key => Consumer->key (enables consumer-lookup during reqToken-authorization stage) + $this->store->set('requesttoconsumer', $token->key, '', $consumer->key, $lifetime); + return $token; } - function new_access_token($requestToken, $consumer, $verifier = null) { - SimpleSAML\Logger::info('OAuth new_access_token(' . $requestToken . ',' . $consumer . ')'); - $accestoken = new OAuthToken(SimpleSAML\Utils\Random::generateID(), SimpleSAML\Utils\Random::generateID()); - $this->store->set('access', $accestoken->key, $consumer->key, $accestoken, $this->config->getValue('accessTokenDuration', 60*60*24) ); - return $accestoken; + function new_access_token($requestToken, $consumer, $verifier = null) + { + SimpleSAML\Logger::info('OAuth new_access_token(' . $requestToken . ',' . $consumer . ')'); + $accesstoken = new OAuthToken(SimpleSAML\Utils\Random::generateID(), SimpleSAML\Utils\Random::generateID()); + $this->store->set('access', $accesstoken->key, $consumer->key, $accesstoken, $this->config->getValue('accessTokenDuration', 60*60*24) ); + return $accesstoken; } - + /** * Return OAuthConsumer-instance that a given requestToken was issued to * @param $requestTokenKey * @return unknown_type */ - public function lookup_consumer_by_requestToken($requestTokenKey) { - SimpleSAML\Logger::info('OAuth lookup_consumer_by_requestToken(' . $requestTokenKey . ')'); - if (! $this->store->exists('requesttorequest', $requestTokenKey, '')) return NULL; - - $request = $this->store->get('requesttorequest', $requestTokenKey, ''); - $consumerKey = $request['value']['consumerKey']; - if (! $consumerKey) { - return NULL; - } - - $consumer = $this->store->get('consumers', $consumerKey['value'], ''); - return $consumer['value']; - } - - + public function lookup_consumer_by_requestToken($requestTokenKey) + { + SimpleSAML\Logger::info('OAuth lookup_consumer_by_requestToken(' . $requestTokenKey . ')'); + if (!$this->store->exists('requesttorequest', $requestTokenKey, '')) { + return null; + } + $request = $this->store->get('requesttorequest', $requestTokenKey, ''); + $consumerKey = $request['value']['consumerKey']; + if (!$consumerKey) { + return null; + } + + $consumer = $this->store->get('consumers', $consumerKey['value'], ''); + return $consumer['value']; + } } diff --git a/modules/oauth/libextinc/OAuth.php b/modules/oauth/libextinc/OAuth.php index acaf4360d41c1a754c36fc25eeb04ba81bcf9dec..c269b993d7684d03beff21c9058cfd86e3c73039 100644 --- a/modules/oauth/libextinc/OAuth.php +++ b/modules/oauth/libextinc/OAuth.php @@ -12,18 +12,20 @@ if (!class_exists('OAuthException')) { /* * Generic exception class */ - class OAuthException extends Exception { + class OAuthException extends Exception + { // pass } } if (!class_exists('OAuthConsumer')) { - class OAuthConsumer { + class OAuthConsumer + { public $key; public $secret; public $callback_url; - public function __construct($key, $secret, $callback_url=null) + public function __construct($key, $secret, $callback_url = null) { $this->key = $key; $this->secret = $secret; @@ -37,7 +39,8 @@ if (!class_exists('OAuthConsumer')) { } } -class OAuthToken { +class OAuthToken +{ // access tokens and request tokens public $key; public $secret; @@ -65,7 +68,8 @@ class OAuthToken { "&oauth_callback_confirmed=true"; } - function __toString() { + public function __toString() + { return $this->to_string(); } } @@ -134,7 +138,7 @@ abstract class OAuthSignatureMethod */ class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod { - function get_name() + public function get_name() { return "HMAC-SHA1"; } @@ -273,7 +277,7 @@ class OAuthRequest public static $version = '1.0'; public static $POST_INPUT = 'php://input'; - public function __construct($http_method, $http_url, $parameters=null) + public function __construct($http_method, $http_url, $parameters = null) { $parameters = ($parameters) ? $parameters : array(); $parameters = array_merge(OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters); @@ -286,7 +290,7 @@ class OAuthRequest /** * attempt to build up a request from what was passed to the server */ - public static function from_request($http_method=NULL, $http_url=null, $parameters=null) + public static function from_request($http_method = null, $http_url = null, $parameters = null) { $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on") ? 'http' @@ -339,7 +343,7 @@ class OAuthRequest /** * pretty much a helper function to set up the request */ - public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=null) + public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters = null) { $parameters = ($parameters) ? $parameters : array(); $defaults = array("oauth_version" => OAuthRequest::$version, @@ -747,7 +751,7 @@ class OAuthServer */ private function check_timestamp($timestamp) { - if (! $timestamp) { + if (!$timestamp) { throw new OAuthException( 'Missing timestamp parameter. The parameter is required' ); @@ -767,7 +771,7 @@ class OAuthServer */ private function check_nonce($consumer, $token, $nonce, $timestamp) { - if (! $nonce) { + if (!$nonce) { throw new OAuthException( 'Missing nonce parameter. The parameter is required' ); diff --git a/modules/saml/hooks/hook_metadata_hosted.php b/modules/saml/hooks/hook_metadata_hosted.php index 510a8133d9f270e876f25570ed2310009836746b..f94d19ff7b37c19012f4a56c3e91fc74d2193ae0 100644 --- a/modules/saml/hooks/hook_metadata_hosted.php +++ b/modules/saml/hooks/hook_metadata_hosted.php @@ -6,7 +6,8 @@ * @param array &$metadataHosted The metadata links for hosted metadata on the frontpage. */ -function saml_hook_metadata_hosted(&$metadataHosted) { +function saml_hook_metadata_hosted(&$metadataHosted) +{ assert(is_array($metadataHosted)); $sources = SimpleSAML_Auth_Source::getSourcesOfType('saml:SP'); diff --git a/modules/saml/lib/Auth/Source/SP.php b/modules/saml/lib/Auth/Source/SP.php index 100776bf43daf54dce15435b238a671c6ebc62eb..6d325025483f3bd798484467ca7218f4e4e71cd4 100644 --- a/modules/saml/lib/Auth/Source/SP.php +++ b/modules/saml/lib/Auth/Source/SP.php @@ -1,794 +1,800 @@ <?php -class sspmod_saml_Auth_Source_SP extends SimpleSAML_Auth_Source { - - /** - * The entity ID of this SP. - * - * @var string - */ - private $entityId; - - - /** - * The metadata of this SP. - * - * @var SimpleSAML_Configuration. - */ - private $metadata; - - - /** - * The IdP the user is allowed to log into. - * - * @var string|NULL The IdP the user can log into, or NULL if the user can log into all IdPs. - */ - private $idp; - - - /** - * URL to discovery service. - * - * @var string|NULL - */ - private $discoURL; - - - /** - * Constructor for SAML SP authentication source. - * - * @param array $info Information about this authentication source. - * @param array $config Configuration. - */ - public function __construct($info, $config) { - assert(is_array($info)); - assert(is_array($config)); - - // Call the parent constructor first, as required by the interface - parent::__construct($info, $config); - - if (!isset($config['entityID'])) { - $config['entityID'] = $this->getMetadataURL(); - } - - /* For compatibility with code that assumes that $metadata->getString('entityid') gives the entity id. */ - $config['entityid'] = $config['entityID']; - - $this->metadata = SimpleSAML_Configuration::loadFromArray($config, 'authsources[' . var_export($this->authId, TRUE) . ']'); - $this->entityId = $this->metadata->getString('entityID'); - $this->idp = $this->metadata->getString('idp', NULL); - $this->discoURL = $this->metadata->getString('discoURL', NULL); - - if (empty($this->discoURL) && SimpleSAML\Module::isModuleEnabled('discojuice')) { - $this->discoURL = SimpleSAML\Module::getModuleURL('discojuice/central.php'); - } - } - - - /** - * Retrieve the URL to the metadata of this SP. - * - * @return string The metadata URL. - */ - public function getMetadataURL() { - - return SimpleSAML\Module::getModuleURL('saml/sp/metadata.php/' . urlencode($this->authId)); - } - - - /** - * Retrieve the entity id of this SP. - * - * @return string The entity id of this SP. - */ - public function getEntityId() { - - return $this->entityId; - } - - - /** - * Retrieve the metadata of this SP. - * - * @return SimpleSAML_Configuration The metadata of this SP. - */ - public function getMetadata() { - - return $this->metadata; - - } - - - /** - * Retrieve the metadata of an IdP. - * - * @param string $entityId The entity id of the IdP. - * @return SimpleSAML_Configuration The metadata of the IdP. - */ - public function getIdPMetadata($entityId) { - assert(is_string($entityId)); - - if ($this->idp !== NULL && $this->idp !== $entityId) { - throw new SimpleSAML_Error_Exception('Cannot retrieve metadata for IdP ' . var_export($entityId, TRUE) . - ' because it isn\'t a valid IdP for this SP.'); - } - - $metadataHandler = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); - - // First, look in saml20-idp-remote. - try { - return $metadataHandler->getMetaDataConfig($entityId, 'saml20-idp-remote'); - } catch (Exception $e) { - /* Metadata wasn't found. */ +class sspmod_saml_Auth_Source_SP extends SimpleSAML_Auth_Source +{ + /** + * The entity ID of this SP. + * + * @var string + */ + private $entityId; + + /** + * The metadata of this SP. + * + * @var SimpleSAML_Configuration. + */ + private $metadata; + + /** + * The IdP the user is allowed to log into. + * + * @var string|null The IdP the user can log into, or null if the user can log into all IdPs. + */ + private $idp; + + /** + * URL to discovery service. + * + * @var string|null + */ + private $discoURL; + + /** + * Constructor for SAML SP authentication source. + * + * @param array $info Information about this authentication source. + * @param array $config Configuration. + */ + public function __construct($info, $config) + { + assert(is_array($info)); + assert(is_array($config)); + + // Call the parent constructor first, as required by the interface + parent::__construct($info, $config); + + if (!isset($config['entityID'])) { + $config['entityID'] = $this->getMetadataURL(); + } + + /* For compatibility with code that assumes that $metadata->getString('entityid') + * gives the entity id. */ + $config['entityid'] = $config['entityID']; + + $this->metadata = SimpleSAML_Configuration::loadFromArray($config, + 'authsources[' . var_export($this->authId, true) . ']'); + $this->entityId = $this->metadata->getString('entityID'); + $this->idp = $this->metadata->getString('idp', null); + $this->discoURL = $this->metadata->getString('discoURL', null); + + if (empty($this->discoURL) && SimpleSAML\Module::isModuleEnabled('discojuice')) { + $this->discoURL = SimpleSAML\Module::getModuleURL('discojuice/central.php'); + } + } + + /** + * Retrieve the URL to the metadata of this SP. + * + * @return string The metadata URL. + */ + public function getMetadataURL() + { + return SimpleSAML\Module::getModuleURL('saml/sp/metadata.php/' . urlencode($this->authId)); + } + + /** + * Retrieve the entity id of this SP. + * + * @return string The entity id of this SP. + */ + public function getEntityId() + { + return $this->entityId; + } + + /** + * Retrieve the metadata of this SP. + * + * @return SimpleSAML_Configuration The metadata of this SP. + */ + public function getMetadata() + { + return $this->metadata; + } + + /** + * Retrieve the metadata of an IdP. + * + * @param string $entityId The entity id of the IdP. + * @return SimpleSAML_Configuration The metadata of the IdP. + */ + public function getIdPMetadata($entityId) + { + assert(is_string($entityId)); + + if ($this->idp !== null && $this->idp !== $entityId) { + throw new SimpleSAML_Error_Exception('Cannot retrieve metadata for IdP ' . + var_export($entityId, true) . + ' because it isn\'t a valid IdP for this SP.'); + } + + $metadataHandler = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); + + // First, look in saml20-idp-remote. + try { + return $metadataHandler->getMetaDataConfig($entityId, 'saml20-idp-remote'); + } catch (Exception $e) { + /* Metadata wasn't found. */ SimpleSAML\Logger::debug('getIdpMetadata: ' . $e->getMessage()); - } + } - /* Not found in saml20-idp-remote, look in shib13-idp-remote. */ - try { - return $metadataHandler->getMetaDataConfig($entityId, 'shib13-idp-remote'); - } catch (Exception $e) { - /* Metadata wasn't found. */ + /* Not found in saml20-idp-remote, look in shib13-idp-remote. */ + try { + return $metadataHandler->getMetaDataConfig($entityId, 'shib13-idp-remote'); + } catch (Exception $e) { + /* Metadata wasn't found. */ SimpleSAML\Logger::debug('getIdpMetadata: ' . $e->getMessage()); - } - - /* Not found. */ - throw new SimpleSAML_Error_Exception('Could not find the metadata of an IdP with entity ID ' . var_export($entityId, TRUE)); - } - - - /** - * Send a SAML1 SSO request to an IdP. - * - * @param SimpleSAML_Configuration $idpMetadata The metadata of the IdP. - * @param array $state The state array for the current authentication. - */ - private function startSSO1(SimpleSAML_Configuration $idpMetadata, array $state) { - - $idpEntityId = $idpMetadata->getString('entityid'); - - $state['saml:idp'] = $idpEntityId; - - $ar = new \SimpleSAML\XML\Shib13\AuthnRequest(); - $ar->setIssuer($this->entityId); - - $id = SimpleSAML_Auth_State::saveState($state, 'saml:sp:sso'); - $ar->setRelayState($id); - - $useArtifact = $idpMetadata->getBoolean('saml1.useartifact', NULL); - if ($useArtifact === NULL) { - $useArtifact = $this->metadata->getBoolean('saml1.useartifact', FALSE); - } - - if ($useArtifact) { - $shire = SimpleSAML\Module::getModuleURL('saml/sp/saml1-acs.php/' . $this->authId . '/artifact'); - } else { - $shire = SimpleSAML\Module::getModuleURL('saml/sp/saml1-acs.php/' . $this->authId); - } - - $url = $ar->createRedirect($idpEntityId, $shire); - - SimpleSAML\Logger::debug('Starting SAML 1 SSO to ' . var_export($idpEntityId, TRUE) . - ' from ' . var_export($this->entityId, TRUE) . '.'); - \SimpleSAML\Utils\HTTP::redirectTrustedURL($url); - } - - - /** - * Send a SAML2 SSO request to an IdP. - * - * @param SimpleSAML_Configuration $idpMetadata The metadata of the IdP. - * @param array $state The state array for the current authentication. - */ - private function startSSO2(SimpleSAML_Configuration $idpMetadata, array $state) { - - if (isset($state['saml:ProxyCount']) && $state['saml:ProxyCount'] < 0) { - SimpleSAML_Auth_State::throwException( - $state, - new \SimpleSAML\Module\saml\Error\ProxyCountExceeded(\SAML2\Constants::STATUS_RESPONDER) - ); - } - - $ar = sspmod_saml_Message::buildAuthnRequest($this->metadata, $idpMetadata); - - $ar->setAssertionConsumerServiceURL(SimpleSAML\Module::getModuleURL('saml/sp/saml2-acs.php/' . $this->authId)); - - if (isset($state['SimpleSAML_Auth_Source.ReturnURL'])) { - $ar->setRelayState($state['SimpleSAML_Auth_Source.ReturnURL']); - } - - if (isset($state['saml:AuthnContextClassRef'])) { - $accr = SimpleSAML\Utils\Arrays::arrayize($state['saml:AuthnContextClassRef']); - $comp = SAML2\Constants::COMPARISON_EXACT; - if (isset($state['saml:AuthnContextComparison']) && in_array($state['AuthnContextComparison'], array( - SAML2\Constants::COMPARISON_EXACT, - SAML2\Constants::COMPARISON_MINIMUM, - SAML2\Constants::COMPARISON_MAXIMUM, - SAML2\Constants::COMPARISON_BETTER, - ), true)) { - $comp = $state['saml:AuthnContextComparison']; - } - $ar->setRequestedAuthnContext(array('AuthnContextClassRef' => $accr, 'Comparison' => $comp)); - } - - if (isset($state['ForceAuthn'])) { - $ar->setForceAuthn((bool)$state['ForceAuthn']); - } - - if (isset($state['isPassive'])) { - $ar->setIsPassive((bool)$state['isPassive']); - } - - if (isset($state['saml:NameID'])) { - if (!is_array($state['saml:NameID']) && !is_a($state['saml:NameID'], '\SAML2\XML\saml\NameID')) { - throw new SimpleSAML_Error_Exception('Invalid value of $state[\'saml:NameID\'].'); - } - $ar->setNameId($state['saml:NameID']); - } - - if (isset($state['saml:NameIDPolicy'])) { - if (is_string($state['saml:NameIDPolicy'])) { - $policy = array( - 'Format' => (string)$state['saml:NameIDPolicy'], - 'AllowCreate' => TRUE, - ); - } elseif (is_array($state['saml:NameIDPolicy'])) { - $policy = $state['saml:NameIDPolicy']; - } else { - throw new SimpleSAML_Error_Exception('Invalid value of $state[\'saml:NameIDPolicy\'].'); - } - $ar->setNameIdPolicy($policy); - } - - if (isset($state['saml:IDPList'])) { - $IDPList = $state['saml:IDPList']; - } else { - $IDPList = array(); - } - - $ar->setIDPList(array_unique(array_merge($this->metadata->getArray('IDPList', array()), - $idpMetadata->getArray('IDPList', array()), - (array) $IDPList))); - - if (isset($state['saml:ProxyCount']) && $state['saml:ProxyCount'] !== null) { - $ar->setProxyCount($state['saml:ProxyCount']); - } elseif ($idpMetadata->getInteger('ProxyCount', null) !== null) { - $ar->setProxyCount($idpMetadata->getInteger('ProxyCount', null)); - } elseif ($this->metadata->getInteger('ProxyCount', null) !== null) { - $ar->setProxyCount($this->metadata->getInteger('ProxyCount', null)); - } - - $requesterID = array(); - if (isset($state['saml:RequesterID'])) { - $requesterID = $state['saml:RequesterID']; - } - - if (isset($state['core:SP'])) { - $requesterID[] = $state['core:SP']; - } - - $ar->setRequesterID($requesterID); - - if (isset($state['saml:Extensions'])) { - $ar->setExtensions($state['saml:Extensions']); - } - - // save IdP entity ID as part of the state - $state['ExpectedIssuer'] = $idpMetadata->getString('entityid'); - - $id = SimpleSAML_Auth_State::saveState($state, 'saml:sp:sso', TRUE); - $ar->setId($id); - - SimpleSAML\Logger::debug('Sending SAML 2 AuthnRequest to ' . var_export($idpMetadata->getString('entityid'), TRUE)); - - /* Select appropriate SSO endpoint */ - if ($ar->getProtocolBinding() === \SAML2\Constants::BINDING_HOK_SSO) { - $dst = $idpMetadata->getDefaultEndpoint('SingleSignOnService', array( - \SAML2\Constants::BINDING_HOK_SSO) - ); - } else { - $dst = $idpMetadata->getDefaultEndpoint('SingleSignOnService', array( - \SAML2\Constants::BINDING_HTTP_REDIRECT, - \SAML2\Constants::BINDING_HTTP_POST) - ); - } - $ar->setDestination($dst['Location']); - - $b = \SAML2\Binding::getBinding($dst['Binding']); - - $this->sendSAML2AuthnRequest($state, $b, $ar); - - assert(false); - } - - - /** - * Function to actually send the authentication request. - * - * This function does not return. - * - * @param array &$state The state array. - * @param \SAML2\Binding $binding The binding. - * @param \SAML2\AuthnRequest $ar The authentication request. - */ - public function sendSAML2AuthnRequest(array &$state, \SAML2\Binding $binding, \SAML2\AuthnRequest $ar) { - $binding->send($ar); - assert(false); - } - - - /** - * Send a SSO request to an IdP. - * - * @param string $idp The entity ID of the IdP. - * @param array $state The state array for the current authentication. - */ - public function startSSO($idp, array $state) { - assert(is_string($idp)); - - $idpMetadata = $this->getIdPMetadata($idp); - - $type = $idpMetadata->getString('metadata-set'); - switch ($type) { - case 'shib13-idp-remote': - $this->startSSO1($idpMetadata, $state); - assert(false); /* Should not return. */ - case 'saml20-idp-remote': - $this->startSSO2($idpMetadata, $state); - assert(false); /* Should not return. */ - default: - /* Should only be one of the known types. */ - assert(false); - } - } - - - /** - * Start an IdP discovery service operation. - * - * @param array $state The state array. - */ - private function startDisco(array $state) { - - $id = SimpleSAML_Auth_State::saveState($state, 'saml:sp:sso'); - - $discoURL = $this->discoURL; - if ($discoURL === NULL) { - /* Fallback to internal discovery service. */ - $discoURL = SimpleSAML\Module::getModuleURL('saml/disco.php'); - } - - $returnTo = SimpleSAML\Module::getModuleURL('saml/sp/discoresp.php', array('AuthID' => $id)); - - $params = array( - 'entityID' => $this->entityId, - 'return' => $returnTo, - 'returnIDParam' => 'idpentityid' - ); - - if(isset($state['saml:IDPList'])) { - $params['IDPList'] = $state['saml:IDPList']; - } - - if (isset($state['isPassive']) && $state['isPassive']) { - $params['isPassive'] = 'true'; - } - - \SimpleSAML\Utils\HTTP::redirectTrustedURL($discoURL, $params); - } - - - /** - * Start login. - * - * This function saves the information about the login, and redirects to the IdP. - * - * @param array &$state Information about the current authentication. - */ - public function authenticate(&$state) { - assert(is_array($state)); - - /* We are going to need the authId in order to retrieve this authentication source later. */ - $state['saml:sp:AuthId'] = $this->authId; - - $idp = $this->idp; - - if (isset($state['saml:idp'])) { - $idp = (string)$state['saml:idp']; - } - - if (isset($state['saml:IDPList']) && sizeof($state['saml:IDPList']) > 0) { - // we have a SAML IDPList (we are a proxy): filter the list of IdPs available - $mdh = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); - $known_idps = $mdh->getList(); - $intersection = array_intersect($state['saml:IDPList'], array_keys($known_idps)); - - if (empty($intersection)) { // all requested IdPs are unknown - throw new SimpleSAML\Module\saml\Error\NoSupportedIDP( - \SAML2\Constants::STATUS_REQUESTER, - 'None of the IdPs requested are supported by this proxy.' - ); - } - - if (!is_null($idp) && !in_array($idp, $intersection, true)) { // the IdP is enforced but not in the IDPList - throw new SimpleSAML\Module\saml\Error\NoAvailableIDP( - \SAML2\Constants::STATUS_REQUESTER, - 'None of the IdPs requested are available to this proxy.' - ); - } - - if (is_null($idp) && sizeof($intersection) === 1) { // only one IdP requested or valid - $idp = current($state['saml:IDPList']); - } - } - - if ($idp === null) { - $this->startDisco($state); - assert(false); - } - - $this->startSSO($idp, $state); - assert(false); - } - - - /** - * Re-authenticate an user. - * - * This function is called by the IdP to give the authentication source a chance to - * interact with the user even in the case when the user is already authenticated. - * - * @param array &$state Information about the current authentication. - */ - public function reauthenticate(array &$state) { - assert(is_array($state)); - - $session = SimpleSAML_Session::getSessionFromRequest(); - $data = $session->getAuthState($this->authId); - foreach ($data as $k => $v) { - $state[$k] = $v; - } - - // check if we have an IDPList specified in the request - if (isset($state['saml:IDPList']) && sizeof($state['saml:IDPList']) > 0 && - !in_array($state['saml:sp:IdP'], $state['saml:IDPList'], true)) - { - /* - * The user has an existing, valid session. However, the SP provided a list of IdPs it accepts for - * authentication, and the IdP the existing session is related to is not in that list. - * - * First, check if we recognize any of the IdPs requested. - */ - - $mdh = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); - $known_idps = $mdh->getList(); - $intersection = array_intersect($state['saml:IDPList'], array_keys($known_idps)); - - if (empty($intersection)) { // all requested IdPs are unknown - throw new SimpleSAML\Module\saml\Error\NoSupportedIDP( - \SAML2\Constants::STATUS_REQUESTER, - 'None of the IdPs requested are supported by this proxy.' - ); - } - - /* - * We have at least one IdP in the IDPList that we recognize, and it's not the one currently in use. Let's - * see if this proxy enforces the use of one single IdP. - */ - if (!is_null($this->idp) && !in_array($this->idp, $intersection, true)) { // an IdP is enforced but not requested - throw new SimpleSAML\Module\saml\Error\NoAvailableIDP( - \SAML2\Constants::STATUS_REQUESTER, - 'None of the IdPs requested are available to this proxy.' - ); - } - - /* - * We need to inform the user, and ask whether we should logout before starting the authentication process - * again with a different IdP, or cancel the current SSO attempt. - */ - SimpleSAML\Logger::warning( - "Reauthentication after logout is needed. The IdP '${state['saml:sp:IdP']}' is not in the IDPList ". - "provided by the Service Provider '${state['core:SP']}'." - ); - - $state['saml:sp:IdPMetadata'] = $this->getIdPMetadata($state['saml:sp:IdP']); - $state['saml:sp:AuthId'] = $this->authId; - self::askForIdPChange($state); - } - } - - - /** - * Ask the user to log out before being able to log in again with a different identity provider. Note that this - * method is intended for instances of SimpleSAMLphp running as a SAML proxy, and therefore acting both as an SP - * and an IdP at the same time. - * - * This method will never return. - * - * @param array $state The state array. The following keys must be defined in the array: - * - 'saml:sp:IdPMetadata': a SimpleSAML_Configuration object containing the metadata of the IdP that authenticated - * the user in the current session. - * - 'saml:sp:AuthId': the identifier of the current authentication source. - * - 'core:IdP': the identifier of the local IdP. - * - 'SPMetadata': an array with the metadata of this local SP. - * - * @throws SimpleSAML_Error_NoPassive In case the authentication request was passive. - */ - public static function askForIdPChange(array &$state) - { - assert(array_key_exists('saml:sp:IdPMetadata', $state)); - assert(array_key_exists('saml:sp:AuthId', $state)); - assert(array_key_exists('core:IdP', $state)); - assert(array_key_exists('SPMetadata', $state)); - - if (isset($state['isPassive']) && (bool)$state['isPassive']) { - // passive request, we cannot authenticate the user - throw new SimpleSAML\Module\saml\Error\NoPassive( - \SAML2\Constants::STATUS_REQUESTER, - 'Reauthentication required' + } + + /* Not found. */ + throw new SimpleSAML_Error_Exception('Could not find the metadata of an IdP with entity ID ' . + var_export($entityId, true)); + } + + /** + * Send a SAML1 SSO request to an IdP. + * + * @param SimpleSAML_Configuration $idpMetadata The metadata of the IdP. + * @param array $state The state array for the current authentication. + */ + private function startSSO1(SimpleSAML_Configuration $idpMetadata, array $state) + { + $idpEntityId = $idpMetadata->getString('entityid'); + + $state['saml:idp'] = $idpEntityId; + + $ar = new \SimpleSAML\XML\Shib13\AuthnRequest(); + $ar->setIssuer($this->entityId); + + $id = SimpleSAML_Auth_State::saveState($state, 'saml:sp:sso'); + $ar->setRelayState($id); + + $useArtifact = $idpMetadata->getBoolean('saml1.useartifact', null); + if ($useArtifact === null) { + $useArtifact = $this->metadata->getBoolean('saml1.useartifact', false); + } + + if ($useArtifact) { + $shire = SimpleSAML\Module::getModuleURL('saml/sp/saml1-acs.php/' . $this->authId . '/artifact'); + } else { + $shire = SimpleSAML\Module::getModuleURL('saml/sp/saml1-acs.php/' . $this->authId); + } + + $url = $ar->createRedirect($idpEntityId, $shire); + + SimpleSAML\Logger::debug('Starting SAML 1 SSO to ' . var_export($idpEntityId, true) . + ' from ' . var_export($this->entityId, true) . '.'); + \SimpleSAML\Utils\HTTP::redirectTrustedURL($url); + } + + /** + * Send a SAML2 SSO request to an IdP. + * + * @param SimpleSAML_Configuration $idpMetadata The metadata of the IdP. + * @param array $state The state array for the current authentication. + */ + private function startSSO2(SimpleSAML_Configuration $idpMetadata, array $state) + { + if (isset($state['saml:ProxyCount']) && $state['saml:ProxyCount'] < 0) { + SimpleSAML_Auth_State::throwException( + $state, + new \SimpleSAML\Module\saml\Error\ProxyCountExceeded(\SAML2\Constants::STATUS_RESPONDER) + ); + } + + $ar = sspmod_saml_Message::buildAuthnRequest($this->metadata, $idpMetadata); + + $ar->setAssertionConsumerServiceURL(SimpleSAML\Module::getModuleURL('saml/sp/saml2-acs.php/' . $this->authId)); + + if (isset($state['SimpleSAML_Auth_Source.ReturnURL'])) { + $ar->setRelayState($state['SimpleSAML_Auth_Source.ReturnURL']); + } + + if (isset($state['saml:AuthnContextClassRef'])) { + $accr = SimpleSAML\Utils\Arrays::arrayize($state['saml:AuthnContextClassRef']); + $comp = SAML2\Constants::COMPARISON_EXACT; + if (isset($state['saml:AuthnContextComparison']) && in_array($state['AuthnContextComparison'], array( + SAML2\Constants::COMPARISON_EXACT, + SAML2\Constants::COMPARISON_MINIMUM, + SAML2\Constants::COMPARISON_MAXIMUM, + SAML2\Constants::COMPARISON_BETTER, + ), true)) { + $comp = $state['saml:AuthnContextComparison']; + } + $ar->setRequestedAuthnContext(array('AuthnContextClassRef' => $accr, 'Comparison' => $comp)); + } + + if (isset($state['ForceAuthn'])) { + $ar->setForceAuthn((bool)$state['ForceAuthn']); + } + + if (isset($state['isPassive'])) { + $ar->setIsPassive((bool)$state['isPassive']); + } + + if (isset($state['saml:NameID'])) { + if (!is_array($state['saml:NameID']) && !is_a($state['saml:NameID'], '\SAML2\XML\saml\NameID')) { + throw new SimpleSAML_Error_Exception('Invalid value of $state[\'saml:NameID\'].'); + } + $ar->setNameId($state['saml:NameID']); + } + + if (isset($state['saml:NameIDPolicy'])) { + if (is_string($state['saml:NameIDPolicy'])) { + $policy = array( + 'Format' => (string)$state['saml:NameIDPolicy'], + 'AllowCreate' => true, + ); + } elseif (is_array($state['saml:NameIDPolicy'])) { + $policy = $state['saml:NameIDPolicy']; + } else { + throw new SimpleSAML_Error_Exception('Invalid value of $state[\'saml:NameIDPolicy\'].'); + } + $ar->setNameIdPolicy($policy); + } + + if (isset($state['saml:IDPList'])) { + $IDPList = $state['saml:IDPList']; + } else { + $IDPList = array(); + } + + $ar->setIDPList(array_unique(array_merge($this->metadata->getArray('IDPList', array()), + $idpMetadata->getArray('IDPList', array()), + (array) $IDPList))); + + if (isset($state['saml:ProxyCount']) && $state['saml:ProxyCount'] !== null) { + $ar->setProxyCount($state['saml:ProxyCount']); + } elseif ($idpMetadata->getInteger('ProxyCount', null) !== null) { + $ar->setProxyCount($idpMetadata->getInteger('ProxyCount', null)); + } elseif ($this->metadata->getInteger('ProxyCount', null) !== null) { + $ar->setProxyCount($this->metadata->getInteger('ProxyCount', null)); + } + + $requesterID = array(); + if (isset($state['saml:RequesterID'])) { + $requesterID = $state['saml:RequesterID']; + } + + if (isset($state['core:SP'])) { + $requesterID[] = $state['core:SP']; + } + + $ar->setRequesterID($requesterID); + + if (isset($state['saml:Extensions'])) { + $ar->setExtensions($state['saml:Extensions']); + } + + // save IdP entity ID as part of the state + $state['ExpectedIssuer'] = $idpMetadata->getString('entityid'); + + $id = SimpleSAML_Auth_State::saveState($state, 'saml:sp:sso', true); + $ar->setId($id); + + SimpleSAML\Logger::debug('Sending SAML 2 AuthnRequest to ' . + var_export($idpMetadata->getString('entityid'), true)); + + /* Select appropriate SSO endpoint */ + if ($ar->getProtocolBinding() === \SAML2\Constants::BINDING_HOK_SSO) { + $dst = $idpMetadata->getDefaultEndpoint('SingleSignOnService', array( + \SAML2\Constants::BINDING_HOK_SSO) + ); + } else { + $dst = $idpMetadata->getDefaultEndpoint('SingleSignOnService', array( + \SAML2\Constants::BINDING_HTTP_REDIRECT, + \SAML2\Constants::BINDING_HTTP_POST) + ); + } + $ar->setDestination($dst['Location']); + + $b = \SAML2\Binding::getBinding($dst['Binding']); + + $this->sendSAML2AuthnRequest($state, $b, $ar); + + assert(false); + } + + /** + * Function to actually send the authentication request. + * + * This function does not return. + * + * @param array &$state The state array. + * @param \SAML2\Binding $binding The binding. + * @param \SAML2\AuthnRequest $ar The authentication request. + */ + public function sendSAML2AuthnRequest(array &$state, \SAML2\Binding $binding, \SAML2\AuthnRequest $ar) + { + $binding->send($ar); + assert(false); + } + + /** + * Send a SSO request to an IdP. + * + * @param string $idp The entity ID of the IdP. + * @param array $state The state array for the current authentication. + */ + public function startSSO($idp, array $state) + { + assert(is_string($idp)); + + $idpMetadata = $this->getIdPMetadata($idp); + + $type = $idpMetadata->getString('metadata-set'); + switch ($type) { + case 'shib13-idp-remote': + $this->startSSO1($idpMetadata, $state); + assert(false); /* Should not return. */ + case 'saml20-idp-remote': + $this->startSSO2($idpMetadata, $state); + assert(false); /* Should not return. */ + default: + /* Should only be one of the known types. */ + assert(false); + } + } + + /** + * Start an IdP discovery service operation. + * + * @param array $state The state array. + */ + private function startDisco(array $state) + { + $id = SimpleSAML_Auth_State::saveState($state, 'saml:sp:sso'); + + $discoURL = $this->discoURL; + if ($discoURL === null) { + /* Fallback to internal discovery service. */ + $discoURL = SimpleSAML\Module::getModuleURL('saml/disco.php'); + } + + $returnTo = SimpleSAML\Module::getModuleURL('saml/sp/discoresp.php', array('AuthID' => $id)); + + $params = array( + 'entityID' => $this->entityId, + 'return' => $returnTo, + 'returnIDParam' => 'idpentityid' + ); + + if(isset($state['saml:IDPList'])) { + $params['IDPList'] = $state['saml:IDPList']; + } + + if (isset($state['isPassive']) && $state['isPassive']) { + $params['isPassive'] = 'true'; + } + + \SimpleSAML\Utils\HTTP::redirectTrustedURL($discoURL, $params); + } + + /** + * Start login. + * + * This function saves the information about the login, and redirects to the IdP. + * + * @param array &$state Information about the current authentication. + */ + public function authenticate(&$state) + { + assert(is_array($state)); + + /* We are going to need the authId in order to retrieve this authentication source later. */ + $state['saml:sp:AuthId'] = $this->authId; + + $idp = $this->idp; + + if (isset($state['saml:idp'])) { + $idp = (string)$state['saml:idp']; + } + + if (isset($state['saml:IDPList']) && sizeof($state['saml:IDPList']) > 0) { + // we have a SAML IDPList (we are a proxy): filter the list of IdPs available + $mdh = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); + $known_idps = $mdh->getList(); + $intersection = array_intersect($state['saml:IDPList'], array_keys($known_idps)); + + if (empty($intersection)) { + // all requested IdPs are unknown + throw new SimpleSAML\Module\saml\Error\NoSupportedIDP( + \SAML2\Constants::STATUS_REQUESTER, + 'None of the IdPs requested are supported by this proxy.' + ); + } + + if (!is_null($idp) && !in_array($idp, $intersection, true)) { + // the IdP is enforced but not in the IDPList + throw new SimpleSAML\Module\saml\Error\NoAvailableIDP( + \SAML2\Constants::STATUS_REQUESTER, + 'None of the IdPs requested are available to this proxy.' + ); + } + + if (is_null($idp) && sizeof($intersection) === 1) { + // only one IdP requested or valid + $idp = current($state['saml:IDPList']); + } + } + + if ($idp === null) { + $this->startDisco($state); + assert(false); + } + + $this->startSSO($idp, $state); + assert(false); + } + + /** + * Re-authenticate an user. + * + * This function is called by the IdP to give the authentication source a chance to + * interact with the user even in the case when the user is already authenticated. + * + * @param array &$state Information about the current authentication. + */ + public function reauthenticate(array &$state) + { + assert(is_array($state)); + + $session = SimpleSAML_Session::getSessionFromRequest(); + $data = $session->getAuthState($this->authId); + foreach ($data as $k => $v) { + $state[$k] = $v; + } + + // check if we have an IDPList specified in the request + if (isset($state['saml:IDPList']) && sizeof($state['saml:IDPList']) > 0 && + !in_array($state['saml:sp:IdP'], $state['saml:IDPList'], true)) + { + /* + * The user has an existing, valid session. However, the SP + * provided a list of IdPs it accepts for authentication, and + * the IdP the existing session is related to is not in that list. + * + * First, check if we recognize any of the IdPs requested. + */ + + $mdh = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); + $known_idps = $mdh->getList(); + $intersection = array_intersect($state['saml:IDPList'], array_keys($known_idps)); + + if (empty($intersection)) { + // all requested IdPs are unknown + throw new SimpleSAML\Module\saml\Error\NoSupportedIDP( + \SAML2\Constants::STATUS_REQUESTER, + 'None of the IdPs requested are supported by this proxy.' + ); + } + + /* + * We have at least one IdP in the IDPList that we recognize, and + * it's not the one currently in use. Let's see if this proxy + * enforces the use of one single IdP. + */ + if (!is_null($this->idp) && !in_array($this->idp, $intersection, true)) { + // an IdP is enforced but not requested + throw new SimpleSAML\Module\saml\Error\NoAvailableIDP( + \SAML2\Constants::STATUS_REQUESTER, + 'None of the IdPs requested are available to this proxy.' + ); + } + + /* + * We need to inform the user, and ask whether we should logout before + * starting the authentication process again with a different IdP, or + * cancel the current SSO attempt. + */ + SimpleSAML\Logger::warning( + "Reauthentication after logout is needed. The IdP '${state['saml:sp:IdP']}' is not in the IDPList ". + "provided by the Service Provider '${state['core:SP']}'." ); - } - - // save the state WITHOUT a restart URL, so that we don't try an IdP-initiated login if something goes wrong - $id = SimpleSAML_Auth_State::saveState($state, 'saml:proxy:invalid_idp', true); - $url = SimpleSAML\Module::getModuleURL('saml/proxy/invalid_session.php'); - SimpleSAML\Utils\HTTP::redirectTrustedURL($url, array('AuthState' => $id)); - assert(false); - } - - - /** - * Log the user out before logging in again. - * - * This method will never return. - * - * @param array $state The state array. - */ - public static function reauthLogout(array $state) - { - SimpleSAML\Logger::debug('Proxy: logging the user out before re-authentication.'); - - if (isset($state['Responder'])) { - $state['saml:proxy:reauthLogout:PrevResponder'] = $state['Responder']; - } - $state['Responder'] = array('sspmod_saml_Auth_Source_SP', 'reauthPostLogout'); - - $idp = SimpleSAML_IdP::getByState($state); - $idp->handleLogoutRequest($state, null); - assert(false); - } - - - /** - * Complete login operation after re-authenticating the user on another IdP. - * - * @param array $state The authentication state. - */ - public static function reauthPostLogin(array $state) { - assert(isset($state['ReturnCallback'])); - - // Update session state - $session = SimpleSAML_Session::getSessionFromRequest(); - $authId = $state['saml:sp:AuthId']; - $session->doLogin($authId, SimpleSAML_Auth_State::getPersistentAuthData($state)); - - // resume the login process - call_user_func($state['ReturnCallback'], $state); - assert(false); - } - - - /** - * Post-logout handler for re-authentication. - * - * This method will never return. - * - * @param SimpleSAML_IdP $idp The IdP we are logging out from. - * @param array &$state The state array with the state during logout. - */ - public static function reauthPostLogout(SimpleSAML_IdP $idp, array $state) { - assert(isset($state['saml:sp:AuthId'])); - - SimpleSAML\Logger::debug('Proxy: logout completed.'); - - if (isset($state['saml:proxy:reauthLogout:PrevResponder'])) { - $state['Responder'] = $state['saml:proxy:reauthLogout:PrevResponder']; - } - - $sp = SimpleSAML_Auth_Source::getById($state['saml:sp:AuthId'], 'sspmod_saml_Auth_Source_SP'); - /** @var sspmod_saml_Auth_Source_SP $authSource */ - SimpleSAML\Logger::debug('Proxy: logging in again.'); - $sp->authenticate($state); - assert(false); - } - - - /** - * Start a SAML 2 logout operation. - * - * @param array $state The logout state. - */ - public function startSLO2(&$state) { - assert(is_array($state)); - assert(array_key_exists('saml:logout:IdP', $state)); - assert(array_key_exists('saml:logout:NameID', $state)); - assert(array_key_exists('saml:logout:SessionIndex', $state)); - - $id = SimpleSAML_Auth_State::saveState($state, 'saml:slosent'); - - $idp = $state['saml:logout:IdP']; - $nameId = $state['saml:logout:NameID']; - $sessionIndex = $state['saml:logout:SessionIndex']; - - $idpMetadata = $this->getIdPMetadata($idp); - - $endpoint = $idpMetadata->getEndpointPrioritizedByBinding('SingleLogoutService', array( - \SAML2\Constants::BINDING_HTTP_REDIRECT, - \SAML2\Constants::BINDING_HTTP_POST), FALSE); - if ($endpoint === FALSE) { - SimpleSAML\Logger::info('No logout endpoint for IdP ' . var_export($idp, TRUE) . '.'); - return; - } - - $lr = sspmod_saml_Message::buildLogoutRequest($this->metadata, $idpMetadata); - $lr->setNameId($nameId); - $lr->setSessionIndex($sessionIndex); - $lr->setRelayState($id); - $lr->setDestination($endpoint['Location']); - - $encryptNameId = $idpMetadata->getBoolean('nameid.encryption', NULL); - if ($encryptNameId === NULL) { - $encryptNameId = $this->metadata->getBoolean('nameid.encryption', FALSE); - } - if ($encryptNameId) { - $lr->encryptNameId(sspmod_saml_Message::getEncryptionKey($idpMetadata)); - } - - $b = \SAML2\Binding::getBinding($endpoint['Binding']); - $b->send($lr); - - assert(false); - } - - - /** - * Start logout operation. - * - * @param array $state The logout state. - */ - public function logout(&$state) { - assert(is_array($state)); - assert(array_key_exists('saml:logout:Type', $state)); - - $logoutType = $state['saml:logout:Type']; - switch ($logoutType) { - case 'saml1': - /* Nothing to do. */ - return; - case 'saml2': - $this->startSLO2($state); - return; - default: - /* Should never happen. */ - assert(false); - } - } - - - /** - * Handle a response from a SSO operation. - * - * @param array $state The authentication state. - * @param string $idp The entity id of the IdP. - * @param array $attributes The attributes. - */ - public function handleResponse(array $state, $idp, array $attributes) { - assert(is_string($idp)); - assert(array_key_exists('LogoutState', $state)); - assert(array_key_exists('saml:logout:Type', $state['LogoutState'])); - - $idpMetadata = $this->getIdpMetadata($idp); - - $spMetadataArray = $this->metadata->toArray(); - $idpMetadataArray = $idpMetadata->toArray(); - - /* Save the IdP in the state array. */ - $state['saml:sp:IdP'] = $idp; - $state['PersistentAuthData'][] = 'saml:sp:IdP'; - - $authProcState = array( - 'saml:sp:IdP' => $idp, - 'saml:sp:State' => $state, - 'ReturnCall' => array('sspmod_saml_Auth_Source_SP', 'onProcessingCompleted'), - - 'Attributes' => $attributes, - 'Destination' => $spMetadataArray, - 'Source' => $idpMetadataArray, - ); - - if (isset($state['saml:sp:NameID'])) { - $authProcState['saml:sp:NameID'] = $state['saml:sp:NameID']; - } - if (isset($state['saml:sp:SessionIndex'])) { - $authProcState['saml:sp:SessionIndex'] = $state['saml:sp:SessionIndex']; - } - - $pc = new SimpleSAML_Auth_ProcessingChain($idpMetadataArray, $spMetadataArray, 'sp'); - $pc->processState($authProcState); - - self::onProcessingCompleted($authProcState); - } - - - /** - * Handle a logout request from an IdP. - * - * @param string $idpEntityId The entity ID of the IdP. - */ - public function handleLogout($idpEntityId) { - assert(is_string($idpEntityId)); - - /* Call the logout callback we registered in onProcessingCompleted(). */ - $this->callLogoutCallback($idpEntityId); - } - - - /** - * Handle an unsolicited login operations. - * - * This method creates a session from the information received. It will then redirect to the given URL. This is used - * to handle IdP initiated SSO. This method will never return. - * - * @param string $authId The id of the authentication source that received the request. - * @param array $state A state array. - * @param string $redirectTo The URL we should redirect the user to after updating the session. The function will - * check if the URL is allowed, so there is no need to manually check the URL on beforehand. Please refer to the - * 'trusted.url.domains' configuration directive for more information about allowing (or disallowing) URLs. - */ - public static function handleUnsolicitedAuth($authId, array $state, $redirectTo) { - assert(is_string($authId)); - assert(is_string($redirectTo)); - - $session = SimpleSAML_Session::getSessionFromRequest(); - $session->doLogin($authId, SimpleSAML_Auth_State::getPersistentAuthData($state)); - - \SimpleSAML\Utils\HTTP::redirectUntrustedURL($redirectTo); - } - - - /** - * Called when we have completed the procssing chain. - * - * @param array $authProcState The processing chain state. - */ - public static function onProcessingCompleted(array $authProcState) { - assert(array_key_exists('saml:sp:IdP', $authProcState)); - assert(array_key_exists('saml:sp:State', $authProcState)); - assert(array_key_exists('Attributes', $authProcState)); - - $idp = $authProcState['saml:sp:IdP']; - $state = $authProcState['saml:sp:State']; - - $sourceId = $state['saml:sp:AuthId']; - $source = SimpleSAML_Auth_Source::getById($sourceId); - if ($source === NULL) { - throw new Exception('Could not find authentication source with id ' . $sourceId); - } - - /* Register a callback that we can call if we receive a logout request from the IdP. */ - $source->addLogoutCallback($idp, $state); - - $state['Attributes'] = $authProcState['Attributes']; - - if (isset($state['saml:sp:isUnsolicited']) && (bool)$state['saml:sp:isUnsolicited']) { - if (!empty($state['saml:sp:RelayState'])) { - $redirectTo = $state['saml:sp:RelayState']; - } else { - $redirectTo = $source->getMetadata()->getString('RelayState', '/'); - } - self::handleUnsolicitedAuth($sourceId, $state, $redirectTo); - } - - SimpleSAML_Auth_Source::completeAuth($state); - } + $state['saml:sp:IdPMetadata'] = $this->getIdPMetadata($state['saml:sp:IdP']); + $state['saml:sp:AuthId'] = $this->authId; + self::askForIdPChange($state); + } + } + + + /** + * Ask the user to log out before being able to log in again with a + * different identity provider. Note that this method is intended for + * instances of SimpleSAMLphp running as a SAML proxy, and therefore + * acting both as an SP and an IdP at the same time. + * + * This method will never return. + * + * @param array $state The state array. + * The following keys must be defined in the array: + * - 'saml:sp:IdPMetadata': a SimpleSAML_Configuration object containing + * the metadata of the IdP that authenticated the user in the current + * session. + * - 'saml:sp:AuthId': the identifier of the current authentication source. + * - 'core:IdP': the identifier of the local IdP. + * - 'SPMetadata': an array with the metadata of this local SP. + * + * @throws SimpleSAML_Error_NoPassive In case the authentication request was passive. + */ + public static function askForIdPChange(array &$state) + { + assert(array_key_exists('saml:sp:IdPMetadata', $state)); + assert(array_key_exists('saml:sp:AuthId', $state)); + assert(array_key_exists('core:IdP', $state)); + assert(array_key_exists('SPMetadata', $state)); + + if (isset($state['isPassive']) && (bool)$state['isPassive']) { + // passive request, we cannot authenticate the user + throw new SimpleSAML\Module\saml\Error\NoPassive( + \SAML2\Constants::STATUS_REQUESTER, + 'Reauthentication required' + ); + } + + // save the state WITHOUT a restart URL, so that we don't try an IdP-initiated login if something goes wrong + $id = SimpleSAML_Auth_State::saveState($state, 'saml:proxy:invalid_idp', true); + $url = SimpleSAML\Module::getModuleURL('saml/proxy/invalid_session.php'); + SimpleSAML\Utils\HTTP::redirectTrustedURL($url, array('AuthState' => $id)); + assert(false); + } + + /** + * Log the user out before logging in again. + * + * This method will never return. + * + * @param array $state The state array. + */ + public static function reauthLogout(array $state) + { + SimpleSAML\Logger::debug('Proxy: logging the user out before re-authentication.'); + + if (isset($state['Responder'])) { + $state['saml:proxy:reauthLogout:PrevResponder'] = $state['Responder']; + } + $state['Responder'] = array('sspmod_saml_Auth_Source_SP', 'reauthPostLogout'); + + $idp = SimpleSAML_IdP::getByState($state); + $idp->handleLogoutRequest($state, null); + assert(false); + } + + /** + * Complete login operation after re-authenticating the user on another IdP. + * + * @param array $state The authentication state. + */ + public static function reauthPostLogin(array $state) + { + assert(isset($state['ReturnCallback'])); + + // Update session state + $session = SimpleSAML_Session::getSessionFromRequest(); + $authId = $state['saml:sp:AuthId']; + $session->doLogin($authId, SimpleSAML_Auth_State::getPersistentAuthData($state)); + + // resume the login process + call_user_func($state['ReturnCallback'], $state); + assert(false); + } + + /** + * Post-logout handler for re-authentication. + * + * This method will never return. + * + * @param SimpleSAML_IdP $idp The IdP we are logging out from. + * @param array &$state The state array with the state during logout. + */ + public static function reauthPostLogout(SimpleSAML_IdP $idp, array $state) + { + assert(isset($state['saml:sp:AuthId'])); + + SimpleSAML\Logger::debug('Proxy: logout completed.'); + + if (isset($state['saml:proxy:reauthLogout:PrevResponder'])) { + $state['Responder'] = $state['saml:proxy:reauthLogout:PrevResponder']; + } + + $sp = SimpleSAML_Auth_Source::getById($state['saml:sp:AuthId'], 'sspmod_saml_Auth_Source_SP'); + /** @var sspmod_saml_Auth_Source_SP $authSource */ + SimpleSAML\Logger::debug('Proxy: logging in again.'); + $sp->authenticate($state); + assert(false); + } + + /** + * Start a SAML 2 logout operation. + * + * @param array $state The logout state. + */ + public function startSLO2(&$state) + { + assert(is_array($state)); + assert(array_key_exists('saml:logout:IdP', $state)); + assert(array_key_exists('saml:logout:NameID', $state)); + assert(array_key_exists('saml:logout:SessionIndex', $state)); + + $id = SimpleSAML_Auth_State::saveState($state, 'saml:slosent'); + + $idp = $state['saml:logout:IdP']; + $nameId = $state['saml:logout:NameID']; + $sessionIndex = $state['saml:logout:SessionIndex']; + + $idpMetadata = $this->getIdPMetadata($idp); + + $endpoint = $idpMetadata->getEndpointPrioritizedByBinding('SingleLogoutService', array( + \SAML2\Constants::BINDING_HTTP_REDIRECT, + \SAML2\Constants::BINDING_HTTP_POST), false); + if ($endpoint === false) { + SimpleSAML\Logger::info('No logout endpoint for IdP ' . var_export($idp, true) . '.'); + return; + } + + $lr = sspmod_saml_Message::buildLogoutRequest($this->metadata, $idpMetadata); + $lr->setNameId($nameId); + $lr->setSessionIndex($sessionIndex); + $lr->setRelayState($id); + $lr->setDestination($endpoint['Location']); + + $encryptNameId = $idpMetadata->getBoolean('nameid.encryption', null); + if ($encryptNameId === null) { + $encryptNameId = $this->metadata->getBoolean('nameid.encryption', false); + } + if ($encryptNameId) { + $lr->encryptNameId(sspmod_saml_Message::getEncryptionKey($idpMetadata)); + } + + $b = \SAML2\Binding::getBinding($endpoint['Binding']); + $b->send($lr); + + assert(false); + } + + /** + * Start logout operation. + * + * @param array $state The logout state. + */ + public function logout(&$state) + { + assert(is_array($state)); + assert(array_key_exists('saml:logout:Type', $state)); + + $logoutType = $state['saml:logout:Type']; + switch ($logoutType) { + case 'saml1': + /* Nothing to do. */ + return; + case 'saml2': + $this->startSLO2($state); + return; + default: + /* Should never happen. */ + assert(false); + } + } + + /** + * Handle a response from a SSO operation. + * + * @param array $state The authentication state. + * @param string $idp The entity id of the IdP. + * @param array $attributes The attributes. + */ + public function handleResponse(array $state, $idp, array $attributes) + { + assert(is_string($idp)); + assert(array_key_exists('LogoutState', $state)); + assert(array_key_exists('saml:logout:Type', $state['LogoutState'])); + + $idpMetadata = $this->getIdpMetadata($idp); + + $spMetadataArray = $this->metadata->toArray(); + $idpMetadataArray = $idpMetadata->toArray(); + + /* Save the IdP in the state array. */ + $state['saml:sp:IdP'] = $idp; + $state['PersistentAuthData'][] = 'saml:sp:IdP'; + + $authProcState = array( + 'saml:sp:IdP' => $idp, + 'saml:sp:State' => $state, + 'ReturnCall' => array('sspmod_saml_Auth_Source_SP', 'onProcessingCompleted'), + + 'Attributes' => $attributes, + 'Destination' => $spMetadataArray, + 'Source' => $idpMetadataArray, + ); + + if (isset($state['saml:sp:NameID'])) { + $authProcState['saml:sp:NameID'] = $state['saml:sp:NameID']; + } + if (isset($state['saml:sp:SessionIndex'])) { + $authProcState['saml:sp:SessionIndex'] = $state['saml:sp:SessionIndex']; + } + + $pc = new SimpleSAML_Auth_ProcessingChain($idpMetadataArray, $spMetadataArray, 'sp'); + $pc->processState($authProcState); + + self::onProcessingCompleted($authProcState); + } + + /** + * Handle a logout request from an IdP. + * + * @param string $idpEntityId The entity ID of the IdP. + */ + public function handleLogout($idpEntityId) + { + assert(is_string($idpEntityId)); + + /* Call the logout callback we registered in onProcessingCompleted(). */ + $this->callLogoutCallback($idpEntityId); + } + + /** + * Handle an unsolicited login operations. + * + * This method creates a session from the information received. It will + * then redirect to the given URL. This is used to handle IdP initiated + * SSO. This method will never return. + * + * @param string $authId The id of the authentication source that received the request. + * @param array $state A state array. + * @param string $redirectTo The URL we should redirect the user to after updating + * the session. The function will check if the URL is allowed, so there is no need to + * manually check the URL on beforehand. Please refer to the 'trusted.url.domains' + * configuration directive for more information about allowing (or disallowing) URLs. + */ + public static function handleUnsolicitedAuth($authId, array $state, $redirectTo) + { + assert(is_string($authId)); + assert(is_string($redirectTo)); + + $session = SimpleSAML_Session::getSessionFromRequest(); + $session->doLogin($authId, SimpleSAML_Auth_State::getPersistentAuthData($state)); + + \SimpleSAML\Utils\HTTP::redirectUntrustedURL($redirectTo); + } + + /** + * Called when we have completed the procssing chain. + * + * @param array $authProcState The processing chain state. + */ + public static function onProcessingCompleted(array $authProcState) + { + assert(array_key_exists('saml:sp:IdP', $authProcState)); + assert(array_key_exists('saml:sp:State', $authProcState)); + assert(array_key_exists('Attributes', $authProcState)); + + $idp = $authProcState['saml:sp:IdP']; + $state = $authProcState['saml:sp:State']; + + $sourceId = $state['saml:sp:AuthId']; + $source = SimpleSAML_Auth_Source::getById($sourceId); + if ($source === null) { + throw new Exception('Could not find authentication source with id ' . $sourceId); + } + + /* Register a callback that we can call if we receive a logout request from the IdP. */ + $source->addLogoutCallback($idp, $state); + + $state['Attributes'] = $authProcState['Attributes']; + + if (isset($state['saml:sp:isUnsolicited']) && (bool)$state['saml:sp:isUnsolicited']) { + if (!empty($state['saml:sp:RelayState'])) { + $redirectTo = $state['saml:sp:RelayState']; + } else { + $redirectTo = $source->getMetadata()->getString('RelayState', '/'); + } + self::handleUnsolicitedAuth($sourceId, $state, $redirectTo); + } + + SimpleSAML_Auth_Source::completeAuth($state); + } } diff --git a/modules/saml/lib/BaseNameIDGenerator.php b/modules/saml/lib/BaseNameIDGenerator.php index 342c51d6619f78386fe8c96bdc1ef05bd0b1d35f..cea1c219688933cf1115bca0641a7f95dfe876f7 100644 --- a/modules/saml/lib/BaseNameIDGenerator.php +++ b/modules/saml/lib/BaseNameIDGenerator.php @@ -5,112 +5,114 @@ * * @package SimpleSAMLphp */ -abstract class sspmod_saml_BaseNameIDGenerator extends SimpleSAML_Auth_ProcessingFilter { - - /** - * What NameQualifier should be used. - * Can be one of: - * - a string: The qualifier to use. - * - FALSE: Do not include a NameQualifier. This is the default. - * - TRUE: Use the IdP entity ID. - * - * @var string|bool - */ - private $nameQualifier; - - - /** - * What SPNameQualifier should be used. - * Can be one of: - * - a string: The qualifier to use. - * - FALSE: Do not include a SPNameQualifier. - * - TRUE: Use the SP entity ID. This is the default. - * - * @var string|bool - */ - private $spNameQualifier; - - - /** - * The format of this NameID. - * - * This property must be initialized the subclass. - * - * @var string - */ - protected $format; - - - /** - * Initialize this filter, parse configuration. - * - * @param array $config Configuration information about this filter. - * @param mixed $reserved For future use. - */ - public function __construct($config, $reserved) { - parent::__construct($config, $reserved); - assert(is_array($config)); - - if (isset($config['NameQualifier'])) { - $this->nameQualifier = $config['NameQualifier']; - } else { - $this->nameQualifier = FALSE; - } - - if (isset($config['SPNameQualifier'])) { - $this->spNameQualifier = $config['SPNameQualifier']; - } else { - $this->spNameQualifier = TRUE; - } - } - - - /** - * Get the NameID value. - * - * @return string|NULL The NameID value. - */ - abstract protected function getValue(array &$state); - - - /** - * Generate transient NameID. - * - * @param array &$state The request state. - */ - public function process(&$state) { - assert(is_array($state)); - assert(is_string($this->format)); - - $value = $this->getValue($state); - if ($value === NULL) { - return; - } - - $nameId = new \SAML2\XML\saml\NameID(); - $nameId->value = $value; - - if ($this->nameQualifier === TRUE) { - if (isset($state['IdPMetadata']['entityid'])) { - $nameId->NameQualifier = $state['IdPMetadata']['entityid']; - } else { - SimpleSAML\Logger::warning('No IdP entity ID, unable to set NameQualifier.'); - } - } elseif (is_string($this->nameQualifier)) { - $nameId->NameQualifier = $this->nameQualifier; - } - - if ($this->spNameQualifier === TRUE) { - if (isset($state['SPMetadata']['entityid'])) { - $nameId->SPNameQualifier = $state['SPMetadata']['entityid']; - } else { - SimpleSAML\Logger::warning('No SP entity ID, unable to set SPNameQualifier.'); - } - } elseif (is_string($this->spNameQualifier)) { - $nameId->SPNameQualifier = $this->spNameQualifier; - } - - $state['saml:NameID'][$this->format] = $nameId; - } +abstract class sspmod_saml_BaseNameIDGenerator extends SimpleSAML_Auth_ProcessingFilter +{ + /** + * What NameQualifier should be used. + * Can be one of: + * - a string: The qualifier to use. + * - FALSE: Do not include a NameQualifier. This is the default. + * - TRUE: Use the IdP entity ID. + * + * @var string|bool + */ + private $nameQualifier; + + + /** + * What SPNameQualifier should be used. + * Can be one of: + * - a string: The qualifier to use. + * - FALSE: Do not include a SPNameQualifier. + * - TRUE: Use the SP entity ID. This is the default. + * + * @var string|bool + */ + private $spNameQualifier; + + + /** + * The format of this NameID. + * + * This property must be initialized the subclass. + * + * @var string + */ + protected $format; + + + /** + * Initialize this filter, parse configuration. + * + * @param array $config Configuration information about this filter. + * @param mixed $reserved For future use. + */ + public function __construct($config, $reserved) + { + parent::__construct($config, $reserved); + assert(is_array($config)); + + if (isset($config['NameQualifier'])) { + $this->nameQualifier = $config['NameQualifier']; + } else { + $this->nameQualifier = false; + } + + if (isset($config['SPNameQualifier'])) { + $this->spNameQualifier = $config['SPNameQualifier']; + } else { + $this->spNameQualifier = true; + } + } + + + /** + * Get the NameID value. + * + * @return string|null The NameID value. + */ + abstract protected function getValue(array &$state); + + + /** + * Generate transient NameID. + * + * @param array &$state The request state. + */ + public function process(&$state) + { + assert(is_array($state)); + assert(is_string($this->format)); + + $value = $this->getValue($state); + if ($value === null) { + return; + } + + $nameId = new \SAML2\XML\saml\NameID(); + $nameId->value = $value; + + if ($this->nameQualifier === true) { + if (isset($state['IdPMetadata']['entityid'])) { + $nameId->NameQualifier = $state['IdPMetadata']['entityid']; + } else { + SimpleSAML\Logger::warning('No IdP entity ID, unable to set NameQualifier.'); + } + } elseif (is_string($this->nameQualifier)) { + $nameId->NameQualifier = $this->nameQualifier; + } + + if ($this->spNameQualifier === true) { + if (isset($state['SPMetadata']['entityid'])) { + $nameId->SPNameQualifier = $state['SPMetadata']['entityid']; + } else { + SimpleSAML\Logger::warning('No SP entity ID, unable to set SPNameQualifier.'); + } + } elseif (is_string($this->spNameQualifier)) { + $nameId->SPNameQualifier = $this->spNameQualifier; + } + + $state['saml:NameID'][$this->format] = $nameId; + } } diff --git a/modules/saml/lib/Error.php b/modules/saml/lib/Error.php index 0a28d6b6108b3702247bf160470e0f54fa115d40..74f9ce873ccb57807172a36a0e8e2c18020425e1 100644 --- a/modules/saml/lib/Error.php +++ b/modules/saml/lib/Error.php @@ -7,190 +7,188 @@ */ class sspmod_saml_Error extends SimpleSAML_Error_Exception { - /** - * The top-level status code. - * - * @var string - */ - private $status; - - - /** - * The second-level status code, or NULL if no second-level status code is defined. - * - * @var string|NULL - */ - private $subStatus; - - - /** - * The status message, or NULL if no status message is defined. - * - * @var string|NULL - */ - private $statusMessage; - - - /** - * Create a SAML 2 error. - * - * @param string $status The top-level status code. - * @param string|NULL $subStatus The second-level status code. Can be NULL, in which case there is no second-level status code. - * @param string|NULL $statusMessage The status message. Can be NULL, in which case there is no status message. - * @param Exception|NULL $cause The cause of this exception. Can be NULL. - */ - public function __construct($status, $subStatus = null, $statusMessage = null, Exception $cause = null) + /** + * The top-level status code. + * + * @var string + */ + private $status; + + /** + * The second-level status code, or NULL if no second-level status code is defined. + * + * @var string|null + */ + private $subStatus; + + /** + * The status message, or NULL if no status message is defined. + * + * @var string|null + */ + private $statusMessage; + + + /** + * Create a SAML 2 error. + * + * @param string $status The top-level status code. + * @param string|null $subStatus The second-level status code. Can be NULL, in which case there is no second-level status code. + * @param string|null $statusMessage The status message. Can be NULL, in which case there is no status message. + * @param Exception|null $cause The cause of this exception. Can be NULL. + */ + public function __construct($status, $subStatus = null, $statusMessage = null, Exception $cause = null) { - assert(is_string($status)); - assert($subStatus === null || is_string($subStatus)); - assert($statusMessage === null || is_string($statusMessage)); - - $st = self::shortStatus($status); - if ($subStatus !== null) { - $st .= '/' . self::shortStatus($subStatus); - } - if ($statusMessage !== null) { - $st .= ': ' . $statusMessage; - } - parent::__construct($st, 0, $cause); - - $this->status = $status; - $this->subStatus = $subStatus; - $this->statusMessage = $statusMessage; - } - - - /** - * Get the top-level status code. - * - * @return string The top-level status code. - */ - public function getStatus() + assert(is_string($status)); + assert($subStatus === null || is_string($subStatus)); + assert($statusMessage === null || is_string($statusMessage)); + + $st = self::shortStatus($status); + if ($subStatus !== null) { + $st .= '/' . self::shortStatus($subStatus); + } + if ($statusMessage !== null) { + $st .= ': ' . $statusMessage; + } + parent::__construct($st, 0, $cause); + + $this->status = $status; + $this->subStatus = $subStatus; + $this->statusMessage = $statusMessage; + } + + + /** + * Get the top-level status code. + * + * @return string The top-level status code. + */ + public function getStatus() { - return $this->status; - } + return $this->status; + } - /** - * Get the second-level status code. - * - * @return string|NULL The second-level status code or NULL if no second-level status code is present. - */ - public function getSubStatus() + /** + * Get the second-level status code. + * + * @return string|null The second-level status code or NULL if no second-level status code is present. + */ + public function getSubStatus() { - return $this->subStatus; - } + return $this->subStatus; + } - /** - * Get the status message. - * - * @return string|NULL The status message or NULL if no status message is present. - */ - public function getStatusMessage() + /** + * Get the status message. + * + * @return string|null The status message or NULL if no status message is present. + */ + public function getStatusMessage() { - return $this->statusMessage; - } - - - /** - * Create a SAML2 error from an exception. - * - * This function attempts to create a SAML2 error with the appropriate - * status codes from an arbitrary exception. - * - * @param Exception $exception The original exception. - * @return sspmod_saml_Error The new exception. - */ - public static function fromException(Exception $exception) + return $this->statusMessage; + } + + + /** + * Create a SAML2 error from an exception. + * + * This function attempts to create a SAML2 error with the appropriate + * status codes from an arbitrary exception. + * + * @param Exception $exception The original exception. + * @return sspmod_saml_Error The new exception. + */ + public static function fromException(Exception $exception) { - if ($exception instanceof sspmod_saml_Error) { - // Return the original exception unchanged - return $exception; - - // TODO: remove this branch in 2.0 - } elseif ($exception instanceof SimpleSAML_Error_NoPassive) { - $e = new self( - \SAML2\Constants::STATUS_RESPONDER, - \SAML2\Constants::STATUS_NO_PASSIVE, - $exception->getMessage(), - $exception - ); - // TODO: remove this branch in 2.0 - } elseif ($exception instanceof SimpleSAML_Error_ProxyCountExceeded) { - $e = new self( - \SAML2\Constants::STATUS_RESPONDER, - \SAML2\Constants::STATUS_PROXY_COUNT_EXCEEDED, - $exception->getMessage(), - $exception - ); - } else { - $e = new self( - \SAML2\Constants::STATUS_RESPONDER, - null, - get_class($exception) . ': ' . $exception->getMessage(), - $exception - ); - } - - return $e; - } - - - /** - * Create a normal exception from a SAML2 error. - * - * This function attempts to reverse the operation of the fromException() function. - * If it is unable to create a more specific exception, it will return the current - * object. - * - * @see sspmod_saml_Error::fromException() - * - * @return SimpleSAML_Error_Exception An exception representing this error. - */ - public function toException() - { - $e = null; - - switch ($this->status) { - case \SAML2\Constants::STATUS_RESPONDER: - switch ($this->subStatus) { - case \SAML2\Constants::STATUS_NO_PASSIVE: - $e = new SimpleSAML\Module\saml\Error\NoPassive( - \SAML2\Constants::STATUS_RESPONDER, - $this->statusMessage + if ($exception instanceof sspmod_saml_Error) { + // Return the original exception unchanged + return $exception; + + // TODO: remove this branch in 2.0 + } elseif ($exception instanceof SimpleSAML_Error_NoPassive) { + $e = new self( + \SAML2\Constants::STATUS_RESPONDER, + \SAML2\Constants::STATUS_NO_PASSIVE, + $exception->getMessage(), + $exception + ); + // TODO: remove this branch in 2.0 + } elseif ($exception instanceof SimpleSAML_Error_ProxyCountExceeded) { + $e = new self( + \SAML2\Constants::STATUS_RESPONDER, + \SAML2\Constants::STATUS_PROXY_COUNT_EXCEEDED, + $exception->getMessage(), + $exception + ); + } else { + $e = new self( + \SAML2\Constants::STATUS_RESPONDER, + null, + get_class($exception) . ': ' . $exception->getMessage(), + $exception ); - break; - } - break; - } - - if ($e === null) { - return $this; - } - - return $e; - } - - - /** - * Create a short version of the status code. - * - * Remove the 'urn:oasis:names:tc:SAML:2.0:status:'-prefix of status codes - * if it is present. - * - * @param string $status The status code. - * @return string A shorter version of the status code. - */ - private static function shortStatus($status) + } + + return $e; + } + + + /** + * Create a normal exception from a SAML2 error. + * + * This function attempts to reverse the operation of the fromException() function. + * If it is unable to create a more specific exception, it will return the current + * object. + * + * @see sspmod_saml_Error::fromException() + * + * @return SimpleSAML_Error_Exception An exception representing this error. + */ + public function toException() + { + $e = null; + + switch ($this->status) { + case \SAML2\Constants::STATUS_RESPONDER: + switch ($this->subStatus) { + case \SAML2\Constants::STATUS_NO_PASSIVE: + $e = new SimpleSAML\Module\saml\Error\NoPassive( + \SAML2\Constants::STATUS_RESPONDER, + $this->statusMessage + ); + break; + } + break; + } + + if ($e === null) { + return $this; + } + + return $e; + } + + + /** + * Create a short version of the status code. + * + * Remove the 'urn:oasis:names:tc:SAML:2.0:status:'-prefix of status codes + * if it is present. + * + * @param string $status The status code. + * @return string A shorter version of the status code. + */ + private static function shortStatus($status) { - assert(is_string($status)); + assert(is_string($status)); - $t = 'urn:oasis:names:tc:SAML:2.0:status:'; - if (substr($status, 0, strlen($t)) === $t) { - return substr($status, strlen($t)); - } + $t = 'urn:oasis:names:tc:SAML:2.0:status:'; + if (substr($status, 0, strlen($t)) === $t) { + return substr($status, strlen($t)); + } - return $status; - } + return $status; + } } diff --git a/modules/saml/lib/IdP/SAML1.php b/modules/saml/lib/IdP/SAML1.php index 68fb13ed84fbbc944cdedc4f8869789beeab0503..fcd3603ce6354ceecb693f307eecc8a3d0ff740d 100644 --- a/modules/saml/lib/IdP/SAML1.php +++ b/modules/saml/lib/IdP/SAML1.php @@ -110,7 +110,7 @@ class sspmod_saml_IdP_SAML1 if (!$found) { throw new Exception('Invalid AssertionConsumerService for SP ' . var_export($spEntityId, true) . ': ' . var_export($shire, true)); - } + } SimpleSAML_Stats::log('saml:idp:AuthnRequest', array( 'spEntityID' => $spEntityId, diff --git a/modules/saml/lib/IdP/SAML2.php b/modules/saml/lib/IdP/SAML2.php index 30d95781daa371e5b9b0151b279ab283653b7fb1..9c6860585e63ecd24d834f960f43b5d035d1c0f4 100644 --- a/modules/saml/lib/IdP/SAML2.php +++ b/modules/saml/lib/IdP/SAML2.php @@ -443,7 +443,7 @@ class sspmod_saml_IdP_SAML2 public static function processSOAPAuthnRequest(array &$state) { if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW'])) { - SimpleSAML_Logger::error("ECP AuthnRequest did not contain Basic Authentication header"); + SimpleSAML\Logger::error("ECP AuthnRequest did not contain Basic Authentication header"); // TODO Throw some sort of ECP-specific exception / convert this to SOAP fault throw new SimpleSAML_Error_Error("WRONGUSERPASS"); } diff --git a/modules/saml/lib/SP/LogoutStore.php b/modules/saml/lib/SP/LogoutStore.php index a92aeadf3ee190b96a2291165f0d9e541853db85..d4a4004509d39e495155a118447325946422f326 100644 --- a/modules/saml/lib/SP/LogoutStore.php +++ b/modules/saml/lib/SP/LogoutStore.php @@ -7,297 +7,297 @@ */ class sspmod_saml_SP_LogoutStore { - /** - * Create logout table in SQL, if it is missing. - * - * @param \SimpleSAML\Store\SQL $store The datastore. - */ - private static function createLogoutTable(\SimpleSAML\Store\SQL $store) + /** + * Create logout table in SQL, if it is missing. + * + * @param \SimpleSAML\Store\SQL $store The datastore. + */ + private static function createLogoutTable(\SimpleSAML\Store\SQL $store) { - $tableVer = $store->getTableVersion('saml_LogoutStore'); - if ($tableVer === 2) { - return; - } elseif ($tableVer === 1) { - /* TableVersion 2 increased the column size to 255 which is the maximum length of a FQDN. */ - $query = 'ALTER TABLE ' . $store->prefix . '_saml_LogoutStore MODIFY _authSource VARCHAR(255) NOT NULL'; - try { - $store->pdo->exec($query); - } catch (Exception $e) { - SimpleSAML\Logger::warning($store->pdo->errorInfo()); - return; - } - $store->setTableVersion('saml_LogoutStore', 2); - return; - } - - $query = 'CREATE TABLE ' . $store->prefix . '_saml_LogoutStore ( - _authSource VARCHAR(255) NOT NULL, - _nameId VARCHAR(40) NOT NULL, - _sessionIndex VARCHAR(50) NOT NULL, - _expire TIMESTAMP NOT NULL, - _sessionId VARCHAR(50) NOT NULL, - UNIQUE (_authSource, _nameID, _sessionIndex) - )'; - $store->pdo->exec($query); - - $query = 'CREATE INDEX ' . $store->prefix . '_saml_LogoutStore_expire ON ' . $store->prefix . '_saml_LogoutStore (_expire)'; - $store->pdo->exec($query); - - $query = 'CREATE INDEX ' . $store->prefix . '_saml_LogoutStore_nameId ON ' . $store->prefix . '_saml_LogoutStore (_authSource, _nameId)'; - $store->pdo->exec($query); - - $store->setTableVersion('saml_LogoutStore', 2); - } - - - /** - * Clean the logout table of expired entries. - * - * @param \SimpleSAML\Store\SQL $store The datastore. - */ - private static function cleanLogoutStore(\SimpleSAML\Store\SQL $store) + $tableVer = $store->getTableVersion('saml_LogoutStore'); + if ($tableVer === 2) { + return; + } elseif ($tableVer === 1) { + /* TableVersion 2 increased the column size to 255 which is the maximum length of a FQDN. */ + $query = 'ALTER TABLE ' . $store->prefix . '_saml_LogoutStore MODIFY _authSource VARCHAR(255) NOT NULL'; + try { + $store->pdo->exec($query); + } catch (Exception $e) { + SimpleSAML\Logger::warning($store->pdo->errorInfo()); + return; + } + $store->setTableVersion('saml_LogoutStore', 2); + return; + } + + $query = 'CREATE TABLE ' . $store->prefix . '_saml_LogoutStore ( + _authSource VARCHAR(255) NOT NULL, + _nameId VARCHAR(40) NOT NULL, + _sessionIndex VARCHAR(50) NOT NULL, + _expire TIMESTAMP NOT NULL, + _sessionId VARCHAR(50) NOT NULL, + UNIQUE (_authSource, _nameID, _sessionIndex) + )'; + $store->pdo->exec($query); + + $query = 'CREATE INDEX ' . $store->prefix . '_saml_LogoutStore_expire ON ' . $store->prefix . '_saml_LogoutStore (_expire)'; + $store->pdo->exec($query); + + $query = 'CREATE INDEX ' . $store->prefix . '_saml_LogoutStore_nameId ON ' . $store->prefix . '_saml_LogoutStore (_authSource, _nameId)'; + $store->pdo->exec($query); + + $store->setTableVersion('saml_LogoutStore', 2); + } + + + /** + * Clean the logout table of expired entries. + * + * @param \SimpleSAML\Store\SQL $store The datastore. + */ + private static function cleanLogoutStore(\SimpleSAML\Store\SQL $store) { - SimpleSAML\Logger::debug('saml.LogoutStore: Cleaning logout store.'); + SimpleSAML\Logger::debug('saml.LogoutStore: Cleaning logout store.'); - $query = 'DELETE FROM ' . $store->prefix . '_saml_LogoutStore WHERE _expire < :now'; - $params = array('now' => gmdate('Y-m-d H:i:s')); + $query = 'DELETE FROM ' . $store->prefix . '_saml_LogoutStore WHERE _expire < :now'; + $params = array('now' => gmdate('Y-m-d H:i:s')); - $query = $store->pdo->prepare($query); - $query->execute($params); - } + $query = $store->pdo->prepare($query); + $query->execute($params); + } - /** - * Register a session in the SQL datastore. - * - * @param \SimpleSAML\Store\SQL $store The datastore. - * @param string $authId The authsource ID. - * @param string $nameId The hash of the users NameID. - * @param string $sessionIndex The SessionIndex of the user. - */ - private static function addSessionSQL(\SimpleSAML\Store\SQL $store, $authId, $nameId, $sessionIndex, $expire, $sessionId) + /** + * Register a session in the SQL datastore. + * + * @param \SimpleSAML\Store\SQL $store The datastore. + * @param string $authId The authsource ID. + * @param string $nameId The hash of the users NameID. + * @param string $sessionIndex The SessionIndex of the user. + */ + private static function addSessionSQL(\SimpleSAML\Store\SQL $store, $authId, $nameId, $sessionIndex, $expire, $sessionId) { - assert(is_string($authId)); - assert(is_string($nameId)); - assert(is_string($sessionIndex)); - assert(is_string($sessionId)); - assert(is_int($expire)); - - self::createLogoutTable($store); - - if (rand(0, 1000) < 10) { - self::cleanLogoutStore($store); - } - - $data = array( - '_authSource' => $authId, - '_nameId' => $nameId, - '_sessionIndex' => $sessionIndex, - '_expire' => gmdate('Y-m-d H:i:s', $expire), - '_sessionId' => $sessionId, - ); - $store->insertOrUpdate($store->prefix . '_saml_LogoutStore', array('_authSource', '_nameId', '_sessionIndex'), $data); - } - - - /** - * Retrieve sessions from the SQL datastore. - * - * @param \SimpleSAML\Store\SQL $store The datastore. - * @param string $authId The authsource ID. - * @param string $nameId The hash of the users NameID. - * @return array Associative array of SessionIndex => SessionId. - */ - private static function getSessionsSQL(\SimpleSAML\Store\SQL $store, $authId, $nameId) + assert(is_string($authId)); + assert(is_string($nameId)); + assert(is_string($sessionIndex)); + assert(is_string($sessionId)); + assert(is_int($expire)); + + self::createLogoutTable($store); + + if (rand(0, 1000) < 10) { + self::cleanLogoutStore($store); + } + + $data = array( + '_authSource' => $authId, + '_nameId' => $nameId, + '_sessionIndex' => $sessionIndex, + '_expire' => gmdate('Y-m-d H:i:s', $expire), + '_sessionId' => $sessionId, + ); + $store->insertOrUpdate($store->prefix . '_saml_LogoutStore', array('_authSource', '_nameId', '_sessionIndex'), $data); + } + + + /** + * Retrieve sessions from the SQL datastore. + * + * @param \SimpleSAML\Store\SQL $store The datastore. + * @param string $authId The authsource ID. + * @param string $nameId The hash of the users NameID. + * @return array Associative array of SessionIndex => SessionId. + */ + private static function getSessionsSQL(\SimpleSAML\Store\SQL $store, $authId, $nameId) { - assert(is_string($authId)); - assert(is_string($nameId)); - - self::createLogoutTable($store); - - $params = array( - '_authSource' => $authId, - '_nameId' => $nameId, - 'now' => gmdate('Y-m-d H:i:s'), - ); - - // We request the columns in lowercase in order to be compatible with PostgreSQL - $query = 'SELECT _sessionIndex AS _sessionindex, _sessionId AS _sessionid FROM ' . $store->prefix . '_saml_LogoutStore' . - ' WHERE _authSource = :_authSource AND _nameId = :_nameId AND _expire >= :now'; - $query = $store->pdo->prepare($query); - $query->execute($params); - - $res = array(); - while ( ($row = $query->fetch(PDO::FETCH_ASSOC)) !== false) { - $res[$row['_sessionindex']] = $row['_sessionid']; - } - - return $res; - } - - - /** - * Retrieve all session IDs from a key-value store. - * - * @param \SimpleSAML\Store $store The datastore. - * @param string $authId The authsource ID. - * @param string $nameId The hash of the users NameID. - * @param array $sessionIndexes The session indexes. - * @return array Associative array of SessionIndex => SessionId. - */ - private static function getSessionsStore(\SimpleSAML\Store $store, $authId, $nameId, array $sessionIndexes) + assert(is_string($authId)); + assert(is_string($nameId)); + + self::createLogoutTable($store); + + $params = array( + '_authSource' => $authId, + '_nameId' => $nameId, + 'now' => gmdate('Y-m-d H:i:s'), + ); + + // We request the columns in lowercase in order to be compatible with PostgreSQL + $query = 'SELECT _sessionIndex AS _sessionindex, _sessionId AS _sessionid FROM ' . $store->prefix . '_saml_LogoutStore' . + ' WHERE _authSource = :_authSource AND _nameId = :_nameId AND _expire >= :now'; + $query = $store->pdo->prepare($query); + $query->execute($params); + + $res = array(); + while ( ($row = $query->fetch(PDO::FETCH_ASSOC)) !== false) { + $res[$row['_sessionindex']] = $row['_sessionid']; + } + + return $res; + } + + + /** + * Retrieve all session IDs from a key-value store. + * + * @param \SimpleSAML\Store $store The datastore. + * @param string $authId The authsource ID. + * @param string $nameId The hash of the users NameID. + * @param array $sessionIndexes The session indexes. + * @return array Associative array of SessionIndex => SessionId. + */ + private static function getSessionsStore(\SimpleSAML\Store $store, $authId, $nameId, array $sessionIndexes) { - assert(is_string($authId)); - assert(is_string($nameId)); - - $res = array(); - foreach ($sessionIndexes as $sessionIndex) { - $sessionId = $store->get('saml.LogoutStore', $nameId . ':' . $sessionIndex); - if ($sessionId === null) { - continue; - } - assert(is_string($sessionId)); - $res[$sessionIndex] = $sessionId; - } - - return $res; - } - - - /** - * Register a new session in the datastore. - * - * Please observe the change of the signature in this method. Previously, the second parameter ($nameId) was forced - * to be an array. However, it has no type restriction now, and the documentation states it must be a - * \SAML2\XML\saml\NameID object. Currently, this function still accepts an array passed as $nameId, and will - * silently convert it to a \SAML2\XML\saml\NameID object. This is done to keep backwards-compatibility, though will - * no longer be possible in the future as the $nameId parameter will be required to be an object. - * - * @param string $authId The authsource ID. - * @param \SAML2\XML\saml\NameID $nameId The NameID of the user. - * @param string|NULL $sessionIndex The SessionIndex of the user. - */ - public static function addSession($authId, $nameId, $sessionIndex, $expire) + assert(is_string($authId)); + assert(is_string($nameId)); + + $res = array(); + foreach ($sessionIndexes as $sessionIndex) { + $sessionId = $store->get('saml.LogoutStore', $nameId . ':' . $sessionIndex); + if ($sessionId === null) { + continue; + } + assert(is_string($sessionId)); + $res[$sessionIndex] = $sessionId; + } + + return $res; + } + + + /** + * Register a new session in the datastore. + * + * Please observe the change of the signature in this method. Previously, the second parameter ($nameId) was forced + * to be an array. However, it has no type restriction now, and the documentation states it must be a + * \SAML2\XML\saml\NameID object. Currently, this function still accepts an array passed as $nameId, and will + * silently convert it to a \SAML2\XML\saml\NameID object. This is done to keep backwards-compatibility, though will + * no longer be possible in the future as the $nameId parameter will be required to be an object. + * + * @param string $authId The authsource ID. + * @param \SAML2\XML\saml\NameID $nameId The NameID of the user. + * @param string|null $sessionIndex The SessionIndex of the user. + */ + public static function addSession($authId, $nameId, $sessionIndex, $expire) { - assert(is_string($authId)); - assert(is_string($sessionIndex) || $sessionIndex === null); - assert(is_int($expire)); - - if ($sessionIndex === null) { - /* This IdP apparently did not include a SessionIndex, and thus probably does not - * support SLO. We still want to add the session to the data store just in case - * it supports SLO, but we don't want an LogoutRequest with a specific - * SessionIndex to match this session. We therefore generate our own session index. - */ - $sessionIndex = SimpleSAML\Utils\Random::generateID(); - } - - $store = \SimpleSAML\Store::getInstance(); - if ($store === false) { - // We don't have a datastore. - return; - } - - // serialize and anonymize the NameID + assert(is_string($authId)); + assert(is_string($sessionIndex) || $sessionIndex === null); + assert(is_int($expire)); + + if ($sessionIndex === null) { + /* This IdP apparently did not include a SessionIndex, and thus probably does not + * support SLO. We still want to add the session to the data store just in case + * it supports SLO, but we don't want an LogoutRequest with a specific + * SessionIndex to match this session. We therefore generate our own session index. + */ + $sessionIndex = SimpleSAML\Utils\Random::generateID(); + } + + $store = \SimpleSAML\Store::getInstance(); + if ($store === false) { + // We don't have a datastore. + return; + } + + // serialize and anonymize the NameID // TODO: remove this conditional statement - if (is_array($nameId)) { - $nameId = \SAML2\XML\saml\NameID::fromArray($nameId); - } - $strNameId = serialize($nameId); - $strNameId = sha1($strNameId); - - /* Normalize SessionIndex. */ - if (strlen($sessionIndex) > 50) { - $sessionIndex = sha1($sessionIndex); - } - - $session = SimpleSAML_Session::getSessionFromRequest(); - $sessionId = $session->getSessionId(); - - if ($store instanceof \SimpleSAML\Store\SQL) { - self::addSessionSQL($store, $authId, $strNameId, $sessionIndex, $expire, $sessionId); - } else { - $store->set('saml.LogoutStore', $strNameId . ':' . $sessionIndex, $sessionId, $expire); - } - } - - - /** - * Log out of the given sessions. - * - * @param string $authId The authsource ID. - * @param \SAML2\XML\saml\NameID $nameId The NameID of the user. - * @param array $sessionIndexes The SessionIndexes we should log out of. Logs out of all if this is empty. - * @returns int|FALSE Number of sessions logged out, or FALSE if not supported. - */ - public static function logoutSessions($authId, $nameId, array $sessionIndexes) + if (is_array($nameId)) { + $nameId = \SAML2\XML\saml\NameID::fromArray($nameId); + } + $strNameId = serialize($nameId); + $strNameId = sha1($strNameId); + + /* Normalize SessionIndex. */ + if (strlen($sessionIndex) > 50) { + $sessionIndex = sha1($sessionIndex); + } + + $session = SimpleSAML_Session::getSessionFromRequest(); + $sessionId = $session->getSessionId(); + + if ($store instanceof \SimpleSAML\Store\SQL) { + self::addSessionSQL($store, $authId, $strNameId, $sessionIndex, $expire, $sessionId); + } else { + $store->set('saml.LogoutStore', $strNameId . ':' . $sessionIndex, $sessionId, $expire); + } + } + + + /** + * Log out of the given sessions. + * + * @param string $authId The authsource ID. + * @param \SAML2\XML\saml\NameID $nameId The NameID of the user. + * @param array $sessionIndexes The SessionIndexes we should log out of. Logs out of all if this is empty. + * @returns int|false Number of sessions logged out, or FALSE if not supported. + */ + public static function logoutSessions($authId, $nameId, array $sessionIndexes) { - assert(is_string($authId)); - - $store = \SimpleSAML\Store::getInstance(); - if ($store === false) { - /* We don't have a datastore. */ - return false; - } - - // serialize and anonymize the NameID - // TODO: remove this conditional statement - if (is_array($nameId)) { - $nameId = \SAML2\XML\saml\NameID::fromArray($nameId); - } - $strNameId = serialize($nameId); - $strNameId = sha1($strNameId); - - /* Normalize SessionIndexes. */ - foreach ($sessionIndexes as &$sessionIndex) { - assert(is_string($sessionIndex)); - if (strlen($sessionIndex) > 50) { - $sessionIndex = sha1($sessionIndex); - } - } - unset($sessionIndex); // Remove reference - - if ($store instanceof \SimpleSAML\Store\SQL) { - $sessions = self::getSessionsSQL($store, $authId, $strNameId); - } elseif (empty($sessionIndexes)) { - /* We cannot fetch all sessions without a SQL store. */ - return false; - } else { - /** @var \SimpleSAML\Store $sessions At this point the store cannot be false */ - $sessions = self::getSessionsStore($store, $authId, $strNameId, $sessionIndexes); - - } - - if (empty($sessionIndexes)) { - $sessionIndexes = array_keys($sessions); - } - - $numLoggedOut = 0; - foreach ($sessionIndexes as $sessionIndex) { - if (!isset($sessions[$sessionIndex])) { - SimpleSAML\Logger::info('saml.LogoutStore: Logout requested for unknown SessionIndex.'); - continue; - } - - $sessionId = $sessions[$sessionIndex]; - - $session = SimpleSAML_Session::getSession($sessionId); - if ($session === null) { - SimpleSAML\Logger::info('saml.LogoutStore: Skipping logout of missing session.'); - continue; - } - - if (!$session->isValid($authId)) { - SimpleSAML\Logger::info('saml.LogoutStore: Skipping logout of session because it isn\'t authenticated.'); - continue; - } - - SimpleSAML\Logger::info('saml.LogoutStore: Logging out of session with trackId [' . $session->getTrackID() . '].'); - $session->doLogout($authId); - $numLoggedOut += 1; - } - - return $numLoggedOut; - } + assert(is_string($authId)); + + $store = \SimpleSAML\Store::getInstance(); + if ($store === false) { + /* We don't have a datastore. */ + return false; + } + + // serialize and anonymize the NameID + // TODO: remove this conditional statement + if (is_array($nameId)) { + $nameId = \SAML2\XML\saml\NameID::fromArray($nameId); + } + $strNameId = serialize($nameId); + $strNameId = sha1($strNameId); + + /* Normalize SessionIndexes. */ + foreach ($sessionIndexes as &$sessionIndex) { + assert(is_string($sessionIndex)); + if (strlen($sessionIndex) > 50) { + $sessionIndex = sha1($sessionIndex); + } + } + unset($sessionIndex); // Remove reference + + if ($store instanceof \SimpleSAML\Store\SQL) { + $sessions = self::getSessionsSQL($store, $authId, $strNameId); + } elseif (empty($sessionIndexes)) { + /* We cannot fetch all sessions without a SQL store. */ + return false; + } else { + /** @var \SimpleSAML\Store $sessions At this point the store cannot be false */ + $sessions = self::getSessionsStore($store, $authId, $strNameId, $sessionIndexes); + + } + + if (empty($sessionIndexes)) { + $sessionIndexes = array_keys($sessions); + } + + $numLoggedOut = 0; + foreach ($sessionIndexes as $sessionIndex) { + if (!isset($sessions[$sessionIndex])) { + SimpleSAML\Logger::info('saml.LogoutStore: Logout requested for unknown SessionIndex.'); + continue; + } + + $sessionId = $sessions[$sessionIndex]; + + $session = SimpleSAML_Session::getSession($sessionId); + if ($session === null) { + SimpleSAML\Logger::info('saml.LogoutStore: Skipping logout of missing session.'); + continue; + } + + if (!$session->isValid($authId)) { + SimpleSAML\Logger::info('saml.LogoutStore: Skipping logout of session because it isn\'t authenticated.'); + continue; + } + + SimpleSAML\Logger::info('saml.LogoutStore: Logging out of session with trackId [' . $session->getTrackID() . '].'); + $session->doLogout($authId); + $numLoggedOut += 1; + } + + return $numLoggedOut; + } } diff --git a/modules/saml/www/idp/certs.php b/modules/saml/www/idp/certs.php index a289d96a2cb473ddb5e8d6e3bd40469db50f5d19..adbfcc80dacfcb28703fc1a17cde41e3f4db76d2 100644 --- a/modules/saml/www/idp/certs.php +++ b/modules/saml/www/idp/certs.php @@ -4,8 +4,9 @@ $config = SimpleSAML_Configuration::getInstance(); $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); -if (!$config->getBoolean('enable.saml20-idp', false)) - throw new SimpleSAML_Error_Error('NOACCESS'); +if (!$config->getBoolean('enable.saml20-idp', false)) { + throw new SimpleSAML_Error_Error('NOACCESS'); +} // Check if valid local session exists.. if ($config->getBoolean('admin.protectmetadata', false)) { @@ -16,17 +17,17 @@ $idpentityid = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted'); $idpmeta = $metadata->getMetaDataConfig($idpentityid, 'saml20-idp-hosted'); switch($_SERVER['PATH_INFO']) { - case '/new_idp.crt': - $certInfo = SimpleSAML\Utils\Crypto::loadPublicKey($idpmeta, FALSE, 'new_'); - break; - case '/idp.crt': - $certInfo = SimpleSAML\Utils\Crypto::loadPublicKey($idpmeta, TRUE); - break; - case '/https.crt': - $certInfo = SimpleSAML\Utils\Crypto::loadPublicKey($idpmeta, TRUE, 'https.'); - break; - default: - throw new SimpleSAML_Error_NotFound('Unknown certificate.'); + case '/new_idp.crt': + $certInfo = SimpleSAML\Utils\Crypto::loadPublicKey($idpmeta, false, 'new_'); + break; + case '/idp.crt': + $certInfo = SimpleSAML\Utils\Crypto::loadPublicKey($idpmeta, true); + break; + case '/https.crt': + $certInfo = SimpleSAML\Utils\Crypto::loadPublicKey($idpmeta, true, 'https.'); + break; + default: + throw new SimpleSAML_Error_NotFound('Unknown certificate.'); } header('Content-Disposition: attachment; filename='.substr($_SERVER['PATH_INFO'], 1)); diff --git a/modules/saml/www/sp/discoresp.php b/modules/saml/www/sp/discoresp.php index db9bda9d5b92fc42f40831fda84c07459b0c1f47..cc55607b1e5b5318d0a8a3c6d4771d69c4549e43 100644 --- a/modules/saml/www/sp/discoresp.php +++ b/modules/saml/www/sp/discoresp.php @@ -5,11 +5,11 @@ */ if (!array_key_exists('AuthID', $_REQUEST)) { - throw new SimpleSAML_Error_BadRequest('Missing AuthID to discovery service response handler'); + throw new SimpleSAML_Error_BadRequest('Missing AuthID to discovery service response handler'); } if (!array_key_exists('idpentityid', $_REQUEST)) { - throw new SimpleSAML_Error_BadRequest('Missing idpentityid to discovery service response handler'); + throw new SimpleSAML_Error_BadRequest('Missing idpentityid to discovery service response handler'); } $state = SimpleSAML_Auth_State::loadState($_REQUEST['AuthID'], 'saml:sp:sso'); @@ -18,11 +18,11 @@ assert(array_key_exists('saml:sp:AuthId', $state)); $sourceId = $state['saml:sp:AuthId']; $source = SimpleSAML_Auth_Source::getById($sourceId); -if ($source === NULL) { - throw new Exception('Could not find authentication source with id ' . $sourceId); +if ($source === null) { + throw new Exception('Could not find authentication source with id ' . $sourceId); } if (!($source instanceof sspmod_saml_Auth_Source_SP)) { - throw new SimpleSAML_Error_Exception('Source type changed?'); + throw new SimpleSAML_Error_Exception('Source type changed?'); } $source->startSSO($_REQUEST['idpentityid'], $state); diff --git a/modules/saml/www/sp/saml1-acs.php b/modules/saml/www/sp/saml1-acs.php index fa1a0efb4e47803738899545578d87ec1dcba31f..830eb53cc711ea7140cfe889620ef938184f95b3 100644 --- a/modules/saml/www/sp/saml1-acs.php +++ b/modules/saml/www/sp/saml1-acs.php @@ -3,11 +3,11 @@ use SimpleSAML\Bindings\Shib13\Artifact; if (!array_key_exists('SAMLResponse', $_REQUEST) && !array_key_exists('SAMLart', $_REQUEST)) { - throw new SimpleSAML_Error_BadRequest('Missing SAMLResponse or SAMLart parameter.'); + throw new SimpleSAML_Error_BadRequest('Missing SAMLResponse or SAMLart parameter.'); } if (!array_key_exists('TARGET', $_REQUEST)) { - throw new SimpleSAML_Error_BadRequest('Missing TARGET parameter.'); + throw new SimpleSAML_Error_BadRequest('Missing TARGET parameter.'); } if (!array_key_exists('PATH_INFO', $_SERVER)) { @@ -16,8 +16,8 @@ if (!array_key_exists('PATH_INFO', $_SERVER)) { $sourceId = $_SERVER['PATH_INFO']; $end = strpos($sourceId, '/', 1); -if ($end === FALSE) { - $end = strlen($sourceId); +if ($end === false) { + $end = strlen($sourceId); } $sourceId = substr($sourceId, 1, $end - 1); @@ -28,41 +28,41 @@ SimpleSAML\Logger::debug('Received SAML1 response'); $target = (string)$_REQUEST['TARGET']; if (preg_match('@^https?://@i', $target)) { - // Unsolicited response - $state = array( - 'saml:sp:isUnsolicited' => TRUE, - 'saml:sp:AuthId' => $sourceId, - 'saml:sp:RelayState' => \SimpleSAML\Utils\HTTP::checkURLAllowed($target), - ); + // Unsolicited response + $state = array( + 'saml:sp:isUnsolicited' => true, + 'saml:sp:AuthId' => $sourceId, + 'saml:sp:RelayState' => \SimpleSAML\Utils\HTTP::checkURLAllowed($target), + ); } else { - $state = SimpleSAML_Auth_State::loadState($_REQUEST['TARGET'], 'saml:sp:sso'); + $state = SimpleSAML_Auth_State::loadState($_REQUEST['TARGET'], 'saml:sp:sso'); - // Check that the authentication source is correct. - assert(array_key_exists('saml:sp:AuthId', $state)); - if ($state['saml:sp:AuthId'] !== $sourceId) { - throw new SimpleSAML_Error_Exception('The authentication source id in the URL does not match the authentication source which sent the request.'); - } + // Check that the authentication source is correct. + assert(array_key_exists('saml:sp:AuthId', $state)); + if ($state['saml:sp:AuthId'] !== $sourceId) { + throw new SimpleSAML_Error_Exception('The authentication source id in the URL does not match the authentication source which sent the request.'); + } - assert(isset($state['saml:idp'])); + assert(isset($state['saml:idp'])); } $spMetadata = $source->getMetadata(); if (array_key_exists('SAMLart', $_REQUEST)) { - if (!isset($state['saml:idp'])) { - /* Unsolicited response. */ - throw new SimpleSAML_Error_Exception('IdP initiated authentication not supported with the SAML 1.1 SAMLart protocol.'); - } - $idpMetadata = $source->getIdPMetadata($state['saml:idp']); - - $responseXML = Artifact::receive($spMetadata, $idpMetadata); - $isValidated = TRUE; /* Artifact binding validated with ssl certificate. */ + if (!isset($state['saml:idp'])) { + /* Unsolicited response. */ + throw new SimpleSAML_Error_Exception('IdP initiated authentication not supported with the SAML 1.1 SAMLart protocol.'); + } + $idpMetadata = $source->getIdPMetadata($state['saml:idp']); + + $responseXML = Artifact::receive($spMetadata, $idpMetadata); + $isValidated = true; /* Artifact binding validated with ssl certificate. */ } elseif (array_key_exists('SAMLResponse', $_REQUEST)) { - $responseXML = $_REQUEST['SAMLResponse']; - $responseXML = base64_decode($responseXML); - $isValidated = FALSE; /* Must check signature on response. */ + $responseXML = $_REQUEST['SAMLResponse']; + $responseXML = base64_decode($responseXML); + $isValidated = false; /* Must check signature on response. */ } else { - assert(false); + assert(false); } $response = new \SimpleSAML\XML\Shib13\AuthnResponse(); @@ -75,12 +75,12 @@ $responseIssuer = $response->getIssuer(); $attributes = $response->getAttributes(); if (isset($state['saml:idp']) && $responseIssuer !== $state['saml:idp']) { - throw new SimpleSAML_Error_Exception('The issuer of the response wasn\'t the destination of the request.'); + throw new SimpleSAML_Error_Exception('The issuer of the response wasn\'t the destination of the request.'); } $logoutState = array( - 'saml:logout:Type' => 'saml1' - ); + 'saml:logout:Type' => 'saml1' + ); $state['LogoutState'] = $logoutState; $state['saml:sp:NameID'] = $response->getNameID(); diff --git a/modules/saml/www/sp/saml2-logout.php b/modules/saml/www/sp/saml2-logout.php index 6fa5a0081791bc2d3588095822fc4c9a8eecfe8c..88826f6555089d715c70342ec986b4272c2d0fb6 100644 --- a/modules/saml/www/sp/saml2-logout.php +++ b/modules/saml/www/sp/saml2-logout.php @@ -7,17 +7,17 @@ */ if (!array_key_exists('PATH_INFO', $_SERVER)) { - throw new SimpleSAML_Error_BadRequest('Missing authentication source ID in logout URL'); + throw new SimpleSAML_Error_BadRequest('Missing authentication source ID in logout URL'); } $sourceId = substr($_SERVER['PATH_INFO'], 1); $source = SimpleSAML_Auth_Source::getById($sourceId); -if ($source === NULL) { - throw new Exception('Could not find authentication source with id ' . $sourceId); +if ($source === null) { + throw new Exception('Could not find authentication source with id ' . $sourceId); } if (!($source instanceof sspmod_saml_Auth_Source_SP)) { - throw new SimpleSAML_Error_Exception('Source type changed?'); + throw new SimpleSAML_Error_Exception('Source type changed?'); } try { @@ -34,9 +34,9 @@ try { $message = $binding->receive(); $idpEntityId = $message->getIssuer(); -if ($idpEntityId === NULL) { - // Without an issuer we have no way to respond to the message. - throw new SimpleSAML_Error_BadRequest('Received message on logout endpoint without issuer.'); +if ($idpEntityId === null) { + // Without an issuer we have no way to respond to the message. + throw new SimpleSAML_Error_BadRequest('Received message on logout endpoint without issuer.'); } $spEntityId = $source->getEntityId(); @@ -48,93 +48,93 @@ $spMetadata = $source->getMetadata(); sspmod_saml_Message::validateMessage($idpMetadata, $spMetadata, $message); $destination = $message->getDestination(); -if ($destination !== NULL && $destination !== \SimpleSAML\Utils\HTTP::getSelfURLNoQuery()) { - throw new SimpleSAML_Error_Exception('Destination in logout message is wrong.'); +if ($destination !== null && $destination !== \SimpleSAML\Utils\HTTP::getSelfURLNoQuery()) { + throw new SimpleSAML_Error_Exception('Destination in logout message is wrong.'); } if ($message instanceof \SAML2\LogoutResponse) { - $relayState = $message->getRelayState(); - if ($relayState === NULL) { - // Somehow, our RelayState has been lost. - throw new SimpleSAML_Error_BadRequest('Missing RelayState in logout response.'); - } + $relayState = $message->getRelayState(); + if ($relayState === null) { + // Somehow, our RelayState has been lost. + throw new SimpleSAML_Error_BadRequest('Missing RelayState in logout response.'); + } - if (!$message->isSuccess()) { - SimpleSAML\Logger::warning('Unsuccessful logout. Status was: ' . sspmod_saml_Message::getResponseError($message)); - } + if (!$message->isSuccess()) { + SimpleSAML\Logger::warning('Unsuccessful logout. Status was: ' . sspmod_saml_Message::getResponseError($message)); + } - $state = SimpleSAML_Auth_State::loadState($relayState, 'saml:slosent'); - $state['saml:sp:LogoutStatus'] = $message->getStatus(); - SimpleSAML_Auth_Source::completeLogout($state); + $state = SimpleSAML_Auth_State::loadState($relayState, 'saml:slosent'); + $state['saml:sp:LogoutStatus'] = $message->getStatus(); + SimpleSAML_Auth_Source::completeLogout($state); } elseif ($message instanceof \SAML2\LogoutRequest) { - SimpleSAML\Logger::debug('module/saml2/sp/logout: Request from ' . $idpEntityId); - SimpleSAML\Logger::stats('saml20-idp-SLO idpinit ' . $spEntityId . ' ' . $idpEntityId); - - if ($message->isNameIdEncrypted()) { - try { - $keys = sspmod_saml_Message::getDecryptionKeys($idpMetadata, $spMetadata); - } catch (Exception $e) { - throw new SimpleSAML_Error_Exception('Error decrypting NameID: ' . $e->getMessage()); - } - - $blacklist = sspmod_saml_Message::getBlacklistedAlgorithms($idpMetadata, $spMetadata); - - $lastException = NULL; - foreach ($keys as $i => $key) { - try { - $message->decryptNameId($key, $blacklist); - SimpleSAML\Logger::debug('Decryption with key #' . $i . ' succeeded.'); - $lastException = NULL; - break; - } catch (Exception $e) { - SimpleSAML\Logger::debug('Decryption with key #' . $i . ' failed with exception: ' . $e->getMessage()); - $lastException = $e; - } - } - if ($lastException !== NULL) { - throw $lastException; - } - } - - $nameId = $message->getNameId(); - $sessionIndexes = $message->getSessionIndexes(); - - $numLoggedOut = sspmod_saml_SP_LogoutStore::logoutSessions($sourceId, $nameId, $sessionIndexes); - if ($numLoggedOut === FALSE) { - /* This type of logout was unsupported. Use the old method. */ - $source->handleLogout($idpEntityId); - $numLoggedOut = count($sessionIndexes); - } - - /* Create an send response. */ - $lr = sspmod_saml_Message::buildLogoutResponse($spMetadata, $idpMetadata); - $lr->setRelayState($message->getRelayState()); - $lr->setInResponseTo($message->getId()); - - if ($numLoggedOut < count($sessionIndexes)) { - SimpleSAML\Logger::warning('Logged out of ' . $numLoggedOut . ' of ' . count($sessionIndexes) . ' sessions.'); - } - - $dst = $idpMetadata->getEndpointPrioritizedByBinding('SingleLogoutService', array( - \SAML2\Constants::BINDING_HTTP_REDIRECT, - \SAML2\Constants::BINDING_HTTP_POST) - ); - - if (!$binding instanceof \SAML2\SOAP) { - $binding = \SAML2\Binding::getBinding($dst['Binding']); - if (isset($dst['ResponseLocation'])) { - $dst = $dst['ResponseLocation']; - } else { - $dst = $dst['Location']; - } - $binding->setDestination($dst); - } - $lr->setDestination($dst); - - $binding->send($lr); + SimpleSAML\Logger::debug('module/saml2/sp/logout: Request from ' . $idpEntityId); + SimpleSAML\Logger::stats('saml20-idp-SLO idpinit ' . $spEntityId . ' ' . $idpEntityId); + + if ($message->isNameIdEncrypted()) { + try { + $keys = sspmod_saml_Message::getDecryptionKeys($idpMetadata, $spMetadata); + } catch (Exception $e) { + throw new SimpleSAML_Error_Exception('Error decrypting NameID: ' . $e->getMessage()); + } + + $blacklist = sspmod_saml_Message::getBlacklistedAlgorithms($idpMetadata, $spMetadata); + + $lastException = null; + foreach ($keys as $i => $key) { + try { + $message->decryptNameId($key, $blacklist); + SimpleSAML\Logger::debug('Decryption with key #' . $i . ' succeeded.'); + $lastException = null; + break; + } catch (Exception $e) { + SimpleSAML\Logger::debug('Decryption with key #' . $i . ' failed with exception: ' . $e->getMessage()); + $lastException = $e; + } + } + if ($lastException !== null) { + throw $lastException; + } + } + + $nameId = $message->getNameId(); + $sessionIndexes = $message->getSessionIndexes(); + + $numLoggedOut = sspmod_saml_SP_LogoutStore::logoutSessions($sourceId, $nameId, $sessionIndexes); + if ($numLoggedOut === false) { + /* This type of logout was unsupported. Use the old method. */ + $source->handleLogout($idpEntityId); + $numLoggedOut = count($sessionIndexes); + } + + /* Create and send response. */ + $lr = sspmod_saml_Message::buildLogoutResponse($spMetadata, $idpMetadata); + $lr->setRelayState($message->getRelayState()); + $lr->setInResponseTo($message->getId()); + + if ($numLoggedOut < count($sessionIndexes)) { + SimpleSAML\Logger::warning('Logged out of ' . $numLoggedOut . ' of ' . count($sessionIndexes) . ' sessions.'); + } + + $dst = $idpMetadata->getEndpointPrioritizedByBinding('SingleLogoutService', array( + \SAML2\Constants::BINDING_HTTP_REDIRECT, + \SAML2\Constants::BINDING_HTTP_POST) + ); + + if (!$binding instanceof \SAML2\SOAP) { + $binding = \SAML2\Binding::getBinding($dst['Binding']); + if (isset($dst['ResponseLocation'])) { + $dst = $dst['ResponseLocation']; + } else { + $dst = $dst['Location']; + } + $binding->setDestination($dst); + } + $lr->setDestination($dst); + + $binding->send($lr); } else { - throw new SimpleSAML_Error_BadRequest('Unknown message received on logout endpoint: ' . get_class($message)); + throw new SimpleSAML_Error_BadRequest('Unknown message received on logout endpoint: ' . get_class($message)); } diff --git a/modules/sqlauth/lib/Auth/Source/SQL.php b/modules/sqlauth/lib/Auth/Source/SQL.php index a99f496ce03eeba41ed2ed36f6d9ab9e4b894cc9..67995ab63bdcc5ebe1adf0a8ed6c2440753e7355 100644 --- a/modules/sqlauth/lib/Auth/Source/SQL.php +++ b/modules/sqlauth/lib/Auth/Source/SQL.php @@ -11,180 +11,188 @@ class sspmod_sqlauth_Auth_Source_SQL extends sspmod_core_Auth_UserPassBase { - /** - * The DSN we should connect to. - */ - private $dsn; - - /** - * The username we should connect to the database with. - */ - private $username; - - /** - * The password we should connect to the database with. - */ - private $password; - - /** - * The query we should use to retrieve the attributes for the user. - * - * The username and password will be available as :username and :password. - */ - private $query; - - /** - * Constructor for this authentication source. - * - * @param array $info Information about this authentication source. - * @param array $config Configuration. - */ - public function __construct($info, $config) + /** + * The DSN we should connect to. + */ + private $dsn; + + /** + * The username we should connect to the database with. + */ + private $username; + + /** + * The password we should connect to the database with. + */ + private $password; + + /** + * The options that we should connect to the database with. + */ + private $options; + + /** + * The query we should use to retrieve the attributes for the user. + * + * The username and password will be available as :username and :password. + */ + private $query; + + /** + * Constructor for this authentication source. + * + * @param array $info Information about this authentication source. + * @param array $config Configuration. + */ + public function __construct($info, $config) { - assert(is_array($info)); - assert(is_array($config)); - - // Call the parent constructor first, as required by the interface - parent::__construct($info, $config); - - // Make sure that all required parameters are present. - foreach (array('dsn', 'username', 'password', 'query') as $param) { - if (!array_key_exists($param, $config)) { - throw new Exception('Missing required attribute \'' . $param . - '\' for authentication source ' . $this->authId); - } - - if (!is_string($config[$param])) { - throw new Exception('Expected parameter \'' . $param . - '\' for authentication source ' . $this->authId . - ' to be a string. Instead it was: ' . - var_export($config[$param], TRUE)); - } - } - - $this->dsn = $config['dsn']; - $this->username = $config['username']; - $this->password = $config['password']; - $this->query = $config['query']; - } - - - /** - * Create a database connection. - * - * @return PDO The database connection. - */ - private function connect() + assert(is_array($info)); + assert(is_array($config)); + + // Call the parent constructor first, as required by the interface + parent::__construct($info, $config); + + // Make sure that all required parameters are present. + foreach (array('dsn', 'username', 'password', 'query') as $param) { + if (!array_key_exists($param, $config)) { + throw new Exception('Missing required attribute \'' . $param . + '\' for authentication source ' . $this->authId); + } + + if (!is_string($config[$param])) { + throw new Exception('Expected parameter \'' . $param . + '\' for authentication source ' . $this->authId . + ' to be a string. Instead it was: ' . + var_export($config[$param], true)); + } + } + + $this->dsn = $config['dsn']; + $this->username = $config['username']; + $this->password = $config['password']; + $this->query = $config['query']; + if (isset($config['options'])) { + $this->options = $config['options']; + } + } + + + /** + * Create a database connection. + * + * @return PDO The database connection. + */ + private function connect() { - try { - $db = new PDO($this->dsn, $this->username, $this->password); - } catch (PDOException $e) { - throw new Exception('sqlauth:' . $this->authId . ': - Failed to connect to \'' . - $this->dsn . '\': '. $e->getMessage()); - } - - $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - - $driver = explode(':', $this->dsn, 2); - $driver = strtolower($driver[0]); - - /* Driver specific initialization. */ - switch ($driver) { - case 'mysql': - /* Use UTF-8. */ - $db->exec("SET NAMES 'utf8mb4'"); - break; - case 'pgsql': - /* Use UTF-8. */ - $db->exec("SET NAMES 'UTF8'"); - break; - } - - return $db; - } - - - /** - * Attempt to log in using the given username and password. - * - * On a successful login, this function should return the users attributes. On failure, - * it should throw an exception. If the error was caused by the user entering the wrong - * username or password, a SimpleSAML_Error_Error('WRONGUSERPASS') should be thrown. - * - * Note that both the username and the password are UTF-8 encoded. - * - * @param string $username The username the user wrote. - * @param string $password The password the user wrote. - * @return array Associative array with the users attributes. - */ - protected function login($username, $password) + try { + $db = new PDO($this->dsn, $this->username, $this->password, $this->options); + } catch (PDOException $e) { + throw new Exception('sqlauth:' . $this->authId . ': - Failed to connect to \'' . + $this->dsn . '\': '. $e->getMessage()); + } + + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $driver = explode(':', $this->dsn, 2); + $driver = strtolower($driver[0]); + + /* Driver specific initialization. */ + switch ($driver) { + case 'mysql': + /* Use UTF-8. */ + $db->exec("SET NAMES 'utf8mb4'"); + break; + case 'pgsql': + /* Use UTF-8. */ + $db->exec("SET NAMES 'UTF8'"); + break; + } + + return $db; + } + + + /** + * Attempt to log in using the given username and password. + * + * On a successful login, this function should return the users attributes. On failure, + * it should throw an exception. If the error was caused by the user entering the wrong + * username or password, a SimpleSAML_Error_Error('WRONGUSERPASS') should be thrown. + * + * Note that both the username and the password are UTF-8 encoded. + * + * @param string $username The username the user wrote. + * @param string $password The password the user wrote. + * @return array Associative array with the users attributes. + */ + protected function login($username, $password) { - assert(is_string($username)); - assert(is_string($password)); - - $db = $this->connect(); - - try { - $sth = $db->prepare($this->query); - } catch (PDOException $e) { - throw new Exception('sqlauth:' . $this->authId . - ': - Failed to prepare query: ' . $e->getMessage()); - } - - try { - $sth->execute(array('username' => $username, 'password' => $password)); - } catch (PDOException $e) { - throw new Exception('sqlauth:' . $this->authId . - ': - Failed to execute query: ' . $e->getMessage()); - } - - try { - $data = $sth->fetchAll(PDO::FETCH_ASSOC); - } catch (PDOException $e) { - throw new Exception('sqlauth:' . $this->authId . - ': - Failed to fetch result set: ' . $e->getMessage()); - } - - SimpleSAML\Logger::info('sqlauth:' . $this->authId . ': Got ' . count($data) . - ' rows from database'); - - if (count($data) === 0) { - /* No rows returned - invalid username/password. */ - SimpleSAML\Logger::error('sqlauth:' . $this->authId . - ': No rows in result set. Probably wrong username/password.'); - throw new SimpleSAML_Error_Error('WRONGUSERPASS'); - } - - /* Extract attributes. We allow the resultset to consist of multiple rows. Attributes - * which are present in more than one row will become multivalued. NULL values and - * duplicate values will be skipped. All values will be converted to strings. - */ - $attributes = array(); - foreach ($data as $row) { - foreach ($row as $name => $value) { - - if ($value === null) { - continue; - } - - $value = (string)$value; - - if (!array_key_exists($name, $attributes)) { - $attributes[$name] = array(); - } - - if (in_array($value, $attributes[$name], TRUE)) { - /* Value already exists in attribute. */ - continue; - } - - $attributes[$name][] = $value; - } - } - - SimpleSAML\Logger::info('sqlauth:' . $this->authId . ': Attributes: ' . - implode(',', array_keys($attributes))); - - return $attributes; - } + assert(is_string($username)); + assert(is_string($password)); + + $db = $this->connect(); + + try { + $sth = $db->prepare($this->query); + } catch (PDOException $e) { + throw new Exception('sqlauth:' . $this->authId . + ': - Failed to prepare query: ' . $e->getMessage()); + } + + try { + $sth->execute(array('username' => $username, 'password' => $password)); + } catch (PDOException $e) { + throw new Exception('sqlauth:' . $this->authId . + ': - Failed to execute query: ' . $e->getMessage()); + } + + try { + $data = $sth->fetchAll(PDO::FETCH_ASSOC); + } catch (PDOException $e) { + throw new Exception('sqlauth:' . $this->authId . + ': - Failed to fetch result set: ' . $e->getMessage()); + } + + SimpleSAML\Logger::info('sqlauth:' . $this->authId . ': Got ' . count($data) . + ' rows from database'); + + if (count($data) === 0) { + /* No rows returned - invalid username/password. */ + SimpleSAML\Logger::error('sqlauth:' . $this->authId . + ': No rows in result set. Probably wrong username/password.'); + throw new SimpleSAML_Error_Error('WRONGUSERPASS'); + } + + /* Extract attributes. We allow the resultset to consist of multiple rows. Attributes + * which are present in more than one row will become multivalued. null values and + * duplicate values will be skipped. All values will be converted to strings. + */ + $attributes = array(); + foreach ($data as $row) { + foreach ($row as $name => $value) { + + if ($value === null) { + continue; + } + + $value = (string)$value; + + if (!array_key_exists($name, $attributes)) { + $attributes[$name] = array(); + } + + if (in_array($value, $attributes[$name], true)) { + /* Value already exists in attribute. */ + continue; + } + + $attributes[$name][] = $value; + } + } + + SimpleSAML\Logger::info('sqlauth:' . $this->authId . ': Attributes: ' . + implode(',', array_keys($attributes))); + + return $attributes; + } } diff --git a/modules/statistics/hooks/hook_cron.php b/modules/statistics/hooks/hook_cron.php index 31d85a8b913443abcaabfbf2e300a92e5da40d07..ccd95e4603b9f11e55bbafca1d11f7cbdbd8c290 100644 --- a/modules/statistics/hooks/hook_cron.php +++ b/modules/statistics/hooks/hook_cron.php @@ -12,7 +12,7 @@ function statistics_hook_cron(&$croninfo) $statconfig = SimpleSAML_Configuration::getConfig('module_statistics.php'); - if (is_null($statconfig->getValue('cron_tag', nul))) { + if (is_null($statconfig->getValue('cron_tag', null))) { return; } if ($statconfig->getValue('cron_tag', null) !== $croninfo['tag']) { diff --git a/modules/statistics/templates/statistics.tpl.php b/modules/statistics/templates/statistics.tpl.php index fa7e1f4e93a8c31c8d0fabbf8f76047a4d9455a9..673070d79e83de5b8b713d72a48655c6ca503b1b 100644 --- a/modules/statistics/templates/statistics.tpl.php +++ b/modules/statistics/templates/statistics.tpl.php @@ -19,38 +19,42 @@ echo '<tr><td class="selecttime_icon"><img src="' . SimpleSAML\Utils\HTTP::getBa echo '<td>'; echo '<form action="#">'; echo $this->data['post_rule']; -echo '<select onchange="submit();" name="rule">'; -foreach ($this->data['available.rules'] as $key => $rule) { - if ($key === $this->data['selected.rule']) { - echo '<option selected="selected" value="' . $key . '">' . $rule['name'] . '</option>'; - } else { - echo '<option value="' . $key . '">' . $rule['name'] . '</option>'; +if (!empty($this->data['available.rules'])) { + echo '<select onchange="submit();" name="rule">'; + foreach ($this->data['available.rules'] as $key => $rule) { + if ($key === $this->data['selected.rule']) { + echo '<option selected="selected" value="' . $key . '">' . $rule['name'] . '</option>'; + } else { + echo '<option value="' . $key . '">' . $rule['name'] . '</option>'; + } } + echo '</select>'; } -echo '</select></form>'; -echo '</td>'; +echo '</form></td>'; // Select delimiter echo '<td class="td_right">'; echo '<form action="#">'; echo $this->data['post_d']; -echo '<select onchange="submit();" name="d">'; -foreach ($this->data['availdelimiters'] as $key => $delim) { - $delimName = $delim; - if (array_key_exists($delim, $this->data['delimiterPresentation'])) { - $delimName = $this->data['delimiterPresentation'][$delim]; - } - - if ($key == '_') { - echo '<option value="_">Total</option>'; - } elseif (isset($_REQUEST['d']) && $delim == $_REQUEST['d']) { - echo '<option selected="selected" value="' . htmlspecialchars($delim) . '">' . htmlspecialchars($delimName) . '</option>'; - } else { - echo '<option value="' . htmlspecialchars($delim) . '">' . htmlspecialchars($delimName) . '</option>'; +if(!empty($this->data['availdelimiters'])) { + echo '<select onchange="submit();" name="d">'; + foreach ($this->data['availdelimiters'] as $key => $delim) { + $delimName = $delim; + if (array_key_exists($delim, $this->data['delimiterPresentation'])) { + $delimName = $this->data['delimiterPresentation'][$delim]; + } + + if ($key == '_') { + echo '<option value="_">Total</option>'; + } elseif (isset($_REQUEST['d']) && $delim == $_REQUEST['d']) { + echo '<option selected="selected" value="' . htmlspecialchars($delim) . '">' . htmlspecialchars($delimName) . '</option>'; + } else { + echo '<option value="' . htmlspecialchars($delim) . '">' . htmlspecialchars($delimName) . '</option>'; + } } + echo '</select>'; } -echo '</select></form>'; -echo '</td></tr>'; +echo '</form></td></tr>'; echo '</table>'; @@ -70,30 +74,34 @@ if (isset($this->data['available.times.prev'])) { echo '<td class="td_right">'; echo '<form action="#">'; echo $this->data['post_res']; -echo '<select onchange="submit();" name="res">'; -foreach ($this->data['available.timeres'] as $key => $timeresname) { - if ($key == $this->data['selected.timeres']) { - echo '<option selected="selected" value="' . $key . '">' . $timeresname . '</option>'; - } else { - echo '<option value="' . $key . '">' . $timeresname . '</option>'; +if (!empty($this->data['available.timeres'])) { + echo '<select onchange="submit();" name="res">'; + foreach ($this->data['available.timeres'] as $key => $timeresname) { + if ($key == $this->data['selected.timeres']) { + echo '<option selected="selected" value="' . $key . '">' . $timeresname . '</option>'; + } else { + echo '<option value="' . $key . '">' . $timeresname . '</option>'; + } } + echo '</select>'; } -echo '</select></form>'; -echo '</td>'; +echo '</form></td>'; echo '<td class="td_left">'; echo '<form action="#">'; echo $this->data['post_time']; -echo '<select onchange="submit();" name="time">'; -foreach ($this->data['available.times'] as $key => $timedescr) { - if ($key == $this->data['selected.time']) { - echo '<option selected="selected" value="' . $key . '">' . $timedescr . '</option>'; - } else { - echo '<option value="' . $key . '">' . $timedescr . '</option>'; +if (!empty($this->data['available.times'])) { + echo '<select onchange="submit();" name="time">'; + foreach ($this->data['available.times'] as $key => $timedescr) { + if ($key == $this->data['selected.time']) { + echo '<option selected="selected" value="' . $key . '">' . $timedescr . '</option>'; + } else { + echo '<option value="' . $key . '">' . $timedescr . '</option>'; + } } + echo '</select>'; } -echo '</select></form>'; -echo '</td>'; +echo '</form></td>'; if (isset($this->data['available.times.next'])) { echo '<td class="td_right td_next_right"><a href="' . $this->data['get_times_next'] . '">Next »</a></td>'; @@ -102,94 +110,96 @@ if (isset($this->data['available.times.next'])) { } echo '</tr></table>'; - - -echo '<div id="tabdiv"><ul class="tabset_tabs"> - <li><a href="#graph">Graph</a></li> - <li><a href="#table">Summary table</a></li> - <li><a href="#debug">Time serie</a></li> -</ul>'; -echo ' - -<div id="graph" class="tabset_content">'; - -echo '<img src="' . htmlspecialchars($this->data['imgurl']) . '" alt="Graph" />'; - -echo '<form action="#">'; -echo '<p class="p_right">Compare with total from this dataset '; -echo $this->data['post_rule2']; -echo '<select onchange="submit();" name="rule2">'; -echo ' <option value="_">None</option>'; -foreach ($this->data['available.rules'] as $key => $rule) { - if ($key === $this->data['selected.rule2']) { - echo '<option selected="selected" value="' . $key . '">' . $rule['name'] . '</option>'; - } else { - echo '<option value="' . $key . '">' . $rule['name'] . '</option>'; +echo '<div id="tabdiv">'; +if (!empty($this->data['results'])){ + echo '<ul class="tabset_tabs"> + <li><a href="#graph">Graph</a></li> + <li><a href="#table">Summary table</a></li> + <li><a href="#debug">Time serie</a></li> + </ul>'; + echo ' + + <div id="graph" class="tabset_content">'; + + echo '<img src="' . htmlspecialchars($this->data['imgurl']) . '" alt="Graph" />'; + + echo '<form action="#">'; + echo '<p class="p_right">Compare with total from this dataset '; + echo $this->data['post_rule2']; + echo '<select onchange="submit();" name="rule2">'; + echo ' <option value="_">None</option>'; + foreach ($this->data['available.rules'] as $key => $rule) { + if ($key === $this->data['selected.rule2']) { + echo '<option selected="selected" value="' . $key . '">' . $rule['name'] . '</option>'; + } else { + echo '<option value="' . $key . '">' . $rule['name'] . '</option>'; + } } -} -echo '</select></p></form>'; + echo '</select></p></form>'; -echo '</div>'; // end graph content. + echo '</div>'; // end graph content. + /** + * Handle table view - - - - - - + */ + $classint = array('odd', 'even'); $i = 0; + echo '<div id="table" class="tabset_content">'; -/** - * Handle table view - - - - - - - */ -$classint = array('odd', 'even'); $i = 0; -echo '<div id="table" class="tabset_content">'; - -if (isset($this->data['pieimgurl'])) { - echo '<img src="' . $this->data['pieimgurl'] . '" alt="Pie chart" />'; -} -echo '<table class="tableview"><tr><th class="value">Value</th><th class="category">Data range</th></tr>'; - -foreach ($this->data['summaryDataset'] as $key => $value) { - $clint = $classint[$i++ % 2]; - - $keyName = $key; - if (array_key_exists($key, $this->data['delimiterPresentation'])) { - $keyName = $this->data['delimiterPresentation'][$key]; + if (isset($this->data['pieimgurl'])) { + echo '<img src="' . $this->data['pieimgurl'] . '" alt="Pie chart" />'; } + echo '<table class="tableview"><tr><th class="value">Value</th><th class="category">Data range</th></tr>'; - if ($key === '_') { - echo '<tr class="total ' . $clint . '"><td class="value">' . $value . '</td><td class="category">' . $keyName . '</td></tr>'; - } else { - echo '<tr class="' . $clint . '"><td class="value">' . $value . '</td><td class="category">' . $keyName . '</td></tr>'; - } -} + foreach ($this->data['summaryDataset'] as $key => $value) { + $clint = $classint[$i++ % 2]; -echo '</table></div>'; -// - - - - - - - End table view - - - - - - - + $keyName = $key; + if (array_key_exists($key, $this->data['delimiterPresentation'])) { + $keyName = $this->data['delimiterPresentation'][$key]; + } -echo '<div id="debug" >'; -echo '<table class="timeseries">'; -echo '<tr><th>Time</th><th>Total</th>'; -foreach ($this->data['topdelimiters'] as $key) { - $keyName = $key; - if (array_key_exists($key, $this->data['delimiterPresentation'])) { - $keyName = $this->data['delimiterPresentation'][$key]; + if ($key === '_') { + echo '<tr class="total ' . $clint . '"><td class="value">' . $value . '</td><td class="category">' . $keyName . '</td></tr>'; + } else { + echo '<tr class="' . $clint . '"><td class="value">' . $value . '</td><td class="category">' . $keyName . '</td></tr>'; + } } - echo'<th>' . $keyName . '</th>'; -} -echo '</tr>'; - -$i = 0; -foreach ($this->data['debugdata'] as $slot => $dd) { - echo '<tr class="' . ((++$i % 2) == 0 ? 'odd' : 'even') . '">'; - echo '<td>' . $dd[0] . '</td>'; - echo '<td class="datacontent">' . $dd[1] . '</td>'; + echo '</table></div>'; + // - - - - - - - End table view - - - - - - - + echo '<div id="debug" >'; + echo '<table class="timeseries">'; + echo '<tr><th>Time</th><th>Total</th>'; foreach ($this->data['topdelimiters'] as $key) { - echo '<td class="datacontent">' . (array_key_exists($key, $this->data['results'][$slot]) ? - $this->data['results'][$slot][$key] : ' ') . '</td>'; + $keyName = $key; + if (array_key_exists($key, $this->data['delimiterPresentation'])) { + $keyName = $this->data['delimiterPresentation'][$key]; + } + echo'<th>' . $keyName . '</th>'; } echo '</tr>'; -} -echo '</table>'; + + $i = 0; + foreach ($this->data['debugdata'] as $slot => $dd) { + echo '<tr class="' . ((++$i % 2) == 0 ? 'odd' : 'even') . '">'; + echo '<td>' . $dd[0] . '</td>'; + echo '<td class="datacontent">' . $dd[1] . '</td>'; + + foreach ($this->data['topdelimiters'] as $key) { + echo '<td class="datacontent">' . (array_key_exists($key, $this->data['results'][$slot]) ? + $this->data['results'][$slot][$key] : ' ') . '</td>'; + } + echo '</tr>'; + } + echo '</table>'; -echo '</div>'; // End debug tab content + echo '</div>'; // End debug tab content +} else { + echo '<h4 align="center">'.$this->data['error'].'</h4>'; + echo '<p align="center"><a href="showstats.php">Clear selection</a></p>'; +} echo '</div>'; // End tab div $this->includeAtTemplateBase('includes/footer.php'); diff --git a/modules/statistics/www/showstats.php b/modules/statistics/www/showstats.php index 051dc9aa55440fd3cd87059ce0a15fe6ab1dc302..b8bb32b68b250d11522a6e8f861f0eff7037c418 100644 --- a/modules/statistics/www/showstats.php +++ b/modules/statistics/www/showstats.php @@ -42,8 +42,35 @@ $ruleset = new sspmod_statistics_Ruleset($statconfig); $statrule = $ruleset->getRule($preferRule); $rule = $statrule->getRuleID(); -$dataset = $statrule->getDataset($preferTimeRes, $preferTime); -$dataset->setDelimiter($delimiter); +$t = new SimpleSAML_XHTML_Template($config, 'statistics:statistics.tpl.php'); +$t->data['pageid'] = 'statistics'; +$t->data['header'] = 'stat'; +$t->data['available.rules'] = $ruleset->availableRulesNames(); +$t->data['selected.rule'] = $rule; +$t->data['selected.rule2'] = $preferRule2; + +try { + $dataset = $statrule->getDataset($preferTimeRes, $preferTime); + $dataset->setDelimiter($delimiter); + $dataset->aggregateSummary(); + $dataset->calculateMax(); + + if (array_key_exists('output', $_REQUEST) && $_REQUEST['output'] === 'csv') { + header('Content-type: text/csv'); + header('Content-Disposition: attachment; filename="simplesamlphp-data.csv"'); + $data = $dataset->getDebugData(); + foreach ($data as $de) { + if (isset($de[1])) { + echo '"'.$de[0].'",'.$de[1]."\n"; + } + } + exit; + } +} catch (Exception $e) { + $t->data['error'] = "No data available"; + $t->show(); + exit; +} $delimiter = $dataset->getDelimiter(); @@ -53,9 +80,6 @@ $availableFileSlots = $statrule->availableFileSlots($timeres); $timeNavigation = $statrule->getTimeNavigation($timeres, $preferTime); -$dataset->aggregateSummary(); -$dataset->calculateMax(); - $piedata = $dataset->getPieData(); $datasets = array(); @@ -69,45 +93,33 @@ $maxes[] = $dataset->getMax(); if (isset($preferRule2)) { $statrule = $ruleset->getRule($preferRule2); - $dataset2 = $statrule->getDataset($preferTimeRes, $preferTime); - $dataset2->aggregateSummary(); - $dataset2->calculateMax(); - - $datasets[] = $dataset2->getPercentValues(); - $maxes[] = $dataset2->getMax(); + try { + $dataset2 = $statrule->getDataset($preferTimeRes, $preferTime); + $dataset2->aggregateSummary(); + $dataset2->calculateMax(); + + $datasets[] = $dataset2->getPercentValues(); + $maxes[] = $dataset2->getMax(); + } catch (Exception $e) { + $t->data['error'] = "No data available to compare"; + $t->show(); + exit; + } } $dimx = $statconfig->getValue('dimension.x', 800); $dimy = $statconfig->getValue('dimension.y', 350); $grapher = new sspmod_statistics_Graph_GoogleCharts($dimx, $dimy); -if (array_key_exists('output', $_REQUEST) && $_REQUEST['output'] === 'csv') { - header('Content-type: text/csv'); - header('Content-Disposition: attachment; filename="simplesamlphp-data.csv"'); - $data = $dataset->getDebugData(); - foreach ($data as $de) { - if (isset($de[1])) { - echo '"' . $de[0] . '",' . $de[1] . "\n"; - } - } - exit; -} - -$t = new SimpleSAML_XHTML_Template($config, 'statistics:statistics.tpl.php'); -$t->data['pageid'] = 'statistics'; -$t->data['header'] = 'stat'; $t->data['imgurl'] = $grapher->show($axis['axis'], $axis['axispos'], $datasets, $maxes); if (isset($piedata)) { - $t->data['pieimgurl'] = $grapher->showPie( $dataset->getDelimiterPresentationPie(), $piedata); + $t->data['pieimgurl'] = $grapher->showPie($dataset->getDelimiterPresentationPie(), $piedata); } -$t->data['available.rules'] = $ruleset->availableRulesNames(); $t->data['available.times'] = $statrule->availableFileSlots($timeres); $t->data['available.timeres'] = $statrule->availableTimeRes(); $t->data['available.times.prev'] = $timeNavigation['prev']; $t->data['available.times.next'] = $timeNavigation['next']; -$t->data['selected.rule']= $rule; -$t->data['selected.rule2']= $preferRule2; $t->data['selected.time'] = $fileslot; $t->data['selected.timeres'] = $timeres; $t->data['selected.delimiter'] = $delimiter; @@ -118,7 +130,7 @@ $t->data['summaryDataset'] = $dataset->getSummary(); $t->data['topdelimiters'] = $dataset->getTopDelimiters(); $t->data['availdelimiters'] = $dataset->availDelimiters(); -$t->data['delimiterPresentation'] = $dataset->getDelimiterPresentation(); +$t->data['delimiterPresentation'] = $dataset->getDelimiterPresentation(); $t->data['post_rule'] = getBaseURL($t, 'post', 'rule'); $t->data['post_rule2'] = getBaseURL($t, 'post', 'rule2'); @@ -154,11 +166,11 @@ function getBaseURL($t, $type = 'get', $key = null, $value = null) } if ($type === 'get') { - return SimpleSAML\Module::getModuleURL("statistics/showstats.php") . '?' . http_build_query($vars, '', '&'); + return SimpleSAML\Module::getModuleURL("statistics/showstats.php").'?'.http_build_query($vars, '', '&'); } else { $text = ''; - foreach($vars as $k => $v) { - $text .= '<input type="hidden" name="' . $k . '" value="'. htmlspecialchars($v) . '" />' . "\n"; + foreach ($vars as $k => $v) { + $text .= '<input type="hidden" name="'.$k.'" value="'.htmlspecialchars($v).'" />'."\n"; } return $text; } diff --git a/templates/_footer.twig b/templates/_footer.twig index f789441c151aca1a93b009605cfe2fba765fe1a2..e5fdde185e30ab4db002183c22a6280e9fa82cb8 100644 --- a/templates/_footer.twig +++ b/templates/_footer.twig @@ -1,6 +1,6 @@ <hr> <img src="/{{ baseurlpath }}resources/icons/ssplogo-fish-small.png" alt="Small fish logo" style="float: right"> - Copyright © 2007-2016 <a href="http://uninett.no/">UNINETT AS</a> + Copyright © 2007-2018 <a href="http://uninett.no/">UNINETT AS</a> <br style="clear: right"> diff --git a/templates/includes/footer.php b/templates/includes/footer.php index 0ca7e12c1166440517eb477a77f83541b26e6987..94e62f7c0fbae60fd42d5ecb092a4327e2cc26e7 100644 --- a/templates/includes/footer.php +++ b/templates/includes/footer.php @@ -10,7 +10,7 @@ if(!empty($this->data['htmlinject']['htmlContentPost'])) { <hr /> <img src="/<?php echo $this->data['baseurlpath']; ?>resources/icons/ssplogo-fish-small.png" alt="Small fish logo" style="float: right" /> - Copyright © 2007-2017 <a href="http://uninett.no/">UNINETT AS</a> + Copyright © 2007-2018 <a href="http://uninett.no/">UNINETT AS</a> <br style="clear: right" /> diff --git a/templates/index.twig b/templates/index.twig index 2a6004aa06f112da48320488c242a7ad286dd17c..6f6f0518063bf61bd837f9295fcf36af0761f21d 100644 --- a/templates/index.twig +++ b/templates/index.twig @@ -2,10 +2,9 @@ {% block content %} <div id="portalmenu" class="ui-tabs ui-widget ui-widget-content ui-corner-all"> <ul class="tabset_tabs ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all"> - <li class="ui-state-default ui-corner-top"><a href="http://simplesaml.arrakis.uninett.no/simplesaml/module.php/core/frontpage_welcome.php">Welcome</a></li> <li class="ui-state-default ui-corner-top ui-tabs-selected ui-state-active"><a href="#">Configuration</a></li> - <li class="ui-state-default ui-corner-top"><a href="http://simplesaml.arrakis.uninett.no/simplesaml/module.php/core/frontpage_auth.php">Authentication</a></li> - <li class="ui-state-default ui-corner-top"><a href="http://simplesaml.arrakis.uninett.no/simplesaml/module.php/core/frontpage_federation.php">Federation</a></li> + <li class="ui-state-default ui-corner-top"><a href="/{{ baseurlpath }}module.php/core/frontpage_auth.php">Authentication</a></li> + <li class="ui-state-default ui-corner-top"><a href="/{{ baseurlpath }}module.php/core/frontpage_federation.php">Federation</a></li> </ul> <div id="portalcontent" class="ui-tabs-panel ui-widget-content ui-corner-bottom"> diff --git a/tests/lib/SimpleSAML/ConfigurationTest.php b/tests/lib/SimpleSAML/ConfigurationTest.php index fced577efb64dd1cf96700502e4939de9d402276..c299cc28368e4f2bcf966a04e8e5cfd1c8fd25fa 100644 --- a/tests/lib/SimpleSAML/ConfigurationTest.php +++ b/tests/lib/SimpleSAML/ConfigurationTest.php @@ -5,11 +5,11 @@ */ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTestCase { - /** * Test SimpleSAML_Configuration::getVersion() */ - public function testGetVersion() { + public function testGetVersion() + { $c = SimpleSAML_Configuration::getOptionalConfig(); $this->assertTrue(is_string($c->getVersion())); } @@ -48,11 +48,12 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest /** * Test SimpleSAML_Configuration::getValue() */ - public function testGetValue() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetValue() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'exists_true' => true, 'exists_null' => null, - )); + ]); $this->assertEquals($c->getValue('missing'), null); $this->assertEquals($c->getValue('missing', true), true); $this->assertEquals($c->getValue('missing', true), true); @@ -67,7 +68,8 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest * Test SimpleSAML_Configuration::getValue(), REQUIRED_OPTION flag. * @expectedException Exception */ - public function testGetValueRequired() { + public function testGetValueRequired() + { $c = SimpleSAML_Configuration::loadFromArray(array()); $c->getValue('missing', SimpleSAML_Configuration::REQUIRED_OPTION); } @@ -75,7 +77,8 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest /** * Test SimpleSAML_Configuration::hasValue() */ - public function testHasValue() { + public function testHasValue() + { $c = SimpleSAML_Configuration::loadFromArray(array( 'exists_true' => true, 'exists_null' => null, @@ -88,7 +91,8 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest /** * Test SimpleSAML_Configuration::hasValue() */ - public function testHasValueOneOf() { + public function testHasValueOneOf() + { $c = SimpleSAML_Configuration::loadFromArray(array( 'exists_true' => true, 'exists_null' => null, @@ -106,8 +110,8 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest /** * Test SimpleSAML_Configuration::getBaseURL() */ - public function testGetBaseURL() { - + public function testGetBaseURL() + { // Need to set a default configuration because the SSP Logger attempts to use it. SimpleSAML_Configuration::loadFromArray(array(), '[ARRAY]', 'simplesaml'); $c = SimpleSAML_Configuration::loadFromArray(array()); @@ -147,7 +151,8 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest /** * Test SimpleSAML_Configuration::getBasePath() */ - public function testGetBasePath() { + public function testGetBasePath() + { $c = SimpleSAML_Configuration::loadFromArray(array()); $this->assertEquals($c->getBasePath(), '/simplesaml/'); @@ -203,10 +208,11 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest /** * Test SimpleSAML_Configuration::resolvePath() */ - public function testResolvePath() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testResolvePath() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'basedir' => '/basedir/', - )); + ]); $this->assertEquals($c->resolvePath(null), null); $this->assertEquals($c->resolvePath('/otherdir'), '/otherdir'); @@ -219,12 +225,13 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest /** * Test SimpleSAML_Configuration::getPathValue() */ - public function testGetPathValue() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetPathValue() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'basedir' => '/basedir/', 'path_opt' => 'path', 'slashes_opt' => 'slashes//', - )); + ]); $this->assertEquals($c->getPathValue('missing'), null); $this->assertEquals($c->getPathValue('path_opt'), '/basedir/path/'); @@ -234,13 +241,14 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest /** * Test SimpleSAML_Configuration::getBaseDir() */ - public function testGetBaseDir() { - $c = SimpleSAML_Configuration::loadFromArray(array()); + public function testGetBaseDir() + { + $c = SimpleSAML_Configuration::loadFromArray([]); $this->assertEquals($c->getBaseDir(), dirname(dirname(dirname(dirname(__FILE__)))) . '/'); - $c = SimpleSAML_Configuration::loadFromArray(array( + $c = SimpleSAML_Configuration::loadFromArray([ 'basedir' => '/basedir', - )); + ]); $this->assertEquals($c->getBaseDir(), '/basedir/'); $c = SimpleSAML_Configuration::loadFromArray(array( @@ -252,11 +260,12 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest /** * Test SimpleSAML_Configuration::getBoolean() */ - public function testGetBoolean() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetBoolean() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'true_opt' => true, 'false_opt' => false, - )); + ]); $this->assertEquals($c->getBoolean('missing_opt', '--missing--'), '--missing--'); $this->assertEquals($c->getBoolean('true_opt', '--missing--'), true); $this->assertEquals($c->getBoolean('false_opt', '--missing--'), false); @@ -266,8 +275,9 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest * Test SimpleSAML_Configuration::getBoolean() missing option * @expectedException Exception */ - public function testGetBooleanMissing() { - $c = SimpleSAML_Configuration::loadFromArray(array()); + public function testGetBooleanMissing() + { + $c = SimpleSAML_Configuration::loadFromArray([]); $c->getBoolean('missing_opt'); } @@ -275,20 +285,22 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest * Test SimpleSAML_Configuration::getBoolean() wrong option * @expectedException Exception */ - public function testGetBooleanWrong() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetBooleanWrong() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'wrong' => 'true', - )); + ]); $c->getBoolean('wrong'); } /** * Test SimpleSAML_Configuration::getString() */ - public function testGetString() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetString() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'str_opt' => 'Hello World!', - )); + ]); $this->assertEquals($c->getString('missing_opt', '--missing--'), '--missing--'); $this->assertEquals($c->getString('str_opt', '--missing--'), 'Hello World!'); } @@ -297,8 +309,9 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest * Test SimpleSAML_Configuration::getString() missing option * @expectedException Exception */ - public function testGetStringMissing() { - $c = SimpleSAML_Configuration::loadFromArray(array()); + public function testGetStringMissing() + { + $c = SimpleSAML_Configuration::loadFromArray([]); $c->getString('missing_opt'); } @@ -306,20 +319,22 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest * Test SimpleSAML_Configuration::getString() wrong option * @expectedException Exception */ - public function testGetStringWrong() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetStringWrong() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'wrong' => false, - )); + ]); $c->getString('wrong'); } /** * Test SimpleSAML_Configuration::getInteger() */ - public function testGetInteger() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetInteger() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'int_opt' => 42, - )); + ]); $this->assertEquals($c->getInteger('missing_opt', '--missing--'), '--missing--'); $this->assertEquals($c->getInteger('int_opt', '--missing--'), 42); } @@ -328,8 +343,9 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest * Test SimpleSAML_Configuration::getInteger() missing option * @expectedException Exception */ - public function testGetIntegerMissing() { - $c = SimpleSAML_Configuration::loadFromArray(array()); + public function testGetIntegerMissing() + { + $c = SimpleSAML_Configuration::loadFromArray([]); $c->getInteger('missing_opt'); } @@ -337,20 +353,22 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest * Test SimpleSAML_Configuration::getInteger() wrong option * @expectedException Exception */ - public function testGetIntegerWrong() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetIntegerWrong() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'wrong' => '42', - )); + ]); $c->getInteger('wrong'); } /** * Test SimpleSAML_Configuration::getIntegerRange() */ - public function testGetIntegerRange() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetIntegerRange() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'int_opt' => 42, - )); + ]); $this->assertEquals($c->getIntegerRange('missing_opt', 0, 100, '--missing--'), '--missing--'); $this->assertEquals($c->getIntegerRange('int_opt', 0, 100), 42); } @@ -359,10 +377,11 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest * Test SimpleSAML_Configuration::getIntegerRange() below limit * @expectedException Exception */ - public function testGetIntegerRangeBelow() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetIntegerRangeBelow() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'int_opt' => 9, - )); + ]); $this->assertEquals($c->getIntegerRange('int_opt', 10, 100), 42); } @@ -370,10 +389,11 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest * Test SimpleSAML_Configuration::getIntegerRange() above limit * @expectedException Exception */ - public function testGetIntegerRangeAbove() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetIntegerRangeAbove() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'int_opt' => 101, - )); + ]); $this->assertEquals($c->getIntegerRange('int_opt', 10, 100), 42); } @@ -392,20 +412,22 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest * Test SimpleSAML_Configuration::getValueValidate() wrong option * @expectedException Exception */ - public function testGetValueValidateWrong() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetValueValidateWrong() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'opt' => 'd', - )); + ]); $c->getValueValidate('opt', array('a', 'b', 'c')); } /** * Test SimpleSAML_Configuration::getArray() */ - public function testGetArray() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetArray() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'opt' => array('a', 'b', 'c'), - )); + ]); $this->assertEquals($c->getArray('missing_opt', '--missing--'), '--missing--'); $this->assertEquals($c->getArray('opt'), array('a', 'b', 'c')); } @@ -414,22 +436,24 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest * Test SimpleSAML_Configuration::getArray() wrong option * @expectedException Exception */ - public function testGetArrayWrong() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetArrayWrong() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'opt' => 'not_an_array', - )); + ]); $c->getArray('opt'); } /** * Test SimpleSAML_Configuration::getArrayize() */ - public function testGetArrayize() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetArrayize() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'opt' => array('a', 'b', 'c'), 'opt_int' => 42, 'opt_str' => 'string', - )); + ]); $this->assertEquals($c->getArrayize('missing_opt', '--missing--'), '--missing--'); $this->assertEquals($c->getArrayize('opt'), array('a', 'b', 'c')); $this->assertEquals($c->getArrayize('opt_int'), array(42)); @@ -439,11 +463,12 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest /** * Test SimpleSAML_Configuration::getArrayizeString() */ - public function testGetArrayizeString() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetArrayizeString() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'opt' => array('a', 'b', 'c'), 'opt_str' => 'string', - )); + ]); $this->assertEquals($c->getArrayizeString('missing_opt', '--missing--'), '--missing--'); $this->assertEquals($c->getArrayizeString('opt'), array('a', 'b', 'c')); $this->assertEquals($c->getArrayizeString('opt_str'), array('string')); @@ -453,20 +478,22 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest * Test SimpleSAML_Configuration::getArrayizeString() option with an array that contains something that isn't a string. * @expectedException Exception */ - public function testGetArrayizeStringWrongValue() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetArrayizeStringWrongValue() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'opt' => array('a', 'b', 42), - )); + ]); $c->getArrayizeString('opt'); } /** * Test SimpleSAML_Configuration::getConfigItem() */ - public function testGetConfigItem() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetConfigItem() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'opt' => array('a' => 42), - )); + ]); $this->assertEquals($c->getConfigItem('missing_opt', '--missing--'), '--missing--'); $opt = $c->getConfigItem('opt'); $this->assertInstanceOf('SimpleSAML_Configuration', $opt); @@ -477,23 +504,25 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest * Test SimpleSAML_Configuration::getConfigItem() wrong option * @expectedException Exception */ - public function testGetConfigItemWrong() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetConfigItemWrong() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'opt' => 'not_an_array', - )); + ]); $c->getConfigItem('opt'); } /** * Test SimpleSAML_Configuration::getConfigList() */ - public function testGetConfigList() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetConfigList() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'opts' => array( 'a' => array('opt1' => 'value1'), 'b' => array('opt2' => 'value2'), ), - )); + ]); $this->assertEquals($c->getConfigList('missing_opt', '--missing--'), '--missing--'); $opts = $c->getConfigList('opts'); $this->assertInternalType('array', $opts); @@ -508,10 +537,11 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest * Test SimpleSAML_Configuration::getConfigList() wrong option * @expectedException Exception */ - public function testGetConfigListWrong() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetConfigListWrong() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'opt' => 'not_an_array', - )); + ]); $c->getConfigList('opt'); } @@ -522,12 +552,12 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest */ public function testGetConfigListWrongArrayValues() { - $c = SimpleSAML_Configuration::loadFromArray(array( + $c = SimpleSAML_Configuration::loadFromArray([ 'opts' => array( 'a', 'b', ), - )); + ]); $c->getConfigList('opts'); } @@ -535,22 +565,24 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest /** * Test SimpleSAML_Configuration::getOptions() */ - public function testGetOptions() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetOptions() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'a' => true, 'b' => null, - )); + ]); $this->assertEquals($c->getOptions(), array('a', 'b')); } /** * Test SimpleSAML_Configuration::toArray() */ - public function testToArray() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testToArray() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'a' => true, 'b' => null, - )); + ]); $this->assertEquals($c->toArray(), array('a' => true, 'b' => null)); } @@ -879,14 +911,15 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest /** * Test SimpleSAML_Configuration::getLocalizedString() */ - public function testGetLocalizedString() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetLocalizedString() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'str_opt' => 'Hello World!', 'str_array' => array( 'en' => 'Hello World!', 'no' => 'Hei Verden!', ), - )); + ]); $this->assertEquals($c->getLocalizedString('missing_opt', '--missing--'), '--missing--'); $this->assertEquals($c->getLocalizedString('str_opt'), array('en' => 'Hello World!')); $this->assertEquals($c->getLocalizedString('str_array'), array('en' => 'Hello World!', 'no' => 'Hei Verden!')); @@ -896,10 +929,11 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest * Test SimpleSAML_Configuration::getLocalizedString() not array nor simple string * @expectedException Exception */ - public function testGetLocalizedStringNotArray() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetLocalizedStringNotArray() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'opt' => 42, - )); + ]); $c->getLocalizedString('opt'); } @@ -907,10 +941,11 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest * Test SimpleSAML_Configuration::getLocalizedString() not string key * @expectedException Exception */ - public function testGetLocalizedStringNotStringKey() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetLocalizedStringNotStringKey() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'opt' => array(42 => 'text'), - )); + ]); $c->getLocalizedString('opt'); } @@ -918,13 +953,37 @@ class Test_SimpleSAML_Configuration extends SimpleSAML\Test\Utils\ClearStateTest * Test SimpleSAML_Configuration::getLocalizedString() not string value * @expectedException Exception */ - public function testGetLocalizedStringNotStringValue() { - $c = SimpleSAML_Configuration::loadFromArray(array( + public function testGetLocalizedStringNotStringValue() + { + $c = SimpleSAML_Configuration::loadFromArray([ 'opt' => array('en' => 42), - )); + ]); $c->getLocalizedString('opt'); } + /** + * Test SimpleSAML_Configuration::getConfig() nonexistent file + * @expectedException Exception + */ + public function testGetConfigNonexistentFile() + { + SimpleSAML_Configuration::getConfig('nonexistent-nopreload.php'); + } + + /** + * Test SimpleSAML_Configuration::getConfig() preloaded nonexistent file + */ + public function testGetConfigNonexistentFilePreload() + { + $c = SimpleSAML_Configuration::loadFromArray([ + 'key' => 'value' + ]); + $virtualFile = 'nonexistent-preload.php'; + SimpleSAML_Configuration::setPreLoadedConfig($c, $virtualFile); + $nc = SimpleSAML_Configuration::getConfig($virtualFile); + $this->assertEquals('value', $nc->getValue('key', null)); + } + /** * Test that Configuration objects can be initialized from an array. * diff --git a/tests/lib/SimpleSAML/Metadata/MetaDataStorageSourceTest.php b/tests/lib/SimpleSAML/Metadata/MetaDataStorageSourceTest.php new file mode 100644 index 0000000000000000000000000000000000000000..35964f2ac4275158fa040d6c7e6486b9e1ee42e4 --- /dev/null +++ b/tests/lib/SimpleSAML/Metadata/MetaDataStorageSourceTest.php @@ -0,0 +1,56 @@ +<?php + + +/** + * Class SimpleSAML_Metadata_MetaDataStorageSourceTest + */ +class SimpleSAML_Metadata_MetaDataStorageSourceTest extends PHPUnit_Framework_TestCase +{ + /** + * Test SimpleSAML_Metadata_MetaDataStorageSourceTest::getConfig XML bad source + * @expectedException Exception + */ + public function testBadXMLSource() { + SimpleSAML_Metadata_MetaDataStorageSource::getSource(["type"=>"xml", "foo"=>"baa"]); + } + + /** + * Test SimpleSAML_Metadata_MetaDataStorageSourceTest::getConfig invalid static XML source + * @expectedException Exception + */ + public function testInvalidStaticXMLSource() { + $strTestXML = " +<EntityDescriptor ID=\"_12345678-90ab-cdef-1234-567890abcdef\" entityID=\"https://saml.idp/entityid\" xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\"> +</EntityDescriptor> +"; + SimpleSAML_Metadata_MetaDataStorageSource::getSource(["type"=>"xml", "xml"=>$strTestXML]); + } + + /** + * Test SimpleSAML_Metadata_MetaDataStorageSourceTest::getConfig XML static XML source + */ + public function testStaticXMLSource() { + $testEntityId = "https://saml.idp/entityid"; + $strTestXML = " +<EntityDescriptor ID=\"_12345678-90ab-cdef-1234-567890abcdef\" entityID=\"$testEntityId\" xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\"> +<RoleDescriptor xsi:type=\"fed:ApplicationServiceType\" +protocolSupportEnumeration=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512 http://schemas.xmlsoap.org/ws/2005/02/trust http://docs.oasis-open.org/wsfed/federation/200706\" +ServiceDisplayName=\"SimpleSAMLphp Test\" +xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" +xmlns:fed=\"http://docs.oasis-open.org/wsfed/federation/200706\"> +<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat> +<SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"https://saml.idp/sso/\"/> +<SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"https://saml.idp/logout/\"/> +</RoleDescriptor> +<IDPSSODescriptor protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\"/> +</EntityDescriptor> +"; + // The primary test here is that - in contrast to the others above - this loads without error + // As a secondary thing, check that the entity ID from the static source provided can be extracted + $source = SimpleSAML_Metadata_MetaDataStorageSource::getSource(["type"=>"xml", "xml"=>$strTestXML]); + $idpSet = $source->getMetadataSet("saml20-idp-remote"); + $this->assertArrayHasKey($testEntityId, $idpSet, "Did not extract expected IdP entity ID from static XML source"); + // Finally verify that a different entity ID does not get loaded + $this->assertCount(1, $idpSet, "Unexpectedly got metadata for an alternate entity than that defined"); + } +} diff --git a/tests/lib/SimpleSAML/Utils/HTTPTest.php b/tests/lib/SimpleSAML/Utils/HTTPTest.php index 073073c9378b12d1ca2b306fdf5392304adf6ca3..d5708322bc32d9affcea91fc9e1541309c2504f0 100644 --- a/tests/lib/SimpleSAML/Utils/HTTPTest.php +++ b/tests/lib/SimpleSAML/Utils/HTTPTest.php @@ -6,8 +6,6 @@ use SimpleSAML\Utils\HTTP; class HTTPTest extends TestCase { - - /** * Set up the environment ($_SERVER) populating the typical variables from a given URL. * @@ -120,7 +118,6 @@ class HTTPTest extends TestCase $_SERVER = $original; } - /** * Test SimpleSAML\Utils\HTTP::getSelfHost() with and without custom port. */ @@ -166,7 +163,6 @@ class HTTPTest extends TestCase $_SERVER = $original; } - /** * Test SimpleSAML\Utils\HTTP::getSelfURL(). */ @@ -292,7 +288,6 @@ class HTTPTest extends TestCase $_SERVER = $original; } - /** * Test SimpleSAML\Utils\HTTP::checkURLAllowed(), without regex. */ @@ -355,6 +350,50 @@ class HTTPTest extends TestCase $_SERVER = $original; } + /** + * Test SimpleSAML\Utils\HTTP::getServerPort(). + */ + public function testGetServerPort() + { + $original = $_SERVER; + + // Test HTTP + non-standard port + $_SERVER['HTTPS'] = 'off'; + $_SERVER['SERVER_PORT'] = '3030'; + $this->assertEquals(HTTP::getServerPort(), ':3030'); + + // Test HTTP + standard port + $_SERVER['SERVER_PORT'] = '80'; + $this->assertEquals(HTTP::getServerPort(), ''); + + // Test HTTP + standard integer port + $_SERVER['SERVER_PORT'] = 80; + $this->assertEquals(HTTP::getServerPort(), ''); + + // Test HTTP + without port + unset($_SERVER['SERVER_PORT']); + $this->assertEquals(HTTP::getServerPort(), ''); + + // Test HTTPS + non-standard port + $_SERVER['HTTPS'] = 'on'; + $_SERVER['SERVER_PORT'] = '3030'; + $this->assertEquals(HTTP::getServerPort(), ':3030'); + + // Test HTTPS + non-standard integer port + $_SERVER['SERVER_PORT'] = 3030; + $this->assertEquals(HTTP::getServerPort(), ':3030'); + + // Test HTTPS + standard port + $_SERVER['SERVER_PORT'] = '443'; + $this->assertEquals(HTTP::getServerPort(), ''); + + // Test HTTPS + without port + unset($_SERVER['SERVER_PORT']); + $this->assertEquals(HTTP::getServerPort(), ''); + + $_SERVER = $original; + } + /** * Test SimpleSAML\Utils\HTTP::checkURLAllowed(), with the regex as a * subdomain of an evil domain. diff --git a/tests/modules/consent/lib/Auth/Process/ConsentTest.php b/tests/modules/consent/lib/Auth/Process/ConsentTest.php index 887e11b433e8daffa88fdef506339124d9622685..97534dc4382390cd61d45bb072f579eacc64b9c5 100644 --- a/tests/modules/consent/lib/Auth/Process/ConsentTest.php +++ b/tests/modules/consent/lib/Auth/Process/ConsentTest.php @@ -120,4 +120,63 @@ class ConsentTest extends TestCase // the state should NOT have changed because NO consent should be necessary (match) $this->assertEquals($request, $result); } + + public function testAttributeHashIsConsistentWhenOrderOfValuesChange() + { + $attributes1 = array( + 'attribute1' => array('val1', 'val2'), + 'attribute2' => array('val1', 'val2') + ); + $attributeHash1 = \sspmod_consent_Auth_Process_Consent::getAttributeHash($attributes1, true); + + $attributes2 = array( + 'attribute1' => array('val1', 'val2'), + 'attribute2' => array('val2', 'val1') + ); + $attributeHash2 = \sspmod_consent_Auth_Process_Consent::getAttributeHash($attributes2, true); + + $this->assertEquals($attributeHash1, $attributeHash2, "Hash is not the same when the order of values changes"); + } + + public function testAttributeHashIsConsistentWhenOrderOfAttributesChange() + { + $attributes1 = array( + 'attribute2' => array('val1', 'val2'), + 'attribute1' => array('val1', 'val2') + ); + $attributeHash1 = \sspmod_consent_Auth_Process_Consent::getAttributeHash($attributes1, true); + + $attributes2 = array( + 'attribute1' => array('val1', 'val2'), + 'attribute2' => array('val1', 'val2') + ); + $attributeHash2 = \sspmod_consent_Auth_Process_Consent::getAttributeHash($attributes2, true); + + $this->assertEquals( + $attributeHash1, + $attributeHash2, + "Hash is not the same when the order of the attributs changes" + ); + } + + public function testAttributeHashIsConsistentWithoutValuesWhenOrderOfAttributesChange() + { + $attributes1 = array( + 'attribute2' => array('val1', 'val2'), + 'attribute1' => array('val1', 'val2') + ); + $attributeHash1 = \sspmod_consent_Auth_Process_Consent::getAttributeHash($attributes1); + + $attributes2 = array( + 'attribute1' => array('val1', 'val2'), + 'attribute2' => array('val1', 'val2') + ); + $attributeHash2 = \sspmod_consent_Auth_Process_Consent::getAttributeHash($attributes2); + + $this->assertEquals( + $attributeHash1, + $attributeHash2, + "Hash is not the same when the order of the attributs changes and the values are not included" + ); + } } diff --git a/tests/modules/core/lib/Auth/Process/CardinalitySingleTest.php b/tests/modules/core/lib/Auth/Process/CardinalitySingleTest.php new file mode 100644 index 0000000000000000000000000000000000000000..5e9d993b2aa1301ccd28e57c44acebaf2ad2b663 --- /dev/null +++ b/tests/modules/core/lib/Auth/Process/CardinalitySingleTest.php @@ -0,0 +1,141 @@ +<?php +// Alias the PHPUnit 6.0 ancestor if available, else fall back to legacy ancestor +if (class_exists('\PHPUnit\Framework\TestCase', true) and !class_exists('\PHPUnit_Framework_TestCase', true)) { + class_alias('\PHPUnit\Framework\TestCase', '\PHPUnit_Framework_TestCase', true); +} + +/** + * Test for the core:CardinalitySingle filter. + */ +class Test_Core_Auth_Process_CardinalitySingleTest extends \PHPUnit_Framework_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 static function processFilter(array $config, array $request) + { + $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1'; + $_SERVER['REQUEST_METHOD'] = 'GET'; + $filter = new sspmod_core_Auth_Process_CardinalitySingle($config, null); + $filter->process($request); + return $request; + } + + protected function setUp() + { + \SimpleSAML_Configuration::loadFromArray(array(), '[ARRAY]', 'simplesaml'); + } + + /** + * Test singleValued + */ + public function testSingleValuedUnchanged() + { + $config = array( + 'singleValued' => array('eduPersonPrincipalName') + ); + $request = array( + 'Attributes' => array( + 'eduPersonPrincipalName' => array('joe@example.com'), + ), + ); + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $expectedData = array('eduPersonPrincipalName' => array('joe@example.com')); + $this->assertEquals($expectedData, $attributes, "Assertion values should not have changed"); + } + + /** + * Test first value extraction + */ + public function testFirstValue() + { + $config = array( + 'firstValue' => array('eduPersonPrincipalName') + ); + $request = array( + 'Attributes' => array( + 'eduPersonPrincipalName' => array('joe@example.com', 'bob@example.net'), + ), + ); + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $expectedData = array('eduPersonPrincipalName' => array('joe@example.com')); + $this->assertEquals($expectedData, $attributes, "Only first value should be returned"); + } + + public function testFirstValueUnchanged() + { + $config = array( + 'firstValue' => array('eduPersonPrincipalName') + ); + $request = array( + 'Attributes' => array( + 'eduPersonPrincipalName' => array('joe@example.com'), + ), + ); + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $expectedData = array('eduPersonPrincipalName' => array('joe@example.com')); + $this->assertEquals($expectedData, $attributes, "Assertion values should not have changed"); + } + + /** + * Test flattening + */ + public function testFlatten() + { + $config = array( + 'flatten' => array('eduPersonPrincipalName'), + 'flattenWith' => '|', + ); + $request = array( + 'Attributes' => array( + 'eduPersonPrincipalName' => array('joe@example.com', 'bob@example.net'), + ), + ); + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $expectedData = array('eduPersonPrincipalName' => array('joe@example.com|bob@example.net')); + $this->assertEquals($expectedData, $attributes, "Flattened string should be returned"); + } + + public function testFlattenUnchanged() + { + $config = array( + 'flatten' => array('eduPersonPrincipalName'), + 'flattenWith' => '|', + ); + $request = array( + 'Attributes' => array( + 'eduPersonPrincipalName' => array('joe@example.com'), + ), + ); + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $expectedData = array('eduPersonPrincipalName' => array('joe@example.com')); + $this->assertEquals($expectedData, $attributes, "Assertion values should not have changed"); + } + + /** + * Test abort + * @expectedException PHPUnit_Framework_Error + * @expectedExceptionMessageRegExp /REQUEST_URI/ + */ + public function testAbort() + { + $config = array( + 'singleValued' => array('eduPersonPrincipalName'), + ); + $request = array( + 'Attributes' => array( + 'eduPersonPrincipalName' => array('joe@example.com', 'bob@example.net'), + ), + ); + self::processFilter($config, $request); + } +} diff --git a/tests/modules/core/lib/Auth/Process/CardinalityTest.php b/tests/modules/core/lib/Auth/Process/CardinalityTest.php new file mode 100644 index 0000000000000000000000000000000000000000..329dcec8feadb21ff8301e6785bc123db4ada4fb --- /dev/null +++ b/tests/modules/core/lib/Auth/Process/CardinalityTest.php @@ -0,0 +1,235 @@ +<?php +// Alias the PHPUnit 6.0 ancestor if available, else fall back to legacy ancestor +if (class_exists('\PHPUnit\Framework\TestCase', true) and !class_exists('\PHPUnit_Framework_TestCase', true)) { + class_alias('\PHPUnit\Framework\TestCase', '\PHPUnit_Framework_TestCase', true); +} + +/** + * Test for the core:Cardinality filter. + */ +class Test_Core_Auth_Process_CardinalityTest extends \PHPUnit_Framework_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 static function processFilter(array $config, array $request) + { + $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1'; + $_SERVER['REQUEST_METHOD'] = 'GET'; + $filter = new sspmod_core_Auth_Process_Cardinality($config, null); + $filter->process($request); + return $request; + } + + protected function setUp() + { + \SimpleSAML_Configuration::loadFromArray(array(), '[ARRAY]', 'simplesaml'); + } + + /* + * Test where a minimum is set but no maximum + */ + public function testMinNoMax() + { + $config = array( + 'mail' => array('min' => 1), + ); + $request = array( + 'Attributes' => array( + 'mail' => array('joe@example.com', 'bob@example.com'), + ), + ); + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $expectedData = array('mail' => array('joe@example.com', 'bob@example.com')); + $this->assertEquals($expectedData, $attributes, "Assertion values should not have changed"); + } + + /* + * Test where a maximum is set but no minimum + */ + public function testMaxNoMin() + { + $config = array( + 'mail' => array('max' => 2), + ); + $request = array( + 'Attributes' => array( + 'mail' => array('joe@example.com', 'bob@example.com'), + ), + ); + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $expectedData = array('mail' => array('joe@example.com', 'bob@example.com')); + $this->assertEquals($expectedData, $attributes, "Assertion values should not have changed"); + } + + /* + * Test in bounds within a maximum an minimum + */ + public function testMaxMin() + { + $config = array( + 'mail' => array('min' => 1, 'max' => 2), + ); + $request = array( + 'Attributes' => array( + 'mail' => array('joe@example.com', 'bob@example.com'), + ), + ); + $result = self::processFilter($config, $request); + $attributes = $result['Attributes']; + $expectedData = array('mail' => array('joe@example.com', 'bob@example.com')); + $this->assertEquals($expectedData, $attributes, "Assertion values should not have changed"); + } + + /** + * Test maximum is out of bounds results in redirect + * @expectedException PHPUnit_Framework_Error + * @expectedExceptionMessageRegExp /REQUEST_URI/ + */ + public function testMaxOutOfBounds() + { + $config = array( + 'mail' => array('max' => 2), + ); + $request = array( + 'Attributes' => array( + 'mail' => array('joe@example.com', 'bob@example.com', 'fred@example.com'), + ), + ); + self::processFilter($config, $request); + } + + /** + * Test minimum is out of bounds results in redirect + * @expectedException PHPUnit_Framework_Error + * @expectedExceptionMessageRegExp /REQUEST_URI/ + */ + public function testMinOutOfBounds() + { + $config = array( + 'mail' => array('min' => 3), + ); + $request = array( + 'Attributes' => array( + 'mail' => array('joe@example.com', 'bob@example.com'), + ), + ); + self::processFilter($config, $request); + } + + /** + * Test missing attribute results in redirect + * @expectedException PHPUnit_Framework_Error + * @expectedExceptionMessageRegExp /REQUEST_URI/ + */ + public function testMissingAttribute() + { + $config = array( + 'mail' => array('min' => 1), + ); + $request = array( + 'Attributes' => array( ), + ); + self::processFilter($config, $request); + } + + /* + * Configuration errors + */ + + /** + * Test invalid minimum values + * @expectedException SimpleSAML_Error_Exception + * @expectedExceptionMessageRegExp /Minimum/ + */ + public function testMinInvalid() + { + $config = array( + 'mail' => array('min' => false), + ); + $request = array( + 'Attributes' => array( + 'mail' => array('joe@example.com', 'bob@example.com'), + ), + ); + self::processFilter($config, $request); + } + + /** + * Test invalid minimum values + * @expectedException SimpleSAML_Error_Exception + * @expectedExceptionMessageRegExp /Minimum/ + */ + public function testMinNegative() + { + $config = array( + 'mail' => array('min' => -1), + ); + $request = array( + 'Attributes' => array( + 'mail' => array('joe@example.com', 'bob@example.com'), + ), + ); + self::processFilter($config, $request); + } + + /** + * Test invalid maximum values + * @expectedException SimpleSAML_Error_Exception + * @expectedExceptionMessageRegExp /Maximum/ + */ + public function testMaxInvalid() + { + $config = array( + 'mail' => array('max' => false), + ); + $request = array( + 'Attributes' => array( + 'mail' => array('joe@example.com', 'bob@example.com'), + ), + ); + self::processFilter($config, $request); + } + + /** + * Test maximum < minimum + * @expectedException SimpleSAML_Error_Exception + * @expectedExceptionMessageRegExp /less than/ + */ + public function testMinGreaterThanMax() + { + $config = array( + 'mail' => array('min' => 2, 'max' => 1), + ); + $request = array( + 'Attributes' => array( + 'mail' => array('joe@example.com', 'bob@example.com'), + ), + ); + self::processFilter($config, $request); + } + + /** + * Test invalid attribute name + * @expectedException SimpleSAML_Error_Exception + * @expectedExceptionMessageRegExp /Invalid attribute/ + */ + public function testInvalidAttributeName() + { + $config = array( + array('min' => 2, 'max' => 1), + ); + $request = array( + 'Attributes' => array( + 'mail' => array('joe@example.com', 'bob@example.com'), + ), + ); + self::processFilter($config, $request); + } +} diff --git a/tests/modules/core/lib/Auth/Process/PHPTest.php b/tests/modules/core/lib/Auth/Process/PHPTest.php index 0e18e9bcd9f10b052fa934a950369b222c9035e4..f4b0342e36319245d93d2e73d394a341ff0a6712 100644 --- a/tests/modules/core/lib/Auth/Process/PHPTest.php +++ b/tests/modules/core/lib/Auth/Process/PHPTest.php @@ -26,12 +26,14 @@ class Test_Core_Auth_Process_PHP extends TestCase /** * Test the configuration of the filter. - * - * @expectedException SimpleSAML_Error_Exception */ public function testInvalidConfiguration() { $config = array(); + $this->setExpectedException( + "SimpleSAML_Error_Exception", + "core:PHP: missing mandatory configuration option 'code'." + ); new sspmod_core_Auth_Process_PHP($config, null); } @@ -43,16 +45,71 @@ class Test_Core_Auth_Process_PHP extends TestCase { $config = array( 'code' => ' - $attributes["key"] = "value"; + $attributes["key"] = array("value"); ', ); $request = array('Attributes' => array()); $expected = array( 'Attributes' => array( - 'key' => 'value', + 'key' => array('value'), + ), + ); + + $this->assertEquals($expected, $this->processFilter($config, $request)); + } + + /** + * Check that the incoming attributes are also available after processing + */ + public function testPreserveIncomingAttributes() + { + $config = array( + 'code' => ' + $attributes["orig2"] = array("value0"); + ', + ); + $request = array( + 'Attributes' => array( + 'orig1' => array('value1', 'value2'), + 'orig2' => array('value3'), + 'orig3' => array('value4') + ) + ); + $expected = array( + 'Attributes' => array( + 'orig1' => array('value1', 'value2'), + 'orig2' => array('value0'), + 'orig3' => array('value4') ), ); $this->assertEquals($expected, $this->processFilter($config, $request)); } + + /** + * Check that throwing an Exception inside the PHP code of the + * filter (a documented use case) works. + */ + public function testThrowExceptionFromFilter() + { + $config = array( + 'code' => ' + if (empty($attributes["uid"])) { + throw new Exception("Missing uid attribute."); + } + $attributes["uid"][0] = strtoupper($attributes["uid"][0]); + ', + ); + $request = array( + 'Attributes' => array( + 'orig1' => array('value1', 'value2'), + ) + ); + + $this->setExpectedException( + "Exception", + "Missing uid attribute." + ); + $this->processFilter($config, $request); + } } diff --git a/tests/modules/core/lib/Storage/SQLPermanentStorageTest.php b/tests/modules/core/lib/Storage/SQLPermanentStorageTest.php new file mode 100644 index 0000000000000000000000000000000000000000..ec54de7d578090b0fdbc60e9710f3d28bef40674 --- /dev/null +++ b/tests/modules/core/lib/Storage/SQLPermanentStorageTest.php @@ -0,0 +1,86 @@ +<?php + +use PHPUnit\Framework\TestCase; + +/** + * Test for the SQLPermanentStorage class. + */ +class Test_Core_Storage_SQLPermanentStorage extends TestCase +{ + private static $sql; + + public static function setUpBeforeClass() + { + // Create instance + $config = \SimpleSAML_Configuration::loadFromArray([ + 'datadir' => sys_get_temp_dir(), + ]); + self::$sql = new sspmod_core_Storage_SQLPermanentStorage('test', $config); + } + + public static function tearDownAfterClass() + { + self::$sql = null; + unlink(sys_get_temp_dir().'/sqllite/test.sqlite'); + } + + public function testSet() + { + // Set a new value + self::$sql->set('testtype', 'testkey1', 'testkey2', 'testvalue', 0); + + // Test getCondition + $result = self::$sql->get(); + $this->assertEquals('testvalue', $result['value']); + } + + public function testSetOverwrite() + { + // Overwrite existing value + self::$sql->set('testtype', 'testkey1', 'testkey2', 'testvaluemodified', 0); + + // Test that the value was actually overwriten + $result = self::$sql->getValue('testtype', 'testkey1', 'testkey2'); + $this->assertEquals('testvaluemodified', $result); + + $result = self::$sql->getList('testtype', 'testkey1', 'testkey2'); + $this->assertEquals('testvaluemodified', $result[0]['value']); + } + + public function testNonexistentKey() + { + // Test that getting some non-existing key will return null + $result = self::$sql->getValue('testtype_nonexistent', 'testkey1_nonexistent', 'testkey2_nonexistent'); + $this->assertNull($result); + $result = self::$sql->getList('testtype_nonexistent', 'testkey1_nonexistent', 'testkey2_nonexistent'); + $this->assertNull($result); + $result = self::$sql->get('testtype_nonexistent', 'testkey1_nonexistent', 'testkey2_nonexistent'); + $this->assertNull($result); + } + + public function testExpiration() + { + // Make sure the earlier created entry has expired now + sleep(1); + + // Make sure we can't get the expired entry anymore + $result = self::$sql->getValue('testtype', 'testkey1', 'testkey2'); + $this->assertNull($result); + + // Now add a second entry that never expires + self::$sql->set('testtype', 'testkey1_nonexpiring', 'testkey2_nonexpiring', 'testvalue_nonexpiring', null); + + // Expire entries and verify that only the second one is still there + self::$sql->removeExpired(); + $result = self::$sql->getValue('testtype', 'testkey1_nonexpiring', 'testkey2_nonexpiring'); + $this->assertEquals('testvalue_nonexpiring', $result); + } + + public function testRemove() + { + // Now remove the nonexpiring entry and make sure it's gone + self::$sql->remove('testtype', 'testkey1_nonexpiring', 'testkey2_nonexpiring'); + $result = self::$sql->getValue('testtype', 'testkey1_nonexpiring', 'testkey2_nonexpiring'); + $this->assertNull($result); + } +} diff --git a/www/admin/index.php b/www/admin/index.php index a1f9558e1446a3821ec98c9038499aaf46c4daaf..65dc1e5dcc55c40b4cfa3b1e88253641c43144fa 100644 --- a/www/admin/index.php +++ b/www/admin/index.php @@ -7,7 +7,7 @@ $config = SimpleSAML_Configuration::getInstance(); $session = SimpleSAML_Session::getSessionFromRequest(); // Check if valid local session exists.. -//SimpleSAML\Utils\Auth::requireAdmin(); +SimpleSAML\Utils\Auth::requireAdmin(); $adminpages = array( 'hostnames.php' => 'Diagnostics on hostname, port and protocol', diff --git a/www/saml2/idp/metadata.php b/www/saml2/idp/metadata.php index 0ea40211490802a293d5ab317a83d75db049120e..8149c9552a1c3b6debe2fb98d883443a3338a943 100644 --- a/www/saml2/idp/metadata.php +++ b/www/saml2/idp/metadata.php @@ -130,7 +130,7 @@ try { if ($idpmeta->getBoolean('saml20.ecp', false)) { $metaArray['SingleSignOnService'][] = array( 'index' => 0, - 'Binding' => SAML2_Const::BINDING_SOAP, + 'Binding' => Constants::BINDING_SOAP, 'Location' => HTTP::getBaseURL().'saml2/idp/SSOService.php', ); }