Initial import

This commit is contained in:
Lukas Reschke 2016-06-29 00:04:23 +02:00
parent 4d2517473a
commit ada6b6ebc8
No known key found for this signature in database
GPG key ID: 9AB0ADB949B6898C
72 changed files with 12731 additions and 0 deletions

5
3rdparty/composer.json vendored Normal file
View file

@ -0,0 +1,5 @@
{
"require": {
"onelogin/php-saml": "^2.9"
}
}

73
3rdparty/composer.lock generated vendored Normal file
View file

@ -0,0 +1,73 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "993f6c41684d235f66993e52d9b7dce0",
"content-hash": "bf3d6d016eca22c120719d73eb98378d",
"packages": [
{
"name": "onelogin/php-saml",
"version": "2.9.0",
"source": {
"type": "git",
"url": "https://github.com/onelogin/php-saml.git",
"reference": "64aff7d58e68d98eaa9220e1041da2bc9214ab51"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/onelogin/php-saml/zipball/64aff7d58e68d98eaa9220e1041da2bc9214ab51",
"reference": "64aff7d58e68d98eaa9220e1041da2bc9214ab51",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-mcrypt": "*",
"ext-openssl": "*",
"php": ">=5.3.2"
},
"require-dev": {
"pdepend/pdepend": "1.1.0",
"phploc/phploc": "*",
"phpunit/phpunit": "4.8",
"satooshi/php-coveralls": "1.0.1",
"sebastian/phpcpd": "*",
"squizlabs/php_codesniffer": "*"
},
"suggest": {
"ext-gettext": "Install gettext and php5-gettext libs to handle translations",
"ext-mcrypt": "Install mcrypt and php5-mcrypt libs in order to support encryption",
"lib-openssl": "Install openssl lib in order to handle with x509 certs (require to support sign and encryption)"
},
"type": "library",
"autoload": {
"classmap": [
"extlib/xmlseclibs",
"lib/Saml",
"lib/Saml2"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "OneLogin PHP SAML Toolkit",
"homepage": "https://onelogin.zendesk.com/hc/en-us/sections/200245634-SAML-Toolkits",
"keywords": [
"SAML2",
"onelogin",
"saml"
],
"time": "2016-06-27 09:24:27"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": []
}

7
3rdparty/vendor/autoload.php vendored Normal file
View file

@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInitcc75f134f7630c1ee3a8e4d7c86f3bcc::getLoader();

413
3rdparty/vendor/composer/ClassLoader.php vendored Normal file
View file

@ -0,0 +1,413 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
if ('\\' == $class[0]) {
$class = substr($class, 1);
}
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative) {
return false;
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if ($file === null && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if ($file === null) {
// Remember that this class does not exist.
return $this->classMap[$class] = false;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
if (0 === strpos($class, $prefix)) {
foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}

21
3rdparty/vendor/composer/LICENSE vendored Normal file
View file

@ -0,0 +1,21 @@
Copyright (c) 2016 Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,27 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'OneLogin_Saml2_Auth' => $vendorDir . '/onelogin/php-saml/lib/Saml2/Auth.php',
'OneLogin_Saml2_AuthnRequest' => $vendorDir . '/onelogin/php-saml/lib/Saml2/AuthnRequest.php',
'OneLogin_Saml2_Constants' => $vendorDir . '/onelogin/php-saml/lib/Saml2/Constants.php',
'OneLogin_Saml2_Error' => $vendorDir . '/onelogin/php-saml/lib/Saml2/Error.php',
'OneLogin_Saml2_LogoutRequest' => $vendorDir . '/onelogin/php-saml/lib/Saml2/LogoutRequest.php',
'OneLogin_Saml2_LogoutResponse' => $vendorDir . '/onelogin/php-saml/lib/Saml2/LogoutResponse.php',
'OneLogin_Saml2_Metadata' => $vendorDir . '/onelogin/php-saml/lib/Saml2/Metadata.php',
'OneLogin_Saml2_Response' => $vendorDir . '/onelogin/php-saml/lib/Saml2/Response.php',
'OneLogin_Saml2_Settings' => $vendorDir . '/onelogin/php-saml/lib/Saml2/Settings.php',
'OneLogin_Saml2_Utils' => $vendorDir . '/onelogin/php-saml/lib/Saml2/Utils.php',
'OneLogin_Saml_AuthRequest' => $vendorDir . '/onelogin/php-saml/lib/Saml/AuthRequest.php',
'OneLogin_Saml_Metadata' => $vendorDir . '/onelogin/php-saml/lib/Saml/Metadata.php',
'OneLogin_Saml_Response' => $vendorDir . '/onelogin/php-saml/lib/Saml/Response.php',
'OneLogin_Saml_Settings' => $vendorDir . '/onelogin/php-saml/lib/Saml/Settings.php',
'OneLogin_Saml_XmlSec' => $vendorDir . '/onelogin/php-saml/lib/Saml/XmlSec.php',
'XMLSecEnc' => $vendorDir . '/onelogin/php-saml/extlib/xmlseclibs/xmlseclibs.php',
'XMLSecurityDSig' => $vendorDir . '/onelogin/php-saml/extlib/xmlseclibs/xmlseclibs.php',
'XMLSecurityKey' => $vendorDir . '/onelogin/php-saml/extlib/xmlseclibs/xmlseclibs.php',
);

View file

@ -0,0 +1,9 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View file

@ -0,0 +1,9 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View file

@ -0,0 +1,45 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInitcc75f134f7630c1ee3a8e4d7c86f3bcc
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInitcc75f134f7630c1ee3a8e4d7c86f3bcc', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInitcc75f134f7630c1ee3a8e4d7c86f3bcc', 'loadClassLoader'));
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
$loader->register(true);
return $loader;
}
}

58
3rdparty/vendor/composer/installed.json vendored Normal file
View file

@ -0,0 +1,58 @@
[
{
"name": "onelogin/php-saml",
"version": "2.9.0",
"version_normalized": "2.9.0.0",
"source": {
"type": "git",
"url": "https://github.com/onelogin/php-saml.git",
"reference": "64aff7d58e68d98eaa9220e1041da2bc9214ab51"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/onelogin/php-saml/zipball/64aff7d58e68d98eaa9220e1041da2bc9214ab51",
"reference": "64aff7d58e68d98eaa9220e1041da2bc9214ab51",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-mcrypt": "*",
"ext-openssl": "*",
"php": ">=5.3.2"
},
"require-dev": {
"pdepend/pdepend": "1.1.0",
"phploc/phploc": "*",
"phpunit/phpunit": "4.8",
"satooshi/php-coveralls": "1.0.1",
"sebastian/phpcpd": "*",
"squizlabs/php_codesniffer": "*"
},
"suggest": {
"ext-gettext": "Install gettext and php5-gettext libs to handle translations",
"ext-mcrypt": "Install mcrypt and php5-mcrypt libs in order to support encryption",
"lib-openssl": "Install openssl lib in order to handle with x509 certs (require to support sign and encryption)"
},
"time": "2016-06-27 09:24:27",
"type": "library",
"installation-source": "dist",
"autoload": {
"classmap": [
"extlib/xmlseclibs",
"lib/Saml",
"lib/Saml2"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "OneLogin PHP SAML Toolkit",
"homepage": "https://onelogin.zendesk.com/hc/en-us/sections/200245634-SAML-Toolkits",
"keywords": [
"SAML2",
"onelogin",
"saml"
]
}
]

View file

@ -0,0 +1,7 @@
service_name: travis-ci
src_dir: lib
coverage_clover: tests/build/logs/clover.xml
json_path: tests/build/logs/coveralls-upload.json

View file

@ -0,0 +1,14 @@
*.swp
*~
.DS_Store
/settings.php
/demo1/settings.php
/demo-old/settings.php
/certs/sp.key
/certs/sp.crt
/certs/metadata.key
/certs/metadata.crt
/tests/build
/vendor
/composer.lock
/.idea

View file

@ -0,0 +1,33 @@
language: php
php:
- 5.6
- 5.5
- 5.4
- 5.3
- 7.0
env:
- TRAVIS=true
before_install:
- curl -s https://getcomposer.org/installer | php
- php composer.phar install --prefer-source --no-interaction
before_script:
- phpenv config-rm xdebug.ini
script:
- phpunit --bootstrap tests/bootstrap.php --configuration tests/phpunit.xml
- php vendor/bin/phpcpd --exclude tests --exclude vendor .
- php vendor/bin/phploc . --exclude vendor
- php vendor/bin/phploc lib/.
- mkdir -p tests/build/dependences
- php vendor/bin/pdepend --summary-xml=tests/build/logs/dependence-summary.xml --jdepend-chart=tests/build/dependences/jdepend.svg --overview-pyramid=tests/build/dependences/pyramid.svg lib/.
- php vendor/bin/phpcs --standard=tests/ZendModStandard lib/Saml2 demo1 demo2 demo-old endpoints tests/src
after_script:
- export TRAVIS=https://travis-ci.org/onelogin/php-saml
- echo $TRAVIS
- echo $TRAVIS_JOB_ID
- php vendor/bin/coveralls --config .coveralls.yml -v

View file

@ -0,0 +1,121 @@
CHANGELOG
=========
v.2.9.0
-------
* Change the decrypt assertion process.
* Add 2 extra validations to prevent Signature wrapping attacks.
* Remove reference to wrong NameIDFormat: urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified should be urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
* [128](https://github.com/onelogin/php-saml/pull/128) Test php7 and upgrade phpunit
* Update Readme with more descriptive requestedAuthnContext description and Security Guidelines
v.2.8.0
-------
* Make NameIDPolicy of AuthNRequest optional
* Make nameID requirement on SAMLResponse optional
* Fix empty URI support
* Symmetric encryption key support
* Add more Auth Context options to the constant class
* Fix DSA_SHA1 constant on xmlseclibs
* Set none requestedAuthnContext as default behaviour
* Update xmlseclibs lib
* Improve formatPrivateKey method
* Fix bug when signing metadata, the SignatureMethod was not provided
* Fix getter for lastRequestID parameter in OneLogin_Saml2_Auth class
* Add $wantEncrypted parameter on addX509KeyDescriptors method that will allow to set KeyDescriptor[use='encryption'] if wantNameIdEncrypted or wantAssertionsEncrypted enabled
* Add $stay parameter on redirectTo method. (login/logout supports $stay but I forgot add this on previous 2.7.0 version)
* Improve code style
v.2.7.0
-------
* Trim acs, slo and issuer urls.
* Fix PHP 7 error (used continue outside a loop/switch).
* Fix bug on organization element of the SP metadata builder.
* Fix typos on documentation. Fix ALOWED Misspell.
* Be able to extract RequestID. Add RequestID validation on demo1.
* Add $stay parameter to login, logout and processSLO method.
v.2.6.1
-------
* Fix bug on cacheDuration of the Metadata XML generated.
* Make SPNameQualifier optional on the generateNameId method. Avoid the use of SPNameQualifier when generating the NameID on the LogoutRequest builder.
* Allows the authn comparsion attribute to be set via config.
* Retrieve Session Timeout after processResponse with getSessionExpiration().
* Improve readme readability.
* Allow single log out to work for applications not leveraging php session_start. Added a callback parameter in order to close the session at processSLO.
v.2.6.0
-------
* Set NAMEID_UNSPECIFIED as default NameIDFormat to prevent conflicts with IdPs that don't support NAMEID_PERSISTENT.
* Now the SP is able to select the algorithm to be used on signatures (DSA_SHA1, RSA_SHA1, RSA_SHA256, RSA_SHA384, RSA_SHA512).
* Change visibility of _decryptAssertion to protected.
* Update xmlseclibs library.
* Handle valid but uncommon dsig block with no URI in the reference.
* login, logout and processSLO now return ->redirectTo instead of just call it.
* Split the setting check methods. Now 1 method for IdP settings and other for SP settings.
* Let the setting object to avoid the IdP setting check. required if we want to publish SP SAML Metadata when the IdP data is still not provided.
v.2.5.0
-------
* Do accesible the ID of the object Logout Request (id attribute).
* Add note about the fact that PHP 5.3 is unssuported.
* Add fingerprint algorithm support.
* Add dependences to composer.
v.2.4.0
-------
* Fix wrong element order in generated metadata.
* Added SLO with nameID and SessionIndex in demo1.
* Improve isHTTPS method in order to support HTTP_X_FORWARDED_PORT.
* Set optional the XMLvalidation (enable/disable it with wantXMLValidation security setting).
v.2.3.0
-------
* Resolve namespace problem. Some IdPs uses saml2p:Response and saml2:Assertion instead of samlp:Response saml:Assertion.
* Improve test and documentation.
* Improve ADFS compatibility.
* Remove unnecessary XSDs files.
* Make available the reason for the saml message invalidation.
* Adding ability to set idp cert once the Setting object initialized.
* Fix status info issue.
* Reject SAML Response if not signed and strict = false.
* Support NameId and SessionIndex in LogoutRequest.
* Add ForceAuh and IsPassive support.
v.2.2.0
-------
* Fix bug with Encrypted nameID on LogoutRequest.
* Fixed usability bug. SP will inform about AuthFail status after process a Response.
* Added SessionIndex support on LogoutRequest, and know is accesible from the Auth class.
* LogoutRequest and LogoutResponse classes now accept non deflated xml.
* Improved the XML metadata/ Decrypted Assertion output. (prettyprint).
* Fix bug in formatPrivateKey method, the key could be not RSA.
* Explicit warning message for signed element problem.
* Decrypt method improved.
* Support more algorithm at the SigAlg in the Signed LogoutRequests and LogoutResponses
* AuthNRequest now stores ID (it can be retrieved later).
* Fixed a typo on the 'NameIdPolicy' attribute that appeared at the README and settings_example file.
v.2.1.0
-------
* The isValid method of the Logout Request is now non-static. (affects processSLO method of Auth.php).
* Logout Request constructor now accepts encoded logout requests.
* Now after validate a message, if fails a method getError of the object will return the cause.
* Fix typos.
* Added extra parameters option to login and logout methods.
* Improve Test (new test, use the new getError method for testing).
* Bugfix namespace problem when getting Attributes.
v.2.0.0
-------
* New PHP SAML Toolkit (SLO, Sign, Encryptation).
v.1.0.0
-------
* Old PHP SAML Toolkit.

View file

@ -0,0 +1,19 @@
Copyright (c) 2010-2014 OneLogin, LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,25 @@
<?php
// Create an __autoload function
// (can conflicts other autoloaders)
// http://php.net/manual/en/language.oop5.autoload.php
$libDir = dirname(__FILE__) . '/lib/Saml2/';
$extlibDir = dirname(__FILE__) . '/extlib/';
// Load composer
if (file_exists('vendor/autoload.php')) {
require 'vendor/autoload.php';
}
// Load now external libs
require_once $extlibDir . 'xmlseclibs/xmlseclibs.php';
$folderInfo = scandir($libDir);
foreach ($folderInfo as $element) {
if (is_file($libDir.$element) && (substr($element, -4) === '.php')) {
include_once $libDir.$element;
}
}

View file

@ -0,0 +1,106 @@
<?php
$advancedSettings = array (
// Security settings
'security' => array (
/** signatures and encryptions offered */
// Indicates that the nameID of the <samlp:logoutRequest> sent by this SP
// will be encrypted.
'nameIdEncrypted' => false,
// Indicates whether the <samlp:AuthnRequest> messages sent by this SP
// will be signed. [The Metadata of the SP will offer this info]
'authnRequestsSigned' => false,
// Indicates whether the <samlp:logoutRequest> messages sent by this SP
// will be signed.
'logoutRequestSigned' => false,
// Indicates whether the <samlp:logoutResponse> messages sent by this SP
// will be signed.
'logoutResponseSigned' => false,
/* Sign the Metadata
False || True (use sp certs) || array (
keyFileName => 'metadata.key',
certFileName => 'metadata.crt'
)
*/
'signMetadata' => false,
/** signatures and encryptions required **/
// Indicates a requirement for the <samlp:Response>, <samlp:LogoutRequest> and
// <samlp:LogoutResponse> elements received by this SP to be signed.
'wantMessagesSigned' => false,
// Indicates a requirement for the <saml:Assertion> elements received by
// this SP to be signed. [The Metadata of the SP will offer this info]
'wantAssertionsSigned' => false,
// Indicates a requirement for the NameID element on the SAMLResponse received
// by this SP to be present.
'wantNameId' => true,
// Indicates a requirement for the NameID received by
// this SP to be encrypted.
'wantNameIdEncrypted' => false,
// Authentication context.
// Set to false and no AuthContext will be sent in the AuthNRequest,
// Set true or don't present this parameter and you will get an AuthContext 'exact' 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'
// Set an array with the possible auth context values: array ('urn:oasis:names:tc:SAML:2.0:ac:classes:Password', 'urn:oasis:names:tc:SAML:2.0:ac:classes:X509'),
'requestedAuthnContext' => false,
// Allows the authn comparison parameter to be set, defaults to 'exact' if
// the setting is not present.
'requestedAuthnContextComparison' => 'exact',
// Indicates if the SP will validate all received xmls.
// (In order to validate the xml, 'strict' and 'wantXMLValidation' must be true).
'wantXMLValidation' => true,
// Algorithm that the toolkit will use on signing process. Options:
// 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'
// 'http://www.w3.org/2000/09/xmldsig#dsa-sha1'
// 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'
// 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384'
// 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512'
'signatureAlgorithm' => 'http://www.w3.org/2000/09/xmldsig#rsa-sha1',
),
// Contact information template, it is recommended to suply a technical and support contacts
'contactPerson' => array (
'technical' => array (
'givenName' => '',
'emailAddress' => ''
),
'support' => array (
'givenName' => '',
'emailAddress' => ''
),
),
// Organization information template, the info in en_US lang is recomended, add more if required
'organization' => array (
'en-US' => array(
'name' => '',
'displayname' => '',
'url' => ''
),
),
);
/* Interoperable SAML 2.0 Web Browser SSO Profile [saml2int] http://saml2int.org/profile/current
'authnRequestsSigned' => false, // SP SHOULD NOT sign the <samlp:AuthnRequest>,
// MUST NOT assume that the IdP validates the sign
'wantAssertionsSigned' => true,
'wantAssertionsEncrypted' => true, // MUST be enabled if SSL/HTTPs is disabled
'wantNameIdEncrypted' => false,
*/

View file

@ -0,0 +1,12 @@
<?php
/**
* Compability with the old PHP Toolkit
*/
define('ONELOGIN_SAML_DIR', 'lib/Saml/');
require_once ONELOGIN_SAML_DIR . 'AuthRequest.php';
require_once ONELOGIN_SAML_DIR . 'Response.php';
require_once ONELOGIN_SAML_DIR . 'Settings.php';
require_once ONELOGIN_SAML_DIR . 'XmlSec.php';
require_once ONELOGIN_SAML_DIR . 'Metadata.php';

View file

@ -0,0 +1,39 @@
{
"name": "onelogin/php-saml",
"description": "OneLogin PHP SAML Toolkit",
"license": "MIT",
"version": "2.9.0",
"homepage": "https://onelogin.zendesk.com/hc/en-us/sections/200245634-SAML-Toolkits",
"keywords": ["saml", "saml2", "onelogin"],
"autoload": {
"classmap": [
"extlib/xmlseclibs",
"lib/Saml",
"lib/Saml2"
]
},
"support": {
"email": "sixto.garcia@onelogin.com",
"issues": "https://github.com/onelogin/php-saml/issues",
"source": "https://github.com/onelogin/php-saml/"
},
"require": {
"php": ">=5.3.2",
"ext-openssl": "*",
"ext-dom": "*",
"ext-mcrypt": "*"
},
"require-dev": {
"phpunit/phpunit": "4.8",
"satooshi/php-coveralls": "1.0.1",
"sebastian/phpcpd": "*",
"phploc/phploc": "*",
"pdepend/pdepend" : "1.1.0",
"squizlabs/php_codesniffer": "*"
},
"suggest": {
"lib-openssl": "Install openssl lib in order to handle with x509 certs (require to support sign and encryption)",
"ext-mcrypt": "Install mcrypt and php5-mcrypt libs in order to support encryption",
"ext-gettext": "Install gettext and php5-gettext libs to handle translations"
}
}

View file

@ -0,0 +1,51 @@
<?php
/**
* SP Assertion Consumer Service Endpoint
*/
session_start();
require_once dirname(dirname(__FILE__)).'/_toolkit_loader.php';
$auth = new OneLogin_Saml2_Auth();
$auth->processResponse();
$errors = $auth->getErrors();
if (!empty($errors)) {
print_r('<p>'.implode(', ', $errors).'</p>');
exit();
}
if (!$auth->isAuthenticated()) {
echo "<p>Not authenticated</p>";
exit();
}
$_SESSION['samlUserdata'] = $auth->getAttributes();
$_SESSION['IdPSessionIndex'] = $auth->getSessionIndex();
if (isset($_POST['RelayState']) && OneLogin_Saml2_Utils::getSelfURL() != $_POST['RelayState']) {
$auth->redirectTo($_POST['RelayState']);
}
$attributes = $_SESSION['samlUserdata'];
if (!empty($attributes)) {
echo '<h1>'._('User attributes:').'</h1>';
echo '<table><thead><th>'._('Name').'</th><th>'._('Values').'</th></thead><tbody>';
foreach ($attributes as $attributeName => $attributeValues) {
echo '<tr><td>'.htmlentities($attributeName).'</td><td><ul>';
foreach ($attributeValues as $attributeValue) {
echo '<li>'.htmlentities($attributeValue).'</li>';
}
echo '</ul></td></tr>';
}
echo '</tbody></table>';
if (!empty($_SESSION['IdPSessionIndex'])) {
echo '<p>The SessionIndex of the IdP is: '.$_SESSION['IdPSessionIndex'].'</p>';
}
} else {
echo _('Attributes not found');
}

View file

@ -0,0 +1,25 @@
<?php
/**
* SP Metadata Endpoint
*/
require_once dirname(dirname(__FILE__)).'/_toolkit_loader.php';
try {
$auth = new OneLogin_Saml2_Auth();
$settings = $auth->getSettings();
$metadata = $settings->getSPMetadata();
$errors = $settings->validateMetadata($metadata);
if (empty($errors)) {
header('Content-Type: text/xml');
echo $metadata;
} else {
throw new OneLogin_Saml2_Error(
'Invalid SP metadata: '.implode(', ', $errors),
OneLogin_Saml2_Error::METADATA_SP_INVALID
);
}
} catch (Exception $e) {
echo $e->getMessage();
}

View file

@ -0,0 +1,21 @@
<?php
/**
* SP Single Logout Service Endpoint
*/
session_start();
require_once dirname(dirname(__FILE__)).'/_toolkit_loader.php';
$auth = new OneLogin_Saml2_Auth();
$auth->processSLO();
$errors = $auth->getErrors();
if (empty($errors)) {
print_r('Sucessfully logged out');
} else {
print_r(implode(', ', $errors));
}

View file

@ -0,0 +1,130 @@
xmlseclibs.php
??, ??? ????, 2.0.0
Features:
- Support for locating specific signature when multiple exist in
document. (griga3k)
23, Jun 2015, 1.4.0
Features:
- Support for PSR-0 standard.
- Support for X509SubjectName. (Milos Tomic)
- Add HMAC-SHA1 support.
Improvements:
- Add how to install to README. (Bernardo Vieira da Silva)
- Code cleanup. (Jaime Pérez)
- Normalilze tests. (Hidde Wieringa)
- Add basic usage to README. (Hidde Wieringa)
21, May 2015, 1.3.2
Bug Fixes:
- Fix Undefined variable notice. (dpieper85)
- Fix typo when setting MimeType attribute. (Eugene OZ)
- Fix validateReference() with enveloping signatures
Features:
- canonicalizeData performance optimization. (Jaime Pérez)
- Add composer support (Maks3w)
19, Jun 2013, 1.3.1
Features:
- return encrypted node from XMLSecEnc::encryptNode() when replace is set to
false. (Olav)
- Add support for RSA SHA384 and RSA_SHA512 and SHA384 digest. (Jaime PŽrez)
- Add options parameter to the add cert methods.
- Add optional issuerSerial creation with cert
Bug Fixes:
- Fix persisted Id when namespaced. (Koen Thomeer)
Improvements:
- Add LICENSE file
- Convert CHANGELOG.txt to UTF-8
26, Sep 2011, 1.3.0
Features:
- Add param to append sig to node when signing. Fixes a problem when using
inclusive canonicalization to append a signature within a namespaced subtree.
ex. $objDSig->sign($objKey, $appendToNode);
- Add ability to encrypt by reference
- Add support for refences within an encrypted key
- Add thumbprint generation capability (XMLSecurityKey->getX509Thumbprint() and
XMLSecurityKey::getRawThumbprint($cert))
- Return signature element node from XMLSecurityDSig::insertSignature() and
XMLSecurityDSig::appendSignature() methods
- Support for <ds:RetrievalMethod> with simple URI Id reference.
- Add XMLSecurityKey::getSymmetricKeySize() method (Olav)
- Add XMLSecEnc::getCipherValue() method (Olav)
- Improve XMLSecurityKey:generateSessionKey() logic (Olav)
Bug Fixes:
- Change split() to explode() as split is now depreciated
- ds:References using empty or simple URI Id reference should never include
comments in canonicalized data.
- Make sure that the elements in EncryptedData are emitted in the correct
sequence.
11 Jan 2010, 1.2.2
Features:
- Add support XPath support when creating signature. Provides support for
working with EBXML documents.
- Add reference option to force creation of URI attribute. For use
when adding a DOM Document where by default no URI attribute is added.
- Add support for RSA-SHA256
Bug Fixes:
- fix bug #5: createDOMDocumentFragment() in decryptNode when data is node
content (patch by Francois Wang)
08 Jul 2008, 1.2.1
Features:
- Attempt to use mhash when hash extension is not present. (Alfredo Cubitos).
- Add fallback to built-in sha1 if both hash and mhash are not available and
throw error for other for other missing hashes. (patch by Olav Morken).
- Add getX509Certificate method to retrieve the x509 cert used for Key.
(patch by Olav Morken).
- Add getValidatedNodes method to retrieve the elements signed by the
signature. (patch by Olav Morken).
- Add insertSignature method for precision signature insertion. Merge
functionality from appendSignature in the process. (Olav Morken, Rob).
- Finally add some tests
Bug Fixes:
- Fix canonicalization for Document node when using PHP < 5.2.
- Add padding for RSA_SHA1. (patch by Olav Morken).
27 Nov 2007, 1.2.0
Features:
- New addReference/List option (overwrite). Boolean flag indicating if URI
value should be overwritten if already existing within document.
Default is TRUE to maintain BC.
18 Nov 2007, 1.1.2
Bug Fixes:
- Remove closing PHP tag to fix extra whitespace characters from being output
11 Nov 2007, 1.1.1
Features:
- Add getRefNodeID() and getRefIDs() methods missed in previous release.
Provide functionality to find URIs of existing reference nodes.
Required by simpleSAMLphp project
Bug Fixes:
- Remove erroneous whitespace causing issues under certain circumastances.
18 Oct 2007, 1.1.0
Features:
- Enable creation of enveloping signature. This allows the creation of
managed information cards.
- Add addObject method for enveloping signatures.
- Add staticGet509XCerts method. Chained certificates within a PEM file can
now be added within the X509Data node.
- Add xpath support within transformations
- Add InclusiveNamespaces prefix list support within exclusive transformations.
Bug Fixes:
- Initialize random number generator for mcrypt_create_iv. (Joan Cornadó).
- Fix an interoperability issue with .NET when encrypting data in CBC mode.
(Joan Cornadó).

View file

@ -0,0 +1,31 @@
Copyright (c) 2007-2013, Robert Richards <rrichards@cdatazone.org>.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Robert Richards nor the names of his
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,65 @@
<?php
class OneLogin_Saml_AuthRequest
{
/**
* @var OneLogin_Saml2_Auth object
*/
protected $auth;
/**
* Constructs the OneLogin_Saml2_Auth, initializing
* the SP SAML instance.
*
* @param array|object $settings SAML Toolkit Settings
*/
public function __construct($settings)
{
$this->auth = new OneLogin_Saml2_Auth($settings);
}
/**
* Obtains the SSO URL containing the AuthRequest
* message deflated.
*
* @param string|null $returnTo
*
* @return string
*
* @throws OneLogin_Saml2_Error
*/
public function getRedirectUrl($returnTo = null)
{
$settings = $this->auth->getSettings();
$authnRequest = new OneLogin_Saml2_AuthnRequest($settings);
$parameters = array('SAMLRequest' => $authnRequest->getRequest());
if (!empty($returnTo)) {
$parameters['RelayState'] = $returnTo;
} else {
$parameters['RelayState'] = OneLogin_Saml2_Utils::getSelfRoutedURLNoQuery();
}
$url = OneLogin_Saml2_Utils::redirect($this->auth->getSSOurl(), $parameters, true);
return $url;
}
/**
* @return string
*/
protected function _generateUniqueID()
{
return OneLogin_Saml2_Utils::generateUniqueID();
}
/**
* @return string
*/
protected function _getTimestamp()
{
$defaultTimezone = date_default_timezone_get();
date_default_timezone_set('UTC');
$timestamp = strftime("%Y-%m-%dT%H:%M:%SZ");
date_default_timezone_set($defaultTimezone);
return $timestamp;
}
}

View file

@ -0,0 +1,39 @@
<?php
class OneLogin_Saml_Metadata
{
const VALIDITY_SECONDS = 604800; // 1 week
protected $_settings;
/**
* @param array|object|null $settings Setting data
*/
public function __construct($settings = null)
{
$auth = new OneLogin_Saml2_Auth($settings);
$this->_settings = $auth->getSettings();
}
/**
* @return string
*
* @throws OneLogin_Saml2_Error
*/
public function getXml()
{
return $this->_settings->getSPMetadata();
}
/**
* @return string
*/
protected function _getMetadataValidTimestamp()
{
$timeZone = date_default_timezone_get();
date_default_timezone_set('UTC');
$time = strftime("%Y-%m-%dT%H:%M:%SZ", time() + self::VALIDITY_SECONDS);
date_default_timezone_set($timeZone);
return $time;
}
}

View file

@ -0,0 +1,39 @@
<?php
class OneLogin_Saml_Response extends OneLogin_Saml2_Response
{
/**
* Constructor that process the SAML Response,
* Internally initializes an SP SAML instance
* and an OneLogin_Saml2_Response.
*
* @param array|object $oldSettings Settings
* @param string $assertion SAML Response
*/
public function __construct($oldSettings, $assertion)
{
$auth = new OneLogin_Saml2_Auth($oldSettings);
$settings = $auth->getSettings();
parent::__construct($settings, $assertion);
}
/**
* Retrieves an Array with the logged user data.
*
* @return array
*/
public function get_saml_attributes()
{
return $this->getAttributes();
}
/**
* Retrieves the nameId
*
* @return string
*/
public function get_nameid()
{
return $this->getNameId();
}
}

View file

@ -0,0 +1,80 @@
<?php
/**
* Holds SAML settings for the SamlResponse and SamlAuthRequest classes.
*
* These settings need to be filled in by the user prior to being used.
*/
class OneLogin_Saml_Settings
{
const NAMEID_EMAIL_ADDRESS = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress';
const NAMEID_X509_SUBJECT_NAME = 'urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName';
const NAMEID_WINDOWS_DOMAIN_QUALIFIED_NAME = 'urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName';
const NAMEID_KERBEROS = 'urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos';
const NAMEID_ENTITY = 'urn:oasis:names:tc:SAML:2.0:nameid-format:entity';
const NAMEID_TRANSIENT = 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient';
const NAMEID_PERSISTENT = 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent';
/**
* The URL to submit SAML authentication requests to.
* @var string
*/
public $idpSingleSignOnUrl = '';
/**
* The URL to submit SAML Logout Request to.
* @var string
*/
public $idpSingleLogOutUrl = '';
/**
* The x509 certificate used to authenticate the request.
* @var string
*/
public $idpPublicCertificate = '';
/**
* The URL where to the SAML Response/SAML Assertion will be posted.
* @var string
*/
public $spReturnUrl = '';
/**
* The name of the application.
* @var string
*/
public $spIssuer = 'php-saml';
/**
* Specifies what format to return the authentication token, i.e, the email address.
* @var string
*/
public $requestedNameIdFormat = self::NAMEID_EMAIL_ADDRESS;
/**
* @return array<string,array> Values (compatibility with the new version)
*/
public function getValues()
{
$values = array();
$values['sp'] = array();
$values['sp']['entityId'] = $this->spIssuer;
$values['sp']['assertionConsumerService'] = array(
'url' => $this->spReturnUrl,
);
$values['sp']['NameIDFormat'] = $this->requestedNameIdFormat;
$values['idp'] = array();
$values['idp']['entityId'] = $this->idpSingleSignOnUrl;
$values['idp']['singleSignOnService'] = array(
'url' => $this->idpSingleSignOnUrl,
);
$values['idp']['singleLogoutService'] = array(
'url' => $this->idpSingleLogOutUrl,
);
$values['idp']['x509cert'] = $this->idpPublicCertificate;
return $values;
}
}

View file

@ -0,0 +1,110 @@
<?php
/**
* Determine if the SAML response is valid using a provided x509 certificate.
*/
class OneLogin_Saml_XmlSec
{
/**
* A SamlResponse class provided to the constructor.
* @var OneLogin_Saml_Settings
*/
protected $_settings;
/**
* The document to be tested.
* @var DomDocument
*/
protected $_document;
/**
* Construct the SamlXmlSec object.
*
* @param OneLogin_Saml_Settings $settings A SamlResponse settings object containing the necessary
* x509 certicate to test the document.
* @param OneLogin_Saml_Response $response The document to test.
*/
public function __construct(OneLogin_Saml_Settings $settings, OneLogin_Saml_Response $response)
{
$this->_settings = $settings;
$this->_document = clone $response->document;
}
/**
* Verify that the document only contains a single Assertion
*
* @return bool TRUE if the document passes.
*/
public function validateNumAssertions()
{
$rootNode = $this->_document;
$assertionNodes = $rootNode->getElementsByTagName('Assertion');
return ($assertionNodes->length == 1);
}
/**
* Verify that the document is still valid according
*
* @return bool
*/
public function validateTimestamps()
{
$rootNode = $this->_document;
$timestampNodes = $rootNode->getElementsByTagName('Conditions');
for ($i = 0; $i < $timestampNodes->length; $i++) {
$nbAttribute = $timestampNodes->item($i)->attributes->getNamedItem("NotBefore");
$naAttribute = $timestampNodes->item($i)->attributes->getNamedItem("NotOnOrAfter");
if ($nbAttribute && strtotime($nbAttribute->textContent) > time()) {
return false;
}
if ($naAttribute && strtotime($naAttribute->textContent) <= time()) {
return false;
}
}
return true;
}
/**
* @return bool
*
* @throws Exception
*/
public function isValid()
{
$singleAssertion = $this->validateNumAssertions();
if (!$singleAssertion) {
throw new Exception('Multiple assertions are not supported');
}
$validTimestamps = $this->validateTimestamps();
if (!$validTimestamps) {
throw new Exception('Timing issues (please check your clock settings)');
}
$objXMLSecDSig = new XMLSecurityDSig();
$objDSig = $objXMLSecDSig->locateSignature($this->_document);
if (!$objDSig) {
throw new Exception('Cannot locate Signature Node');
}
$objXMLSecDSig->canonicalizeSignedInfo();
$objXMLSecDSig->idKeys = array('ID');
$objKey = $objXMLSecDSig->locateKey();
if (!$objKey) {
throw new Exception('We have no idea about the key');
}
try {
$objXMLSecDSig->validateReference();
} catch (Exception $e) {
throw new Exception('Reference Validation Failed');
}
XMLSecEnc::staticLocateKeyInfo($objKey, $objDSig);
$objKey->loadKey($this->_settings->idpPublicCertificate, false, true);
return ($objXMLSecDSig->verify($objKey) === 1);
}
}

View file

@ -0,0 +1,514 @@
<?php
/**
* Main class of OneLogin's PHP Toolkit
*
*/
class OneLogin_Saml2_Auth
{
/**
* Settings data.
*
* @var OneLogin_Saml2_Settings
*/
private $_settings;
/**
* User attributes data.
*
* @var array
*/
private $_attributes = array();
/**
* NameID
*
* @var string
*/
private $_nameid;
/**
* If user is authenticated.
*
* @var bool
*/
private $_authenticated = false;
/**
* SessionIndex. When the user is logged, this stored it
* from the AuthnStatement of the SAML Response
*
* @var string
*/
private $_sessionIndex;
/**
* SessionNotOnOrAfter. When the user is logged, this stored it
* from the AuthnStatement of the SAML Response
*
* @var DateTime
*/
private $_sessionExpiration;
/**
* If any error.
*
* @var array
*/
private $_errors = array();
/**
* Reason of the last error.
*
* @var string
*/
private $_errorReason;
/**
* Last AuthNRequest ID or LogoutRequest ID generated by this Service Provider
*
* @var string
*/
private $_lastRequestID;
/**
* Initializes the SP SAML instance.
*
* @param array|object|null $oldSettings Setting data (You can provide a OneLogin_Saml_Settings, the settings object of the Saml folder implementation)
*/
public function __construct($oldSettings = null)
{
$this->_settings = new OneLogin_Saml2_Settings($oldSettings);
}
/**
* Returns the settings info
*
* @return OneLogin_Saml2_Settings The settings data.
*/
public function getSettings()
{
return $this->_settings;
}
/**
* Set the strict mode active/disable
*
* @param bool $value Strict parameter
*
* @return array The settings data.
*/
public function setStrict($value)
{
assert('is_bool($value)');
$this->_settings->setStrict($value);
}
/**
* Process the SAML Response sent by the IdP.
*
* @param string|null $requestId The ID of the AuthNRequest sent by this SP to the IdP
*
* @throws OneLogin_Saml2_Error
*/
public function processResponse($requestId = null)
{
$this->_errors = array();
if (isset($_POST) && isset($_POST['SAMLResponse'])) {
// AuthnResponse -- HTTP_POST Binding
$response = new OneLogin_Saml2_Response($this->_settings, $_POST['SAMLResponse']);
if ($response->isValid($requestId)) {
$this->_attributes = $response->getAttributes();
$this->_nameid = $response->getNameId();
$this->_authenticated = true;
$this->_sessionIndex = $response->getSessionIndex();
$this->_sessionExpiration = $response->getSessionNotOnOrAfter();
} else {
$this->_errors[] = 'invalid_response';
$this->_errorReason = $response->getError();
}
} else {
$this->_errors[] = 'invalid_binding';
throw new OneLogin_Saml2_Error(
'SAML Response not found, Only supported HTTP_POST Binding',
OneLogin_Saml2_Error::SAML_RESPONSE_NOT_FOUND
);
}
}
/**
* Process the SAML Logout Response / Logout Request sent by the IdP.
*
* @param bool $keepLocalSession When false will destroy the local session, otherwise will keep it
* @param string|null $requestId The ID of the LogoutRequest sent by this SP to the IdP
* @param bool $retrieveParametersFromServer
* @param callable $cbDeleteSession
* @param bool $stay True if we want to stay (returns the url string) False to redirect
*
* @return string|void
*
* @throws OneLogin_Saml2_Error
*/
public function processSLO($keepLocalSession = false, $requestId = null, $retrieveParametersFromServer = false, $cbDeleteSession = null, $stay=false)
{
$this->_errors = array();
if (isset($_GET) && isset($_GET['SAMLResponse'])) {
$logoutResponse = new OneLogin_Saml2_LogoutResponse($this->_settings, $_GET['SAMLResponse']);
if (!$logoutResponse->isValid($requestId, $retrieveParametersFromServer)) {
$this->_errors[] = 'invalid_logout_response';
$this->_errorReason = $logoutResponse->getError();
} else if ($logoutResponse->getStatus() !== OneLogin_Saml2_Constants::STATUS_SUCCESS) {
$this->_errors[] = 'logout_not_success';
} else {
if (!$keepLocalSession) {
if ($cbDeleteSession === null) {
OneLogin_Saml2_Utils::deleteLocalSession();
} else {
call_user_func($cbDeleteSession);
}
}
}
} else if (isset($_GET) && isset($_GET['SAMLRequest'])) {
$logoutRequest = new OneLogin_Saml2_LogoutRequest($this->_settings, $_GET['SAMLRequest']);
if (!$logoutRequest->isValid($retrieveParametersFromServer)) {
$this->_errors[] = 'invalid_logout_request';
$this->_errorReason = $logoutRequest->getError();
} else {
if (!$keepLocalSession) {
if ($cbDeleteSession === null) {
OneLogin_Saml2_Utils::deleteLocalSession();
} else {
call_user_func($cbDeleteSession);
}
}
$inResponseTo = $logoutRequest->id;
$responseBuilder = new OneLogin_Saml2_LogoutResponse($this->_settings);
$responseBuilder->build($inResponseTo);
$logoutResponse = $responseBuilder->getResponse();
$parameters = array('SAMLResponse' => $logoutResponse);
if (isset($_GET['RelayState'])) {
$parameters['RelayState'] = $_GET['RelayState'];
}
$security = $this->_settings->getSecurityData();
if (isset($security['logoutResponseSigned']) && $security['logoutResponseSigned']) {
$signature = $this->buildResponseSignature($logoutResponse, $parameters['RelayState'], $security['signatureAlgorithm']);
$parameters['SigAlg'] = $security['signatureAlgorithm'];
$parameters['Signature'] = $signature;
}
return $this->redirectTo($this->getSLOurl(), $parameters, $stay);
}
} else {
$this->_errors[] = 'invalid_binding';
throw new OneLogin_Saml2_Error(
'SAML LogoutRequest/LogoutResponse not found. Only supported HTTP_REDIRECT Binding',
OneLogin_Saml2_Error::SAML_LOGOUTMESSAGE_NOT_FOUND
);
}
}
/**
* Redirects the user to the url past by parameter
* or to the url that we defined in our SSO Request.
*
* @param string $url The target URL to redirect the user.
* @param array $parameters Extra parameters to be passed as part of the url
* @param bool $stay True if we want to stay (returns the url string) False to redirect
*/
public function redirectTo($url = '', $parameters = array(), $stay = false)
{
assert('is_string($url)');
assert('is_array($parameters)');
if (empty($url) && isset($_REQUEST['RelayState'])) {
$url = $_REQUEST['RelayState'];
}
return OneLogin_Saml2_Utils::redirect($url, $parameters, $stay);
}
/**
* Checks if the user is authenticated or not.
*
* @return bool True if the user is authenticated
*/
public function isAuthenticated()
{
return $this->_authenticated;
}
/**
* Returns the set of SAML attributes.
*
* @return array Attributes of the user.
*/
public function getAttributes()
{
return $this->_attributes;
}
/**
* Returns the nameID
*
* @return string The nameID of the assertion
*/
public function getNameId()
{
return $this->_nameid;
}
/**
* Returns the SessionIndex
*
* @return string|null The SessionIndex of the assertion
*/
public function getSessionIndex()
{
return $this->_sessionIndex;
}
/**
* Returns the SessionNotOnOrAfter
*
* @return DateTime|null The SessionNotOnOrAfter of the assertion
*/
public function getSessionExpiration()
{
return $this->_sessionExpiration;
}
/**
* Returns if there were any error
*
* @return array Errors
*/
public function getErrors()
{
return $this->_errors;
}
/**
* Returns the reason for the last error
*
* @return string Error reason
*/
public function getLastErrorReason()
{
return $this->_errorReason;
}
/**
* Returns the requested SAML attribute
*
* @param string $name The requested attribute of the user.
*
* @return array|null Requested SAML attribute ($name).
*/
public function getAttribute($name)
{
assert('is_string($name)');
$value = null;
if (isset($this->_attributes[$name])) {
return $this->_attributes[$name];
}
return $value;
}
/**
* Initiates the SSO process.
*
* @param string|null $returnTo The target URL the user should be returned to after login.
* @param array $parameters Extra parameters to be added to the GET
* @param bool $forceAuthn When true the AuthNReuqest will set the ForceAuthn='true'
* @param bool $isPassive When true the AuthNReuqest will set the Ispassive='true'
* @param bool $stay True if we want to stay (returns the url string) False to redirect
* @param bool $setNameIdPolicy When true the AuthNReuqest will set a nameIdPolicy element
*
* @return If $stay is True, it return a string with the SLO URL + LogoutRequest + parameters
*/
public function login($returnTo = null, $parameters = array(), $forceAuthn = false, $isPassive = false, $stay=false, $setNameIdPolicy = true)
{
assert('is_array($parameters)');
$authnRequest = new OneLogin_Saml2_AuthnRequest($this->_settings, $forceAuthn, $isPassive, $setNameIdPolicy);
$this->_lastRequestID = $authnRequest->getId();
$samlRequest = $authnRequest->getRequest();
$parameters['SAMLRequest'] = $samlRequest;
if (!empty($returnTo)) {
$parameters['RelayState'] = $returnTo;
} else {
$parameters['RelayState'] = OneLogin_Saml2_Utils::getSelfRoutedURLNoQuery();
}
$security = $this->_settings->getSecurityData();
if (isset($security['authnRequestsSigned']) && $security['authnRequestsSigned']) {
$signature = $this->buildRequestSignature($samlRequest, $parameters['RelayState'], $security['signatureAlgorithm']);
$parameters['SigAlg'] = $security['signatureAlgorithm'];
$parameters['Signature'] = $signature;
}
return $this->redirectTo($this->getSSOurl(), $parameters, $stay);
}
/**
* Initiates the SLO process.
*
* @param string|null $returnTo The target URL the user should be returned to after logout.
* @param array $parameters Extra parameters to be added to the GET
* @param string|null $nameId The NameID that will be set in the LogoutRequest.
* @param string|null $sessionIndex The SessionIndex (taken from the SAML Response in the SSO process).
* @param bool $stay True if we want to stay (returns the url string) False to redirect
*
* @return If $stay is True, it return a string with the SLO URL + LogoutRequest + parameters
*
* @throws OneLogin_Saml2_Error
*/
public function logout($returnTo = null, $parameters = array(), $nameId = null, $sessionIndex = null, $stay=false)
{
assert('is_array($parameters)');
$sloUrl = $this->getSLOurl();
if (empty($sloUrl)) {
throw new OneLogin_Saml2_Error(
'The IdP does not support Single Log Out',
OneLogin_Saml2_Error::SAML_SINGLE_LOGOUT_NOT_SUPPORTED
);
}
if (empty($nameId) && !empty($this->_nameid)) {
$nameId = $this->_nameid;
}
$logoutRequest = new OneLogin_Saml2_LogoutRequest($this->_settings, null, $nameId, $sessionIndex);
$this->_lastRequestID = $logoutRequest->id;
$samlRequest = $logoutRequest->getRequest();
$parameters['SAMLRequest'] = $samlRequest;
if (!empty($returnTo)) {
$parameters['RelayState'] = $returnTo;
} else {
$parameters['RelayState'] = OneLogin_Saml2_Utils::getSelfRoutedURLNoQuery();
}
$security = $this->_settings->getSecurityData();
if (isset($security['logoutRequestSigned']) && $security['logoutRequestSigned']) {
$signature = $this->buildRequestSignature($samlRequest, $parameters['RelayState'], $security['signatureAlgorithm']);
$parameters['SigAlg'] = $security['signatureAlgorithm'];
$parameters['Signature'] = $signature;
}
return $this->redirectTo($sloUrl, $parameters, $stay);
}
/**
* Gets the SSO url.
*
* @return string The url of the Single Sign On Service
*/
public function getSSOurl()
{
$idpData = $this->_settings->getIdPData();
return $idpData['singleSignOnService']['url'];
}
/**
* Gets the SLO url.
*
* @return string The url of the Single Logout Service
*/
public function getSLOurl()
{
$url = null;
$idpData = $this->_settings->getIdPData();
if (isset($idpData['singleLogoutService']) && isset($idpData['singleLogoutService']['url'])) {
$url = $idpData['singleLogoutService']['url'];
}
return $url;
}
/**
* Gets the ID of the last AuthNRequest or LogoutRequest generated by the Service Provider.
*
* @return string The ID of the Request SAML message.
*/
public function getLastRequestID()
{
return $this->_lastRequestID;
}
/**
* Generates the Signature for a SAML Request
*
* @param string $samlRequest The SAML Request
* @param string $relayState The RelayState
* @param string $signAlgorithm Signature algorithm method
*
* @return string A base64 encoded signature
*
* @throws Exception
* @throws OneLogin_Saml2_Error
*/
public function buildRequestSignature($samlRequest, $relayState, $signAlgorithm = XMLSecurityKey::RSA_SHA1)
{
if (!$this->_settings->checkSPCerts()) {
throw new OneLogin_Saml2_Error(
"Trying to sign the SAML Request but can't load the SP certs",
OneLogin_Saml2_Error::SP_CERTS_NOT_FOUND
);
}
$key = $this->_settings->getSPkey();
$objKey = new XMLSecurityKey($signAlgorithm, array('type' => 'private'));
$objKey->loadKey($key, false);
$msg = 'SAMLRequest='.urlencode($samlRequest);
$msg .= '&RelayState='.urlencode($relayState);
$msg .= '&SigAlg=' . urlencode($signAlgorithm);
$signature = $objKey->signData($msg);
return base64_encode($signature);
}
/**
* Generates the Signature for a SAML Response
*
* @param string $samlResponse The SAML Response
* @param string $relayState The RelayState
* @param string $signAlgorithm Signature algorithm method
*
* @return string A base64 encoded signature
*
* @throws Exception
* @throws OneLogin_Saml2_Error
*/
public function buildResponseSignature($samlResponse, $relayState, $signAlgorithm = XMLSecurityKey::RSA_SHA1)
{
if (!$this->_settings->checkSPCerts()) {
throw new OneLogin_Saml2_Error(
"Trying to sign the SAML Response but can't load the SP certs",
OneLogin_Saml2_Error::SP_CERTS_NOT_FOUND
);
}
$key = $this->_settings->getSPkey();
$objKey = new XMLSecurityKey($signAlgorithm, array('type' => 'private'));
$objKey->loadKey($key, false);
$msg = 'SAMLResponse='.urlencode($samlResponse);
$msg .= '&RelayState='.urlencode($relayState);
$msg .= '&SigAlg=' . urlencode($signAlgorithm);
$signature = $objKey->signData($msg);
return base64_encode($signature);
}
}

View file

@ -0,0 +1,158 @@
<?php
/**
* SAML 2 Authentication Request
*
*/
class OneLogin_Saml2_AuthnRequest
{
/**
* Object that represents the setting info
* @var OneLogin_Saml2_Settings
*/
protected $_settings;
/**
* SAML AuthNRequest string
* @var string
*/
private $_authnRequest;
/**
* SAML AuthNRequest ID.
* @var string
*/
private $_id;
/**
* Constructs the AuthnRequest object.
*
* @param OneLogin_Saml2_Settings $settings Settings
* @param bool $forceAuthn When true the AuthNReuqest will set the ForceAuthn='true'
* @param bool $isPassive When true the AuthNReuqest will set the Ispassive='true'
* @param bool $setNameIdPolicy When true the AuthNReuqest will set a nameIdPolicy
*/
public function __construct(OneLogin_Saml2_Settings $settings, $forceAuthn = false, $isPassive = false, $setNameIdPolicy = true)
{
$this->_settings = $settings;
$spData = $this->_settings->getSPData();
$idpData = $this->_settings->getIdPData();
$security = $this->_settings->getSecurityData();
$id = OneLogin_Saml2_Utils::generateUniqueID();
$issueInstant = OneLogin_Saml2_Utils::parseTime2SAML(time());
$nameIdPolicyStr = '';
if ($setNameIdPolicy) {
$nameIDPolicyFormat = $spData['NameIDFormat'];
if (isset($security['wantNameIdEncrypted']) && $security['wantNameIdEncrypted']) {
$nameIDPolicyFormat = OneLogin_Saml2_Constants::NAMEID_ENCRYPTED;
}
$nameIdPolicyStr = <<<NAMEIDPOLICY
<samlp:NameIDPolicy
Format="{$nameIDPolicyFormat}"
AllowCreate="true" />
NAMEIDPOLICY;
}
$providerNameStr = '';
$organizationData = $settings->getOrganization();
if (!empty($organizationData)) {
$langs = array_keys($organizationData);
if (in_array('en-US', $langs)) {
$lang = 'en-US';
} else {
$lang = $langs[0];
}
if (isset($organizationData[$lang]['displayname']) && !empty($organizationData[$lang]['displayname'])) {
$providerNameStr = <<<PROVIDERNAME
ProviderName="{$organizationData[$lang]['displayname']}"
PROVIDERNAME;
}
}
$forceAuthnStr = '';
if ($forceAuthn) {
$forceAuthnStr = <<<FORCEAUTHN
ForceAuthn="true"
FORCEAUTHN;
}
$isPassiveStr = '';
if ($isPassive) {
$isPassiveStr = <<<ISPASSIVE
IsPassive="true"
ISPASSIVE;
}
$requestedAuthnStr = '';
if (isset($security['requestedAuthnContext']) && $security['requestedAuthnContext'] !== false) {
$authnComparison = 'exact';
if (isset($security['requestedAuthnContextComparison'])) {
$authnComparison = $security['requestedAuthnContextComparison'];
}
if ($security['requestedAuthnContext'] === true) {
$requestedAuthnStr = <<<REQUESTEDAUTHN
<samlp:RequestedAuthnContext Comparison="$authnComparison">
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
</samlp:RequestedAuthnContext>
REQUESTEDAUTHN;
} else {
$requestedAuthnStr .= " <samlp:RequestedAuthnContext Comparison=\"$authnComparison\">\n";
foreach ($security['requestedAuthnContext'] as $contextValue) {
$requestedAuthnStr .= " <saml:AuthnContextClassRef>".$contextValue."</saml:AuthnContextClassRef>\n";
}
$requestedAuthnStr .= ' </samlp:RequestedAuthnContext>';
}
}
$request = <<<AUTHNREQUEST
<samlp:AuthnRequest
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="$id"
Version="2.0"
{$providerNameStr}{$forceAuthnStr}{$isPassiveStr}
IssueInstant="$issueInstant"
Destination="{$idpData['singleSignOnService']['url']}"
ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
AssertionConsumerServiceURL="{$spData['assertionConsumerService']['url']}">
<saml:Issuer>{$spData['entityId']}</saml:Issuer>
{$nameIdPolicyStr}
{$requestedAuthnStr}
</samlp:AuthnRequest>
AUTHNREQUEST;
$this->_id = $id;
$this->_authnRequest = $request;
}
/**
* Returns deflated, base64 encoded, unsigned AuthnRequest.
*
*/
public function getRequest()
{
$deflatedRequest = gzdeflate($this->_authnRequest);
$base64Request = base64_encode($deflatedRequest);
return $base64Request;
}
/**
* Returns the AuthNRequest ID.
*
* @return string
*/
public function getId()
{
return $this->_id;
}
}

View file

@ -0,0 +1,69 @@
<?php
/**
* Constants of OneLogin PHP Toolkit
*
* Defines all required constants
*/
class OneLogin_Saml2_Constants
{
// Value added to the current time in time condition validations
const ALLOWED_CLOCK_DRIFT = 180; // 3 min in seconds
// NameID Formats
const NAMEID_EMAIL_ADDRESS = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress';
const NAMEID_X509_SUBJECT_NAME = 'urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName';
const NAMEID_WINDOWS_DOMAIN_QUALIFIED_NAME = 'urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName';
const NAMEID_UNSPECIFIED = 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified';
const NAMEID_KERBEROS = 'urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos';
const NAMEID_ENTITY = 'urn:oasis:names:tc:SAML:2.0:nameid-format:entity';
const NAMEID_TRANSIENT = 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient';
const NAMEID_PERSISTENT = 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent';
const NAMEID_ENCRYPTED = 'urn:oasis:names:tc:SAML:2.0:nameid-format:encrypted';
// Attribute Name Formats
const ATTRNAME_FORMAT_UNSPECIFIED = 'urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified';
const ATTRNAME_FORMAT_URI = 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri';
const ATTRNAME_FORMAT_BASIC = 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic';
// Namespaces
const NS_SAML = 'urn:oasis:names:tc:SAML:2.0:assertion';
const NS_SAMLP = 'urn:oasis:names:tc:SAML:2.0:protocol';
const NS_SOAP = 'http://schemas.xmlsoap.org/soap/envelope/';
const NS_MD = 'urn:oasis:names:tc:SAML:2.0:metadata';
const NS_XS = 'http://www.w3.org/2001/XMLSchema';
const NS_XSI = 'http://www.w3.org/2001/XMLSchema-instance';
const NS_XENC = 'http://www.w3.org/2001/04/xmlenc#';
const NS_DS = 'http://www.w3.org/2000/09/xmldsig#';
// Bindings
const BINDING_HTTP_POST = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST';
const BINDING_HTTP_REDIRECT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect';
const BINDING_HTTP_ARTIFACT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact';
const BINDING_SOAP = 'urn:oasis:names:tc:SAML:2.0:bindings:SOAP';
const BINDING_DEFLATE = 'urn:oasis:names:tc:SAML:2.0:bindings:URL-Encoding:DEFLATE';
// Auth Context Class
const AC_UNSPECIFIED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified';
const AC_PASSWORD = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Password';
const AC_PASSWORD_PROTECTED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport';
const AC_X509 = 'urn:oasis:names:tc:SAML:2.0:ac:classes:X509';
const AC_SMARTCARD = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Smartcard';
const AC_KERBEROS = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos';
const AC_WINDOWS = 'urn:federation:authentication:windows';
const AC_TLS = 'urn:oasis:names:tc:SAML:2.0:ac:classes:TLSClient';
// Subject Confirmation
const CM_BEARER = 'urn:oasis:names:tc:SAML:2.0:cm:bearer';
const CM_HOLDER_KEY = 'urn:oasis:names:tc:SAML:2.0:cm:holder-of-key';
const CM_SENDER_VOUCHES = 'urn:oasis:names:tc:SAML:2.0:cm:sender-vouches';
// Status Codes
const STATUS_SUCCESS = 'urn:oasis:names:tc:SAML:2.0:status:Success';
const STATUS_REQUESTER = 'urn:oasis:names:tc:SAML:2.0:status:Requester';
const STATUS_RESPONDER = 'urn:oasis:names:tc:SAML:2.0:status:Responder';
const STATUS_VERSION_MISMATCH = 'urn:oasis:names:tc:SAML:2.0:status:VersionMismatch';
const STATUS_NO_PASSIVE = 'urn:oasis:names:tc:SAML:2.0:status:NoPassive';
const STATUS_PARTIAL_LOGOUT = 'urn:oasis:names:tc:SAML:2.0:status:PartialLogout';
const STATUS_PROXY_COUNT_EXCEEDED = 'urn:oasis:names:tc:SAML:2.0:status:ProxyCountExceeded';
}

View file

@ -0,0 +1,41 @@
<?php
/**
* Error class of OneLogin PHP Toolkit
*
* Defines the Error class
*/
class OneLogin_Saml2_Error extends Exception
{
// Errors
const SETTINGS_FILE_NOT_FOUND = 0;
const SETTINGS_INVALID_SYNTAX = 1;
const SETTINGS_INVALID = 2;
const METADATA_SP_INVALID = 3;
const SP_CERTS_NOT_FOUND = 4;
const REDIRECT_INVALID_URL = 5;
const PUBLIC_CERT_FILE_NOT_FOUND = 6;
const PRIVATE_KEY_FILE_NOT_FOUND = 7;
const SAML_RESPONSE_NOT_FOUND = 8;
const SAML_LOGOUTMESSAGE_NOT_FOUND = 9;
const SAML_LOGOUTREQUEST_INVALID = 10;
const SAML_LOGOUTRESPONSE_INVALID = 11;
const SAML_SINGLE_LOGOUT_NOT_SUPPORTED = 12;
/**
* Constructor
*
* @param string $msg Describes the error.
* @param int $code The code error (defined in the error class).
* @param array|null $args Arguments used in the message that describes the error.
*/
public function __construct($msg, $code = 0, $args = null)
{
assert('is_string($msg)');
assert('is_int($code)');
$message = OneLogin_Saml2_Utils::t($msg, $args);
parent::__construct($message, $code);
}
}

View file

@ -0,0 +1,384 @@
<?php
/**
* SAML 2 Logout Request
*
*/
class OneLogin_Saml2_LogoutRequest
{
/**
* Contains the ID of the Logout Request
* @var string
*/
public $id;
/**
* Object that represents the setting info
* @var OneLogin_Saml2_Settings
*/
protected $_settings;
/**
* SAML Logout Request
* @var string
*/
protected $_logoutRequest;
/**
* After execute a validation process, this var contains the cause
* @var string
*/
private $_error;
/**
* Constructs the Logout Request object.
*
* @param OneLogin_Saml2_Settings $settings Settings
* @param string|null $request A UUEncoded Logout Request.
* @param string|null $nameId The NameID that will be set in the LogoutRequest.
* @param string|null $sessionIndex The SessionIndex (taken from the SAML Response in the SSO process).
*/
public function __construct(OneLogin_Saml2_Settings $settings, $request = null, $nameId = null, $sessionIndex = null)
{
$this->_settings = $settings;
if (!isset($request) || empty($request)) {
$spData = $this->_settings->getSPData();
$idpData = $this->_settings->getIdPData();
$security = $this->_settings->getSecurityData();
$id = OneLogin_Saml2_Utils::generateUniqueID();
$this->id = $id;
$nameIdValue = OneLogin_Saml2_Utils::generateUniqueID();
$issueInstant = OneLogin_Saml2_Utils::parseTime2SAML(time());
$cert = null;
if (isset($security['nameIdEncrypted']) && $security['nameIdEncrypted']) {
$cert = $idpData['x509cert'];
}
if (!empty($nameId)) {
$nameIdFormat = $spData['NameIDFormat'];
$spNameQualifier = null;
} else {
$nameId = $idpData['entityId'];
$nameIdFormat = OneLogin_Saml2_Constants::NAMEID_ENTITY;
$spNameQualifier = $spData['entityId'];
}
$nameIdObj = OneLogin_Saml2_Utils::generateNameId(
$nameId,
$spNameQualifier,
$nameIdFormat,
$cert
);
$sessionIndexStr = isset($sessionIndex) ? "<samlp:SessionIndex>{$sessionIndex}</samlp:SessionIndex>" : "";
$logoutRequest = <<<LOGOUTREQUEST
<samlp:LogoutRequest
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="{$id}"
Version="2.0"
IssueInstant="{$issueInstant}"
Destination="{$idpData['singleLogoutService']['url']}">
<saml:Issuer>{$spData['entityId']}</saml:Issuer>
{$nameIdObj}
{$sessionIndexStr}
</samlp:LogoutRequest>
LOGOUTREQUEST;
} else {
$decoded = base64_decode($request);
// We try to inflate
$inflated = @gzinflate($decoded);
if ($inflated != false) {
$logoutRequest = $inflated;
} else {
$logoutRequest = $decoded;
}
$this->id = self::getID($logoutRequest);
}
$this->_logoutRequest = $logoutRequest;
}
/**
* Returns the Logout Request defated, base64encoded, unsigned
*
* @return string Deflated base64 encoded Logout Request
*/
public function getRequest()
{
$deflatedRequest = gzdeflate($this->_logoutRequest);
return base64_encode($deflatedRequest);
}
/**
* Returns the ID of the Logout Request.
*
* @param string|DOMDocument $request Logout Request Message
*
* @return string ID
*/
public static function getID($request)
{
if ($request instanceof DOMDocument) {
$dom = $request;
} else {
$dom = new DOMDocument();
$dom = OneLogin_Saml2_Utils::loadXML($dom, $request);
}
$id = $dom->documentElement->getAttribute('ID');
return $id;
}
/**
* Gets the NameID Data of the the Logout Request.
*
* @param string|DOMDocument $request Logout Request Message
* @param string|null $key The SP key
*
* @return array Name ID Data (Value, Format, NameQualifier, SPNameQualifier)
*
* @throws Exception
*/
public static function getNameIdData($request, $key = null)
{
if ($request instanceof DOMDocument) {
$dom = $request;
} else {
$dom = new DOMDocument();
$dom = OneLogin_Saml2_Utils::loadXML($dom, $request);
}
$encryptedEntries = OneLogin_Saml2_Utils::query($dom, '/samlp:LogoutRequest/saml:EncryptedID');
if ($encryptedEntries->length == 1) {
$encryptedDataNodes = $encryptedEntries->item(0)->getElementsByTagName('EncryptedData');
$encryptedData = $encryptedDataNodes->item(0);
if (empty($key)) {
throw new Exception("Key is required in order to decrypt the NameID");
}
$seckey = new XMLSecurityKey(XMLSecurityKey::RSA_1_5, array('type'=>'private'));
$seckey->loadKey($key);
$nameId = OneLogin_Saml2_Utils::decryptElement($encryptedData, $seckey);
} else {
$entries = OneLogin_Saml2_Utils::query($dom, '/samlp:LogoutRequest/saml:NameID');
if ($entries->length == 1) {
$nameId = $entries->item(0);
}
}
if (!isset($nameId)) {
throw new Exception("Not NameID found in the Logout Request");
}
$nameIdData = array();
$nameIdData['Value'] = $nameId->nodeValue;
foreach (array('Format', 'SPNameQualifier', 'NameQualifier') as $attr) {
if ($nameId->hasAttribute($attr)) {
$nameIdData[$attr] = $nameId->getAttribute($attr);
}
}
return $nameIdData;
}
/**
* Gets the NameID of the Logout Request.
*
* @param string|DOMDocument $request Logout Request Message
* @param string|null $key The SP key
*
* @return string Name ID Value
*/
public static function getNameId($request, $key = null)
{
$nameId = self::getNameIdData($request, $key);
return $nameId['Value'];
}
/**
* Gets the Issuer of the Logout Request.
*
* @param string|DOMDocument $request Logout Request Message
*
* @return string|null $issuer The Issuer
*/
public static function getIssuer($request)
{
if ($request instanceof DOMDocument) {
$dom = $request;
} else {
$dom = new DOMDocument();
$dom = OneLogin_Saml2_Utils::loadXML($dom, $request);
}
$issuer = null;
$issuerNodes = OneLogin_Saml2_Utils::query($dom, '/samlp:LogoutRequest/saml:Issuer');
if ($issuerNodes->length == 1) {
$issuer = $issuerNodes->item(0)->textContent;
}
return $issuer;
}
/**
* Gets the SessionIndexes from the Logout Request.
* Notice: Our Constructor only support 1 SessionIndex but this parser
* extracts an array of all the SessionIndex found on a
* Logout Request, that could be many.
*
* @param string|DOMDocument $request Logout Request Message
*
* @return array The SessionIndex value
*/
public static function getSessionIndexes($request)
{
if ($request instanceof DOMDocument) {
$dom = $request;
} else {
$dom = new DOMDocument();
$dom = OneLogin_Saml2_Utils::loadXML($dom, $request);
}
$sessionIndexes = array();
$sessionIndexNodes = OneLogin_Saml2_Utils::query($dom, '/samlp:LogoutRequest/samlp:SessionIndex');
foreach ($sessionIndexNodes as $sessionIndexNode) {
$sessionIndexes[] = $sessionIndexNode->textContent;
}
return $sessionIndexes;
}
/**
* Checks if the Logout Request recieved is valid.
*
* @return bool If the Logout Request is or not valid
*/
public function isValid($retrieveParametersFromServer=false)
{
$this->_error = null;
try {
$dom = new DOMDocument();
$dom = OneLogin_Saml2_Utils::loadXML($dom, $this->_logoutRequest);
$idpData = $this->_settings->getIdPData();
$idPEntityId = $idpData['entityId'];
if ($this->_settings->isStrict()) {
$security = $this->_settings->getSecurityData();
if ($security['wantXMLValidation']) {
$res = OneLogin_Saml2_Utils::validateXML($dom, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive());
if (!$res instanceof DOMDocument) {
throw new Exception("Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd");
}
}
$currentURL = OneLogin_Saml2_Utils::getSelfRoutedURLNoQuery();
// Check NotOnOrAfter
if ($dom->documentElement->hasAttribute('NotOnOrAfter')) {
$na = OneLogin_Saml2_Utils::parseSAML2Time($dom->documentElement->getAttribute('NotOnOrAfter'));
if ($na <= time()) {
throw new Exception('Timing issues (please check your clock settings)');
}
}
// Check destination
if ($dom->documentElement->hasAttribute('Destination')) {
$destination = $dom->documentElement->getAttribute('Destination');
if (!empty($destination)) {
if (strpos($destination, $currentURL) === false) {
throw new Exception("The LogoutRequest was received at $currentURL instead of $destination");
}
}
}
$nameId = $this->getNameId($dom, $this->_settings->getSPkey());
// Check issuer
$issuer = $this->getIssuer($dom);
if (!empty($issuer) && $issuer != $idPEntityId) {
throw new Exception("Invalid issuer in the Logout Request");
}
if ($security['wantMessagesSigned']) {
if (!isset($_GET['Signature'])) {
throw new Exception("The Message of the Logout Request is not signed and the SP require it");
}
}
}
if (isset($_GET['Signature'])) {
if (!isset($_GET['SigAlg'])) {
$signAlg = XMLSecurityKey::RSA_SHA1;
} else {
$signAlg = $_GET['SigAlg'];
}
if ($retrieveParametersFromServer) {
$signedQuery = 'SAMLRequest='.OneLogin_Saml2_Utils::extractOriginalQueryParam('SAMLRequest');
if (isset($_GET['RelayState'])) {
$signedQuery .= '&RelayState='.OneLogin_Saml2_Utils::extractOriginalQueryParam('RelayState');
}
$signedQuery .= '&SigAlg='.OneLogin_Saml2_Utils::extractOriginalQueryParam('SigAlg');
} else {
$signedQuery = 'SAMLRequest='.urlencode($_GET['SAMLRequest']);
if (isset($_GET['RelayState'])) {
$signedQuery .= '&RelayState='.urlencode($_GET['RelayState']);
}
$signedQuery .= '&SigAlg='.urlencode($signAlg);
}
if (!isset($idpData['x509cert']) || empty($idpData['x509cert'])) {
throw new Exception('In order to validate the sign on the Logout Request, the x509cert of the IdP is required');
}
$cert = $idpData['x509cert'];
$objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type' => 'public'));
$objKey->loadKey($cert, false, true);
if ($signAlg != XMLSecurityKey::RSA_SHA1) {
try {
$objKey = OneLogin_Saml2_Utils::castKey($objKey, $signAlg, 'public');
} catch (Exception $e) {
throw new Exception('Invalid signAlg in the recieved Logout Request');
}
}
if (!$objKey->verifySignature($signedQuery, base64_decode($_GET['Signature']))) {
throw new Exception('Signature validation failed. Logout Request rejected');
}
}
return true;
} catch (Exception $e) {
$this->_error = $e->getMessage();
$debug = $this->_settings->isDebugActive();
if ($debug) {
echo $this->_error;
}
return false;
}
}
/* After execute a validation process, if fails this method returns the cause
*
* @return string Cause
*/
public function getError()
{
return $this->_error;
}
}

View file

@ -0,0 +1,264 @@
<?php
/**
* SAML 2 Logout Response
*
*/
class OneLogin_Saml2_LogoutResponse
{
/**
* Object that represents the setting info
* @var OneLogin_Saml2_Settings
*/
protected $_settings;
/**
* The decoded, unprocessed XML response provided to the constructor.
* @var string
*/
protected $_logoutResponse;
/**
* A DOMDocument class loaded from the SAML LogoutResponse.
* @var DomDocument
*/
public $document;
/**
* After execute a validation process, if it fails, this var contains the cause
* @var string
*/
private $_error;
/**
* Constructs a Logout Response object (Initialize params from settings and if provided
* load the Logout Response.
*
* @param OneLogin_Saml2_Settings $settings Settings.
* @param string|null $response An UUEncoded SAML Logout response from the IdP.
*/
public function __construct(OneLogin_Saml2_Settings $settings, $response = null)
{
$this->_settings = $settings;
if ($response) {
$decoded = base64_decode($response);
$inflated = @gzinflate($decoded);
if ($inflated != false) {
$this->_logoutResponse = $inflated;
} else {
$this->_logoutResponse = $decoded;
}
$this->document = new DOMDocument();
$this->document = OneLogin_Saml2_Utils::loadXML($this->document, $this->_logoutResponse);
}
}
/**
* Gets the Issuer of the Logout Response.
*
* @return string|null $issuer The Issuer
*/
public function getIssuer()
{
$issuer = null;
$issuerNodes = $this->_query('/samlp:LogoutResponse/saml:Issuer');
if ($issuerNodes->length == 1) {
$issuer = $issuerNodes->item(0)->textContent;
}
return $issuer;
}
/**
* Gets the Status of the Logout Response.
*
* @return string The Status
*/
public function getStatus()
{
$entries = $this->_query('/samlp:LogoutResponse/samlp:Status/samlp:StatusCode');
if ($entries->length == 0) {
return null;
}
$status = $entries->item(0)->getAttribute('Value');
return $status;
}
/**
* Determines if the SAML LogoutResponse is valid
*
* @param string|null $requestId The ID of the LogoutRequest sent by this SP to the IdP
* @param bool $retrieveParametersFromServer
*
* @return bool Returns if the SAML LogoutResponse is or not valid
*
* @throws Exception
*/
public function isValid($requestId = null, $retrieveParametersFromServer=false)
{
$this->_error = null;
try {
$idpData = $this->_settings->getIdPData();
$idPEntityId = $idpData['entityId'];
if ($this->_settings->isStrict()) {
$security = $this->_settings->getSecurityData();
if ($security['wantXMLValidation']) {
$res = OneLogin_Saml2_Utils::validateXML($this->document, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive());
if (!$res instanceof DOMDocument) {
throw new Exception("Invalid SAML Logout Response. Not match the saml-schema-protocol-2.0.xsd");
}
}
// Check if the InResponseTo of the Logout Response matchs the ID of the Logout Request (requestId) if provided
if (isset($requestId) && $this->document->documentElement->hasAttribute('InResponseTo')) {
$inResponseTo = $this->document->documentElement->getAttribute('InResponseTo');
if ($requestId != $inResponseTo) {
throw new Exception("The InResponseTo of the Logout Response: $inResponseTo, does not match the ID of the Logout request sent by the SP: $requestId");
}
}
// Check issuer
$issuer = $this->getIssuer();
if (!empty($issuer) && $issuer != $idPEntityId) {
throw new Exception("Invalid issuer in the Logout Response");
}
$currentURL = OneLogin_Saml2_Utils::getSelfRoutedURLNoQuery();
// Check destination
if ($this->document->documentElement->hasAttribute('Destination')) {
$destination = $this->document->documentElement->getAttribute('Destination');
if (!empty($destination)) {
if (strpos($destination, $currentURL) === false) {
throw new Exception("The LogoutResponse was received at $currentURL instead of $destination");
}
}
}
if ($security['wantMessagesSigned']) {
if (!isset($_GET['Signature'])) {
throw new Exception("The Message of the Logout Response is not signed and the SP requires it");
}
}
}
if (isset($_GET['Signature'])) {
if (!isset($_GET['SigAlg'])) {
$signAlg = XMLSecurityKey::RSA_SHA1;
} else {
$signAlg = $_GET['SigAlg'];
}
if ($retrieveParametersFromServer) {
$signedQuery = 'SAMLResponse='.OneLogin_Saml2_Utils::extractOriginalQueryParam('SAMLResponse');
if (isset($_GET['RelayState'])) {
$signedQuery .= '&RelayState='.OneLogin_Saml2_Utils::extractOriginalQueryParam('RelayState');
}
$signedQuery .= '&SigAlg='.OneLogin_Saml2_Utils::extractOriginalQueryParam('SigAlg');
} else {
$signedQuery = 'SAMLResponse='.urlencode($_GET['SAMLResponse']);
if (isset($_GET['RelayState'])) {
$signedQuery .= '&RelayState='.urlencode($_GET['RelayState']);
}
$signedQuery .= '&SigAlg='.urlencode($signAlg);
}
if (!isset($idpData['x509cert']) || empty($idpData['x509cert'])) {
throw new Exception('In order to validate the sign on the Logout Response, the x509cert of the IdP is required');
}
$cert = $idpData['x509cert'];
$objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type' => 'public'));
$objKey->loadKey($cert, false, true);
if ($signAlg != XMLSecurityKey::RSA_SHA1) {
try {
$objKey = OneLogin_Saml2_Utils::castKey($objKey, $signAlg, 'public');
} catch (Exception $e) {
throw new Exception('Invalid signAlg in the recieved Logout Response');
}
}
if (!$objKey->verifySignature($signedQuery, base64_decode($_GET['Signature']))) {
throw new Exception('Signature validation failed. Logout Response rejected');
}
}
return true;
} catch (Exception $e) {
$this->_error = $e->getMessage();
$debug = $this->_settings->isDebugActive();
if ($debug) {
echo $this->_error;
}
return false;
}
}
/**
* Extracts a node from the DOMDocument (Logout Response Menssage)
*
* @param string $query Xpath Expresion
*
* @return DOMNodeList The queried node
*/
private function _query($query)
{
return OneLogin_Saml2_Utils::query($this->document, $query);
}
/**
* Generates a Logout Response object.
*
* @param string $inResponseTo InResponseTo value for the Logout Response.
*/
public function build($inResponseTo)
{
$spData = $this->_settings->getSPData();
$idpData = $this->_settings->getIdPData();
$id = OneLogin_Saml2_Utils::generateUniqueID();
$issueInstant = OneLogin_Saml2_Utils::parseTime2SAML(time());
$logoutResponse = <<<LOGOUTRESPONSE
<samlp:LogoutResponse xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="{$id}"
Version="2.0"
IssueInstant="{$issueInstant}"
Destination="{$idpData['singleLogoutService']['url']}"
InResponseTo="{$inResponseTo}"
>
<saml:Issuer>{$spData['entityId']}</saml:Issuer>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" />
</samlp:Status>
</samlp:LogoutResponse>
LOGOUTRESPONSE;
$this->_logoutResponse = $logoutResponse;
}
/**
* Returns a Logout Response object.
*
* @return string Logout Response deflated and base64 encoded
*/
public function getResponse()
{
$deflatedResponse = gzdeflate($this->_logoutResponse);
return base64_encode($deflatedResponse);
}
/* After execute a validation process, if fails this method returns the cause.
*
* @return string Cause
*/
public function getError()
{
return $this->_error;
}
}

View file

@ -0,0 +1,184 @@
<?php
/**
* Metadata lib of OneLogin PHP Toolkit
*
*/
class OneLogin_Saml2_Metadata
{
const TIME_VALID = 172800; // 2 days
const TIME_CACHED = 604800; // 1 week
/**
* Generates the metadata of the SP based on the settings
*
* @param array $sp The SP data
* @param bool|string $authnsign authnRequestsSigned attribute
* @param bool|string $wsign wantAssertionsSigned attribute
* @param DateTime|null $validUntil Metadata's valid time
* @param int|null $cacheDuration Duration of the cache in seconds
* @param array $contacts Contacts info
* @param array $organization Organization ingo
* @param array $attributes
* @return string SAML Metadata XML
*/
public static function builder($sp, $authnsign = false, $wsign = false, $validUntil = null, $cacheDuration = null, $contacts = array(), $organization = array(), $attributes = array())
{
if (!isset($validUntil)) {
$validUntil = time() + self::TIME_VALID;
}
$validUntilTime = gmdate('Y-m-d\TH:i:s\Z', $validUntil);
if (!isset($cacheDuration)) {
$cacheDuration = self::TIME_CACHED;
}
$sls = '';
if (isset($sp['singleLogoutService'])) {
$sls = <<<SLS_TEMPLATE
<md:SingleLogoutService Binding="{$sp['singleLogoutService']['binding']}"
Location="{$sp['singleLogoutService']['url']}" />
SLS_TEMPLATE;
}
if ($authnsign) {
$strAuthnsign = 'true';
} else {
$strAuthnsign = 'false';
}
if ($wsign) {
$strWsign = 'true';
} else {
$strWsign = 'false';
}
$strOrganization = '';
if (!empty($organization)) {
$organizationInfoNames = array();
$organizationInfoDisplaynames = array();
$organizationInfoUrls = array();
foreach ($organization as $lang => $info) {
$organizationInfoNames[] = <<<ORGANIZATION_NAME
<md:OrganizationName xml:lang="{$lang}">{$info['name']}</md:OrganizationName>
ORGANIZATION_NAME;
$organizationInfoDisplaynames[] = <<<ORGANIZATION_DISPLAY
<md:OrganizationDisplayName xml:lang="{$lang}">{$info['displayname']}</md:OrganizationDisplayName>
ORGANIZATION_DISPLAY;
$organizationInfoUrls[] = <<<ORGANIZATION_URL
<md:OrganizationURL xml:lang="{$lang}">{$info['url']}</md:OrganizationURL>
ORGANIZATION_URL;
}
$orgData = implode("\n", $organizationInfoNames)."\n".implode("\n", $organizationInfoDisplaynames)."\n".implode("\n", $organizationInfoUrls);
$strOrganization = <<<ORGANIZATIONSTR
<md:Organization>
{$orgData}
</md:Organization>
ORGANIZATIONSTR;
}
$strContacts = '';
if (!empty($contacts)) {
$contactsInfo = array();
foreach ($contacts as $type => $info) {
$contactsInfo[] = <<<CONTACT
<md:ContactPerson contactType="{$type}">
<md:GivenName>{$info['givenName']}</md:GivenName>
<md:EmailAddress>{$info['emailAddress']}</md:EmailAddress>
</md:ContactPerson>
CONTACT;
}
$strContacts = "\n".implode("\n", $contactsInfo);
}
$metadata = <<<METADATA_TEMPLATE
<?xml version="1.0"?>
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
validUntil="{$validUntilTime}"
cacheDuration="PT{$cacheDuration}S"
entityID="{$sp['entityId']}">
<md:SPSSODescriptor AuthnRequestsSigned="{$strAuthnsign}" WantAssertionsSigned="{$strWsign}" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
{$sls} <md:NameIDFormat>{$sp['NameIDFormat']}</md:NameIDFormat>
<md:AssertionConsumerService Binding="{$sp['assertionConsumerService']['binding']}"
Location="{$sp['assertionConsumerService']['url']}"
index="1" />
</md:SPSSODescriptor>{$strOrganization}{$strContacts}
</md:EntityDescriptor>
METADATA_TEMPLATE;
return $metadata;
}
/**
* Signs the metadata with the key/cert provided
*
* @param string $metadata SAML Metadata XML
* @param string $key x509 key
* @param string $cert x509 cert
*
* @return string Signed Metadata
*/
public static function signMetadata($metadata, $key, $cert, $signAlgorithm = XMLSecurityKey::RSA_SHA1)
{
return OneLogin_Saml2_Utils::addSign($metadata, $key, $cert, $signAlgorithm);
}
/**
* Adds the x509 descriptors (sign/encriptation) to the metadata
* The same cert will be used for sign/encrypt
*
* @param string $metadata SAML Metadata XML
* @param string $cert x509 cert
* @param bool $wantsEncrypted Whether to include the KeyDescriptor for encryption
*
* @return string Metadata with KeyDescriptors
*/
public static function addX509KeyDescriptors($metadata, $cert, $wantsEncrypted = true)
{
$xml = new DOMDocument();
$xml->preserveWhiteSpace = false;
$xml->formatOutput = true;
try {
$xml = OneLogin_Saml2_Utils::loadXML($xml, $metadata);
if (!$xml) {
throw new Exception('Error parsing metadata');
}
} catch (Exception $e) {
throw new Exception('Error parsing metadata. '.$e->getMessage());
}
$formatedCert = OneLogin_Saml2_Utils::formatCert($cert, false);
$x509Certificate = $xml->createElementNS(OneLogin_Saml2_Constants::NS_DS, 'X509Certificate', $formatedCert);
$keyData = $xml->createElementNS(OneLogin_Saml2_Constants::NS_DS, 'ds:X509Data');
$keyData->appendChild($x509Certificate);
$keyInfo = $xml->createElementNS(OneLogin_Saml2_Constants::NS_DS, 'ds:KeyInfo');
$keyInfo->appendChild($keyData);
$keyDescriptor = $xml->createElementNS(OneLogin_Saml2_Constants::NS_MD, "md:KeyDescriptor");
$SPSSODescriptor = $xml->getElementsByTagName('SPSSODescriptor')->item(0);
$SPSSODescriptor->insertBefore($keyDescriptor->cloneNode(), $SPSSODescriptor->firstChild);
if ($wantsEncrypted === true) {
$SPSSODescriptor->insertBefore($keyDescriptor->cloneNode(), $SPSSODescriptor->firstChild);
}
$signing = $xml->getElementsByTagName('KeyDescriptor')->item(0);
$signing->setAttribute('use', 'signing');
$signing->appendChild($keyInfo);
if ($wantsEncrypted === true) {
$encryption = $xml->getElementsByTagName('KeyDescriptor')->item(1);
$encryption->setAttribute('use', 'encryption');
$encryption->appendChild($keyInfo->cloneNode(true));
}
return $xml->saveXML();
}
}

View file

@ -0,0 +1,754 @@
<?php
/**
* SAML 2 Authentication Response
*
*/
class OneLogin_Saml2_Response
{
/**
* Settings
* @var OneLogin_Saml2_Settings
*/
protected $_settings;
/**
* The decoded, unprocessed XML response provided to the constructor.
* @var string
*/
public $response;
/**
* A DOMDocument class loaded from the SAML Response.
* @var DomDocument
*/
public $document;
/**
* A DOMDocument class loaded from the SAML Response (Decrypted).
* @var DomDocument
*/
public $decryptedDocument;
/**
* The response contains an encrypted assertion.
* @var bool
*/
public $encrypted = false;
/**
* After validation, if it fail this var has the cause of the problem
* @var string
*/
private $_error;
/**
* Constructs the SAML Response object.
*
* @param OneLogin_Saml2_Settings $settings Settings.
* @param string $response A UUEncoded SAML response from the IdP.
*
* @throws Exception
*/
public function __construct(OneLogin_Saml2_Settings $settings, $response)
{
$this->_settings = $settings;
$this->response = base64_decode($response);
$this->document = new DOMDocument();
$this->document = OneLogin_Saml2_Utils::loadXML($this->document, $this->response);
if (!$this->document) {
throw new Exception('SAML Response could not be processed');
}
// Quick check for the presence of EncryptedAssertion
$encryptedAssertionNodes = $this->document->getElementsByTagName('EncryptedAssertion');
if ($encryptedAssertionNodes->length !== 0) {
$this->decryptedDocument = clone $this->document;
$this->encrypted = true;
$this->_decryptAssertion($this->decryptedDocument);
}
}
/**
* Determines if the SAML Response is valid using the certificate.
*
* @param string|null $requestId The ID of the AuthNRequest sent by this SP to the IdP
*
* @return bool Validate the document
*
* @throws Exception
*/
public function isValid($requestId = null)
{
$this->_error = null;
try {
// Check SAML version
if ($this->document->documentElement->getAttribute('Version') != '2.0') {
throw new Exception('Unsupported SAML version');
}
if (!$this->document->documentElement->hasAttribute('ID')) {
throw new Exception('Missing ID attribute on SAML Response');
}
$status = $this->checkStatus();
$singleAssertion = $this->validateNumAssertions();
if (!$singleAssertion) {
throw new Exception('SAML Response must contain 1 assertion');
}
$idpData = $this->_settings->getIdPData();
$idPEntityId = $idpData['entityId'];
$spData = $this->_settings->getSPData();
$spEntityId = $spData['entityId'];
$signedElements = $this->processSignedElements();
if ($this->_settings->isStrict()) {
$security = $this->_settings->getSecurityData();
if ($security['wantXMLValidation']) {
$errorXmlMsg = "Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd";
$res = OneLogin_Saml2_Utils::validateXML($this->document, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive());
if (!$res instanceof DOMDocument) {
throw new Exception($errorXmlMsg);
}
# If encrypted, check also the decrypted document
if ($this->encrypted) {
$res = OneLogin_Saml2_Utils::validateXML($this->decryptedDocument, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive());
if (!$res instanceof DOMDocument) {
throw new Exception($errorXmlMsg);
}
}
}
$currentURL = OneLogin_Saml2_Utils::getSelfRoutedURLNoQuery();
if ($this->document->documentElement->hasAttribute('InResponseTo')) {
$responseInResponseTo = $this->document->documentElement->getAttribute('InResponseTo');
}
// Check if the InResponseTo of the Response matchs the ID of the AuthNRequest (requestId) if provided
if (isset($requestId) && isset($responseInResponseTo)) {
if ($requestId != $responseInResponseTo) {
throw new Exception("The InResponseTo of the Response: $responseInResponseTo, does not match the ID of the AuthNRequest sent by the SP: $requestId");
}
}
if (!$this->encrypted && $security['wantAssertionsEncrypted']) {
throw new Exception("The assertion of the Response is not encrypted and the SP requires it");
}
if ($security['wantNameIdEncrypted']) {
$encryptedIdNodes = $this->_queryAssertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData');
if ($encryptedIdNodes->length == 0) {
throw new Exception("The NameID of the Response is not encrypted and the SP requires it");
}
}
// Validate Asserion timestamps
$validTimestamps = $this->validateTimestamps();
if (!$validTimestamps) {
throw new Exception('Timing issues (please check your clock settings)');
}
// EncryptedAttributes are not supported
$encryptedAttributeNodes = $this->_queryAssertion('/saml:AttributeStatement/saml:EncryptedAttribute');
if ($encryptedAttributeNodes->length > 0) {
throw new Exception("There is an EncryptedAttribute in the Response and this SP not support them");
}
// Check destination
if ($this->document->documentElement->hasAttribute('Destination')) {
$destination = trim($this->document->documentElement->getAttribute('Destination'));
if (!empty($destination)) {
if (strpos($destination, $currentURL) !== 0) {
$currentURLrouted = OneLogin_Saml2_Utils::getSelfRoutedURLNoQuery();
if (strpos($destination, $currentURLrouted) !== 0) {
throw new Exception("The response was received at $currentURL instead of $destination");
}
}
}
}
// Check audience
$validAudiences = $this->getAudiences();
if (!empty($validAudiences) && !in_array($spEntityId, $validAudiences)) {
throw new Exception("$spEntityId is not a valid audience for this Response");
}
// Check the issuers
$issuers = $this->getIssuers();
foreach ($issuers as $issuer) {
$trimmedIssuer = trim($issuer);
if (empty($trimmedIssuer) || $trimmedIssuer !== $idPEntityId) {
throw new Exception("Invalid issuer in the Assertion/Response");
}
}
// Check the session Expiration
$sessionExpiration = $this->getSessionNotOnOrAfter();
if (!empty($sessionExpiration) && $sessionExpiration <= time()) {
throw new Exception("The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response");
}
// Check the SubjectConfirmation, at least one SubjectConfirmation must be valid
$anySubjectConfirmation = false;
$subjectConfirmationNodes = $this->_queryAssertion('/saml:Subject/saml:SubjectConfirmation');
foreach ($subjectConfirmationNodes as $scn) {
if ($scn->hasAttribute('Method') && $scn->getAttribute('Method') != OneLogin_Saml2_Constants::CM_BEARER) {
continue;
}
$subjectConfirmationDataNodes = $scn->getElementsByTagName('SubjectConfirmationData');
if ($subjectConfirmationDataNodes->length == 0) {
continue;
} else {
$scnData = $subjectConfirmationDataNodes->item(0);
if ($scnData->hasAttribute('InResponseTo')) {
$inResponseTo = $scnData->getAttribute('InResponseTo');
if ($responseInResponseTo != $inResponseTo) {
continue;
}
}
if ($scnData->hasAttribute('Recipient')) {
$recipient = $scnData->getAttribute('Recipient');
if (!empty($recipient) && strpos($recipient, $currentURL) === false) {
continue;
}
}
if ($scnData->hasAttribute('NotOnOrAfter')) {
$noa = OneLogin_Saml2_Utils::parseSAML2Time($scnData->getAttribute('NotOnOrAfter'));
if ($noa <= time()) {
continue;
}
}
if ($scnData->hasAttribute('NotBefore')) {
$nb = OneLogin_Saml2_Utils::parseSAML2Time($scnData->getAttribute('NotBefore'));
if ($nb > time()) {
continue;
}
}
$anySubjectConfirmation = true;
break;
}
}
if (!$anySubjectConfirmation) {
throw new Exception("A valid SubjectConfirmation was not found on this Response");
}
if ($security['wantAssertionsSigned'] && !in_array('Assertion', $signedElements)) {
throw new Exception("The Assertion of the Response is not signed and the SP requires it");
}
if ($security['wantMessagesSigned'] && !in_array('Response', $signedElements)) {
throw new Exception("The Message of the Response is not signed and the SP requires it");
}
}
if (!empty($signedElements)) {
$cert = $idpData['x509cert'];
$fingerprint = $idpData['certFingerprint'];
$fingerprintalg = $idpData['certFingerprintAlgorithm'];
# If find a Signature on the Response, validates it checking the original response
if (in_array('Response', $signedElements)) {
$documentToValidate = $this->document;
} else {
# Otherwise validates the assertion (decrypted assertion if was encrypted)
$documentToValidate = $this->decryptedDocument;
if ($this->encrypted) {
$documentToValidate = $this->decryptedDocument;
$encryptedIDNodes = OneLogin_Saml2_Utils::query($this->decryptedDocument, '/samlp:Response/saml:Assertion/saml:Subject/saml:EncryptedID');
if ($encryptedIDNodes->length > 0) {
throw new Exception('Unsigned SAML Response that contains a signed and encrypted Assertion with encrypted nameId is not supported.');
}
} else {
$documentToValidate = $this->document;
}
}
if (!OneLogin_Saml2_Utils::validateSign($documentToValidate, $cert, $fingerprint, $fingerprintalg)) {
throw new Exception('Signature validation failed. SAML Response rejected');
}
} else {
throw new Exception('No Signature found. SAML Response rejected');
}
return true;
} catch (Exception $e) {
$this->_error = $e->getMessage();
$debug = $this->_settings->isDebugActive();
if ($debug) {
echo $this->_error;
}
return false;
}
}
/**
* Checks if the Status is success
*
* @throws $statusExceptionMsg If status is not success
*/
public function checkStatus()
{
$status = OneLogin_Saml2_Utils::getStatus($this->document);
if (isset($status['code']) && $status['code'] !== OneLogin_Saml2_Constants::STATUS_SUCCESS) {
$explodedCode = explode(':', $status['code']);
$printableCode = array_pop($explodedCode);
$statusExceptionMsg = 'The status code of the Response was not Success, was '.$printableCode;
if (!empty($status['msg'])) {
$statusExceptionMsg .= ' -> '.$status['msg'];
}
throw new Exception($statusExceptionMsg);
}
}
/**
* Gets the audiences.
*
* @return array @audience The valid audiences of the response
*/
public function getAudiences()
{
$audiences = array();
$entries = $this->_queryAssertion('/saml:Conditions/saml:AudienceRestriction/saml:Audience');
foreach ($entries as $entry) {
$value = trim($entry->textContent);
if (!empty($value)) {
$audiences[] = $value;
}
}
return array_unique($audiences);
}
/**
* Gets the Issuers (from Response and Assertion).
*
* @return array @issuers The issuers of the assertion/response
*/
public function getIssuers()
{
$issuers = array();
$responseIssuer = $this->_query('/samlp:Response/saml:Issuer');
if ($responseIssuer->length == 1) {
$issuers[] = $responseIssuer->item(0)->textContent;
}
$assertionIssuer = $this->_queryAssertion('/saml:Issuer');
if ($assertionIssuer->length == 1) {
$issuers[] = $assertionIssuer->item(0)->textContent;
}
return array_unique($issuers);
}
/**
* Gets the NameID Data provided by the SAML response from the IdP.
*
* @return array Name ID Data (Value, Format, NameQualifier, SPNameQualifier)
*/
public function getNameIdData()
{
$encryptedIdDataEntries = $this->_queryAssertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData');
if ($encryptedIdDataEntries->length == 1) {
$encryptedData = $encryptedIdDataEntries->item(0);
$key = $this->_settings->getSPkey();
$seckey = new XMLSecurityKey(XMLSecurityKey::RSA_1_5, array('type'=>'private'));
$seckey->loadKey($key);
$nameId = OneLogin_Saml2_Utils::decryptElement($encryptedData, $seckey);
} else {
$entries = $this->_queryAssertion('/saml:Subject/saml:NameID');
if ($entries->length == 1) {
$nameId = $entries->item(0);
}
}
$nameIdData = array();
if (!isset($nameId)) {
$security = $this->_settings->getSecurityData();
if ($security['wantNameId']) {
throw new Exception("Not NameID found in the assertion of the Response");
}
} else {
$nameIdData['Value'] = $nameId->nodeValue;
foreach (array('Format', 'SPNameQualifier', 'NameQualifier') as $attr) {
if ($nameId->hasAttribute($attr)) {
$nameIdData[$attr] = $nameId->getAttribute($attr);
}
}
}
return $nameIdData;
}
/**
* Gets the NameID provided by the SAML response from the IdP.
*
* @return string Name ID Value
*/
public function getNameId()
{
$nameIdvalue = null;
$nameIdData = $this->getNameIdData();
if (!empty($nameIdData) && isset($nameIdData['Value'])) {
$nameIdvalue = $nameIdData['Value'];
}
return $nameIdvalue;
}
/**
* Gets the SessionNotOnOrAfter from the AuthnStatement.
* Could be used to set the local session expiration
*
* @return int|null The SessionNotOnOrAfter value
*/
public function getSessionNotOnOrAfter()
{
$notOnOrAfter = null;
$entries = $this->_queryAssertion('/saml:AuthnStatement[@SessionNotOnOrAfter]');
if ($entries->length !== 0) {
$notOnOrAfter = OneLogin_Saml2_Utils::parseSAML2Time($entries->item(0)->getAttribute('SessionNotOnOrAfter'));
}
return $notOnOrAfter;
}
/**
* Gets the SessionIndex from the AuthnStatement.
* Could be used to be stored in the local session in order
* to be used in a future Logout Request that the SP could
* send to the SP, to set what specific session must be deleted
*
* @return string|null The SessionIndex value
*/
public function getSessionIndex()
{
$sessionIndex = null;
$entries = $this->_queryAssertion('/saml:AuthnStatement[@SessionIndex]');
if ($entries->length !== 0) {
$sessionIndex = $entries->item(0)->getAttribute('SessionIndex');
}
return $sessionIndex;
}
/**
* Gets the Attributes from the AttributeStatement element.
*
* @return array The attributes of the SAML Assertion
*/
public function getAttributes()
{
$attributes = array();
/* EncryptedAttributes not supported
$encriptedAttributes = $this->_queryAssertion('/saml:AttributeStatement/saml:EncryptedAttribute');
if ($encriptedAttributes->length > 0) {
foreach ($encriptedAttributes as $encriptedAttribute) {
$key = $this->_settings->getSPkey();
$seckey = new XMLSecurityKey(XMLSecurityKey::RSA_1_5, array('type'=>'private'));
$seckey->loadKey($key);
$attribute = OneLogin_Saml2_Utils::decryptElement($encriptedAttribute->firstChild(), $seckey);
}
}
*/
$entries = $this->_queryAssertion('/saml:AttributeStatement/saml:Attribute');
/** @var $entry DOMNode */
foreach ($entries as $entry) {
$attributeName = $entry->attributes->getNamedItem('Name')->nodeValue;
$attributeValues = array();
foreach ($entry->childNodes as $childNode) {
$tagName = ($childNode->prefix ? $childNode->prefix.':' : '') . 'AttributeValue';
if ($childNode->nodeType == XML_ELEMENT_NODE && $childNode->tagName === $tagName) {
$attributeValues[] = $childNode->nodeValue;
}
}
$attributes[$attributeName] = $attributeValues;
}
return $attributes;
}
/**
* Verifies that the document only contains a single Assertion (encrypted or not).
*
* @return bool TRUE if the document passes.
*/
public function validateNumAssertions()
{
$encryptedAssertionNodes = $this->document->getElementsByTagName('EncryptedAssertion');
$assertionNodes = $this->document->getElementsByTagName('Assertion');
return ($assertionNodes->length + $encryptedAssertionNodes->length == 1);
}
/**
* Verifies the signature nodes:
* - Checks that are Response or Assertion
* - Check that IDs and reference URI are unique and consistent.
*
* @return array Signed element tags
*/
public function processSignedElements()
{
$signedElements = array();
$verifiedSeis = array();
$verifiedIds = array();
if ($this->encrypted) {
$signNodes = $this->decryptedDocument->getElementsByTagName('Signature');
} else {
$signNodes = $this->document->getElementsByTagName('Signature');
}
foreach ($signNodes as $signNode) {
$signedElement = $signNode->parentNode->localName;
if ($signedElement != 'Response' && $signedElement != 'Assertion') {
throw new Exception('Invalid Signature Element '.$signedElement.' SAML Response rejected');
}
# Check that reference URI matches the parent ID and no duplicate References or IDs
$idValue = $signNode->parentNode->getAttribute('ID');
if (empty($idValue)) {
throw new Exception('Signed Element must contain an ID. SAML Response rejected');
}
if (in_array($idValue, $verifiedIds)) {
throw new Exception('Duplicated ID. SAML Response rejected');
}
$verifiedIds[] = $idValue;
$ref = $signNode->getElementsByTagName('Reference');
if (!empty($ref)) {
$ref = $ref->item(0);
$sei = $ref->getAttribute('URI');
if (!empty($sei)) {
$sei = substr($sei, 1);
if ($sei != $idValue) {
throw new Exception('Found an invalid Signed Element. SAML Response rejected');
}
if (in_array($sei, $verifiedSeis)) {
throw new Exception('Duplicated Reference URI. SAML Response rejected');
}
$verifiedSeis[] = $sei;
}
}
$signedElements[] = $signedElement;
}
if (!empty($signedElements)) {
// Check SignedElements
if (!$this->validateSignedElements($signedElements)) {
throw new Exception('Found an unexpected Signature Element. SAML Response rejected');
}
}
return $signedElements;
}
/**
* Verifies that the document is still valid according Conditions Element.
*
* @return bool
*/
public function validateTimestamps()
{
if ($this->encrypted) {
$document = $this->decryptedDocument;
} else {
$document = $this->document;
}
$timestampNodes = $document->getElementsByTagName('Conditions');
for ($i = 0; $i < $timestampNodes->length; $i++) {
$nbAttribute = $timestampNodes->item($i)->attributes->getNamedItem("NotBefore");
$naAttribute = $timestampNodes->item($i)->attributes->getNamedItem("NotOnOrAfter");
if ($nbAttribute && OneLogin_SAML2_Utils::parseSAML2Time($nbAttribute->textContent) > time() + OneLogin_Saml2_Constants::ALLOWED_CLOCK_DRIFT) {
return false;
}
if ($naAttribute && OneLogin_SAML2_Utils::parseSAML2Time($naAttribute->textContent) + OneLogin_Saml2_Constants::ALLOWED_CLOCK_DRIFT <= time()) {
return false;
}
}
return true;
}
/**
* Verifies that the document has the expected signed nodes.
*
* @return bool
*/
public function validateSignedElements($signedElements)
{
if (count($signedElements) > 2) {
return false;
}
$ocurrence = array_count_values($signedElements);
if ((in_array('Response', $signedElements) && $ocurrence['Response'] > 1) ||
(in_array('Assertion', $signedElements) && $ocurrence['Assertion'] > 1) ||
!in_array('Response', $signedElements) && !in_array('Assertion', $signedElements)
) {
return false;
}
return true;
}
/**
* Extracts a node from the DOMDocument (Assertion).
*
* @param string $assertionXpath Xpath Expresion
*
* @return DOMNodeList The queried node
*
* @throws Exception
*/
protected function _queryAssertion($assertionXpath)
{
if ($this->encrypted) {
$xpath = new DOMXPath($this->decryptedDocument);
} else {
$xpath = new DOMXPath($this->document);
}
$xpath->registerNamespace('samlp', OneLogin_Saml2_Constants::NS_SAMLP);
$xpath->registerNamespace('saml', OneLogin_Saml2_Constants::NS_SAML);
$xpath->registerNamespace('ds', OneLogin_Saml2_Constants::NS_DS);
$xpath->registerNamespace('xenc', OneLogin_Saml2_Constants::NS_XENC);
$assertionNode = '/samlp:Response/saml:Assertion';
$signatureQuery = $assertionNode . '/ds:Signature/ds:SignedInfo/ds:Reference';
$assertionReferenceNode = $xpath->query($signatureQuery)->item(0);
if (!$assertionReferenceNode) {
// is the response signed as a whole?
$signatureQuery = '/samlp:Response/ds:Signature/ds:SignedInfo/ds:Reference';
$assertionReferenceNode = $xpath->query($signatureQuery)->item(0);
if ($assertionReferenceNode) {
$uri = $assertionReferenceNode->attributes->getNamedItem('URI')->nodeValue;
if (empty($uri)) {
$id = $assertionReferenceNode->parentNode->parentNode->parentNode->attributes->getNamedItem('ID')->nodeValue;
} else {
$id = substr($assertionReferenceNode->attributes->getNamedItem('URI')->nodeValue, 1);
}
$nameQuery = "/samlp:Response[@ID='$id']/saml:Assertion" . $assertionXpath;
} else {
$nameQuery = "/samlp:Response/saml:Assertion" . $assertionXpath;
}
} else {
$uri = $assertionReferenceNode->attributes->getNamedItem('URI')->nodeValue;
if (empty($uri)) {
$id = $assertionReferenceNode->parentNode->parentNode->parentNode->attributes->getNamedItem('ID')->nodeValue;
} else {
$id = substr($assertionReferenceNode->attributes->getNamedItem('URI')->nodeValue, 1);
}
$nameQuery = $assertionNode."[@ID='$id']" . $assertionXpath;
}
return $xpath->query($nameQuery);
}
/**
* Extracts nodes that match the query from the DOMDocument (Response Menssage)
*
* @param string $query Xpath Expresion
*
* @return DOMNodeList The queried nodes
*/
private function _query($query)
{
return OneLogin_Saml2_Utils::query($this->document, $query);
}
/**
* Decrypts the Assertion (DOMDocument)
*
* @param DomNode $dom DomDocument
*
* @return DOMDocument Decrypted Assertion
*
* @throws Exception
*/
protected function _decryptAssertion($dom)
{
$pem = $this->_settings->getSPkey();
if (empty($pem)) {
throw new Exception("No private key available, check settings");
}
$objenc = new XMLSecEnc();
$encData = $objenc->locateEncryptedData($dom);
if (!$encData) {
throw new Exception("Cannot locate encrypted assertion");
}
$objenc->setNode($encData);
$objenc->type = $encData->getAttribute("Type");
if (!$objKey = $objenc->locateKey()) {
throw new Exception("Unknown algorithm");
}
$key = null;
if ($objKeyInfo = $objenc->locateKeyInfo($objKey)) {
if ($objKeyInfo->isEncrypted) {
$objencKey = $objKeyInfo->encryptedCtx;
$objKeyInfo->loadKey($pem, false, false);
$key = $objencKey->decryptKey($objKeyInfo);
} else {
// symmetric encryption key support
$objKeyInfo->loadKey($pem, false, false);
}
}
if (empty($objKey->key)) {
$objKey->loadKey($key);
}
$decrypted = $objenc->decryptNode($objKey, true);
if ($decrypted instanceof DOMDocument) {
return $decrypted;
} else {
$encryptedAssertion = $decrypted->parentNode;
$container = $encryptedAssertion->parentNode;
$container->replaceChild($decrypted, $encryptedAssertion);
return $decrypted->ownerDocument;
}
}
/* After execute a validation process, if fails this method returns the cause
*
* @return string Cause
*/
public function getError()
{
return $this->_error;
}
}

View file

@ -0,0 +1,873 @@
<?php
/**
* Configuration of the OneLogin PHP Toolkit
*
*/
class OneLogin_Saml2_Settings
{
/**
* List of paths.
*
* @var array
*/
private $_paths = array();
/**
* Strict. If active, PHP Toolkit will reject unsigned or unencrypted messages
* if it expects them signed or encrypted. If not, the messages will be accepted
* and some security issues will be also relaxed.
*
* @var bool
*/
private $_strict = false;
/**
* Activate debug mode
*
* @var bool
*/
private $_debug = false;
/**
* SP data.
*
* @var array
*/
private $_sp = array();
/**
* IdP data.
*
* @var array
*/
private $_idp = array();
/**
* Security Info related to the SP.
*
* @var array
*/
private $_security = array();
/**
* Setting contacts.
*
* @var array
*/
private $_contacts = array();
/**
* Setting organization.
*
* @var array
*/
private $_organization = array();
/**
* Setting errors.
*
* @var array
*/
private $_errors = array();
/**
* Setting errors.
*
* @var array
*/
private $_spValidationOnly = false;
/**
* Initializes the settings:
* - Sets the paths of the different folders
* - Loads settings info from settings file or array/object provided
*
* @param array|object|null $settings SAML Toolkit Settings
*
* @throws OneLogin_Saml2_Error If any settings parameter is invalid
*/
public function __construct($settings = null, $spValidationOnly = false)
{
$this->_spValidationOnly = $spValidationOnly;
$this->_loadPaths();
if (!isset($settings)) {
if (!$this->_loadSettingsFromFile()) {
throw new OneLogin_Saml2_Error(
'Invalid file settings: %s',
OneLogin_Saml2_Error::SETTINGS_INVALID,
array(implode(', ', $this->_errors))
);
}
$this->_addDefaultValues();
} else if (is_array($settings)) {
if (!$this->_loadSettingsFromArray($settings)) {
throw new OneLogin_Saml2_Error(
'Invalid array settings: %s',
OneLogin_Saml2_Error::SETTINGS_INVALID,
array(implode(', ', $this->_errors))
);
}
} else {
if (!$this->_loadSettingsFromArray($settings->getValues())) {
throw new OneLogin_Saml2_Error(
'Invalid array settings: %s',
OneLogin_Saml2_Error::SETTINGS_INVALID,
array(implode(', ', $this->_errors))
);
}
}
$this->formatIdPCert();
$this->formatSPCert();
$this->formatSPKey();
}
/**
* Sets the paths of the different folders
*/
private function _loadPaths()
{
$basePath = dirname(dirname(dirname(__FILE__))).'/';
$this->_paths = array (
'base' => $basePath,
'config' => $basePath,
'cert' => $basePath.'certs/',
'lib' => $basePath.'lib/',
'extlib' => $basePath.'extlib/'
);
if (defined('ONELOGIN_CUSTOMPATH')) {
$this->_paths['config'] = ONELOGIN_CUSTOMPATH;
$this->_paths['cert'] = ONELOGIN_CUSTOMPATH.'certs/';
}
}
/**
* Returns base path.
*
* @return string The base toolkit folder path
*/
public function getBasePath()
{
return $this->_paths['base'];
}
/**
* Returns cert path.
*
* @return string The cert folder path
*/
public function getCertPath()
{
return $this->_paths['cert'];
}
/**
* Returns config path.
*
* @return string The config folder path
*/
public function getConfigPath()
{
return $this->_paths['config'];
}
/**
* Returns lib path.
*
* @return string The library folder path
*/
public function getLibPath()
{
return $this->_paths['lib'];
}
/**
* Returns external lib path.
*
* @return string The external library folder path
*/
public function getExtLibPath()
{
return $this->_paths['extlib'];
}
/**
* Returns schema path.
*
* @return string The external library folder path
*/
public function getSchemasPath()
{
return $this->_paths['lib'].'schemas/';
}
/**
* Loads settings info from a settings Array
*
* @param array $settings SAML Toolkit Settings
*
* @return bool True if the settings info is valid
*/
private function _loadSettingsFromArray($settings)
{
if (isset($settings['sp'])) {
$this->_sp = $settings['sp'];
}
if (isset($settings['idp'])) {
$this->_idp = $settings['idp'];
}
$errors = $this->checkSettings($settings);
if (empty($errors)) {
$this->_errors = array();
if (isset($settings['strict'])) {
$this->_strict = $settings['strict'];
}
if (isset($settings['debug'])) {
$this->_debug = $settings['debug'];
}
if (isset($settings['security'])) {
$this->_security = $settings['security'];
}
if (isset($settings['contactPerson'])) {
$this->_contacts = $settings['contactPerson'];
}
if (isset($settings['organization'])) {
$this->_organization = $settings['organization'];
}
$this->_addDefaultValues();
return true;
} else {
$this->_errors = $errors;
return false;
}
}
/**
* Loads settings info from the settings file
*
* @return bool True if the settings info is valid
* @throws OneLogin_Saml2_Error
*/
private function _loadSettingsFromFile()
{
$filename = $this->getConfigPath().'settings.php';
if (!file_exists($filename)) {
throw new OneLogin_Saml2_Error(
'Settings file not found: %s',
OneLogin_Saml2_Error::SETTINGS_FILE_NOT_FOUND,
array($filename)
);
}
include $filename;
// Add advance_settings if exists
$advancedFilename = $this->getConfigPath().'advanced_settings.php';
if (file_exists($advancedFilename)) {
include $advancedFilename;
$settings = array_merge($settings, $advancedSettings);
}
return $this->_loadSettingsFromArray($settings);
}
/**
* Add default values if the settings info is not complete
*/
private function _addDefaultValues()
{
if (!isset($this->_sp['assertionConsumerService']['binding'])) {
$this->_sp['assertionConsumerService']['binding'] = OneLogin_Saml2_Constants::BINDING_HTTP_POST;
}
if (isset($this->_sp['singleLogoutService']) && !isset($this->_sp['singleLogoutService']['binding'])) {
$this->_sp['singleLogoutService']['binding'] = OneLogin_Saml2_Constants::BINDING_HTTP_REDIRECT;
}
// Related to nameID
if (!isset($this->_sp['NameIDFormat'])) {
$this->_sp['NameIDFormat'] = OneLogin_Saml2_Constants::NAMEID_UNSPECIFIED;
}
if (!isset($this->_security['nameIdEncrypted'])) {
$this->_security['nameIdEncrypted'] = false;
}
if (!isset($this->_security['requestedAuthnContext'])) {
$this->_security['requestedAuthnContext'] = true;
}
// sign provided
if (!isset($this->_security['authnRequestsSigned'])) {
$this->_security['authnRequestsSigned'] = false;
}
if (!isset($this->_security['logoutRequestSigned'])) {
$this->_security['logoutRequestSigned'] = false;
}
if (!isset($this->_security['logoutResponseSigned'])) {
$this->_security['logoutResponseSigned'] = false;
}
if (!isset($this->_security['signMetadata'])) {
$this->_security['signMetadata'] = false;
}
// sign expected
if (!isset($this->_security['wantMessagesSigned'])) {
$this->_security['wantMessagesSigned'] = false;
}
if (!isset($this->_security['wantAssertionsSigned'])) {
$this->_security['wantAssertionsSigned'] = false;
}
// NameID element expected
if (!isset($this->_security['wantNameId'])) {
$this->_security['wantNameId'] = true;
}
// encrypt expected
if (!isset($this->_security['wantAssertionsEncrypted'])) {
$this->_security['wantAssertionsEncrypted'] = false;
}
if (!isset($this->_security['wantNameIdEncrypted'])) {
$this->_security['wantNameIdEncrypted'] = false;
}
// XML validation
if (!isset($this->_security['wantXMLValidation'])) {
$this->_security['wantXMLValidation'] = true;
}
// Algorithm
if (!isset($this->_security['signatureAlgorithm'])) {
$this->_security['signatureAlgorithm'] = XMLSecurityKey::RSA_SHA1;
}
// Certificates / Private key /Fingerprint
if (!isset($this->_idp['x509cert'])) {
$this->_idp['x509cert'] = '';
}
if (!isset($this->_idp['certFingerprint'])) {
$this->_idp['certFingerprint'] = '';
}
if (!isset($this->_idp['certFingerprintAlgorithm'])) {
$this->_idp['certFingerprintAlgorithm'] = 'sha1';
}
if (!isset($this->_sp['x509cert'])) {
$this->_sp['x509cert'] = '';
}
if (!isset($this->_sp['privateKey'])) {
$this->_sp['privateKey'] = '';
}
}
/**
* Checks the settings info.
*
* @param array $settings Array with settings data
*
* @return array $errors Errors found on the settings data
*/
public function checkSettings($settings)
{
assert('is_array($settings)');
if (!is_array($settings) || empty($settings)) {
$errors = array('invalid_syntax');
} else {
$errors = array();
if (!$this->_spValidationOnly) {
$idpErrors = $this->checkIdPSettings($settings);
$errors = array_merge($idpErrors, $errors);
}
$spErrors = $this->checkSPSettings($settings);
$errors = array_merge($spErrors, $errors);
}
return $errors;
}
/**
* Checks the IdP settings info.
*
* @param array $settings Array with settings data
*
* @return array $errors Errors found on the IdP settings data
*/
public function checkIdPSettings($settings)
{
assert('is_array($settings)');
if (!is_array($settings) || empty($settings)) {
return array('invalid_syntax');
}
$errors = array();
if (!isset($settings['idp']) || empty($settings['idp'])) {
$errors[] = 'idp_not_found';
} else {
$idp = $settings['idp'];
if (!isset($idp['entityId']) || empty($idp['entityId'])) {
$errors[] = 'idp_entityId_not_found';
}
if (!isset($idp['singleSignOnService'])
|| !isset($idp['singleSignOnService']['url'])
|| empty($idp['singleSignOnService']['url'])
) {
$errors[] = 'idp_sso_not_found';
} else if (!filter_var($idp['singleSignOnService']['url'], FILTER_VALIDATE_URL)) {
$errors[] = 'idp_sso_url_invalid';
}
if (isset($idp['singleLogoutService'])
&& isset($idp['singleLogoutService']['url'])
&& !empty($idp['singleLogoutService']['url'])
&& !filter_var($idp['singleLogoutService']['url'], FILTER_VALIDATE_URL)
) {
$errors[] = 'idp_slo_url_invalid';
}
if (isset($settings['security'])) {
$security = $settings['security'];
$existsX509 = isset($idp['x509cert']) && !empty($idp['x509cert']);
$existsFingerprint = isset($idp['certFingerprint']) && !empty($idp['certFingerprint']);
if (((isset($security['wantAssertionsSigned']) && $security['wantAssertionsSigned'] == true)
|| (isset($security['wantMessagesSigned']) && $security['wantMessagesSigned'] == true))
&& !($existsX509 || $existsFingerprint)
) {
$errors[] = 'idp_cert_or_fingerprint_not_found_and_required';
}
if ((isset($security['nameIdEncrypted']) && $security['nameIdEncrypted'] == true)
&& !($existsX509)
) {
$errors[] = 'idp_cert_not_found_and_required';
}
}
}
return $errors;
}
/**
* Checks the SP settings info.
*
* @param array $settings Array with settings data
*
* @return array $errors Errors found on the SP settings data
*/
public function checkSPSettings($settings)
{
assert('is_array($settings)');
if (!is_array($settings) || empty($settings)) {
return array('invalid_syntax');
}
$errors = array();
if (!isset($settings['sp']) || empty($settings['sp'])) {
$errors[] = 'sp_not_found';
} else {
$sp = $settings['sp'];
$security = array();
if (isset($settings['security'])) {
$security = $settings['security'];
}
if (!isset($sp['entityId']) || empty($sp['entityId'])) {
$errors[] = 'sp_entityId_not_found';
}
if (!isset($sp['assertionConsumerService'])
|| !isset($sp['assertionConsumerService']['url'])
|| empty($sp['assertionConsumerService']['url'])
) {
$errors[] = 'sp_acs_not_found';
} else if (!filter_var($sp['assertionConsumerService']['url'], FILTER_VALIDATE_URL)) {
$errors[] = 'sp_acs_url_invalid';
}
if (isset($sp['singleLogoutService'])
&& isset($sp['singleLogoutService']['url'])
&& !filter_var($sp['singleLogoutService']['url'], FILTER_VALIDATE_URL)
) {
$errors[] = 'sp_sls_url_invalid';
}
if (isset($security['signMetadata']) && is_array($security['signMetadata'])) {
if (!isset($security['signMetadata']['keyFileName'])
|| !isset($security['signMetadata']['certFileName'])
) {
$errors[] = 'sp_signMetadata_invalid';
}
}
if (((isset($security['authnRequestsSigned']) && $security['authnRequestsSigned'] == true)
|| (isset($security['logoutRequestSigned']) && $security['logoutRequestSigned'] == true)
|| (isset($security['logoutResponseSigned']) && $security['logoutResponseSigned'] == true)
|| (isset($security['wantAssertionsEncrypted']) && $security['wantAssertionsEncrypted'] == true)
|| (isset($security['wantNameIdEncrypted']) && $security['wantNameIdEncrypted'] == true))
&& !$this->checkSPCerts()
) {
$errors[] = 'sp_certs_not_found_and_required';
}
}
if (isset($settings['contactPerson'])) {
$types = array_keys($settings['contactPerson']);
$validTypes = array('technical', 'support', 'administrative', 'billing', 'other');
foreach ($types as $type) {
if (!in_array($type, $validTypes)) {
$errors[] = 'contact_type_invalid';
break;
}
}
foreach ($settings['contactPerson'] as $type => $contact) {
if (!isset($contact['givenName']) || empty($contact['givenName'])
|| !isset($contact['emailAddress']) || empty($contact['emailAddress'])
) {
$errors[] = 'contact_not_enought_data';
break;
}
}
}
if (isset($settings['organization'])) {
foreach ($settings['organization'] as $organization) {
if (!isset($organization['name']) || empty($organization['name'])
|| !isset($organization['displayname']) || empty($organization['displayname'])
|| !isset($organization['url']) || empty($organization['url'])
) {
$errors[] = 'organization_not_enought_data';
break;
}
}
}
return $errors;
}
/**
* Checks if the x509 certs of the SP exists and are valid.
*
* @return bool
*/
public function checkSPCerts()
{
$key = $this->getSPkey();
$cert = $this->getSPcert();
return (!empty($key) && !empty($cert));
}
/**
* Returns the x509 private key of the SP.
*
* @return string SP private key
*/
public function getSPkey()
{
$key = null;
if (isset($this->_sp['privateKey']) && !empty($this->_sp['privateKey'])) {
$key = $this->_sp['privateKey'];
} else {
$keyFile = $this->_paths['cert'].'sp.key';
if (file_exists($keyFile)) {
$key = file_get_contents($keyFile);
}
}
return $key;
}
/**
* Returns the x509 public cert of the SP.
*
* @return string SP public cert
*/
public function getSPcert()
{
$cert = null;
if (isset($this->_sp['x509cert']) && !empty($this->_sp['x509cert'])) {
$cert = $this->_sp['x509cert'];
} else {
$certFile = $this->_paths['cert'].'sp.crt';
if (file_exists($certFile)) {
$cert = file_get_contents($certFile);
}
}
return $cert;
}
/**
* Gets the IdP data.
*
* @return array IdP info
*/
public function getIdPData()
{
return $this->_idp;
}
/**
* Gets the SP data.
*
* @return array SP info
*/
public function getSPData()
{
return $this->_sp;
}
/**
* Gets security data.
*
* @return array SP info
*/
public function getSecurityData()
{
return $this->_security;
}
/**
* Gets contact data.
*
* @return array SP info
*/
public function getContacts()
{
return $this->_contacts;
}
/**
* Gets organization data.
*
* @return array SP info
*/
public function getOrganization()
{
return $this->_organization;
}
/**
* Gets the SP metadata. The XML representation.
*
* @return string SP metadata (xml)
* @throws Exception
* @throws OneLogin_Saml2_Error
*/
public function getSPMetadata()
{
$metadata = OneLogin_Saml2_Metadata::builder($this->_sp, $this->_security['authnRequestsSigned'], $this->_security['wantAssertionsSigned'], null, null, $this->getContacts(), $this->getOrganization());
$cert = $this->getSPcert();
if (!empty($cert)) {
$metadata = OneLogin_Saml2_Metadata::addX509KeyDescriptors(
$metadata,
$cert,
$this->_security['wantNameIdEncrypted'] || $this->_security['wantAssertionsEncrypted']
);
}
//Sign Metadata
if (isset($this->_security['signMetadata']) && $this->_security['signMetadata'] !== false) {
if ($this->_security['signMetadata'] === true) {
$keyMetadata = $this->getSPkey();
$certMetadata = $cert;
if (!$keyMetadata) {
throw new OneLogin_Saml2_Error(
'Private key not found.',
OneLogin_Saml2_Error::PRIVATE_KEY_FILE_NOT_FOUND
);
}
if (!$certMetadata) {
throw new OneLogin_Saml2_Error(
'Public cert file not found.',
OneLogin_Saml2_Error::PUBLIC_CERT_FILE_NOT_FOUND
);
}
} else {
if (!isset($this->_security['signMetadata']['keyFileName'])
|| !isset($this->_security['signMetadata']['certFileName'])
) {
throw new OneLogin_Saml2_Error(
'Invalid Setting: signMetadata value of the sp is not valid',
OneLogin_Saml2_Error::SETTINGS_INVALID_SYNTAX
);
}
$keyFileName = $this->_security['signMetadata']['keyFileName'];
$certFileName = $this->_security['signMetadata']['certFileName'];
$keyMetadataFile = $this->_paths['cert'].$keyFileName;
$certMetadataFile = $this->_paths['cert'].$certFileName;
if (!file_exists($keyMetadataFile)) {
throw new OneLogin_Saml2_Error(
'Private key file not found: %s',
OneLogin_Saml2_Error::PRIVATE_KEY_FILE_NOT_FOUND,
array($keyMetadataFile)
);
}
if (!file_exists($certMetadataFile)) {
throw new OneLogin_Saml2_Error(
'Public cert file not found: %s',
OneLogin_Saml2_Error::PUBLIC_CERT_FILE_NOT_FOUND,
array($certMetadataFile)
);
}
$keyMetadata = file_get_contents($keyMetadataFile);
$certMetadata = file_get_contents($certMetadataFile);
}
$signatureAlgorithm = $this->_security['signatureAlgorithm'];
$metadata = OneLogin_Saml2_Metadata::signMetadata($metadata, $keyMetadata, $certMetadata, $signatureAlgorithm);
}
return $metadata;
}
/**
* Validates an XML SP Metadata.
*
* @param string $xml Metadata's XML that will be validate
*
* @return Array The list of found errors
*/
public function validateMetadata($xml)
{
assert('is_string($xml)');
$errors = array();
$res = OneLogin_Saml2_Utils::validateXML($xml, 'saml-schema-metadata-2.0.xsd', $this->_debug);
if (!$res instanceof DOMDocument) {
$errors[] = $res;
} else {
$dom = $res;
$element = $dom->documentElement;
if ($element->tagName !== 'md:EntityDescriptor') {
$errors[] = 'noEntityDescriptor_xml';
} else {
$validUntil = $cacheDuration = $expireTime = null;
if ($element->hasAttribute('validUntil')) {
$validUntil = OneLogin_Saml2_Utils::parseSAML2Time($element->getAttribute('validUntil'));
}
if ($element->hasAttribute('cacheDuration')) {
$cacheDuration = $element->getAttribute('cacheDuration');
}
$expireTime = OneLogin_Saml2_Utils::getExpireTime($cacheDuration, $validUntil);
if (isset($expireTime) && time() > $expireTime) {
$errors[] = 'expired_xml';
}
}
}
// TODO: Support Metadata Sign Validation
return $errors;
}
/**
* Formats the IdP cert.
*/
public function formatIdPCert()
{
if (isset($this->_idp['x509cert'])) {
$this->_idp['x509cert'] = OneLogin_Saml2_Utils::formatCert($this->_idp['x509cert']);
}
}
/**
* Formats the SP cert.
*/
public function formatSPCert()
{
if (isset($this->_sp['x509cert'])) {
$this->_sp['x509cert'] = OneLogin_Saml2_Utils::formatCert($this->_sp['x509cert']);
}
}
/**
* Formats the SP private key.
*/
public function formatSPKey()
{
if (isset($this->_sp['privateKey'])) {
$this->_sp['privateKey'] = OneLogin_Saml2_Utils::formatPrivateKey($this->_sp['privateKey']);
}
}
/**
* Returns an array with the errors, the array is empty when the settings is ok.
*
* @return array Errors
*/
public function getErrors()
{
return $this->_errors;
}
/**
* Activates or deactivates the strict mode.
*
* @param bool $value Strict parameter
*/
public function setStrict($value)
{
assert('is_bool($value)');
$this->_strict = $value;
}
/**
* Returns if the 'strict' mode is active.
*
* @return bool Strict parameter
*/
public function isStrict()
{
return $this->_strict;
}
/**
* Returns if the debug is active.
*
* @return bool Debug parameter
*/
public function isDebugActive()
{
return $this->_debug;
}
/**
* Sets the IdP certificate.
*
* @param string $value IdP certificate
*/
public function setIdPCert($cert)
{
$this->_idp['x509cert'] = $cert;
$this->formatIdPCert();
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,283 @@
<?xml version="1.0" encoding="US-ASCII"?>
<schema
targetNamespace="urn:oasis:names:tc:SAML:2.0:assertion"
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"
elementFormDefault="unqualified"
attributeFormDefault="unqualified"
blockDefault="substitution"
version="2.0">
<import namespace="http://www.w3.org/2000/09/xmldsig#"
schemaLocation="xmldsig-core-schema.xsd"/>
<import namespace="http://www.w3.org/2001/04/xmlenc#"
schemaLocation="xenc-schema.xsd"/>
<annotation>
<documentation>
Document identifier: saml-schema-assertion-2.0
Location: http://docs.oasis-open.org/security/saml/v2.0/
Revision history:
V1.0 (November, 2002):
Initial Standard Schema.
V1.1 (September, 2003):
Updates within the same V1.0 namespace.
V2.0 (March, 2005):
New assertion schema for SAML V2.0 namespace.
</documentation>
</annotation>
<attributeGroup name="IDNameQualifiers">
<attribute name="NameQualifier" type="string" use="optional"/>
<attribute name="SPNameQualifier" type="string" use="optional"/>
</attributeGroup>
<element name="BaseID" type="saml:BaseIDAbstractType"/>
<complexType name="BaseIDAbstractType" abstract="true">
<attributeGroup ref="saml:IDNameQualifiers"/>
</complexType>
<element name="NameID" type="saml:NameIDType"/>
<complexType name="NameIDType">
<simpleContent>
<extension base="string">
<attributeGroup ref="saml:IDNameQualifiers"/>
<attribute name="Format" type="anyURI" use="optional"/>
<attribute name="SPProvidedID" type="string" use="optional"/>
</extension>
</simpleContent>
</complexType>
<complexType name="EncryptedElementType">
<sequence>
<element ref="xenc:EncryptedData"/>
<element ref="xenc:EncryptedKey" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
</complexType>
<element name="EncryptedID" type="saml:EncryptedElementType"/>
<element name="Issuer" type="saml:NameIDType"/>
<element name="AssertionIDRef" type="NCName"/>
<element name="AssertionURIRef" type="anyURI"/>
<element name="Assertion" type="saml:AssertionType"/>
<complexType name="AssertionType">
<sequence>
<element ref="saml:Issuer"/>
<element ref="ds:Signature" minOccurs="0"/>
<element ref="saml:Subject" minOccurs="0"/>
<element ref="saml:Conditions" minOccurs="0"/>
<element ref="saml:Advice" minOccurs="0"/>
<choice minOccurs="0" maxOccurs="unbounded">
<element ref="saml:Statement"/>
<element ref="saml:AuthnStatement"/>
<element ref="saml:AuthzDecisionStatement"/>
<element ref="saml:AttributeStatement"/>
</choice>
</sequence>
<attribute name="Version" type="string" use="required"/>
<attribute name="ID" type="ID" use="required"/>
<attribute name="IssueInstant" type="dateTime" use="required"/>
</complexType>
<element name="Subject" type="saml:SubjectType"/>
<complexType name="SubjectType">
<choice>
<sequence>
<choice>
<element ref="saml:BaseID"/>
<element ref="saml:NameID"/>
<element ref="saml:EncryptedID"/>
</choice>
<element ref="saml:SubjectConfirmation" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<element ref="saml:SubjectConfirmation" maxOccurs="unbounded"/>
</choice>
</complexType>
<element name="SubjectConfirmation" type="saml:SubjectConfirmationType"/>
<complexType name="SubjectConfirmationType">
<sequence>
<choice minOccurs="0">
<element ref="saml:BaseID"/>
<element ref="saml:NameID"/>
<element ref="saml:EncryptedID"/>
</choice>
<element ref="saml:SubjectConfirmationData" minOccurs="0"/>
</sequence>
<attribute name="Method" type="anyURI" use="required"/>
</complexType>
<element name="SubjectConfirmationData" type="saml:SubjectConfirmationDataType"/>
<complexType name="SubjectConfirmationDataType" mixed="true">
<complexContent>
<restriction base="anyType">
<sequence>
<any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="NotBefore" type="dateTime" use="optional"/>
<attribute name="NotOnOrAfter" type="dateTime" use="optional"/>
<attribute name="Recipient" type="anyURI" use="optional"/>
<attribute name="InResponseTo" type="NCName" use="optional"/>
<attribute name="Address" type="string" use="optional"/>
<anyAttribute namespace="##other" processContents="lax"/>
</restriction>
</complexContent>
</complexType>
<complexType name="KeyInfoConfirmationDataType" mixed="false">
<complexContent>
<restriction base="saml:SubjectConfirmationDataType">
<sequence>
<element ref="ds:KeyInfo" maxOccurs="unbounded"/>
</sequence>
</restriction>
</complexContent>
</complexType>
<element name="Conditions" type="saml:ConditionsType"/>
<complexType name="ConditionsType">
<choice minOccurs="0" maxOccurs="unbounded">
<element ref="saml:Condition"/>
<element ref="saml:AudienceRestriction"/>
<element ref="saml:OneTimeUse"/>
<element ref="saml:ProxyRestriction"/>
</choice>
<attribute name="NotBefore" type="dateTime" use="optional"/>
<attribute name="NotOnOrAfter" type="dateTime" use="optional"/>
</complexType>
<element name="Condition" type="saml:ConditionAbstractType"/>
<complexType name="ConditionAbstractType" abstract="true"/>
<element name="AudienceRestriction" type="saml:AudienceRestrictionType"/>
<complexType name="AudienceRestrictionType">
<complexContent>
<extension base="saml:ConditionAbstractType">
<sequence>
<element ref="saml:Audience" maxOccurs="unbounded"/>
</sequence>
</extension>
</complexContent>
</complexType>
<element name="Audience" type="anyURI"/>
<element name="OneTimeUse" type="saml:OneTimeUseType" />
<complexType name="OneTimeUseType">
<complexContent>
<extension base="saml:ConditionAbstractType"/>
</complexContent>
</complexType>
<element name="ProxyRestriction" type="saml:ProxyRestrictionType"/>
<complexType name="ProxyRestrictionType">
<complexContent>
<extension base="saml:ConditionAbstractType">
<sequence>
<element ref="saml:Audience" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="Count" type="nonNegativeInteger" use="optional"/>
</extension>
</complexContent>
</complexType>
<element name="Advice" type="saml:AdviceType"/>
<complexType name="AdviceType">
<choice minOccurs="0" maxOccurs="unbounded">
<element ref="saml:AssertionIDRef"/>
<element ref="saml:AssertionURIRef"/>
<element ref="saml:Assertion"/>
<element ref="saml:EncryptedAssertion"/>
<any namespace="##other" processContents="lax"/>
</choice>
</complexType>
<element name="EncryptedAssertion" type="saml:EncryptedElementType"/>
<element name="Statement" type="saml:StatementAbstractType"/>
<complexType name="StatementAbstractType" abstract="true"/>
<element name="AuthnStatement" type="saml:AuthnStatementType"/>
<complexType name="AuthnStatementType">
<complexContent>
<extension base="saml:StatementAbstractType">
<sequence>
<element ref="saml:SubjectLocality" minOccurs="0"/>
<element ref="saml:AuthnContext"/>
</sequence>
<attribute name="AuthnInstant" type="dateTime" use="required"/>
<attribute name="SessionIndex" type="string" use="optional"/>
<attribute name="SessionNotOnOrAfter" type="dateTime" use="optional"/>
</extension>
</complexContent>
</complexType>
<element name="SubjectLocality" type="saml:SubjectLocalityType"/>
<complexType name="SubjectLocalityType">
<attribute name="Address" type="string" use="optional"/>
<attribute name="DNSName" type="string" use="optional"/>
</complexType>
<element name="AuthnContext" type="saml:AuthnContextType"/>
<complexType name="AuthnContextType">
<sequence>
<choice>
<sequence>
<element ref="saml:AuthnContextClassRef"/>
<choice minOccurs="0">
<element ref="saml:AuthnContextDecl"/>
<element ref="saml:AuthnContextDeclRef"/>
</choice>
</sequence>
<choice>
<element ref="saml:AuthnContextDecl"/>
<element ref="saml:AuthnContextDeclRef"/>
</choice>
</choice>
<element ref="saml:AuthenticatingAuthority" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
</complexType>
<element name="AuthnContextClassRef" type="anyURI"/>
<element name="AuthnContextDeclRef" type="anyURI"/>
<element name="AuthnContextDecl" type="anyType"/>
<element name="AuthenticatingAuthority" type="anyURI"/>
<element name="AuthzDecisionStatement" type="saml:AuthzDecisionStatementType"/>
<complexType name="AuthzDecisionStatementType">
<complexContent>
<extension base="saml:StatementAbstractType">
<sequence>
<element ref="saml:Action" maxOccurs="unbounded"/>
<element ref="saml:Evidence" minOccurs="0"/>
</sequence>
<attribute name="Resource" type="anyURI" use="required"/>
<attribute name="Decision" type="saml:DecisionType" use="required"/>
</extension>
</complexContent>
</complexType>
<simpleType name="DecisionType">
<restriction base="string">
<enumeration value="Permit"/>
<enumeration value="Deny"/>
<enumeration value="Indeterminate"/>
</restriction>
</simpleType>
<element name="Action" type="saml:ActionType"/>
<complexType name="ActionType">
<simpleContent>
<extension base="string">
<attribute name="Namespace" type="anyURI" use="required"/>
</extension>
</simpleContent>
</complexType>
<element name="Evidence" type="saml:EvidenceType"/>
<complexType name="EvidenceType">
<choice maxOccurs="unbounded">
<element ref="saml:AssertionIDRef"/>
<element ref="saml:AssertionURIRef"/>
<element ref="saml:Assertion"/>
<element ref="saml:EncryptedAssertion"/>
</choice>
</complexType>
<element name="AttributeStatement" type="saml:AttributeStatementType"/>
<complexType name="AttributeStatementType">
<complexContent>
<extension base="saml:StatementAbstractType">
<choice maxOccurs="unbounded">
<element ref="saml:Attribute"/>
<element ref="saml:EncryptedAttribute"/>
</choice>
</extension>
</complexContent>
</complexType>
<element name="Attribute" type="saml:AttributeType"/>
<complexType name="AttributeType">
<sequence>
<element ref="saml:AttributeValue" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="Name" type="string" use="required"/>
<attribute name="NameFormat" type="anyURI" use="optional"/>
<attribute name="FriendlyName" type="string" use="optional"/>
<anyAttribute namespace="##other" processContents="lax"/>
</complexType>
<element name="AttributeValue" type="anyType" nillable="true"/>
<element name="EncryptedAttribute" type="saml:EncryptedElementType"/>
</schema>

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
targetNamespace="urn:oasis:names:tc:SAML:2.0:ac"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="urn:oasis:names:tc:SAML:2.0:ac"
blockDefault="substitution"
version="2.0">
<xs:annotation>
<xs:documentation>
Document identifier: saml-schema-authn-context-2.0
Location: http://docs.oasis-open.org/security/saml/v2.0/
Revision history:
V2.0 (March, 2005):
New core authentication context schema for SAML V2.0.
This is just an include of all types from the schema
referred to in the include statement below.
</xs:documentation>
</xs:annotation>
<xs:include schemaLocation="saml-schema-authn-context-types-2.0.xsd"/>
</xs:schema>

View file

@ -0,0 +1,821 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
version="2.0">
<xs:annotation>
<xs:documentation>
Document identifier: saml-schema-authn-context-types-2.0
Location: http://docs.oasis-open.org/security/saml/v2.0/
Revision history:
V2.0 (March, 2005):
New core authentication context schema types for SAML V2.0.
</xs:documentation>
</xs:annotation>
<xs:element name="AuthenticationContextDeclaration" type="AuthnContextDeclarationBaseType">
<xs:annotation>
<xs:documentation>
A particular assertion on an identity
provider's part with respect to the authentication
context associated with an authentication assertion.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="Identification" type="IdentificationType">
<xs:annotation>
<xs:documentation>
Refers to those characteristics that describe the
processes and mechanisms
the Authentication Authority uses to initially create
an association between a Principal
and the identity (or name) by which the Principal will
be known
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="PhysicalVerification">
<xs:annotation>
<xs:documentation>
This element indicates that identification has been
performed in a physical
face-to-face meeting with the principal and not in an
online manner.
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attribute name="credentialLevel">
<xs:simpleType>
<xs:restriction base="xs:NMTOKEN">
<xs:enumeration value="primary"/>
<xs:enumeration value="secondary"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name="WrittenConsent" type="ExtensionOnlyType"/>
<xs:element name="TechnicalProtection" type="TechnicalProtectionBaseType">
<xs:annotation>
<xs:documentation>
Refers to those characterstics that describe how the
'secret' (the knowledge or possession
of which allows the Principal to authenticate to the
Authentication Authority) is kept secure
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="SecretKeyProtection" type="SecretKeyProtectionType">
<xs:annotation>
<xs:documentation>
This element indicates the types and strengths of
facilities
of a UA used to protect a shared secret key from
unauthorized access and/or use.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="PrivateKeyProtection" type="PrivateKeyProtectionType">
<xs:annotation>
<xs:documentation>
This element indicates the types and strengths of
facilities
of a UA used to protect a private key from
unauthorized access and/or use.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="KeyActivation" type="KeyActivationType">
<xs:annotation>
<xs:documentation>The actions that must be performed
before the private key can be used. </xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="KeySharing" type="KeySharingType">
<xs:annotation>
<xs:documentation>Whether or not the private key is shared
with the certificate authority.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="KeyStorage" type="KeyStorageType">
<xs:annotation>
<xs:documentation>
In which medium is the key stored.
memory - the key is stored in memory.
smartcard - the key is stored in a smartcard.
token - the key is stored in a hardware token.
MobileDevice - the key is stored in a mobile device.
MobileAuthCard - the key is stored in a mobile
authentication card.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="SubscriberLineNumber" type="ExtensionOnlyType"/>
<xs:element name="UserSuffix" type="ExtensionOnlyType"/>
<xs:element name="Password" type="PasswordType">
<xs:annotation>
<xs:documentation>
This element indicates that a password (or passphrase)
has been used to
authenticate the Principal to a remote system.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="ActivationPin" type="ActivationPinType">
<xs:annotation>
<xs:documentation>
This element indicates that a Pin (Personal
Identification Number) has been used to authenticate the Principal to
some local system in order to activate a key.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="Token" type="TokenType">
<xs:annotation>
<xs:documentation>
This element indicates that a hardware or software
token is used
as a method of identifying the Principal.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="TimeSyncToken" type="TimeSyncTokenType">
<xs:annotation>
<xs:documentation>
This element indicates that a time synchronization
token is used to identify the Principal. hardware -
the time synchonization
token has been implemented in hardware. software - the
time synchronization
token has been implemented in software. SeedLength -
the length, in bits, of the
random seed used in the time synchronization token.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="Smartcard" type="ExtensionOnlyType">
<xs:annotation>
<xs:documentation>
This element indicates that a smartcard is used to
identity the Principal.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="Length" type="LengthType">
<xs:annotation>
<xs:documentation>
This element indicates the minimum and/or maximum
ASCII length of the password which is enforced (by the UA or the
IdP). In other words, this is the minimum and/or maximum number of
ASCII characters required to represent a valid password.
min - the minimum number of ASCII characters required
in a valid password, as enforced by the UA or the IdP.
max - the maximum number of ASCII characters required
in a valid password, as enforced by the UA or the IdP.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="ActivationLimit" type="ActivationLimitType">
<xs:annotation>
<xs:documentation>
This element indicates the length of time for which an
PIN-based authentication is valid.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="Generation">
<xs:annotation>
<xs:documentation>
Indicates whether the password was chosen by the
Principal or auto-supplied by the Authentication Authority.
principalchosen - the Principal is allowed to choose
the value of the password. This is true even if
the initial password is chosen at random by the UA or
the IdP and the Principal is then free to change
the password.
automatic - the password is chosen by the UA or the
IdP to be cryptographically strong in some sense,
or to satisfy certain password rules, and that the
Principal is not free to change it or to choose a new password.
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attribute name="mechanism" use="required">
<xs:simpleType>
<xs:restriction base="xs:NMTOKEN">
<xs:enumeration value="principalchosen"/>
<xs:enumeration value="automatic"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name="AuthnMethod" type="AuthnMethodBaseType">
<xs:annotation>
<xs:documentation>
Refers to those characteristics that define the
mechanisms by which the Principal authenticates to the Authentication
Authority.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="PrincipalAuthenticationMechanism" type="PrincipalAuthenticationMechanismType">
<xs:annotation>
<xs:documentation>
The method that a Principal employs to perform
authentication to local system components.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="Authenticator" type="AuthenticatorBaseType">
<xs:annotation>
<xs:documentation>
The method applied to validate a principal's
authentication across a network
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="ComplexAuthenticator" type="ComplexAuthenticatorType">
<xs:annotation>
<xs:documentation>
Supports Authenticators with nested combinations of
additional complexity.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="PreviousSession" type="ExtensionOnlyType">
<xs:annotation>
<xs:documentation>
Indicates that the Principal has been strongly
authenticated in a previous session during which the IdP has set a
cookie in the UA. During the present session the Principal has only
been authenticated by the UA returning the cookie to the IdP.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="ResumeSession" type="ExtensionOnlyType">
<xs:annotation>
<xs:documentation>
Rather like PreviousSession but using stronger
security. A secret that was established in a previous session with
the Authentication Authority has been cached by the local system and
is now re-used (e.g. a Master Secret is used to derive new session
keys in TLS, SSL, WTLS).
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="ZeroKnowledge" type="ExtensionOnlyType">
<xs:annotation>
<xs:documentation>
This element indicates that the Principal has been
authenticated by a zero knowledge technique as specified in ISO/IEC
9798-5.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="SharedSecretChallengeResponse" type="SharedSecretChallengeResponseType"/>
<xs:complexType name="SharedSecretChallengeResponseType">
<xs:annotation>
<xs:documentation>
This element indicates that the Principal has been
authenticated by a challenge-response protocol utilizing shared secret
keys and symmetric cryptography.
</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element ref="Extension" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="method" type="xs:anyURI" use="optional"/>
</xs:complexType>
<xs:element name="DigSig" type="PublicKeyType">
<xs:annotation>
<xs:documentation>
This element indicates that the Principal has been
authenticated by a mechanism which involves the Principal computing a
digital signature over at least challenge data provided by the IdP.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="AsymmetricDecryption" type="PublicKeyType">
<xs:annotation>
<xs:documentation>
The local system has a private key but it is used
in decryption mode, rather than signature mode. For example, the
Authentication Authority generates a secret and encrypts it using the
local system's public key: the local system then proves it has
decrypted the secret.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="AsymmetricKeyAgreement" type="PublicKeyType">
<xs:annotation>
<xs:documentation>
The local system has a private key and uses it for
shared secret key agreement with the Authentication Authority (e.g.
via Diffie Helman).
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:complexType name="PublicKeyType">
<xs:sequence>
<xs:element ref="Extension" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="keyValidation" use="optional"/>
</xs:complexType>
<xs:element name="IPAddress" type="ExtensionOnlyType">
<xs:annotation>
<xs:documentation>
This element indicates that the Principal has been
authenticated through connection from a particular IP address.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="SharedSecretDynamicPlaintext" type="ExtensionOnlyType">
<xs:annotation>
<xs:documentation>
The local system and Authentication Authority
share a secret key. The local system uses this to encrypt a
randomised string to pass to the Authentication Authority.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="AuthenticatorTransportProtocol" type="AuthenticatorTransportProtocolType">
<xs:annotation>
<xs:documentation>
The protocol across which Authenticator information is
transferred to an Authentication Authority verifier.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="HTTP" type="ExtensionOnlyType">
<xs:annotation>
<xs:documentation>
This element indicates that the Authenticator has been
transmitted using bare HTTP utilizing no additional security
protocols.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="IPSec" type="ExtensionOnlyType">
<xs:annotation>
<xs:documentation>
This element indicates that the Authenticator has been
transmitted using a transport mechanism protected by an IPSEC session.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="WTLS" type="ExtensionOnlyType">
<xs:annotation>
<xs:documentation>
This element indicates that the Authenticator has been
transmitted using a transport mechanism protected by a WTLS session.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="MobileNetworkNoEncryption" type="ExtensionOnlyType">
<xs:annotation>
<xs:documentation>
This element indicates that the Authenticator has been
transmitted solely across a mobile network using no additional
security mechanism.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="MobileNetworkRadioEncryption" type="ExtensionOnlyType"/>
<xs:element name="MobileNetworkEndToEndEncryption" type="ExtensionOnlyType"/>
<xs:element name="SSL" type="ExtensionOnlyType">
<xs:annotation>
<xs:documentation>
This element indicates that the Authenticator has been
transmitted using a transport mechnanism protected by an SSL or TLS
session.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="PSTN" type="ExtensionOnlyType"/>
<xs:element name="ISDN" type="ExtensionOnlyType"/>
<xs:element name="ADSL" type="ExtensionOnlyType"/>
<xs:element name="OperationalProtection" type="OperationalProtectionType">
<xs:annotation>
<xs:documentation>
Refers to those characteristics that describe
procedural security controls employed by the Authentication Authority.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="SecurityAudit" type="SecurityAuditType"/>
<xs:element name="SwitchAudit" type="ExtensionOnlyType"/>
<xs:element name="DeactivationCallCenter" type="ExtensionOnlyType"/>
<xs:element name="GoverningAgreements" type="GoverningAgreementsType">
<xs:annotation>
<xs:documentation>
Provides a mechanism for linking to external (likely
human readable) documents in which additional business agreements,
(e.g. liability constraints, obligations, etc) can be placed.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="GoverningAgreementRef" type="GoverningAgreementRefType"/>
<xs:simpleType name="nymType">
<xs:restriction base="xs:NMTOKEN">
<xs:enumeration value="anonymity"/>
<xs:enumeration value="verinymity"/>
<xs:enumeration value="pseudonymity"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="AuthnContextDeclarationBaseType">
<xs:sequence>
<xs:element ref="Identification" minOccurs="0"/>
<xs:element ref="TechnicalProtection" minOccurs="0"/>
<xs:element ref="OperationalProtection" minOccurs="0"/>
<xs:element ref="AuthnMethod" minOccurs="0"/>
<xs:element ref="GoverningAgreements" minOccurs="0"/>
<xs:element ref="Extension" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="ID" type="xs:ID" use="optional"/>
</xs:complexType>
<xs:complexType name="IdentificationType">
<xs:sequence>
<xs:element ref="PhysicalVerification" minOccurs="0"/>
<xs:element ref="WrittenConsent" minOccurs="0"/>
<xs:element ref="GoverningAgreements" minOccurs="0"/>
<xs:element ref="Extension" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="nym" type="nymType">
<xs:annotation>
<xs:documentation>
This attribute indicates whether or not the
Identification mechanisms allow the actions of the Principal to be
linked to an actual end user.
</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:complexType name="TechnicalProtectionBaseType">
<xs:sequence>
<xs:choice minOccurs="0">
<xs:element ref="PrivateKeyProtection"/>
<xs:element ref="SecretKeyProtection"/>
</xs:choice>
<xs:element ref="Extension" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="OperationalProtectionType">
<xs:sequence>
<xs:element ref="SecurityAudit" minOccurs="0"/>
<xs:element ref="DeactivationCallCenter" minOccurs="0"/>
<xs:element ref="Extension" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="AuthnMethodBaseType">
<xs:sequence>
<xs:element ref="PrincipalAuthenticationMechanism" minOccurs="0"/>
<xs:element ref="Authenticator" minOccurs="0"/>
<xs:element ref="AuthenticatorTransportProtocol" minOccurs="0"/>
<xs:element ref="Extension" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="GoverningAgreementsType">
<xs:sequence>
<xs:element ref="GoverningAgreementRef" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="GoverningAgreementRefType">
<xs:attribute name="governingAgreementRef" type="xs:anyURI" use="required"/>
</xs:complexType>
<xs:complexType name="PrincipalAuthenticationMechanismType">
<xs:sequence>
<xs:element ref="Password" minOccurs="0"/>
<xs:element ref="RestrictedPassword" minOccurs="0"/>
<xs:element ref="Token" minOccurs="0"/>
<xs:element ref="Smartcard" minOccurs="0"/>
<xs:element ref="ActivationPin" minOccurs="0"/>
<xs:element ref="Extension" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="preauth" type="xs:integer" use="optional"/>
</xs:complexType>
<xs:group name="AuthenticatorChoiceGroup">
<xs:choice>
<xs:element ref="PreviousSession"/>
<xs:element ref="ResumeSession"/>
<xs:element ref="DigSig"/>
<xs:element ref="Password"/>
<xs:element ref="RestrictedPassword"/>
<xs:element ref="ZeroKnowledge"/>
<xs:element ref="SharedSecretChallengeResponse"/>
<xs:element ref="SharedSecretDynamicPlaintext"/>
<xs:element ref="IPAddress"/>
<xs:element ref="AsymmetricDecryption"/>
<xs:element ref="AsymmetricKeyAgreement"/>
<xs:element ref="SubscriberLineNumber"/>
<xs:element ref="UserSuffix"/>
<xs:element ref="ComplexAuthenticator"/>
</xs:choice>
</xs:group>
<xs:group name="AuthenticatorSequenceGroup">
<xs:sequence>
<xs:element ref="PreviousSession" minOccurs="0"/>
<xs:element ref="ResumeSession" minOccurs="0"/>
<xs:element ref="DigSig" minOccurs="0"/>
<xs:element ref="Password" minOccurs="0"/>
<xs:element ref="RestrictedPassword" minOccurs="0"/>
<xs:element ref="ZeroKnowledge" minOccurs="0"/>
<xs:element ref="SharedSecretChallengeResponse" minOccurs="0"/>
<xs:element ref="SharedSecretDynamicPlaintext" minOccurs="0"/>
<xs:element ref="IPAddress" minOccurs="0"/>
<xs:element ref="AsymmetricDecryption" minOccurs="0"/>
<xs:element ref="AsymmetricKeyAgreement" minOccurs="0"/>
<xs:element ref="SubscriberLineNumber" minOccurs="0"/>
<xs:element ref="UserSuffix" minOccurs="0"/>
<xs:element ref="Extension" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:group>
<xs:complexType name="AuthenticatorBaseType">
<xs:sequence>
<xs:group ref="AuthenticatorChoiceGroup"/>
<xs:group ref="AuthenticatorSequenceGroup"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ComplexAuthenticatorType">
<xs:sequence>
<xs:group ref="AuthenticatorChoiceGroup"/>
<xs:group ref="AuthenticatorSequenceGroup"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="AuthenticatorTransportProtocolType">
<xs:sequence>
<xs:choice minOccurs="0">
<xs:element ref="HTTP"/>
<xs:element ref="SSL"/>
<xs:element ref="MobileNetworkNoEncryption"/>
<xs:element ref="MobileNetworkRadioEncryption"/>
<xs:element ref="MobileNetworkEndToEndEncryption"/>
<xs:element ref="WTLS"/>
<xs:element ref="IPSec"/>
<xs:element ref="PSTN"/>
<xs:element ref="ISDN"/>
<xs:element ref="ADSL"/>
</xs:choice>
<xs:element ref="Extension" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="KeyActivationType">
<xs:sequence>
<xs:element ref="ActivationPin" minOccurs="0"/>
<xs:element ref="Extension" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="KeySharingType">
<xs:attribute name="sharing" type="xs:boolean" use="required"/>
</xs:complexType>
<xs:complexType name="PrivateKeyProtectionType">
<xs:sequence>
<xs:element ref="KeyActivation" minOccurs="0"/>
<xs:element ref="KeyStorage" minOccurs="0"/>
<xs:element ref="KeySharing" minOccurs="0"/>
<xs:element ref="Extension" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PasswordType">
<xs:sequence>
<xs:element ref="Length" minOccurs="0"/>
<xs:element ref="Alphabet" minOccurs="0"/>
<xs:element ref="Generation" minOccurs="0"/>
<xs:element ref="Extension" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="ExternalVerification" type="xs:anyURI" use="optional"/>
</xs:complexType>
<xs:element name="RestrictedPassword" type="RestrictedPasswordType"/>
<xs:complexType name="RestrictedPasswordType">
<xs:complexContent>
<xs:restriction base="PasswordType">
<xs:sequence>
<xs:element name="Length" type="RestrictedLengthType" minOccurs="1"/>
<xs:element ref="Generation" minOccurs="0"/>
<xs:element ref="Extension" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="ExternalVerification" type="xs:anyURI" use="optional"/>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="RestrictedLengthType">
<xs:complexContent>
<xs:restriction base="LengthType">
<xs:attribute name="min" use="required">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:minInclusive value="3"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="max" type="xs:integer" use="optional"/>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="ActivationPinType">
<xs:sequence>
<xs:element ref="Length" minOccurs="0"/>
<xs:element ref="Alphabet" minOccurs="0"/>
<xs:element ref="Generation" minOccurs="0"/>
<xs:element ref="ActivationLimit" minOccurs="0"/>
<xs:element ref="Extension" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:element name="Alphabet" type="AlphabetType"/>
<xs:complexType name="AlphabetType">
<xs:attribute name="requiredChars" type="xs:string" use="required"/>
<xs:attribute name="excludedChars" type="xs:string" use="optional"/>
<xs:attribute name="case" type="xs:string" use="optional"/>
</xs:complexType>
<xs:complexType name="TokenType">
<xs:sequence>
<xs:element ref="TimeSyncToken"/>
<xs:element ref="Extension" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="DeviceTypeType">
<xs:restriction base="xs:NMTOKEN">
<xs:enumeration value="hardware"/>
<xs:enumeration value="software"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="booleanType">
<xs:restriction base="xs:NMTOKEN">
<xs:enumeration value="true"/>
<xs:enumeration value="false"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="TimeSyncTokenType">
<xs:attribute name="DeviceType" type="DeviceTypeType" use="required"/>
<xs:attribute name="SeedLength" type="xs:integer" use="required"/>
<xs:attribute name="DeviceInHand" type="booleanType" use="required"/>
</xs:complexType>
<xs:complexType name="ActivationLimitType">
<xs:choice>
<xs:element ref="ActivationLimitDuration"/>
<xs:element ref="ActivationLimitUsages"/>
<xs:element ref="ActivationLimitSession"/>
</xs:choice>
</xs:complexType>
<xs:element name="ActivationLimitDuration" type="ActivationLimitDurationType">
<xs:annotation>
<xs:documentation>
This element indicates that the Key Activation Limit is
defined as a specific duration of time.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="ActivationLimitUsages" type="ActivationLimitUsagesType">
<xs:annotation>
<xs:documentation>
This element indicates that the Key Activation Limit is
defined as a number of usages.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="ActivationLimitSession" type="ActivationLimitSessionType">
<xs:annotation>
<xs:documentation>
This element indicates that the Key Activation Limit is
the session.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:complexType name="ActivationLimitDurationType">
<xs:attribute name="duration" type="xs:duration" use="required"/>
</xs:complexType>
<xs:complexType name="ActivationLimitUsagesType">
<xs:attribute name="number" type="xs:integer" use="required"/>
</xs:complexType>
<xs:complexType name="ActivationLimitSessionType"/>
<xs:complexType name="LengthType">
<xs:attribute name="min" type="xs:integer" use="required"/>
<xs:attribute name="max" type="xs:integer" use="optional"/>
</xs:complexType>
<xs:simpleType name="mediumType">
<xs:restriction base="xs:NMTOKEN">
<xs:enumeration value="memory"/>
<xs:enumeration value="smartcard"/>
<xs:enumeration value="token"/>
<xs:enumeration value="MobileDevice"/>
<xs:enumeration value="MobileAuthCard"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="KeyStorageType">
<xs:attribute name="medium" type="mediumType" use="required"/>
</xs:complexType>
<xs:complexType name="SecretKeyProtectionType">
<xs:sequence>
<xs:element ref="KeyActivation" minOccurs="0"/>
<xs:element ref="KeyStorage" minOccurs="0"/>
<xs:element ref="Extension" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="SecurityAuditType">
<xs:sequence>
<xs:element ref="SwitchAudit" minOccurs="0"/>
<xs:element ref="Extension" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ExtensionOnlyType">
<xs:sequence>
<xs:element ref="Extension" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:element name="Extension" type="ExtensionType"/>
<xs:complexType name="ExtensionType">
<xs:sequence>
<xs:any namespace="##other" processContents="lax" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:schema>

View file

@ -0,0 +1,336 @@
<?xml version="1.0" encoding="UTF-8"?>
<schema
targetNamespace="urn:oasis:names:tc:SAML:2.0:metadata"
xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
xmlns="http://www.w3.org/2001/XMLSchema"
elementFormDefault="unqualified"
attributeFormDefault="unqualified"
blockDefault="substitution"
version="2.0">
<import namespace="http://www.w3.org/2000/09/xmldsig#"
schemaLocation="xmldsig-core-schema.xsd"/>
<import namespace="http://www.w3.org/2001/04/xmlenc#"
schemaLocation="xenc-schema.xsd"/>
<import namespace="urn:oasis:names:tc:SAML:2.0:assertion"
schemaLocation="saml-schema-assertion-2.0.xsd"/>
<import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="xml.xsd"/>
<annotation>
<documentation>
Document identifier: saml-schema-metadata-2.0
Location: http://docs.oasis-open.org/security/saml/v2.0/
Revision history:
V2.0 (March, 2005):
Schema for SAML metadata, first published in SAML 2.0.
</documentation>
</annotation>
<simpleType name="entityIDType">
<restriction base="anyURI">
<maxLength value="1024"/>
</restriction>
</simpleType>
<complexType name="localizedNameType">
<simpleContent>
<extension base="string">
<attribute ref="xml:lang" use="required"/>
</extension>
</simpleContent>
</complexType>
<complexType name="localizedURIType">
<simpleContent>
<extension base="anyURI">
<attribute ref="xml:lang" use="required"/>
</extension>
</simpleContent>
</complexType>
<element name="Extensions" type="md:ExtensionsType"/>
<complexType final="#all" name="ExtensionsType">
<sequence>
<any namespace="##other" processContents="lax" maxOccurs="unbounded"/>
</sequence>
</complexType>
<complexType name="EndpointType">
<sequence>
<any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="Binding" type="anyURI" use="required"/>
<attribute name="Location" type="anyURI" use="required"/>
<attribute name="ResponseLocation" type="anyURI" use="optional"/>
<anyAttribute namespace="##other" processContents="lax"/>
</complexType>
<complexType name="IndexedEndpointType">
<complexContent>
<extension base="md:EndpointType">
<attribute name="index" type="unsignedShort" use="required"/>
<attribute name="isDefault" type="boolean" use="optional"/>
</extension>
</complexContent>
</complexType>
<element name="EntitiesDescriptor" type="md:EntitiesDescriptorType"/>
<complexType name="EntitiesDescriptorType">
<sequence>
<element ref="ds:Signature" minOccurs="0"/>
<element ref="md:Extensions" minOccurs="0"/>
<choice minOccurs="1" maxOccurs="unbounded">
<element ref="md:EntityDescriptor"/>
<element ref="md:EntitiesDescriptor"/>
</choice>
</sequence>
<attribute name="validUntil" type="dateTime" use="optional"/>
<attribute name="cacheDuration" type="duration" use="optional"/>
<attribute name="ID" type="ID" use="optional"/>
<attribute name="Name" type="string" use="optional"/>
</complexType>
<element name="EntityDescriptor" type="md:EntityDescriptorType"/>
<complexType name="EntityDescriptorType">
<sequence>
<element ref="ds:Signature" minOccurs="0"/>
<element ref="md:Extensions" minOccurs="0"/>
<choice>
<choice maxOccurs="unbounded">
<element ref="md:RoleDescriptor"/>
<element ref="md:IDPSSODescriptor"/>
<element ref="md:SPSSODescriptor"/>
<element ref="md:AuthnAuthorityDescriptor"/>
<element ref="md:AttributeAuthorityDescriptor"/>
<element ref="md:PDPDescriptor"/>
</choice>
<element ref="md:AffiliationDescriptor"/>
</choice>
<element ref="md:Organization" minOccurs="0"/>
<element ref="md:ContactPerson" minOccurs="0" maxOccurs="unbounded"/>
<element ref="md:AdditionalMetadataLocation" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="entityID" type="md:entityIDType" use="required"/>
<attribute name="validUntil" type="dateTime" use="optional"/>
<attribute name="cacheDuration" type="duration" use="optional"/>
<attribute name="ID" type="ID" use="optional"/>
<anyAttribute namespace="##other" processContents="lax"/>
</complexType>
<element name="Organization" type="md:OrganizationType"/>
<complexType name="OrganizationType">
<sequence>
<element ref="md:Extensions" minOccurs="0"/>
<element ref="md:OrganizationName" maxOccurs="unbounded"/>
<element ref="md:OrganizationDisplayName" maxOccurs="unbounded"/>
<element ref="md:OrganizationURL" maxOccurs="unbounded"/>
</sequence>
<anyAttribute namespace="##other" processContents="lax"/>
</complexType>
<element name="OrganizationName" type="md:localizedNameType"/>
<element name="OrganizationDisplayName" type="md:localizedNameType"/>
<element name="OrganizationURL" type="md:localizedURIType"/>
<element name="ContactPerson" type="md:ContactType"/>
<complexType name="ContactType">
<sequence>
<element ref="md:Extensions" minOccurs="0"/>
<element ref="md:Company" minOccurs="0"/>
<element ref="md:GivenName" minOccurs="0"/>
<element ref="md:SurName" minOccurs="0"/>
<element ref="md:EmailAddress" minOccurs="0" maxOccurs="unbounded"/>
<element ref="md:TelephoneNumber" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="contactType" type="md:ContactTypeType" use="required"/>
<anyAttribute namespace="##other" processContents="lax"/>
</complexType>
<element name="Company" type="string"/>
<element name="GivenName" type="string"/>
<element name="SurName" type="string"/>
<element name="EmailAddress" type="anyURI"/>
<element name="TelephoneNumber" type="string"/>
<simpleType name="ContactTypeType">
<restriction base="string">
<enumeration value="technical"/>
<enumeration value="support"/>
<enumeration value="administrative"/>
<enumeration value="billing"/>
<enumeration value="other"/>
</restriction>
</simpleType>
<element name="AdditionalMetadataLocation" type="md:AdditionalMetadataLocationType"/>
<complexType name="AdditionalMetadataLocationType">
<simpleContent>
<extension base="anyURI">
<attribute name="namespace" type="anyURI" use="required"/>
</extension>
</simpleContent>
</complexType>
<element name="RoleDescriptor" type="md:RoleDescriptorType"/>
<complexType name="RoleDescriptorType" abstract="true">
<sequence>
<element ref="ds:Signature" minOccurs="0"/>
<element ref="md:Extensions" minOccurs="0"/>
<element ref="md:KeyDescriptor" minOccurs="0" maxOccurs="unbounded"/>
<element ref="md:Organization" minOccurs="0"/>
<element ref="md:ContactPerson" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="ID" type="ID" use="optional"/>
<attribute name="validUntil" type="dateTime" use="optional"/>
<attribute name="cacheDuration" type="duration" use="optional"/>
<attribute name="protocolSupportEnumeration" type="md:anyURIListType" use="required"/>
<attribute name="errorURL" type="anyURI" use="optional"/>
<anyAttribute namespace="##other" processContents="lax"/>
</complexType>
<simpleType name="anyURIListType">
<list itemType="anyURI"/>
</simpleType>
<element name="KeyDescriptor" type="md:KeyDescriptorType"/>
<complexType name="KeyDescriptorType">
<sequence>
<element ref="ds:KeyInfo"/>
<element ref="md:EncryptionMethod" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="use" type="md:KeyTypes" use="optional"/>
</complexType>
<simpleType name="KeyTypes">
<restriction base="string">
<enumeration value="encryption"/>
<enumeration value="signing"/>
</restriction>
</simpleType>
<element name="EncryptionMethod" type="xenc:EncryptionMethodType"/>
<complexType name="SSODescriptorType" abstract="true">
<complexContent>
<extension base="md:RoleDescriptorType">
<sequence>
<element ref="md:ArtifactResolutionService" minOccurs="0" maxOccurs="unbounded"/>
<element ref="md:SingleLogoutService" minOccurs="0" maxOccurs="unbounded"/>
<element ref="md:ManageNameIDService" minOccurs="0" maxOccurs="unbounded"/>
<element ref="md:NameIDFormat" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
</extension>
</complexContent>
</complexType>
<element name="ArtifactResolutionService" type="md:IndexedEndpointType"/>
<element name="SingleLogoutService" type="md:EndpointType"/>
<element name="ManageNameIDService" type="md:EndpointType"/>
<element name="NameIDFormat" type="anyURI"/>
<element name="IDPSSODescriptor" type="md:IDPSSODescriptorType"/>
<complexType name="IDPSSODescriptorType">
<complexContent>
<extension base="md:SSODescriptorType">
<sequence>
<element ref="md:SingleSignOnService" maxOccurs="unbounded"/>
<element ref="md:NameIDMappingService" minOccurs="0" maxOccurs="unbounded"/>
<element ref="md:AssertionIDRequestService" minOccurs="0" maxOccurs="unbounded"/>
<element ref="md:AttributeProfile" minOccurs="0" maxOccurs="unbounded"/>
<element ref="saml:Attribute" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="WantAuthnRequestsSigned" type="boolean" use="optional"/>
</extension>
</complexContent>
</complexType>
<element name="SingleSignOnService" type="md:EndpointType"/>
<element name="NameIDMappingService" type="md:EndpointType"/>
<element name="AssertionIDRequestService" type="md:EndpointType"/>
<element name="AttributeProfile" type="anyURI"/>
<element name="SPSSODescriptor" type="md:SPSSODescriptorType"/>
<complexType name="SPSSODescriptorType">
<complexContent>
<extension base="md:SSODescriptorType">
<sequence>
<element ref="md:AssertionConsumerService" maxOccurs="unbounded"/>
<element ref="md:AttributeConsumingService" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="AuthnRequestsSigned" type="boolean" use="optional"/>
<attribute name="WantAssertionsSigned" type="boolean" use="optional"/>
</extension>
</complexContent>
</complexType>
<element name="AssertionConsumerService" type="md:IndexedEndpointType"/>
<element name="AttributeConsumingService" type="md:AttributeConsumingServiceType"/>
<complexType name="AttributeConsumingServiceType">
<sequence>
<element ref="md:ServiceName" maxOccurs="unbounded"/>
<element ref="md:ServiceDescription" minOccurs="0" maxOccurs="unbounded"/>
<element ref="md:RequestedAttribute" maxOccurs="unbounded"/>
</sequence>
<attribute name="index" type="unsignedShort" use="required"/>
<attribute name="isDefault" type="boolean" use="optional"/>
</complexType>
<element name="ServiceName" type="md:localizedNameType"/>
<element name="ServiceDescription" type="md:localizedNameType"/>
<element name="RequestedAttribute" type="md:RequestedAttributeType"/>
<complexType name="RequestedAttributeType">
<complexContent>
<extension base="saml:AttributeType">
<attribute name="isRequired" type="boolean" use="optional"/>
</extension>
</complexContent>
</complexType>
<element name="AuthnAuthorityDescriptor" type="md:AuthnAuthorityDescriptorType"/>
<complexType name="AuthnAuthorityDescriptorType">
<complexContent>
<extension base="md:RoleDescriptorType">
<sequence>
<element ref="md:AuthnQueryService" maxOccurs="unbounded"/>
<element ref="md:AssertionIDRequestService" minOccurs="0" maxOccurs="unbounded"/>
<element ref="md:NameIDFormat" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
</extension>
</complexContent>
</complexType>
<element name="AuthnQueryService" type="md:EndpointType"/>
<element name="PDPDescriptor" type="md:PDPDescriptorType"/>
<complexType name="PDPDescriptorType">
<complexContent>
<extension base="md:RoleDescriptorType">
<sequence>
<element ref="md:AuthzService" maxOccurs="unbounded"/>
<element ref="md:AssertionIDRequestService" minOccurs="0" maxOccurs="unbounded"/>
<element ref="md:NameIDFormat" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
</extension>
</complexContent>
</complexType>
<element name="AuthzService" type="md:EndpointType"/>
<element name="AttributeAuthorityDescriptor" type="md:AttributeAuthorityDescriptorType"/>
<complexType name="AttributeAuthorityDescriptorType">
<complexContent>
<extension base="md:RoleDescriptorType">
<sequence>
<element ref="md:AttributeService" maxOccurs="unbounded"/>
<element ref="md:AssertionIDRequestService" minOccurs="0" maxOccurs="unbounded"/>
<element ref="md:NameIDFormat" minOccurs="0" maxOccurs="unbounded"/>
<element ref="md:AttributeProfile" minOccurs="0" maxOccurs="unbounded"/>
<element ref="saml:Attribute" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
</extension>
</complexContent>
</complexType>
<element name="AttributeService" type="md:EndpointType"/>
<element name="AffiliationDescriptor" type="md:AffiliationDescriptorType"/>
<complexType name="AffiliationDescriptorType">
<sequence>
<element ref="ds:Signature" minOccurs="0"/>
<element ref="md:Extensions" minOccurs="0"/>
<element ref="md:AffiliateMember" maxOccurs="unbounded"/>
</sequence>
<attribute name="affiliationOwnerID" type="md:entityIDType" use="required"/>
<attribute name="validUntil" type="dateTime" use="optional"/>
<attribute name="cacheDuration" type="duration" use="optional"/>
<attribute name="ID" type="ID" use="optional"/>
<anyAttribute namespace="##other" processContents="lax"/>
</complexType>
<element name="AffiliateMember" type="md:entityIDType"/>
</schema>

View file

@ -0,0 +1,302 @@
<?xml version="1.0" encoding="UTF-8"?>
<schema
targetNamespace="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
elementFormDefault="unqualified"
attributeFormDefault="unqualified"
blockDefault="substitution"
version="2.0">
<import namespace="urn:oasis:names:tc:SAML:2.0:assertion"
schemaLocation="saml-schema-assertion-2.0.xsd"/>
<import namespace="http://www.w3.org/2000/09/xmldsig#"
schemaLocation="xmldsig-core-schema.xsd"/>
<annotation>
<documentation>
Document identifier: saml-schema-protocol-2.0
Location: http://docs.oasis-open.org/security/saml/v2.0/
Revision history:
V1.0 (November, 2002):
Initial Standard Schema.
V1.1 (September, 2003):
Updates within the same V1.0 namespace.
V2.0 (March, 2005):
New protocol schema based in a SAML V2.0 namespace.
</documentation>
</annotation>
<complexType name="RequestAbstractType" abstract="true">
<sequence>
<element ref="saml:Issuer" minOccurs="0"/>
<element ref="ds:Signature" minOccurs="0"/>
<element ref="samlp:Extensions" minOccurs="0"/>
</sequence>
<attribute name="ID" type="ID" use="required"/>
<attribute name="Version" type="string" use="required"/>
<attribute name="IssueInstant" type="dateTime" use="required"/>
<attribute name="Destination" type="anyURI" use="optional"/>
<attribute name="Consent" type="anyURI" use="optional"/>
</complexType>
<element name="Extensions" type="samlp:ExtensionsType"/>
<complexType name="ExtensionsType">
<sequence>
<any namespace="##other" processContents="lax" maxOccurs="unbounded"/>
</sequence>
</complexType>
<complexType name="StatusResponseType">
<sequence>
<element ref="saml:Issuer" minOccurs="0"/>
<element ref="ds:Signature" minOccurs="0"/>
<element ref="samlp:Extensions" minOccurs="0"/>
<element ref="samlp:Status"/>
</sequence>
<attribute name="ID" type="ID" use="required"/>
<attribute name="InResponseTo" type="NCName" use="optional"/>
<attribute name="Version" type="string" use="required"/>
<attribute name="IssueInstant" type="dateTime" use="required"/>
<attribute name="Destination" type="anyURI" use="optional"/>
<attribute name="Consent" type="anyURI" use="optional"/>
</complexType>
<element name="Status" type="samlp:StatusType"/>
<complexType name="StatusType">
<sequence>
<element ref="samlp:StatusCode"/>
<element ref="samlp:StatusMessage" minOccurs="0"/>
<element ref="samlp:StatusDetail" minOccurs="0"/>
</sequence>
</complexType>
<element name="StatusCode" type="samlp:StatusCodeType"/>
<complexType name="StatusCodeType">
<sequence>
<element ref="samlp:StatusCode" minOccurs="0"/>
</sequence>
<attribute name="Value" type="anyURI" use="required"/>
</complexType>
<element name="StatusMessage" type="string"/>
<element name="StatusDetail" type="samlp:StatusDetailType"/>
<complexType name="StatusDetailType">
<sequence>
<any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
</complexType>
<element name="AssertionIDRequest" type="samlp:AssertionIDRequestType"/>
<complexType name="AssertionIDRequestType">
<complexContent>
<extension base="samlp:RequestAbstractType">
<sequence>
<element ref="saml:AssertionIDRef" maxOccurs="unbounded"/>
</sequence>
</extension>
</complexContent>
</complexType>
<element name="SubjectQuery" type="samlp:SubjectQueryAbstractType"/>
<complexType name="SubjectQueryAbstractType" abstract="true">
<complexContent>
<extension base="samlp:RequestAbstractType">
<sequence>
<element ref="saml:Subject"/>
</sequence>
</extension>
</complexContent>
</complexType>
<element name="AuthnQuery" type="samlp:AuthnQueryType"/>
<complexType name="AuthnQueryType">
<complexContent>
<extension base="samlp:SubjectQueryAbstractType">
<sequence>
<element ref="samlp:RequestedAuthnContext" minOccurs="0"/>
</sequence>
<attribute name="SessionIndex" type="string" use="optional"/>
</extension>
</complexContent>
</complexType>
<element name="RequestedAuthnContext" type="samlp:RequestedAuthnContextType"/>
<complexType name="RequestedAuthnContextType">
<choice>
<element ref="saml:AuthnContextClassRef" maxOccurs="unbounded"/>
<element ref="saml:AuthnContextDeclRef" maxOccurs="unbounded"/>
</choice>
<attribute name="Comparison" type="samlp:AuthnContextComparisonType" use="optional"/>
</complexType>
<simpleType name="AuthnContextComparisonType">
<restriction base="string">
<enumeration value="exact"/>
<enumeration value="minimum"/>
<enumeration value="maximum"/>
<enumeration value="better"/>
</restriction>
</simpleType>
<element name="AttributeQuery" type="samlp:AttributeQueryType"/>
<complexType name="AttributeQueryType">
<complexContent>
<extension base="samlp:SubjectQueryAbstractType">
<sequence>
<element ref="saml:Attribute" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
</extension>
</complexContent>
</complexType>
<element name="AuthzDecisionQuery" type="samlp:AuthzDecisionQueryType"/>
<complexType name="AuthzDecisionQueryType">
<complexContent>
<extension base="samlp:SubjectQueryAbstractType">
<sequence>
<element ref="saml:Action" maxOccurs="unbounded"/>
<element ref="saml:Evidence" minOccurs="0"/>
</sequence>
<attribute name="Resource" type="anyURI" use="required"/>
</extension>
</complexContent>
</complexType>
<element name="AuthnRequest" type="samlp:AuthnRequestType"/>
<complexType name="AuthnRequestType">
<complexContent>
<extension base="samlp:RequestAbstractType">
<sequence>
<element ref="saml:Subject" minOccurs="0"/>
<element ref="samlp:NameIDPolicy" minOccurs="0"/>
<element ref="saml:Conditions" minOccurs="0"/>
<element ref="samlp:RequestedAuthnContext" minOccurs="0"/>
<element ref="samlp:Scoping" minOccurs="0"/>
</sequence>
<attribute name="ForceAuthn" type="boolean" use="optional"/>
<attribute name="IsPassive" type="boolean" use="optional"/>
<attribute name="ProtocolBinding" type="anyURI" use="optional"/>
<attribute name="AssertionConsumerServiceIndex" type="unsignedShort" use="optional"/>
<attribute name="AssertionConsumerServiceURL" type="anyURI" use="optional"/>
<attribute name="AttributeConsumingServiceIndex" type="unsignedShort" use="optional"/>
<attribute name="ProviderName" type="string" use="optional"/>
</extension>
</complexContent>
</complexType>
<element name="NameIDPolicy" type="samlp:NameIDPolicyType"/>
<complexType name="NameIDPolicyType">
<attribute name="Format" type="anyURI" use="optional"/>
<attribute name="SPNameQualifier" type="string" use="optional"/>
<attribute name="AllowCreate" type="boolean" use="optional"/>
</complexType>
<element name="Scoping" type="samlp:ScopingType"/>
<complexType name="ScopingType">
<sequence>
<element ref="samlp:IDPList" minOccurs="0"/>
<element ref="samlp:RequesterID" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="ProxyCount" type="nonNegativeInteger" use="optional"/>
</complexType>
<element name="RequesterID" type="anyURI"/>
<element name="IDPList" type="samlp:IDPListType"/>
<complexType name="IDPListType">
<sequence>
<element ref="samlp:IDPEntry" maxOccurs="unbounded"/>
<element ref="samlp:GetComplete" minOccurs="0"/>
</sequence>
</complexType>
<element name="IDPEntry" type="samlp:IDPEntryType"/>
<complexType name="IDPEntryType">
<attribute name="ProviderID" type="anyURI" use="required"/>
<attribute name="Name" type="string" use="optional"/>
<attribute name="Loc" type="anyURI" use="optional"/>
</complexType>
<element name="GetComplete" type="anyURI"/>
<element name="Response" type="samlp:ResponseType"/>
<complexType name="ResponseType">
<complexContent>
<extension base="samlp:StatusResponseType">
<choice minOccurs="0" maxOccurs="unbounded">
<element ref="saml:Assertion"/>
<element ref="saml:EncryptedAssertion"/>
</choice>
</extension>
</complexContent>
</complexType>
<element name="ArtifactResolve" type="samlp:ArtifactResolveType"/>
<complexType name="ArtifactResolveType">
<complexContent>
<extension base="samlp:RequestAbstractType">
<sequence>
<element ref="samlp:Artifact"/>
</sequence>
</extension>
</complexContent>
</complexType>
<element name="Artifact" type="string"/>
<element name="ArtifactResponse" type="samlp:ArtifactResponseType"/>
<complexType name="ArtifactResponseType">
<complexContent>
<extension base="samlp:StatusResponseType">
<sequence>
<any namespace="##any" processContents="lax" minOccurs="0"/>
</sequence>
</extension>
</complexContent>
</complexType>
<element name="ManageNameIDRequest" type="samlp:ManageNameIDRequestType"/>
<complexType name="ManageNameIDRequestType">
<complexContent>
<extension base="samlp:RequestAbstractType">
<sequence>
<choice>
<element ref="saml:NameID"/>
<element ref="saml:EncryptedID"/>
</choice>
<choice>
<element ref="samlp:NewID"/>
<element ref="samlp:NewEncryptedID"/>
<element ref="samlp:Terminate"/>
</choice>
</sequence>
</extension>
</complexContent>
</complexType>
<element name="NewID" type="string"/>
<element name="NewEncryptedID" type="saml:EncryptedElementType"/>
<element name="Terminate" type="samlp:TerminateType"/>
<complexType name="TerminateType"/>
<element name="ManageNameIDResponse" type="samlp:StatusResponseType"/>
<element name="LogoutRequest" type="samlp:LogoutRequestType"/>
<complexType name="LogoutRequestType">
<complexContent>
<extension base="samlp:RequestAbstractType">
<sequence>
<choice>
<element ref="saml:BaseID"/>
<element ref="saml:NameID"/>
<element ref="saml:EncryptedID"/>
</choice>
<element ref="samlp:SessionIndex" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="Reason" type="string" use="optional"/>
<attribute name="NotOnOrAfter" type="dateTime" use="optional"/>
</extension>
</complexContent>
</complexType>
<element name="SessionIndex" type="string"/>
<element name="LogoutResponse" type="samlp:StatusResponseType"/>
<element name="NameIDMappingRequest" type="samlp:NameIDMappingRequestType"/>
<complexType name="NameIDMappingRequestType">
<complexContent>
<extension base="samlp:RequestAbstractType">
<sequence>
<choice>
<element ref="saml:BaseID"/>
<element ref="saml:NameID"/>
<element ref="saml:EncryptedID"/>
</choice>
<element ref="samlp:NameIDPolicy"/>
</sequence>
</extension>
</complexContent>
</complexType>
<element name="NameIDMappingResponse" type="samlp:NameIDMappingResponseType"/>
<complexType name="NameIDMappingResponseType">
<complexContent>
<extension base="samlp:StatusResponseType">
<choice>
<element ref="saml:NameID"/>
<element ref="saml:EncryptedID"/>
</choice>
</extension>
</complexContent>
</complexType>
</schema>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<schema
targetNamespace="urn:oasis:names:tc:SAML:metadata:attribute"
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute"
elementFormDefault="unqualified"
attributeFormDefault="unqualified"
blockDefault="substitution"
version="2.0">
<annotation>
<documentation>
Document title: SAML V2.0 Metadata Extention for Entity Attributes Schema
Document identifier: sstc-metadata-attr.xsd
Location: http://www.oasis-open.org/committees/documents.php?wg_abbrev=security
Revision history:
V1.0 (November 2008):
Initial version.
</documentation>
</annotation>
<import namespace="urn:oasis:names:tc:SAML:2.0:assertion"
schemaLocation="saml-schema-assertion-2.0.xsd"/>
<element name="EntityAttributes" type="mdattr:EntityAttributesType"/>
<complexType name="EntityAttributesType">
<choice maxOccurs="unbounded">
<element ref="saml:Attribute"/>
<element ref="saml:Assertion"/>
</choice>
</complexType>
</schema>

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<schema
targetNamespace="urn:oasis:names:tc:SAML:attribute:ext"
xmlns="http://www.w3.org/2001/XMLSchema"
elementFormDefault="unqualified"
attributeFormDefault="unqualified"
blockDefault="substitution"
version="2.0">
<annotation>
<documentation>
Document title: SAML V2.0 Attribute Extension Schema
Document identifier: sstc-saml-attribute-ext.xsd
Location: http://www.oasis-open.org/committees/documents.php?wg_abbrev=security
Revision history:
V1.0 (October 2008):
Initial version.
</documentation>
</annotation>
<attribute name="OriginalIssuer" type="anyURI"/>
<attribute name="LastModified" type="dateTime"/>
</schema>

View file

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<schema
targetNamespace="urn:oasis:names:tc:SAML:metadata:algsupport"
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:alg="urn:oasis:names:tc:SAML:metadata:algsupport"
elementFormDefault="unqualified"
attributeFormDefault="unqualified"
blockDefault="substitution"
version="1.0">
<annotation>
<documentation>
Document title: Metadata Extension Schema for SAML V2.0 Metadata Profile for Algorithm Support Version 1.0
Document identifier: sstc-saml-metadata-algsupport.xsd
Location: http://docs.oasis-open.org/security/saml/Post2.0/
Revision history:
V1.0 (June 2010):
Initial version.
</documentation>
</annotation>
<element name="DigestMethod" type="alg:DigestMethodType"/>
<complexType name="DigestMethodType">
<sequence>
<any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="Algorithm" type="anyURI" use="required"/>
</complexType>
<element name="SigningMethod" type="alg:SigningMethodType"/>
<complexType name="SigningMethodType">
<sequence>
<any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="Algorithm" type="anyURI" use="required"/>
<attribute name="MinKeySize" type="positiveInteger"/>
<attribute name="MaxKeySize" type="positiveInteger"/>
</complexType>
</schema>

View file

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<schema
targetNamespace="urn:oasis:names:tc:SAML:metadata:ui"
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui"
elementFormDefault="unqualified"
attributeFormDefault="unqualified"
blockDefault="substitution"
version="1.0">
<annotation>
<documentation>
Document title: Metadata Extension Schema for SAML V2.0 Metadata Extensions for Login and Discovery User Interface Version 1.0
Document identifier: sstc-saml-metadata-ui-v1.0.xsd
Location: http://docs.oasis-open.org/security/saml/Post2.0/
Revision history:
16 November 2010:
Added Keywords element/type.
01 November 2010
Changed filename.
September 2010:
Initial version.
</documentation>
</annotation>
<import namespace="urn:oasis:names:tc:SAML:2.0:metadata"
schemaLocation="saml-schema-metadata-2.0.xsd"/>
<import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="xml.xsd"/>
<element name="UIInfo" type="mdui:UIInfoType" />
<complexType name="UIInfoType">
<choice minOccurs="0" maxOccurs="unbounded">
<element ref="mdui:DisplayName"/>
<element ref="mdui:Description"/>
<element ref="mdui:Keywords"/>
<element ref="mdui:Logo"/>
<element ref="mdui:InformationURL"/>
<element ref="mdui:PrivacyStatementURL"/>
<any namespace="##other" processContents="lax"/>
</choice>
</complexType>
<element name="DisplayName" type="md:localizedNameType"/>
<element name="Description" type="md:localizedNameType"/>
<element name="InformationURL" type="md:localizedURIType"/>
<element name="PrivacyStatementURL" type="md:localizedURIType"/>
<element name="Keywords" type="mdui:KeywordsType"/>
<complexType name="KeywordsType">
<simpleContent>
<extension base="mdui:listOfStrings">
<attribute ref="xml:lang" use="required"/>
</extension>
</simpleContent>
</complexType>
<simpleType name="listOfStrings">
<list itemType="string"/>
</simpleType>
<element name="Logo" type="mdui:LogoType"/>
<complexType name="LogoType">
<simpleContent>
<extension base="anyURI">
<attribute name="height" type="positiveInteger" use="required"/>
<attribute name="width" type="positiveInteger" use="required"/>
<attribute ref="xml:lang"/>
</extension>
</simpleContent>
</complexType>
<element name="DiscoHints" type="mdui:DiscoHintsType"/>
<complexType name="DiscoHintsType">
<choice minOccurs="0" maxOccurs="unbounded">
<element ref="mdui:IPHint"/>
<element ref="mdui:DomainHint"/>
<element ref="mdui:GeolocationHint"/>
<any namespace="##other" processContents="lax"/>
</choice>
</complexType>
<element name="IPHint" type="string"/>
<element name="DomainHint" type="string"/>
<element name="GeolocationHint" type="anyURI"/>
</schema>

View file

@ -0,0 +1,136 @@
<?xml version="1.0" encoding="utf-8"?>
<schema xmlns='http://www.w3.org/2001/XMLSchema' version='1.0'
xmlns:xenc='http://www.w3.org/2001/04/xmlenc#'
xmlns:ds='http://www.w3.org/2000/09/xmldsig#'
targetNamespace='http://www.w3.org/2001/04/xmlenc#'
elementFormDefault='qualified'>
<import namespace='http://www.w3.org/2000/09/xmldsig#'
schemaLocation='xmldsig-core-schema.xsd'/>
<complexType name='EncryptedType' abstract='true'>
<sequence>
<element name='EncryptionMethod' type='xenc:EncryptionMethodType'
minOccurs='0'/>
<element ref='ds:KeyInfo' minOccurs='0'/>
<element ref='xenc:CipherData'/>
<element ref='xenc:EncryptionProperties' minOccurs='0'/>
</sequence>
<attribute name='Id' type='ID' use='optional'/>
<attribute name='Type' type='anyURI' use='optional'/>
<attribute name='MimeType' type='string' use='optional'/>
<attribute name='Encoding' type='anyURI' use='optional'/>
</complexType>
<complexType name='EncryptionMethodType' mixed='true'>
<sequence>
<element name='KeySize' minOccurs='0' type='xenc:KeySizeType'/>
<element name='OAEPparams' minOccurs='0' type='base64Binary'/>
<any namespace='##other' minOccurs='0' maxOccurs='unbounded'/>
</sequence>
<attribute name='Algorithm' type='anyURI' use='required'/>
</complexType>
<simpleType name='KeySizeType'>
<restriction base="integer"/>
</simpleType>
<element name='CipherData' type='xenc:CipherDataType'/>
<complexType name='CipherDataType'>
<choice>
<element name='CipherValue' type='base64Binary'/>
<element ref='xenc:CipherReference'/>
</choice>
</complexType>
<element name='CipherReference' type='xenc:CipherReferenceType'/>
<complexType name='CipherReferenceType'>
<choice>
<element name='Transforms' type='xenc:TransformsType' minOccurs='0'/>
</choice>
<attribute name='URI' type='anyURI' use='required'/>
</complexType>
<complexType name='TransformsType'>
<sequence>
<element ref='ds:Transform' maxOccurs='unbounded'/>
</sequence>
</complexType>
<element name='EncryptedData' type='xenc:EncryptedDataType'/>
<complexType name='EncryptedDataType'>
<complexContent>
<extension base='xenc:EncryptedType'>
</extension>
</complexContent>
</complexType>
<!-- Children of ds:KeyInfo -->
<element name='EncryptedKey' type='xenc:EncryptedKeyType'/>
<complexType name='EncryptedKeyType'>
<complexContent>
<extension base='xenc:EncryptedType'>
<sequence>
<element ref='xenc:ReferenceList' minOccurs='0'/>
<element name='CarriedKeyName' type='string' minOccurs='0'/>
</sequence>
<attribute name='Recipient' type='string'
use='optional'/>
</extension>
</complexContent>
</complexType>
<element name="AgreementMethod" type="xenc:AgreementMethodType"/>
<complexType name="AgreementMethodType" mixed="true">
<sequence>
<element name="KA-Nonce" minOccurs="0" type="base64Binary"/>
<!-- <element ref="ds:DigestMethod" minOccurs="0"/> -->
<any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
<element name="OriginatorKeyInfo" minOccurs="0" type="ds:KeyInfoType"/>
<element name="RecipientKeyInfo" minOccurs="0" type="ds:KeyInfoType"/>
</sequence>
<attribute name="Algorithm" type="anyURI" use="required"/>
</complexType>
<!-- End Children of ds:KeyInfo -->
<element name='ReferenceList'>
<complexType>
<choice minOccurs='1' maxOccurs='unbounded'>
<element name='DataReference' type='xenc:ReferenceType'/>
<element name='KeyReference' type='xenc:ReferenceType'/>
</choice>
</complexType>
</element>
<complexType name='ReferenceType'>
<sequence>
<any namespace='##other' minOccurs='0' maxOccurs='unbounded'/>
</sequence>
<attribute name='URI' type='anyURI' use='required'/>
</complexType>
<element name='EncryptionProperties' type='xenc:EncryptionPropertiesType'/>
<complexType name='EncryptionPropertiesType'>
<sequence>
<element ref='xenc:EncryptionProperty' maxOccurs='unbounded'/>
</sequence>
<attribute name='Id' type='ID' use='optional'/>
</complexType>
<element name='EncryptionProperty' type='xenc:EncryptionPropertyType'/>
<complexType name='EncryptionPropertyType' mixed='true'>
<choice maxOccurs='unbounded'>
<any namespace='##other' processContents='lax'/>
</choice>
<attribute name='Target' type='anyURI' use='optional'/>
<attribute name='Id' type='ID' use='optional'/>
<anyAttribute namespace="http://www.w3.org/XML/1998/namespace"/>
</complexType>
</schema>

View file

@ -0,0 +1,287 @@
<?xml version='1.0'?>
<?xml-stylesheet href="../2008/09/xsd.xsl" type="text/xsl"?>
<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns ="http://www.w3.org/1999/xhtml"
xml:lang="en">
<xs:annotation>
<xs:documentation>
<div>
<h1>About the XML namespace</h1>
<div class="bodytext">
<p>
This schema document describes the XML namespace, in a form
suitable for import by other schema documents.
</p>
<p>
See <a href="http://www.w3.org/XML/1998/namespace.html">
http://www.w3.org/XML/1998/namespace.html</a> and
<a href="http://www.w3.org/TR/REC-xml">
http://www.w3.org/TR/REC-xml</a> for information
about this namespace.
</p>
<p>
Note that local names in this namespace are intended to be
defined only by the World Wide Web Consortium or its subgroups.
The names currently defined in this namespace are listed below.
They should not be used with conflicting semantics by any Working
Group, specification, or document instance.
</p>
<p>
See further below in this document for more information about <a
href="#usage">how to refer to this schema document from your own
XSD schema documents</a> and about <a href="#nsversioning">the
namespace-versioning policy governing this schema document</a>.
</p>
</div>
</div>
</xs:documentation>
</xs:annotation>
<xs:attribute name="lang">
<xs:annotation>
<xs:documentation>
<div>
<h3>lang (as an attribute name)</h3>
<p>
denotes an attribute whose value
is a language code for the natural language of the content of
any element; its value is inherited. This name is reserved
by virtue of its definition in the XML specification.</p>
</div>
<div>
<h4>Notes</h4>
<p>
Attempting to install the relevant ISO 2- and 3-letter
codes as the enumerated possible values is probably never
going to be a realistic possibility.
</p>
<p>
See BCP 47 at <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt">
http://www.rfc-editor.org/rfc/bcp/bcp47.txt</a>
and the IANA language subtag registry at
<a href="http://www.iana.org/assignments/language-subtag-registry">
http://www.iana.org/assignments/language-subtag-registry</a>
for further information.
</p>
<p>
The union allows for the 'un-declaration' of xml:lang with
the empty string.
</p>
</div>
</xs:documentation>
</xs:annotation>
<xs:simpleType>
<xs:union memberTypes="xs:language">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value=""/>
</xs:restriction>
</xs:simpleType>
</xs:union>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="space">
<xs:annotation>
<xs:documentation>
<div>
<h3>space (as an attribute name)</h3>
<p>
denotes an attribute whose
value is a keyword indicating what whitespace processing
discipline is intended for the content of the element; its
value is inherited. This name is reserved by virtue of its
definition in the XML specification.</p>
</div>
</xs:documentation>
</xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:NCName">
<xs:enumeration value="default"/>
<xs:enumeration value="preserve"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="base" type="xs:anyURI"> <xs:annotation>
<xs:documentation>
<div>
<h3>base (as an attribute name)</h3>
<p>
denotes an attribute whose value
provides a URI to be used as the base for interpreting any
relative URIs in the scope of the element on which it
appears; its value is inherited. This name is reserved
by virtue of its definition in the XML Base specification.</p>
<p>
See <a
href="http://www.w3.org/TR/xmlbase/">http://www.w3.org/TR/xmlbase/</a>
for information about this attribute.
</p>
</div>
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="id" type="xs:ID">
<xs:annotation>
<xs:documentation>
<div>
<h3>id (as an attribute name)</h3>
<p>
denotes an attribute whose value
should be interpreted as if declared to be of type ID.
This name is reserved by virtue of its definition in the
xml:id specification.</p>
<p>
See <a
href="http://www.w3.org/TR/xml-id/">http://www.w3.org/TR/xml-id/</a>
for information about this attribute.
</p>
</div>
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attributeGroup name="specialAttrs">
<xs:attribute ref="xml:base"/>
<xs:attribute ref="xml:lang"/>
<xs:attribute ref="xml:space"/>
<xs:attribute ref="xml:id"/>
</xs:attributeGroup>
<xs:annotation>
<xs:documentation>
<div>
<h3>Father (in any context at all)</h3>
<div class="bodytext">
<p>
denotes Jon Bosak, the chair of
the original XML Working Group. This name is reserved by
the following decision of the W3C XML Plenary and
XML Coordination groups:
</p>
<blockquote>
<p>
In appreciation for his vision, leadership and
dedication the W3C XML Plenary on this 10th day of
February, 2000, reserves for Jon Bosak in perpetuity
the XML name "xml:Father".
</p>
</blockquote>
</div>
</div>
</xs:documentation>
</xs:annotation>
<xs:annotation>
<xs:documentation>
<div xml:id="usage" id="usage">
<h2><a name="usage">About this schema document</a></h2>
<div class="bodytext">
<p>
This schema defines attributes and an attribute group suitable
for use by schemas wishing to allow <code>xml:base</code>,
<code>xml:lang</code>, <code>xml:space</code> or
<code>xml:id</code> attributes on elements they define.
</p>
<p>
To enable this, such a schema must import this schema for
the XML namespace, e.g. as follows:
</p>
<pre>
&lt;schema . . .>
. . .
&lt;import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/xml.xsd"/>
</pre>
<p>
or
</p>
<pre>
&lt;import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
</pre>
<p>
Subsequently, qualified reference to any of the attributes or the
group defined below will have the desired effect, e.g.
</p>
<pre>
&lt;type . . .>
. . .
&lt;attributeGroup ref="xml:specialAttrs"/>
</pre>
<p>
will define a type which will schema-validate an instance element
with any of those attributes.
</p>
</div>
</div>
</xs:documentation>
</xs:annotation>
<xs:annotation>
<xs:documentation>
<div id="nsversioning" xml:id="nsversioning">
<h2><a name="nsversioning">Versioning policy for this schema document</a></h2>
<div class="bodytext">
<p>
In keeping with the XML Schema WG's standard versioning
policy, this schema document will persist at
<a href="http://www.w3.org/2009/01/xml.xsd">
http://www.w3.org/2009/01/xml.xsd</a>.
</p>
<p>
At the date of issue it can also be found at
<a href="http://www.w3.org/2001/xml.xsd">
http://www.w3.org/2001/xml.xsd</a>.
</p>
<p>
The schema document at that URI may however change in the future,
in order to remain compatible with the latest version of XML
Schema itself, or with the XML namespace itself. In other words,
if the XML Schema or XML namespaces change, the version of this
document at <a href="http://www.w3.org/2001/xml.xsd">
http://www.w3.org/2001/xml.xsd
</a>
will change accordingly; the version at
<a href="http://www.w3.org/2009/01/xml.xsd">
http://www.w3.org/2009/01/xml.xsd
</a>
will not change.
</p>
<p>
Previous dated (and unchanging) versions of this schema
document are at:
</p>
<ul>
<li><a href="http://www.w3.org/2009/01/xml.xsd">
http://www.w3.org/2009/01/xml.xsd</a></li>
<li><a href="http://www.w3.org/2007/08/xml.xsd">
http://www.w3.org/2007/08/xml.xsd</a></li>
<li><a href="http://www.w3.org/2004/10/xml.xsd">
http://www.w3.org/2004/10/xml.xsd</a></li>
<li><a href="http://www.w3.org/2001/03/xml.xsd">
http://www.w3.org/2001/03/xml.xsd</a></li>
</ul>
</div>
</div>
</xs:documentation>
</xs:annotation>
</xs:schema>

View file

@ -0,0 +1,309 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Schema for XML Signatures
http://www.w3.org/2000/09/xmldsig#
$Revision: 1.1 $ on $Date: 2002/02/08 20:32:26 $ by $Author: reagle $
Copyright 2001 The Internet Society and W3C (Massachusetts Institute
of Technology, Institut National de Recherche en Informatique et en
Automatique, Keio University). All Rights Reserved.
http://www.w3.org/Consortium/Legal/
This document is governed by the W3C Software License [1] as described
in the FAQ [2].
[1] http://www.w3.org/Consortium/Legal/copyright-software-19980720
[2] http://www.w3.org/Consortium/Legal/IPR-FAQ-20000620.html#DTD
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
targetNamespace="http://www.w3.org/2000/09/xmldsig#"
version="0.1" elementFormDefault="qualified">
<!-- Basic Types Defined for Signatures -->
<simpleType name="CryptoBinary">
<restriction base="base64Binary">
</restriction>
</simpleType>
<!-- Start Signature -->
<element name="Signature" type="ds:SignatureType"/>
<complexType name="SignatureType">
<sequence>
<element ref="ds:SignedInfo"/>
<element ref="ds:SignatureValue"/>
<element ref="ds:KeyInfo" minOccurs="0"/>
<element ref="ds:Object" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="Id" type="ID" use="optional"/>
</complexType>
<element name="SignatureValue" type="ds:SignatureValueType"/>
<complexType name="SignatureValueType">
<simpleContent>
<extension base="base64Binary">
<attribute name="Id" type="ID" use="optional"/>
</extension>
</simpleContent>
</complexType>
<!-- Start SignedInfo -->
<element name="SignedInfo" type="ds:SignedInfoType"/>
<complexType name="SignedInfoType">
<sequence>
<element ref="ds:CanonicalizationMethod"/>
<element ref="ds:SignatureMethod"/>
<element ref="ds:Reference" maxOccurs="unbounded"/>
</sequence>
<attribute name="Id" type="ID" use="optional"/>
</complexType>
<element name="CanonicalizationMethod" type="ds:CanonicalizationMethodType"/>
<complexType name="CanonicalizationMethodType" mixed="true">
<sequence>
<any namespace="##any" minOccurs="0" maxOccurs="unbounded"/>
<!-- (0,unbounded) elements from (1,1) namespace -->
</sequence>
<attribute name="Algorithm" type="anyURI" use="required"/>
</complexType>
<element name="SignatureMethod" type="ds:SignatureMethodType"/>
<complexType name="SignatureMethodType" mixed="true">
<sequence>
<element name="HMACOutputLength" minOccurs="0" type="ds:HMACOutputLengthType"/>
<any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
<!-- (0,unbounded) elements from (1,1) external namespace -->
</sequence>
<attribute name="Algorithm" type="anyURI" use="required"/>
</complexType>
<!-- Start Reference -->
<element name="Reference" type="ds:ReferenceType"/>
<complexType name="ReferenceType">
<sequence>
<element ref="ds:Transforms" minOccurs="0"/>
<element ref="ds:DigestMethod"/>
<element ref="ds:DigestValue"/>
</sequence>
<attribute name="Id" type="ID" use="optional"/>
<attribute name="URI" type="anyURI" use="optional"/>
<attribute name="Type" type="anyURI" use="optional"/>
</complexType>
<element name="Transforms" type="ds:TransformsType"/>
<complexType name="TransformsType">
<sequence>
<element ref="ds:Transform" maxOccurs="unbounded"/>
</sequence>
</complexType>
<element name="Transform" type="ds:TransformType"/>
<complexType name="TransformType" mixed="true">
<choice minOccurs="0" maxOccurs="unbounded">
<any namespace="##other" processContents="lax"/>
<!-- (1,1) elements from (0,unbounded) namespaces -->
<element name="XPath" type="string"/>
</choice>
<attribute name="Algorithm" type="anyURI" use="required"/>
</complexType>
<!-- End Reference -->
<element name="DigestMethod" type="ds:DigestMethodType"/>
<complexType name="DigestMethodType" mixed="true">
<sequence>
<any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="Algorithm" type="anyURI" use="required"/>
</complexType>
<element name="DigestValue" type="ds:DigestValueType"/>
<simpleType name="DigestValueType">
<restriction base="base64Binary"/>
</simpleType>
<!-- End SignedInfo -->
<!-- Start KeyInfo -->
<element name="KeyInfo" type="ds:KeyInfoType"/>
<complexType name="KeyInfoType" mixed="true">
<choice maxOccurs="unbounded">
<element ref="ds:KeyName"/>
<element ref="ds:KeyValue"/>
<element ref="ds:RetrievalMethod"/>
<element ref="ds:X509Data"/>
<element ref="ds:PGPData"/>
<element ref="ds:SPKIData"/>
<element ref="ds:MgmtData"/>
<any processContents="lax" namespace="##other"/>
<!-- (1,1) elements from (0,unbounded) namespaces -->
</choice>
<attribute name="Id" type="ID" use="optional"/>
</complexType>
<element name="KeyName" type="string"/>
<element name="MgmtData" type="string"/>
<element name="KeyValue" type="ds:KeyValueType"/>
<complexType name="KeyValueType" mixed="true">
<choice>
<element ref="ds:DSAKeyValue"/>
<element ref="ds:RSAKeyValue"/>
<any namespace="##other" processContents="lax"/>
</choice>
</complexType>
<element name="RetrievalMethod" type="ds:RetrievalMethodType"/>
<complexType name="RetrievalMethodType">
<sequence>
<element ref="ds:Transforms" minOccurs="0"/>
</sequence>
<attribute name="URI" type="anyURI"/>
<attribute name="Type" type="anyURI" use="optional"/>
</complexType>
<!-- Start X509Data -->
<element name="X509Data" type="ds:X509DataType"/>
<complexType name="X509DataType">
<sequence maxOccurs="unbounded">
<choice>
<element name="X509IssuerSerial" type="ds:X509IssuerSerialType"/>
<element name="X509SKI" type="base64Binary"/>
<element name="X509SubjectName" type="string"/>
<element name="X509Certificate" type="base64Binary"/>
<element name="X509CRL" type="base64Binary"/>
<any namespace="##other" processContents="lax"/>
</choice>
</sequence>
</complexType>
<complexType name="X509IssuerSerialType">
<sequence>
<element name="X509IssuerName" type="string"/>
<element name="X509SerialNumber" type="integer"/>
</sequence>
</complexType>
<!-- End X509Data -->
<!-- Begin PGPData -->
<element name="PGPData" type="ds:PGPDataType"/>
<complexType name="PGPDataType">
<choice>
<sequence>
<element name="PGPKeyID" type="base64Binary"/>
<element name="PGPKeyPacket" type="base64Binary" minOccurs="0"/>
<any namespace="##other" processContents="lax" minOccurs="0"
maxOccurs="unbounded"/>
</sequence>
<sequence>
<element name="PGPKeyPacket" type="base64Binary"/>
<any namespace="##other" processContents="lax" minOccurs="0"
maxOccurs="unbounded"/>
</sequence>
</choice>
</complexType>
<!-- End PGPData -->
<!-- Begin SPKIData -->
<element name="SPKIData" type="ds:SPKIDataType"/>
<complexType name="SPKIDataType">
<sequence maxOccurs="unbounded">
<element name="SPKISexp" type="base64Binary"/>
<any namespace="##other" processContents="lax" minOccurs="0"/>
</sequence>
</complexType>
<!-- End SPKIData -->
<!-- End KeyInfo -->
<!-- Start Object (Manifest, SignatureProperty) -->
<element name="Object" type="ds:ObjectType"/>
<complexType name="ObjectType" mixed="true">
<sequence minOccurs="0" maxOccurs="unbounded">
<any namespace="##any" processContents="lax"/>
</sequence>
<attribute name="Id" type="ID" use="optional"/>
<attribute name="MimeType" type="string" use="optional"/> <!-- add a grep facet -->
<attribute name="Encoding" type="anyURI" use="optional"/>
</complexType>
<element name="Manifest" type="ds:ManifestType"/>
<complexType name="ManifestType">
<sequence>
<element ref="ds:Reference" maxOccurs="unbounded"/>
</sequence>
<attribute name="Id" type="ID" use="optional"/>
</complexType>
<element name="SignatureProperties" type="ds:SignaturePropertiesType"/>
<complexType name="SignaturePropertiesType">
<sequence>
<element ref="ds:SignatureProperty" maxOccurs="unbounded"/>
</sequence>
<attribute name="Id" type="ID" use="optional"/>
</complexType>
<element name="SignatureProperty" type="ds:SignaturePropertyType"/>
<complexType name="SignaturePropertyType" mixed="true">
<choice maxOccurs="unbounded">
<any namespace="##other" processContents="lax"/>
<!-- (1,1) elements from (1,unbounded) namespaces -->
</choice>
<attribute name="Target" type="anyURI" use="required"/>
<attribute name="Id" type="ID" use="optional"/>
</complexType>
<!-- End Object (Manifest, SignatureProperty) -->
<!-- Start Algorithm Parameters -->
<simpleType name="HMACOutputLengthType">
<restriction base="integer"/>
</simpleType>
<!-- Start KeyValue Element-types -->
<element name="DSAKeyValue" type="ds:DSAKeyValueType"/>
<complexType name="DSAKeyValueType">
<sequence>
<sequence minOccurs="0">
<element name="P" type="ds:CryptoBinary"/>
<element name="Q" type="ds:CryptoBinary"/>
</sequence>
<element name="G" type="ds:CryptoBinary" minOccurs="0"/>
<element name="Y" type="ds:CryptoBinary"/>
<element name="J" type="ds:CryptoBinary" minOccurs="0"/>
<sequence minOccurs="0">
<element name="Seed" type="ds:CryptoBinary"/>
<element name="PgenCounter" type="ds:CryptoBinary"/>
</sequence>
</sequence>
</complexType>
<element name="RSAKeyValue" type="ds:RSAKeyValueType"/>
<complexType name="RSAKeyValueType">
<sequence>
<element name="Modulus" type="ds:CryptoBinary"/>
<element name="Exponent" type="ds:CryptoBinary"/>
</sequence>
</complexType>
<!-- End KeyValue Element-types -->
<!-- End Signature -->
</schema>

View file

@ -0,0 +1,6 @@
{
"php-saml": {
"version": "2.9.0",
"released": "27/06/2016"
}
}

View file

@ -0,0 +1,26 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "test"
msgstr "test"
msgid "test2: %s"
msgstr "test2: %s"
#: index.php:15 endpoints/acs.php:35
msgid "User attributes:"
msgstr ""
#: index.php:16 endpoints/acs.php:36
msgid "Name"
msgstr ""
#: index.php:16 endpoints/acs.php:36
msgid "Values"
msgstr ""
#: index.php:26 endpoints/acs.php:46
msgid "Attributes not found"
msgstr ""

View file

@ -0,0 +1,27 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "test"
msgstr "prueba"
msgid "test2: %s"
msgstr "prueba2: %s"
#: index.php:15 endpoints/acs.php:35
msgid "User attributes:"
msgstr "Atributos del usuario:"
#: index.php:16 endpoints/acs.php:36
msgid "Name"
msgstr "Nombre"
#: index.php:16 endpoints/acs.php:36
msgid "Values"
msgstr "Valores"
#: index.php:26 endpoints/acs.php:46
msgid "Attributes not found"
msgstr "Atributos no encontrados"

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" ?>
<phpdoc>
<title>OneLogin's SAML PHP Toolkit</title>
<parser>
<default-package-name>OneLogin_SAML_Toolkit</default-package-name>
<target>docs/Saml3</target>
</parser>
<extensions>
<extension>php</extension>
</extensions>
<transformer>
<target>docs/Saml3</target>
</transformer>
<transformations>
<template name="onelogin" />
<!-- <template name="clean" /> -->
</transformations>
<files>
<directory>lib/Saml2/*</directory>
<ignore>lib/Saml/*</ignore>
<ignore>tests/*</ignore>
<ignore>demo1/*</ignore>
<ignore>demo2/*</ignore>
<ignore>demo-old/*</ignore>
<ignore>endpoints/*</ignore>
<ignore>extlib/*</ignore>
<ignore>locale/*</ignore>
<ignore>docs/*</ignore>
<ignore>vendor/*</ignore>
<ignore>compatibility.php</ignore>
<ignore>_toolkit_loader.php</ignore>
<ignore>settings.php</ignore>
<ignore>settings_example.php</ignore>
<ignore>advanced_settings.php</ignore>
<ignore>advanced_settings_example</ignore>
</files>
</phpdoc>

View file

@ -0,0 +1,84 @@
<?php
$settings = array (
// If 'strict' is True, then the PHP Toolkit will reject unsigned
// or unencrypted messages if it expects them signed or encrypted
// Also will reject the messages if not strictly follow the SAML
// standard: Destination, NameId, Conditions ... are validated too.
'strict' => false,
// Enable debug mode (to print errors)
'debug' => false,
// Service Provider Data that we are deploying
'sp' => array (
// Identifier of the SP entity (must be a URI)
'entityId' => '',
// Specifies info about where and how the <AuthnResponse> message MUST be
// returned to the requester, in this case our SP.
'assertionConsumerService' => array (
// URL Location where the <Response> from the IdP will be returned
'url' => '',
// SAML protocol binding to be used when returning the <Response>
// message. Onelogin Toolkit supports for this endpoint the
// HTTP-Redirect binding only
'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
),
// Specifies info about where and how the <Logout Response> message MUST be
// returned to the requester, in this case our SP.
'singleLogoutService' => array (
// URL Location where the <Response> from the IdP will be returned
'url' => '',
// SAML protocol binding to be used when returning the <Response>
// message. Onelogin Toolkit supports for this endpoint the
// HTTP-Redirect binding only
'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
),
// Specifies constraints on the name identifier to be used to
// represent the requested subject.
// Take a look on lib/Saml2/Constants.php to see the NameIdFormat supported
'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified',
// Usually x509cert and privateKey of the SP are provided by files placed at
// the certs folder. But we can also provide them with the following parameters
'x509cert' => '',
'privateKey' => '',
),
// Identity Provider Data that we want connect with our SP
'idp' => array (
// Identifier of the IdP entity (must be a URI)
'entityId' => '',
// SSO endpoint info of the IdP. (Authentication Request protocol)
'singleSignOnService' => array (
// URL Target of the IdP where the SP will send the Authentication Request Message
'url' => '',
// SAML protocol binding to be used when returning the <Response>
// message. Onelogin Toolkit supports for this endpoint the
// HTTP-POST binding only
'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
),
// SLO endpoint info of the IdP.
'singleLogoutService' => array (
// URL Location of the IdP where the SP will send the SLO Request
'url' => '',
// SAML protocol binding to be used when returning the <Response>
// message. Onelogin Toolkit supports for this endpoint the
// HTTP-Redirect binding only
'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
),
// Public x509 certificate of the IdP
'x509cert' => '',
/*
* Instead of use the whole x509cert you can use a fingerprint
* (openssl x509 -noout -fingerprint -in "idp.crt" to generate it,
* or add for example the -sha256 , -sha384 or -sha512 parameter)
*
* If a fingerprint is provided, then the certFingerprintAlgorithm is required in order to
* let the toolkit know which Algorithm was used. Possible values: sha1, sha256, sha384 or sha512
* 'sha1' is the default value.
*/
// 'certFingerprint' => '',
// 'certFingerprintAlgorithm' => 'sha1',
),
);

25
admin.php Normal file
View file

@ -0,0 +1,25 @@
<?php
/**
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
$app = new \OCA\User_SAML\AppInfo\Application();
/** @var \OCA\User_SAML\Controller\SettingsController $controller */
$controller = $app->getContainer()->query('SettingsController');
return $controller->displayPanel()->render();

42
appinfo/app.php Normal file
View file

@ -0,0 +1,42 @@
<?php
/**
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
require_once __DIR__ . '/../3rdparty/vendor/autoload.php';
$urlGenerator = \OC::$server->getURLGenerator();
$userBackend = new \OCA\User_SAML\UserBackend(
\OC::$server->getConfig(),
\OC::$server->getLogger(),
\OC::$server->getURLGenerator(),
\OC::$server->getSession()
);
OC_User::useBackend($userBackend);
OC_User::handleApacheAuth();
// Redirect all requests to the login page to the SAML login
$currentUrl = explode('?', $_SERVER['REQUEST_URI'], 2)[0];
if($currentUrl === '/server/index.php/login' && !OC_User::isLoggedIn()) {
header('Location: '.$urlGenerator->linkToRouteAbsolute('user_saml.SAML.login'));
exit();
}
\OCP\App::registerPersonal('user_saml', 'admin');

12
appinfo/info.xml Normal file
View file

@ -0,0 +1,12 @@
<?xml version="1.0"?>
<info>
<id>user_saml</id>
<name>SAML Authentication</name>
<description>Authenticates user against a SAML backend, such as Shibboleth.</description>
<licence>AGPL</licence>
<author>Nextcloud</author>
<version>1.0.0</version>
<dependencies>
<owncloud min-version="9.0" max-version="9.1" />
</dependencies>
</info>

46
appinfo/routes.php Normal file
View file

@ -0,0 +1,46 @@
<?php
/**
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\User_SAML\AppInfo;
(new \OCP\AppFramework\App('user_saml'))->registerRoutes($this, array('routes' => array(
[
'name' => 'SAML#login',
'url' => '/saml/login',
'verb' => 'GET',
],
[
'name' => 'SAML#getMetadata',
'url' => '/saml/metadata',
'verb' => 'GET',
],
[
'name' => 'SAML#assertionConsumerService',
'url' => '/saml/acs',
'verb' => 'POST',
],
[
'name' => 'SAML#singleLogoutService',
'url' => '/saml/sls',
'verb' => 'GET',
],
)));

11
css/settings.css Normal file
View file

@ -0,0 +1,11 @@
#user-saml input[type="text"],textarea {
width: 100%;
}
#user-saml input.required {
background-color: lightgoldenrodyellow;
}
#user-saml input[type="checkbox"] {
vertical-align:middle;
}

49
js/settings.js Normal file
View file

@ -0,0 +1,49 @@
function setSAMLConfigValue(category, setting, value) {
OC.msg.startSaving('#user-saml-save-indicator');
OC.AppConfig.setValue('user_saml', category+'-'+setting, value);
OC.msg.finishedSaving('#user-saml-save-indicator', {status: 'success', data: {message: t('user_saml', 'Saved')}});
}
$(function() {
// Enable tabs
$('#user-saml-settings').tabs();
$('input:checkbox[value="1"]').attr('checked', true);
$('#user-saml-sp input[type="text"], #user-saml-sp textarea').change(function(e) {
var el = $(this);
$.when(el.focusout()).then(function() {
var key = $(this).attr('name');
setSAMLConfigValue('sp', key, $(this).val());
});
if (e.keyCode === 13) {
var key = $(this).attr('name');
setSAMLConfigValue('sp', key, $(this).val());
}
});
$('#user-saml-idp input[type="text"], #user-saml-idp textarea').change(function(e) {
var el = $(this);
$.when(el.focusout()).then(function() {
var key = $(this).attr('name');
setSAMLConfigValue('idp', key, $(this).val());
});
if (e.keyCode === 13) {
var key = $(this).attr('name');
setSAMLConfigValue('idp', key, $(this).val());
}
});
$('#user-saml-security input[type="checkbox"]').change(function(e) {
var el = $(this);
$.when(el.focusout()).then(function() {
var key = $(this).attr('name');
if($(this).val() === "0") {
$(this).val("1");
} else {
$(this).val("0");
}
setSAMLConfigValue('security', key, $(this).val());
});
});
});

View file

@ -0,0 +1,59 @@
<?php
/**
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\User_SAML\AppInfo;
use OCA\User_SAML\Controller\SAMLController;
use OCA\User_SAML\Controller\SettingsController;
use OCA\User_SAML\SAMLSettings;
use OCP\AppFramework\App;
use OCP\AppFramework\IAppContainer;
class Application extends App {
public function __construct(array $urlParams = array()) {
parent::__construct('user_saml', $urlParams);
$container = $this->getContainer();
/**
* Controller
*/
$container->registerService('SettingsController', function(IAppContainer $c) {
/** @var \OC\Server $server */
$server = $c->query('ServerContainer');
return new SettingsController(
$c->getAppName(),
$server->getRequest(),
$server->getL10N('user_saml')
);
});
$container->registerService('SAMLController', function(IAppContainer $c) {
/** @var \OC\Server $server */
$server = $c->query('ServerContainer');
return new SAMLController(
$c->getAppName(),
$server->getRequest(),
$server->getSession(),
$server->getUserSession(),
new SAMLSettings($server->getURLGenerator(), $server->getConfig())
);
});
}
}

View file

@ -0,0 +1,125 @@
<?php
/**
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\User_SAML\Controller;
use OCA\User_SAML\SAMLSettings;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\IRequest;
use OCP\ISession;
use OCP\IUserSession;
class SAMLController extends Controller {
/** @var ISession */
private $session;
/** @var IUserSession */
private $userSession;
/** @var SAMLSettings */
private $SAMLSettings;
/**
* @param string $appName
* @param IRequest $request
* @param ISession $session
* @param IUserSession $userSession
* @param SAMLSettings $SAMLSettings
*/
public function __construct($appName,
IRequest $request,
ISession $session,
IUserSession $userSession,
SAMLSettings $SAMLSettings) {
parent::__construct($appName, $request);
$this->session = $session;
$this->userSession = $userSession;
$this->SAMLSettings = $SAMLSettings;
}
/**
* @PublicPage
* @NoCSRFRequired
*/
public function login() {
$auth = new \OneLogin_Saml2_Auth($this->SAMLSettings->getOneLoginSettingsArray());
$auth->login(\OC::$server->getURLGenerator()->getAbsoluteURL('/'));
}
/**
* @PublicPage
* @NoCSRFRequired
*/
public function getMetadata() {
$settings = new \OneLogin_Saml2_Settings($this->SAMLSettings->getOneLoginSettingsArray());
$metadata = $settings->getSPMetadata();
$errors = $settings->validateMetadata($metadata);
if (empty($errors)) {
return new Http\DataDownloadResponse($metadata, 'metadata.xml', 'text/xml');
} else {
throw new \OneLogin_Saml2_Error(
'Invalid SP metadata: '.implode(', ', $errors),
\OneLogin_Saml2_Error::METADATA_SP_INVALID
);
}
}
/**
* @PublicPage
* @NoCSRFRequired
* @UseSession
*/
public function assertionConsumerService() {
$auth = new \OneLogin_Saml2_Auth($this->SAMLSettings->getOneLoginSettingsArray());
$auth->processResponse(null);
$errors = $auth->getErrors();
// FIXME: Appframworkize
if (!empty($errors)) {
print_r('<p>'.implode(', ', $errors).'</p>');
}
if (!$auth->isAuthenticated()) {
echo "<p>Not authenticated</p>";
exit();
}
$this->session->set('user_saml.samlUserData', $auth->getAttributes());
$this->session->set('user_saml.samlNameId', $auth->getNameId());
$this->session->set('user_saml.samlSessionIndex', $auth->getSessionIndex());
$this->session->set('user_saml.samlSessionExpiration', $auth->getSessionExpiration());
return new Http\RedirectResponse(\OC::$server->getURLGenerator()->getAbsoluteURL('/'));
}
/**
* @PublicPage
* @NoCSRFRequired
*/
public function singleLogoutService() {
$auth = new \OneLogin_Saml2_Auth($this->SAMLSettings->getOneLoginSettingsArray());
$returnTo = null;
$parameters = array();
$nameId = $this->session->get('user_saml.samlNameId');
$sessionIndex = $this->session->get('user_saml.samlSessionIndex');
$this->userSession->logout();
$auth->logout($returnTo, $parameters, $nameId, $sessionIndex);
}
}

View file

@ -0,0 +1,76 @@
<?php
/**
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\User_SAML\Controller;
use OCA\User_SAML\SAMLSettings;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\IL10N;
use OCP\IRequest;
use OCP\ISession;
use OCP\IUserSession;
class SettingsController extends Controller {
/** @var IL10N */
private $l10n;
/**
* @param string $appName
* @param IRequest $request
* @param IL10N $l10n
*/
public function __construct($appName,
IRequest $request,
IL10N $l10n) {
parent::__construct($appName, $request);
$this->l10n = $l10n;
}
public function displayPanel() {
$serviceProviderFields = [
'x509cert' => $this->l10n->t('X.509 certificate of the Service Provider'),
'privateKey' => $this->l10n->t('Private key of the Service Provider'),
];
$securityOfferFields = [
'nameIdEncrypted' => $this->l10n->t('Indicates that the nameID of the <samlp:logoutRequest> sent by this SP will be encrypted.'),
'authnRequestsSigned' => $this->l10n->t('Indicates whether the <samlp:AuthnRequest> messages sent by this SP will be signed. [Metadata of the SP will offer this info]'),
'logoutRequestSigned' => $this->l10n->t('Indicates whether the <samlp:logoutRequest> messages sent by this SP will be signed.'),
'logoutResponseSigned' => $this->l10n->t('Indicates whether the <samlp:logoutResponse> messages sent by this SP will be signed.'),
'signMetadata' => $this->l10n->t('Whether the metadata should be signed.'),
];
$securityRequiredFields = [
'wantMessagesSigned' => $this->l10n->t('Indicates a requirement for the <samlp:Response>, <samlp:LogoutRequest> and <samlp:LogoutResponse> elements received by this SP to be signed.'),
'wantAssertionsSigned' => $this->l10n->t('Indicates a requirement for the <saml:Assertion> elements received by this SP to be signed. [Metadata of the SP will offer this info]'),
'wantAssertionsEncrypted' => $this->l10n->t('Indicates a requirement for the <saml:Assertion> elements received by this SP to be encrypted.'),
'wantNameIdEncrypted' => $this->l10n->t('Indicates a requirement for the NameID received by this SP to be encrypted.'),
'wantXMLValidation' => $this->l10n->t('Indicates if the SP will validate all received XMLs.'),
];
$params = [
'sp' => $serviceProviderFields,
'security-offer' => $securityOfferFields,
'security-required' => $securityRequiredFields,
];
return new Http\TemplateResponse($this->appName, 'settings', $params, 'blank');
}
}

100
lib/samlsettings.php Normal file
View file

@ -0,0 +1,100 @@
<?php
/**
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\User_SAML;
use OCP\AppFramework\Http;
use OCP\IConfig;
use OCP\IURLGenerator;
class SAMLSettings {
/** @var IURLGenerator */
private $urlGenerator;
/** @var IConfig */
private $config;
/**
* @param IURLGenerator $urlGenerator
* @param IConfig $config
*/
public function __construct(IURLGenerator $urlGenerator,
IConfig $config) {
$this->urlGenerator = $urlGenerator;
$this->config = $config;
}
public function getOneLoginSettingsArray() {
$settings = [
//'debug' => true,
'strict' => true,
'security' => [
'nameIdEncrypted' => ($this->config->getAppValue('user_saml', 'security-nameIdEncrypted', '0') === '1') ? true : false,
'authnRequestsSigned' => ($this->config->getAppValue('user_saml', 'security-authnRequestsSigned', '0') === '1') ? true : false,
'logoutRequestSigned' => ($this->config->getAppValue('user_saml', 'security-logoutRequestSigned', '0') === '1') ? true : false,
'logoutResponseSigned' => ($this->config->getAppValue('user_saml', 'security-logoutResponseSigned', '0') === '1') ? true : false,
'signMetadata' => ($this->config->getAppValue('user_saml', 'security-signMetadata', '0') === '1') ? true : false,
'wantMessagesSigned' => ($this->config->getAppValue('user_saml', 'security-wantMessagesSigned', '0') === '1') ? true : false,
'wantAssertionsSigned' => ($this->config->getAppValue('user_saml', 'security-wantAssertionsSigned', '0') === '1') ? true : false,
'wantAssertionsEncrypted' => ($this->config->getAppValue('user_saml', 'security-wantAssertionsEncrypted', '0') === '1') ? true : false,
'wantNameId' => ($this->config->getAppValue('user_saml', 'security-wantNameId', '0') === '1') ? true : false,
'wantNameIdEncrypted' => ($this->config->getAppValue('user_saml', 'security-wantNameIdEncrypted', '0') === '1') ? true : false,
'wantXMLValidation' => ($this->config->getAppValue('user_saml', 'security-wantXMLValidation', '0') === '1') ? true : false,
],
'sp' => [
'entityId' => $this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.getMetadata'),
'assertionConsumerService' => [
'url' => $this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.assertionConsumerService'),
],
'singleLogoutService' => [
'url' => $this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.singleLogoutService'),
],
],
'idp' => [
'entityId' => $this->config->getAppValue('user_saml', 'idp-entityId', ''),
'singleSignOnService' => [
'url' => $this->config->getAppValue('user_saml', 'idp-singleSignOnService.url', ''),
],
'singleLogoutService' => [
'url' => $this->config->getAppValue('user_saml', 'idp-singleLogoutService.url', ''),
],
],
];
$spx509cert = $this->config->getAppValue('user_saml', 'sp-x509cert', '');
$spxprivateKey = $this->config->getAppValue('user_saml', 'sp-privateKey', '');
if($spx509cert !== '') {
$settings['sp']['x509cert'] = $spx509cert;
}
if($spxprivateKey !== '') {
$settings['sp']['privateKey'] = $spxprivateKey;
}
$idpx509cert = $this->config->getAppValue('user_saml', 'idp-x509cert', '');
if($idpx509cert !== '') {
$settings['idp']['x509cert'] = $idpx509cert;
}
return $settings;
}
}

181
lib/userbackend.php Normal file
View file

@ -0,0 +1,181 @@
<?php
/**
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\User_SAML;
use OCP\Authentication\IApacheBackend;
use OCP\UserInterface;
use OCP\IUserBackend;
use OCP\IConfig;
use OCP\ILogger;
use OCP\IURLGenerator;
use OCP\ISession;
class UserBackend implements IApacheBackend, UserInterface, IUserBackend {
/** @var IConfig */
private $config;
/** @var ILogger */
private $logger;
/** @var IURLGenerator */
private $urlGenerator;
/** @var ISession */
private $session;
/**
* @param IConfig $config
* @param ILogger $logger
* @param IURLGenerator $urlGenerator
* @param ISession $session
*/
public function __construct(IConfig $config,
ILogger $logger,
IURLGenerator $urlGenerator,
ISession $session) {
$this->config = $config;
$this->logger = $logger;
$this->urlGenerator = $urlGenerator;
$this->session = $session;
}
/**
* Check if backend implements actions
* @param int $actions bitwise-or'ed actions
* @return boolean
*
* Returns the supported actions as int to be
* compared with \OC_User_Backend::CREATE_USER etc.
* @since 4.5.0
*/
public function implementsActions($actions) {
return false;
}
/**
* delete a user
* @param string $uid The username of the user to delete
* @return bool
* @since 4.5.0
*/
public function deleteUser($uid) {
return false;
}
/**
* Get a list of all users
*
* @param string $search
* @param null|int $limit
* @param null|int $offset
* @return string[] an array of all uids
* @since 4.5.0
*/
public function getUsers($search = '', $limit = null, $offset = null) {
return false;
}
/**
* check if a user exists
* @param string $uid the username
* @return boolean
* @since 4.5.0
*/
public function userExists($uid) {
return true;
}
/**
* get display name of the user
* @param string $uid user ID of the user
* @return string display name
* @since 4.5.0
*/
public function getDisplayName($uid) {
return false;
}
/**
* Get a list of all display names and user ids.
*
* @param string $search
* @param string|null $limit
* @param string|null $offset
* @return array an array of all displayNames (value) and the corresponding uids (key)
* @since 4.5.0
*/
public function getDisplayNames($search = '', $limit = null, $offset = null) {
return [];
}
/**
* Check if a user list is available or not
* @return boolean if users can be listed or not
* @since 4.5.0
*/
public function hasUserListings() {
return false;
}
/**
* In case the user has been authenticated by Apache true is returned.
*
* @return boolean whether Apache reports a user as currently logged in.
* @since 6.0.0
*/
public function isSessionActive() {
if($this->session->exists('user_saml.samlUserData')) {
return true;
}
return false;
}
/**
* Creates an attribute which is added to the logout hyperlink. It can
* supply any attribute(s) which are valid for <a>.
*
* @return string with one or more HTML attributes.
* @since 6.0.0
*/
public function getLogoutAttribute() {
// FIXME: Detect if SLO is configured
return 'href="'.$this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.singleLogoutService').'"';
}
/**
* Return the id of the current user
* @return string
* @since 6.0.0
*/
public function getCurrentUserId() {
// FIXME: Don't harcode
return $this->session->get('user_saml.samlUserData')['urn:oid:0.9.2342.19200300.100.1.1'][0];
}
/**
* Backend name to be shown in user management
* @return string the name of the backend to be shown
* @since 8.0.0
*/
public function getBackendName() {
return 'user_saml';
}
}

54
templates/settings.php Normal file
View file

@ -0,0 +1,54 @@
<?php
script('user_saml', 'settings');
style('user_saml', 'settings');
/** @var array $_ */
?>
<form id="user-saml" class="section" action="#" method="post">
<h2><?php p($l->t('SAML')); ?></h2>
<div id="user-saml-save-indicator" class="msg success inlineblock" style="display: none;">Saved</div>
<div id="user-saml-settings">
<ul>
<li><a href="#user-saml-sp"><?php p($l->t('Service Provider Data')) ?></a></li>
<li><a href="#user-saml-idp"><?php p($l->t('Identity Provider Data')) ?></a></li>
<li><a href="#user-saml-security"><?php p($l->t('Security settings')) ?></a></li>
<li><a href="#user-saml-general"><?php p($l->t('General')) ?></a></li>
</ul>
<div id="user-saml-sp">
<p><?php print_unescaped($l->t('If your Service Provider should use certificates you can optionally specify them here.')) ?></p>
<?php foreach($_['sp'] as $key => $text): ?>
<textarea name="<?php p($key) ?>" placeholder="<?php p($text) ?>"><?php p(\OC::$server->getConfig()->getAppValue('user_saml', 'sp-'.$key, '')) ?></textarea>
<?php endforeach; ?>
</div>
<div id="user-saml-idp">
<p><?php print_unescaped($l->t('Configure your IdP settings here, all yellow input fields are required, others optional.')) ?></p>
<input name="entityId" value="<?php p(\OC::$server->getConfig()->getAppValue('user_saml', 'sp-entityId', '')) ?>" type="text" class="required" placeholder="<?php p($l->t('Identifier of the IdP entity (must be a URI)')) ?>"/>
<input name="singleSignOnService.url" value="<?php p(\OC::$server->getConfig()->getAppValue('user_saml', 'sp-singleSignOnService.url', '')) ?>" type="text" class="required" placeholder="<?php p($l->t('URL Target of the IdP where the SP will send the Authentication Request Message')) ?>"/>
<input name="singleLogoutService.url" value="<?php p(\OC::$server->getConfig()->getAppValue('user_saml', 'sp-singleLogoutService.url', '')) ?>" type="text" placeholder="<?php p($l->t('URL Location of the IdP where the SP will send the SLO Request')) ?>"/>
<textarea name="x509cert" placeholder="<?php p($l->t('Public X.509 certificate of the IdP')) ?>"><?php p(\OC::$server->getConfig()->getAppValue('user_saml', 'sp-x509cert', '')) ?></textarea>
</div>
<div id="user-saml-security">
<p><?php print_unescaped($l->t('For increased security we recommend enabling the following settings if supported by your environment.')) ?></p>
<h3><?php p($l->t('Signatures and encryption offered')) ?></h3>
<?php foreach($_['security-offer'] as $key => $text): ?>
<input type="checkbox" id="user-saml-<?php p($key)?>" name="<?php p($key)?>" value="<?php p(\OC::$server->getConfig()->getAppValue('user_saml', 'security-'.$key, '0')) ?>">
<label for="user-saml-<?php p($key)?>"><?php p($text) ?></label><br/>
<?php endforeach; ?>
<h3><?php p($l->t('Signatures and encryption required')) ?></h3>
<?php foreach($_['security-required'] as $key => $text): ?>
<input type="checkbox" id="user-saml-<?php p($key)?>" name="<?php p($key)?>" value="<?php p(\OC::$server->getConfig()->getAppValue('user_saml', 'security-'.$key, '0')) ?>">
<label for="user-saml-<?php p($key)?>"><?php p($text) ?></label><br/>
<?php endforeach; ?>
</div>
<div id="user-saml-general">
<!-- FIXME: Add mapping editor -->
<!-- FIXME: Add "Disable timeout from SAML" switch (checked by default)-->
</div>
<a href="<?php p(\OC::$server->getURLGenerator()->linkToRoute('user_saml.SAML.getMetadata')) ?>" class="button"><?php p($l->t('Download metadata XML')) ?></a>
<!-- FIXME: Add test settings -->
<a class="button"><?php p($l->t('Test settings')) ?></a>
</div>
</form>