summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore160
-rw-r--r--attributemap/facebook2name.php29
-rw-r--r--attributemap/linkedin2name.php25
-rw-r--r--attributemap/myspace2name.php22
-rw-r--r--attributemap/name2oid.php3
-rw-r--r--attributemap/name2urn.php3
-rw-r--r--attributemap/oid2name.php3
-rw-r--r--attributemap/oid2urn.php3
-rw-r--r--attributemap/twitter2name.php21
-rw-r--r--attributemap/urn2name.php3
-rw-r--r--attributemap/urn2oid.php3
-rw-r--r--attributemap/windowslive2name.php20
-rwxr-xr-xbin/build-release.sh2
-rwxr-xr-xbin/importPdoMetadata.php27
-rwxr-xr-xbin/initMDSPdo.php27
-rw-r--r--composer.json2
-rw-r--r--composer.lock163
-rw-r--r--config-templates/config.php71
-rw-r--r--docs/simplesamlphp-automated_metadata.txt16
-rw-r--r--docs/simplesamlphp-database.txt91
-rw-r--r--docs/simplesamlphp-install.txt112
-rw-r--r--docs/simplesamlphp-metadata-pdostoragehandler.txt79
-rw-r--r--docs/simplesamlphp-reference-idp-remote.txt3
-rw-r--r--lib/SimpleSAML/Auth/Source.php719
-rw-r--r--lib/SimpleSAML/AuthMemCookie.php286
-rw-r--r--lib/SimpleSAML/Bindings/Shib13/HTTPPost.php253
-rw-r--r--lib/SimpleSAML/Configuration.php2396
-rw-r--r--lib/SimpleSAML/Database.php303
-rw-r--r--lib/SimpleSAML/Error/Exception.php486
-rw-r--r--lib/SimpleSAML/IdP.php1085
-rw-r--r--lib/SimpleSAML/IdP/LogoutHandler.php98
-rw-r--r--lib/SimpleSAML/IdP/LogoutIFrame.php150
-rw-r--r--lib/SimpleSAML/IdP/LogoutTraditional.php174
-rw-r--r--lib/SimpleSAML/Logger.php52
-rw-r--r--lib/SimpleSAML/Memcache.php847
-rw-r--r--lib/SimpleSAML/Metadata/MetaDataStorageHandler.php693
-rw-r--r--lib/SimpleSAML/Metadata/MetaDataStorageHandlerFlatFile.php261
-rw-r--r--lib/SimpleSAML/Metadata/MetaDataStorageHandlerMDX.php555
-rw-r--r--lib/SimpleSAML/Metadata/MetaDataStorageHandlerPdo.php244
-rw-r--r--lib/SimpleSAML/Metadata/MetaDataStorageHandlerSerialize.php530
-rw-r--r--lib/SimpleSAML/Metadata/MetaDataStorageHandlerXML.php193
-rw-r--r--lib/SimpleSAML/Metadata/MetaDataStorageSource.php455
-rw-r--r--lib/SimpleSAML/Metadata/SAMLBuilder.php1390
-rw-r--r--lib/SimpleSAML/Metadata/SAMLParser.php2806
-rw-r--r--lib/SimpleSAML/Metadata/Signer.php392
-rw-r--r--lib/SimpleSAML/Module.php404
-rw-r--r--lib/SimpleSAML/Session.php51
-rw-r--r--lib/SimpleSAML/SessionHandler.php309
-rw-r--r--lib/SimpleSAML/SessionHandlerCookie.php266
-rw-r--r--lib/SimpleSAML/SessionHandlerPHP.php438
-rw-r--r--lib/SimpleSAML/SessionHandlerStore.php99
-rw-r--r--lib/SimpleSAML/Stats.php172
-rw-r--r--lib/SimpleSAML/Stats/Output.php36
-rw-r--r--lib/SimpleSAML/Store.php165
-rw-r--r--lib/SimpleSAML/Utilities.php1324
-rw-r--r--lib/SimpleSAML/Utils/Config.php8
-rw-r--r--lib/SimpleSAML/Utils/Config/Metadata.php45
-rw-r--r--lib/SimpleSAML/Utils/Crypto.php112
-rw-r--r--lib/SimpleSAML/Utils/Net.php4
-rw-r--r--lib/SimpleSAML/Utils/System.php6
-rw-r--r--lib/SimpleSAML/XHTML/IdPDisco.php1101
-rw-r--r--lib/SimpleSAML/XHTML/Template.php1400
-rw-r--r--lib/_autoload.php21
-rw-r--r--lib/_autoload_modules.php6
-rw-r--r--log/_placeholder.php2
-rw-r--r--modules/aselect/default-disable3
-rw-r--r--modules/aselect/docs/aselect.txt49
-rw-r--r--modules/aselect/lib/Auth/Source/aselect.php207
-rw-r--r--modules/aselect/www/credentials.php67
-rw-r--r--modules/authX509/docs/authX509.txt4
-rw-r--r--modules/autotest/default-disable3
-rw-r--r--modules/autotest/docs/test.txt19
-rw-r--r--modules/autotest/www/attributes.php31
-rw-r--r--modules/autotest/www/login.php20
-rw-r--r--modules/autotest/www/logout.php24
-rw-r--r--modules/consentAdmin/www/consentAdmin.php20
-rw-r--r--modules/consentSimpleAdmin/config-templates/module_consentSimpleAdmin.php16
-rw-r--r--modules/consentSimpleAdmin/default-disable3
-rw-r--r--modules/consentSimpleAdmin/dictionaries/consentsimpleadmin.definition.json26
-rw-r--r--modules/consentSimpleAdmin/dictionaries/consentsimpleadmin.translation.json242
-rw-r--r--modules/consentSimpleAdmin/hooks/hook_frontpage.php20
-rw-r--r--modules/consentSimpleAdmin/hooks/hook_sanitycheck.php32
-rw-r--r--modules/consentSimpleAdmin/templates/consentadmin.php34
-rw-r--r--modules/consentSimpleAdmin/templates/consentstats.php20
-rw-r--r--modules/consentSimpleAdmin/www/consentAdmin.php87
-rw-r--r--modules/consentSimpleAdmin/www/consentStats.php29
-rw-r--r--modules/core/docs/authproc_php.txt29
-rw-r--r--modules/core/lib/Auth/Process/PHP.php102
-rw-r--r--modules/core/www/frontpage_config.php2
-rw-r--r--modules/core/www/postredirect.php8
-rw-r--r--modules/discojuice/config-templates/discojuice.php42
-rw-r--r--modules/discojuice/default-disable0
-rw-r--r--modules/discojuice/templates/central.tpl.php77
-rw-r--r--modules/discojuice/www/central.php49
-rw-r--r--modules/discojuice/www/response.html60
-rw-r--r--modules/discopower/lib/PowerIdPDisco.php686
-rw-r--r--modules/logpeek/config-templates/module_logpeek.php11
-rw-r--r--modules/logpeek/default-disable3
-rw-r--r--modules/logpeek/hooks/hook_frontpage.php16
-rw-r--r--modules/logpeek/lib/File/reverseRead.php220
-rw-r--r--modules/logpeek/lib/Syslog/parseLine.php24
-rw-r--r--modules/logpeek/templates/logpeek.php27
-rw-r--r--modules/logpeek/www/index.php53
-rw-r--r--modules/metaedit/config-template/module_metaedit.php11
-rw-r--r--modules/metaedit/default-disable3
-rw-r--r--modules/metaedit/hooks/hook_frontpage.php17
-rw-r--r--modules/metaedit/lib/MetaEditor.php177
-rw-r--r--modules/metaedit/templates/formedit.php21
-rw-r--r--modules/metaedit/templates/metalist.php49
-rw-r--r--modules/metaedit/templates/saved.php15
-rw-r--r--modules/metaedit/templates/xmlimport.tpl.php16
-rw-r--r--modules/metaedit/www/edit.php81
-rw-r--r--modules/metaedit/www/index.php51
-rw-r--r--modules/metaedit/www/resources/style.css37
-rw-r--r--modules/metaedit/www/xmlimport.php9
-rw-r--r--modules/metarefresh/config-templates/config-metarefresh.php27
-rw-r--r--modules/metarefresh/hooks/hook_cron.php17
-rw-r--r--modules/metarefresh/lib/MetaLoader.php40
-rw-r--r--modules/metarefresh/www/fetch.php17
-rw-r--r--modules/saml/lib/SP/LogoutStore.php3
-rw-r--r--modules/saml/www/sp/saml2-acs.php262
-rw-r--r--modules/saml2debug/default-disable3
-rw-r--r--modules/saml2debug/hooks/hook_frontpage.php16
-rw-r--r--modules/saml2debug/templates/debug.tpl.php68
-rw-r--r--modules/saml2debug/www/debug.php63
-rw-r--r--modules/statistics/lib/StatDataset.php624
-rw-r--r--tests/lib/SimpleSAML/DatabaseTest.php297
-rw-r--r--tests/lib/SimpleSAML/Utils/ConfigTest.php2
-rw-r--r--tests/lib/SimpleSAML/Utils/CryptoTest.php78
-rw-r--r--tests/modules/core/lib/Auth/Process/PHPTest.php103
-rw-r--r--www/_include.php169
-rw-r--r--www/admin/hostnames.php12
-rw-r--r--www/authmemcookie.php166
-rw-r--r--www/errorreport.php136
-rw-r--r--www/logout.php16
-rw-r--r--www/module.php318
-rw-r--r--www/saml2/idp/metadata.php410
137 files changed, 13679 insertions, 13245 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2aed44e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,160 @@
+.gitignore
+log
+config
+metadata
+cert
+
+# https://www.gitignore.io/api/osx,windows,linux,netbeans,sublimetext,composer,phpstorm,vagrant
+# Created by https://www.gitignore.io
+
+# Created by https://www.gitignore.io
+
+### OSX ###
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+
+### Windows ###
+# Windows image file caches
+Thumbs.db
+ehthumbs.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+
+### Linux ###
+*~
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+### Eclipse ###
+.project
+
+### NetBeans ###
+nbproject/private/
+build/
+nbbuild/
+dist/
+nbdist/
+nbactions.xml
+nb-configuration.xml
+.nb-gradle/
+
+
+### SublimeText ###
+# cache files for sublime text
+*.tmlanguage.cache
+*.tmPreferences.cache
+*.stTheme.cache
+
+# workspace files are user-specific
+*.sublime-workspace
+
+# project files should be checked into the repository, unless a significant
+# proportion of contributors will probably not be using SublimeText
+# *.sublime-project
+
+# sftp configuration file
+sftp-config.json
+
+
+### Composer ###
+composer.phar
+vendor/
+
+# Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file
+# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
+# composer.lock
+
+
+### PhpStorm ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
+
+*.iml
+
+## Directory-based project format:
+.idea/
+# if you remove the above rule, at least ignore the following:
+
+# User-specific stuff:
+# .idea/workspace.xml
+# .idea/tasks.xml
+# .idea/dictionaries
+
+# Sensitive or high-churn files:
+# .idea/dataSources.ids
+# .idea/dataSources.xml
+# .idea/sqlDataSources.xml
+# .idea/dynamic.xml
+# .idea/uiDesigner.xml
+
+# Gradle:
+# .idea/gradle.xml
+# .idea/libraries
+
+# Mongo Explorer plugin:
+# .idea/mongoSettings.xml
+
+## File-based project format:
+*.ipr
+*.iws
+
+## Plugin-specific files:
+
+# IntelliJ
+/out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+
+
+### Vagrant ###
+.vagrant/
diff --git a/attributemap/facebook2name.php b/attributemap/facebook2name.php
index eb3f7a1..5867ea7 100644
--- a/attributemap/facebook2name.php
+++ b/attributemap/facebook2name.php
@@ -1,21 +1,18 @@
<?php
$attributemap = array(
- // Generated Facebook Attributes
- 'facebook_user' => 'eduPersonPrincipalName', // username OR uid @ facebook.com
- 'facebook_targetedID' => 'eduPersonTargetedID', // http://facebook.com!uid
- 'facebook_cn' => 'cn', // duplicate of displayName
+ // Generated Facebook Attributes
+ 'facebook_user' => 'eduPersonPrincipalName', // username OR uid @ facebook.com
+ 'facebook_targetedID' => 'eduPersonTargetedID', // http://facebook.com!uid
+ 'facebook_cn' => 'cn', // duplicate of displayName
- // Attributes Returned by Facebook
- 'facebook.first_name' => 'givenName',
- 'facebook.last_name' => 'sn',
- 'facebook.name' => 'displayName', // or 'cn'
- 'facebook.email' => 'mail',
- //'facebook.pic' => 'jpegPhoto', // URL not image data
- //'facebook.pic_square' => 'jpegPhoto', // URL not image data
- 'facebook.username' => 'uid', // facebook username (maybe blank)
- //'facebook.uid' => 'uid', // numeric facebook user id
- 'facebook.profile_url' => 'labeledURI',
- 'facebook.locale' => 'preferredLanguage',
- 'facebook.about_me' => 'description',
+ // Attributes Returned by Facebook
+ 'facebook.first_name' => 'givenName',
+ 'facebook.last_name' => 'sn',
+ 'facebook.name' => 'displayName', // or 'cn'
+ 'facebook.email' => 'mail',
+ 'facebook.username' => 'uid', // facebook username (maybe blank)
+ 'facebook.profile_url' => 'labeledURI',
+ 'facebook.locale' => 'preferredLanguage',
+ 'facebook.about_me' => 'description',
);
diff --git a/attributemap/linkedin2name.php b/attributemap/linkedin2name.php
index 0d3a433..97231dd 100644
--- a/attributemap/linkedin2name.php
+++ b/attributemap/linkedin2name.php
@@ -1,19 +1,18 @@
<?php
$attributemap = array(
- // See http://developer.linkedin.com/docs/DOC-1061 for LinkedIn Profile fields.
- // NB: JSON response requires the conversion of field names from hyphened to camelCase.
- // For instance, first-name becomes firstName.
+ // See http://developer.linkedin.com/docs/DOC-1061 for LinkedIn Profile fields.
+ // NB: JSON response requires the conversion of field names from hyphened to camelCase.
+ // For instance, first-name becomes firstName.
- // Generated LinkedIn Attributes
- 'linkedin_user' => 'eduPersonPrincipalName', // id @ linkedin.com
- 'linkedin_targetedID' => 'eduPersonTargetedID', // http://linkedin.com!id
+ // Generated LinkedIn Attributes
+ 'linkedin_user' => 'eduPersonPrincipalName', // id @ linkedin.com
+ 'linkedin_targetedID' => 'eduPersonTargetedID', // http://linkedin.com!id
- // Attributes Returned by LinkedIn
- 'linkedin.firstName' => 'givenName',
- 'linkedin.lastName' => 'sn',
- 'linkedin.id' => 'uid', // alpha + mixed case user id
- //'linkedin.pictureUrl' => 'jpegPhoto', // URL not image data
- 'linkedin.headline' => 'title',
- 'linkedin.summary' => 'description',
+ // Attributes Returned by LinkedIn
+ 'linkedin.firstName' => 'givenName',
+ 'linkedin.lastName' => 'sn',
+ 'linkedin.id' => 'uid', // alpha + mixed case user id
+ 'linkedin.headline' => 'title',
+ 'linkedin.summary' => 'description',
);
diff --git a/attributemap/myspace2name.php b/attributemap/myspace2name.php
index dbc52d4..6111f61 100644
--- a/attributemap/myspace2name.php
+++ b/attributemap/myspace2name.php
@@ -1,18 +1,16 @@
<?php
$attributemap = array(
- // See http://developerwiki.myspace.com/index.php?title=People_API for attributes
+ // See http://developerwiki.myspace.com/index.php?title=People_API for attributes
- // Generated MySpace Attributes
- 'myspace_user' => 'eduPersonPrincipalName', // username OR uid @ myspace.com
- 'myspace_targetedID' => 'eduPersonTargetedID', // http://myspace.com!uid
- 'myspace_username' => 'uid', // myspace username (maybe numeric uid)
- //'myspace_uid' => 'uid', // numeric myspace user id
+ // Generated MySpace Attributes
+ 'myspace_user' => 'eduPersonPrincipalName', // username OR uid @ myspace.com
+ 'myspace_targetedID' => 'eduPersonTargetedID', // http://myspace.com!uid
+ 'myspace_username' => 'uid', // myspace username (maybe numeric uid)
- // Attributes Returned by MySpace
- 'myspace.name.givenName' => 'givenName',
- 'myspace.name.familyName' => 'sn',
- 'myspace.displayName' => 'displayName',
- //'myspace.thumbnailUrl' => 'jpegPhoto', // URL not image data
- 'myspace.profileUrl' => 'labeledURI',
+ // Attributes Returned by MySpace
+ 'myspace.name.givenName' => 'givenName',
+ 'myspace.name.familyName' => 'sn',
+ 'myspace.displayName' => 'displayName',
+ 'myspace.profileUrl' => 'labeledURI',
);
diff --git a/attributemap/name2oid.php b/attributemap/name2oid.php
index 5071450..479635a 100644
--- a/attributemap/name2oid.php
+++ b/attributemap/name2oid.php
@@ -129,11 +129,14 @@ $attributemap = array(
'roleOccupant' => 'urn:oid:2.5.4.33',
'roomNumber' => 'urn:oid:0.9.2342.19200300.100.1.6',
'sOARecord' => 'urn:oid:0.9.2342.19200300.100.1.30',
+ 'schacGender' => 'urn:oid:1.3.6.1.4.1.25178.1.2.2',
'searchGuide' => 'urn:oid:2.5.4.14',
'secretary' => 'urn:oid:0.9.2342.19200300.100.1.21',
'seeAlso' => 'urn:oid:2.5.4.34',
'serialNumber' => 'urn:oid:2.5.4.5',
'singleLevelQuality' => 'urn:oid:0.9.2342.19200300.100.1.50',
+ 'sisSchoolGrade' => 'urn:oid:1.2.752.194.10.2.2',
+ 'sisLegalGuardianFor' => 'urn:oid:1.2.752.194.10.2.1',
'sn' => 'urn:oid:2.5.4.4',
'st' => 'urn:oid:2.5.4.8',
'stateOrProvinceName' => 'urn:oid:2.5.4.8',
diff --git a/attributemap/name2urn.php b/attributemap/name2urn.php
index b9689d5..97b9d30 100644
--- a/attributemap/name2urn.php
+++ b/attributemap/name2urn.php
@@ -128,11 +128,14 @@ $attributemap = array(
'roleOccupant' => 'urn:mace:dir:attribute-def:roleOccupant',
'roomNumber' => 'urn:mace:dir:attribute-def:roomNumber',
'sOARecord' => 'urn:mace:dir:attribute-def:sOARecord',
+ 'schacGender' => 'urn:mace:dir:attribute-def:schacGender',
'searchGuide' => 'urn:mace:dir:attribute-def:searchGuide',
'secretary' => 'urn:mace:dir:attribute-def:secretary',
'seeAlso' => 'urn:mace:dir:attribute-def:seeAlso',
'serialNumber' => 'urn:mace:dir:attribute-def:serialNumber',
'singleLevelQuality' => 'urn:mace:dir:attribute-def:singleLevelQuality',
+ 'sisSchoolGrade' => 'urn:mace:dir:attribute-def:sisSchoolGrade',
+ 'sisLegalGuardianFor' => 'urn:mace:dir:attribute-def:sisLegalGuardianFor',
'sn' => 'urn:mace:dir:attribute-def:sn',
'st' => 'urn:mace:dir:attribute-def:st',
'stateOrProvinceName' => 'urn:mace:dir:attribute-def:stateOrProvinceName',
diff --git a/attributemap/oid2name.php b/attributemap/oid2name.php
index 3422d03..3ed618e 100644
--- a/attributemap/oid2name.php
+++ b/attributemap/oid2name.php
@@ -47,6 +47,8 @@ $attributemap = array(
'urn:oid:0.9.2342.19200300.100.1.8' => 'userClass',
'urn:oid:0.9.2342.19200300.100.1.9' => 'host',
'urn:oid:1.2.840.113549.1.9.1' => 'email',
+ 'urn:oid:1.2.752.194.10.2.2' => 'sisSchoolGrade',
+ 'urn:oid:1.2.752.194.10.2.1' => 'sisLegalGuardianFor',
'urn:oid:1.3.6.1.4.1.2428.90.1.1' => 'norEduOrgUniqueNumber',
'urn:oid:1.3.6.1.4.1.2428.90.1.11' => 'norEduOrgSchemaVersion',
'urn:oid:1.3.6.1.4.1.2428.90.1.12' => 'norEduOrgNIN',
@@ -75,6 +77,7 @@ $attributemap = array(
'urn:oid:1.3.6.1.4.1.5923.1.2.1.5' => 'eduOrgSuperiorURI',
'urn:oid:1.3.6.1.4.1.5923.1.2.1.6' => 'eduOrgWhitePagesURI',
'urn:oid:1.3.6.1.4.1.5923.1.5.1.1' => 'isMemberOf',
+ 'urn:oid:1.3.6.1.4.1.25178.1.2.2' => 'schacGender',
'urn:oid:2.16.840.1.113730.3.1.1' => 'carLicense',
'urn:oid:2.16.840.1.113730.3.1.2' => 'departmentNumber',
'urn:oid:2.16.840.1.113730.3.1.216' => 'userPKCS12',
diff --git a/attributemap/oid2urn.php b/attributemap/oid2urn.php
index 0a15e9e..6f5a58a 100644
--- a/attributemap/oid2urn.php
+++ b/attributemap/oid2urn.php
@@ -47,6 +47,8 @@ $attributemap = array(
'urn:oid:0.9.2342.19200300.100.1.8' => 'urn:mace:dir:attribute-def:userClass',
'urn:oid:0.9.2342.19200300.100.1.9' => 'urn:mace:dir:attribute-def:host',
'urn:oid:1.2.840.113549.1.9.1' => 'urn:mace:dir:attribute-def:email',
+ 'urn:oid:1.2.752.194.10.2.2' => 'urn:mace:dir:attribute-def:sisSchoolGrade',
+ 'urn:oid:1.2.752.194.10.2.1' => 'urn:mace:dir:attribute-def:sisLegalGuardianFor',
'urn:oid:1.3.6.1.4.1.2428.90.1.1' => 'urn:mace:dir:attribute-def:norEduOrgUniqueNumber',
'urn:oid:1.3.6.1.4.1.2428.90.1.11' => 'urn:mace:dir:attribute-def:norEduOrgSchemaVersion',
'urn:oid:1.3.6.1.4.1.2428.90.1.12' => 'urn:mace:dir:attribute-def:norEduOrgNIN',
@@ -74,6 +76,7 @@ $attributemap = array(
'urn:oid:1.3.6.1.4.1.5923.1.2.1.4' => 'urn:mace:dir:attribute-def:eduOrgLegalName',
'urn:oid:1.3.6.1.4.1.5923.1.2.1.5' => 'urn:mace:dir:attribute-def:eduOrgSuperiorURI',
'urn:oid:1.3.6.1.4.1.5923.1.2.1.6' => 'urn:mace:dir:attribute-def:eduOrgWhitePagesURI',
+ 'urn:oid:1.3.6.1.4.1.25178.1.2.2' => 'urn:mace:dir:attribute-def:schacGender',
'urn:oid:2.16.840.1.113730.3.1.1' => 'urn:mace:dir:attribute-def:carLicense',
'urn:oid:2.16.840.1.113730.3.1.2' => 'urn:mace:dir:attribute-def:departmentNumber',
'urn:oid:2.16.840.1.113730.3.1.216' => 'urn:mace:dir:attribute-def:userPKCS12',
diff --git a/attributemap/twitter2name.php b/attributemap/twitter2name.php
index 605b561..27c9e75 100644
--- a/attributemap/twitter2name.php
+++ b/attributemap/twitter2name.php
@@ -1,17 +1,14 @@
<?php
$attributemap = array(
- // Generated Twitter Attributes
- 'twitter_screen_n_realm' => 'eduPersonPrincipalName', // screen_name@twitter.com
- //'twitter_at_screen_name' => 'eduPersonPrincipalName', // legacy @twitter format
- 'twitter_targetedID' => 'eduPersonTargetedID', // http://twitter.com!id_str
+ // Generated Twitter Attributes
+ 'twitter_screen_n_realm' => 'eduPersonPrincipalName', // screen_name@twitter.com
+ 'twitter_targetedID' => 'eduPersonTargetedID', // http://twitter.com!id_str
- // Attributes Returned by Twitter
- 'twitter.screen_name' => 'uid', // equivalent to twitter username without leading @
- //'twitter.id_str' => 'uid', // persistent numeric twitter user id
- 'twitter.name' => 'displayName',
- 'twitter.url' => 'labeledURI',
- 'twitter.lang' => 'preferredLanguage',
- //'twitter.profile_image_url' => 'jpegPhoto',
- 'twitter.description' => 'description',
+ // Attributes Returned by Twitter
+ 'twitter.screen_name' => 'uid', // equivalent to twitter username without leading @
+ 'twitter.name' => 'displayName',
+ 'twitter.url' => 'labeledURI',
+ 'twitter.lang' => 'preferredLanguage',
+ 'twitter.description' => 'description',
);
diff --git a/attributemap/urn2name.php b/attributemap/urn2name.php
index 14d2d96..ba378a3 100644
--- a/attributemap/urn2name.php
+++ b/attributemap/urn2name.php
@@ -128,11 +128,14 @@ $attributemap = array(
'urn:mace:dir:attribute-def:roleOccupant' => 'roleOccupant',
'urn:mace:dir:attribute-def:roomNumber' => 'roomNumber',
'urn:mace:dir:attribute-def:sOARecord' => 'sOARecord',
+ 'urn:mace:dir:attribute-def:schacGender' => 'schacGender',
'urn:mace:dir:attribute-def:searchGuide' => 'searchGuide',
'urn:mace:dir:attribute-def:secretary' => 'secretary',
'urn:mace:dir:attribute-def:seeAlso' => 'seeAlso',
'urn:mace:dir:attribute-def:serialNumber' => 'serialNumber',
'urn:mace:dir:attribute-def:singleLevelQuality' => 'singleLevelQuality',
+ 'urn:mace:dir:attribute-def:sisSchoolGrade' =>'sisSchoolGrade',
+ 'urn:mace:dir:attribute-def:sisLegalGuardianFor' => 'sisLegalGuardianFor',
'urn:mace:dir:attribute-def:sn' => 'sn',
'urn:mace:dir:attribute-def:st' => 'st',
'urn:mace:dir:attribute-def:stateOrProvinceName' => 'stateOrProvinceName',
diff --git a/attributemap/urn2oid.php b/attributemap/urn2oid.php
index d8f1179..8f4ed34 100644
--- a/attributemap/urn2oid.php
+++ b/attributemap/urn2oid.php
@@ -128,11 +128,14 @@ $attributemap = array(
'urn:mace:dir:attribute-def:roleOccupant' => 'urn:oid:2.5.4.33',
'urn:mace:dir:attribute-def:roomNumber' => 'urn:oid:0.9.2342.19200300.100.1.6',
'urn:mace:dir:attribute-def:sOARecord' => 'urn:oid:0.9.2342.19200300.100.1.30',
+ 'urn:mace:dir:attribute-def:schacGender' => 'urn:oid:1.3.6.1.4.1.25178.1.2.2',
'urn:mace:dir:attribute-def:searchGuide' => 'urn:oid:2.5.4.14',
'urn:mace:dir:attribute-def:secretary' => 'urn:oid:0.9.2342.19200300.100.1.21',
'urn:mace:dir:attribute-def:seeAlso' => 'urn:oid:2.5.4.34',
'urn:mace:dir:attribute-def:serialNumber' => 'urn:oid:2.5.4.5',
'urn:mace:dir:attribute-def:singleLevelQuality' => 'urn:oid:0.9.2342.19200300.100.1.50',
+ 'urn:mace:dir:attribute-def:sisSchoolGrade' =>'urn:oid:1.2.752.194.10.2.2',
+ 'urn:mace:dir:attribute-def:sisLegalGuardianFor' => 'urn:oid:1.2.752.194.10.2.1',
'urn:mace:dir:attribute-def:sn' => 'urn:oid:2.5.4.4',
'urn:mace:dir:attribute-def:st' => 'urn:oid:2.5.4.8',
'urn:mace:dir:attribute-def:stateOrProvinceName' => 'urn:oid:2.5.4.8',
diff --git a/attributemap/windowslive2name.php b/attributemap/windowslive2name.php
index f43d0f9..e0821f1 100644
--- a/attributemap/windowslive2name.php
+++ b/attributemap/windowslive2name.php
@@ -1,16 +1,14 @@
<?php
$attributemap = array(
- // Generated Windows Live ID Attributes
- 'windowslive_user' => 'eduPersonPrincipalName', // uid @ windowslive.com
- 'windowslive_targetedID' => 'eduPersonTargetedID', // http://windowslive.com!uid
- 'windowslive_uid' => 'uid', // windows live id
- 'windowslive_mail' => 'mail',
-
- // Attributes Returned by Windows Live ID
- 'windowslive.FirstName' => 'givenName',
- 'windowslive.LastName' => 'sn',
- 'windowslive.Location' => 'l',
- //'windowslive.ThumbnailImageLink'=> 'jpegPhoto', // URL not image data
+ // Generated Windows Live ID Attributes
+ 'windowslive_user' => 'eduPersonPrincipalName', // uid @ windowslive.com
+ 'windowslive_targetedID' => 'eduPersonTargetedID', // http://windowslive.com!uid
+ 'windowslive_uid' => 'uid', // windows live id
+ 'windowslive_mail' => 'mail',
+ // Attributes Returned by Windows Live ID
+ 'windowslive.FirstName' => 'givenName',
+ 'windowslive.LastName' => 'sn',
+ 'windowslive.Location' => 'l',
);
diff --git a/bin/build-release.sh b/bin/build-release.sh
index f484d7d..0a478eb 100755
--- a/bin/build-release.sh
+++ b/bin/build-release.sh
@@ -43,7 +43,7 @@ if [ -f "$TARGET/composer.json" ]; then
php "$TARGET/composer.phar" install --no-dev --prefer-dist -o -d "$TARGET"
fi
-mkdir -p "$TARGET/config" "$TARGET/metadata"
+mkdir -p "$TARGET/config" "$TARGET/metadata" "$TARGET/cert" "$TARGET/log"
cp -rv "$TARGET/config-templates/"* "$TARGET/config/"
cp -rv "$TARGET/metadata-templates/"* "$TARGET/metadata/"
rm -rf "$TARGET/.git"
diff --git a/bin/importPdoMetadata.php b/bin/importPdoMetadata.php
new file mode 100755
index 0000000..d09dd70
--- /dev/null
+++ b/bin/importPdoMetadata.php
@@ -0,0 +1,27 @@
+<?php
+$baseDir = dirname(dirname(__FILE__));
+
+require_once $baseDir . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . '_autoload.php';
+require_once $baseDir . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'config.php';
+
+# Iterate through configured metadata sources and ensure
+# that a PDO source exists.
+foreach ($config['metadata.sources'] as $s) {
+ # If pdo is configured, create the new handler and add in the metadata sets.
+ if ($s['type'] === "pdo") {
+ $mdshp = new SimpleSAML_Metadata_MetaDataStorageHandlerPdo($s);
+ $mdshp->initDatabase();
+
+ foreach (glob("metadata/*.php") as $filename) {
+ $metadata = array();
+ require_once $filename;
+ $set = basename($filename, ".php");
+ echo "importing set '$set'..." . PHP_EOL;
+
+ foreach ($metadata as $k => $v) {
+ echo "\t$k" . PHP_EOL;
+ $mdshp->addEntry($k, $set, $v);
+ }
+ }
+ }
+}
diff --git a/bin/initMDSPdo.php b/bin/initMDSPdo.php
new file mode 100755
index 0000000..ff6d08e
--- /dev/null
+++ b/bin/initMDSPdo.php
@@ -0,0 +1,27 @@
+#!/usr/bin/env php
+<?php
+
+/* This is the base directory of the simpleSAMLphp installation. */
+$baseDir = dirname(dirname(__FILE__));
+
+/* Add library autoloader and configuration. */
+require_once $baseDir . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . '_autoload.php';
+require_once $baseDir . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'config.php';
+
+echo "Initializing Metadata Database...". PHP_EOL;
+
+# Iterate through configured metadata sources and ensure
+# that a PDO source exists.
+foreach ($config['metadata.sources'] as $source) {
+ # If pdo is configured, create the new handler and initialize the DB.
+ if ($source['type'] === "pdo") {
+ $metadataStorageHandler = new SimpleSAML_Metadata_MetaDataStorageHandlerPdo($source);
+ $result = $metadataStorageHandler->initDatabase();
+
+ if ($result === false) {
+ echo "Failed to intialize metadata database.". PHP_EOL;
+ } else {
+ echo "Successfully initialized metadata database.". PHP_EOL;
+ }
+ }
+}
diff --git a/composer.json b/composer.json
index e7f130c..7360322 100644
--- a/composer.json
+++ b/composer.json
@@ -24,7 +24,7 @@
"require": {
"php": ">=5.3",
"simplesamlphp/saml2": "~1.5.3",
- "robrichards/xmlseclibs": "~1.3.2",
+ "robrichards/xmlseclibs": "~1.4.1",
"whitehat101/apr1-md5": "~1.0"
},
"require-dev": {
diff --git a/composer.lock b/composer.lock
index 992d081..7f52c28 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "hash": "7890fcd74adbdb7443a92bfdbae345da",
+ "hash": "f83d5b28436a311ffa09a7b514228ca6",
"packages": [
{
"name": "psr/log",
@@ -46,26 +46,29 @@
},
{
"name": "robrichards/xmlseclibs",
- "version": "1.3.2",
+ "version": "1.4.1",
"source": {
"type": "git",
"url": "https://github.com/robrichards/xmlseclibs.git",
- "reference": "5e3c8a03990b9a97d1ea714bcba391d2f58e5b2d"
+ "reference": "465f18a8e1196c279b1298a3b08bcbee71ea4e4e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/robrichards/xmlseclibs/zipball/5e3c8a03990b9a97d1ea714bcba391d2f58e5b2d",
- "reference": "5e3c8a03990b9a97d1ea714bcba391d2f58e5b2d",
+ "url": "https://api.github.com/repos/robrichards/xmlseclibs/zipball/465f18a8e1196c279b1298a3b08bcbee71ea4e4e",
+ "reference": "465f18a8e1196c279b1298a3b08bcbee71ea4e4e",
"shasum": ""
},
+ "require": {
+ "php": ">= 5.2"
+ },
"suggest": {
"ext/mcrypt": "MCrypt extension",
"ext/openssl": "OpenSSL extension"
},
"type": "library",
"autoload": {
- "files": [
- "xmlseclibs.php"
+ "classmap": [
+ "src/"
]
},
"notification-url": "https://packagist.org/downloads/",
@@ -80,7 +83,7 @@
"xml",
"xmldsig"
],
- "time": "2015-05-21 19:26:03"
+ "time": "2015-07-31 12:22:14"
},
{
"name": "simplesamlphp/saml2",
@@ -336,16 +339,16 @@
},
{
"name": "phpunit/php-file-iterator",
- "version": "1.4.0",
+ "version": "1.4.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
- "reference": "a923bb15680d0089e2316f7a4af8f437046e96bb"
+ "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a923bb15680d0089e2316f7a4af8f437046e96bb",
- "reference": "a923bb15680d0089e2316f7a4af8f437046e96bb",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0",
+ "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0",
"shasum": ""
},
"require": {
@@ -379,20 +382,20 @@
"filesystem",
"iterator"
],
- "time": "2015-04-02 05:19:05"
+ "time": "2015-06-21 13:08:43"
},
{
"name": "phpunit/php-text-template",
- "version": "1.2.0",
+ "version": "1.2.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-text-template.git",
- "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a"
+ "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a",
- "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
+ "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
"shasum": ""
},
"require": {
@@ -401,20 +404,17 @@
"type": "library",
"autoload": {
"classmap": [
- "Text/"
+ "src/"
]
},
"notification-url": "https://packagist.org/downloads/",
- "include-path": [
- ""
- ],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
- "email": "sb@sebastian-bergmann.de",
+ "email": "sebastian@phpunit.de",
"role": "lead"
}
],
@@ -423,20 +423,20 @@
"keywords": [
"template"
],
- "time": "2014-01-30 17:20:04"
+ "time": "2015-06-21 13:50:34"
},
{
"name": "phpunit/php-timer",
- "version": "1.0.5",
+ "version": "1.0.7",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-timer.git",
- "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c"
+ "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c",
- "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3e82f4e9fc92665fafd9157568e4dcb01d014e5b",
+ "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b",
"shasum": ""
},
"require": {
@@ -445,13 +445,10 @@
"type": "library",
"autoload": {
"classmap": [
- "PHP/"
+ "src/"
]
},
"notification-url": "https://packagist.org/downloads/",
- "include-path": [
- ""
- ],
"license": [
"BSD-3-Clause"
],
@@ -467,7 +464,7 @@
"keywords": [
"timer"
],
- "time": "2013-08-02 07:42:54"
+ "time": "2015-06-21 08:01:12"
},
{
"name": "phpunit/php-token-stream",
@@ -718,21 +715,20 @@
},
{
"name": "symfony/config",
- "version": "v2.6.7",
- "target-dir": "Symfony/Component/Config",
+ "version": "v2.7.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/Config.git",
- "reference": "b6fddb4aa2daaa2b06f0040071ac131b4a1ecf25"
+ "reference": "6c905bbed1e728226de656e4c07d620dfe9e80d9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/Config/zipball/b6fddb4aa2daaa2b06f0040071ac131b4a1ecf25",
- "reference": "b6fddb4aa2daaa2b06f0040071ac131b4a1ecf25",
+ "url": "https://api.github.com/repos/symfony/Config/zipball/6c905bbed1e728226de656e4c07d620dfe9e80d9",
+ "reference": "6c905bbed1e728226de656e4c07d620dfe9e80d9",
"shasum": ""
},
"require": {
- "php": ">=5.3.3",
+ "php": ">=5.3.9",
"symfony/filesystem": "~2.3"
},
"require-dev": {
@@ -741,11 +737,11 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.6-dev"
+ "dev-master": "2.7-dev"
}
},
"autoload": {
- "psr-0": {
+ "psr-4": {
"Symfony\\Component\\Config\\": ""
}
},
@@ -765,25 +761,24 @@
],
"description": "Symfony Config Component",
"homepage": "https://symfony.com",
- "time": "2015-05-02 15:18:45"
+ "time": "2015-07-09 16:07:40"
},
{
"name": "symfony/console",
- "version": "v2.6.7",
- "target-dir": "Symfony/Component/Console",
+ "version": "v2.7.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/Console.git",
- "reference": "ebc5679854aa24ed7d65062e9e3ab0b18a917272"
+ "reference": "d6cf02fe73634c96677e428f840704bfbcaec29e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/Console/zipball/ebc5679854aa24ed7d65062e9e3ab0b18a917272",
- "reference": "ebc5679854aa24ed7d65062e9e3ab0b18a917272",
+ "url": "https://api.github.com/repos/symfony/Console/zipball/d6cf02fe73634c96677e428f840704bfbcaec29e",
+ "reference": "d6cf02fe73634c96677e428f840704bfbcaec29e",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=5.3.9"
},
"require-dev": {
"psr/log": "~1.0",
@@ -799,11 +794,11 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.6-dev"
+ "dev-master": "2.7-dev"
}
},
"autoload": {
- "psr-0": {
+ "psr-4": {
"Symfony\\Component\\Console\\": ""
}
},
@@ -823,25 +818,24 @@
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
- "time": "2015-05-02 15:18:45"
+ "time": "2015-07-28 15:18:12"
},
{
"name": "symfony/event-dispatcher",
- "version": "v2.6.7",
- "target-dir": "Symfony/Component/EventDispatcher",
+ "version": "v2.7.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/EventDispatcher.git",
- "reference": "672593bc4b0043a0acf91903bb75a1c82d8f2e02"
+ "reference": "9310b5f9a87ec2ea75d20fec0b0017c77c66dac3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/672593bc4b0043a0acf91903bb75a1c82d8f2e02",
- "reference": "672593bc4b0043a0acf91903bb75a1c82d8f2e02",
+ "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/9310b5f9a87ec2ea75d20fec0b0017c77c66dac3",
+ "reference": "9310b5f9a87ec2ea75d20fec0b0017c77c66dac3",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=5.3.9"
},
"require-dev": {
"psr/log": "~1.0",
@@ -858,11 +852,11 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.6-dev"
+ "dev-master": "2.7-dev"
}
},
"autoload": {
- "psr-0": {
+ "psr-4": {
"Symfony\\Component\\EventDispatcher\\": ""
}
},
@@ -882,25 +876,24 @@
],
"description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com",
- "time": "2015-05-02 15:18:45"
+ "time": "2015-06-18 19:21:56"
},
{
"name": "symfony/filesystem",
- "version": "v2.6.7",
- "target-dir": "Symfony/Component/Filesystem",
+ "version": "v2.7.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/Filesystem.git",
- "reference": "f73904bd2dae525c42ea1f0340c7c98480ecacde"
+ "reference": "2d7b2ddaf3f548f4292df49a99d19c853d43f0b8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/Filesystem/zipball/f73904bd2dae525c42ea1f0340c7c98480ecacde",
- "reference": "f73904bd2dae525c42ea1f0340c7c98480ecacde",
+ "url": "https://api.github.com/repos/symfony/Filesystem/zipball/2d7b2ddaf3f548f4292df49a99d19c853d43f0b8",
+ "reference": "2d7b2ddaf3f548f4292df49a99d19c853d43f0b8",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=5.3.9"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.7"
@@ -908,11 +901,11 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.6-dev"
+ "dev-master": "2.7-dev"
}
},
"autoload": {
- "psr-0": {
+ "psr-4": {
"Symfony\\Component\\Filesystem\\": ""
}
},
@@ -932,25 +925,24 @@
],
"description": "Symfony Filesystem Component",
"homepage": "https://symfony.com",
- "time": "2015-05-08 00:09:07"
+ "time": "2015-07-09 16:07:40"
},
{
"name": "symfony/stopwatch",
- "version": "v2.6.7",
- "target-dir": "Symfony/Component/Stopwatch",
+ "version": "v2.7.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/Stopwatch.git",
- "reference": "b470f87c69837cb71115f1fa720388bb19b63635"
+ "reference": "b07a866719bbac5294c67773340f97b871733310"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/Stopwatch/zipball/b470f87c69837cb71115f1fa720388bb19b63635",
- "reference": "b470f87c69837cb71115f1fa720388bb19b63635",
+ "url": "https://api.github.com/repos/symfony/Stopwatch/zipball/b07a866719bbac5294c67773340f97b871733310",
+ "reference": "b07a866719bbac5294c67773340f97b871733310",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=5.3.9"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.7"
@@ -958,11 +950,11 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.6-dev"
+ "dev-master": "2.7-dev"
}
},
"autoload": {
- "psr-0": {
+ "psr-4": {
"Symfony\\Component\\Stopwatch\\": ""
}
},
@@ -982,25 +974,24 @@
],
"description": "Symfony Stopwatch Component",
"homepage": "https://symfony.com",
- "time": "2015-05-02 15:18:45"
+ "time": "2015-07-01 18:23:16"
},
{
"name": "symfony/yaml",
- "version": "v2.6.7",
- "target-dir": "Symfony/Component/Yaml",
+ "version": "v2.7.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/Yaml.git",
- "reference": "f157ab074e453ecd4c0fa775f721f6e67a99d9e2"
+ "reference": "71340e996171474a53f3d29111d046be4ad8a0ff"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/Yaml/zipball/f157ab074e453ecd4c0fa775f721f6e67a99d9e2",
- "reference": "f157ab074e453ecd4c0fa775f721f6e67a99d9e2",
+ "url": "https://api.github.com/repos/symfony/Yaml/zipball/71340e996171474a53f3d29111d046be4ad8a0ff",
+ "reference": "71340e996171474a53f3d29111d046be4ad8a0ff",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=5.3.9"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.7"
@@ -1008,11 +999,11 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.6-dev"
+ "dev-master": "2.7-dev"
}
},
"autoload": {
- "psr-0": {
+ "psr-4": {
"Symfony\\Component\\Yaml\\": ""
}
},
@@ -1032,7 +1023,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
- "time": "2015-05-02 15:18:45"
+ "time": "2015-07-28 14:07:07"
}
],
"aliases": [],
diff --git a/config-templates/config.php b/config-templates/config.php
index b45616b..7a8db7f 100644
--- a/config-templates/config.php
+++ b/config-templates/config.php
@@ -114,7 +114,7 @@ $config = array(
* SimpleSAML_Logger::WARNING No statistics, only warnings/errors
* SimpleSAML_Logger::NOTICE Statistics and errors
* SimpleSAML_Logger::INFO Verbose logs
- * SimpleSAML_Logger::DEBUG Full debug logs - not reccomended for production
+ * SimpleSAML_Logger::DEBUG Full debug logs - not recommended for production
*
* Choose logging handler.
*
@@ -197,6 +197,61 @@ $config = array(
),
+
+ /*
+ * Database
+ *
+ * This database configuration is optional. If you are not using
+ * core functionality or modules that require a database, you can
+ * skip this configuration.
+ */
+
+ /*
+ * Database connection string.
+ * Ensure that you have the required PDO database driver installed
+ * for your connection string.
+ */
+ 'database.dsn' => 'mysql:host=localhost;dbname=saml',
+
+ /*
+ * SQL database credentials
+ */
+ 'database.username' => 'simplesamlphp',
+ 'database.password' => 'secret',
+
+ /*
+ * (Optional) Table prefix
+ */
+ 'database.prefix' => '',
+
+ /*
+ * True or false if you would like a persistent database connection
+ */
+ 'database.persistent' => false,
+
+ /*
+ * Database slave configuration is optional as well. If you are only
+ * running a single database server, leave this blank. If you have
+ * a master/slave configuration, you can define as many slave servers
+ * as you want here. Slaves will be picked at random to be queried from.
+ *
+ * Configuration options in the slave array are exactly the same as the
+ * options for the master (shown above) with the exception of the table
+ * prefix.
+ */
+ 'database.slaves' => array(
+ /*
+ array(
+ 'dsn' => 'mysql:host=myslave;dbname=saml',
+ 'username' => 'simplesamlphp',
+ 'password' => 'secret',
+ 'persistent' => false,
+ ),
+ */
+ ),
+
+
+
/*
* Enable
*
@@ -571,6 +626,15 @@ $config = array(
* - 'cachelength': Maximum time metadata cah be cached, in seconds. Default to 24
* hours (86400 seconds). Optional.
*
+ * PDO metadata handler:
+ * This metadata handler looks up metadata of an entity stored in a database.
+ *
+ * Note: If you are using the PDO metadata handler, you must configure the database
+ * options in this configuration file.
+ *
+ * The PDO metadata handler defines the following options:
+ * - 'type': This is always 'pdo'.
+ *
*
* Examples:
*
@@ -593,6 +657,11 @@ $config = array(
* array('type' => 'mdx', server => 'http://mdx.server.com:8080', 'cachedir' => '/var/simplesamlphp/mdx-cache', 'cachelength' => 86400)
* ),
*
+ * This example defines an pdo source.
+ * 'metadata.sources' => array(
+ * array('type' => 'pdo')
+ * ),
+ *
* Default:
* 'metadata.sources' => array(
* array('type' => 'flatfile')
diff --git a/docs/simplesamlphp-automated_metadata.txt b/docs/simplesamlphp-automated_metadata.txt
index bd8833d..8c3a5c4 100644
--- a/docs/simplesamlphp-automated_metadata.txt
+++ b/docs/simplesamlphp-automated_metadata.txt
@@ -132,6 +132,18 @@ The metarefresh module supports the following configuration options:
: The format of the generated metadata files. This must match the
metadata source added in `config.php`.
+`types`
+: The sets of entities to load. An array containing strings identifying the different types of entities that will be
+ loaded. Valid types are:
+
+ * saml20-idp-remote
+ * saml20-sp-remote
+ * shib13-idp-remote
+ * shib13-sp-remote
+ * attributeauthority-remote
+
+ All entity types will be loaded by default.
+
Each metadata source has the following options:
`src`
@@ -154,6 +166,10 @@ Each metadata source has the following options:
: This is an array which will be combined with the metadata fetched to
generate the final metadata array.
+`types`
+: Same as the option with the same name at the metadata set level. This option has precedence when both are specified,
+ allowing a more fine grained configuration for every metadata source.
+
After you have configured the metadata sources, you need to give the
web-server write access to the output directories. Following the previous example:
diff --git a/docs/simplesamlphp-database.txt b/docs/simplesamlphp-database.txt
new file mode 100644
index 0000000..663fdce
--- /dev/null
+++ b/docs/simplesamlphp-database.txt
@@ -0,0 +1,91 @@
+SimpleSAML\Database
+=============================
+
+<!--
+ This file is written in Markdown syntax.
+ For more information about how to use the Markdown syntax, read here:
+ http://daringfireball.net/projects/markdown/syntax
+-->
+
+
+<!-- {{TOC}} -->
+
+Purpose
+-------
+This document covers the SimpleSAML\Database class and is only relevant to anyone writing code for SimpleSAMLphp, including modules, that require a database connection.
+
+The Database class provides a single class that can be used to connect to a database which can be shared by anything within SimpleSAMLphp.
+
+Getting Started
+---------------
+If you are just using the already configured database, which would normally be the case, all you need to do is get the global instance of the Database class.
+
+ $db = SimpleSAML\Database::getInstance();
+
+If there is a requirement to connect to an alternate database server (ex. authenticating users that exist on a different SQL server or database) you can specify an alternate configuration.
+
+ $config = new SimpleSAML_Configuration($myconfigarray, "mymodule/lib/Auth/Source/myauth.php");
+ $db = SimpleSAML\Database::getInstance($config);
+
+That will create a new instance of the database, separate from the global instance, specific to the configuration defined in $myconfigarray. If you are going to specify an alternate config, your configuration array must contain the same keys that exist in the master config (database.dsn, database.username, database.password, database.prefix, etc).
+
+Database Prefix
+---------------
+Administrators can add a prefix to all the table names that this database classes accesses and you should take that in account when querying. Assuming that a prefix has been configured as "sp_":
+
+ $table = $db->applyPrefix("saml20_idp_hosted");
+
+$table would be set to "sp_saml20_idp_hosted"
+
+Querying The Database
+---------------------
+You can query the database through two public functions read() and write() which are fairly self-explanitory when it comes to determining which one to use when querying.
+
+### Writing to The Database
+Since the database class allows administrators to configure master and slave database servers, the write function will always use the master database connection.
+
+The write function takes 2 parameters: SQL, params.
+
+ $table = $db->applyPrefix("test");
+ $values = array(
+ 'id' => 20,
+ 'data' => 'Some data',
+ );
+
+ $query = $db->write("INSERT INTO $table (id, data) VALUES (:id, :data)", $values);
+
+The values specified in the $values array will be bound to the placeholders and will be executed on the master. By default, values are binded as PDO::PARAM_STR. If you need to override this, you can specify it in the values array.
+
+ $table = $db->applyPrefix("test");
+ $values = array(
+ 'id' => array(20, PDO::PARAM_INT),
+ 'data' => 'Some data',
+ );
+
+ $query = $db->write("INSERT INTO $table (id, data) VALUES (:id, :data)", $values);
+
+You can also skip usage of prepared statements. You should **only** use this if you have a statement that has no user input (ex. CREATE TABLE). If the params variable is explicity set to false, it will skip usage of prepared statements. This is only available when writing to the database.
+
+ $table = $db->applyPrefix("test");
+ $query = $db->write("CREATE TABLE IF NOT EXISTS $table (id INT(16) NOT NULL, data TEXT NOT NULL)", false);
+
+### Reading The Database
+Since the database class allows administrators to configure master and slave database servers, the read function will randomly select a slave server to query. If no slaves are configured, it will read from the master.
+
+The read function takes 2 parameters: SQL, params.
+
+ $table = $db->applyPrefix("test");
+ $values = array(
+ 'id' => 20,
+ );
+
+ $query = $db->read("SELECT * FROM $table WHERE id = :id", $values);
+
+The values specified in the $values array will be bound to the placeholders and will be executed on the selected slave. By default, values are binded as PDO::PARAM_STR. If you need to override this, you can specify it in the values array.
+
+ $table = $db->applyPrefix("test");
+ $values = array(
+ 'id' => array(20, PDO::PARAM_INT),
+ );
+
+ $query = $db->read("SELECT * FROM $table WHERE id = :id", $values);
diff --git a/docs/simplesamlphp-install.txt b/docs/simplesamlphp-install.txt
index 76a5fb4..11f6c23 100644
--- a/docs/simplesamlphp-install.txt
+++ b/docs/simplesamlphp-install.txt
@@ -1,4 +1,4 @@
-simpleSAMLphp Installation and Configuration
+SimpleSAMLphp Installation and Configuration
============================================
<!--
@@ -11,21 +11,21 @@ simpleSAMLphp Installation and Configuration
<!-- {{TOC}} -->
-simpleSAMLphp news and documentation
+SimpleSAMLphp news and documentation
------------------------------------
-This document is part of the simpleSAMLphp documentation suite.
+This document is part of the SimpleSAMLphp documentation suite.
- * [List of all simpleSAMLphp documentation](http://simplesamlphp.org/docs)
- * [Latest news about simpleSAMLphp](http://rnd.feide.no/taxonomy/term/4). (Also contains an RSS feed)
- * [simpleSAMLphp homepage](https://simplesamlphp.org)
+ * [List of all SimpleSAMLphp documentation](http://simplesamlphp.org/docs)
+ * [Latest news about SimpleSAMLphp](http://rnd.feide.no/taxonomy/term/4). (Also contains an RSS feed)
+ * [SimpleSAMLphp homepage](https://simplesamlphp.org)
Development version
--------------------
-This document is about the latest stable version of simpleSAMLphp.
-If you want to install the development version, look at the instructions for [installing simpleSAMLphp from the repository](simplesamlphp-install-repo).
+This document is about the latest stable version of SimpleSAMLphp.
+If you want to install the development version, look at the instructions for [installing SimpleSAMLphp from the repository](simplesamlphp-install-repo).
Prerequisites
@@ -34,7 +34,8 @@ Prerequisites
* Some webserver capable of executing PHP scripts.
* PHP version >= 5.3.0.
* Support for the following PHP extensions:
- * Always required: `date`, `dom`, `hash`, `libxml`, `openssl`, `pcre`, `SPL`, `zlib`, `mcrypt`
+ * Always required: `date`, `dom`, `hash`, `libxml`, `openssl`, `pcre`, `SPL`, `zlib`
+ * When using encryption or digital signatures: `mcrypt`
* When authenticating against LDAP server: `ldap`
* When authenticating against RADIUS server: `radius`
* When saving session information to memcache-server: `memcache`
@@ -45,25 +46,26 @@ Prerequisites
What actual packages are required for the various extensions varies between different platforms and distributions.
-Download and install simpleSAMLphp
+Download and install SimpleSAMLphp
----------------------------------
-The most recent release of simpleSAMLphp is found at [https://simplesamlphp.org/download](https://simplesamlphp.org/download).
+The most recent release of SimpleSAMLphp is found at [https://simplesamlphp.org/download](https://simplesamlphp.org/download).
-Go to the directory where you want to install simpleSAMLphp, and extract the archive file you just downloaded:
+Go to the directory where you want to install SimpleSAMLphp, and extract the archive file you just downloaded:
cd /var
tar xzf simplesamlphp-1.x.y.tar.gz
mv simplesamlphp-1.x.y simplesamlphp
-## Upgrading from a previous version of simpleSAMLphp
+## Upgrading from a previous version of SimpleSAMLphp
Extract the new version:
cd /var
tar xzf simplesamlphp-1.x.y.tar.gz
-Copy the configuration files from the previous version:
+Copy the configuration files from the previous version (in case the configuration directory is inside SimpleSAMLphp,
+keep reading for other alternatives):
cd /var/simplesamlphp-1.x.y
rm -rf config metadata
@@ -77,7 +79,7 @@ Replace the old version with the new version:
mv simplesamlphp-1.x.y simplesamlphp
-If the format of the config files or metadata has changed from your previous version of simpleSAMLphp (check the revision log), you may have to update your configuration and metadata after updating the simpleSAMLphp code:
+If the format of the config files or metadata has changed from your previous version of SimpleSAMLphp (check the revision log), you may have to update your configuration and metadata after updating the SimpleSAMLphp code:
### Upgrading configuration files
@@ -90,42 +92,62 @@ This will ensure that all new entries in the latest version of config.php are in
Most likely the metadata format is backwards compatible. If not, you should receive a very clear error message at startup indicating how and what you need to update. You should look through the metadata in the metadata-templates directory after the upgrade to see whether recommended defaults have been changed.
+### Alternative location for configuration files
+
+By default, SimpleSAMLphp looks for its configuration in the `config` directory in the root of its own directory. This
+has some drawbacks, like making it harder to use SimpleSAMLphp as a composer dependency, or to package it for different
+operating systems.
+
+However, it is now possible to specify an alternate location for the configuration directory by setting an environment
+variable with this location. This way, the configuration directory doesn't need to be inside the library's directory,
+making it easier to manage and to update. The simplest way to set this environment variable is to set it in your web
+server's configuration. See the next section for more information.
+
Configuring Apache
------------------
-Examples below assume that simpleSAMLphp is installed in the default location, `/var/simplesamlphp`. You may choose another location, but this requires a path update in a few files. See Appendix for details ‹Installing simpleSAMLphp in alternative locations›.
+Examples below assume that SimpleSAMLphp is installed in the default location, `/var/simplesamlphp`. You may choose another location, but this requires a path update in a few files. See Appendix for details ‹Installing SimpleSAMLphp in alternative locations›.
-The only subdirectories of `simpleSAMLphp` that needs to be accessible from the web is `www`. There are several ways of putting the simpleSAMLphp depending on the way web sites are structured on your apache web server. Here is what I believe is the best configuration.
+The only subdirectories of `SimpleSAMLphp` that needs to be accessible from the web is `www`. There are several ways of putting the SimpleSAMLphp depending on the way web sites are structured on your apache web server. Here is what I believe is the best configuration.
-Find the Apache configuration file for the virtual hosts where you want to run simpleSAMLphp. The configuration may look like this:
+Find the Apache configuration file for the virtual hosts where you want to run SimpleSAMLphp. The configuration may look like this:
<VirtualHost *>
ServerName service.example.com
DocumentRoot /var/www/service.example.com
-
+
+ SetEnv SIMPLESAMLPHP_CONFIG_DIR /var/simplesamlphp/config
+
Alias /simplesaml /var/simplesamlphp/www
</VirtualHost>
-Note the `Alias` directive, which gives control to simpleSAMLphp for all urls matching `http(s)://service.example.com/simplesaml/*`. simpleSAMLphp makes several SAML interfaces available on the web; all of them are included in the `www` subdirectory of your simpleSAMLphp installation. You can name the alias whatever you want, but the name must be specified in the `config.php` file of simpleSAML as described in [the section called “simpleSAMLphp configuration: config.php”](#sect.config "simpleSAMLphp configuration: config.php"). Here is an example of how this configuration may look like in `config.php`:
+Note the `Alias` directive, which gives control to SimpleSAMLphp for all urls matching `http(s)://service.example.com/simplesaml/*`. SimpleSAMLphp makes several SAML interfaces available on the web; all of them are included in the `www` subdirectory of your SimpleSAMLphp installation. You can name the alias whatever you want, but the name must be specified in the `config.php` file of simpleSAML as described in [the section called “SimpleSAMLphp configuration: config.php”](#sect.config "SimpleSAMLphp configuration: config.php"). Here is an example of how this configuration may look like in `config.php`:
$config = array (
[...]
'baseurlpath' => 'simplesaml/',
-simpleSAMLphp configuration: config.php
+Note also the `SetEnv` directive. It sets the `SIMPLESAMLPHP_CONFIG_DIR` environment variable, in this case, to the
+default location for the configuration directory. You can always omit this environment variable, and SimpleSAMLphp will
+then look for the `config` directory inside its own directory. If you need to move your configuration to a different
+location, you can use this environment variable to tell SimpleSAMLphp where to look for configuration files. Remember
+this works only for the `config` directory. If you need your metadata to be in a different directory too, use the
+`metadatadir` configuration option to specify the location.
+
+SimpleSAMLphp configuration: config.php
---------------------------------------
There is a few steps that you should edit in the main configuration
file, `config.php`, right away:
-- Set a administrator password. This is needed to access some of the pages in your simpleSAMLphp installation web interface.
+- Set a administrator password. This is needed to access some of the pages in your SimpleSAMLphp installation web interface.
'auth.adminpassword' => 'setnewpasswordhere',
Hashed passwords can also be used here. See the [`authcrypt`](./authcrypt:authcrypt) documentation for more information.
-- Set a secret salt. This should be a random string. Some parts of the simpleSAMLphp needs this salt to generate cryptographically secure hashes. SimpleSAMLphp will give an error if the salt is not changed from the default value. The command below can help you to generated a random string on (some) unix systems:
+- Set a secret salt. This should be a random string. Some parts of the SimpleSAMLphp needs this salt to generate cryptographically secure hashes. SimpleSAMLphp will give an error if the salt is not changed from the default value. The command below can help you to generated a random string on (some) unix systems:
tr -c -d '0123456789abcdefghijklmnopqrstuvwxyz' </dev/urandom | dd bs=32 count=1 2>/dev/null;echo
@@ -137,13 +159,13 @@ file, `config.php`, right away:
Set technical contact information. This information will be
available in the generated metadata. The e-mail address will also
be used for receiving error reports sent automatically by
- simpleSAMLphp. Here is an example:
+ SimpleSAMLphp. Here is an example:
'technicalcontact_name' => 'Andreas Åkre Solberg',
'technicalcontact_email' => 'andreas.solberg@uninett.no',
-
- If you use simpleSAMLphp in a country where English is not
+ If you use SimpleSAMLphp in a country where English is not
widespread, you may want to change the default language from
English to something else:
@@ -162,13 +184,13 @@ Configuring PHP
### Sending e-mails from PHP
-Some parts of simpleSAMLphp will allow you to send e-mails. In example sending error reports to technical admin, as well as sending in metadata to the federation administrators. If you want to make use of this functionality, you should make sure your PHP installation is configured to be able to send e-mails. It's a common problem that PHP is not configured to send e-mails properly. The configuration differs from system to system. On UNIX, PHP is using sendmail, on Windows SMTP.
+Some parts of SimpleSAMLphp will allow you to send e-mails. In example sending error reports to technical admin, as well as sending in metadata to the federation administrators. If you want to make use of this functionality, you should make sure your PHP installation is configured to be able to send e-mails. It's a common problem that PHP is not configured to send e-mails properly. The configuration differs from system to system. On UNIX, PHP is using sendmail, on Windows SMTP.
Enable modules
--------------
-If you want to enable some of the modules that are installed with simpleSAMLphp, but are disabled by default, you should create an empty file in the module directory named `enable`.
+If you want to enable some of the modules that are installed with SimpleSAMLphp, but are disabled by default, you should create an empty file in the module directory named `enable`.
# Enabling the consent module
cd modules
@@ -184,10 +206,10 @@ to `disable`.
-The simpleSAMLphp installation webpage
+The SimpleSAMLphp installation webpage
--------------------------------------
-After installing simpleSAMLphp, you can access the homepage of your installation, which contains some information and a few links to the test services. The URL of an installation can be e.g.:
+After installing SimpleSAMLphp, you can access the homepage of your installation, which contains some information and a few links to the test services. The URL of an installation can be e.g.:
https://service.example.org/simplesaml/
@@ -196,13 +218,13 @@ The exact link depends on how you set it up with Apache, and of course on your h
### Warning
Don't click on any of the links yet, because they require you to
-either have setup simpleSAMLphp as an Service Provider or as an
+either have setup SimpleSAMLphp as an Service Provider or as an
Identity Provider.
-Here is an example screenshot of what the simpleSAMLphp page looks
+Here is an example screenshot of what the SimpleSAMLphp page looks
like:
-![Screenshot of the simpleSAMLphp installation page.](http://rnd.feide.no/doc/resources/simplesamlphp-install/screenshot-installationpage.png)
+![Screenshot of the SimpleSAMLphp installation page.](http://rnd.feide.no/doc/resources/simplesamlphp-install/screenshot-installationpage.png)
### Check your PHP environment
@@ -210,9 +232,9 @@ At the bottom of the installation page are some green lights. simpleSAML runs so
## Next steps
-You have now successfully installed simpleSAMLphp, and the next steps depends on whether you want to setup a service provider, to protect a website by authentication or if you want to setup an identity provider and connect it to a user catalog. Documentation on bridging between federation protocols is found in a separate document.
+You have now successfully installed SimpleSAMLphp, and the next steps depends on whether you want to setup a service provider, to protect a website by authentication or if you want to setup an identity provider and connect it to a user catalog. Documentation on bridging between federation protocols is found in a separate document.
- * [Using simpleSAMLphp as a SAML Service Provider](simplesamlphp-sp)
+ * [Using SimpleSAMLphp as a SAML Service Provider](simplesamlphp-sp)
* [Hosted SP Configuration Reference](./saml:sp)
* [IdP remote reference](simplesamlphp-reference-idp-remote)
* [Connecting SimpleSAMLphp as a SP to UK Access Federation or InCommon](simplesamlphp-ukaccess)
@@ -228,24 +250,24 @@ You have now successfully installed simpleSAMLphp, and the next steps depends on
Support
-------
-If you need help to make this work, or want to discuss simpleSAMLphp with other users of the software, you are fortunate: Around simpleSAMLphp there is a great Open source community, and you are welcome to join! The forums are open for you to ask questions, contribute answers other further questions, request improvements or contribute with code or plugins of your own.
+If you need help to make this work, or want to discuss SimpleSAMLphp with other users of the software, you are fortunate: Around SimpleSAMLphp there is a great Open source community, and you are welcome to join! The forums are open for you to ask questions, contribute answers other further questions, request improvements or contribute with code or plugins of your own.
-- [simpleSAMLphp homepage](https://simplesamlphp.org)
-- [List of all available simpleSAMLphp documentation](https://simplesamlphp.org/docs/)
-- [Join the simpleSAMLphp user's mailing list](https://simplesamlphp.org/lists)
+- [SimpleSAMLphp homepage](https://simplesamlphp.org)
+- [List of all available SimpleSAMLphp documentation](https://simplesamlphp.org/docs/)
+- [Join the SimpleSAMLphp user's mailing list](https://simplesamlphp.org/lists)
-Installing simpleSAMLphp in alternative locations
+Installing SimpleSAMLphp in alternative locations
-------------------------------------------------
-There may be several reasons why you want to install simpleSAMLphp
+There may be several reasons why you want to install SimpleSAMLphp
in an alternative way.
-1. You are installing simpleSAMLphp in a hosted environment where you
+1. You are installing SimpleSAMLphp in a hosted environment where you
do not have root access, and cannot change Apache configuration.
- Still you can install simpleSAMLphp - keep on reading.
+ Still you can install SimpleSAMLphp - keep on reading.
2. You have full permissions to the server, but cannot edit Apache
configuration for some reason, politics, policy or whatever.
@@ -253,9 +275,9 @@ in an alternative way.
The SimpleSAMLphp code contains one folder named `simplesamlphp`. In this folder there are a lot of subfolders for library, metadata, configuration and much more. One of these folders is named `www`. This and *only this* folder should be exposed on the web. The recommended configuration is to put the whole `simplesamlphp` folder outside the webroot, and then link in the `www` folder by using the `Alias` directive, as described in [the section called “Configuring Apache”](#sect.apacheconfig "Configuring Apache"). But this is not the only possible way.
-As an example, let's see how you can install simpleSAMLphp in your home directory on a shared hosting server.
+As an example, let's see how you can install SimpleSAMLphp in your home directory on a shared hosting server.
-Extract the simpleSAMLphp archive in your home directory:
+Extract the SimpleSAMLphp archive in your home directory:
cd ~
tar xzf simplesamlphp-1.x.y.tar.gz
@@ -318,4 +340,4 @@ to:
### Note
-In a future version of simpleSAMLphp we'll make this a bit easier, and let you only change the path one place, instead of three as described above.
+In a future version of SimpleSAMLphp we'll make this a bit easier, and let you only change the path one place, instead of three as described above.
diff --git a/docs/simplesamlphp-metadata-pdostoragehandler.txt b/docs/simplesamlphp-metadata-pdostoragehandler.txt
new file mode 100644
index 0000000..82656fb
--- /dev/null
+++ b/docs/simplesamlphp-metadata-pdostoragehandler.txt
@@ -0,0 +1,79 @@
+PDO Metadata Storage Handler
+=============================
+
+<!--
+ This file is written in Markdown syntax.
+ For more information about how to use the Markdown syntax, read here:
+ http://daringfireball.net/projects/markdown/syntax
+-->
+
+
+<!-- {{TOC}} -->
+
+Introduction
+------------
+
+If you want to run a clustered SimpleSAMLphp IdP service and you would like to have centralized storage for metadata, you can use the PDO metadata storage handler.
+
+The present document explains how to configure SimpleSAMLphp and your database.
+
+
+
+Preparations
+------------
+
+You will need to have the appropriate PDO drivers for your database and you will have to configure the database section within the config/config.php file.
+
+
+
+Configuring SimpleSAMLphp
+-----------------------------
+
+You will first need to configure a PDO metadata source.
+
+ [root@simplesamlphp simplesamlphp]# vi config/config.php
+
+Here is an example of flatfile plus PDO:
+
+ 'metadata.sources' => array(
+ array('type' => 'flatfile'),
+ array('type' => 'pdo'),
+ ),
+
+
+
+Initializing the Database
+-------------------------
+
+
+Once you have configured your metadata sources to include a PDO source, you will need to initialize the database. This process will create tables in the database for each type of metadata set (saml20-idp-hosted, saml20-idp-remote, saml20-sp-remote, etc).
+
+ [root@simplesamlphp simplesamlphp]# php bin/initMDSPdo.php
+
+If you connect to your database, you will see 11 new empty tables; one for each metadata set.
+
+
+Adding Metadata
+---------------
+
+With the PDO metadata storage handler, metadata is stored in the table for the appropriate set and is stored in JSON format.
+
+As an example, here is the saml20_idp_hosted table:
+
+entity_id | entity_data
+----------------|-------------------------------------------------------------------------------------------------------------------------
+`__DEFAULT:1__` | {"host":"\_\_DEFAULT\_\_","privatekey":"idp.key","certificate":"idp.crt","auth":"example-ldap","userid.attribute":"uid"}
+
+Another example is the saml20_idp_remote table:
+
+entity_id | entity_data
+-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+https://openidp.feide.no | {"name":{"en":"Feide OpenIdP - guest users","no":"Feide Gjestebrukere"},"description":"Here you can login with your account on Feide RnD OpenID. If you do not already have an account on this identity provider, you can create a new one by following the create new account link and follow the instructions.","SingleSignOnService":"https:\/\/openidp.feide.no\/simplesaml\/saml2\/idp\/SSOService.php","SingleLogoutService":"https:\/\/openidp.feide.no\/simplesaml\/saml2\/idp\/SingleLogoutService.php","certFingerprint":"c9ed4dfb07caf13fc21e0fec1572047eb8a7a4cb"}
+
+There is an included script in the `bin` directory that will import all flatfile metadata files and store them in the database, but you can use an external tool to maintain the metadata in the database. This document will only cover adding metadata using the included utility, but the tables above should provide enough information if you would like to create a utility to manage your metadata externally.
+
+To import all flatfile metadata files into the PDO database, run the following script
+
+ [root@simplesamlphp simplesamlphp]# php bin/importPdoMetadata.php
+
+In the event that you import a metadata for an entity id that already exists in the database, it will be overwritten.
diff --git a/docs/simplesamlphp-reference-idp-remote.txt b/docs/simplesamlphp-reference-idp-remote.txt
index 0ecf9e0..078b3ce 100644
--- a/docs/simplesamlphp-reference-idp-remote.txt
+++ b/docs/simplesamlphp-reference-idp-remote.txt
@@ -111,6 +111,9 @@ The following SAML 2.0 options are available:
discouraged to do so. For your own safety, please include the string 'http://www.w3.org/2001/04/xmlenc#rsa-1_5' if
you make use of this option.
+`hide.from.discovery`
+: Whether to hide hide this IdP from the local discovery or not. Set to true to hide it. Defaults to false.
+
`nameid.encryption`
: Whether NameIDs sent to this IdP should be encrypted. The default
value is `FALSE`.
diff --git a/lib/SimpleSAML/Auth/Source.php b/lib/SimpleSAML/Auth/Source.php
index fb2b5a1..4f071fa 100644
--- a/lib/SimpleSAML/Auth/Source.php
+++ b/lib/SimpleSAML/Auth/Source.php
@@ -1,352 +1,387 @@
<?php
+
/**
* This class defines a base class for authentication source.
*
* An authentication source is any system which somehow authenticate the user.
*
* @author Olav Morken, UNINETT AS.
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-abstract class SimpleSAML_Auth_Source {
-
-
- /**
- * The authentication source identifier.
- *
- * This identifier can be used to look up this object, for example when returning from a login form.
- */
- protected $authId;
-
-
- /**
- * Constructor for an authentication source.
- *
- * Any authentication source which implements its own constructor must call this
- * constructor first.
- *
- * @param array $info Information about this authentication source.
- * @param array &$config Configuration for this authentication source.
- */
- public function __construct($info, &$config) {
- assert('is_array($info)');
- assert('is_array($config)');
-
- assert('array_key_exists("AuthId", $info)');
- $this->authId = $info['AuthId'];
- }
-
-
- /**
- * Get sources of a specific type.
- *
- * @param string $type The type of the authentication source.
- * @return array Array of SimpleSAML_Auth_Source objects of the specified type.
- */
- public static function getSourcesOfType($type) {
- assert('is_string($type)');
-
- $config = SimpleSAML_Configuration::getConfig('authsources.php');
-
- $ret = array();
-
- $sources = $config->getOptions();
- foreach ($sources as $id) {
- $source = $config->getArray($id);
-
- if (!array_key_exists(0, $source) || !is_string($source[0])) {
- throw new Exception('Invalid authentication source \'' . $id .
- '\': First element must be a string which identifies the authentication source.');
- }
-
- if ($source[0] !== $type) {
- continue;
- }
-
- $ret[] = self::parseAuthSource($id, $source);
- }
-
- return $ret;
- }
-
-
- /**
- * Retrieve the ID of this authentication source.
- *
- * @return string The ID of this authentication source.
- */
- public function getAuthId() {
-
- return $this->authId;
- }
-
-
- /**
- * Process a request.
- *
- * If an authentication source returns from this function, it is assumed to have
- * authenticated the user, and should have set elements in $state with the attributes
- * of the user.
- *
- * If the authentication process requires additional steps which make it impossible to
- * complete before returning from this function, the authentication source should
- * save the state, and at a later stage, load the state, update it with the authentication
- * information about the user, and call completeAuth with the state array.
- *
- * @param array &$state Information about the current authentication.
- */
- abstract public function authenticate(&$state);
-
-
- /**
- * Reauthenticate an user.
- *
- * This function is called by the IdP to give the authentication source a chance to
- * interact with the user even in the case when the user is already authenticated.
- *
- * @param array &$state Information about the current authentication.
- */
- public function reauthenticate(array &$state) {
- assert('isset($state["ReturnCallback"])');
-
- /* The default implementation just copies over the previous authentication data. */
- $session = SimpleSAML_Session::getSessionFromRequest();
- $data = $session->getAuthState($this->authId);
- foreach ($data as $k => $v) {
- $state[$k] = $v;
- }
- }
-
-
- /**
- * Complete authentication.
- *
- * This function should be called if authentication has completed. It will never return,
- * except in the case of exceptions. Exceptions thrown from this page should not be caught,
- * but should instead be passed to the top-level exception handler.
- *
- * @param array &$state Information about the current authentication.
- */
- public static function completeAuth(&$state) {
- assert('is_array($state)');
- assert('array_key_exists("LoginCompletedHandler", $state)');
-
- SimpleSAML_Auth_State::deleteState($state);
-
- $func = $state['LoginCompletedHandler'];
- assert('is_callable($func)');
-
- call_user_func($func, $state);
- assert(FALSE);
- }
-
-
- /**
- * Log out from this authentication source.
- *
- * This function should be overridden if the authentication source requires special
- * steps to complete a logout operation.
- *
- * If the logout process requires a redirect, the state should be saved. Once the
- * logout operation is completed, the state should be restored, and completeLogout
- * should be called with the state. If this operation can be completed without
- * showing the user a page, or redirecting, this function should return.
- *
- * @param array &$state Information about the current logout operation.
- */
- public function logout(&$state) {
- assert('is_array($state)');
-
- /* Default logout handler which doesn't do anything. */
- }
-
-
- /**
- * Complete logout.
- *
- * This function should be called after logout has completed. It will never return,
- * except in the case of exceptions. Exceptions thrown from this page should not be caught,
- * but should instead be passed to the top-level exception handler.
- *
- * @param array &$state Information about the current authentication.
- */
- public static function completeLogout(&$state) {
- assert('is_array($state)');
- assert('array_key_exists("LogoutCompletedHandler", $state)');
-
- SimpleSAML_Auth_State::deleteState($state);
-
- $func = $state['LogoutCompletedHandler'];
- assert('is_callable($func)');
-
- call_user_func($func, $state);
- assert(FALSE);
- }
-
-
- /**
- * Create authentication source object from configuration array.
- *
- * This function takes an array with the configuration for an authentication source object,
- * and returns the object.
- *
- * @param string $authId The authentication source identifier.
- * @param array $config The configuration.
- * @return SimpleSAML_Auth_Source The parsed authentication source.
- */
- private static function parseAuthSource($authId, $config) {
- assert('is_string($authId)');
- assert('is_array($config)');
-
- if (!array_key_exists(0, $config) || !is_string($config[0])) {
- throw new Exception('Invalid authentication source \'' . $authId .
- '\': First element must be a string which identifies the authentication source.');
- }
-
- $className = SimpleSAML_Module::resolveClass($config[0], 'Auth_Source',
- 'SimpleSAML_Auth_Source');
-
- $info = array('AuthId' => $authId);
- unset($config[0]);
- return new $className($info, $config);
- }
-
-
- /**
- * Retrieve authentication source.
- *
- * This function takes an id of an authentication source, and returns the
- * AuthSource object. If no authentication source with the given id can be found,
- * NULL will be returned.
- *
- * If the $type parameter is specified, this function will return an
- * authentication source of the given type. If no authentication source or if an
- * authentication source of a different type is found, an exception will be thrown.
- *
- * @param string $authId The authentication source identifier.
- * @param string|NULL $type The type of authentication source. If NULL, any type will be accepted.
- * @return SimpleSAML_Auth_Source|NULL The AuthSource object, or NULL if no authentication
- * source with the given identifier is found.
- */
- public static function getById($authId, $type = NULL) {
- assert('is_string($authId)');
- assert('is_null($type) || is_string($type)');
-
- /* For now - load and parse config file. */
- $config = SimpleSAML_Configuration::getConfig('authsources.php');
-
- $authConfig = $config->getArray($authId, NULL);
- if ($authConfig === NULL) {
- if ($type !== NULL) {
- throw new SimpleSAML_Error_Exception('No authentication source with id ' .
- var_export($authId, TRUE) . ' found.');
- }
- return NULL;
- }
-
- $ret = self::parseAuthSource($authId, $authConfig);
-
- if ($type === NULL || $ret instanceof $type) {
- return $ret;
- }
-
- /* The authentication source doesn't have the correct type. */
- throw new SimpleSAML_Error_Exception('Invalid type of authentication source ' .
- var_export($authId, TRUE) . '. Was ' . var_export(get_class($ret), TRUE) .
- ', should be ' . var_export($type, TRUE) . '.');
- }
-
-
- /**
- * Add a logout callback association.
- *
- * This function adds a logout callback association, which allows us to initiate
- * a logout later based on the $assoc-value.
- *
- * Note that logout-associations exists per authentication source. A logout association
- * from one authentication source cannot be called from a different authentication source.
- *
- * @param string $assoc The identifier for this logout association.
- * @param array $state The state array passed to the authenticate-function.
- */
- protected function addLogoutCallback($assoc, $state) {
- assert('is_string($assoc)');
- assert('is_array($state)');
-
- if (!array_key_exists('LogoutCallback', $state)) {
- /* The authentication requester doesn't have a logout callback. */
- return;
- }
- $callback = $state['LogoutCallback'];
-
- if (array_key_exists('LogoutCallbackState', $state)) {
- $callbackState = $state['LogoutCallbackState'];
- } else {
- $callbackState = array();
- }
-
- $id = strlen($this->authId) . ':' . $this->authId . $assoc;
-
- $data = array(
- 'callback' => $callback,
- 'state' => $callbackState,
- );
-
-
- $session = SimpleSAML_Session::getSessionFromRequest();
- $session->setData('SimpleSAML_Auth_Source.LogoutCallbacks', $id, $data,
- SimpleSAML_Session::DATA_TIMEOUT_SESSION_END);
- }
-
-
- /**
- * Call a logout callback based on association.
- *
- * This function calls a logout callback based on an association saved with
- * addLogoutCallback(...).
- *
- * This function always returns.
- *
- * @param string $assoc The logout association which should be called.
- */
- protected function callLogoutCallback($assoc) {
- assert('is_string($assoc)');
-
- $id = strlen($this->authId) . ':' . $this->authId . $assoc;
-
- $session = SimpleSAML_Session::getSessionFromRequest();
-
- $data = $session->getData('SimpleSAML_Auth_Source.LogoutCallbacks', $id);
- if ($data === NULL) {
- /* FIXME: fix for IdP-first flow (issue 397) -> reevaluate logout callback infrastructure */
- $session->doLogout($this->authId);
-
- return;
- }
-
- assert('is_array($data)');
- assert('array_key_exists("callback", $data)');
- assert('array_key_exists("state", $data)');
-
- $callback = $data['callback'];
- $callbackState = $data['state'];
-
- $session->deleteData('SimpleSAML_Auth_Source.LogoutCallbacks', $id);
- call_user_func($callback, $callbackState);
- }
-
-
- /**
- * Retrieve list of authentication sources.
- *
- * @param string $authId The authentication source identifier.
- * @return array The id of all authentication sources.
- */
- public static function getSources() {
-
- $config = SimpleSAML_Configuration::getOptionalConfig('authsources.php');
-
- return $config->getOptions();
- }
-
+abstract class SimpleSAML_Auth_Source
+{
+
+
+ /**
+ * The authentication source identifier. This identifier can be used to look up this object, for example when
+ * returning from a login form.
+ *
+ * @var string
+ */
+ protected $authId;
+
+
+ /**
+ * Constructor for an authentication source.
+ *
+ * Any authentication source which implements its own constructor must call this
+ * constructor first.
+ *
+ * @param array $info Information about this authentication source.
+ * @param array &$config Configuration for this authentication source.
+ */
+ public function __construct($info, &$config)
+ {
+ assert('is_array($info)');
+ assert('is_array($config)');
+
+ assert('array_key_exists("AuthId", $info)');
+ $this->authId = $info['AuthId'];
+ }
+
+
+ /**
+ * Get sources of a specific type.
+ *
+ * @param string $type The type of the authentication source.
+ *
+ * @return SimpleSAML_Auth_Source[] Array of SimpleSAML_Auth_Source objects of the specified type.
+ * @throws Exception If the authentication source is invalid.
+ */
+ public static function getSourcesOfType($type)
+ {
+ assert('is_string($type)');
+
+ $config = SimpleSAML_Configuration::getConfig('authsources.php');
+
+ $ret = array();
+
+ $sources = $config->getOptions();
+ foreach ($sources as $id) {
+ $source = $config->getArray($id);
+
+ self::validateSource($source, $id);
+
+ if ($source[0] !== $type) {
+ continue;
+ }
+
+ $ret[] = self::parseAuthSource($id, $source);
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * Retrieve the ID of this authentication source.
+ *
+ * @return string The ID of this authentication source.
+ */
+ public function getAuthId()
+ {
+ return $this->authId;
+ }
+
+
+ /**
+ * Process a request.
+ *
+ * If an authentication source returns from this function, it is assumed to have
+ * authenticated the user, and should have set elements in $state with the attributes
+ * of the user.
+ *
+ * If the authentication process requires additional steps which make it impossible to
+ * complete before returning from this function, the authentication source should
+ * save the state, and at a later stage, load the state, update it with the authentication
+ * information about the user, and call completeAuth with the state array.
+ *
+ * @param array &$state Information about the current authentication.
+ */
+ abstract public function authenticate(&$state);
+
+
+ /**
+ * Reauthenticate an user.
+ *
+ * This function is called by the IdP to give the authentication source a chance to
+ * interact with the user even in the case when the user is already authenticated.
+ *
+ * @param array &$state Information about the current authentication.
+ */
+ public function reauthenticate(array &$state)
+ {
+ assert('isset($state["ReturnCallback"])');
+
+ // the default implementation just copies over the previous authentication data
+ $session = SimpleSAML_Session::getSessionFromRequest();
+ $data = $session->getAuthState($this->authId);
+ foreach ($data as $k => $v) {
+ $state[$k] = $v;
+ }
+ }
+
+
+ /**
+ * Complete authentication.
+ *
+ * This function should be called if authentication has completed. It will never return,
+ * except in the case of exceptions. Exceptions thrown from this page should not be caught,
+ * but should instead be passed to the top-level exception handler.
+ *
+ * @param array &$state Information about the current authentication.
+ */
+ public static function completeAuth(&$state)
+ {
+ assert('is_array($state)');
+ assert('array_key_exists("LoginCompletedHandler", $state)');
+
+ SimpleSAML_Auth_State::deleteState($state);
+
+ $func = $state['LoginCompletedHandler'];
+ assert('is_callable($func)');
+
+ call_user_func($func, $state);
+ assert(false);
+ }
+
+
+ /**
+ * Log out from this authentication source.
+ *
+ * This function should be overridden if the authentication source requires special
+ * steps to complete a logout operation.
+ *
+ * If the logout process requires a redirect, the state should be saved. Once the
+ * logout operation is completed, the state should be restored, and completeLogout
+ * should be called with the state. If this operation can be completed without
+ * showing the user a page, or redirecting, this function should return.
+ *
+ * @param array &$state Information about the current logout operation.
+ */
+ public function logout(&$state)
+ {
+ assert('is_array($state)');
+ // default logout handler which doesn't do anything
+ }
+
+
+ /**
+ * Complete logout.
+ *
+ * This function should be called after logout has completed. It will never return,
+ * except in the case of exceptions. Exceptions thrown from this page should not be caught,
+ * but should instead be passed to the top-level exception handler.
+ *
+ * @param array &$state Information about the current authentication.
+ */
+ public static function completeLogout(&$state)
+ {
+ assert('is_array($state)');
+ assert('array_key_exists("LogoutCompletedHandler", $state)');
+
+ SimpleSAML_Auth_State::deleteState($state);
+
+ $func = $state['LogoutCompletedHandler'];
+ assert('is_callable($func)');
+
+ call_user_func($func, $state);
+ assert(false);
+ }
+
+
+ /**
+ * Create authentication source object from configuration array.
+ *
+ * This function takes an array with the configuration for an authentication source object,
+ * and returns the object.
+ *
+ * @param string $authId The authentication source identifier.
+ * @param array $config The configuration.
+ *
+ * @return SimpleSAML_Auth_Source The parsed authentication source.
+ * @throws Exception If the authentication source is invalid.
+ */
+ private static function parseAuthSource($authId, $config)
+ {
+ assert('is_string($authId)');
+ assert('is_array($config)');
+
+ self::validateSource($config, $authId);
+
+ $className = SimpleSAML_Module::resolveClass($config[0], 'Auth_Source', 'SimpleSAML_Auth_Source');
+
+ $info = array('AuthId' => $authId);
+ unset($config[0]);
+ return new $className($info, $config);
+ }
+
+
+ /**
+ * Retrieve authentication source.
+ *
+ * This function takes an id of an authentication source, and returns the
+ * AuthSource object. If no authentication source with the given id can be found,
+ * NULL will be returned.
+ *
+ * If the $type parameter is specified, this function will return an
+ * authentication source of the given type. If no authentication source or if an
+ * authentication source of a different type is found, an exception will be thrown.
+ *
+ * @param string $authId The authentication source identifier.
+ * @param string|NULL $type The type of authentication source. If NULL, any type will be accepted.
+ *
+ * @return SimpleSAML_Auth_Source|NULL The AuthSource object, or NULL if no authentication
+ * source with the given identifier is found.
+ * @throws SimpleSAML_Error_Exception If no such authentication source is found or it is invalid.
+ */
+ public static function getById($authId, $type = null)
+ {
+ assert('is_string($authId)');
+ assert('is_null($type) || is_string($type)');
+
+ // for now - load and parse config file
+ $config = SimpleSAML_Configuration::getConfig('authsources.php');
+
+ $authConfig = $config->getArray($authId, null);
+ if ($authConfig === null) {
+ if ($type !== null) {
+ throw new SimpleSAML_Error_Exception(
+ 'No authentication source with id '.
+ var_export($authId, true).' found.'
+ );
+ }
+ return null;
+ }
+
+ $ret = self::parseAuthSource($authId, $authConfig);
+
+ if ($type === null || $ret instanceof $type) {
+ return $ret;
+ }
+
+ // the authentication source doesn't have the correct type
+ throw new SimpleSAML_Error_Exception(
+ 'Invalid type of authentication source '.
+ var_export($authId, true).'. Was '.var_export(get_class($ret), true).
+ ', should be '.var_export($type, true).'.'
+ );
+ }
+
+
+ /**
+ * Add a logout callback association.
+ *
+ * This function adds a logout callback association, which allows us to initiate
+ * a logout later based on the $assoc-value.
+ *
+ * Note that logout-associations exists per authentication source. A logout association
+ * from one authentication source cannot be called from a different authentication source.
+ *
+ * @param string $assoc The identifier for this logout association.
+ * @param array $state The state array passed to the authenticate-function.
+ */
+ protected function addLogoutCallback($assoc, $state)
+ {
+ assert('is_string($assoc)');
+ assert('is_array($state)');
+
+ if (!array_key_exists('LogoutCallback', $state)) {
+ // the authentication requester doesn't have a logout callback
+ return;
+ }
+ $callback = $state['LogoutCallback'];
+
+ if (array_key_exists('LogoutCallbackState', $state)) {
+ $callbackState = $state['LogoutCallbackState'];
+ } else {
+ $callbackState = array();
+ }
+
+ $id = strlen($this->authId).':'.$this->authId.$assoc;
+
+ $data = array(
+ 'callback' => $callback,
+ 'state' => $callbackState,
+ );
+
+ $session = SimpleSAML_Session::getSessionFromRequest();
+ $session->setData(
+ 'SimpleSAML_Auth_Source.LogoutCallbacks',
+ $id,
+ $data,
+ SimpleSAML_Session::DATA_TIMEOUT_SESSION_END
+ );
+ }
+
+
+ /**
+ * Call a logout callback based on association.
+ *
+ * This function calls a logout callback based on an association saved with
+ * addLogoutCallback(...).
+ *
+ * This function always returns.
+ *
+ * @param string $assoc The logout association which should be called.
+ */
+ protected function callLogoutCallback($assoc)
+ {
+ assert('is_string($assoc)');
+
+ $id = strlen($this->authId).':'.$this->authId.$assoc;
+
+ $session = SimpleSAML_Session::getSessionFromRequest();
+
+ $data = $session->getData('SimpleSAML_Auth_Source.LogoutCallbacks', $id);
+ if ($data === null) {
+ // FIXME: fix for IdP-first flow (issue 397) -> reevaluate logout callback infrastructure
+ $session->doLogout($this->authId);
+
+ return;
+ }
+
+ assert('is_array($data)');
+ assert('array_key_exists("callback", $data)');
+ assert('array_key_exists("state", $data)');
+
+ $callback = $data['callback'];
+ $callbackState = $data['state'];
+
+ $session->deleteData('SimpleSAML_Auth_Source.LogoutCallbacks', $id);
+ call_user_func($callback, $callbackState);
+ }
+
+
+ /**
+ * Retrieve list of authentication sources.
+ *
+ * @return array The id of all authentication sources.
+ */
+ public static function getSources()
+ {
+ $config = SimpleSAML_Configuration::getOptionalConfig('authsources.php');
+
+ return $config->getOptions();
+ }
+
+
+ /**
+ * Make sure that the first element of an auth source is its identifier.
+ *
+ * @param array $source An array with the auth source configuration.
+ * @param string $id The auth source identifier.
+ *
+ * @throws Exception If the first element of $source is not an identifier for the auth source.
+ */
+ protected static function validateSource($source, $id)
+ {
+ if (!array_key_exists(0, $source) || !is_string($source[0])) {
+ throw new Exception(
+ 'Invalid authentication source \''.$id.
+ '\': First element must be a string which identifies the authentication source.'
+ );
+ }
+ }
}
diff --git a/lib/SimpleSAML/AuthMemCookie.php b/lib/SimpleSAML/AuthMemCookie.php
index dc1348f..c896656 100644
--- a/lib/SimpleSAML/AuthMemCookie.php
+++ b/lib/SimpleSAML/AuthMemCookie.php
@@ -1,146 +1,158 @@
<?php
+
/**
* This is a helper class for the Auth MemCookie module.
* It handles the configuration, and implements the logout handler.
*
* @author Olav Morken, UNINETT AS.
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-class SimpleSAML_AuthMemCookie {
-
- /**
- * This is the singleton instance of this class.
- */
- private static $instance = NULL;
-
-
- /**
- * The configuration for Auth MemCookie.
- */
- private $amcConfig;
-
- /**
- * This function is used to retrieve the singleton instance of this class.
- *
- * @return The singleton instance of this class.
- */
- public static function getInstance() {
- if(self::$instance === NULL) {
- self::$instance = new SimpleSAML_AuthMemCookie();
- }
-
- return self::$instance;
- }
-
-
- /**
- * This function implements the constructor for this class. It loads the Auth MemCookie configuration.
- */
- private function __construct() {
- /* Load Auth MemCookie configuration. */
- $this->amcConfig = SimpleSAML_Configuration::getConfig('authmemcookie.php');
- }
-
-
- /**
- * Retrieve the authentication source that should be used to authenticate the user.
- *
- * @return string The login type which should be used for Auth MemCookie.
- */
- public function getAuthSource() {
-
- return $this->amcConfig->getString('authsource');
- }
-
-
- /**
- * This function retrieves the name of the cookie from the configuration.
- *
- * @return string The name of the cookie.
- */
- public function getCookieName() {
- $cookieName = $this->amcConfig->getString('cookiename', 'AuthMemCookie');
- if(!is_string($cookieName) || strlen($cookieName) === 0) {
- throw new Exception('Configuration option \'cookiename\' contains an invalid value. This option should be a string.');
- }
-
- return $cookieName;
- }
-
-
- /**
- * This function retrieves the name of the attribute which contains the username from the configuration.
- *
- * @return string The name of the attribute which contains the username.
- */
- public function getUsernameAttr() {
- $usernameAttr = $this->amcConfig->getString('username', NULL);
-
- return $usernameAttr;
- }
-
-
- /**
- * This function retrieves the name of the attribute which contains the groups from the configuration.
- *
- * @return string The name of the attribute which contains the groups.
- */
- public function getGroupsAttr() {
- $groupsAttr = $this->amcConfig->getString('groups', NULL);
-
- return $groupsAttr;
- }
-
-
- /**
- * This function creates and initializes a Memcache object from our configuration.
- *
- * @return Memcache A Memcache object initialized from our configuration.
- */
- public function getMemcache() {
-
- $memcacheHost = $this->amcConfig->getString('memcache.host', '127.0.0.1');
- $memcachePort = $this->amcConfig->getInteger('memcache.port', 11211);
-
- $memcache = new Memcache;
-
- foreach (explode(',', $memcacheHost) as $memcacheHost) {
- $memcache->addServer($memcacheHost, $memcachePort);
- }
-
- return $memcache;
- }
-
-
- /**
- * This function logs the user out by deleting the session information from memcache.
- */
- private function doLogout() {
-
- $cookieName = $this->getCookieName();
-
- /* Check if we have a valid cookie. */
- if(!array_key_exists($cookieName, $_COOKIE)) {
- return;
- }
-
- $sessionID = $_COOKIE[$cookieName];
-
- /* Delete the session from memcache. */
- $memcache = $this->getMemcache();
- $memcache->delete($sessionID);
-
- /* Delete the session cookie. */
- $sessionHandler = SimpleSAML_SessionHandler::getSessionHandler();
- $sessionHandler->setCookie($cookieName, NULL);
- }
-
-
- /**
- * This function implements the logout handler. It deletes the information from Memcache.
- */
- public static function logoutHandler() {
- self::getInstance()->doLogout();
- }
+class SimpleSAML_AuthMemCookie
+{
+
+ /**
+ * @var SimpleSAML_AuthMemCookie This is the singleton instance of this class.
+ */
+ private static $instance = null;
+
+
+ /**
+ * @var SimpleSAML_Configuration The configuration for Auth MemCookie.
+ */
+ private $amcConfig;
+
+
+ /**
+ * This function is used to retrieve the singleton instance of this class.
+ *
+ * @return SimpleSAML_AuthMemCookie The singleton instance of this class.
+ */
+ public static function getInstance()
+ {
+ if (self::$instance === null) {
+ self::$instance = new SimpleSAML_AuthMemCookie();
+ }
+
+ return self::$instance;
+ }
+
+
+ /**
+ * This function implements the constructor for this class. It loads the Auth MemCookie configuration.
+ */
+ private function __construct()
+ {
+ // load AuthMemCookie configuration
+ $this->amcConfig = SimpleSAML_Configuration::getConfig('authmemcookie.php');
+ }
+
+
+ /**
+ * Retrieve the authentication source that should be used to authenticate the user.
+ *
+ * @return string The login type which should be used for Auth MemCookie.
+ */
+ public function getAuthSource()
+ {
+ return $this->amcConfig->getString('authsource');
+ }
+
+
+ /**
+ * This function retrieves the name of the cookie from the configuration.
+ *
+ * @return string The name of the cookie.
+ * @throws Exception If the value of the 'cookiename' configuration option is invalid.
+ */
+ public function getCookieName()
+ {
+ $cookieName = $this->amcConfig->getString('cookiename', 'AuthMemCookie');
+ if (!is_string($cookieName) || strlen($cookieName) === 0) {
+ throw new Exception(
+ "Configuration option 'cookiename' contains an invalid value. This option should be a string."
+ );
+ }
+
+ return $cookieName;
+ }
+
+
+ /**
+ * This function retrieves the name of the attribute which contains the username from the configuration.
+ *
+ * @return string The name of the attribute which contains the username.
+ */
+ public function getUsernameAttr()
+ {
+ $usernameAttr = $this->amcConfig->getString('username', null);
+
+ return $usernameAttr;
+ }
+
+
+ /**
+ * This function retrieves the name of the attribute which contains the groups from the configuration.
+ *
+ * @return string The name of the attribute which contains the groups.
+ */
+ public function getGroupsAttr()
+ {
+ $groupsAttr = $this->amcConfig->getString('groups', null);
+
+ return $groupsAttr;
+ }
+
+
+ /**
+ * This function creates and initializes a Memcache object from our configuration.
+ *
+ * @return Memcache A Memcache object initialized from our configuration.
+ */
+ public function getMemcache()
+ {
+ $memcacheHost = $this->amcConfig->getString('memcache.host', '127.0.0.1');
+ $memcachePort = $this->amcConfig->getInteger('memcache.port', 11211);
+
+ $memcache = new Memcache;
+
+ foreach (explode(',', $memcacheHost) as $memcacheHost) {
+ $memcache->addServer($memcacheHost, $memcachePort);
+ }
+
+ return $memcache;
+ }
+
+
+ /**
+ * This function logs the user out by deleting the session information from memcache.
+ */
+ private function doLogout()
+ {
+ $cookieName = $this->getCookieName();
+
+ // check if we have a valid cookie
+ if (!array_key_exists($cookieName, $_COOKIE)) {
+ return;
+ }
+
+ $sessionID = $_COOKIE[$cookieName];
+
+ // delete the session from memcache
+ $memcache = $this->getMemcache();
+ $memcache->delete($sessionID);
+
+ // delete the session cookie
+ $sessionHandler = SimpleSAML_SessionHandler::getSessionHandler();
+ $sessionHandler->setCookie($cookieName, null);
+ }
+
+
+ /**
+ * This function implements the logout handler. It deletes the information from Memcache.
+ */
+ public static function logoutHandler()
+ {
+ self::getInstance()->doLogout();
+ }
}
diff --git a/lib/SimpleSAML/Bindings/Shib13/HTTPPost.php b/lib/SimpleSAML/Bindings/Shib13/HTTPPost.php
index c97cf89..70624ce 100644
--- a/lib/SimpleSAML/Bindings/Shib13/HTTPPost.php
+++ b/lib/SimpleSAML/Bindings/Shib13/HTTPPost.php
@@ -1,121 +1,148 @@
<?php
+
/**
* Implementation of the Shibboleth 1.3 HTTP-POST binding.
*
* @author Andreas Åkre Solberg, UNINETT AS. <andreas.solberg@uninett.no>
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-class SimpleSAML_Bindings_Shib13_HTTPPost {
-
- private $configuration = null;
- private $metadata = null;
-
- function __construct(SimpleSAML_Configuration $configuration, SimpleSAML_Metadata_MetaDataStorageHandler $metadatastore) {
- $this->configuration = $configuration;
- $this->metadata = $metadatastore;
- }
-
- /**
- * Send an authenticationResponse using HTTP-POST.
- *
- * @param string $response The response which should be sent.
- * @param SimpleSAML_Configuration $idpmd The metadata of the IdP which is sending the response.
- * @param SimpleSAML_Configuration $spmd The metadata of the SP which is receiving the response.
- * @param string|NULL $relayState The relaystate for the SP.
- * @param string $shire The shire which should receive the response.
- */
- public function sendResponse($response, SimpleSAML_Configuration $idpmd, SimpleSAML_Configuration $spmd, $relayState, $shire) {
-
- \SimpleSAML\Utils\XML::checkSAMLMessage($response, 'saml11');
-
- $privatekey = SimpleSAML\Utils\Crypto::loadPrivateKey($idpmd, TRUE);
- $publickey = SimpleSAML\Utils\Crypto::loadPublicKey($idpmd, TRUE);
-
- $responsedom = new DOMDocument();
- $responsedom->loadXML(str_replace ("\r", "", $response));
-
- $responseroot = $responsedom->getElementsByTagName('Response')->item(0);
- $firstassertionroot = $responsedom->getElementsByTagName('Assertion')->item(0);
-
- /* Determine what we should sign - either the Response element or the Assertion. The default
- * is to sign the Assertion, but that can be overridden by the 'signresponse' option in the
- * SP metadata or 'saml20.signresponse' in the global configuration.
- */
- $signResponse = FALSE;
- if ($spmd->hasValue('signresponse')) {
- $signResponse = $spmd->getBoolean['signresponse'];
- } else {
- $signResponse = $this->configuration->getBoolean('shib13.signresponse', TRUE);
- }
-
- /* Check if we have an assertion to sign. Force to sign the response if not. */
- if ($firstassertionroot === NULL) {
- $signResponse = TRUE;
- }
-
- $signer = new SimpleSAML_XML_Signer(array(
- 'privatekey_array' => $privatekey,
- 'publickey_array' => $publickey,
- 'id' => ($signResponse ? 'ResponseID' : 'AssertionID') ,
- ));
-
- if ($idpmd->hasValue('certificatechain')) {
- $signer->addCertificate($idpmd->getString('certificatechain'));
- }
-
- if ($signResponse) {
- /* Sign the response - this must be done after encrypting the assertion. */
- /* We insert the signature before the saml2p:Status element. */
- $statusElements = SimpleSAML\Utils\XML::getDOMChildren($responseroot, 'Status', '@saml1p');
- assert('count($statusElements) === 1');
- $signer->sign($responseroot, $responseroot, $statusElements[0]);
-
- } else {
- /* Sign the assertion */
- $signer->sign($firstassertionroot, $firstassertionroot);
- }
-
- $response = $responsedom->saveXML();
-
- \SimpleSAML\Utils\XML::debugSAMLMessage($response, 'out');
-
- \SimpleSAML\Utils\HTTP::submitPOSTData($shire, array(
- 'TARGET' => $relayState,
- 'SAMLResponse' => base64_encode($response),
- ));
-
- }
-
-
- /**
- * Decode a received response.
- *
- * @param array $post POST data received.
- * @return SimpleSAML_XML_Shib13_AuthnResponse Response.
- */
- public function decodeResponse($post) {
- assert('is_array($post)');
-
- if (!array_key_exists('SAMLResponse', $post)) {
- throw new Exception('Missing required SAMLResponse parameter.');
- }
- $rawResponse = $post['SAMLResponse'];
- $samlResponseXML = base64_decode($rawResponse);
-
- \SimpleSAML\Utils\XML::debugSAMLMessage($samlResponseXML, 'in');
-
- \SimpleSAML\Utils\XML::checkSAMLMessage($samlResponseXML, 'saml11');
-
- $samlResponse = new SimpleSAML_XML_Shib13_AuthnResponse();
- $samlResponse->setXML($samlResponseXML);
-
- if (array_key_exists('TARGET', $post)) {
- $samlResponse->setRelayState($post['TARGET']);
- }
-
- return $samlResponse;
- }
-
+class SimpleSAML_Bindings_Shib13_HTTPPost
+{
+
+ /**
+ * @var SimpleSAML_Configuration
+ */
+ private $configuration = null;
+
+ /**
+ * @var SimpleSAML_Metadata_MetaDataStorageHandler
+ */
+ private $metadata = null;
+
+
+ /**
+ * Constructor for the SimpleSAML_Bindings_Shib13_HTTPPost class.
+ *
+ * @param SimpleSAML_Configuration $configuration The configuration to use.
+ * @param SimpleSAML_Metadata_MetaDataStorageHandler $metadatastore A store where to find metadata.
+ */
+ public function __construct(
+ SimpleSAML_Configuration $configuration,
+ SimpleSAML_Metadata_MetaDataStorageHandler $metadatastore
+ ) {
+ $this->configuration = $configuration;
+ $this->metadata = $metadatastore;
+ }
+
+
+ /**
+ * Send an authenticationResponse using HTTP-POST.
+ *
+ * @param string $response The response which should be sent.
+ * @param SimpleSAML_Configuration $idpmd The metadata of the IdP which is sending the response.
+ * @param SimpleSAML_Configuration $spmd The metadata of the SP which is receiving the response.
+ * @param string|null $relayState The relaystate for the SP.
+ * @param string $shire The shire which should receive the response.
+ */
+ public function sendResponse(
+ $response,
+ SimpleSAML_Configuration $idpmd,
+ SimpleSAML_Configuration $spmd,
+ $relayState,
+ $shire
+ ) {
+
+ \SimpleSAML\Utils\XML::checkSAMLMessage($response, 'saml11');
+
+ $privatekey = SimpleSAML\Utils\Crypto::loadPrivateKey($idpmd, true);
+ $publickey = SimpleSAML\Utils\Crypto::loadPublicKey($idpmd, true);
+
+ $responsedom = new DOMDocument();
+ $responsedom->loadXML(str_replace("\r", "", $response));
+
+ $responseroot = $responsedom->getElementsByTagName('Response')->item(0);
+ $firstassertionroot = $responsedom->getElementsByTagName('Assertion')->item(0);
+
+ /* Determine what we should sign - either the Response element or the Assertion. The default is to sign the
+ * Assertion, but that can be overridden by the 'signresponse' option in the SP metadata or
+ * 'saml20.signresponse' in the global configuration.
+ *
+ * TODO: neither 'signresponse' nor 'shib13.signresponse' are valid options any longer. Remove!
+ */
+ if ($spmd->hasValue('signresponse')) {
+ $signResponse = $spmd->getBoolean('signresponse');
+ } else {
+ $signResponse = $this->configuration->getBoolean('shib13.signresponse', true);
+ }
+
+ // check if we have an assertion to sign. Force to sign the response if not
+ if ($firstassertionroot === null) {
+ $signResponse = true;
+ }
+
+ $signer = new SimpleSAML_XML_Signer(array(
+ 'privatekey_array' => $privatekey,
+ 'publickey_array' => $publickey,
+ 'id' => ($signResponse ? 'ResponseID' : 'AssertionID'),
+ ));
+
+ if ($idpmd->hasValue('certificatechain')) {
+ $signer->addCertificate($idpmd->getString('certificatechain'));
+ }
+
+ if ($signResponse) {
+ // sign the response - this must be done after encrypting the assertion
+ // we insert the signature before the saml2p:Status element
+ $statusElements = SimpleSAML\Utils\XML::getDOMChildren($responseroot, 'Status', '@saml1p');
+ assert('count($statusElements) === 1');
+ $signer->sign($responseroot, $responseroot, $statusElements[0]);
+ } else {
+ /* Sign the assertion */
+ $signer->sign($firstassertionroot, $firstassertionroot);
+ }
+
+ $response = $responsedom->saveXML();
+
+ \SimpleSAML\Utils\XML::debugSAMLMessage($response, 'out');
+
+ \SimpleSAML\Utils\HTTP::submitPOSTData($shire, array(
+ 'TARGET' => $relayState,
+ 'SAMLResponse' => base64_encode($response),
+ ));
+ }
+
+
+ /**
+ * Decode a received response.
+ *
+ * @param array $post POST data received.
+ *
+ * @return SimpleSAML_XML_Shib13_AuthnResponse The response decoded into an object.
+ *
+ * @throws Exception If there is no SAMLResponse parameter.
+ */
+ public function decodeResponse($post)
+ {
+ assert('is_array($post)');
+
+ if (!array_key_exists('SAMLResponse', $post)) {
+ throw new Exception('Missing required SAMLResponse parameter.');
+ }
+ $rawResponse = $post['SAMLResponse'];
+ $samlResponseXML = base64_decode($rawResponse);
+
+ \SimpleSAML\Utils\XML::debugSAMLMessage($samlResponseXML, 'in');
+
+ \SimpleSAML\Utils\XML::checkSAMLMessage($samlResponseXML, 'saml11');
+
+ $samlResponse = new SimpleSAML_XML_Shib13_AuthnResponse();
+ $samlResponse->setXML($samlResponseXML);
+
+ if (array_key_exists('TARGET', $post)) {
+ $samlResponse->setRelayState($post['TARGET']);
+ }
+
+ return $samlResponse;
+ }
}
-
diff --git a/lib/SimpleSAML/Configuration.php b/lib/SimpleSAML/Configuration.php
index 517e6d1..4f9bf36 100644
--- a/lib/SimpleSAML/Configuration.php
+++ b/lib/SimpleSAML/Configuration.php
@@ -1,1138 +1,1272 @@
<?php
-
+
+
/**
* Configuration of SimpleSAMLphp
*
* @author Andreas Aakre Solberg, UNINETT AS. <andreas.solberg@uninett.no>
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-class SimpleSAML_Configuration {
-
- /**
- * A default value which means that the given option is required.
- */
- const REQUIRED_OPTION = '___REQUIRED_OPTION___';
-
-
- /**
- * Associative array with mappings from instance-names to configuration objects.
- */
- private static $instance = array();
-
-
- /**
- * Configration directories.
- *
- * This associative array contains the mappings from configuration sets to
- * configuration directories.
- */
- private static $configDirs = array();
-
-
- /**
- * Cache of loaded configuration files.
- *
- * The index in the array is the full path to the file.
- */
- private static $loadedConfigs = array();
-
-
- /**
- * The configuration array.
- */
- private $configuration;
-
-
- /**
- * The location which will be given when an error occurs.
- */
- private $location;
-
-
- /**
- * The file this configuration was loaded from.
- */
- private $filename = NULL;
-
-
-
- /**
- * Initializes a configuration from the given array.
- *
- * @param array $config The configuration array.
- * @param string $location The location which will be given when an error occurs.
- */
- public function __construct($config, $location) {
- assert('is_array($config)');
- assert('is_string($location)');
-
- $this->configuration = $config;
- $this->location = $location;
- }
-
-
- /**
- * Load the given configuration file.
- *
- * @param string $filename The full path of the configuration file.
- * @param bool @required Whether the file is required.
- * @return SimpleSAML_Configuration The configuration file. An exception will be thrown if the
- * configuration file is missing.
- */
- private static function loadFromFile($filename, $required) {
- assert('is_string($filename)');
- assert('is_bool($required)');
-
- if (array_key_exists($filename, self::$loadedConfigs)) {
- return self::$loadedConfigs[$filename];
- }
-
- if (file_exists($filename)) {
- $config = 'UNINITIALIZED';
-
- /* The file initializes a variable named '$config'. */
- require($filename);
-
- /* Check that $config is initialized to an array. */
- if (!is_array($config)) {
- throw new Exception('Invalid configuration file: ' . $filename);
- }
-
- } elseif ($required) {
- /* File does not exist, but is required. */
- throw new Exception('Missing configuration file: ' . $filename);
-
- } else {
- /* File does not exist, but is optional. */
- $config = array();
- }
-
- $cfg = new SimpleSAML_Configuration($config, $filename);
- $cfg->filename = $filename;
-
- self::$loadedConfigs[$filename] = $cfg;
-
- return $cfg;
- }
-
-
- /**
- * Set the directory for configuration files for the given configuration set.
- *
- * @param string $path The directory which contains the configuration files.
- * @param string $configSet The configuration set. Defaults to 'simplesaml'.
- */
- public static function setConfigDir($path, $configSet = 'simplesaml') {
- assert('is_string($path)');
- assert('is_string($configSet)');
-
- self::$configDirs[$configSet] = $path;
- }
-
-
- /**
- * Load a configuration file from a configuration set.
- *
- * @param string $filename The name of the configuration file.
- * @param string $configSet The configuration set. Optional, defaults to 'simplesaml'.
- */
- public static function getConfig($filename = 'config.php', $configSet = 'simplesaml') {
- assert('is_string($filename)');
- assert('is_string($configSet)');
-
- if (!array_key_exists($configSet, self::$configDirs)) {
- if ($configSet !== 'simplesaml') {
- throw new Exception('Configuration set \'' . $configSet . '\' not initialized.');
- } else {
- self::$configDirs['simplesaml'] = SimpleSAML\Utils\Config::getConfigDir();
- }
- }
-
- $dir = self::$configDirs[$configSet];
- $filePath = $dir . '/' . $filename;
- return self::loadFromFile($filePath, TRUE);
- }
-
-
- /**
- * Load a configuration file from a configuration set.
- *
- * This function will return a configuration object even if the file does not exist.
- *
- * @param string $filename The name of the configuration file.
- * @param string $configSet The configuration set. Optional, defaults to 'simplesaml'.
- * @return SimpleSAML_Configuration A configuration object.
- */
- public static function getOptionalConfig($filename = 'config.php', $configSet = 'simplesaml') {
- assert('is_string($filename)');
- assert('is_string($configSet)');
-
- if (!array_key_exists($configSet, self::$configDirs)) {
- if ($configSet !== 'simplesaml') {
- throw new Exception('Configuration set \'' . $configSet . '\' not initialized.');
- } else {
- self::$configDirs['simplesaml'] = SimpleSAML\Utils\Config::getConfigDir();
- }
- }
-
- $dir = self::$configDirs[$configSet];
- $filePath = $dir . '/' . $filename;
- return self::loadFromFile($filePath, FALSE);
- }
-
-
- /**
- * Loads a configuration from the given array.
- *
- * @param array $config The configuration array.
- * @param string $location The location which will be given when an error occurs. Optional.
- * @return SimpleSAML_Configuration The configuration object.
- */
- public static function loadFromArray($config, $location = '[ARRAY]') {
- assert('is_array($config)');
- assert('is_string($location)');
-
- return new SimpleSAML_Configuration($config, $location);
- }
-
-
- /**
- * Get a configuration file by its instance name.
- *
- * This function retrieves a configuration file by its instance name. The instance
- * name is initialized by the init function, or by copyFromBase function.
- *
- * If no configuration file with the given instance name is found, an exception will
- * be thrown.
- *
- * @param string $instancename The instance name of the configuration file. Depreceated.
- * @return SimpleSAML_Configuration The configuration object.
- */
- public static function getInstance($instancename = 'simplesaml') {
- assert('is_string($instancename)');
-
- if ($instancename === 'simplesaml') {
- return self::getConfig();
- }
-
- if (!array_key_exists($instancename, self::$instance))
- throw new Exception('Configuration with name ' . $instancename . ' is not initialized.');
- return self::$instance[$instancename];
- }
-
-
- /**
- * Initialize a instance name with the given configuration file.
- *
- * @see setConfigDir()
- * @depreceated This function is superseeded by the setConfigDir function.
- */
- public static function init($path, $instancename = 'simplesaml', $configfilename = 'config.php') {
- assert('is_string($path)');
- assert('is_string($instancename)');
- assert('is_string($configfilename)');
-
- if ($instancename === 'simplesaml') {
- /* For backwards compatibility. */
- self::setConfigDir($path, 'simplesaml');
- }
-
- /* Check if we already have loaded the given config - return the existing instance if we have. */
- if(array_key_exists($instancename, self::$instance)) {
- return self::$instance[$instancename];
- }
-
- self::$instance[$instancename] = self::loadFromFile($path . '/' . $configfilename, TRUE);
- }
-
-
- /**
- * Load a configuration file which is located in the same directory as this configuration file.
- *
- * @see getConfig()
- * @depreceated This function is superseeded by the getConfig() function.
- */
- public function copyFromBase($instancename, $filename) {
- assert('is_string($instancename)');
- assert('is_string($filename)');
- assert('$this->filename !== NULL');
-
- /* Check if we already have loaded the given config - return the existing instance if we have. */
- if(array_key_exists($instancename, self::$instance)) {
- return self::$instance[$instancename];
- }
-
- $dir = dirname($this->filename);
-
- self::$instance[$instancename] = self::loadFromFile($dir . '/' . $filename, TRUE);
- return self::$instance[$instancename];
- }
-
-
- /**
- * Retrieve the current version of simpleSAMLphp.
- *
- * @return string
- */
- public function getVersion() {
- return 'trunk';
- }
-
-
- /**
- * Retrieve a configuration option set in config.php.
- *
- * @param $name Name of the configuration option.
- * @param $default Default value of the configuration option. This parameter will default to NULL if not
- * specified. This can be set to SimpleSAML_Configuration::REQUIRED_OPTION, which will
- * cause an exception to be thrown if the option isn't found.
- * @return The configuration option with name $name, or $default if the option was not found.
- */
- public function getValue($name, $default = NULL) {
-
- /* Return the default value if the option is unset. */
- if (!array_key_exists($name, $this->configuration)) {
- if($default === self::REQUIRED_OPTION) {
- throw new Exception($this->location . ': Could not retrieve the required option ' .
- var_export($name, TRUE));
- }
- return $default;
- }
-
- return $this->configuration[$name];
- }
-
-
- /**
- * Check whether an key in the configuration exists...
- */
- public function hasValue($name) {
- return array_key_exists($name, $this->configuration);
- }
- /**
- * Check whether an key in the configuration exists...
- */
- public function hasValueOneOf($names) {
- foreach($names AS $name) if ($this->hasValue($name)) return TRUE;
- return FALSE;
- }
-
- /**
- * Retrieve the absolute path of the simpleSAMLphp installation,
- * relative to the root of the website.
- *
- * For example: simplesaml/
- *
- * The path will always end with a '/' and never have a leading slash.
- *
- * @return string The absolute path relative to the root of the website.
- */
- public function getBaseURL() {
- $baseURL = $this->getString('baseurlpath', 'simplesaml/');
-
- if (preg_match('/^\*(.*)$/D', $baseURL, $matches)) {
- /* deprecated behaviour, will be removed in the future */
- return \SimpleSAML\Utils\HTTP::getFirstPathElement(false) . $matches[1];
- }
-
- if (preg_match('#^https?://[^/]*/(.*)$#', $baseURL, $matches)) {
- /* we have a full url, we need to strip the path */
- return $matches[1];
- } elseif ($baseURL === '' || $baseURL === '/') {
- /* Root directory of site. */
- return '';
- } elseif (preg_match('#^/?([^/]?.*/)#D', $baseURL, $matches)) {
- /* local path only */
- return $matches[1];
- } else {
- /* invalid format */
- throw new SimpleSAML_Error_Exception('Incorrect format for option \'baseurlpath\'. Value is: "'.
- $this->getString('baseurlpath', 'simplesaml/') . '". Valid format is in the form'.
- ' [(http|https)://(hostname|fqdn)[:port]]/[path/to/simplesaml/].');
- }
- }
-
-
- /**
- * This function resolves a path which may be relative to the
- * simpleSAMLphp base directory.
- *
- * The path will never end with a '/'.
- *
- * @param $path The path we should resolve. This option may be NULL.
- * @return $path if $path is an absolute path, or $path prepended with
- * the base directory of this simpleSAMLphp installation. We
- * will return NULL if $path is NULL.
- */
- public function resolvePath($path) {
- if($path === NULL) {
- return NULL;
- }
-
- assert('is_string($path)');
-
- /* Prepend path with basedir if it doesn't start with a slash or a Windows
- * drive letter (e.g. "C:\"). We assume getBaseDir ends with a slash.
- */
- if ($path[0] !== '/' &&
- !(preg_match('@^[a-z]:[\\\\/]@i', $path, $matches) && is_dir($matches[0])))
- $path = $this->getBaseDir() . $path;
-
- /* Remove trailing slashes. */
- while (substr($path, -1) === '/') {
- $path = substr($path, 0, -1);
- }
-
- return $path;
- }
-
-
- /**
- * Retrieve a path configuration option set in config.php.
- * The function will always return an absolute path unless the
- * option is not set. It will then return the default value.
- *
- * It checks if the value starts with a slash, and prefixes it
- * with the value from getBaseDir if it doesn't.
- *
- * @param $name Name of the configuration option.
- * @param $default Default value of the configuration option.
- * This parameter will default to NULL if not specified.
- * @return The path configuration option with name $name, or $default if
- * the option was not found.
- */
- public function getPathValue($name, $default = NULL) {
-
- /* Return the default value if the option is unset. */
- if (!array_key_exists($name, $this->configuration)) {
- $path = $default;
- } else {
- $path = $this->configuration[$name];
- }
-
- if ($path === NULL) {
- return NULL;
- }
-
- return $this->resolvePath($path) . '/';
- }
-
-
- /**
- * Retrieve the base directory for this simpleSAMLphp installation.
- * This function first checks the 'basedir' configuration option. If
- * this option is undefined or NULL, then we fall back to looking at
- * the current filename.
- *
- * @return The absolute path to the base directory for this simpleSAMLphp
- * installation. This path will always end with a slash.
- */
- public function getBaseDir() {
- /* Check if a directory is configured in the configuration
- * file.
- */
- $dir = $this->getString('basedir', NULL);
- if($dir !== NULL) {
- /* Add trailing slash if it is missing. */
- if(substr($dir, -1) !== '/') {
- $dir .= '/';
- }
-
- return $dir;
- }
-
- /* The directory wasn't set in the configuration file. Our
- * path is <base directory>/lib/SimpleSAML/Configuration.php
- */
-
- $dir = __FILE__;
- assert('basename($dir) === "Configuration.php"');
-
- $dir = dirname($dir);
- assert('basename($dir) === "SimpleSAML"');
-
- $dir = dirname($dir);
- assert('basename($dir) === "lib"');
-
- $dir = dirname($dir);
-
- /* Add trailing slash. */
- $dir .= '/';
-
- return $dir;
- }
-
-
- /**
- * This function retrieves a boolean configuration option.
- *
- * An exception will be thrown if this option isn't a boolean, or if this option isn't found, and no
- * default value is given.
- *
- * @param $name The name of the option.
- * @param $default A default value which will be returned if the option isn't found. The option will be
- * required if this parameter isn't given. The default value can be any value, including
- * NULL.
- * @return The option with the given name, or $default if the option isn't found and $default is specified.
- */
- public function getBoolean($name, $default = self::REQUIRED_OPTION) {
- assert('is_string($name)');
-
- $ret = $this->getValue($name, $default);
-
- if($ret === $default) {
- /* The option wasn't found, or it matches the default value. In any case, return
- * this value.
- */
- return $ret;
- }
-
- if(!is_bool($ret)) {
- throw new Exception($this->location . ': The option ' . var_export($name, TRUE) .
- ' is not a valid boolean value.');
- }
-
- return $ret;
- }
-
-
- /**
- * This function retrieves a string configuration option.
- *
- * An exception will be thrown if this option isn't a string, or if this option isn't found, and no
- * default value is given.
- *
- * @param $name The name of the option.
- * @param $default A default value which will be returned if the option isn't found. The option will be
- * required if this parameter isn't given. The default value can be any value, including
- * NULL.
- * @return The option with the given name, or $default if the option isn't found and $default is specified.
- */
- public function getString($name, $default = self::REQUIRED_OPTION) {
- assert('is_string($name)');
-
- $ret = $this->getValue($name, $default);
-
- if($ret === $default) {
- /* The option wasn't found, or it matches the default value. In any case, return
- * this value.
- */
- return $ret;
- }
-
- if(!is_string($ret)) {
- throw new Exception($this->location . ': The option ' . var_export($name, TRUE) .
- ' is not a valid string value.');
- }
-
- return $ret;
- }
-
-
- /**
- * This function retrieves an integer configuration option.
- *
- * An exception will be thrown if this option isn't an integer, or if this option isn't found, and no
- * default value is given.
- *
- * @param $name The name of the option.
- * @param $default A default value which will be returned if the option isn't found. The option will be
- * required if this parameter isn't given. The default value can be any value, including
- * NULL.
- * @return The option with the given name, or $default if the option isn't found and $default is specified.
- */
- public function getInteger($name, $default = self::REQUIRED_OPTION) {
- assert('is_string($name)');
-
- $ret = $this->getValue($name, $default);
-
- if($ret === $default) {
- /* The option wasn't found, or it matches the default value. In any case, return
- * this value.
- */
- return $ret;
- }
-
- if(!is_int($ret)) {
- throw new Exception($this->location . ': The option ' . var_export($name, TRUE) .
- ' is not a valid integer value.');
- }
-
- return $ret;
- }
-
-
- /**
- * This function retrieves an integer configuration option where the value must be in the specified range.
- *
- * An exception will be thrown if:
- * - the option isn't an integer
- * - the option isn't found, and no default value is given
- * - the value is outside of the allowed range
- *
- * @param $name The name of the option.
- * @param $minimum The smallest value which is allowed.
- * @param $maximum The largest value which is allowed.
- * @param $default A default value which will be returned if the option isn't found. The option will be
- * required if this parameter isn't given. The default value can be any value, including
- * NULL.
- * @return The option with the given name, or $default if the option isn't found and $default is specified.
- */
- public function getIntegerRange($name, $minimum, $maximum, $default = self::REQUIRED_OPTION) {
- assert('is_string($name)');
- assert('is_int($minimum)');
- assert('is_int($maximum)');
-
- $ret = $this->getInteger($name, $default);
-
- if($ret === $default) {
- /* The option wasn't found, or it matches the default value. In any case, return
- * this value.
- */
- return $ret;
- }
-
- if ($ret < $minimum || $ret > $maximum) {
- throw new Exception($this->location . ': Value of option ' . var_export($name, TRUE) .
- ' is out of range. Value is ' . $ret . ', allowed range is ['
- . $minimum . ' - ' . $maximum . ']');
- }
-
- return $ret;
- }
-
-
- /**
- * Retrieve a configuration option with one of the given values.
- *
- * This will check that the configuration option matches one of the given values. The match will use
- * strict comparison. An exception will be thrown if it does not match.
- *
- * The option can be mandatory or optional. If no default value is given, it will be considered to be
- * mandatory, and an exception will be thrown if it isn't provided. If a default value is given, it
- * is considered to be optional, and the default value is returned. The default value is automatically
- * included in the list of allowed values.
- *
- * @param $name The name of the option.
- * @param $allowedValues The values the option is allowed to take, as an array.
- * @param $default The default value which will be returned if the option isn't found. If this parameter
- * isn't given, the option will be considered to be mandatory. The default value can be
- * any value, including NULL.
- * @return The option with the given name, or $default if the option isn't found adn $default is given.
- */
- public function getValueValidate($name, $allowedValues, $default = self::REQUIRED_OPTION) {
- assert('is_string($name)');
- assert('is_array($allowedValues)');
-
- $ret = $this->getValue($name, $default);
- if($ret === $default) {
- /* The option wasn't found, or it matches the default value. In any case, return
- * this value.
- */
- return $ret;
- }
-
- if(!in_array($ret, $allowedValues, TRUE)) {
- $strValues = array();
- foreach($allowedValues as $av) {
- $strValues[] = var_export($av, TRUE);
- }
- $strValues = implode(', ', $strValues);
-
- throw new Exception($this->location . ': Invalid value given for the option ' .
- var_export($name, TRUE) . '. It should have one of the following values: ' .
- $strValues . '; but it had the following value: ' . var_export($ret, TRUE));
- }
-
- return $ret;
- }
-
-
- /**
- * This function retrieves an array configuration option.
- *
- * An exception will be thrown if this option isn't an array, or if this option isn't found, and no
- * default value is given.
- *
- * @param string $name The name of the option.
- * @param mixed$default A default value which will be returned if the option isn't found. The option will be
- * required if this parameter isn't given. The default value can be any value, including
- * NULL.
- * @return mixed The option with the given name, or $default if the option isn't found and $default is specified.
- */
- public function getArray($name, $default = self::REQUIRED_OPTION) {
- assert('is_string($name)');
-
- $ret = $this->getValue($name, $default);
-
- if ($ret === $default) {
- /* The option wasn't found, or it matches the default value. In any case, return
- * this value.
- */
- return $ret;
- }
-
- if (!is_array($ret)) {
- throw new Exception($this->location . ': The option ' . var_export($name, TRUE) .
- ' is not an array.');
- }
-
- return $ret;
- }
-
-
- /**
- * This function retrieves an array configuration option.
- *
- * If the configuration option isn't an array, it will be converted to an array.
- *
- * @param string $name The name of the option.
- * @param mixed $default A default value which will be returned if the option isn't found. The option will be
- * required if this parameter isn't given. The default value can be any value, including
- * NULL.
- * @return array The option with the given name, or $default if the option isn't found and $default is specified.
- */
- public function getArrayize($name, $default = self::REQUIRED_OPTION) {
- assert('is_string($name)');
-
- $ret = $this->getValue($name, $default);
-
- if ($ret === $default) {
- /* The option wasn't found, or it matches the default value. In any case, return
- * this value.
- */
- return $ret;
- }
-
- if (!is_array($ret)) {
- $ret = array($ret);
- }
-
- return $ret;
- }
-
-
- /**
- * This function retrieves a configuration option with a string or an array of strings.
- *
- * If the configuration option is a string, it will be converted to an array with a single string
- *
- * @param string $name The name of the option.
- * @param mixed $default A default value which will be returned if the option isn't found. The option will be
- * required if this parameter isn't given. The default value can be any value, including
- * NULL.
- * @return array The option with the given name, or $default if the option isn't found and $default is specified.
- */
- public function getArrayizeString($name, $default = self::REQUIRED_OPTION) {
- assert('is_string($name)');
-
- $ret = $this->getArrayize($name, $default);
-
- if ($ret === $default) {
- /* The option wasn't found, or it matches the default value. In any case, return
- * this value.
- */
- return $ret;
- }
-
- foreach ($ret as $value) {
- if (!is_string($value)) {
- throw new Exception($this->location . ': The option ' . var_export($name, TRUE) .
- ' must be a string or an array of strings.');
- }
- }
-
- return $ret;
- }
-
-
- /**
- * Retrieve an array as a SimpleSAML_Configuration object.
- *
- * This function will load the value of an option into a SimpleSAML_Configuration
- * object. The option must contain an array.
- *
- * An exception will be thrown if this option isn't an array, or if this option
- * isn't found, and no default value is given.
- *
- * @param string $name The name of the option.
- * @param mixed $default A default value which will be returned if the option isn't found. The option will be
- * required if this parameter isn't given. The default value can be any value, including
- * NULL.
- * @return mixed The option with the given name, or $default if the option isn't found and $default is specified.
- */
- public function getConfigItem($name, $default = self::REQUIRED_OPTION) {
- assert('is_string($name)');
-
- $ret = $this->getValue($name, $default);
-
- if ($ret === $default) {
- /* The option wasn't found, or it matches the default value. In any case, return
- * this value.
- */
- return $ret;
- }
-
- if (!is_array($ret)) {
- throw new Exception($this->location . ': The option ' . var_export($name, TRUE) .
- ' is not an array.');
- }
-
- return self::loadFromArray($ret, $this->location . '[' . var_export($name, TRUE) . ']');
- }
-
-
- /**
- * Retrieve an array of arrays as an array of SimpleSAML_Configuration objects.
- *
- * This function will retrieve an option containing an array of arrays, and create an
- * array of SimpleSAML_Configuration objects from that array. The indexes in the new
- * array will be the same as the original indexes, but the values will be
- * SimpleSAML_Configuration objects.
- *
- * An exception will be thrown if this option isn't an array of arrays, or if this option
- * isn't found, and no default value is given.
- *
- * @param string $name The name of the option.
- * @param string $location Name of the items in the array.
- * @param mixed $default A default value which will be returned if the option isn't found. The option will be
- * required if this parameter isn't given. The default value can be any value, including
- * NULL.
- * @return mixed The option with the given name, or $default if the option isn't found and $default is specified.
- */
- public function getConfigList($name, $default = self::REQUIRED_OPTION) {
- assert('is_string($name)');
-
- $ret = $this->getValue($name, $default);
-
- if ($ret === $default) {
- /* The option wasn't found, or it matches the default value. In any case, return
- * this value.
- */
- return $ret;
- }
-
- if (!is_array($ret)) {
- throw new Exception($this->location . ': The option ' . var_export($name, TRUE) .
- ' is not an array.');
- }
-
- $out = array();
- foreach ($ret as $index => $config) {
- $newLoc = $this->location . '[' . var_export($name, TRUE) . '][' .
- var_export($index, TRUE) . ']';
- if (!is_array($config)) {
- throw new Exception($newLoc . ': The value of this element was expected to be an array.');
- }
- $out[$index] = self::loadFromArray($config, $newLoc);
- }
-
- return $out;
- }
-
-
- /**
- * Retrieve list of options.
- *
- * This function returns the name of all options which are defined in this
- * configuration file, as an array of strings.
- *
- * @return array Name of all options defined in this configuration file.
- */
- public function getOptions() {
-
- return array_keys($this->configuration);
- }
-
-
- /**
- * Convert this configuration object back to an array.
- *
- * @return array An associative array with all configuration options and values.
- */
- public function toArray() {
-
- return $this->configuration;
- }
-
-
- /**
- * Retrieve the default binding for the given endpoint type.
- *
- * This function combines the current metadata type (saml 2 / saml 1.1)
- * with the endpoint type to determine which binding is the default.
- *
- * @param string $endpointType The endpoint type.
- * @return string The default binding.
- */
- private function getDefaultBinding($endpointType) {
- assert('is_string($endpointType)');
-
- $set = $this->getString('metadata-set');
- switch ($set.':'.$endpointType) {
- case 'saml20-idp-remote:SingleSignOnService':
- case 'saml20-idp-remote:SingleLogoutService':
- case 'saml20-sp-remote:SingleLogoutService':
- return SAML2_Const::BINDING_HTTP_REDIRECT;
- case 'saml20-sp-remote:AssertionConsumerService':
- return SAML2_Const::BINDING_HTTP_POST;
- case 'saml20-idp-remote:ArtifactResolutionService':
- return SAML2_Const::BINDING_SOAP;
- case 'shib13-idp-remote:SingleSignOnService':
- return 'urn:mace:shibboleth:1.0:profiles:AuthnRequest';
- case 'shib13-sp-remote:AssertionConsumerService':
- return 'urn:oasis:names:tc:SAML:1.0:profiles:browser-post';
- default:
- throw new Exception('Missing default binding for ' . $endpointType . ' in ' . $set);
- }
- }
-
-
- /**
- * Helper function for dealing with metadata endpoints.
- *
- * @param string $endpointType The endpoint type.
- * @return array Array of endpoints of the given type.
- */
- public function getEndpoints($endpointType) {
- assert('is_string($endpointType)');
-
- $loc = $this->location . '[' . var_export($endpointType, TRUE) . ']:';
-
- if (!array_key_exists($endpointType, $this->configuration)) {
- /* No endpoints of the given type. */
- return array();
- }
-
-
- $eps = $this->configuration[$endpointType];
- if (is_string($eps)) {
- /* For backwards-compatibility. */
- $eps = array($eps);
- } elseif (!is_array($eps)) {
- throw new Exception($loc . ': Expected array or string.');
- }
-
-
- foreach ($eps as $i => &$ep) {
- $iloc = $loc . '[' . var_export($i, TRUE) . ']';
-
- if (is_string($ep)) {
- /* For backwards-compatibility. */
- $ep = array(
- 'Location' => $ep,
- 'Binding' => $this->getDefaultBinding($endpointType),
- );
- $responseLocation = $this->getString($endpointType . 'Response', NULL);
- if ($responseLocation !== NULL) {
- $ep['ResponseLocation'] = $responseLocation;
- }
- } elseif (!is_array($ep)) {
- throw new Exception($iloc . ': Expected a string or an array.');
- }
-
- if (!array_key_exists('Location', $ep)) {
- throw new Exception($iloc . ': Missing Location.');
- }
- if (!is_string($ep['Location'])) {
- throw new Exception($iloc . ': Location must be a string.');
- }
-
- if (!array_key_exists('Binding', $ep)) {
- throw new Exception($iloc . ': Missing Binding.');
- }
- if (!is_string($ep['Binding'])) {
- throw new Exception($iloc . ': Binding must be a string.');
- }
-
- if (array_key_exists('ResponseLocation', $ep)) {
- if (!is_string($ep['ResponseLocation'])) {
- throw new Exception($iloc . ': ResponseLocation must be a string.');
- }
- }
-
- if (array_key_exists('index', $ep)) {
- if (!is_int($ep['index'])) {
- throw new Exception($iloc . ': index must be an integer.');
- }
- }
-
- }
-
- return $eps;
- }
-
-
- /**
- * Find an endpoint of the given type, using a list of supported bindings as a way to prioritize.
- *
- * @param string $endpointType The endpoint type.
- * @param array $bindings Sorted array of acceptable bindings.
- * @param mixed $default The default value to return if no matching endpoint is found. If no default is provided, an exception will be thrown.
- * @return array|NULL The default endpoint, or NULL if no acceptable endpoints are used.
- */
- public function getEndpointPrioritizedByBinding($endpointType, array $bindings, $default = self::REQUIRED_OPTION) {
- assert('is_string($endpointType)');
-
- $endpoints = $this->getEndpoints($endpointType);
-
- foreach ($bindings as $binding) {
- foreach ($endpoints as $ep) {
- if ($ep['Binding'] === $binding) {
- return $ep;
- }
- }
- }
-
- if ($default === self::REQUIRED_OPTION) {
- $loc = $this->location . '[' . var_export($endpointType, TRUE) . ']:';
- throw new Exception($loc . 'Could not find a supported ' . $endpointType . ' endpoint.');
- }
-
- return $default;
- }
-
-
- /**
- * Find the default endpoint of the given type.
- *
- * @param string $endpointType The endpoint type.
- * @param array $bindings Array with acceptable bindings. Can be NULL if any binding is allowed.
- * @param mixed $default The default value to return if no matching endpoint is found. If no default is provided, an exception will be thrown.
- * @return array|NULL The default endpoint, or NULL if no acceptable endpoints are used.
- */
- public function getDefaultEndpoint($endpointType, array $bindings = NULL, $default = self::REQUIRED_OPTION) {
- assert('is_string($endpointType)');
-
- $endpoints = $this->getEndpoints($endpointType);
-
- $defaultEndpoint = \SimpleSAML\Utils\Config\Metadata::getDefaultEndpoint($endpoints, $bindings);
- if ($defaultEndpoint !== NULL) {
- return $defaultEndpoint;
- }
-
- if ($default === self::REQUIRED_OPTION) {
- $loc = $this->location . '[' . var_export($endpointType, TRUE) . ']:';
- throw new Exception($loc . 'Could not find a supported ' . $endpointType . ' endpoint.');
- }
-
- return $default;
- }
-
-
- /**
- * Retrieve a string which may be localized into many languages.
- *
- * The default language returned is always 'en'.
- * @param string $name The name of the option.
- * @param mixed $default The default value. If no default is given, and the option isn't found, an exception will be thrown.
- * @return array Associative array with language=>string pairs.
- */
- public function getLocalizedString($name, $default = self::REQUIRED_OPTION) {
- assert('is_string($name)');
-
- $ret = $this->getValue($name, $default);
- if($ret === $default) {
- /* The option wasn't found, or it matches the default value. In any case, return
- * this value.
- */
- return $ret;
- }
-
- $loc = $this->location . '[' . var_export($name, TRUE) . ']';
-
- if (is_string($ret)) {
- $ret = array('en' => $ret,);
- }
-
- if (!is_array($ret)) {
- throw new Exception($loc . ': Must be an array or a string.');
- }
-
- foreach ($ret as $k => $v) {
- if (!is_string($k)) {
- throw new Exception($loc . ': Invalid language code: ' . var_export($k, TRUE));
- }
- if (!is_string($v)) {
- throw new Exception($loc . '[' . var_export($v, TRUE) . ']: Must be a string.');
- }
- }
-
- return $ret;
- }
-
- /**
- * Get public key from metadata.
- *
- * @param string|NULL $use The purpose this key can be used for. (encryption or signing).
- * @param bool $required Whether the public key is required. If this is TRUE, a
- * missing key will cause an exception. Default is FALSE.
- * @param string $prefix The prefix which should be used when reading from the metadata
- * array. Defaults to ''.
- * @return array|NULL Public key data, or NULL if no public key or was found.
- */
- public function getPublicKeys($use = NULL, $required = FALSE, $prefix = '') {
- assert('is_bool($required)');
- assert('is_string($prefix)');
-
- if ($this->hasValue($prefix . 'keys')) {
- $ret = array();
- foreach ($this->getArray($prefix . 'keys') as $key) {
- if ($use !== NULL && isset($key[$use]) && !$key[$use]) {
- continue;
- }
- if (isset($key['X509Certificate'])) {
- /* Strip whitespace from key. */
- $key['X509Certificate'] = preg_replace('/\s+/', '', $key['X509Certificate']);
- }
- $ret[] = $key;
-
- }
- if (!empty($ret)) {
- return $ret;
- }
- } elseif ($this->hasValue($prefix . 'certData')) {
- $certData = $this->getString($prefix . 'certData');
- $certData = preg_replace('/\s+/', '', $certData);
- return array(
- array(
- 'encryption' => TRUE,
- 'signing' => TRUE,
- 'type' => 'X509Certificate',
- 'X509Certificate' => $certData,
- ),
- );
- } elseif ($this->hasValue($prefix . 'certificate')) {
- $file = $this->getString($prefix . 'certificate');
- $file = \SimpleSAML\Utils\Config::getCertPath($file);
- $data = @file_get_contents($file);
-
- if ($data === FALSE) {
- throw new Exception($this->location . ': Unable to load certificate/public key from file "' . $file . '".');
- }
-
- /* Extract certificate data (if this is a certificate). */
- $pattern = '/^-----BEGIN CERTIFICATE-----([^-]*)^-----END CERTIFICATE-----/m';
- if (!preg_match($pattern, $data, $matches)) {
- throw new SimpleSAML_Error_Exception($this->location . ': Could not find PEM encoded certificate in "' . $file . '".');
- }
- $certData = preg_replace('/\s+/', '', $matches[1]);
-
- return array(
- array(
- 'encryption' => TRUE,
- 'signing' => TRUE,
- 'type' => 'X509Certificate',
- 'X509Certificate' => $certData,
- ),
- );
- }
-
- if ($required) {
- throw new SimpleSAML_Error_Exception($this->location . ': Missing certificate in metadata.');
- } else {
- return NULL;
- }
- }
-
+class SimpleSAML_Configuration
+{
+
+ /**
+ * A default value which means that the given option is required.
+ *
+ * @var string
+ */
+ const REQUIRED_OPTION = '___REQUIRED_OPTION___';
+
+
+ /**
+ * Associative array with mappings from instance-names to configuration objects.
+ *
+ * @var array
+ */
+ private static $instance = array();
+
+
+ /**
+ * Configuration directories.
+ *
+ * This associative array contains the mappings from configuration sets to
+ * configuration directories.
+ *
+ * @var array
+ */
+ private static $configDirs = array();
+
+
+ /**
+ * Cache of loaded configuration files.
+ *
+ * The index in the array is the full path to the file.
+ *
+ * @var array
+ */
+ private static $loadedConfigs = array();
+
+
+ /**
+ * The configuration array.
+ *
+ * @var array
+ */
+ private $configuration;
+
+
+ /**
+ * The location which will be given when an error occurs.
+ *
+ * @var string
+ */
+ private $location;
+
+
+ /**
+ * The file this configuration was loaded from.
+ *
+ * @var string|null
+ */
+ private $filename = null;
+
+
+ /**
+ * Initializes a configuration from the given array.
+ *
+ * @param array $config The configuration array.
+ * @param string $location The location which will be given when an error occurs.
+ */
+ public function __construct($config, $location)
+ {
+ assert('is_array($config)');
+ assert('is_string($location)');
+
+ $this->configuration = $config;
+ $this->location = $location;
+ }
+
+
+ /**
+ * Load the given configuration file.
+ *
+ * @param string $filename The full path of the configuration file.
+ * @param bool $required Whether the file is required.
+ *
+ * @return SimpleSAML_Configuration The configuration file. An exception will be thrown if the
+ * configuration file is missing.
+ *
+ * @throws Exception If the configuration file is invalid or missing.
+ */
+ private static function loadFromFile($filename, $required)
+ {
+ assert('is_string($filename)');
+ assert('is_bool($required)');
+
+ if (array_key_exists($filename, self::$loadedConfigs)) {
+ return self::$loadedConfigs[$filename];
+ }
+
+ if (file_exists($filename)) {
+ $config = 'UNINITIALIZED';
+
+ // the file initializes a variable named '$config'
+ require($filename);
+
+ // check that $config is initialized to an array
+ if (!is_array($config)) {
+ throw new Exception('Invalid configuration file: '.$filename);
+ }
+ } elseif ($required) {
+ // file does not exist, but is required
+ throw new Exception('Missing configuration file: '.$filename);
+ } else {
+ // file does not exist, but is optional
+ $config = array();
+ }
+
+ $cfg = new SimpleSAML_Configuration($config, $filename);
+ $cfg->filename = $filename;
+
+ self::$loadedConfigs[$filename] = $cfg;
+
+ return $cfg;
+ }
+
+
+ /**
+ * Set the directory for configuration files for the given configuration set.
+ *
+ * @param string $path The directory which contains the configuration files.
+ * @param string $configSet The configuration set. Defaults to 'simplesaml'.
+ */
+ public static function setConfigDir($path, $configSet = 'simplesaml')
+ {
+ assert('is_string($path)');
+ assert('is_string($configSet)');
+
+ self::$configDirs[$configSet] = $path;
+ }
+
+
+ /**
+ * Load a configuration file from a configuration set.
+ *
+ * @param string $filename The name of the configuration file.
+ * @param string $configSet The configuration set. Optional, defaults to 'simplesaml'.
+ *
+ * @return SimpleSAML_Configuration The SimpleSAML_Configuration object.
+ * @throws Exception If the configuration set is not initialized.
+ */
+ public static function getConfig($filename = 'config.php', $configSet = 'simplesaml')
+ {
+ assert('is_string($filename)');
+ assert('is_string($configSet)');
+
+ if (!array_key_exists($configSet, self::$configDirs)) {
+ if ($configSet !== 'simplesaml') {
+ throw new Exception('Configuration set \''.$configSet.'\' not initialized.');
+ } else {
+ self::$configDirs['simplesaml'] = SimpleSAML\Utils\Config::getConfigDir();
+ }
+ }
+
+ $dir = self::$configDirs[$configSet];
+ $filePath = $dir.'/'.$filename;
+ return self::loadFromFile($filePath, true);
+ }
+
+
+ /**
+ * Load a configuration file from a configuration set.
+ *
+ * This function will return a configuration object even if the file does not exist.
+ *
+ * @param string $filename The name of the configuration file.
+ * @param string $configSet The configuration set. Optional, defaults to 'simplesaml'.
+ *
+ * @return SimpleSAML_Configuration A configuration object.
+ * @throws Exception If the configuration set is not initialized.
+ */
+ public static function getOptionalConfig($filename = 'config.php', $configSet = 'simplesaml')
+ {
+ assert('is_string($filename)');
+ assert('is_string($configSet)');
+
+ if (!array_key_exists($configSet, self::$configDirs)) {
+ if ($configSet !== 'simplesaml') {
+ throw new Exception('Configuration set \''.$configSet.'\' not initialized.');
+ } else {
+ self::$configDirs['simplesaml'] = SimpleSAML\Utils\Config::getConfigDir();
+ }
+ }
+
+ $dir = self::$configDirs[$configSet];
+ $filePath = $dir.'/'.$filename;
+ return self::loadFromFile($filePath, false);
+ }
+
+
+ /**
+ * Loads a configuration from the given array.
+ *
+ * @param array $config The configuration array.
+ * @param string $location The location which will be given when an error occurs. Optional.
+ *
+ * @return SimpleSAML_Configuration The configuration object.
+ */
+ public static function loadFromArray($config, $location = '[ARRAY]')
+ {
+ assert('is_array($config)');
+ assert('is_string($location)');
+
+ return new SimpleSAML_Configuration($config, $location);
+ }
+
+
+ /**
+ * Get a configuration file by its instance name.
+ *
+ * This function retrieves a configuration file by its instance name. The instance
+ * name is initialized by the init function, or by copyFromBase function.
+ *
+ * If no configuration file with the given instance name is found, an exception will
+ * be thrown.
+ *
+ * @param string $instancename The instance name of the configuration file. Deprecated.
+ *
+ * @return SimpleSAML_Configuration The configuration object.
+ *
+ * @throws Exception If the configuration with $instancename name is not initialized.
+ */
+ public static function getInstance($instancename = 'simplesaml')
+ {
+ assert('is_string($instancename)');
+
+ if ($instancename === 'simplesaml') {
+ return self::getConfig();
+ }
+
+ if (!array_key_exists($instancename, self::$instance)) {
+ throw new Exception('Configuration with name '.$instancename.' is not initialized.');
+ }
+ return self::$instance[$instancename];
+ }
+
+
+ /**
+ * Initialize a instance name with the given configuration file.
+ *
+ * TODO: remove.
+ *
+ * @param string $path
+ * @param string $instancename
+ * @param string $configfilename
+ *
+ * @see setConfigDir()
+ * @deprecated This function is superseeded by the setConfigDir function.
+ */
+ public static function init($path, $instancename = 'simplesaml', $configfilename = 'config.php')
+ {
+ assert('is_string($path)');
+ assert('is_string($instancename)');
+ assert('is_string($configfilename)');
+
+ if ($instancename === 'simplesaml') {
+ // for backwards compatibility
+ self::setConfigDir($path, 'simplesaml');
+ }
+
+ // check if we already have loaded the given config - return the existing instance if we have
+ if (array_key_exists($instancename, self::$instance)) {
+ return self::$instance[$instancename];
+ }
+
+ self::$instance[$instancename] = self::loadFromFile($path.'/'.$configfilename, true);
+ return self::$instance[$instancename];
+ }
+
+
+ /**
+ * Load a configuration file which is located in the same directory as this configuration file.
+ *
+ * TODO: remove.
+ *
+ * @param string $instancename
+ * @param string $filename
+ *
+ * @see getConfig()
+ * @deprecated This function is superseeded by the getConfig() function.
+ */
+ public function copyFromBase($instancename, $filename)
+ {
+ assert('is_string($instancename)');
+ assert('is_string($filename)');
+ assert('$this->filename !== NULL');
+
+ // check if we already have loaded the given config - return the existing instance if we have
+ if (array_key_exists($instancename, self::$instance)) {
+ return self::$instance[$instancename];
+ }
+
+ $dir = dirname($this->filename);
+
+ self::$instance[$instancename] = self::loadFromFile($dir.'/'.$filename, true);
+ return self::$instance[$instancename];
+ }
+
+
+ /**
+ * Retrieve the current version of SimpleSAMLphp.
+ *
+ * @return string
+ */
+ public function getVersion()
+ {
+ return 'trunk';
+ }
+
+
+ /**
+ * Retrieve a configuration option set in config.php.
+ *
+ * @param string $name Name of the configuration option.
+ * @param mixed $default Default value of the configuration option. This parameter will default to null if not
+ * specified. This can be set to SimpleSAML_Configuration::REQUIRED_OPTION, which will
+ * cause an exception to be thrown if the option isn't found.
+ *
+ * @return mixed The configuration option with name $name, or $default if the option was not found.
+ *
+ * @throws Exception If the required option cannot be retrieved.
+ */
+ public function getValue($name, $default = null)
+ {
+ // return the default value if the option is unset
+ if (!array_key_exists($name, $this->configuration)) {
+ if ($default === self::REQUIRED_OPTION) {
+ throw new Exception(
+ $this->location.': Could not retrieve the required option '.
+ var_export($name, true)
+ );
+ }
+ return $default;
+ }
+
+ return $this->configuration[$name];
+ }
+
+
+ /**
+ * Check whether an key in the configuration exists.
+ *
+ * @param string $name The key in the configuration to look for.
+ *
+ * @return boolean If the value is set in this configuration.
+ */
+ public function hasValue($name)
+ {
+ return array_key_exists($name, $this->configuration);
+ }
+
+
+ /**
+ * Check whether any key of the set given exists in the configuration.
+ *
+ * @param array $names An array of options to look for.
+ *
+ * @return boolean If any of the keys in $names exist in the configuration
+ */
+ public function hasValueOneOf($names)
+ {
+ foreach ($names as $name) {
+ if ($this->hasValue($name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Retrieve the absolute path of the SimpleSAMLphp installation, relative to the root of the website.
+ *
+ * For example: simplesaml/
+ *
+ * The path will always end with a '/' and never have a leading slash.
+ *
+ * @return string The absolute path relative to the root of the website.
+ *
+ * @throws SimpleSAML_Error_Exception If the format of 'baseurlpath' is incorrect.
+ */
+ public function getBaseURL()
+ {
+ $baseURL = $this->getString('baseurlpath', 'simplesaml/');
+
+ if (preg_match('/^\*(.*)$/D', $baseURL, $matches)) {
+ // deprecated behaviour, will be removed in the future
+ return \SimpleSAML\Utils\HTTP::getFirstPathElement(false).$matches[1];
+ }
+
+ if (preg_match('#^https?://[^/]*/(.*)$#', $baseURL, $matches)) {
+ // we have a full url, we need to strip the path
+ return $matches[1];
+ } elseif ($baseURL === '' || $baseURL === '/') {
+ // Root directory of site
+ return '';
+ } elseif (preg_match('#^/?([^/]?.*/)#D', $baseURL, $matches)) {
+ // local path only
+ return $matches[1];
+ } else {
+ // invalid format
+ throw new SimpleSAML_Error_Exception(
+ 'Incorrect format for option \'baseurlpath\'. Value is: "'.
+ $this->getString('baseurlpath', 'simplesaml/').'". Valid format is in the form'.
+ ' [(http|https)://(hostname|fqdn)[:port]]/[path/to/simplesaml/].'
+ );
+ }
+ }
+
+
+ /**
+ * This function resolves a path which may be relative to the SimpleSAMLphp base directory.
+ *
+ * The path will never end with a '/'.
+ *
+ * @param string|null $path The path we should resolve. This option may be null.
+ *
+ * @return string|null $path if $path is an absolute path, or $path prepended with the base directory of this
+ * SimpleSAMLphp installation. We will return NULL if $path is null.
+ */
+ public function resolvePath($path)
+ {
+ if ($path === null) {
+ return null;
+ }
+
+ assert('is_string($path)');
+
+ /* Prepend path with basedir if it doesn't start with a slash or a Windows drive letter (e.g. "C:\"). We assume
+ * getBaseDir ends with a slash.
+ */
+ if ($path[0] !== '/' &&
+ !(preg_match('@^[a-z]:[\\\\/]@i', $path, $matches) && is_dir($matches[0]))
+ ) {
+ $path = $this->getBaseDir().$path;
+ }
+
+ // remove trailing slashes
+ $path = rtrim($path, '/');
+
+ return $path;
+ }
+
+
+ /**
+ * Retrieve a path configuration option set in config.php.
+ *
+ * The function will always return an absolute path unless the option is not set. It will then return the default
+ * value.
+ *
+ * It checks if the value starts with a slash, and prefixes it with the value from getBaseDir if it doesn't.
+ *
+ * @param string $name Name of the configuration option.
+ * @param string|null $default Default value of the configuration option. This parameter will default to null if
+ * not specified.
+ *
+ * @return string|null The path configuration option with name $name, or $default if the option was not found.
+ */
+ public function getPathValue($name, $default = null)
+ {
+ // return the default value if the option is unset
+ if (!array_key_exists($name, $this->configuration)) {
+ $path = $default;
+ } else {
+ $path = $this->configuration[$name];
+ }
+
+ if ($path === null) {
+ return null;
+ }
+
+ return $this->resolvePath($path).'/';
+ }
+
+
+ /**
+ * Retrieve the base directory for this SimpleSAMLphp installation.
+ *
+ * This function first checks the 'basedir' configuration option. If this option is undefined or null, then we
+ * fall back to looking at the current filename.
+ *
+ * @return string The absolute path to the base directory for this SimpleSAMLphp installation. This path will
+ * always end with a slash.
+ */
+ public function getBaseDir()
+ {
+ // check if a directory is configured in the configuration file
+ $dir = $this->getString('basedir', null);
+ if ($dir !== null) {
+ // add trailing slash if it is missing
+ if (substr($dir, -1) !== '/') {
+ $dir .= '/';
+ }
+
+ return $dir;
+ }
+
+ // the directory wasn't set in the configuration file, path is <base directory>/lib/SimpleSAML/Configuration.php
+ $dir = __FILE__;
+ assert('basename($dir) === "Configuration.php"');
+
+ $dir = dirname($dir);
+ assert('basename($dir) === "SimpleSAML"');
+
+ $dir = dirname($dir);
+ assert('basename($dir) === "lib"');
+
+ $dir = dirname($dir);
+
+ /* Add trailing slash. */
+ $dir .= '/';
+
+ return $dir;
+ }
+
+
+ /**
+ * This function retrieves a boolean configuration option.
+ *
+ * An exception will be thrown if this option isn't a boolean, or if this option isn't found, and no default value
+ * is given.
+ *
+ * @param string $name The name of the option.
+ * @param mixed $default A default value which will be returned if the option isn't found. The option will be
+ * required if this parameter isn't given. The default value can be any value, including
+ * null.
+ *
+ * @return boolean|mixed The option with the given name, or $default if the option isn't found and $default is
+ * specified.
+ *
+ * @throws Exception If the option is not boolean.
+ */
+ public function getBoolean($name, $default = self::REQUIRED_OPTION)
+ {
+ assert('is_string($name)');
+
+ $ret = $this->getValue($name, $default);
+
+ if ($ret === $default) {
+ // the option wasn't found, or it matches the default value. In any case, return this value
+ return $ret;
+ }
+
+ if (!is_bool($ret)) {
+ throw new Exception(
+ $this->location.': The option '.var_export($name, true).
+ ' is not a valid boolean value.'
+ );
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * This function retrieves a string configuration option.
+ *
+ * An exception will be thrown if this option isn't a string, or if this option isn't found, and no default value
+ * is given.
+ *
+ * @param string $name The name of the option.
+ * @param mixed $default A default value which will be returned if the option isn't found. The option will be
+ * required if this parameter isn't given. The default value can be any value, including
+ * null.
+ *
+ * @return string|mixed The option with the given name, or $default if the option isn't found and $default is
+ * specified.
+ *
+ * @throws Exception If the option is not a string.
+ */
+ public function getString($name, $default = self::REQUIRED_OPTION)
+ {
+ assert('is_string($name)');
+
+ $ret = $this->getValue($name, $default);
+
+ if ($ret === $default) {
+ // the option wasn't found, or it matches the default value. In any case, return this value
+ return $ret;
+ }
+
+ if (!is_string($ret)) {
+ throw new Exception(
+ $this->location.': The option '.var_export($name, true).
+ ' is not a valid string value.'
+ );
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * This function retrieves an integer configuration option.
+ *
+ * An exception will be thrown if this option isn't an integer, or if this option isn't found, and no default value
+ * is given.
+ *
+ * @param string $name The name of the option.
+ * @param mixed $default A default value which will be returned if the option isn't found. The option will be
+ * required if this parameter isn't given. The default value can be any value, including
+ * null.
+ *
+ * @return int|mixed The option with the given name, or $default if the option isn't found and $default is
+ * specified.
+ *
+ * @throws Exception If the option is not an integer.
+ */
+ public function getInteger($name, $default = self::REQUIRED_OPTION)
+ {
+ assert('is_string($name)');
+
+ $ret = $this->getValue($name, $default);
+
+ if ($ret === $default) {
+ // the option wasn't found, or it matches the default value. In any case, return this value
+ return $ret;
+ }
+
+ if (!is_int($ret)) {
+ throw new Exception(
+ $this->location.': The option '.var_export($name, true).
+ ' is not a valid integer value.'
+ );
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * This function retrieves an integer configuration option where the value must be in the specified range.
+ *
+ * An exception will be thrown if:
+ * - the option isn't an integer
+ * - the option isn't found, and no default value is given
+ * - the value is outside of the allowed range
+ *
+ * @param string $name The name of the option.
+ * @param int $minimum The smallest value which is allowed.
+ * @param int $maximum The largest value which is allowed.
+ * @param mixed $default A default value which will be returned if the option isn't found. The option will be
+ * required if this parameter isn't given. The default value can be any value, including
+ * null.
+ *
+ * @return int|mixed The option with the given name, or $default if the option isn't found and $default is
+ * specified.
+ *
+ * @throws Exception If the option is not in the range specified.
+ */
+ public function getIntegerRange($name, $minimum, $maximum, $default = self::REQUIRED_OPTION)
+ {
+ assert('is_string($name)');
+ assert('is_int($minimum)');
+ assert('is_int($maximum)');
+
+ $ret = $this->getInteger($name, $default);
+
+ if ($ret === $default) {
+ // the option wasn't found, or it matches the default value. In any case, return this value
+ return $ret;
+ }
+
+ if ($ret < $minimum || $ret > $maximum) {
+ throw new Exception(
+ $this->location.': Value of option '.var_export($name, true).
+ ' is out of range. Value is '.$ret.', allowed range is ['
+ .$minimum.' - '.$maximum.']'
+ );
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * Retrieve a configuration option with one of the given values.
+ *
+ * This will check that the configuration option matches one of the given values. The match will use
+ * strict comparison. An exception will be thrown if it does not match.
+ *
+ * The option can be mandatory or optional. If no default value is given, it will be considered to be
+ * mandatory, and an exception will be thrown if it isn't provided. If a default value is given, it
+ * is considered to be optional, and the default value is returned. The default value is automatically
+ * included in the list of allowed values.
+ *
+ * @param string $name The name of the option.
+ * @param array $allowedValues The values the option is allowed to take, as an array.
+ * @param mixed $default The default value which will be returned if the option isn't found. If this parameter
+ * isn't given, the option will be considered to be mandatory. The default value can be
+ * any value, including null.
+ *
+ * @return mixed The option with the given name, or $default if the option isn't found adn $default is given.
+ *
+ * @throws Exception If the option does not have any of the allowed values.
+ */
+ public function getValueValidate($name, $allowedValues, $default = self::REQUIRED_OPTION)
+ {
+ assert('is_string($name)');
+ assert('is_array($allowedValues)');
+
+ $ret = $this->getValue($name, $default);
+ if ($ret === $default) {
+ // the option wasn't found, or it matches the default value. In any case, return this value
+ return $ret;
+ }
+
+ if (!in_array($ret, $allowedValues, true)) {
+ $strValues = array();
+ foreach ($allowedValues as $av) {
+ $strValues[] = var_export($av, true);
+ }
+ $strValues = implode(', ', $strValues);
+
+ throw new Exception(
+ $this->location.': Invalid value given for the option '.
+ var_export($name, true).'. It should have one of the following values: '.
+ $strValues.'; but it had the following value: '.var_export($ret, true)
+ );
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * This function retrieves an array configuration option.
+ *
+ * An exception will be thrown if this option isn't an array, or if this option isn't found, and no
+ * default value is given.
+ *
+ * @param string $name The name of the option.
+ * @param mixed $default A default value which will be returned if the option isn't found. The option will be
+ * required if this parameter isn't given. The default value can be any value, including
+ * null.
+ *
+ * @return array|mixed The option with the given name, or $default if the option isn't found and $default is
+ * specified.
+ *
+ * @throws Exception If the option is not an array.
+ */
+ public function getArray($name, $default = self::REQUIRED_OPTION)
+ {
+ assert('is_string($name)');
+
+ $ret = $this->getValue($name, $default);
+
+ if ($ret === $default) {
+ // the option wasn't found, or it matches the default value. In any case, return this value
+ return $ret;
+ }
+
+ if (!is_array($ret)) {
+ throw new Exception($this->location.': The option '.var_export($name, true).' is not an array.');
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * This function retrieves an array configuration option.
+ *
+ * If the configuration option isn't an array, it will be converted to an array.
+ *
+ * @param string $name The name of the option.
+ * @param mixed $default A default value which will be returned if the option isn't found. The option will be
+ * required if this parameter isn't given. The default value can be any value, including
+ * null.
+ *
+ * @return array The option with the given name, or $default if the option isn't found and $default is specified.
+ */
+ public function getArrayize($name, $default = self::REQUIRED_OPTION)
+ {
+ assert('is_string($name)');
+
+ $ret = $this->getValue($name, $default);
+
+ if ($ret === $default) {
+ // the option wasn't found, or it matches the default value. In any case, return this value
+ return $ret;
+ }
+
+ if (!is_array($ret)) {
+ $ret = array($ret);
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * This function retrieves a configuration option with a string or an array of strings.
+ *
+ * If the configuration option is a string, it will be converted to an array with a single string
+ *
+ * @param string $name The name of the option.
+ * @param mixed $default A default value which will be returned if the option isn't found. The option will be
+ * required if this parameter isn't given. The default value can be any value, including
+ * null.
+ *
+ * @return array The option with the given name, or $default if the option isn't found and $default is specified.
+ *
+ * @throws Exception If the option is not a string or an array of strings.
+ */
+ public function getArrayizeString($name, $default = self::REQUIRED_OPTION)
+ {
+ assert('is_string($name)');
+
+ $ret = $this->getArrayize($name, $default);
+
+ if ($ret === $default) {
+ // the option wasn't found, or it matches the default value. In any case, return this value
+ return $ret;
+ }
+
+ foreach ($ret as $value) {
+ if (!is_string($value)) {
+ throw new Exception(
+ $this->location.': The option '.var_export($name, true).
+ ' must be a string or an array of strings.'
+ );
+ }
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * Retrieve an array as a SimpleSAML_Configuration object.
+ *
+ * This function will load the value of an option into a SimpleSAML_Configuration object. The option must contain
+ * an array.
+ *
+ * An exception will be thrown if this option isn't an array, or if this option isn't found, and no default value
+ * is given.
+ *
+ * @param string $name The name of the option.
+ * @param mixed $default A default value which will be returned if the option isn't found. The option will be
+ * required if this parameter isn't given. The default value can be any value, including
+ * null.
+ *
+ * @return mixed The option with the given name, or $default if the option isn't found and $default is specified.
+ *
+ * @throws Exception If the option is not an array.
+ */
+ public function getConfigItem($name, $default = self::REQUIRED_OPTION)
+ {
+ assert('is_string($name)');
+
+ $ret = $this->getValue($name, $default);
+
+ if ($ret === $default) {
+ // the option wasn't found, or it matches the default value. In any case, return this value
+ return $ret;
+ }
+
+ if (!is_array($ret)) {
+ throw new Exception(
+ $this->location.': The option '.var_export($name, true).
+ ' is not an array.'
+ );
+ }
+
+ return self::loadFromArray($ret, $this->location.'['.var_export($name, true).']');
+ }
+
+
+ /**
+ * Retrieve an array of arrays as an array of SimpleSAML_Configuration objects.
+ *
+ * This function will retrieve an option containing an array of arrays, and create an array of
+ * SimpleSAML_Configuration objects from that array. The indexes in the new array will be the same as the original
+ * indexes, but the values will be SimpleSAML_Configuration objects.
+ *
+ * An exception will be thrown if this option isn't an array of arrays, or if this option isn't found, and no
+ * default value is given.
+ *
+ * @param string $name The name of the option.
+ * @param mixed $default A default value which will be returned if the option isn't found. The option will be
+ * required if this parameter isn't given. The default value can be any value, including
+ * null.
+ *
+ * @return mixed The option with the given name, or $default if the option isn't found and $default is specified.
+ *
+ * @throws Exception If the value of this element is not an array.
+ */
+ public function getConfigList($name, $default = self::REQUIRED_OPTION)
+ {
+ assert('is_string($name)');
+
+ $ret = $this->getValue($name, $default);
+
+ if ($ret === $default) {
+ // the option wasn't found, or it matches the default value. In any case, return this value
+ return $ret;
+ }
+
+ if (!is_array($ret)) {
+ throw new Exception(
+ $this->location.': The option '.var_export($name, true).
+ ' is not an array.'
+ );
+ }
+
+ $out = array();
+ foreach ($ret as $index => $config) {
+ $newLoc = $this->location.'['.var_export($name, true).']['.
+ var_export($index, true).']';
+ if (!is_array($config)) {
+ throw new Exception($newLoc.': The value of this element was expected to be an array.');
+ }
+ $out[$index] = self::loadFromArray($config, $newLoc);
+ }
+
+ return $out;
+ }
+
+
+ /**
+ * Retrieve list of options.
+ *
+ * This function returns the name of all options which are defined in this
+ * configuration file, as an array of strings.
+ *
+ * @return array Name of all options defined in this configuration file.
+ */
+ public function getOptions()
+ {
+ return array_keys($this->configuration);
+ }
+
+
+ /**
+ * Convert this configuration object back to an array.
+ *
+ * @return array An associative array with all configuration options and values.
+ */
+ public function toArray()
+ {
+ return $this->configuration;
+ }
+
+
+ /**
+ * Retrieve the default binding for the given endpoint type.
+ *
+ * This function combines the current metadata type (SAML 2 / SAML 1.1)
+ * with the endpoint type to determine which binding is the default.
+ *
+ * @param string $endpointType The endpoint type.
+ *
+ * @return string The default binding.
+ *
+ * @throws Exception If the default binding is missing for this endpoint type.
+ */
+ private function getDefaultBinding($endpointType)
+ {
+ assert('is_string($endpointType)');
+
+ $set = $this->getString('metadata-set');
+ switch ($set.':'.$endpointType) {
+ case 'saml20-idp-remote:SingleSignOnService':
+ case 'saml20-idp-remote:SingleLogoutService':
+ case 'saml20-sp-remote:SingleLogoutService':
+ return SAML2_Const::BINDING_HTTP_REDIRECT;
+ case 'saml20-sp-remote:AssertionConsumerService':
+ return SAML2_Const::BINDING_HTTP_POST;
+ case 'saml20-idp-remote:ArtifactResolutionService':
+ return SAML2_Const::BINDING_SOAP;
+ case 'shib13-idp-remote:SingleSignOnService':
+ return 'urn:mace:shibboleth:1.0:profiles:AuthnRequest';
+ case 'shib13-sp-remote:AssertionConsumerService':
+ return 'urn:oasis:names:tc:SAML:1.0:profiles:browser-post';
+ default:
+ throw new Exception('Missing default binding for '.$endpointType.' in '.$set);
+ }
+ }
+
+
+ /**
+ * Helper function for dealing with metadata endpoints.
+ *
+ * @param string $endpointType The endpoint type.
+ *
+ * @return array Array of endpoints of the given type.
+ *
+ * @throws Exception If any element of the configuration options for this endpoint type is incorrect.
+ */
+ public function getEndpoints($endpointType)
+ {
+ assert('is_string($endpointType)');
+
+ $loc = $this->location.'['.var_export($endpointType, true).']:';
+
+ if (!array_key_exists($endpointType, $this->configuration)) {
+ // no endpoints of the given type
+ return array();
+ }
+
+
+ $eps = $this->configuration[$endpointType];
+ if (is_string($eps)) {
+ // for backwards-compatibility
+ $eps = array($eps);
+ } elseif (!is_array($eps)) {
+ throw new Exception($loc.': Expected array or string.');
+ }
+
+
+ foreach ($eps as $i => &$ep) {
+ $iloc = $loc.'['.var_export($i, true).']';
+
+ if (is_string($ep)) {
+ // for backwards-compatibility
+ $ep = array(
+ 'Location' => $ep,
+ 'Binding' => $this->getDefaultBinding($endpointType),
+ );
+ $responseLocation = $this->getString($endpointType.'Response', null);
+ if ($responseLocation !== null) {
+ $ep['ResponseLocation'] = $responseLocation;
+ }
+ } elseif (!is_array($ep)) {
+ throw new Exception($iloc.': Expected a string or an array.');
+ }
+
+ if (!array_key_exists('Location', $ep)) {
+ throw new Exception($iloc.': Missing Location.');
+ }
+ if (!is_string($ep['Location'])) {
+ throw new Exception($iloc.': Location must be a string.');
+ }
+
+ if (!array_key_exists('Binding', $ep)) {
+ throw new Exception($iloc.': Missing Binding.');
+ }
+ if (!is_string($ep['Binding'])) {
+ throw new Exception($iloc.': Binding must be a string.');
+ }
+
+ if (array_key_exists('ResponseLocation', $ep)) {
+ if (!is_string($ep['ResponseLocation'])) {
+ throw new Exception($iloc.': ResponseLocation must be a string.');
+ }
+ }
+
+ if (array_key_exists('index', $ep)) {
+ if (!is_int($ep['index'])) {
+ throw new Exception($iloc.': index must be an integer.');
+ }
+ }
+ }
+
+ return $eps;
+ }
+
+
+ /**
+ * Find an endpoint of the given type, using a list of supported bindings as a way to prioritize.
+ *
+ * @param string $endpointType The endpoint type.
+ * @param array $bindings Sorted array of acceptable bindings.
+ * @param mixed $default The default value to return if no matching endpoint is found. If no default is provided,
+ * an exception will be thrown.
+ *
+ * @return array|null The default endpoint, or null if no acceptable endpoints are used.
+ *
+ * @throws Exception If no supported endpoint is found.
+ */
+ public function getEndpointPrioritizedByBinding($endpointType, array $bindings, $default = self::REQUIRED_OPTION)
+ {
+ assert('is_string($endpointType)');
+
+ $endpoints = $this->getEndpoints($endpointType);
+
+ foreach ($bindings as $binding) {
+ foreach ($endpoints as $ep) {
+ if ($ep['Binding'] === $binding) {
+ return $ep;
+ }
+ }
+ }
+
+ if ($default === self::REQUIRED_OPTION) {
+ $loc = $this->location.'['.var_export($endpointType, true).']:';
+ throw new Exception($loc.'Could not find a supported '.$endpointType.' endpoint.');
+ }
+
+ return $default;
+ }
+
+
+ /**
+ * Find the default endpoint of the given type.
+ *
+ * @param string $endpointType The endpoint type.
+ * @param array $bindings Array with acceptable bindings. Can be null if any binding is allowed.
+ * @param mixed $default The default value to return if no matching endpoint is found. If no default is provided,
+ * an exception will be thrown.
+ *
+ * @return array|null The default endpoint, or null if no acceptable endpoints are used.
+ *
+ * @throws Exception If no supported endpoint is found.
+ */
+ public function getDefaultEndpoint($endpointType, array $bindings = null, $default = self::REQUIRED_OPTION)
+ {
+ assert('is_string($endpointType)');
+
+ $endpoints = $this->getEndpoints($endpointType);
+
+ $defaultEndpoint = \SimpleSAML\Utils\Config\Metadata::getDefaultEndpoint($endpoints, $bindings);
+ if ($defaultEndpoint !== null) {
+ return $defaultEndpoint;
+ }
+
+ if ($default === self::REQUIRED_OPTION) {
+ $loc = $this->location.'['.var_export($endpointType, true).']:';
+ throw new Exception($loc.'Could not find a supported '.$endpointType.' endpoint.');
+ }
+
+ return $default;
+ }
+
+
+ /**
+ * Retrieve a string which may be localized into many languages.
+ *
+ * The default language returned is always 'en'.
+ *
+ * @param string $name The name of the option.
+ * @param mixed $default The default value. If no default is given, and the option isn't found, an exception will
+ * be thrown.
+ *
+ * @return array Associative array with language => string pairs.
+ *
+ * @throws Exception If the translation is not an array or a string, or its index or value are not strings.
+ */
+ public function getLocalizedString($name, $default = self::REQUIRED_OPTION)
+ {
+ assert('is_string($name)');
+
+ $ret = $this->getValue($name, $default);
+ if ($ret === $default) {
+ // the option wasn't found, or it matches the default value. In any case, return this value
+ return $ret;
+ }
+
+ $loc = $this->location.'['.var_export($name, true).']';
+
+ if (is_string($ret)) {
+ $ret = array('en' => $ret,);
+ }
+
+ if (!is_array($ret)) {
+ throw new Exception($loc.': Must be an array or a string.');
+ }
+
+ foreach ($ret as $k => $v) {
+ if (!is_string($k)) {
+ throw new Exception($loc.': Invalid language code: '.var_export($k, true));
+ }
+ if (!is_string($v)) {
+ throw new Exception($loc.'['.var_export($v, true).']: Must be a string.');
+ }
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * Get public key from metadata.
+ *
+ * @param string|null $use The purpose this key can be used for. (encryption or signing).
+ * @param bool $required Whether the public key is required. If this is true, a
+ * missing key will cause an exception. Default is false.
+ * @param string $prefix The prefix which should be used when reading from the metadata
+ * array. Defaults to ''.
+ *
+ * @return array|null Public key data, or null if no public key or was found.
+ *
+ * @throws Exception If the certificate or public key cannot be loaded from a file.
+ * @throws SimpleSAML_Error_Exception If the file does not contain a valid PEM-encoded certificate, or there is no
+ * certificate in the metadata.
+ */
+ public function getPublicKeys($use = null, $required = false, $prefix = '')
+ {
+ assert('is_bool($required)');
+ assert('is_string($prefix)');
+
+ if ($this->hasValue($prefix.'keys')) {
+ $ret = array();
+ foreach ($this->getArray($prefix.'keys') as $key) {
+ if ($use !== null && isset($key[$use]) && !$key[$use]) {
+ continue;
+ }
+ if (isset($key['X509Certificate'])) {
+ /* Strip whitespace from key. */
+ $key['X509Certificate'] = preg_replace('/\s+/', '', $key['X509Certificate']);
+ }
+ $ret[] = $key;
+ }
+ if (!empty($ret)) {
+ return $ret;
+ }
+ } elseif ($this->hasValue($prefix.'certData')) {
+ $certData = $this->getString($prefix.'certData');
+ $certData = preg_replace('/\s+/', '', $certData);
+ return array(
+ array(
+ 'encryption' => true,
+ 'signing' => true,
+ 'type' => 'X509Certificate',
+ 'X509Certificate' => $certData,
+ ),
+ );
+ } elseif ($this->hasValue($prefix.'certificate')) {
+ $file = $this->getString($prefix.'certificate');
+ $file = \SimpleSAML\Utils\Config::getCertPath($file);
+ $data = @file_get_contents($file);
+
+ if ($data === false) {
+ throw new Exception($this->location.': Unable to load certificate/public key from file "'.$file.'".');
+ }
+
+ // extract certificate data (if this is a certificate)
+ $pattern = '/^-----BEGIN CERTIFICATE-----([^-]*)^-----END CERTIFICATE-----/m';
+ if (!preg_match($pattern, $data, $matches)) {
+ throw new SimpleSAML_Error_Exception(
+ $this->location.': Could not find PEM encoded certificate in "'.$file.'".'
+ );
+ }
+ $certData = preg_replace('/\s+/', '', $matches[1]);
+
+ return array(
+ array(
+ 'encryption' => true,
+ 'signing' => true,
+ 'type' => 'X509Certificate',
+ 'X509Certificate' => $certData,
+ ),
+ );
+ }
+
+ if ($required) {
+ throw new SimpleSAML_Error_Exception($this->location.': Missing certificate in metadata.');
+ } else {
+ return null;
+ }
+ }
}
diff --git a/lib/SimpleSAML/Database.php b/lib/SimpleSAML/Database.php
new file mode 100644
index 0000000..e66288b
--- /dev/null
+++ b/lib/SimpleSAML/Database.php
@@ -0,0 +1,303 @@
+<?php
+namespace SimpleSAML;
+
+/**
+ * This file implements functions to read and write to a group of database
+ * servers.
+ *
+ * This database class supports a single database, or a master/slave
+ * configuration with as many defined slaves as a user would like.
+ *
+ * The goal of this class is to provide a single mechanism to connect to a database
+ * that can be reused by any component within SimpleSAMLphp including modules.
+ * When using this class, the global configuration should be passed here, but
+ * in the case of a module that has a good reason to use a different database,
+ * such as sqlauth, an alternative config file can be provided.
+ *
+ * @author Tyler Antonio, University of Alberta. <tantonio@ualberta.ca>
+ * @package SimpleSAMLphp
+ */
+
+class Database
+{
+
+ /**
+ * This variable holds the instance of the session - Singleton approach.
+ */
+ private static $instance = array();
+
+ /**
+ * PDO Object for the Master database server
+ */
+ private $dbMaster;
+
+ /**
+ * Array of PDO Objects for configured database
+ * slaves
+ */
+ private $dbSlaves = array();
+
+ /**
+ * Prefix to apply to the tables
+ */
+ private $tablePrefix;
+
+ /**
+ * Array with information on the last error occurred.
+ */
+ private $lastError;
+
+
+ /**
+ * Retrieves the current database instance. Will create a new one if there isn't an existing connection.
+ *
+ * @param \SimpleSAML_Configuration $altConfig Optional: Instance of a SimpleSAML_Configuration class
+ *
+ * @return \SimpleSAML\Database The shared database connection.
+ */
+ public static function getInstance($altConfig = null)
+ {
+ $config = ($altConfig) ? $altConfig : \SimpleSAML_Configuration::getInstance();
+ $instanceId = self::generateInstanceId($config);
+
+ // check if we already have initialized the session
+ if (isset(self::$instance[$instanceId])) {
+ return self::$instance[$instanceId];
+ }
+
+ // create a new session
+ self::$instance[$instanceId] = new Database($config);
+ return self::$instance[$instanceId];
+ }
+
+
+ /**
+ * Private constructor that restricts instantiation to getInstance().
+ *
+ * @param \SimpleSAML_Configuration $config Instance of the SimpleSAML_Configuration class
+ */
+ private function __construct($config)
+ {
+ $driverOptions = array();
+ if ($config->getBoolean('database.persistent', true)) {
+ $driverOptions = array(\PDO::ATTR_PERSISTENT => true);
+ }
+
+ // connect to the master
+ $this->dbMaster = $this->connect(
+ $config->getString('database.dsn'),
+ $config->getString('database.username', null),
+ $config->getString('database.password', null),
+ $driverOptions
+ );
+
+ // connect to any configured slaves
+ $slaves = $config->getArray('database.slaves', array());
+ if (count($slaves >= 1)) {
+ foreach ($slaves as $slave) {
+ array_push(
+ $this->dbSlaves,
+ $this->connect(
+ $slave['dsn'],
+ $slave['username'],
+ $slave['password'],
+ $driverOptions
+ )
+ );
+ }
+ }
+
+ $this->tablePrefix = $config->getString('database.prefix', '');
+ }
+
+
+ /**
+ * Generate an Instance ID based on the database
+ * configuration.
+ *
+ * @param \SimpleSAML_Configuration $config Configuration class
+ *
+ * @return string $instanceId
+ */
+ private static function generateInstanceId($config)
+ {
+ $assembledConfig = array(
+ 'master' => array(
+ 'database.dsn' => $config->getString('database.dsn'),
+ 'database.username' => $config->getString('database.username', null),
+ 'database.password' => $config->getString('database.password', null),
+ 'database.prefix' => $config->getString('database.prefix', ''),
+ 'database.persistent' => $config->getBoolean('database.persistent', false),
+ ),
+ 'slaves' => $config->getArray('database.slaves', array()),
+ );
+
+ return sha1(serialize($assembledConfig));
+ }
+
+
+ /**
+ * This function connects to a database.
+ *
+ * @param string $dsn Database connection string
+ * @param string $username SQL user
+ * @param string $password SQL password
+ * @param array $options PDO options
+ *
+ * @throws \Exception If an error happens while trying to connect to the database.
+ * @return \PDO object
+ */
+ private function connect($dsn, $username, $password, $options)
+ {
+ try {
+ $db = new \PDO($dsn, $username, $password, $options);
+ $db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
+
+ return $db;
+ } catch (\PDOException $e) {
+ throw new \Exception("Database error: ".$e->getMessage());
+ }
+ }
+
+
+ /**
+ * This function randomly selects a slave database server
+ * to query. In the event no slaves are configured, it
+ * will return the master.
+ *
+ * @return \PDO object
+ */
+ private function getSlave()
+ {
+ if (count($this->dbSlaves) > 0) {
+ $slaveId = rand(0, count($this->dbSlaves) - 1);
+ return $this->dbSlaves[$slaveId];
+ } else {
+ return $this->dbMaster;
+ }
+ }
+
+
+ /**
+ * This function simply applies the table prefix to a supplied table name.
+ *
+ * @param string $table Table to apply prefix to, if configured
+ *
+ * @return string Table with configured prefix
+ */
+ public function applyPrefix($table)
+ {
+ return $this->tablePrefix.$table;
+ }
+
+
+ /**
+ * This function queries the database
+ *
+ * @param \PDO $db PDO object to use
+ * @param string $stmt Prepared SQL statement
+ * @param array $params Parameters
+ *
+ * @throws \Exception If an error happens while trying to execute the query.
+ * @return \PDOStatement object
+ */
+ private function query($db, $stmt, $params)
+ {
+ assert('is_object($db)');
+ assert('is_string($stmt)');
+ assert('is_array($params)');
+
+ try {
+ $query = $db->prepare($stmt);
+
+ foreach ($params as $param => $value) {
+ if (is_array($value)) {
+ $query->bindValue(":$param", $value[0], ($value[1]) ? $value[1] : \PDO::PARAM_STR);
+ } else {
+ $query->bindValue(":$param", $value, \PDO::PARAM_STR);
+ }
+ }
+
+ $query->execute();
+
+ return $query;
+ } catch (\PDOException $e) {
+ $this->lastError = $db->errorInfo();
+ throw new \Exception("Database error: ".$e->getMessage());
+ }
+ }
+
+
+ /**
+ * This function queries the database without using a
+ * prepared statement.
+ *
+ * @param \PDO $db PDO object to use
+ * @param string $stmt Prepared SQL statement
+ *
+ * @throws \Exception If an error happens while trying to execute the query.
+ * @return \PDOStatement object
+ */
+ private function exec($db, $stmt)
+ {
+ assert('is_object($db)');
+ assert('is_string($stmt)');
+
+ try {
+ $query = $db->exec($stmt);
+
+ return $query;
+ } catch (\PDOException $e) {
+ $this->lastError = $db->errorInfo();
+ throw new \Exception("Database error: ".$e->getMessage());
+ }
+ }
+
+
+ /**
+ * This executes queries directly on the master.
+ *
+ * @param string $stmt Prepared SQL statement
+ * @param array $params Parameters
+ *
+ * @return \PDOStatement object
+ */
+ public function write($stmt, $params = array())
+ {
+ $db = $this->dbMaster;
+
+ if (is_array($params)) {
+ return $this->query($db, $stmt, $params);
+ } else {
+ return $this->exec($db, $stmt);
+ }
+ }
+
+
+ /**
+ * This executes queries on a database server
+ * that is determined by this::getSlave()
+ *
+ * @param string $stmt Prepared SQL statement
+ * @param array $params Parameters
+ *
+ * @return \PDOStatement object
+ */
+ public function read($stmt, $params = array())
+ {
+ $db = $this->getSlave();
+
+ return $this->query($db, $stmt, $params);
+ }
+
+
+ /**
+ * Return an array with information about the last operation performed in the database.
+ *
+ * @return array The array with error information.
+ */
+ public function getLastError()
+ {
+ return $this->lastError;
+ }
+}
diff --git a/lib/SimpleSAML/Error/Exception.php b/lib/SimpleSAML/Error/Exception.php
index 55689a2..2872b33 100644
--- a/lib/SimpleSAML/Error/Exception.php
+++ b/lib/SimpleSAML/Error/Exception.php
@@ -1,246 +1,258 @@
<?php
+
/**
- * Baseclass for simpleSAML Exceptions
+ * Base class for SimpleSAMLphp Exceptions
*
* This class tries to make sure that every exception is serializable.
*
* @author Thomas Graff <thomas.graff@uninett.no>
- * @package simpleSAMLphp_base
+ * @package SimpleSAMLphp
*/
-class SimpleSAML_Error_Exception extends Exception {
-
- /**
- * The backtrace for this exception.
- *
- * We need to save the backtrace, since we cannot rely on
- * serializing the Exception::trace-variable.
- *
- * @var string
- */
- private $backtrace;
-
-
- /**
- * The cause of this exception.
- *
- * @var SimpleSAML_Error_Exception
- */
- private $cause;
-
-
- /**
- * Constructor for this error.
- *
- * Note that the cause will be converted to a SimpleSAML_Error_UnserializableException
- * unless it is a subclass of SimpleSAML_Error_Exception.
- *
- * @param string $message Exception message
- * @param int $code Error code
- * @param Exception|NULL $cause The cause of this exception.
- */
- public function __construct($message, $code = 0, Exception $cause = NULL) {
- assert('is_string($message)');
- assert('is_int($code)');
-
- parent::__construct($message, $code);
-
- $this->initBacktrace($this);
-
- if ($cause !== NULL) {
- $this->cause = SimpleSAML_Error_Exception::fromException($cause);
- }
- }
-
-
- /**
- * Convert any exception into a SimpleSAML_Error_Exception.
- *
- * @param Exception $e The exception.
- * @return SimpleSAML_Error_Exception The new exception.
- */
- public static function fromException(Exception $e) {
-
- if ($e instanceof SimpleSAML_Error_Exception) {
- return $e;
- }
- return new SimpleSAML_Error_UnserializableException($e);
- }
-
-
- /**
- * Load the backtrace from the given exception.
- *
- * @param Exception $exception The exception we should fetch the backtrace from.
- */
- protected function initBacktrace(Exception $exception) {
-
- $this->backtrace = array();
-
- /* Position in the top function on the stack. */
- $pos = $exception->getFile() . ':' . $exception->getLine();
-
- foreach($exception->getTrace() as $t) {
-
- $function = $t['function'];
- if(array_key_exists('class', $t)) {
- $function = $t['class'] . '::' . $function;
- }
-
- $this->backtrace[] = $pos . ' (' . $function . ')';
-
- if(array_key_exists('file', $t)) {
- $pos = $t['file'] . ':' . $t['line'];
- } else {
- $pos = '[builtin]';
- }
- }
-
- $this->backtrace[] = $pos . ' (N/A)';
- }
-
-
- /**
- * Retrieve the backtrace.
- *
- * @return array An array where each function call is a single item.
- */
- public function getBacktrace() {
- return $this->backtrace;
- }
-
-
- /**
- * Retrieve the cause of this exception.
- *
- * @return SimpleSAML_Error_Exception|NULL The cause of this exception.
- */
- public function getCause() {
- return $this->cause;
- }
-
-
- /**
- * Retrieve the class of this exception.
- *
- * @return string The classname.
- */
- public function getClass() {
- return get_class($this);
- }
-
-
- /**
- * Format this exception for logging.
- *
- * Create an array with lines for logging.
- *
- * @return array Log lines which should be written out.
- */
- public function format() {
-
- $ret = array();
-
- $e = $this;
- do {
- $err = $e->getClass() . ': ' . $e->getMessage();
- if ($e === $this) {
- $ret[] = $err;
- } else {
- $ret[] = 'Caused by: ' . $err;
- }
-
- $ret[] = 'Backtrace:';
-
- $depth = count($e->backtrace);
- foreach ($e->backtrace as $i => $trace) {
- $ret[] = ($depth - $i - 1) . ' ' . $trace;
- }
-
- $e = $e->cause;
- } while ($e !== NULL);
-
- return $ret;
- }
-
-
- /**
- * Print the exception to the log with log level error.
- *
- * This function will write this exception to the log, including a full backtrace.
- */
- public function logError() {
-
- $lines = $this->format();
- foreach ($lines as $line) {
- SimpleSAML_Logger::error($line);
- }
- }
-
-
- /**
- * Print the exception to the log with log level warning.
- *
- * This function will write this exception to the log, including a full backtrace.
- */
- public function logWarning() {
-
- $lines = $this->format();
- foreach ($lines as $line) {
- SimpleSAML_Logger::warning($line);
- }
- }
-
-
- /**
- * Print the exception to the log with log level info.
- *
- * This function will write this exception to the log, including a full backtrace.
- */
- public function logInfo() {
-
- $lines = $this->format();
- foreach ($lines as $line) {
- SimpleSAML_Logger::debug($line);
- }
- }
-
-
- /**
- * Print the exception to the log with log level debug.
- *
- * This function will write this exception to the log, including a full backtrace.
- */
- public function logDebug() {
-
- $lines = $this->format();
- foreach ($lines as $line) {
- SimpleSAML_Logger::debug($line);
- }
- }
-
-
- /**
- * Function for serialization.
- *
- * This function builds a list of all variables which should be serialized.
- * It will serialize all variables except the Exception::trace variable.
- *
- * @return array Array with the variables which should be serialized.
- */
- public function __sleep() {
-
- $ret = array();
-
- $ret = array_keys((array)$this);
-
- foreach ($ret as $i => $e) {
- if ($e === "\0Exception\0trace") {
- unset($ret[$i]);
- }
- }
-
- return $ret;
- }
-
+class SimpleSAML_Error_Exception extends Exception
+{
+
+ /**
+ * The backtrace for this exception.
+ *
+ * We need to save the backtrace, since we cannot rely on
+ * serializing the Exception::trace-variable.
+ *
+ * @var array
+ */
+ private $backtrace;
+
+
+ /**
+ * The cause of this exception.
+ *
+ * @var SimpleSAML_Error_Exception
+ */
+ private $cause;
+
+
+ /**
+ * Constructor for this error.
+ *
+ * Note that the cause will be converted to a SimpleSAML_Error_UnserializableException unless it is a subclass of
+ * SimpleSAML_Error_Exception.
+ *
+ * @param string $message Exception message
+ * @param int $code Error code
+ * @param Exception|null $cause The cause of this exception.
+ */
+ public function __construct($message, $code = 0, Exception $cause = null)
+ {
+ assert('is_string($message)');
+ assert('is_int($code)');
+
+ parent::__construct($message, $code);
+
+ $this->initBacktrace($this);
+
+ if ($cause !== null) {
+ $this->cause = SimpleSAML_Error_Exception::fromException($cause);
+ }
+ }
+
+
+ /**
+ * Convert any exception into a SimpleSAML_Error_Exception.
+ *
+ * @param Exception $e The exception.
+ *
+ * @return SimpleSAML_Error_Exception The new exception.
+ */
+ public static function fromException(Exception $e)
+ {
+
+ if ($e instanceof SimpleSAML_Error_Exception) {
+ return $e;
+ }
+ return new SimpleSAML_Error_UnserializableException($e);
+ }
+
+
+ /**
+ * Load the backtrace from the given exception.
+ *
+ * @param Exception $exception The exception we should fetch the backtrace from.
+ */
+ protected function initBacktrace(Exception $exception)
+ {
+
+ $this->backtrace = array();
+
+ // position in the top function on the stack
+ $pos = $exception->getFile().':'.$exception->getLine();
+
+ foreach ($exception->getTrace() as $t) {
+
+ $function = $t['function'];
+ if (array_key_exists('class', $t)) {
+ $function = $t['class'].'::'.$function;
+ }
+
+ $this->backtrace[] = $pos.' ('.$function.')';
+
+ if (array_key_exists('file', $t)) {
+ $pos = $t['file'].':'.$t['line'];
+ } else {
+ $pos = '[builtin]';
+ }
+ }
+
+ $this->backtrace[] = $pos.' (N/A)';
+ }
+
+
+ /**
+ * Retrieve the backtrace.
+ *
+ * @return array An array where each function call is a single item.
+ */
+ public function getBacktrace()
+ {
+ return $this->backtrace;
+ }
+
+
+ /**
+ * Retrieve the cause of this exception.
+ *
+ * @return SimpleSAML_Error_Exception|null The cause of this exception.
+ */
+ public function getCause()
+ {
+ return $this->cause;
+ }
+
+
+ /**
+ * Retrieve the class of this exception.
+ *
+ * @return string The name of the class.
+ */
+ public function getClass()
+ {
+ return get_class($this);
+ }
+
+
+ /**
+ * Format this exception for logging.
+ *
+ * Create an array of lines for logging.
+ *
+ * @return array Log lines that should be written out.
+ */
+ public function format()
+ {
+
+ $ret = array();
+
+ $e = $this;
+ do {
+ $err = $e->getClass().': '.$e->getMessage();
+ if ($e === $this) {
+ $ret[] = $err;
+ } else {
+ $ret[] = 'Caused by: '.$err;
+ }
+
+ $ret[] = 'Backtrace:';
+
+ $depth = count($e->backtrace);
+ foreach ($e->backtrace as $i => $trace) {
+ $ret[] = ($depth - $i - 1).' '.$trace;
+ }
+
+ $e = $e->cause;
+ } while ($e !== null);
+
+ return $ret;
+ }
+
+
+ /**
+ * Print the exception to the log with log level error.
+ *
+ * This function will write this exception to the log, including a full backtrace.
+ */
+ public function logError()
+ {
+
+ $lines = $this->format();
+ foreach ($lines as $line) {
+ SimpleSAML_Logger::error($line);
+ }
+ }
+
+
+ /**
+ * Print the exception to the log with log level warning.
+ *
+ * This function will write this exception to the log, including a full backtrace.
+ */
+ public function logWarning()
+ {
+
+ $lines = $this->format();
+ foreach ($lines as $line) {
+ SimpleSAML_Logger::warning($line);
+ }
+ }
+
+
+ /**
+ * Print the exception to the log with log level info.
+ *
+ * This function will write this exception to the log, including a full backtrace.
+ */
+ public function logInfo()
+ {
+
+ $lines = $this->format();
+ foreach ($lines as $line) {
+ SimpleSAML_Logger::debug($line);
+ }
+ }
+
+
+ /**
+ * Print the exception to the log with log level debug.
+ *
+ * This function will write this exception to the log, including a full backtrace.
+ */
+ public function logDebug()
+ {
+
+ $lines = $this->format();
+ foreach ($lines as $line) {
+ SimpleSAML_Logger::debug($line);
+ }
+ }
+
+
+ /**
+ * Function for serialization.
+ *
+ * This function builds a list of all variables which should be serialized. It will serialize all variables except
+ * the Exception::trace variable.
+ *
+ * @return array Array with the variables that should be serialized.
+ */
+ public function __sleep()
+ {
+
+ $ret = array_keys((array) $this);
+
+ foreach ($ret as $i => $e) {
+ if ($e === "\0Exception\0trace") {
+ unset($ret[$i]);
+ }
+ }
+
+ return $ret;
+ }
}
diff --git a/lib/SimpleSAML/IdP.php b/lib/SimpleSAML/IdP.php
index 7ba6193..86cf72e 100644
--- a/lib/SimpleSAML/IdP.php
+++ b/lib/SimpleSAML/IdP.php
@@ -1,538 +1,565 @@
<?php
+
/**
* IdP class.
*
* This class implements the various functions used by IdP.
*
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-class SimpleSAML_IdP {
-
- /**
- * A cache for resolving IdP id's.
- *
- * @var array
- */
- private static $idpCache = array();
-
-
- /**
- * The identifier for this IdP.
- *
- * @var string
- */
- private $id;
-
-
- /**
- * The "association group" for this IdP.
- *
- * We use this to support cross-protocol logout until
- * we implement a cross-protocol IdP.
- *
- * @var string
- */
- private $associationGroup;
-
-
- /**
- * The configuration for this IdP.
- *
- * @var SimpleSAML_Configuration
- */
- private $config;
-
-
- /**
- * Our authsource.
- *
- * @var SimpleSAML_Auth_Simple
- */
- private $authSource;
-
-
- /**
- * Initialize an IdP.
- *
- * @param string $id The identifier of this IdP.
- */
- private function __construct($id) {
- assert('is_string($id)');
-
- $this->id = $id;
-
- $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
- $globalConfig = SimpleSAML_Configuration::getInstance();
-
- if (substr($id, 0, 6) === 'saml2:') {
- if (!$globalConfig->getBoolean('enable.saml20-idp', FALSE)) {
- throw new SimpleSAML_Error_Exception('enable.saml20-idp disabled in config.php.');
- }
- $this->config = $metadata->getMetaDataConfig(substr($id, 6), 'saml20-idp-hosted');
- } elseif (substr($id, 0, 6) === 'saml1:') {
- if (!$globalConfig->getBoolean('enable.shib13-idp', FALSE)) {
- throw new SimpleSAML_Error_Exception('enable.shib13-idp disabled in config.php.');
- }
- $this->config = $metadata->getMetaDataConfig(substr($id, 6), 'shib13-idp-hosted');
- } elseif (substr($id, 0, 5) === 'adfs:') {
- if (!$globalConfig->getBoolean('enable.adfs-idp', FALSE)) {
- throw new SimpleSAML_Error_Exception('enable.adfs-idp disabled in config.php.');
- }
- $this->config = $metadata->getMetaDataConfig(substr($id, 5), 'adfs-idp-hosted');
-
- try {
- /* This makes the ADFS IdP use the same SP associations as the SAML 2.0 IdP. */
- $saml2EntityId = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted');
- $this->associationGroup = 'saml2:' . $saml2EntityId;
-
- } catch (Exception $e) {
- /* Probably no SAML 2 IdP configured for this host. Ignore the error. */
- }
- } else {
- assert(FALSE);
- }
-
- if ($this->associationGroup === NULL) {
- $this->associationGroup = $this->id;
- }
-
-
- $auth = $this->config->getString('auth');
- if (SimpleSAML_Auth_Source::getById($auth) !== NULL) {
- $this->authSource = new SimpleSAML_Auth_Simple($auth);
- } else {
- throw new SimpleSAML_Error_Exception('No such "'.$auth.'" auth source found.');
- }
- }
-
-
- /**
- * Retrieve the ID of this IdP.
- *
- * @return string The ID of this IdP.
- */
- public function getId() {
- return $this->id;
- }
-
-
- /**
- * Retrieve an IdP by ID.
- *
- * @param string $id The identifier of the IdP.
- * @return SimpleSAML_IdP The IdP.
- */
- public static function getById($id) {
- assert('is_string($id)');
-
- if (isset(self::$idpCache[$id])) {
- return self::$idpCache[$id];
- }
-
- $idp = new self($id);
- self::$idpCache[$id] = $idp;
- return $idp;
- }
-
-
- /**
- * Retrieve the IdP "owning" the state.
- *
- * @param array &$state The state array.
- * @return SimpleSAML_IdP The IdP.
- */
- public static function getByState(array &$state) {
- assert('isset($state["core:IdP"])');
-
- return self::getById($state['core:IdP']);
- }
-
-
- /**
- * Retrieve the configuration for this IdP.
- *
- * @return SimpleSAML_Configuration The configuration object.
- */
- public function getConfig() {
-
- return $this->config;
- }
-
-
- /**
- * Get SP name.
- *
- * @param string $assocId The association identifier.
- * @return array|NULL The name of the SP, as an associative array of language=>text, or NULL if this isn't an SP.
- */
- public function getSPName($assocId) {
- assert('is_string($assocId)');
-
- $prefix = substr($assocId, 0, 4);
- $spEntityId = substr($assocId, strlen($prefix) + 1);
- $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
-
- if ($prefix === 'saml') {
- try {
- $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'saml20-sp-remote');
- } catch (Exception $e) {
- try {
- $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'shib13-sp-remote');
- } catch (Exception $e) {
- return NULL;
- }
- }
- } else if ($prefix === 'adfs') {
- $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'adfs-sp-remote');
- } else {
- return NULL;
- }
-
- if ($spMetadata->hasValue('name')) {
- return $spMetadata->getLocalizedString('name');
- } elseif ($spMetadata->hasValue('OrganizationDisplayName')) {
- return $spMetadata->getLocalizedString('OrganizationDisplayName');
- } else {
- return array('en' => $spEntityId);
- }
- }
-
-
- /**
- * Add an SP association.
- *
- * @param array The SP association.
- */
- public function addAssociation(array $association) {
- assert('isset($association["id"])');
- assert('isset($association["Handler"])');
-
- $association['core:IdP'] = $this->id;
-
- $session = SimpleSAML_Session::getSessionFromRequest();
- $session->addAssociation($this->associationGroup, $association);
- }
-
-
- /**
- * Retrieve list of SP associations.
- *
- * @return array List of SP associations.
- */
- public function getAssociations() {
-
- $session = SimpleSAML_Session::getSessionFromRequest();
- return $session->getAssociations($this->associationGroup);
- }
-
-
- /**
- * Remove an SP association.
- *
- * @param string $assocId The association id.
- */
- public function terminateAssociation($assocId) {
- assert('is_string($assocId)');
-
- $session = SimpleSAML_Session::getSessionFromRequest();
- $session->terminateAssociation($this->associationGroup, $assocId);
- }
-
-
- /**
- * Is the current user authenticated?
- *
- * @return bool TRUE if the user is authenticated, FALSE if not.
- */
- public function isAuthenticated() {
- return $this->authSource->isAuthenticated();
- }
-
-
- /**
- * Called after authproc has run.
- *
- * @param array $state The authentication request state array.
- */
- public static function postAuthProc(array $state) {
- assert('is_callable($state["Responder"])');
-
- if (isset($state['core:SP'])) {
- $session = SimpleSAML_Session::getSessionFromRequest();
- $session->setData('core:idp-ssotime', $state['core:IdP'] . ';' . $state['core:SP'],
- time(), SimpleSAML_Session::DATA_TIMEOUT_SESSION_END);
- }
-
- call_user_func($state['Responder'], $state);
- assert('FALSE');
- }
-
-
- /**
- * The user is authenticated.
- *
- * @param array $state The authentication request state arrray.
- */
- public static function postAuth(array $state) {
-
- $idp = SimpleSAML_IdP::getByState($state);
-
- if (!$idp->isAuthenticated()) {
- throw new SimpleSAML_Error_Exception('Not authenticated.');
- }
-
- $state['Attributes'] = $idp->authSource->getAttributes();
-
- if (isset($state['SPMetadata'])) {
- $spMetadata = $state['SPMetadata'];
- } else {
- $spMetadata = array();
- }
-
- if (isset($state['core:SP'])) {
- $session = SimpleSAML_Session::getSessionFromRequest();
- $previousSSOTime = $session->getData('core:idp-ssotime', $state['core:IdP'] . ';' . $state['core:SP']);
- if ($previousSSOTime !== NULL) {
- $state['PreviousSSOTimestamp'] = $previousSSOTime;
- }
- }
-
- $idpMetadata = $idp->getConfig()->toArray();
-
- $pc = new SimpleSAML_Auth_ProcessingChain($idpMetadata, $spMetadata, 'idp');
-
- $state['ReturnCall'] = array('SimpleSAML_IdP', 'postAuthProc');
- $state['Destination'] = $spMetadata;
- $state['Source'] = $idpMetadata;
-
- $pc->processState($state);
-
- self::postAuthProc($state);
- }
-
-
- /**
- * Authenticate the user.
- *
- * This function authenticates the user.
- *
- * @param array &$state The authentication request state.
- */
- private function authenticate(array &$state) {
-
- if (isset($state['isPassive']) && (bool)$state['isPassive']) {
- throw new SimpleSAML_Error_NoPassive('Passive authentication not supported.');
- }
-
- $this->authSource->login($state);
- }
-
-
- /**
- * Reuthenticate the user.
- *
- * This function reauthenticates an user with an existing session. This
- * gives the authentication source a chance to do additional work when
- * reauthenticating for SSO.
- *
- * Note: This function is not used when ForceAuthn=true.
- *
- * @param array &$state The authentication request state.
- */
- private function reauthenticate(array &$state) {
-
- $sourceImpl = $this->authSource->getAuthSource();
- if ($sourceImpl === NULL) {
- /* Backwards-compatibility with non-authsource IdP. */
- foreach ($this->authSource->getAuthDataArray() as $k => $v) {
- $state[$k] = $v;
- }
- return;
- }
-
- $sourceImpl->reauthenticate($state);
- }
-
-
- /**
- * Process authentication requests.
- *
- * @param array &$state The authentication request state.
- */
- public function handleAuthenticationRequest(array &$state) {
- assert('isset($state["Responder"])');
-
- $state['core:IdP'] = $this->id;
-
- if (isset($state['SPMetadata']['entityid'])) {
- $spEntityId = $state['SPMetadata']['entityid'];
- } elseif (isset($state['SPMetadata']['entityID'])) {
- $spEntityId = $state['SPMetadata']['entityID'];
- } else {
- $spEntityId = NULL;
- }
- $state['core:SP'] = $spEntityId;
-
- /* First, check whether we need to authenticate the user. */
- if (isset($state['ForceAuthn']) && (bool)$state['ForceAuthn']) {
- /* Force authentication is in effect. */
- $needAuth = TRUE;
- } else {
- $needAuth = !$this->isAuthenticated();
- }
-
- $state['IdPMetadata'] = $this->getConfig()->toArray();
- $state['ReturnCallback'] = array('SimpleSAML_IdP', 'postAuth');
-
- try {
- if ($needAuth) {
- $this->authenticate($state);
- assert('FALSE');
- } else {
- $this->reauthenticate($state);
- }
- $this->postAuth($state);
- } catch (SimpleSAML_Error_Exception $e) {
- SimpleSAML_Auth_State::throwException($state, $e);
- } catch (Exception $e) {
- $e = new SimpleSAML_Error_UnserializableException($e);
- SimpleSAML_Auth_State::throwException($state, $e);
- }
- }
-
-
- /**
- * Find the logout handler of this IdP.
- *
- * @return string The logout handler class.
- */
- public function getLogoutHandler() {
-
- /* Find the logout handler. */
- $logouttype = $this->getConfig()->getString('logouttype', 'traditional');
- switch ($logouttype) {
- case 'traditional':
- $handler = 'SimpleSAML_IdP_LogoutTraditional';
- break;
- case 'iframe':
- $handler = 'SimpleSAML_IdP_LogoutIFrame';
- break;
- default:
- throw new SimpleSAML_Error_Exception('Unknown logout handler: ' . var_export($logouttype, TRUE));
- }
-
- return new $handler($this);
-
- }
-
-
- /**
- * Finish the logout operation.
- *
- * This function will never return.
- *
- * @param array &$state The logout request state.
- */
- public function finishLogout(array &$state) {
- assert('isset($state["Responder"])');
-
- $idp = SimpleSAML_IdP::getByState($state);
- call_user_func($state['Responder'], $idp, $state);
- assert('FALSE');
- }
-
-
- /**
- * Process a logout request.
- *
- * This function will never return.
- *
- * @param array &$state The logout request state.
- * @param string|NULL $assocId The association we received the logout request from, or NULL if there was no association.
- */
- public function handleLogoutRequest(array &$state, $assocId) {
- assert('isset($state["Responder"])');
- assert('is_string($assocId) || is_null($assocId)');
-
- $state['core:IdP'] = $this->id;
- $state['core:TerminatedAssocId'] = $assocId;
-
- if ($assocId !== NULL) {
- $this->terminateAssociation($assocId);
- $session = SimpleSAML_Session::getSessionFromRequest();
- $session->deleteData('core:idp-ssotime', $this->id . ':' . $state['saml:SPEntityId']);
- }
-
- /* Terminate the local session. */
- $id = SimpleSAML_Auth_State::saveState($state, 'core:Logout:afterbridge');
- $returnTo = SimpleSAML_Module::getModuleURL('core/idp/resumelogout.php',
- array('id' => $id)
- );
-
- $this->authSource->logout($returnTo);
-
- $handler = $this->getLogoutHandler();
- $handler->startLogout($state, $assocId);
- assert('FALSE');
- }
-
-
- /**
- * Process a logout response.
- *
- * This function will never return.
- *
- * @param string $assocId The association that is terminated.
- * @param string|NULL $relayState The RelayState from the start of the logout.
- * @param SimpleSAML_Error_Exception|NULL $error The error that occurred during session termination (if any).
- */
- public function handleLogoutResponse($assocId, $relayState, SimpleSAML_Error_Exception $error = NULL) {
- assert('is_string($assocId)');
- assert('is_string($relayState) || is_null($relayState)');
-
- $session = SimpleSAML_Session::getSessionFromRequest();
- $session->deleteData('core:idp-ssotime', $this->id . ';' . substr($assocId, strpos($assocId, ':') +1));
-
- $handler = $this->getLogoutHandler();
- $handler->onResponse($assocId, $relayState, $error);
-
- assert('FALSE');
- }
-
-
- /**
- * Log out, then redirect to a URL.
- *
- * This function never returns.
- *
- * @param string $url The URL the user should be returned to after logout.
- */
- public function doLogoutRedirect($url) {
- assert('is_string($url)');
-
- $state = array(
- 'Responder' => array('SimpleSAML_IdP', 'finishLogoutRedirect'),
- 'core:Logout:URL' => $url,
- );
-
- $this->handleLogoutRequest($state, NULL);
- assert('FALSE');
- }
-
-
- /**
- * Redirect to a URL after logout.
- *
- * This function never returns.
- *
- * @param array &$state The logout state from doLogoutRedirect().
- */
- public static function finishLogoutRedirect(SimpleSAML_IdP $idp, array $state) {
- assert('isset($state["core:Logout:URL"])');
-
- \SimpleSAML\Utils\HTTP::redirectTrustedURL($state['core:Logout:URL']);
- assert('FALSE');
- }
-
+class SimpleSAML_IdP
+{
+
+ /**
+ * A cache for resolving IdP id's.
+ *
+ * @var array
+ */
+ private static $idpCache = array();
+
+
+ /**
+ * The identifier for this IdP.
+ *
+ * @var string
+ */
+ private $id;
+
+
+ /**
+ * The "association group" for this IdP.
+ *
+ * We use this to support cross-protocol logout until
+ * we implement a cross-protocol IdP.
+ *
+ * @var string
+ */
+ private $associationGroup;
+
+
+ /**
+ * The configuration for this IdP.
+ *
+ * @var SimpleSAML_Configuration
+ */
+ private $config;
+
+
+ /**
+ * Our authsource.
+ *
+ * @var SimpleSAML_Auth_Simple
+ */
+ private $authSource;
+
+
+ /**
+ * Initialize an IdP.
+ *
+ * @param string $id The identifier of this IdP.
+ *
+ * @throws SimpleSAML_Error_Exception If the IdP is disabled or no such auth source was found.
+ */
+ private function __construct($id)
+ {
+ assert('is_string($id)');
+
+ $this->id = $id;
+
+ $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+ $globalConfig = SimpleSAML_Configuration::getInstance();
+
+ if (substr($id, 0, 6) === 'saml2:') {
+ if (!$globalConfig->getBoolean('enable.saml20-idp', false)) {
+ throw new SimpleSAML_Error_Exception('enable.saml20-idp disabled in config.php.');
+ }
+ $this->config = $metadata->getMetaDataConfig(substr($id, 6), 'saml20-idp-hosted');
+ } elseif (substr($id, 0, 6) === 'saml1:') {
+ if (!$globalConfig->getBoolean('enable.shib13-idp', false)) {
+ throw new SimpleSAML_Error_Exception('enable.shib13-idp disabled in config.php.');
+ }
+ $this->config = $metadata->getMetaDataConfig(substr($id, 6), 'shib13-idp-hosted');
+ } elseif (substr($id, 0, 5) === 'adfs:') {
+ if (!$globalConfig->getBoolean('enable.adfs-idp', false)) {
+ throw new SimpleSAML_Error_Exception('enable.adfs-idp disabled in config.php.');
+ }
+ $this->config = $metadata->getMetaDataConfig(substr($id, 5), 'adfs-idp-hosted');
+
+ try {
+ // this makes the ADFS IdP use the same SP associations as the SAML 2.0 IdP
+ $saml2EntityId = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted');
+ $this->associationGroup = 'saml2:'.$saml2EntityId;
+ } catch (Exception $e) {
+ // probably no SAML 2 IdP configured for this host. Ignore the error
+ }
+ } else {
+ assert(false);
+ }
+
+ if ($this->associationGroup === null) {
+ $this->associationGroup = $this->id;
+ }
+
+ $auth = $this->config->getString('auth');
+ if (SimpleSAML_Auth_Source::getById($auth) !== null) {
+ $this->authSource = new SimpleSAML_Auth_Simple($auth);
+ } else {
+ throw new SimpleSAML_Error_Exception('No such "'.$auth.'" auth source found.');
+ }
+ }
+
+
+ /**
+ * Retrieve the ID of this IdP.
+ *
+ * @return string The ID of this IdP.
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+
+ /**
+ * Retrieve an IdP by ID.
+ *
+ * @param string $id The identifier of the IdP.
+ *
+ * @return SimpleSAML_IdP The IdP.
+ */
+ public static function getById($id)
+ {
+ assert('is_string($id)');
+
+ if (isset(self::$idpCache[$id])) {
+ return self::$idpCache[$id];
+ }
+
+ $idp = new self($id);
+ self::$idpCache[$id] = $idp;
+ return $idp;
+ }
+
+
+ /**
+ * Retrieve the IdP "owning" the state.
+ *
+ * @param array &$state The state array.
+ *
+ * @return SimpleSAML_IdP The IdP.
+ */
+ public static function getByState(array &$state)
+ {
+ assert('isset($state["core:IdP"])');
+
+ return self::getById($state['core:IdP']);
+ }
+
+
+ /**
+ * Retrieve the configuration for this IdP.
+ *
+ * @return SimpleSAML_Configuration The configuration object.
+ */
+ public function getConfig()
+ {
+ return $this->config;
+ }
+
+
+ /**
+ * Get SP name.
+ *
+ * @param string $assocId The association identifier.
+ *
+ * @return array|null The name of the SP, as an associative array of language => text, or null if this isn't an SP.
+ */
+ public function getSPName($assocId)
+ {
+ assert('is_string($assocId)');
+
+ $prefix = substr($assocId, 0, 4);
+ $spEntityId = substr($assocId, strlen($prefix) + 1);
+ $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+
+ if ($prefix === 'saml') {
+ try {
+ $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'saml20-sp-remote');
+ } catch (Exception $e) {
+ try {
+ $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'shib13-sp-remote');
+ } catch (Exception $e) {
+ return null;
+ }
+ }
+ } else {
+ if ($prefix === 'adfs') {
+ $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'adfs-sp-remote');
+ } else {
+ return null;
+ }
+ }
+
+ if ($spMetadata->hasValue('name')) {
+ return $spMetadata->getLocalizedString('name');
+ } elseif ($spMetadata->hasValue('OrganizationDisplayName')) {
+ return $spMetadata->getLocalizedString('OrganizationDisplayName');
+ } else {
+ return array('en' => $spEntityId);
+ }
+ }
+
+
+ /**
+ * Add an SP association.
+ *
+ * @param array $association The SP association.
+ */
+ public function addAssociation(array $association)
+ {
+ assert('isset($association["id"])');
+ assert('isset($association["Handler"])');
+
+ $association['core:IdP'] = $this->id;
+
+ $session = SimpleSAML_Session::getSessionFromRequest();
+ $session->addAssociation($this->associationGroup, $association);
+ }
+
+
+ /**
+ * Retrieve list of SP associations.
+ *
+ * @return array List of SP associations.
+ */
+ public function getAssociations()
+ {
+ $session = SimpleSAML_Session::getSessionFromRequest();
+ return $session->getAssociations($this->associationGroup);
+ }
+
+
+ /**
+ * Remove an SP association.
+ *
+ * @param string $assocId The association id.
+ */
+ public function terminateAssociation($assocId)
+ {
+ assert('is_string($assocId)');
+
+ $session = SimpleSAML_Session::getSessionFromRequest();
+ $session->terminateAssociation($this->associationGroup, $assocId);
+ }
+
+
+ /**
+ * Is the current user authenticated?
+ *
+ * @return boolean True if the user is authenticated, false otherwise.
+ */
+ public function isAuthenticated()
+ {
+ return $this->authSource->isAuthenticated();
+ }
+
+
+ /**
+ * Called after authproc has run.
+ *
+ * @param array $state The authentication request state array.
+ */
+ public static function postAuthProc(array $state)
+ {
+ assert('is_callable($state["Responder"])');
+
+ if (isset($state['core:SP'])) {
+ $session = SimpleSAML_Session::getSessionFromRequest();
+ $session->setData(
+ 'core:idp-ssotime',
+ $state['core:IdP'].';'.$state['core:SP'],
+ time(),
+ SimpleSAML_Session::DATA_TIMEOUT_SESSION_END
+ );
+ }
+
+ call_user_func($state['Responder'], $state);
+ assert('FALSE');
+ }
+
+
+ /**
+ * The user is authenticated.
+ *
+ * @param array $state The authentication request state array.
+ *
+ * @throws SimpleSAML_Error_Exception If we are not authenticated.
+ */
+ public static function postAuth(array $state)
+ {
+ $idp = SimpleSAML_IdP::getByState($state);
+
+ if (!$idp->isAuthenticated()) {
+ throw new SimpleSAML_Error_Exception('Not authenticated.');
+ }
+
+ $state['Attributes'] = $idp->authSource->getAttributes();
+
+ if (isset($state['SPMetadata'])) {
+ $spMetadata = $state['SPMetadata'];
+ } else {
+ $spMetadata = array();
+ }
+
+ if (isset($state['core:SP'])) {
+ $session = SimpleSAML_Session::getSessionFromRequest();
+ $previousSSOTime = $session->getData('core:idp-ssotime', $state['core:IdP'].';'.$state['core:SP']);
+ if ($previousSSOTime !== null) {
+ $state['PreviousSSOTimestamp'] = $previousSSOTime;
+ }
+ }
+
+ $idpMetadata = $idp->getConfig()->toArray();
+
+ $pc = new SimpleSAML_Auth_ProcessingChain($idpMetadata, $spMetadata, 'idp');
+
+ $state['ReturnCall'] = array('SimpleSAML_IdP', 'postAuthProc');
+ $state['Destination'] = $spMetadata;
+ $state['Source'] = $idpMetadata;
+
+ $pc->processState($state);
+
+ self::postAuthProc($state);
+ }
+
+
+ /**
+ * Authenticate the user.
+ *
+ * This function authenticates the user.
+ *
+ * @param array &$state The authentication request state.
+ *
+ * @throws SimpleSAML_Error_NoPassive If we were asked to do passive authentication.
+ */
+ private function authenticate(array &$state)
+ {
+ if (isset($state['isPassive']) && (bool) $state['isPassive']) {
+ throw new SimpleSAML_Error_NoPassive('Passive authentication not supported.');
+ }
+
+ $this->authSource->login($state);
+ }
+
+
+ /**
+ * Re-authenticate the user.
+ *
+ * This function re-authenticates an user with an existing session. This gives the authentication source a chance
+ * to do additional work when re-authenticating for SSO.
+ *
+ * Note: This function is not used when ForceAuthn=true.
+ *
+ * @param array &$state The authentication request state.
+ *
+ * @throws SimpleSAML_Error_Exception If there is no auth source defined for this IdP.
+ */
+ private function reauthenticate(array &$state)
+ {
+ $sourceImpl = $this->authSource->getAuthSource();
+ if ($sourceImpl === null) {
+ throw new SimpleSAML_Error_Exception('No such auth source defined.');
+ }
+
+ $sourceImpl->reauthenticate($state);
+ }
+
+
+ /**
+ * Process authentication requests.
+ *
+ * @param array &$state The authentication request state.
+ */
+ public function handleAuthenticationRequest(array &$state)
+ {
+ assert('isset($state["Responder"])');
+
+ $state['core:IdP'] = $this->id;
+
+ if (isset($state['SPMetadata']['entityid'])) {
+ $spEntityId = $state['SPMetadata']['entityid'];
+ } elseif (isset($state['SPMetadata']['entityID'])) {
+ $spEntityId = $state['SPMetadata']['entityID'];
+ } else {
+ $spEntityId = null;
+ }
+ $state['core:SP'] = $spEntityId;
+
+ // first, check whether we need to authenticate the user
+ if (isset($state['ForceAuthn']) && (bool) $state['ForceAuthn']) {
+ // force authentication is in effect
+ $needAuth = true;
+ } else {
+ $needAuth = !$this->isAuthenticated();
+ }
+
+ $state['IdPMetadata'] = $this->getConfig()->toArray();
+ $state['ReturnCallback'] = array('SimpleSAML_IdP', 'postAuth');
+
+ try {
+ if ($needAuth) {
+ $this->authenticate($state);
+ assert('FALSE');
+ } else {
+ $this->reauthenticate($state);
+ }
+ $this->postAuth($state);
+ } catch (SimpleSAML_Error_Exception $e) {
+ SimpleSAML_Auth_State::throwException($state, $e);
+ } catch (Exception $e) {
+ $e = new SimpleSAML_Error_UnserializableException($e);
+ SimpleSAML_Auth_State::throwException($state, $e);
+ }
+ }
+
+
+ /**
+ * Find the logout handler of this IdP.
+ *
+ * @return SimpleSAML_IdP_LogoutHandler The logout handler class.
+ *
+ * @throws SimpleSAML_Error_Exception If we cannot find a logout handler.
+ */
+ public function getLogoutHandler()
+ {
+ // find the logout handler
+ $logouttype = $this->getConfig()->getString('logouttype', 'traditional');
+ switch ($logouttype) {
+ case 'traditional':
+ $handler = 'SimpleSAML_IdP_LogoutTraditional';
+ break;
+ case 'iframe':
+ $handler = 'SimpleSAML_IdP_LogoutIFrame';
+ break;
+ default:
+ throw new SimpleSAML_Error_Exception('Unknown logout handler: '.var_export($logouttype, true));
+ }
+
+ return new $handler($this);
+ }
+
+
+ /**
+ * Finish the logout operation.
+ *
+ * This function will never return.
+ *
+ * @param array &$state The logout request state.
+ */
+ public function finishLogout(array &$state)
+ {
+ assert('isset($state["Responder"])');
+
+ $idp = SimpleSAML_IdP::getByState($state);
+ call_user_func($state['Responder'], $idp, $state);
+ assert('false');
+ }
+
+
+ /**
+ * Process a logout request.
+ *
+ * This function will never return.
+ *
+ * @param array &$state The logout request state.
+ * @param string|null $assocId The association we received the logout request from, or null if there was no
+ * association.
+ */
+ public function handleLogoutRequest(array &$state, $assocId)
+ {
+ assert('isset($state["Responder"])');
+ assert('is_string($assocId) || is_null($assocId)');
+
+ $state['core:IdP'] = $this->id;
+ $state['core:TerminatedAssocId'] = $assocId;
+
+ if ($assocId !== null) {
+ $this->terminateAssociation($assocId);
+ $session = SimpleSAML_Session::getSessionFromRequest();
+ $session->deleteData('core:idp-ssotime', $this->id.':'.$state['saml:SPEntityId']);
+ }
+
+ // terminate the local session
+ $id = SimpleSAML_Auth_State::saveState($state, 'core:Logout:afterbridge');
+ $returnTo = SimpleSAML_Module::getModuleURL('core/idp/resumelogout.php', array('id' => $id));
+
+ $this->authSource->logout($returnTo);
+
+ $handler = $this->getLogoutHandler();
+ $handler->startLogout($state, $assocId);
+ assert('false');
+ }
+
+
+ /**
+ * Process a logout response.
+ *
+ * This function will never return.
+ *
+ * @param string $assocId The association that is terminated.
+ * @param string|null $relayState The RelayState from the start of the logout.
+ * @param SimpleSAML_Error_Exception|null $error The error that occurred during session termination (if any).
+ */
+ public function handleLogoutResponse($assocId, $relayState, SimpleSAML_Error_Exception $error = null)
+ {
+ assert('is_string($assocId)');
+ assert('is_string($relayState) || is_null($relayState)');
+
+ $session = SimpleSAML_Session::getSessionFromRequest();
+ $session->deleteData('core:idp-ssotime', $this->id.';'.substr($assocId, strpos($assocId, ':') + 1));
+
+ $handler = $this->getLogoutHandler();
+ $handler->onResponse($assocId, $relayState, $error);
+
+ assert('false');
+ }
+
+
+ /**
+ * Log out, then redirect to a URL.
+ *
+ * This function never returns.
+ *
+ * @param string $url The URL the user should be returned to after logout.
+ */
+ public function doLogoutRedirect($url)
+ {
+ assert('is_string($url)');
+
+ $state = array(
+ 'Responder' => array('SimpleSAML_IdP', 'finishLogoutRedirect'),
+ 'core:Logout:URL' => $url,
+ );
+
+ $this->handleLogoutRequest($state, null);
+ assert('false');
+ }
+
+
+ /**
+ * Redirect to a URL after logout.
+ *
+ * This function never returns.
+ *
+ * @param SimpleSAML_IdP $idp Deprecated. Will be removed.
+ * @param array &$state The logout state from doLogoutRedirect().
+ */
+ public static function finishLogoutRedirect(SimpleSAML_IdP $idp, array $state)
+ {
+ assert('isset($state["core:Logout:URL"])');
+
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL($state['core:Logout:URL']);
+ assert('false');
+ }
}
diff --git a/lib/SimpleSAML/IdP/LogoutHandler.php b/lib/SimpleSAML/IdP/LogoutHandler.php
index b3e3317..a5ba929 100644
--- a/lib/SimpleSAML/IdP/LogoutHandler.php
+++ b/lib/SimpleSAML/IdP/LogoutHandler.php
@@ -1,55 +1,57 @@
<?php
+
/**
* Base class for logout handlers.
*
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-abstract class SimpleSAML_IdP_LogoutHandler {
-
- /**
- * The IdP we are logging out from.
- *
- * @var SimpleSAML_IdP
- */
- protected $idp;
-
-
- /**
- * Initialize this logout handler.
- *
- * @param SimpleSAML_IdP $idp The IdP we are logging out from.
- */
- public function __construct(SimpleSAML_IdP $idp) {
- $this->idp = $idp;
- }
-
-
- /**
- * Start a logout operation.
- *
- * This function must never return.
- *
- * @param array &$state The logout state.
- * @param string|NULL $assocId The association that started the logout.
- */
- abstract public function startLogout(array &$state, $assocId);
-
-
- /**
- * Handles responses to our logout requests.
- *
- * This function will never return.
- *
- * @param string $assocId The association that is terminated.
- * @param string|NULL $relayState The RelayState from the start of the logout.
- * @param SimpleSAML_Error_Exception|NULL $error The error that occurred during session termination (if any).
- */
- public function onResponse($assocId, $relayState, SimpleSAML_Error_Exception $error = NULL) {
- assert('is_string($assocId)');
- assert('is_string($relayState) || is_null($relayState)');
-
- /* Don't do anything by default. */
- }
-
+abstract class SimpleSAML_IdP_LogoutHandler
+{
+
+ /**
+ * The IdP we are logging out from.
+ *
+ * @var SimpleSAML_IdP
+ */
+ protected $idp;
+
+
+ /**
+ * Initialize this logout handler.
+ *
+ * @param SimpleSAML_IdP $idp The IdP we are logging out from.
+ */
+ public function __construct(SimpleSAML_IdP $idp)
+ {
+ $this->idp = $idp;
+ }
+
+
+ /**
+ * Start a logout operation.
+ *
+ * This function must never return.
+ *
+ * @param array &$state The logout state.
+ * @param string|null $assocId The association that started the logout.
+ */
+ abstract public function startLogout(array &$state, $assocId);
+
+
+ /**
+ * Handles responses to our logout requests.
+ *
+ * This function will never return.
+ *
+ * @param string $assocId The association that is terminated.
+ * @param string|null $relayState The RelayState from the start of the logout.
+ * @param SimpleSAML_Error_Exception|null $error The error that occurred during session termination (if any).
+ */
+ public function onResponse($assocId, $relayState, SimpleSAML_Error_Exception $error = null)
+ {
+ assert('is_string($assocId)');
+ assert('is_string($relayState) || is_null($relayState)');
+ // don't do anything by default
+ }
}
diff --git a/lib/SimpleSAML/IdP/LogoutIFrame.php b/lib/SimpleSAML/IdP/LogoutIFrame.php
index e7fdc6e..dd31d4c 100644
--- a/lib/SimpleSAML/IdP/LogoutIFrame.php
+++ b/lib/SimpleSAML/IdP/LogoutIFrame.php
@@ -1,93 +1,97 @@
<?php
+
/**
- * Class which handles iframe logout.
+ * Class that handles iframe logout.
*
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-class SimpleSAML_IdP_LogoutIFrame extends SimpleSAML_IdP_LogoutHandler {
+class SimpleSAML_IdP_LogoutIFrame extends SimpleSAML_IdP_LogoutHandler
+{
- /**
- * Start the logout operation.
- *
- * @param array &$state The logout state.
- * @param string|NULL $assocId The SP we are logging out from.
- */
- public function startLogout(array &$state, $assocId) {
- assert('is_string($assocId) || is_null($assocId)');
+ /**
+ * Start the logout operation.
+ *
+ * @param array &$state The logout state.
+ * @param string|null $assocId The SP we are logging out from.
+ */
+ public function startLogout(array &$state, $assocId)
+ {
+ assert('is_string($assocId) || is_null($assocId)');
- $associations = $this->idp->getAssociations();
+ $associations = $this->idp->getAssociations();
- if (count($associations) === 0) {
- $this->idp->finishLogout($state);
- }
+ if (count($associations) === 0) {
+ $this->idp->finishLogout($state);
+ }
- foreach ($associations as $id => &$association) {
- $idp = SimpleSAML_IdP::getByState($association);
- $association['core:Logout-IFrame:Name'] = $idp->getSPName($id);
- $association['core:Logout-IFrame:State'] = 'onhold';
- }
- $state['core:Logout-IFrame:Associations'] = $associations;
+ foreach ($associations as $id => &$association) {
+ $idp = SimpleSAML_IdP::getByState($association);
+ $association['core:Logout-IFrame:Name'] = $idp->getSPName($id);
+ $association['core:Logout-IFrame:State'] = 'onhold';
+ }
+ $state['core:Logout-IFrame:Associations'] = $associations;
- if (!is_null($assocId)) {
- $spName = $this->idp->getSPName($assocId);
- if ($spName === NULL) {
- $spName = array('en' => $assocId);
- }
+ if (!is_null($assocId)) {
+ $spName = $this->idp->getSPName($assocId);
+ if ($spName === null) {
+ $spName = array('en' => $assocId);
+ }
- $state['core:Logout-IFrame:From'] = $spName;
- } else {
- $state['core:Logout-IFrame:From'] = NULL;
- }
+ $state['core:Logout-IFrame:From'] = $spName;
+ } else {
+ $state['core:Logout-IFrame:From'] = null;
+ }
- $params = array(
- 'id' => SimpleSAML_Auth_State::saveState($state, 'core:Logout-IFrame'),
- );
- if (isset($state['core:Logout-IFrame:InitType'])) {
- $params['type'] = $state['core:Logout-IFrame:InitType'];
- }
+ $params = array(
+ 'id' => SimpleSAML_Auth_State::saveState($state, 'core:Logout-IFrame'),
+ );
+ if (isset($state['core:Logout-IFrame:InitType'])) {
+ $params['type'] = $state['core:Logout-IFrame:InitType'];
+ }
- $url = SimpleSAML_Module::getModuleURL('core/idp/logout-iframe.php', $params);
- \SimpleSAML\Utils\HTTP::redirectTrustedURL($url);
- }
+ $url = SimpleSAML_Module::getModuleURL('core/idp/logout-iframe.php', $params);
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL($url);
+ }
- /**
- * Continue the logout operation.
- *
- * This function will never return.
- *
- * @param string $assocId The association that is terminated.
- * @param string|NULL $relayState The RelayState from the start of the logout.
- * @param SimpleSAML_Error_Exception|NULL $error The error that occurred during session termination (if any).
- */
- public function onResponse($assocId, $relayState, SimpleSAML_Error_Exception $error = NULL) {
- assert('is_string($assocId)');
+ /**
+ * Continue the logout operation.
+ *
+ * This function will never return.
+ *
+ * @param string $assocId The association that is terminated.
+ * @param string|NULL $relayState The RelayState from the start of the logout.
+ * @param SimpleSAML_Error_Exception|null $error The error that occurred during session termination (if any).
+ */
+ public function onResponse($assocId, $relayState, SimpleSAML_Error_Exception $error = null)
+ {
+ assert('is_string($assocId)');
- $spId = sha1($assocId);
- $this->idp->terminateAssociation($assocId);
+ $spId = sha1($assocId);
+ $this->idp->terminateAssociation($assocId);
- echo('<!DOCTYPE html>
+ $header = <<<HEADER
+<!DOCTYPE html>
<html>
-<head>
-<title>Logout response from ' . htmlspecialchars(var_export($assocId, TRUE)) . '</title>
-<script>
-');
- if ($error) {
- $errorMsg = $error->getMessage();
- echo('window.parent.logoutFailed("' . $spId . '", "' . addslashes($errorMsg) . '");');
- } else {
- echo('window.parent.logoutCompleted("' . $spId . '");');
- }
- echo('
-</script>
-</head>
-<body>
-</body>
+ <head>
+ <title>Logout response from %s</title>
+ <script>
+HEADER;
+ printf($header, htmlspecialchars(var_export($assocId, true)));
+ if ($error) {
+ $errorMsg = $error->getMessage();
+ echo('window.parent.logoutFailed("'.$spId.'", "'.addslashes($errorMsg).'");');
+ } else {
+ echo('window.parent.logoutCompleted("'.$spId.'");');
+ }
+ echo <<<FOOTER
+ </script>
+ </head>
+ <body>
+ </body>
</html>
-');
-
- exit(0);
- }
-
+FOOTER;
+ exit(0);
+ }
}
diff --git a/lib/SimpleSAML/IdP/LogoutTraditional.php b/lib/SimpleSAML/IdP/LogoutTraditional.php
index 7632cab..d362067 100644
--- a/lib/SimpleSAML/IdP/LogoutTraditional.php
+++ b/lib/SimpleSAML/IdP/LogoutTraditional.php
@@ -1,92 +1,96 @@
<?php
+
/**
* Class which handles traditional logout.
*
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-class SimpleSAML_IdP_LogoutTraditional extends SimpleSAML_IdP_LogoutHandler {
-
- /**
- * Picks the next SP and issues a logout request.
- *
- * This function never returns.
- *
- * @param array &$state The logout state.
- */
- private function logoutNextSP(array &$state) {
-
- $association = array_pop($state['core:LogoutTraditional:Remaining']);
- if ($association === NULL) {
- $this->idp->finishLogout($state);
- }
-
- $relayState = SimpleSAML_Auth_State::saveState($state, 'core:LogoutTraditional', TRUE);
-
- $id = $association['id'];
- SimpleSAML_Logger::info('Logging out of ' . var_export($id, TRUE) . '.');
-
- try {
- $idp = SimpleSAML_IdP::getByState($association);
- $url = call_user_func(array($association['Handler'], 'getLogoutURL'), $idp, $association, $relayState);
- \SimpleSAML\Utils\HTTP::redirectTrustedURL($url);
- } catch (Exception $e) {
- SimpleSAML_Logger::warning('Unable to initialize logout to ' . var_export($id, TRUE) . '.');
- $this->idp->terminateAssociation($id);
- $state['core:Failed'] = TRUE;
-
- /* Try the next SP. */
- $this->logoutNextSP($state);
- assert('FALSE');
- }
- }
-
-
- /**
- * Start the logout operation.
- *
- * This function never returns.
- *
- * @param array &$state The logout state.
- * @param string $assocId The association that started the logout.
- */
- public function startLogout(array &$state, $assocId) {
-
- $state['core:LogoutTraditional:Remaining'] = $this->idp->getAssociations();
-
- self::logoutNextSP($state);
- }
-
-
- /**
- * Continue the logout operation.
- *
- * This function will never return.
- *
- * @param string $assocId The association that is terminated.
- * @param string|NULL $relayState The RelayState from the start of the logout.
- * @param SimpleSAML_Error_Exception|NULL $error The error that occurred during session termination (if any).
- */
- public function onResponse($assocId, $relayState, SimpleSAML_Error_Exception $error = NULL) {
- assert('is_string($assocId)');
- assert('is_string($relayState) || is_null($relayState)');
-
- if ($relayState === NULL) {
- throw new SimpleSAML_Error_Exception('RelayState lost during logout.');
- }
-
- $state = SimpleSAML_Auth_State::loadState($relayState, 'core:LogoutTraditional');
-
- if ($error === NULL) {
- SimpleSAML_Logger::info('Logged out of ' . var_export($assocId, TRUE) . '.');
- $this->idp->terminateAssociation($assocId);
- } else {
- SimpleSAML_Logger::warning('Error received from ' . var_export($assocId, TRUE) . ' during logout:');
- $error->logWarning();
- $state['core:Failed'] = TRUE;
- }
-
- self::logoutNextSP($state);
- }
-
+class SimpleSAML_IdP_LogoutTraditional extends SimpleSAML_IdP_LogoutHandler
+{
+
+ /**
+ * Picks the next SP and issues a logout request.
+ *
+ * This function never returns.
+ *
+ * @param array &$state The logout state.
+ */
+ private function logoutNextSP(array &$state)
+ {
+ $association = array_pop($state['core:LogoutTraditional:Remaining']);
+ if ($association === null) {
+ $this->idp->finishLogout($state);
+ }
+
+ $relayState = SimpleSAML_Auth_State::saveState($state, 'core:LogoutTraditional', true);
+
+ $id = $association['id'];
+ SimpleSAML_Logger::info('Logging out of '.var_export($id, true).'.');
+
+ try {
+ $idp = SimpleSAML_IdP::getByState($association);
+ $url = call_user_func(array($association['Handler'], 'getLogoutURL'), $idp, $association, $relayState);
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL($url);
+ } catch (Exception $e) {
+ SimpleSAML_Logger::warning('Unable to initialize logout to '.var_export($id, true).'.');
+ $this->idp->terminateAssociation($id);
+ $state['core:Failed'] = true;
+
+ /* Try the next SP. */
+ $this->logoutNextSP($state);
+ assert('FALSE');
+ }
+ }
+
+
+ /**
+ * Start the logout operation.
+ *
+ * This function never returns.
+ *
+ * @param array &$state The logout state.
+ * @param string $assocId The association that started the logout.
+ */
+ public function startLogout(array &$state, $assocId)
+ {
+ $state['core:LogoutTraditional:Remaining'] = $this->idp->getAssociations();
+
+ self::logoutNextSP($state);
+ }
+
+
+ /**
+ * Continue the logout operation.
+ *
+ * This function will never return.
+ *
+ * @param string $assocId The association that is terminated.
+ * @param string|null $relayState The RelayState from the start of the logout.
+ * @param SimpleSAML_Error_Exception|null $error The error that occurred during session termination (if any).
+ *
+ * @throws SimpleSAML_Error_Exception If the RelayState was lost during logout.
+ */
+ public function onResponse($assocId, $relayState, SimpleSAML_Error_Exception $error = null)
+ {
+ assert('is_string($assocId)');
+ assert('is_string($relayState) || is_null($relayState)');
+
+ if ($relayState === null) {
+ throw new SimpleSAML_Error_Exception('RelayState lost during logout.');
+ }
+
+ $state = SimpleSAML_Auth_State::loadState($relayState, 'core:LogoutTraditional');
+
+ if ($error === null) {
+ SimpleSAML_Logger::info('Logged out of '.var_export($assocId, true).'.');
+ $this->idp->terminateAssociation($assocId);
+ } else {
+ SimpleSAML_Logger::warning('Error received from '.var_export($assocId, true).' during logout:');
+ $error->logWarning();
+ $state['core:Failed'] = true;
+ }
+
+ self::logoutNextSP($state);
+ }
}
diff --git a/lib/SimpleSAML/Logger.php b/lib/SimpleSAML/Logger.php
index 40c7980..ea20881 100644
--- a/lib/SimpleSAML/Logger.php
+++ b/lib/SimpleSAML/Logger.php
@@ -1,19 +1,35 @@
<?php
+
/**
* The main logger class for SimpleSAMLphp.
*
* @author Lasse Birnbaum Jensen, SDU.
* @author Andreas Åkre Solberg, UNINETT AS. <andreas.solberg@uninett.no>
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
* @version $ID$
*/
-
class SimpleSAML_Logger
{
- private static $loggingHandler = NULL;
- private static $logLevel = NULL;
- private static $captureLog = FALSE;
+
+ /**
+ * @var SimpleSAML_Logger_LoggingHandler|false|null
+ */
+ private static $loggingHandler = null;
+
+ /**
+ * @var integer|null
+ */
+ private static $logLevel = null;
+
+ /**
+ * @var boolean
+ */
+ private static $captureLog = false;
+
+ /**
+ * @var array
+ */
private static $capturedLog = array();
/**
@@ -34,7 +50,7 @@ class SimpleSAML_Logger
* This variable holds the track ID we have retrieved from the session class. It can also be NULL, in which case
* we haven't fetched the track ID yet, or TRACKID_FETCHING, which means that we are fetching the track ID now.
*/
- private static $trackid = NULL;
+ private static $trackid = null;
/**
* This variable holds the format used to log any message. Its use varies depending on the log handler used (for
@@ -129,6 +145,7 @@ class SimpleSAML_Logger
self::log(self::WARNING, $string);
}
+
/**
* We reserve the notice level for statistics, so do not use this level for other kind of log messages.
*
@@ -170,7 +187,7 @@ class SimpleSAML_Logger
*/
public static function stats($string)
{
- self::log(self::NOTICE, $string, TRUE);
+ self::log(self::NOTICE, $string, true);
}
@@ -179,7 +196,7 @@ class SimpleSAML_Logger
*
* @var boolean $val Whether to capture logs or not. Defaults to TRUE.
*/
- public static function setCaptureLog($val = TRUE)
+ public static function setCaptureLog($val = true)
{
self::$captureLog = $val;
}
@@ -197,7 +214,7 @@ class SimpleSAML_Logger
private static function createLoggingHandler()
{
// set to FALSE to indicate that it is being initialized
- self::$loggingHandler = FALSE;
+ self::$loggingHandler = false;
// get the configuration
$config = SimpleSAML_Configuration::getInstance();
@@ -219,7 +236,7 @@ class SimpleSAML_Logger
$sh = new SimpleSAML_Logger_LoggingHandlerErrorLog();
} else {
throw new Exception(
- 'Invalid value for the [logging.handler] configuration option. Unknown handler: ' . $handler
+ 'Invalid value for the [logging.handler] configuration option. Unknown handler: '.$handler
);
}
@@ -231,9 +248,14 @@ class SimpleSAML_Logger
}
- private static function log($level, $string, $statsLog = FALSE)
+ private static function log($level, $string, $statsLog = false)
{
- if (self::$loggingHandler === NULL) {
+ if (php_sapi_name() === 'cli' || defined('STDIN')) {
+ // we are being executed from the CLI, nowhere to log
+ return;
+ }
+
+ if (self::$loggingHandler === null) {
/* Initialize logging. */
self::createLoggingHandler();
@@ -244,7 +266,7 @@ class SimpleSAML_Logger
self::log($msg['level'], $msg['string'], $msg['statsLog']);
}
}
- } elseif (self::$loggingHandler === FALSE) {
+ } elseif (self::$loggingHandler === false) {
// some error occurred while initializing logging
if (empty(self::$earlyLog)) {
// this is the first message
@@ -257,7 +279,7 @@ class SimpleSAML_Logger
}
if (self::$captureLog) {
- $ts = microtime(TRUE);
+ $ts = microtime(true);
$msecs = (int) (($ts - (int) $ts) * 1000);
$ts = GMdate('H:i:s', $ts).sprintf('.%03d', $msecs).'Z';
self::$capturedLog[] = $ts.' '.$string;
@@ -296,7 +318,7 @@ class SimpleSAML_Logger
return 'NA';
}
- if (self::$trackid === NULL) {
+ if (self::$trackid === null) {
// no track ID yet, fetch it from the session class
// mark it as currently being fetched
diff --git a/lib/SimpleSAML/Memcache.php b/lib/SimpleSAML/Memcache.php
index 791f43a..94c439c 100644
--- a/lib/SimpleSAML/Memcache.php
+++ b/lib/SimpleSAML/Memcache.php
@@ -1,5 +1,6 @@
<?php
+
/**
* This file implements functions to read and write to a group of memcache
* servers.
@@ -14,419 +15,439 @@
* values will lead to incorrect behaviour.
*
* @author Olav Morken, UNINETT AS.
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-class SimpleSAML_Memcache {
-
- /**
- * Cache of the memcache servers we are using.
- */
- private static $serverGroups = NULL;
-
-
- /**
- * Find data stored with a given key.
- *
- * @param $key The key of the data.
- * @return The data stored with the given key, or NULL if no data matching the key was found.
- */
- public static function get($key) {
- SimpleSAML_Logger::debug("loading key $key from memcache");
-
- $latestInfo = NULL;
- $latestTime = 0.0;
- $latestData = NULL;
- $mustUpdate = FALSE;
-
- /* Search all the servers for the given id. */
- foreach(self::getMemcacheServers() as $server) {
- $serializedInfo = $server->get($key);
- if($serializedInfo === FALSE) {
- /* Either the server is down, or we don't have the value stored on that server. */
- $mustUpdate = TRUE;
- continue;
- }
-
- /* Unserialize the object. */
- $info = unserialize($serializedInfo);
-
- /*
- * Make sure that this is an array with two keys:
- * - 'timestamp': The time the data was saved.
- * - 'data': The data.
- */
- if(!is_array($info)) {
- SimpleSAML_Logger::warning('Retrieved invalid data from a memcache server.' .
- ' Data was not an array.');
- continue;
- }
- if(!array_key_exists('timestamp', $info)) {
- SimpleSAML_Logger::warning('Retrieved invalid data from a memcache server.' .
- ' Missing timestamp.');
- continue;
- }
- if(!array_key_exists('data', $info)) {
- SimpleSAML_Logger::warning('Retrieved invalid data from a memcache server.' .
- ' Missing data.');
- continue;
- }
-
- if($latestInfo === NULL) {
- /* First info found. */
- $latestInfo = $serializedInfo;
- $latestTime = $info['timestamp'];
- $latestData = $info['data'];
- continue;
- }
-
- if($info['timestamp'] === $latestTime && $serializedInfo === $latestInfo) {
- /* This data matches the data from the other server(s). */
- continue;
- }
-
- /*
- * Different data from different servers. We need to update at least one
- * of them to maintain synchronization.
- */
-
- $mustUpdate = TRUE;
-
- /* Update if data in $info is newer than $latestData. */
- if($latestTime < $info['timestamp']) {
- $latestInfo = $serializedInfo;
- $latestTime = $info['timestamp'];
- $latestData = $info['data'];
- }
- }
-
- if($latestData === NULL) {
- /* We didn't find any data matching the key. */
- SimpleSAML_Logger::debug("key $key not found in memcache");
- return NULL;
- }
-
- if($mustUpdate) {
- /* We found data matching the key, but some of the servers need updating. */
- SimpleSAML_Logger::debug("Memcache servers out of sync for $key, forcing sync");
- self::set($key, $latestData);
- }
-
- return $latestData;
- }
-
-
- /**
- * Save a key-value pair to the memcache servers.
- *
- * @param $key The key of the data.
- * @param $value The value of the data.
- * @param int|NULL $expire The expiration timestamp of the data.
- */
- public static function set($key, $value, $expire = NULL) {
- SimpleSAML_Logger::debug("saving key $key to memcache");
- $savedInfo = array(
- 'timestamp' => microtime(TRUE),
- 'data' => $value
- );
-
- if ($expire === NULL) {
- $expire = self::getExpireTime();
- }
-
- $savedInfoSerialized = serialize($savedInfo);
-
- /* Store this object to all groups of memcache servers. */
- foreach(self::getMemcacheServers() as $server) {
- $server->set($key, $savedInfoSerialized, 0, $expire);
- }
- }
-
-
- /**
- * Delete a key-value pair from the memcache servers.
- *
- * @param string $key The key we should delete.
- */
- public static function delete($key) {
- assert('is_string($key)');
- SimpleSAML_Logger::debug("deleting key $key from memcache");
-
- /* Store this object to all groups of memcache servers. */
- foreach(self::getMemcacheServers() as $server) {
- $server->delete($key);
- }
- }
-
-
- /**
- * This function adds a server from the 'memcache_store.servers'
- * configuration option to a Memcache object.
- *
- * The server parameter is an array with the following keys:
- * - hostname
- * Hostname or ip address to the memcache server.
- * - port (optional)
- * port number the memcache server is running on. This
- * defaults to memcache.default_port if no value is given.
- * The default value of memcache.default_port is 11211.
- * - weight (optional)
- * The weight of this server in the load balancing
- * cluster.
- * - timeout (optional)
- * The timeout for contacting this server, in seconds.
- * The default value is 3 seconds.
- *
- * @param $memcache The Memcache object we should add this server to.
- * @param $server The server we should add.
- */
- private static function addMemcacheServer($memcache, $server) {
-
- /* The hostname option is required. */
- if(!array_key_exists('hostname', $server)) {
- throw new Exception('hostname setting missing from server in the' .
- ' \'memcache_store.servers\' configuration option.');
- }
-
- $hostname = $server['hostname'];
-
- /* The hostname must be a valid string. */
- if(!is_string($hostname)) {
- throw new Exception('Invalid hostname for server in the' .
- ' \'memcache_store.servers\' configuration option. The hostname is' .
- ' supposed to be a string.');
- }
-
- /* Check if the user has specified a port number. */
- if(array_key_exists('port', $server)) {
- /* Get the port number from the array, and validate it. */
- $port = (int)$server['port'];
- if(($port <= 0) || ($port > 65535)) {
- throw new Exception('Invalid port for server in the' .
- ' \'memcache_store.servers\' configuration option. The port number' .
- ' is supposed to be an integer between 0 and 65535.');
- }
- } else {
- /* Use the default port number from the ini-file. */
- $port = (int)ini_get('memcache.default_port');
- if($port <= 0 || $port > 65535) {
- /* Invalid port number from the ini-file. fall back to the default. */
- $port = 11211;
- }
- }
-
- /* Check if the user has specified a weight for this server. */
- if(array_key_exists('weight', $server)) {
- /* Get the weight and validate it. */
- $weight = (int)$server['weight'];
- if($weight <= 0) {
- throw new Exception('Invalid weight for server in the' .
- ' \'memcache_store.servers\' configuration option. The weight is' .
- ' supposed to be a positive integer.');
- }
- } else {
- /* Use a default weight of 1. */
- $weight = 1;
- }
-
- /* Check if the user has specified a timeout for this server. */
- if(array_key_exists('timeout', $server)) {
- /* Get the timeout and validate it. */
- $timeout = (int)$server['timeout'];
- if($timeout <= 0) {
- throw new Exception('Invalid timeout for server in the' .
- ' \'memcache_store.servers\' configuration option. The timeout is' .
- ' supposed to be a positive integer.');
- }
- } else {
- /* Use a default timeout of 3 seconds. */
- $timeout = 3;
- }
-
- /* Add this server to the Memcache object. */
- $memcache->addServer($hostname, $port, TRUE, $weight, $timeout);
- }
-
-
- /**
- * This function takes in a list of servers belonging to a group and
- * creates a Memcache object from the servers in the group.
- *
- * @param array $group Array of servers which should be created as a group.
- * @return A Memcache object of the servers in the group.
- */
- private static function loadMemcacheServerGroup(array $group) {
-
- if(!class_exists('Memcache')) {
- throw new Exception('Missing Memcache class. Is the memcache extension installed?');
- }
-
- /* Create the Memcache object. */
- $memcache = new Memcache();
-
- /* Iterate over all the servers in the group and add them to the Memcache object. */
- foreach($group as $index => $server) {
- /*
- * Make sure that we don't have an index. An index would be a sign of invalid configuration.
- */
- if(!is_int($index)) {
- throw new Exception('Invalid index on element in the' .
- ' \'memcache_store.servers\' configuration option. Perhaps you' .
- ' have forgotten to add an array(...) around one of the server groups?' .
- ' The invalid index was: ' . $index);
- }
-
- /*
- * Make sure that the server object is an array. Each server is an array with
- * name-value pairs.
- */
- if(!is_array($server)) {
- throw new Exception('Invalid value for the server with index ' . $index .
- '. Remeber that the \'memcache_store.servers\' configuration option' .
- ' contains an array of arrays of arrays.');
- }
-
- self::addMemcacheServer($memcache, $server);
- }
-
- return $memcache;
- }
-
-
- /**
- * This function gets a list of all configured memcache servers. This list is initialized based
- * on the content of 'memcache_store.servers' in the configuration.
- *
- * @return Array with Memcache objects.
- */
- private static function getMemcacheServers() {
-
- /* Check if we have loaded the servers already. */
- if(self::$serverGroups != NULL) {
- return self::$serverGroups;
- }
-
- /* Initialize the servers-array. */
- self::$serverGroups = array();
-
- /* Load the configuration. */
- $config = SimpleSAML_Configuration::getInstance();
-
-
- $groups = $config->getArray('memcache_store.servers');
-
- /* Iterate over all the groups in the 'memcache_store.servers' configuration option. */
- foreach($groups as $index => $group) {
- /*
- * Make sure that the group doesn't have an index. An index would be a sign of
- * invalid configuration.
- */
- if(!is_int($index)) {
- throw new Exception('Invalid index on element in the \'memcache_store.servers\'' .
- ' configuration option. Perhaps you have forgotten to add an array(...)' .
- ' around one of the server groups? The invalid index was: ' . $index);
- }
-
- /*
- * Make sure that the group is an array. Each group is an array of servers. Each server is
- * an array of name => value pairs for that server.
- */
- if(!is_array($group)) {
- throw new Exception('Invalid value for the server with index ' . $index .
- '. Remeber that the \'memcache_store.servers\' configuration option' .
- ' contains an array of arrays of arrays.');
- }
-
- /* Parse and add this group to the server group list. */
- self::$serverGroups[] = self::loadMemcacheServerGroup($group);
- }
-
- return self::$serverGroups;
- }
-
-
- /**
- * This is a helper-function which returns the expire value of data
- * we should store to the memcache servers.
- *
- * The value is set depending on the configuration. If no value is
- * set in the configuration, then we will use a default value of 0.
- * 0 means that the item will never expire.
- *
- * @return The value which should be passed in the set(...) calls to the memcache objects.
- */
- private static function getExpireTime()
- {
- /* Get the configuration instance. */
- $config = SimpleSAML_Configuration::getInstance();
- assert($config instanceof SimpleSAML_Configuration);
-
- /* Get the expire-value from the configuration. */
- $expire = $config->getInteger('memcache_store.expires', 0);
-
- /* It must be a positive integer. */
- if($expire < 0) {
- throw new Exception('The value of \'memcache_store.expires\' in the' .
- ' configuration can\'t be a negative integer.');
- }
-
- /* If the configuration option is 0, then we should
- * return 0. This allows the user to specify that the data
- * shouldn't expire.
- */
- if($expire == 0) {
- return 0;
- }
-
- /* The expire option is given as the number of seconds into the
- * future an item should expire. We convert this to an actual
- * timestamp.
- */
- $expireTime = time() + $expire;
-
- return $expireTime;
- }
-
-
- /**
- * This function retrieves statistics about all memcache server groups.
- *
- * @return Array with the names of each stat and an array with the value for each
- * server group.
- */
- public static function getStats()
- {
- $ret = array();
-
- foreach(self::getMemcacheServers() as $sg) {
- $stats = $sg->getExtendedStats();
- if($stats === FALSE) {
- throw new Exception('Failed to get memcache server status.');
- }
-
- $stats = SimpleSAML\Utils\Arrays::transpose($stats);
-
- $ret = array_merge_recursive($ret, $stats);
- }
-
- return $ret;
- }
-
-
- /**
- * Retrieve statistics directly in the form returned by getExtendedStats, for
- * all server groups.
- *
- * @return Array with the extended stats output for each server group.
- */
- public static function getRawStats() {
- $ret = array();
-
- foreach(self::getMemcacheServers() as $sg) {
- $stats = $sg->getExtendedStats();
- $ret[] = $stats;
- }
-
- return $ret;
- }
+class SimpleSAML_Memcache
+{
+
+ /**
+ * Cache of the memcache servers we are using.
+ *
+ * @var Memcache[]|null
+ */
+ private static $serverGroups = null;
+
+
+ /**
+ * Find data stored with a given key.
+ *
+ * @param string $key The key of the data.
+ *
+ * @return mixed The data stored with the given key, or null if no data matching the key was found.
+ */
+ public static function get($key)
+ {
+ SimpleSAML_Logger::debug("loading key $key from memcache");
+
+ $latestInfo = null;
+ $latestTime = 0.0;
+ $latestData = null;
+ $mustUpdate = false;
+
+ // search all the servers for the given id
+ foreach (self::getMemcacheServers() as $server) {
+ $serializedInfo = $server->get($key);
+ if ($serializedInfo === false) {
+ // either the server is down, or we don't have the value stored on that server
+ $mustUpdate = true;
+ continue;
+ }
+
+ // unserialize the object
+ $info = unserialize($serializedInfo);
+
+ /*
+ * Make sure that this is an array with two keys:
+ * - 'timestamp': The time the data was saved.
+ * - 'data': The data.
+ */
+ if (!is_array($info)) {
+ SimpleSAML_Logger::warning(
+ 'Retrieved invalid data from a memcache server. Data was not an array.'
+ );
+ continue;
+ }
+ if (!array_key_exists('timestamp', $info)) {
+ SimpleSAML_Logger::warning(
+ 'Retrieved invalid data from a memcache server. Missing timestamp.'
+ );
+ continue;
+ }
+ if (!array_key_exists('data', $info)) {
+ SimpleSAML_Logger::warning(
+ 'Retrieved invalid data from a memcache server. Missing data.'
+ );
+ continue;
+ }
+
+ if ($latestInfo === null) {
+ // first info found
+ $latestInfo = $serializedInfo;
+ $latestTime = $info['timestamp'];
+ $latestData = $info['data'];
+ continue;
+ }
+
+ if ($info['timestamp'] === $latestTime && $serializedInfo === $latestInfo) {
+ // this data matches the data from the other server(s)
+ continue;
+ }
+
+ // different data from different servers. We need to update at least one of them to maintain sync.
+ $mustUpdate = true;
+
+ // update if data in $info is newer than $latestData
+ if ($latestTime < $info['timestamp']) {
+ $latestInfo = $serializedInfo;
+ $latestTime = $info['timestamp'];
+ $latestData = $info['data'];
+ }
+ }
+
+ if ($latestData === null) {
+ // we didn't find any data matching the key
+ SimpleSAML_Logger::debug("key $key not found in memcache");
+ return null;
+ }
+
+ if ($mustUpdate) {
+ // we found data matching the key, but some of the servers need updating
+ SimpleSAML_Logger::debug("Memcache servers out of sync for $key, forcing sync");
+ self::set($key, $latestData);
+ }
+
+ return $latestData;
+ }
+
+
+ /**
+ * Save a key-value pair to the memcache servers.
+ *
+ * @param string $key The key of the data.
+ * @param mixed $value The value of the data.
+ * @param integer|null $expire The expiration timestamp of the data.
+ */
+ public static function set($key, $value, $expire = null)
+ {
+ SimpleSAML_Logger::debug("saving key $key to memcache");
+ $savedInfo = array(
+ 'timestamp' => microtime(true),
+ 'data' => $value
+ );
+
+ if ($expire === null) {
+ $expire = self::getExpireTime();
+ }
+
+ $savedInfoSerialized = serialize($savedInfo);
+
+ // store this object to all groups of memcache servers
+ foreach (self::getMemcacheServers() as $server) {
+ $server->set($key, $savedInfoSerialized, 0, $expire);
+ }
+ }
+
+
+ /**
+ * Delete a key-value pair from the memcache servers.
+ *
+ * @param string $key The key we should delete.
+ */
+ public static function delete($key)
+ {
+ assert('is_string($key)');
+ SimpleSAML_Logger::debug("deleting key $key from memcache");
+
+ // store this object to all groups of memcache servers
+ foreach (self::getMemcacheServers() as $server) {
+ $server->delete($key);
+ }
+ }
+
+
+ /**
+ * This function adds a server from the 'memcache_store.servers'
+ * configuration option to a Memcache object.
+ *
+ * The server parameter is an array with the following keys:
+ * - hostname
+ * Hostname or ip address to the memcache server.
+ * - port (optional)
+ * port number the memcache server is running on. This
+ * defaults to memcache.default_port if no value is given.
+ * The default value of memcache.default_port is 11211.
+ * - weight (optional)
+ * The weight of this server in the load balancing
+ * cluster.
+ * - timeout (optional)
+ * The timeout for contacting this server, in seconds.
+ * The default value is 3 seconds.
+ *
+ * @param Memcache $memcache The Memcache object we should add this server to.
+ * @param array $server An associative array with the configuration options for the server to add.
+ *
+ * @throws Exception If any configuration option for the server is invalid.
+ */
+ private static function addMemcacheServer($memcache, $server)
+ {
+ // the hostname option is required
+ if (!array_key_exists('hostname', $server)) {
+ throw new Exception(
+ "hostname setting missing from server in the 'memcache_store.servers' configuration option."
+ );
+ }
+
+ $hostname = $server['hostname'];
+
+ // the hostname must be a valid string
+ if (!is_string($hostname)) {
+ throw new Exception(
+ "Invalid hostname for server in the 'memcache_store.servers' configuration option. The hostname is".
+ ' supposed to be a string.'
+ );
+ }
+
+ // check if the user has specified a port number
+ if (array_key_exists('port', $server)) {
+ // get the port number from the array, and validate it
+ $port = (int) $server['port'];
+ if (($port <= 0) || ($port > 65535)) {
+ throw new Exception(
+ "Invalid port for server in the 'memcache_store.servers' configuration option. The port number".
+ ' is supposed to be an integer between 0 and 65535.'
+ );
+ }
+ } else {
+ // use the default port number from the ini-file
+ $port = (int) ini_get('memcache.default_port');
+ if ($port <= 0 || $port > 65535) {
+ // invalid port number from the ini-file. fall back to the default
+ $port = 11211;
+ }
+ }
+
+ // check if the user has specified a weight for this server
+ if (array_key_exists('weight', $server)) {
+ // get the weight and validate it
+ $weight = (int) $server['weight'];
+ if ($weight <= 0) {
+ throw new Exception(
+ "Invalid weight for server in the 'memcache_store.servers' configuration option. The weight is".
+ ' supposed to be a positive integer.'
+ );
+ }
+ } else {
+ // use a default weight of 1
+ $weight = 1;
+ }
+
+ // check if the user has specified a timeout for this server
+ if (array_key_exists('timeout', $server)) {
+ // get the timeout and validate it
+ $timeout = (int) $server['timeout'];
+ if ($timeout <= 0) {
+ throw new Exception(
+ "Invalid timeout for server in the 'memcache_store.servers' configuration option. The timeout is".
+ ' supposed to be a positive integer.'
+ );
+ }
+ } else {
+ // use a default timeout of 3 seconds
+ $timeout = 3;
+ }
+
+ // add this server to the Memcache object
+ $memcache->addServer($hostname, $port, true, $weight, $timeout);
+ }
+
+
+ /**
+ * This function takes in a list of servers belonging to a group and
+ * creates a Memcache object from the servers in the group.
+ *
+ * @param array $group Array of servers which should be created as a group.
+ *
+ * @return Memcache A Memcache object of the servers in the group
+ *
+ * @throws Exception If the servers configuration is invalid.
+ */
+ private static function loadMemcacheServerGroup(array $group)
+ {
+ if (!class_exists('Memcache')) {
+ throw new Exception('Missing Memcache class. Is the memcache extension installed?');
+ }
+
+ // create the Memcache object
+ $memcache = new Memcache();
+
+ // iterate over all the servers in the group and add them to the Memcache object
+ foreach ($group as $index => $server) {
+ // make sure that we don't have an index. An index would be a sign of invalid configuration
+ if (!is_int($index)) {
+ throw new Exception(
+ "Invalid index on element in the 'memcache_store.servers' configuration option. Perhaps you".
+ ' have forgotten to add an array(...) around one of the server groups? The invalid index was: '.
+ $index
+ );
+ }
+
+ // make sure that the server object is an array. Each server is an array with name-value pairs
+ if (!is_array($server)) {
+ throw new Exception(
+ 'Invalid value for the server with index '.$index.
+ '. Remeber that the \'memcache_store.servers\' configuration option'.
+ ' contains an array of arrays of arrays.'
+ );
+ }
+
+ self::addMemcacheServer($memcache, $server);
+ }
+
+ return $memcache;
+ }
+
+
+ /**
+ * This function gets a list of all configured memcache servers. This list is initialized based
+ * on the content of 'memcache_store.servers' in the configuration.
+ *
+ * @return Memcache[] Array with Memcache objects.
+ *
+ * @throws Exception If the servers configuration is invalid.
+ */
+ private static function getMemcacheServers()
+ {
+ // check if we have loaded the servers already
+ if (self::$serverGroups != null) {
+ return self::$serverGroups;
+ }
+
+ // initialize the servers-array
+ self::$serverGroups = array();
+
+ // load the configuration
+ $config = SimpleSAML_Configuration::getInstance();
+
+
+ $groups = $config->getArray('memcache_store.servers');
+
+ // iterate over all the groups in the 'memcache_store.servers' configuration option
+ foreach ($groups as $index => $group) {
+ // make sure that the group doesn't have an index. An index would be a sign of invalid configuration
+ if (!is_int($index)) {
+ throw new Exception(
+ "Invalid index on element in the 'memcache_store.servers'".
+ ' configuration option. Perhaps you have forgotten to add an array(...)'.
+ ' around one of the server groups? The invalid index was: '.$index
+ );
+ }
+
+ /*
+ * Make sure that the group is an array. Each group is an array of servers. Each server is
+ * an array of name => value pairs for that server.
+ */
+ if (!is_array($group)) {
+ throw new Exception(
+ "Invalid value for the server with index ".$index.
+ ". Remeber that the 'memcache_store.servers' configuration option".
+ ' contains an array of arrays of arrays.'
+ );
+ }
+
+ // parse and add this group to the server group list
+ self::$serverGroups[] = self::loadMemcacheServerGroup($group);
+ }
+
+ return self::$serverGroups;
+ }
+
+
+ /**
+ * This is a helper-function which returns the expire value of data
+ * we should store to the memcache servers.
+ *
+ * The value is set depending on the configuration. If no value is
+ * set in the configuration, then we will use a default value of 0.
+ * 0 means that the item will never expire.
+ *
+ * @return integer The value which should be passed in the set(...) calls to the memcache objects.
+ *
+ * @throws Exception If the option 'memcache_store.expires' has a negative value.
+ */
+ private static function getExpireTime()
+ {
+ // get the configuration instance
+ $config = SimpleSAML_Configuration::getInstance();
+ assert($config instanceof SimpleSAML_Configuration);
+
+ // get the expire-value from the configuration
+ $expire = $config->getInteger('memcache_store.expires', 0);
+
+ // it must be a positive integer
+ if ($expire < 0) {
+ throw new Exception(
+ "The value of 'memcache_store.expires' in the configuration can't be a negative integer."
+ );
+ }
+
+ /* If the configuration option is 0, then we should return 0. This allows the user to specify that the data
+ * shouldn't expire.
+ */
+ if ($expire == 0) {
+ return 0;
+ }
+
+ /* The expire option is given as the number of seconds into the future an item should expire. We convert this
+ * to an actual timestamp.
+ */
+ $expireTime = time() + $expire;
+
+ return $expireTime;
+ }
+
+
+ /**
+ * This function retrieves statistics about all memcache server groups.
+ *
+ * @return array Array with the names of each stat and an array with the value for each server group.
+ *
+ * @throws Exception If memcache server status couldn't be retrieved.
+ */
+ public static function getStats()
+ {
+ $ret = array();
+
+ foreach (self::getMemcacheServers() as $sg) {
+ $stats = $sg->getExtendedStats();
+ if ($stats === false) {
+ throw new Exception('Failed to get memcache server status.');
+ }
+
+ $stats = SimpleSAML\Utils\Arrays::transpose($stats);
+
+ $ret = array_merge_recursive($ret, $stats);
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * Retrieve statistics directly in the form returned by getExtendedStats, for
+ * all server groups.
+ *
+ * @return array An array with the extended stats output for each server group.
+ */
+ public static function getRawStats()
+ {
+ $ret = array();
+
+ foreach (self::getMemcacheServers() as $sg) {
+ $stats = $sg->getExtendedStats();
+ $ret[] = $stats;
+ }
+
+ return $ret;
+ }
}
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php
index aa93416..3903e72 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php
@@ -1,339 +1,368 @@
<?php
+
/**
* This file defines a class for metadata handling.
*
* @author Andreas Åkre Solberg, UNINETT AS. <andreas.solberg@uninett.no>
- * @package simpleSAMLphp
- */
-class SimpleSAML_Metadata_MetaDataStorageHandler {
-
-
- /**
- * This static variable contains a reference to the current
- * instance of the metadata handler. This variable will be NULL if
- * we haven't instantiated a metadata handler yet.
- */
- private static $metadataHandler = NULL;
-
-
- /**
- * This is a list of all the metadata sources we have in our metadata
- * chain. When we need metadata, we will look through this chain from start to end.
- */
- private $sources;
-
-
- /**
- * This function retrieves the current instance of the metadata handler.
- * The metadata handler will be instantiated if this is the first call
- * to this fuunction.
- *
- * @return The current metadata handler instance.
- */
- public static function getMetadataHandler() {
- if(self::$metadataHandler === NULL) {
- self::$metadataHandler = new SimpleSAML_Metadata_MetaDataStorageHandler();
- }
-
- return self::$metadataHandler;
- }
-
-
- /**
- * This constructor initializes this metadata storage handler. It will load and
- * parse the configuration, and initialize the metadata source list.
- */
- protected function __construct() {
-
- $config = SimpleSAML_Configuration::getInstance();
-
- $sourcesConfig = $config->getArray('metadata.sources', NULL);
-
- /* For backwards compatibility, and to provide a default configuration. */
- if($sourcesConfig === NULL) {
- $type = $config->getString('metadata.handler', 'flatfile');
- $sourcesConfig = array(array('type' => $type));
- }
-
- try {
- $this->sources = SimpleSAML_Metadata_MetaDataStorageSource::parseSources($sourcesConfig);
- } catch (Exception $e) {
- throw new Exception('Invalid configuration of the \'metadata.sources\'' .
- ' configuration option: ' . $e->getMessage());
- }
-
- }
-
-
- /**
- * This function is used to generate some metadata elements automatically.
- *
- * @param $property The metadata property which should be autogenerated.
- * @param $set The set we the property comes from.
- * @return The autogenerated metadata property.
- */
- public function getGenerated($property, $set) {
-
- /* First we check if the user has overridden this property in the metadata. */
- try {
- $metadataSet = $this->getMetaDataCurrent($set);
- if(array_key_exists($property, $metadataSet)) {
- return $metadataSet[$property];
- }
- } catch(Exception $e) {
- /* Probably metadata wasn't found. In any case we continue by generating the metadata. */
- }
-
- /* Get the configuration. */
- $config = SimpleSAML_Configuration::getInstance();
- assert($config instanceof SimpleSAML_Configuration);
-
- $baseurl = \SimpleSAML\Utils\HTTP::getSelfURLHost() . '/' .
- $config->getBaseURL();
-
- if ($set == 'saml20-sp-hosted') {
- switch ($property) {
- case 'SingleLogoutServiceBinding' :
- return SAML2_Const::BINDING_HTTP_REDIRECT;
- }
- } elseif($set == 'saml20-idp-hosted') {
- switch ($property) {
- case 'SingleSignOnService' :
- return $baseurl . 'saml2/idp/SSOService.php';
-
- case 'SingleSignOnServiceBinding' :
- return SAML2_Const::BINDING_HTTP_REDIRECT;
-
- case 'SingleLogoutService' :
- return $baseurl . 'saml2/idp/SingleLogoutService.php';
-
- case 'SingleLogoutServiceBinding' :
- return SAML2_Const::BINDING_HTTP_REDIRECT;
- }
- } elseif($set == 'shib13-idp-hosted') {
- switch ($property) {
- case 'SingleSignOnService' :
- return $baseurl . 'shib13/idp/SSOService.php';
- }
- }
-
- throw new Exception('Could not generate metadata property ' . $property . ' for set ' . $set . '.');
- }
-
-
- /**
- * This function lists all known metadata in the given set. It is returned as an associative array
- * where the key is the entity id.
- *
- * @param $set The set we want to list metadata from.
- * @return An associative array with the metadata from from the given set.
- */
- public function getList($set = 'saml20-idp-remote') {
-
- assert('is_string($set)');
-
- $result = array();
-
- foreach($this->sources as $source) {
- $srcList = $source->getMetadataSet($set);
-
- foreach($srcList AS $key => $le) {
- if (array_key_exists('expire', $le)) {
- if ($le['expire'] < time()) {
- unset($srcList[$key]);
- SimpleSAML_Logger::warning("Dropping metadata entity " .
- var_export($key,true) . ", expired " .
- SimpleSAML\Utils\Time::generateTimestamp($le['expire']) .
- ".");
- }
- }
- }
-
-
-
- /* $result is the last argument to array_merge because we want the content already
- * in $result to have precedence.
- */
- $result = array_merge($srcList, $result);
- }
-
- return $result;
- }
-
-
- /**
- * This function retrieves metadata for the current entity based on the hostname/path the request
- * was directed to. It will throw an exception if it is unable to locate the metadata.
- *
- * @param $set The set we want metadata from.
- * @return An associative array with the metadata.
- */
- public function getMetaDataCurrent($set) {
- return $this->getMetaData(NULL, $set);
- }
-
-
- /**
- * This function locates the current entity id based on the hostname/path combination the user accessed.
- * It will throw an exception if it is unable to locate the entity id.
- *
- * @param $set The set we look for the entity id in.
- * @param $type Do you want to return the metaindex or the entityID. [entityid|metaindex]
- * @return The entity id which is associated with the current hostname/path combination.
- */
- public function getMetaDataCurrentEntityID($set, $type = 'entityid') {
-
- assert('is_string($set)');
-
- /* First we look for the hostname/path combination. */
- $currenthostwithpath = \SimpleSAML\Utils\HTTP::getSelfHostWithPath(); // sp.example.org/university
-
- foreach($this->sources as $source) {
- $index = $source->getEntityIdFromHostPath($currenthostwithpath, $set, $type);
- if($index !== NULL) {
- return $index;
- }
- }
-
-
- /* Then we look for the hostname. */
- $currenthost = \SimpleSAML\Utils\HTTP::getSelfHost(); // sp.example.org
- if(strpos($currenthost, ":") !== FALSE) {
- $currenthostdecomposed = explode(":", $currenthost);
- $currenthost = $currenthostdecomposed[0];
- }
-
- foreach($this->sources as $source) {
- $index = $source->getEntityIdFromHostPath($currenthost, $set, $type);
- if($index !== NULL) {
- return $index;
- }
- }
-
-
- /* Then we look for the DEFAULT entry. */
- foreach($this->sources as $source) {
- $entityId = $source->getEntityIdFromHostPath('__DEFAULT__', $set, $type);
- if($entityId !== NULL) {
- return $entityId;
- }
- }
-
-
-
- /* We were unable to find the hostname/path in any metadata source. */
- throw new Exception('Could not find any default metadata entities in set [' . $set . '] for host [' . $currenthost . ' : ' . $currenthostwithpath . ']');
- }
-
- /**
- * This method will call getPreferredEntityIdFromCIDRhint() on all of the
- * sources.
- *
- * @param $set Which set of metadata we are looking it up in.
- * @param $ip IP address
- * @return The entity id of a entity which have a CIDR hint where the provided
- * IP address match.
- */
- public function getPreferredEntityIdFromCIDRhint($set, $ip) {
-
- foreach($this->sources as $source) {
- $entityId = $source->getPreferredEntityIdFromCIDRhint($set, $ip);
- if($entityId !== NULL) {
- return $entityId;
- }
- }
-
- return NULL;
-
- }
-
- /**
- * This function looks up the metadata for the given entity id in the given set. It will throw an
- * exception if it is unable to locate the metadata.
- *
- * @param $index The entity id we are looking up. This parameter may be NULL, in which case we look up
- * the current entity id based on the current hostname/path.
- * @param $set The set of metadata we are looking up the entity id in.
- */
- public function getMetaData($index, $set) {
-
- assert('is_string($set)');
-
- if($index === NULL) {
- $index = $this->getMetaDataCurrentEntityID($set, 'metaindex');
- }
-
- assert('is_string($index)');
-
- foreach($this->sources as $source) {
- $metadata = $source->getMetaData($index, $set);
-
- if($metadata !== NULL) {
-
- if (array_key_exists('expire', $metadata)) {
- if ($metadata['expire'] < time()) {
- throw new Exception('Metadata for the entity [' . $index . '] expired ' .
- (time() - $metadata['expire']) . ' seconds ago.'
- );
- }
- }
-
- $metadata['metadata-index'] = $index;
- $metadata['metadata-set'] = $set;
- assert('array_key_exists("entityid", $metadata)');
- return $metadata;
- }
- }
-
- throw new SimpleSAML_Error_MetadataNotFound($index);
- }
-
-
- /**
- * Retrieve the metadata as a configuration object.
- *
- * This function will throw an exception if it is unable to locate the metadata.
- *
- * @param string $entityId The entity ID we are looking up.
- * @param string $set The metadata set we are searching.
- * @return SimpleSAML_Configuration The configuration object representing the metadata.
- */
- public function getMetaDataConfig($entityId, $set) {
- assert('is_string($entityId)');
- assert('is_string($set)');
-
- $metadata = $this->getMetaData($entityId, $set);
- return SimpleSAML_Configuration::loadFromArray($metadata, $set . '/' . var_export($entityId, TRUE));
- }
-
- public function getMetaDataConfigForSha1($sha1, $set) {
- assert('is_string($sha1)');
- assert('is_string($set)');
-
-
- $result = array();
-
- foreach($this->sources as $source) {
- $srcList = $source->getMetadataSet($set);
-
-
- /* $result is the last argument to array_merge because we want the content already
- * in $result to have precedence.
- */
- $result = array_merge($srcList, $result);
- }
- foreach($result as $remote_provider ){
-
- if(sha1($remote_provider['entityid'])==$sha1){
- $remote_provider['metadata-set'] = $set;
-
- return SimpleSAML_Configuration::loadFromArray($remote_provider, $set . '/' . var_export($remote_provider['entityid'], TRUE));
- }
- }
-
- return null;
- }
-
+ * @package SimpleSAMLphp
+ */
+class SimpleSAML_Metadata_MetaDataStorageHandler
+{
+
+
+ /**
+ * This static variable contains a reference to the current
+ * instance of the metadata handler. This variable will be null if
+ * we haven't instantiated a metadata handler yet.
+ *
+ * @var SimpleSAML_Metadata_MetaDataStorageHandler
+ */
+ private static $metadataHandler = null;
+
+
+ /**
+ * This is a list of all the metadata sources we have in our metadata
+ * chain. When we need metadata, we will look through this chain from start to end.
+ *
+ * @var SimpleSAML_Metadata_MetaDataStorageSource[]
+ */
+ private $sources;
+
+
+ /**
+ * This function retrieves the current instance of the metadata handler.
+ * The metadata handler will be instantiated if this is the first call
+ * to this function.
+ *
+ * @return SimpleSAML_Metadata_MetaDataStorageHandler The current metadata handler instance.
+ */
+ public static function getMetadataHandler()
+ {
+ if (self::$metadataHandler === null) {
+ self::$metadataHandler = new SimpleSAML_Metadata_MetaDataStorageHandler();
+ }
+
+ return self::$metadataHandler;
+ }
+
+
+ /**
+ * This constructor initializes this metadata storage handler. It will load and
+ * parse the configuration, and initialize the metadata source list.
+ */
+ protected function __construct()
+ {
+ $config = SimpleSAML_Configuration::getInstance();
+
+ $sourcesConfig = $config->getArray('metadata.sources', null);
+
+ // for backwards compatibility, and to provide a default configuration
+ if ($sourcesConfig === null) {
+ $type = $config->getString('metadata.handler', 'flatfile');
+ $sourcesConfig = array(array('type' => $type));
+ }
+
+ try {
+ $this->sources = SimpleSAML_Metadata_MetaDataStorageSource::parseSources($sourcesConfig);
+ } catch (Exception $e) {
+ throw new Exception(
+ "Invalid configuration of the 'metadata.sources' configuration option: ".$e->getMessage()
+ );
+ }
+ }
+
+
+ /**
+ * This function is used to generate some metadata elements automatically.
+ *
+ * @param string $property The metadata property which should be auto-generated.
+ * @param string $set The set we the property comes from.
+ *
+ * @return string The auto-generated metadata property.
+ * @throws Exception If the metadata cannot be generated automatically.
+ */
+ public function getGenerated($property, $set)
+ {
+ // first we check if the user has overridden this property in the metadata
+ try {
+ $metadataSet = $this->getMetaDataCurrent($set);
+ if (array_key_exists($property, $metadataSet)) {
+ return $metadataSet[$property];
+ }
+ } catch (Exception $e) {
+ // probably metadata wasn't found. In any case we continue by generating the metadata
+ }
+
+ // get the configuration
+ $config = SimpleSAML_Configuration::getInstance();
+ assert($config instanceof SimpleSAML_Configuration);
+
+ $baseurl = \SimpleSAML\Utils\HTTP::getSelfURLHost().'/'.
+ $config->getBaseURL();
+
+ if ($set == 'saml20-sp-hosted') {
+ if ($property === 'SingleLogoutServiceBinding') {
+ return SAML2_Const::BINDING_HTTP_REDIRECT;
+ }
+ } elseif ($set == 'saml20-idp-hosted') {
+ switch ($property) {
+ case 'SingleSignOnService':
+ return $baseurl.'saml2/idp/SSOService.php';
+
+ case 'SingleSignOnServiceBinding':
+ return SAML2_Const::BINDING_HTTP_REDIRECT;
+
+ case 'SingleLogoutService':
+ return $baseurl.'saml2/idp/SingleLogoutService.php';
+
+ case 'SingleLogoutServiceBinding':
+ return SAML2_Const::BINDING_HTTP_REDIRECT;
+ }
+ } elseif ($set == 'shib13-idp-hosted') {
+ if ($property === 'SingleSignOnService') {
+ return $baseurl.'shib13/idp/SSOService.php';
+ }
+ }
+
+ throw new Exception('Could not generate metadata property '.$property.' for set '.$set.'.');
+ }
+
+
+ /**
+ * This function lists all known metadata in the given set. It is returned as an associative array
+ * where the key is the entity id.
+ *
+ * @param string $set The set we want to list metadata from.
+ *
+ * @return array An associative array with the metadata from from the given set.
+ */
+ public function getList($set = 'saml20-idp-remote')
+ {
+ assert('is_string($set)');
+
+ $result = array();
+
+ foreach ($this->sources as $source) {
+ $srcList = $source->getMetadataSet($set);
+
+ foreach ($srcList as $key => $le) {
+ if (array_key_exists('expire', $le)) {
+ if ($le['expire'] < time()) {
+ unset($srcList[$key]);
+ SimpleSAML_Logger::warning(
+ "Dropping metadata entity ".var_export($key, true).", expired ".
+ SimpleSAML\Utils\Time::generateTimestamp($le['expire'])."."
+ );
+ }
+ }
+ }
+
+ /* $result is the last argument to array_merge because we want the content already
+ * in $result to have precedence.
+ */
+ $result = array_merge($srcList, $result);
+ }
+
+ return $result;
+ }
+
+
+ /**
+ * This function retrieves metadata for the current entity based on the hostname/path the request
+ * was directed to. It will throw an exception if it is unable to locate the metadata.
+ *
+ * @param string $set The set we want metadata from.
+ *
+ * @return array An associative array with the metadata.
+ */
+ public function getMetaDataCurrent($set)
+ {
+ return $this->getMetaData(null, $set);
+ }
+
+
+ /**
+ * This function locates the current entity id based on the hostname/path combination the user accessed.
+ * It will throw an exception if it is unable to locate the entity id.
+ *
+ * @param string $set The set we look for the entity id in.
+ * @param string $type Do you want to return the metaindex or the entityID. [entityid|metaindex]
+ *
+ * @return string The entity id which is associated with the current hostname/path combination.
+ * @throws Exception If no default metadata can be found in the set for the current host.
+ */
+ public function getMetaDataCurrentEntityID($set, $type = 'entityid')
+ {
+ assert('is_string($set)');
+
+ // first we look for the hostname/path combination
+ $currenthostwithpath = \SimpleSAML\Utils\HTTP::getSelfHostWithPath(); // sp.example.org/university
+
+ foreach ($this->sources as $source) {
+ $index = $source->getEntityIdFromHostPath($currenthostwithpath, $set, $type);
+ if ($index !== null) {
+ return $index;
+ }
+ }
+
+ // then we look for the hostname
+ $currenthost = \SimpleSAML\Utils\HTTP::getSelfHost(); // sp.example.org
+ if (strpos($currenthost, ":") !== false) {
+ $currenthostdecomposed = explode(":", $currenthost);
+ $currenthost = $currenthostdecomposed[0];
+ }
+
+ foreach ($this->sources as $source) {
+ $index = $source->getEntityIdFromHostPath($currenthost, $set, $type);
+ if ($index !== null) {
+ return $index;
+ }
+ }
+
+ // then we look for the DEFAULT entry
+ foreach ($this->sources as $source) {
+ $entityId = $source->getEntityIdFromHostPath('__DEFAULT__', $set, $type);
+ if ($entityId !== null) {
+ return $entityId;
+ }
+ }
+
+ // we were unable to find the hostname/path in any metadata source
+ throw new Exception(
+ 'Could not find any default metadata entities in set ['.$set.'] for host ['.$currenthost.' : '.
+ $currenthostwithpath.']'
+ );
+ }
+
+
+ /**
+ * This method will call getPreferredEntityIdFromCIDRhint() on all of the
+ * sources.
+ *
+ * @param string $set Which set of metadata we are looking it up in.
+ * @param string $ip IP address
+ *
+ * @return string The entity id of a entity which have a CIDR hint where the provided
+ * IP address match.
+ */
+ public function getPreferredEntityIdFromCIDRhint($set, $ip)
+ {
+ foreach ($this->sources as $source) {
+ $entityId = $source->getPreferredEntityIdFromCIDRhint($set, $ip);
+ if ($entityId !== null) {
+ return $entityId;
+ }
+ }
+
+ return null;
+ }
+
+
+ /**
+ * This function looks up the metadata for the given entity id in the given set. It will throw an
+ * exception if it is unable to locate the metadata.
+ *
+ * @param string $index The entity id we are looking up. This parameter may be NULL, in which case we look up
+ * the current entity id based on the current hostname/path.
+ * @param string $set The set of metadata we are looking up the entity id in.
+ *
+ * @return array The metadata array describing the specified entity.
+ * @throws Exception If metadata for the specified entity is expired.
+ * @throws SimpleSAML_Error_MetadataNotFound If no metadata for the entity specified can be found.
+ */
+ public function getMetaData($index, $set)
+ {
+ assert('is_string($set)');
+
+ if ($index === null) {
+ $index = $this->getMetaDataCurrentEntityID($set, 'metaindex');
+ }
+
+ assert('is_string($index)');
+
+ foreach ($this->sources as $source) {
+ $metadata = $source->getMetaData($index, $set);
+
+ if ($metadata !== null) {
+
+ if (array_key_exists('expire', $metadata)) {
+ if ($metadata['expire'] < time()) {
+ throw new Exception(
+ 'Metadata for the entity ['.$index.'] expired '.
+ (time() - $metadata['expire']).' seconds ago.'
+ );
+ }
+ }
+
+ $metadata['metadata-index'] = $index;
+ $metadata['metadata-set'] = $set;
+ assert('array_key_exists("entityid", $metadata)');
+ return $metadata;
+ }
+ }
+
+ throw new SimpleSAML_Error_MetadataNotFound($index);
+ }
+
+
+ /**
+ * Retrieve the metadata as a configuration object.
+ *
+ * This function will throw an exception if it is unable to locate the metadata.
+ *
+ * @param string $entityId The entity ID we are looking up.
+ * @param string $set The metadata set we are searching.
+ *
+ * @return SimpleSAML_Configuration The configuration object representing the metadata.
+ * @throws SimpleSAML_Error_MetadataNotFound If no metadata for the entity specified can be found.
+ */
+ public function getMetaDataConfig($entityId, $set)
+ {
+ assert('is_string($entityId)');
+ assert('is_string($set)');
+
+ $metadata = $this->getMetaData($entityId, $set);
+ return SimpleSAML_Configuration::loadFromArray($metadata, $set.'/'.var_export($entityId, true));
+ }
+
+
+ /**
+ * Search for an entity's metadata, given the SHA1 digest of its entity ID.
+ *
+ * @param string $sha1 The SHA1 digest of the entity ID.
+ * @param string $set The metadata set we are searching.
+ *
+ * @return null|SimpleSAML_Configuration The metadata corresponding to the entity, or null if the entity cannot be
+ * found.
+ */
+ public function getMetaDataConfigForSha1($sha1, $set)
+ {
+ assert('is_string($sha1)');
+ assert('is_string($set)');
+
+ $result = array();
+
+ foreach ($this->sources as $source) {
+ $srcList = $source->getMetadataSet($set);
+
+ /* $result is the last argument to array_merge because we want the content already
+ * in $result to have precedence.
+ */
+ $result = array_merge($srcList, $result);
+ }
+ foreach ($result as $remote_provider) {
+
+ if (sha1($remote_provider['entityid']) == $sha1) {
+ $remote_provider['metadata-set'] = $set;
+
+ return SimpleSAML_Configuration::loadFromArray(
+ $remote_provider,
+ $set.'/'.var_export($remote_provider['entityid'], true)
+ );
+ }
+ }
+
+ return null;
+ }
}
-
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerFlatFile.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerFlatFile.php
index 656426b..6a1688e 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerFlatFile.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerFlatFile.php
@@ -1,135 +1,144 @@
<?php
+
/**
* This file defines a flat file metadata source.
* Instantiation of session handler objects should be done through
* the class method getMetadataHandler().
*
- * @author Andreas �kre Solberg, UNINETT AS. <andreas.solberg@uninett.no>
- * @package simpleSAMLphp
+ * @author Andreas Åkre Solberg, UNINETT AS. <andreas.solberg@uninett.no>
+ * @package SimpleSAMLphp
*/
-class SimpleSAML_Metadata_MetaDataStorageHandlerFlatFile extends SimpleSAML_Metadata_MetaDataStorageSource {
-
- /**
- * This is the directory we will load metadata files from. The path will always end
- * with a '/'.
- */
- private $directory;
-
-
- /**
- * This is an associative array which stores the different metadata sets we have loaded.
- */
- private $cachedMetadata = array();
-
-
- /**
- * This constructor initializes the flatfile metadata storage handler with the
- * specified configuration. The configuration is an associative array with the following
- * possible elements:
- * - 'directory': The directory we should load metadata from. The default directory is
- * set in the 'metadatadir' configuration option in 'config.php'.
- *
- * @param $config An associtive array with the configuration for this handler.
- */
- protected function __construct($config) {
- assert('is_array($config)');
-
- /* Get the configuration. */
- $globalConfig = SimpleSAML_Configuration::getInstance();
-
-
- /* Find the path to the directory we should search for metadata in. */
- if(array_key_exists('directory', $config)) {
- $this->directory = $config['directory'];
- } else {
- $this->directory = $globalConfig->getString('metadatadir', 'metadata/');
- }
-
- /* Resolve this directory relative to the simpleSAMLphp directory (unless it is
- * an absolute path).
- */
- $this->directory = $globalConfig->resolvePath($this->directory) . '/';
- }
-
-
- /**
- * This function loads the given set of metadata from a file our metadata directory.
- * This function returns NULL if it is unable to locate the given set in the metadata directory.
- *
- * @param $set The set of metadata we are loading.
- * @return Associative array with the metadata, or NULL if we are unable to load metadata from the given file.
- */
- private function load($set) {
-
- $metadatasetfile = $this->directory . $set . '.php';
-
- if (!file_exists($metadatasetfile)) {
- return NULL;
- }
-
- $metadata = array();
-
- include($metadatasetfile);
-
- if (!is_array($metadata)) {
- throw new Exception('Could not load metadata set [' . $set . '] from file: ' . $metadatasetfile);
- }
-
- return $metadata;
- }
-
-
- /**
- * This function retrieves the given set of metadata. It will return an empty array if it is
- * unable to locate it.
- *
- * @param $set The set of metadata we are retrieving.
- * @return Asssociative array with the metadata. Each element in the array is an entity, and the
- * key is the entity id.
- */
- public function getMetadataSet($set) {
-
- if(array_key_exists($set, $this->cachedMetadata)) {
- return $this->cachedMetadata[$set];
- }
-
- $metadataSet = $this->load($set);
- if($metadataSet === NULL) {
- $metadataSet = array();
- }
-
- /* Add the entity id of an entry to each entry in the metadata. */
- foreach ($metadataSet AS $entityId => &$entry) {
- if (preg_match('/__DYNAMIC(:[0-9]+)?__/', $entityId)) {
- $entry['entityid'] = $this->generateDynamicHostedEntityID($set);
- } else {
- $entry['entityid'] = $entityId;
- }
- }
-
- $this->cachedMetadata[$set] = $metadataSet;
-
- return $metadataSet;
- }
-
- private function generateDynamicHostedEntityID($set) {
-
- /* Get the configuration. */
- $baseurl = \SimpleSAML\Utils\HTTP::getBaseURL();
-
- if ($set === 'saml20-idp-hosted') {
- return $baseurl . 'saml2/idp/metadata.php';
- } elseif($set === 'shib13-idp-hosted') {
- return $baseurl . 'shib13/idp/metadata.php';
- } elseif($set === 'wsfed-sp-hosted') {
- return 'urn:federation:' . \SimpleSAML\Utils\HTTP::getSelfHost();
- } elseif($set === 'adfs-idp-hosted') {
- return 'urn:federation:' . \SimpleSAML\Utils\HTTP::getSelfHost() . ':idp';
- } else {
- throw new Exception('Can not generate dynamic EntityID for metadata of this type: [' . $set . ']');
- }
- }
-
-
+class SimpleSAML_Metadata_MetaDataStorageHandlerFlatFile extends SimpleSAML_Metadata_MetaDataStorageSource
+{
+
+ /**
+ * This is the directory we will load metadata files from. The path will always end
+ * with a '/'.
+ *
+ * @var string
+ */
+ private $directory;
+
+
+ /**
+ * This is an associative array which stores the different metadata sets we have loaded.
+ *
+ * @var array
+ */
+ private $cachedMetadata = array();
+
+
+ /**
+ * This constructor initializes the flatfile metadata storage handler with the
+ * specified configuration. The configuration is an associative array with the following
+ * possible elements:
+ * - 'directory': The directory we should load metadata from. The default directory is
+ * set in the 'metadatadir' configuration option in 'config.php'.
+ *
+ * @param array $config An associative array with the configuration for this handler.
+ */
+ protected function __construct($config)
+ {
+ assert('is_array($config)');
+
+ // get the configuration
+ $globalConfig = SimpleSAML_Configuration::getInstance();
+
+ // find the path to the directory we should search for metadata in
+ if (array_key_exists('directory', $config)) {
+ $this->directory = $config['directory'];
+ } else {
+ $this->directory = $globalConfig->getString('metadatadir', 'metadata/');
+ }
+
+ /* Resolve this directory relative to the simpleSAMLphp directory (unless it is
+ * an absolute path).
+ */
+ $this->directory = $globalConfig->resolvePath($this->directory).'/';
+ }
+
+
+ /**
+ * This function loads the given set of metadata from a file our metadata directory.
+ * This function returns null if it is unable to locate the given set in the metadata directory.
+ *
+ * @param string $set The set of metadata we are loading.
+ *
+ * @return array An associative array with the metadata, or null if we are unable to load metadata from the given
+ * file.
+ * @throws Exception If the metadata set cannot be loaded.
+ */
+ private function load($set)
+ {
+ $metadatasetfile = $this->directory.$set.'.php';
+
+ if (!file_exists($metadatasetfile)) {
+ return null;
+ }
+
+ $metadata = array();
+
+ include($metadatasetfile);
+
+ if (!is_array($metadata)) {
+ throw new Exception('Could not load metadata set ['.$set.'] from file: '.$metadatasetfile);
+ }
+
+ return $metadata;
+ }
+
+
+ /**
+ * This function retrieves the given set of metadata. It will return an empty array if it is
+ * unable to locate it.
+ *
+ * @param string $set The set of metadata we are retrieving.
+ *
+ * @return array An associative array with the metadata. Each element in the array is an entity, and the
+ * key is the entity id.
+ */
+ public function getMetadataSet($set)
+ {
+ if (array_key_exists($set, $this->cachedMetadata)) {
+ return $this->cachedMetadata[$set];
+ }
+
+ $metadataSet = $this->load($set);
+ if ($metadataSet === null) {
+ $metadataSet = array();
+ }
+
+ // add the entity id of an entry to each entry in the metadata
+ foreach ($metadataSet as $entityId => &$entry) {
+ if (preg_match('/__DYNAMIC(:[0-9]+)?__/', $entityId)) {
+ $entry['entityid'] = $this->generateDynamicHostedEntityID($set);
+ } else {
+ $entry['entityid'] = $entityId;
+ }
+ }
+
+ $this->cachedMetadata[$set] = $metadataSet;
+
+ return $metadataSet;
+ }
+
+
+ private function generateDynamicHostedEntityID($set)
+ {
+ // get the configuration
+ $baseurl = \SimpleSAML\Utils\HTTP::getBaseURL();
+
+ if ($set === 'saml20-idp-hosted') {
+ return $baseurl.'saml2/idp/metadata.php';
+ } elseif ($set === 'shib13-idp-hosted') {
+ return $baseurl.'shib13/idp/metadata.php';
+ } elseif ($set === 'wsfed-sp-hosted') {
+ return 'urn:federation:'.\SimpleSAML\Utils\HTTP::getSelfHost();
+ } elseif ($set === 'adfs-idp-hosted') {
+ return 'urn:federation:'.\SimpleSAML\Utils\HTTP::getSelfHost().':idp';
+ } else {
+ throw new Exception('Can not generate dynamic EntityID for metadata of this type: ['.$set.']');
+ }
+ }
}
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerMDX.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerMDX.php
index 7f61a62..c06a9e0 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerMDX.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerMDX.php
@@ -1,286 +1,321 @@
<?php
+
/**
* This class implements SAML Metadata Exchange Protocol
*
* @author Andreas Åkre Solberg, UNINETT AS.
* @author Olav Morken, UNINETT AS.
* @author Tamas Frank, NIIFI
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-class SimpleSAML_Metadata_MetaDataStorageHandlerMDX extends SimpleSAML_Metadata_MetaDataStorageSource {
+class SimpleSAML_Metadata_MetaDataStorageHandlerMDX extends SimpleSAML_Metadata_MetaDataStorageSource
+{
/**
* The URL of MDX server (url:port)
+ *
+ * @var string
*/
private $server;
/**
- * The fingerprint of the certificate used to sign the metadata. You don't need this option if you don't want to validate the signature on the metadata.
+ * The fingerprint of the certificate used to sign the metadata. You don't need this option if you don't want to
+ * validate the signature on the metadata.
+ *
+ * @var string|null
*/
private $validateFingerprint;
- /**
- * The cache directory, or NULL if no cache directory is configured.
- */
- private $cacheDir;
+ /**
+ * The cache directory, or null if no cache directory is configured.
+ *
+ * @var string|null
+ */
+ private $cacheDir;
- /**
- * The maximum cache length, in seconds.
- */
- private $cacheLength;
+ /**
+ * The maximum cache length, in seconds.
+ *
+ * @var integer
+ */
+ private $cacheLength;
- /**
- * This function initializes the dynamic XML metadata source.
- *
- * Options:
- * - 'server': URL of the MDX server (url:port). Mandatory.
+ /**
+ * This function initializes the dynamic XML metadata source.
+ *
+ * Options:
+ * - 'server': URL of the MDX server (url:port). Mandatory.
* - 'validateFingerprint': The fingerprint of the certificate used to sign the metadata.
- * You don't need this option if you don't want to validate the signature on the metadata. Optional.
- * - 'cachedir': Directory where metadata can be cached. Optional.
- * - 'cachelength': Maximum time metadata cah be cached, in seconds. Default to 24
- * hours (86400 seconds).
- *
- * @param array $config The configuration for this instance of the XML metadata source.
- */
- protected function __construct($config) {
- assert('is_array($config)');
-
- if (!array_key_exists('server', $config)){
- throw new Exception('Error, the $server parameter of the config has not been set!');
- } else {
- $this->server = $config['server'];
- }
-
- if (array_key_exists('validateFingerprint', $config)) {
- $this->validateFingerprint = $config['validateFingerprint'];
- } else {
- $this->validateFingerprint = NULL;
- }
-
- if (array_key_exists('cachedir', $config)) {
- $globalConfig = SimpleSAML_Configuration::getInstance();
- $this->cacheDir = $globalConfig->resolvePath($config['cachedir']);
- } else {
- $this->cacheDir = NULL;
- }
-
- if (array_key_exists('cachelength', $config)) {
- $this->cacheLength = $config['cachelength'];
- } else {
- $this->cacheLength = 86400;
- }
-
- }
-
-
- /**
- * This function returns an associative array with metadata for all entities in the given set. The
- * key of the array is the entity id.
- *
- * @param $set The set we want to list metadata for.
- * @return An associative array with all entities in the given set.
- */
- public function getMetadataSet($set) {
-
- /* We don't have this metadata set. */
- return array();
- }
-
-
- /**
- * Find the cache file name for an entity,
- *
- * @param string $set The metadata set this entity belongs to.
- * @param string $entityId The entity id of this entity.
- * @return string The full path to the cache file.
- */
- private function getCacheFilename($set, $entityId) {
- assert('is_string($set)');
- assert('is_string($entityId)');
-
- $cachekey = sha1($entityId);
- $globalConfig = SimpleSAML_Configuration::getInstance();
- return $this->cacheDir . '/' . $set . '-' . $cachekey . '.cached.xml';
- }
-
-
- /**
- * Load a entity from the cache.
- *
- * @param string $set The metadata set this entity belongs to.
- * @param string $entityId The entity id of this entity.
- * @return array|NULL The associative array with the metadata for this entity, or NULL
- * if the entity could not be found.
- */
- private function getFromCache($set, $entityId) {
- assert('is_string($set)');
- assert('is_string($entityId)');
-
- if (empty($this->cacheDir)) {
- return NULL;
- }
-
- $cachefilename = $this->getCacheFilename($set, $entityId);
- if (!file_exists($cachefilename)) return NULL;
- if (!is_readable($cachefilename)) throw new Exception('Could not read cache file for entity [' . $cachefilename. ']');
- SimpleSAML_Logger::debug('MetaData - Handler.MDX: Reading cache [' . $entityId . '] => [' . $cachefilename . ']' );
-
- /* Ensure that this metadata isn't older that the cachelength option allows. This
- * must be verified based on the file, since this option may be changed after the
- * file is written.
- */
- $stat = stat($cachefilename);
- if ($stat['mtime'] + $this->cacheLength <= time()) {
- SimpleSAML_Logger::debug('MetaData - Handler.MDX: Cache file older that the cachelength option allows.');
- return NULL;
- }
-
- $rawData = file_get_contents($cachefilename);
- if (empty($rawData)) {
- $error = error_get_last();
- throw new Exception('Error reading metadata from cache file "' . $cachefilename . '": ' .
- $error['message']);
- }
-
- $data = unserialize($rawData);
- if ($data === FALSE) {
- throw new Exception('Error deserializing cached data from file "' . $cachefilename .'".');
- }
-
- if (!is_array($data)) {
- throw new Exception('Cached metadata from "' . $cachefilename . '" wasn\'t an array.');
- }
-
- return $data;
- }
-
-
- /**
- * Save a entity to the cache.
- *
- * @param string $set The metadata set this entity belongs to.
- * @param string $entityId The entity id of this entity.
- * @param array $data The associative array with the metadata for this entity.
- */
- private function writeToCache($set, $entityId, $data) {
- assert('is_string($set)');
- assert('is_string($entityId)');
- assert('is_array($data)');
-
- if (empty($this->cacheDir)) {
- return;
- }
-
- $cachefilename = $this->getCacheFilename($set, $entityId);
- if (!is_writable(dirname($cachefilename))) throw new Exception('Could not write cache file for entity [' . $cachefilename. ']');
- SimpleSAML_Logger::debug('MetaData - Handler.MDX: Writing cache [' . $entityId . '] => [' . $cachefilename . ']' );
- file_put_contents($cachefilename, serialize($data));
- }
-
-
- /**
- * Retrieve metadata for the correct set from a SAML2Parser.
- *
- * @param SimpleSAML_Metadata_SAMLParser $entity A SAML2Parser representing an entity.
- * @param string $set The metadata set we are looking for.
- * @return array|NULL The associative array with the metadata, or NULL if no metadata for
- * the given set was found.
- */
- private static function getParsedSet(SimpleSAML_Metadata_SAMLParser $entity, $set) {
- assert('is_string($set)');
-
- switch($set) {
- case 'saml20-idp-remote':
- return $entity->getMetadata20IdP();
- case 'saml20-sp-remote':
- return $entity->getMetadata20SP();
- case 'shib13-idp-remote':
- return $entity->getMetadata1xIdP();
- case 'shib13-sp-remote':
- return $entity->getMetadata1xSP();
- case 'attributeauthority-remote':
- $ret = $entity->getAttributeAuthorities();
- return $ret[0];
-
- default:
- SimpleSAML_Logger::warning('MetaData - Handler.MDX: Unknown metadata set: ' . $set);
- }
-
- return NULL;
- }
-
-
- /**
- * Overriding this function from the superclass SimpleSAML_Metadata_MetaDataStorageSource.
- *
- * This function retrieves metadata for the given entity id in the given set of metadata.
- * It will return NULL if it is unable to locate the metadata.
- *
- * This class implements this function using the getMetadataSet-function. A subclass should
- * override this function if it doesn't implement the getMetadataSet function, or if the
- * implementation of getMetadataSet is slow.
- *
- * @param $index The entityId or metaindex we are looking up.
- * @param $set The set we are looking for metadata in.
- * @return An associative array with metadata for the given entity, or NULL if we are unable to
- * locate the entity.
- */
- public function getMetaData($index, $set) {
- assert('is_string($index)');
- assert('is_string($set)');
-
- SimpleSAML_Logger::info('MetaData - Handler.MDX: Loading metadata entity [' . $index . '] from [' . $set . ']' );
-
- /* Read from cache if possible. */
- $data = $this->getFromCache($set, $index);
-
- if ($data !== NULL && array_key_exists('expires', $data) && $data['expires'] < time()) {
- /* Metadata has expired. */
- $data = NULL;
- }
-
- if (isset($data)) {
- /* Metadata found in cache and not expired. */
- SimpleSAML_Logger::debug('MetaData - Handler.MDX: Using cached metadata for: ' . $index . '.');
- return $data;
- }
-
- /* Look at Metadata Query Protocol: https://github.com/iay/md-query/blob/master/draft-young-md-query.txt */
- $mdx_url = $this->server . '/entities/' . urlencode( $index);
-
- SimpleSAML_Logger::debug('MetaData - Handler.MDX: Downloading metadata for "'. $index .'" from [' . $mdx_url . ']' );
- try {
- $xmldata = \SimpleSAML\Utils\HTTP::fetch($mdx_url);
- } catch(Exception $e) {
- SimpleSAML_Logger::warning('Fetching metadata for ' . $index . ': ' . $e->getMessage());
- }
-
- if (empty($xmldata)) {
- $error = error_get_last();
- throw new Exception('Error downloading metadata for "'. $index .'" from "' . $mdx_url . '": ' .
- $error['message']);
- }
-
- $entity = SimpleSAML_Metadata_SAMLParser::parseString($xmldata);
- SimpleSAML_Logger::debug('MetaData - Handler.MDX: Completed parsing of [' . $mdx_url . ']' );
-
- if( $this->validateFingerprint !== NULL) {
- if(!$entity->validateFingerprint($this->validateFingerprint)) {
- throw new Exception ('Error, could not verify signature for entity: ' . $index . '".');
- }
- }
-
- $data = self::getParsedSet($entity, $set);
- if ($data === NULL) {
- throw new Exception('No metadata for set "' . $set . '" available from "' . $index . '".');
- }
-
- $this->writeToCache($set, $index, $data);
-
- return $data;
- }
+ * You don't need this option if you don't want to validate the signature on the metadata.
+ * Optional.
+ * - 'cachedir': Directory where metadata can be cached. Optional.
+ * - 'cachelength': Maximum time metadata cah be cached, in seconds. Default to 24
+ * hours (86400 seconds).
+ *
+ * @param array $config The configuration for this instance of the XML metadata source.
+ *
+ * @throws Exception If no server option can be found in the configuration.
+ */
+ protected function __construct($config)
+ {
+ assert('is_array($config)');
+
+ if (!array_key_exists('server', $config)) {
+ throw new Exception("The 'server' configuration option is not set.");
+ } else {
+ $this->server = $config['server'];
+ }
+
+ if (array_key_exists('validateFingerprint', $config)) {
+ $this->validateFingerprint = $config['validateFingerprint'];
+ } else {
+ $this->validateFingerprint = null;
+ }
+
+ if (array_key_exists('cachedir', $config)) {
+ $globalConfig = SimpleSAML_Configuration::getInstance();
+ $this->cacheDir = $globalConfig->resolvePath($config['cachedir']);
+ } else {
+ $this->cacheDir = null;
+ }
+
+ if (array_key_exists('cachelength', $config)) {
+ $this->cacheLength = $config['cachelength'];
+ } else {
+ $this->cacheLength = 86400;
+ }
+ }
+
+
+ /**
+ * This function is not implemented.
+ *
+ * @param string $set The set we want to list metadata for.
+ *
+ * @return array An empty array.
+ */
+ public function getMetadataSet($set)
+ {
+ // we don't have this metadata set
+ return array();
+ }
+
+
+ /**
+ * Find the cache file name for an entity,
+ *
+ * @param string $set The metadata set this entity belongs to.
+ * @param string $entityId The entity id of this entity.
+ *
+ * @return string The full path to the cache file.
+ */
+ private function getCacheFilename($set, $entityId)
+ {
+ assert('is_string($set)');
+ assert('is_string($entityId)');
+
+ $cachekey = sha1($entityId);
+ return $this->cacheDir.'/'.$set.'-'.$cachekey.'.cached.xml';
+ }
+
+
+ /**
+ * Load a entity from the cache.
+ *
+ * @param string $set The metadata set this entity belongs to.
+ * @param string $entityId The entity id of this entity.
+ *
+ * @return array|NULL The associative array with the metadata for this entity, or NULL
+ * if the entity could not be found.
+ * @throws Exception If an error occurs while loading metadata from cache.
+ */
+ private function getFromCache($set, $entityId)
+ {
+ assert('is_string($set)');
+ assert('is_string($entityId)');
+
+ if (empty($this->cacheDir)) {
+ return null;
+ }
+
+ $cachefilename = $this->getCacheFilename($set, $entityId);
+ if (!file_exists($cachefilename)) {
+ return null;
+ }
+ if (!is_readable($cachefilename)) {
+ throw new Exception('Could not read cache file for entity ['.$cachefilename.']');
+ }
+ SimpleSAML_Logger::debug('MetaData - Handler.MDX: Reading cache ['.$entityId.'] => ['.$cachefilename.']');
+
+ /* Ensure that this metadata isn't older that the cachelength option allows. This
+ * must be verified based on the file, since this option may be changed after the
+ * file is written.
+ */
+ $stat = stat($cachefilename);
+ if ($stat['mtime'] + $this->cacheLength <= time()) {
+ SimpleSAML_Logger::debug('MetaData - Handler.MDX: Cache file older that the cachelength option allows.');
+ return null;
+ }
+
+ $rawData = file_get_contents($cachefilename);
+ if (empty($rawData)) {
+ $error = error_get_last();
+ throw new Exception(
+ 'Error reading metadata from cache file "'.$cachefilename.'": '.$error['message']
+ );
+ }
+
+ $data = unserialize($rawData);
+ if ($data === false) {
+ throw new Exception('Error unserializing cached data from file "'.$cachefilename.'".');
+ }
+
+ if (!is_array($data)) {
+ throw new Exception('Cached metadata from "'.$cachefilename.'" wasn\'t an array.');
+ }
+
+ return $data;
+ }
+
+
+ /**
+ * Save a entity to the cache.
+ *
+ * @param string $set The metadata set this entity belongs to.
+ * @param string $entityId The entity id of this entity.
+ * @param array $data The associative array with the metadata for this entity.
+ *
+ * @throws Exception If metadata cannot be written to cache.
+ */
+ private function writeToCache($set, $entityId, $data)
+ {
+ assert('is_string($set)');
+ assert('is_string($entityId)');
+ assert('is_array($data)');
+
+ if (empty($this->cacheDir)) {
+ return;
+ }
+
+ $cachefilename = $this->getCacheFilename($set, $entityId);
+ if (!is_writable(dirname($cachefilename))) {
+ throw new Exception('Could not write cache file for entity ['.$cachefilename.']');
+ }
+ SimpleSAML_Logger::debug('MetaData - Handler.MDX: Writing cache ['.$entityId.'] => ['.$cachefilename.']');
+ file_put_contents($cachefilename, serialize($data));
+ }
+
+
+ /**
+ * Retrieve metadata for the correct set from a SAML2Parser.
+ *
+ * @param SimpleSAML_Metadata_SAMLParser $entity A SAML2Parser representing an entity.
+ * @param string $set The metadata set we are looking for.
+ *
+ * @return array|NULL The associative array with the metadata, or NULL if no metadata for
+ * the given set was found.
+ */
+ private static function getParsedSet(SimpleSAML_Metadata_SAMLParser $entity, $set)
+ {
+ assert('is_string($set)');
+
+ switch ($set) {
+ case 'saml20-idp-remote':
+ return $entity->getMetadata20IdP();
+ case 'saml20-sp-remote':
+ return $entity->getMetadata20SP();
+ case 'shib13-idp-remote':
+ return $entity->getMetadata1xIdP();
+ case 'shib13-sp-remote':
+ return $entity->getMetadata1xSP();
+ case 'attributeauthority-remote':
+ $ret = $entity->getAttributeAuthorities();
+ return $ret[0];
+
+ default:
+ SimpleSAML_Logger::warning('MetaData - Handler.MDX: Unknown metadata set: '.$set);
+ }
+
+ return null;
+ }
+
+
+ /**
+ * Overriding this function from the superclass SimpleSAML_Metadata_MetaDataStorageSource.
+ *
+ * This function retrieves metadata for the given entity id in the given set of metadata.
+ * It will return NULL if it is unable to locate the metadata.
+ *
+ * This class implements this function using the getMetadataSet-function. A subclass should
+ * override this function if it doesn't implement the getMetadataSet function, or if the
+ * implementation of getMetadataSet is slow.
+ *
+ * @param string $index The entityId or metaindex we are looking up.
+ * @param string $set The set we are looking for metadata in.
+ *
+ * @return array An associative array with metadata for the given entity, or NULL if we are unable to
+ * locate the entity.
+ * @throws Exception If an error occurs while downloading metadata, validating the signature or writing to cache.
+ */
+ public function getMetaData($index, $set)
+ {
+ assert('is_string($index)');
+ assert('is_string($set)');
+
+ SimpleSAML_Logger::info('MetaData - Handler.MDX: Loading metadata entity ['.$index.'] from ['.$set.']');
+
+ // read from cache if possible
+ $data = $this->getFromCache($set, $index);
+
+ if ($data !== null && array_key_exists('expires', $data) && $data['expires'] < time()) {
+ // metadata has expired
+ $data = null;
+ }
+
+ if (isset($data)) {
+ // metadata found in cache and not expired
+ SimpleSAML_Logger::debug('MetaData - Handler.MDX: Using cached metadata for: '.$index.'.');
+ return $data;
+ }
+
+ // look at Metadata Query Protocol: https://github.com/iay/md-query/blob/master/draft-young-md-query.txt
+ $mdx_url = $this->server.'/entities/'.urlencode($index);
+
+ SimpleSAML_Logger::debug('MetaData - Handler.MDX: Downloading metadata for "'.$index.'" from ['.$mdx_url.']');
+ try {
+ $xmldata = \SimpleSAML\Utils\HTTP::fetch($mdx_url);
+ } catch (Exception $e) {
+ SimpleSAML_Logger::warning('Fetching metadata for '.$index.': '.$e->getMessage());
+ }
+
+ if (empty($xmldata)) {
+ $error = error_get_last();
+ throw new Exception(
+ 'Error downloading metadata for "'.$index.'" from "'.$mdx_url.'": '.$error['message']
+ );
+ }
+
+ /** @var string $xmldata */
+ $entity = SimpleSAML_Metadata_SAMLParser::parseString($xmldata);
+ SimpleSAML_Logger::debug('MetaData - Handler.MDX: Completed parsing of ['.$mdx_url.']');
+
+ if ($this->validateFingerprint !== null) {
+ if (!$entity->validateFingerprint($this->validateFingerprint)) {
+ throw new Exception('Error, could not verify signature for entity: '.$index.'".');
+ }
+ }
+
+ $data = self::getParsedSet($entity, $set);
+ if ($data === null) {
+ throw new Exception('No metadata for set "'.$set.'" available from "'.$index.'".');
+ }
+
+ $this->writeToCache($set, $index, $data);
+
+ return $data;
+ }
}
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerPdo.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerPdo.php
new file mode 100644
index 0000000..cb66c36
--- /dev/null
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerPdo.php
@@ -0,0 +1,244 @@
+<?php
+
+
+/**
+ * Class for handling metadata files stored in a database.
+ *
+ * This class has been based off a previous version written by
+ * mooknarf@gmail.com and patched to work with the latest version
+ * of SimpleSAMLphp
+ *
+ * @author Tyler Antonio, University of Alberta <tantonio@ualberta.ca>
+ * @package SimpleSAMLphp
+ */
+class SimpleSAML_Metadata_MetaDataStorageHandlerPdo extends SimpleSAML_Metadata_MetaDataStorageSource
+{
+
+ /**
+ * The PDO object
+ */
+ private $db;
+
+ /**
+ * Prefix to apply to the metadata table
+ */
+ private $tablePrefix;
+
+ /**
+ * This is an associative array which stores the different metadata sets we have loaded.
+ */
+ private $cachedMetadata = array();
+
+ /**
+ * All the metadata sets supported by this MetaDataStorageHandler
+ */
+ public $supportedSets = array(
+ 'adfs-idp-hosted',
+ 'adfs-sp-remote',
+ 'saml20-idp-hosted',
+ 'saml20-idp-remote',
+ 'saml20-sp-remote',
+ 'shib13-idp-hosted',
+ 'shib13-idp-remote',
+ 'shib13-sp-hosted',
+ 'shib13-sp-remote',
+ 'wsfed-idp-remote',
+ 'wsfed-sp-hosted'
+ );
+
+
+ /**
+ * This constructor initializes the PDO metadata storage handler with the specified
+ * configuration. The configuration is an associative array with the following
+ * possible elements (set in config.php):
+ * - 'usePersistentConnection': TRUE/FALSE if database connection should be persistent.
+ * - 'dsn': The database connection string.
+ * - 'username': Database user name
+ * - 'password': Password for the database user.
+ *
+ * @param array $config An associative array with the configuration for this handler.
+ */
+ public function __construct($config)
+ {
+ assert('is_array($config)');
+
+ $this->db = SimpleSAML\Database::getInstance();
+ }
+
+
+ /**
+ * This function loads the given set of metadata from a file to a configured database.
+ * This function returns NULL if it is unable to locate the given set in the metadata directory.
+ *
+ * @param string $set The set of metadata we are loading.
+ *
+ * @return array $metadata Associative array with the metadata, or NULL if we are unable to load metadata from the
+ * given file.
+ *
+ * @throws Exception If a database error occurs.
+ */
+ private function load($set)
+ {
+ assert('is_string($set)');
+
+ $tableName = $this->getTableName($set);
+
+ if (!in_array($set, $this->supportedSets)) {
+ return null;
+ }
+
+ $stmt = $this->db->read("SELECT entity_id, entity_data FROM $tableName");
+ if ($stmt->execute()) {
+ $metadata = array();
+
+ while ($d = $stmt->fetch()) {
+ $metadata[$d['entity_id']] = json_decode($d['entity_data'], true);
+ }
+
+ return $metadata;
+ } else {
+ throw new Exception('PDO metadata handler: Database error: '.var_export($this->db->getLastError(), true));
+ }
+ }
+
+
+ /**
+ * Retrieve a list of all available metadata for a given set.
+ *
+ * @param string $set The set we are looking for metadata in.
+ *
+ * @return array $metadata An associative array with all the metadata for the given set.
+ */
+ public function getMetadataSet($set)
+ {
+ assert('is_string($set)');
+
+ if (array_key_exists($set, $this->cachedMetadata)) {
+ return $this->cachedMetadata[$set];
+ }
+
+ $metadataSet = $this->load($set);
+ if ($metadataSet === null) {
+ $metadataSet = array();
+ }
+
+ foreach ($metadataSet as $entityId => &$entry) {
+ if (preg_match('/__DYNAMIC(:[0-9]+)?__/', $entityId)) {
+ $entry['entityid'] = $this->generateDynamicHostedEntityID($set);
+ } else {
+ $entry['entityid'] = $entityId;
+ }
+ }
+
+ $this->cachedMetadata[$set] = $metadataSet;
+ return $metadataSet;
+ }
+
+
+ private function generateDynamicHostedEntityID($set)
+ {
+ assert('is_string($set)');
+
+ // get the configuration
+ $baseurl = \SimpleSAML\Utils\HTTP::getBaseURL();
+
+ if ($set === 'saml20-idp-hosted') {
+ return $baseurl.'saml2/idp/metadata.php';
+ } elseif ($set === 'saml20-sp-hosted') {
+ return $baseurl.'saml2/sp/metadata.php';
+ } elseif ($set === 'shib13-idp-hosted') {
+ return $baseurl.'shib13/idp/metadata.php';
+ } elseif ($set === 'shib13-sp-hosted') {
+ return $baseurl.'shib13/sp/metadata.php';
+ } elseif ($set === 'wsfed-sp-hosted') {
+ return 'urn:federation:'.\SimpleSAML\Utils\HTTP::getSelfHost();
+ } elseif ($set === 'adfs-idp-hosted') {
+ return 'urn:federation:'.\SimpleSAML\Utils\HTTP::getSelfHost().':idp';
+ } else {
+ throw new Exception('Can not generate dynamic EntityID for metadata of this type: ['.$set.']');
+ }
+ }
+
+
+ /**
+ * Add metadata to the configured database
+ *
+ * @param string $index Entity ID
+ * @param string $set The set to add the metadata to
+ * @param array $entityData Metadata
+ *
+ * @return bool True/False if entry was successfully added
+ */
+ public function addEntry($index, $set, $entityData)
+ {
+ assert('is_string($index)');
+ assert('is_string($set)');
+ assert('is_array($entityData)');
+
+ if (!in_array($set, $this->supportedSets)) {
+ return false;
+ }
+
+ $tableName = $this->getTableName($set);
+
+ $metadata = $this->db->read(
+ "SELECT entity_id, entity_data FROM $tableName WHERE entity_id = :entity_id",
+ array(
+ 'entity_id' => $index,
+ )
+ );
+
+ $retrivedEntityIDs = $metadata->fetch();
+
+ $params = array(
+ 'entity_id' => $index,
+ 'entity_data' => json_encode($entityData),
+ );
+
+ if ($retrivedEntityIDs !== false && count($retrivedEntityIDs) > 0) {
+ $stmt = $this->db->write(
+ "UPDATE $tableName SET entity_data = :entity_data WHERE entity_id = :entity_id",
+ $params
+ );
+ } else {
+ $stmt = $this->db->write(
+ "INSERT INTO $tableName (entity_id, entity_data) VALUES (:entity_id, :entity_data)",
+ $params
+ );
+ }
+
+ return 1 === $stmt->rowCount();
+ }
+
+
+ /**
+ * Replace the -'s to an _ in table names for Metadata sets
+ * since SQL does not allow a - in a table name.
+ *
+ * @param string $table Table
+ *
+ * @return string Replaced table name
+ */
+ private function getTableName($table)
+ {
+ assert('is_string($table)');
+
+ return $this->db->applyPrefix(str_replace("-", "_", $this->tablePrefix.$table));
+ }
+
+
+ /**
+ * Initialize the configured database
+ */
+ public function initDatabase()
+ {
+ foreach ($this->supportedSets as $set) {
+ $tableName = $this->getTableName($set);
+ $this->db->write(
+ "CREATE TABLE IF NOT EXISTS $tableName (entity_id VARCHAR(255) PRIMARY KEY NOT NULL, entity_data ".
+ "TEXT NOT NULL)"
+ );
+ }
+ }
+
+}
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSerialize.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSerialize.php
index fae34c9..0357fbe 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSerialize.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSerialize.php
@@ -1,261 +1,283 @@
<?php
+
/**
* Class for handling metadata files in serialized format.
*
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-class SimpleSAML_Metadata_MetaDataStorageHandlerSerialize extends SimpleSAML_Metadata_MetaDataStorageSource {
-
- /**
- * The file extension we use for our metadata files.
- */
- const EXTENSION = '.serialized';
-
-
- /**
- * The base directory where metadata is stored.
- */
- private $directory;
-
-
- /**
- * Constructor for this metadata handler.
- *
- * Parses configuration.
- *
- * @param array $config The configuration for this metadata handler.
- */
- public function __construct($config) {
- assert('is_array($config)');
-
- $globalConfig = SimpleSAML_Configuration::getInstance();
-
- $cfgHelp = SimpleSAML_Configuration::loadFromArray($config, 'serialize metadata source');
-
- $this->directory = $cfgHelp->getString('directory');
-
- /* Resolve this directory relative to the simpleSAMLphp directory (unless it is
- * an absolute path).
- */
- $this->directory = $globalConfig->resolvePath($this->directory);
- }
-
-
- /**
- * Helper function for retrieving the path of a metadata file.
- *
- * @param string $entityId The entity ID.
- * @param string $set The metadata set.
- * @return string The path to the metadata file.
- */
- private function getMetadataPath($entityId, $set) {
- assert('is_string($entityId)');
- assert('is_string($set)');
-
- return $this->directory . '/' . rawurlencode($set) . '/' . rawurlencode($entityId) . self::EXTENSION;
- }
-
-
- /**
- * Retrieve a list of all available metadata sets.
- *
- * @return array An array with the available sets.
- */
- public function getMetadataSets() {
-
- $ret = array();
-
- $dh = @opendir($this->directory);
- if ($dh === FALSE) {
- SimpleSAML_Logger::warning('Serialize metadata handler: Unable to open directory: ' . var_export($this->directory, TRUE));
- return $ret;
- }
-
- while ( ($entry = readdir($dh)) !== FALSE) {
-
- if ($entry[0] === '.') {
- /* Skip '..', '.' and hidden files. */
- continue;
- }
-
- $path = $this->directory . '/' . $entry;
-
- if (!is_dir($path)) {
- SimpleSAML_Logger::warning('Serialize metadata handler: Metadata directory contained a file where only directories should exist: ' . var_export($path, TRUE));
- continue;
- }
-
- $ret[] = rawurldecode($entry);
- }
-
- closedir($dh);
-
- return $ret;
- }
-
-
- /**
- * Retrieve a list of all available metadata for a given set.
- *
- * @param string $set The set we are looking for metadata in.
- * @return array An associative array with all the metadata for the given set.
- */
- public function getMetadataSet($set) {
- assert('is_string($set)');
-
- $ret = array();
-
- $dir = $this->directory . '/' . rawurlencode($set);
- if (!is_dir($dir)) {
- /* Probably some code asked for a metadata set which wasn't available. */
- return $ret;
- }
-
- $dh = @opendir($dir);
- if ($dh === FALSE) {
- SimpleSAML_Logger::warning('Serialize metadata handler: Unable to open directory: ' . var_export($dir, TRUE));
- return $ret;
- }
-
- $extLen = strlen(self::EXTENSION);
-
- while ( ($file = readdir($dh)) !== FALSE) {
- if (strlen($file) <= $extLen) {
- continue;
- }
-
- if (substr($file, -$extLen) !== self::EXTENSION) {
- continue;
- }
-
- $entityId = substr($file, 0, -$extLen);
- $entityId = rawurldecode($entityId);
-
- $md = $this->getMetaData($entityId, $set);
- if ($md !== NULL) {
- $ret[$entityId] = $md;
- }
- }
-
- closedir($dh);
-
- return $ret;
- }
-
-
- /**
- * Retrieve a metadata entry.
- *
- * @param string $entityId The entityId we are looking up.
- * @param string $set The set we are looking for metadata in.
- * @return array An associative array with metadata for the given entity, or NULL if we are unable to
- * locate the entity.
- */
- public function getMetaData($entityId, $set) {
- assert('is_string($entityId)');
- assert('is_string($set)');
-
- $filePath = $this->getMetadataPath($entityId, $set);
-
- if (!file_exists($filePath)) {
- return NULL;
- }
-
- $data = @file_get_contents($filePath);
- if ($data === FALSE) {
- $error = error_get_last();
- SimpleSAML_Logger::warning('Error reading file ' . $filePath .
- ': ' . $error['message']);
- return NULL;
- }
-
- $data = @unserialize($data);
- if ($data === FALSE) {
- SimpleSAML_Logger::warning('Error deserializing file: ' . $filePath);
- return NULL;
- }
-
- return $data;
- }
-
-
- /**
- * Save a metadata entry.
- *
- * @param string $entityId The entityId of the metadata entry.
- * @param string $set The metadata set this metadata entry belongs to.
- * @param array $metadata The metadata.
- */
- public function saveMetadata($entityId, $set, $metadata) {
- assert('is_string($entityId)');
- assert('is_string($set)');
- assert('is_array($metadata)');
-
- $filePath = $this->getMetadataPath($entityId, $set);
- $newPath = $filePath . '.new';
-
- $dir = dirname($filePath);
- if (!is_dir($dir)) {
- SimpleSAML_Logger::info('Creating directory: ' . $dir);
- $res = @mkdir($dir, 0777, TRUE);
- if ($res === FALSE) {
- $error = error_get_last();
- SimpleSAML_Logger::error('Failed to create directory ' . $dir .
- ': ' . $error['message']);
- return FALSE;
- }
- }
-
- $data = serialize($metadata);
-
- SimpleSAML_Logger::debug('Writing: ' . $newPath);
-
- $res = file_put_contents($newPath, $data);
- if ($res === FALSE) {
- $error = error_get_last();
- SimpleSAML_Logger::error('Error saving file ' . $newPath .
- ': ' . $error['message']);
- return FALSE;
- }
-
- $res = rename($newPath, $filePath);
- if ($res === FALSE) {
- $error = error_get_last();
- SimpleSAML_Logger::error('Error renaming ' . $newPath . ' to ' . $filePath .
- ': ' . $error['message']);
- return FALSE;
- }
-
-
- return TRUE;
- }
-
-
- /**
- * Delete a metadata entry.
- *
- * @param string $entityId The entityId of the metadata entry.
- * @param string $set The metadata set this metadata entry belongs to.
- */
- public function deleteMetadata($entityId, $set) {
- assert('is_string($entityId)');
- assert('is_string($set)');
-
- $filePath = $this->getMetadataPath($entityId, $set);
-
- if (!file_exists($filePath)) {
- SimpleSAML_Logger::warning('Attempted to erase non-existant metadata entry ' .
- var_export($entityId, TRUE) . ' in set ' . var_export($set, TRUE) . '.');
- return;
- }
-
- $res = unlink($filePath);
- if ($res === FALSE) {
- $error = error_get_last();
- SimpleSAML_Logger::error('Failed to delete file ' . $filePath .
- ': ' . $error['message']);
- }
- }
-
+class SimpleSAML_Metadata_MetaDataStorageHandlerSerialize extends SimpleSAML_Metadata_MetaDataStorageSource
+{
+
+ /**
+ * The file extension we use for our metadata files.
+ *
+ * @var string
+ */
+ const EXTENSION = '.serialized';
+
+
+ /**
+ * The base directory where metadata is stored.
+ *
+ * @var string
+ */
+ private $directory;
+
+
+ /**
+ * Constructor for this metadata handler.
+ *
+ * Parses configuration.
+ *
+ * @param array $config The configuration for this metadata handler.
+ */
+ public function __construct($config)
+ {
+ assert('is_array($config)');
+
+ $globalConfig = SimpleSAML_Configuration::getInstance();
+
+ $cfgHelp = SimpleSAML_Configuration::loadFromArray($config, 'serialize metadata source');
+
+ $this->directory = $cfgHelp->getString('directory');
+
+ /* Resolve this directory relative to the simpleSAMLphp directory (unless it is
+ * an absolute path).
+ */
+ $this->directory = $globalConfig->resolvePath($this->directory);
+ }
+
+
+ /**
+ * Helper function for retrieving the path of a metadata file.
+ *
+ * @param string $entityId The entity ID.
+ * @param string $set The metadata set.
+ *
+ * @return string The path to the metadata file.
+ */
+ private function getMetadataPath($entityId, $set)
+ {
+ assert('is_string($entityId)');
+ assert('is_string($set)');
+
+ return $this->directory.'/'.rawurlencode($set).'/'.rawurlencode($entityId).self::EXTENSION;
+ }
+
+
+ /**
+ * Retrieve a list of all available metadata sets.
+ *
+ * @return array An array with the available sets.
+ */
+ public function getMetadataSets()
+ {
+ $ret = array();
+
+ $dh = @opendir($this->directory);
+ if ($dh === false) {
+ SimpleSAML_Logger::warning(
+ 'Serialize metadata handler: Unable to open directory: '.var_export($this->directory, true)
+ );
+ return $ret;
+ }
+
+ while (($entry = readdir($dh)) !== false) {
+
+ if ($entry[0] === '.') {
+ // skip '..', '.' and hidden files
+ continue;
+ }
+
+ $path = $this->directory.'/'.$entry;
+
+ if (!is_dir($path)) {
+ SimpleSAML_Logger::warning(
+ 'Serialize metadata handler: Metadata directory contained a file where only directories should '.
+ 'exist: '.var_export($path, true)
+ );
+ continue;
+ }
+
+ $ret[] = rawurldecode($entry);
+ }
+
+ closedir($dh);
+
+ return $ret;
+ }
+
+
+ /**
+ * Retrieve a list of all available metadata for a given set.
+ *
+ * @param string $set The set we are looking for metadata in.
+ *
+ * @return array An associative array with all the metadata for the given set.
+ */
+ public function getMetadataSet($set)
+ {
+ assert('is_string($set)');
+
+ $ret = array();
+
+ $dir = $this->directory.'/'.rawurlencode($set);
+ if (!is_dir($dir)) {
+ // probably some code asked for a metadata set which wasn't available
+ return $ret;
+ }
+
+ $dh = @opendir($dir);
+ if ($dh === false) {
+ SimpleSAML_Logger::warning('Serialize metadata handler: Unable to open directory: '.var_export($dir, true));
+ return $ret;
+ }
+
+ $extLen = strlen(self::EXTENSION);
+
+ while (($file = readdir($dh)) !== false) {
+ if (strlen($file) <= $extLen) {
+ continue;
+ }
+
+ if (substr($file, -$extLen) !== self::EXTENSION) {
+ continue;
+ }
+
+ $entityId = substr($file, 0, -$extLen);
+ $entityId = rawurldecode($entityId);
+
+ $md = $this->getMetaData($entityId, $set);
+ if ($md !== null) {
+ $ret[$entityId] = $md;
+ }
+ }
+
+ closedir($dh);
+
+ return $ret;
+ }
+
+
+ /**
+ * Retrieve a metadata entry.
+ *
+ * @param string $entityId The entityId we are looking up.
+ * @param string $set The set we are looking for metadata in.
+ *
+ * @return array An associative array with metadata for the given entity, or NULL if we are unable to
+ * locate the entity.
+ */
+ public function getMetaData($entityId, $set)
+ {
+ assert('is_string($entityId)');
+ assert('is_string($set)');
+
+ $filePath = $this->getMetadataPath($entityId, $set);
+
+ if (!file_exists($filePath)) {
+ return null;
+ }
+
+ $data = @file_get_contents($filePath);
+ if ($data === false) {
+ $error = error_get_last();
+ SimpleSAML_Logger::warning(
+ 'Error reading file '.$filePath.': '.$error['message']
+ );
+ return null;
+ }
+
+ $data = @unserialize($data);
+ if ($data === false) {
+ SimpleSAML_Logger::warning('Error unserializing file: '.$filePath);
+ return null;
+ }
+
+ return $data;
+ }
+
+
+ /**
+ * Save a metadata entry.
+ *
+ * @param string $entityId The entityId of the metadata entry.
+ * @param string $set The metadata set this metadata entry belongs to.
+ * @param array $metadata The metadata.
+ *
+ * @return boolean True if successfully saved, false otherwise.
+ */
+ public function saveMetadata($entityId, $set, $metadata)
+ {
+ assert('is_string($entityId)');
+ assert('is_string($set)');
+ assert('is_array($metadata)');
+
+ $filePath = $this->getMetadataPath($entityId, $set);
+ $newPath = $filePath.'.new';
+
+ $dir = dirname($filePath);
+ if (!is_dir($dir)) {
+ SimpleSAML_Logger::info('Creating directory: '.$dir);
+ $res = @mkdir($dir, 0777, true);
+ if ($res === false) {
+ $error = error_get_last();
+ SimpleSAML_Logger::error('Failed to create directory '.$dir.': '.$error['message']);
+ return false;
+ }
+ }
+
+ $data = serialize($metadata);
+
+ SimpleSAML_Logger::debug('Writing: '.$newPath);
+
+ $res = file_put_contents($newPath, $data);
+ if ($res === false) {
+ $error = error_get_last();
+ SimpleSAML_Logger::error('Error saving file '.$newPath.': '.$error['message']);
+ return false;
+ }
+
+ $res = rename($newPath, $filePath);
+ if ($res === false) {
+ $error = error_get_last();
+ SimpleSAML_Logger::error('Error renaming '.$newPath.' to '.$filePath.': '.$error['message']);
+ return false;
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Delete a metadata entry.
+ *
+ * @param string $entityId The entityId of the metadata entry.
+ * @param string $set The metadata set this metadata entry belongs to.
+ */
+ public function deleteMetadata($entityId, $set)
+ {
+ assert('is_string($entityId)');
+ assert('is_string($set)');
+
+ $filePath = $this->getMetadataPath($entityId, $set);
+
+ if (!file_exists($filePath)) {
+ SimpleSAML_Logger::warning(
+ 'Attempted to erase nonexistent metadata entry '.
+ var_export($entityId, true).' in set '.var_export($set, true).'.'
+ );
+ return;
+ }
+
+ $res = unlink($filePath);
+ if ($res === false) {
+ $error = error_get_last();
+ SimpleSAML_Logger::error(
+ 'Failed to delete file '.$filePath.
+ ': '.$error['message']
+ );
+ }
+ }
}
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerXML.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerXML.php
index 9cfdb0c..36c3568 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerXML.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerXML.php
@@ -1,102 +1,109 @@
<?php
+
/**
* This class implements a metadata source which loads metadata from XML files.
* The XML files should be in the SAML 2.0 metadata format.
*
* @author Olav Morken, UNINETT AS.
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-class SimpleSAML_Metadata_MetaDataStorageHandlerXML extends SimpleSAML_Metadata_MetaDataStorageSource {
-
- /**
- * This variable contains an associative array with the parsed metadata.
- */
- private $metadata;
-
-
- /**
- * This function initializes the XML metadata source. The configuration must contain one of
- * the following options:
- * - 'file': Path to a file with the metadata. This path is relative to the simpleSAMLphp
- * base directory.
- * - 'url': URL we should download the metadata from. This is only meant for testing.
- *
- * @param $config The configuration for this instance of the XML metadata source.
- */
- protected function __construct($config) {
-
- /* Get the configuration. */
- $globalConfig = SimpleSAML_Configuration::getInstance();
-
- if(array_key_exists('file', $config)) {
- $src = $globalConfig->resolvePath($config['file']);
- } elseif(array_key_exists('url', $config)) {
- $src = $config['url'];
- } else {
- throw new Exception('Missing either \'file\' or \'url\' in XML metadata source configuration.');
- }
-
-
- $SP1x = array();
- $IdP1x = array();
- $SP20 = array();
- $IdP20 = array();
- $AAD = array();
-
- $entities = SimpleSAML_Metadata_SAMLParser::parseDescriptorsFile($src);
- foreach($entities as $entityId => $entity) {
-
- $md = $entity->getMetadata1xSP();
- if($md !== NULL) {
- $SP1x[$entityId] = $md;
- }
-
- $md = $entity->getMetadata1xIdP();
- if($md !== NULL) {
- $IdP1x[$entityId] = $md;
- }
-
- $md = $entity->getMetadata20SP();
- if($md !== NULL) {
- $SP20[$entityId] = $md;
- }
-
- $md = $entity->getMetadata20IdP();
- if($md !== NULL) {
- $IdP20[$entityId] = $md;
- }
-
- $md = $entity->getAttributeAuthorities();
- if (count($md) > 0) {
- $AAD[$entityId] = $md[0];
- }
- }
-
- $this->metadata = array(
- 'shib13-sp-remote' => $SP1x,
- 'shib13-idp-remote' => $IdP1x,
- 'saml20-sp-remote' => $SP20,
- 'saml20-idp-remote' => $IdP20,
- 'attributeauthority-remote' => $AAD,
- );
-
- }
-
-
- /**
- * This function returns an associative array with metadata for all entities in the given set. The
- * key of the array is the entity id.
- *
- * @param $set The set we want to list metadata for.
- * @return An associative array with all entities in the given set.
- */
- public function getMetadataSet($set) {
- if(array_key_exists($set, $this->metadata)) {
- return $this->metadata[$set];
- }
-
- /* We don't have this metadata set. */
- return array();
- }
+class SimpleSAML_Metadata_MetaDataStorageHandlerXML extends SimpleSAML_Metadata_MetaDataStorageSource
+{
+
+ /**
+ * This variable contains an associative array with the parsed metadata.
+ *
+ * @var array
+ */
+ private $metadata;
+
+
+ /**
+ * This function initializes the XML metadata source. The configuration must contain one of
+ * the following options:
+ * - 'file': Path to a file with the metadata. This path is relative to the simpleSAMLphp
+ * base directory.
+ * - 'url': URL we should download the metadata from. This is only meant for testing.
+ *
+ * @param array $config The configuration for this instance of the XML metadata source.
+ *
+ * @throws Exception If neither the 'file' or 'url' options are defined in the configuration.
+ */
+ protected function __construct($config)
+ {
+ // get the configuration
+ $globalConfig = SimpleSAML_Configuration::getInstance();
+
+ if (array_key_exists('file', $config)) {
+ $src = $globalConfig->resolvePath($config['file']);
+ } elseif (array_key_exists('url', $config)) {
+ $src = $config['url'];
+ } else {
+ throw new Exception("Missing either 'file' or 'url' in XML metadata source configuration.");
+ }
+
+
+ $SP1x = array();
+ $IdP1x = array();
+ $SP20 = array();
+ $IdP20 = array();
+ $AAD = array();
+
+ $entities = SimpleSAML_Metadata_SAMLParser::parseDescriptorsFile($src);
+ foreach ($entities as $entityId => $entity) {
+
+ $md = $entity->getMetadata1xSP();
+ if ($md !== null) {
+ $SP1x[$entityId] = $md;
+ }
+
+ $md = $entity->getMetadata1xIdP();
+ if ($md !== null) {
+ $IdP1x[$entityId] = $md;
+ }
+
+ $md = $entity->getMetadata20SP();
+ if ($md !== null) {
+ $SP20[$entityId] = $md;
+ }
+
+ $md = $entity->getMetadata20IdP();
+ if ($md !== null) {
+ $IdP20[$entityId] = $md;
+ }
+
+ $md = $entity->getAttributeAuthorities();
+ if (count($md) > 0) {
+ $AAD[$entityId] = $md[0];
+ }
+ }
+
+ $this->metadata = array(
+ 'shib13-sp-remote' => $SP1x,
+ 'shib13-idp-remote' => $IdP1x,
+ 'saml20-sp-remote' => $SP20,
+ 'saml20-idp-remote' => $IdP20,
+ 'attributeauthority-remote' => $AAD,
+ );
+ }
+
+
+ /**
+ * This function returns an associative array with metadata for all entities in the given set. The
+ * key of the array is the entity id.
+ *
+ * @param string $set The set we want to list metadata for.
+ *
+ * @return array An associative array with all entities in the given set.
+ */
+ public function getMetadataSet($set)
+ {
+ if (array_key_exists($set, $this->metadata)) {
+ return $this->metadata[$set];
+ }
+
+ // we don't have this metadata set
+ return array();
+ }
}
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageSource.php b/lib/SimpleSAML/Metadata/MetaDataStorageSource.php
index 1fc1160..bc7e896 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageSource.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageSource.php
@@ -1,5 +1,6 @@
<?php
+
/**
* This abstract class defines an interface for metadata storage sources.
*
@@ -9,219 +10,247 @@
*
* @author Olav Morken, UNINETT AS.
* @author Andreas Aakre Solberg, UNINETT AS.
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-abstract class SimpleSAML_Metadata_MetaDataStorageSource {
-
-
- /**
- * Parse array with metadata sources.
- *
- * This function accepts an array with metadata sources, and returns an array with
- * each metadata source as an object.
- *
- * @param array $sourcesConfig Array with metadata source configuration.
- * @return array Parsed metadata configuration.
- */
- public static function parseSources($sourcesConfig) {
- assert('is_array($sourcesConfig)');
-
- $sources = array();
-
- foreach ($sourcesConfig as $sourceConfig) {
- if (!is_array($sourceConfig)) {
- throw new Exception('Found an element in metadata source' .
- ' configuration which wasn\'t an array.');
- }
-
- $sources[] = self::getSource($sourceConfig);
- }
-
- return $sources;
- }
-
-
- /**
- * This function creates a metadata source based on the given configuration.
- * The type of source is based on the 'type' parameter in the configuration.
- * The default type is 'flatfile'.
- *
- * @param $sourceConfig Associative array with the configuration for this metadata source.
- * @return An instance of a metadata source with the given configuration.
- */
- public static function getSource($sourceConfig) {
-
- assert(is_array($sourceConfig));
-
- if(array_key_exists('type', $sourceConfig)) {
- $type = $sourceConfig['type'];
- } else {
- $type = 'flatfile';
- }
-
- switch($type) {
- case 'flatfile':
- return new SimpleSAML_Metadata_MetaDataStorageHandlerFlatFile($sourceConfig);
- case 'xml':
- return new SimpleSAML_Metadata_MetaDataStorageHandlerXML($sourceConfig);
- case 'serialize':
- return new SimpleSAML_Metadata_MetaDataStorageHandlerSerialize($sourceConfig);
- case 'mdx':
- return new SimpleSAML_Metadata_MetaDataStorageHandlerMDX($sourceConfig);
- default:
- throw new Exception('Invalid metadata source type: "' . $type . '".');
- }
- }
-
-
- /**
- * This function attempts to generate an associative array with metadata for all entities in the
- * given set. The key of the array is the entity id.
- *
- * A subclass should override this function if it is able to easily generate this list.
- *
- * @param $set The set we want to list metadata for.
- * @return An associative array with all entities in the given set, or an empty array if we are
- * unable to generate this list.
- */
- public function getMetadataSet($set) {
- return array();
- }
-
-
- /**
- * This function resolves an host/path combination to an entity id.
- *
- * This class implements this function using the getMetadataSet-function. A subclass should
- * override this function if it doesn't implement the getMetadataSet function, or if the
- * implementation of getMetadataSet is slow.
- *
- * @param $hostPath The host/path combination we are looking up.
- * @param $set Which set of metadata we are looking it up in.
- * @param $type Do you want to return the metaindex or the entityID. [entityid|metaindex]
-
- * @return An entity id which matches the given host/path combination, or NULL if
- * we are unable to locate one which matches.
- */
- public function getEntityIdFromHostPath($hostPath, $set, $type = 'entityid') {
-
- $metadataSet = $this->getMetadataSet($set);
- if ($metadataSet === NULL) {
- /* This metadata source does not have this metadata set. */
- return NULL;
- }
-
- foreach($metadataSet AS $index => $entry) {
-
- if(!array_key_exists('host', $entry)) {
- continue;
- }
-
- if($hostPath === $entry['host']) {
- if ($type === 'entityid') {
- return $entry['entityid'];
- } else {
- return $index;
- }
- }
- }
-
- /* No entries matched - we should return NULL. */
- return NULL;
- }
-
- /**
- * This function will go through all the metadata, and check the hint.cidr
- * parameter, which defines a network space (ip range) for each remote entry.
- * This function returns the entityID for any of the entities that have an
- * IP range which the IP falls within.
- *
- * @param $set Which set of metadata we are looking it up in.
- * @param $ip IP address
- * @param $type Do you want to return the metaindex or the entityID. [entityid|metaindex]
- * @return The entity id of a entity which have a CIDR hint where the provided
- * IP address match.
- */
- public function getPreferredEntityIdFromCIDRhint($set, $ip, $type = 'entityid') {
-
- $metadataSet = $this->getMetadataSet($set);
-
- foreach($metadataSet AS $index => $entry) {
-
- if(!array_key_exists('hint.cidr', $entry)) continue;
- if(!is_array($entry['hint.cidr'])) continue;
-
- foreach ($entry['hint.cidr'] AS $hint_entry) {
- if (SimpleSAML\Utils\Net::ipCIDRcheck($hint_entry, $ip)) {
- if ($type === 'entityid') {
- return $entry['entityid'];
- } else {
- return $index;
- }
- }
- }
-
- }
-
- /* No entries matched - we should return NULL. */
- return NULL;
- }
-
- /*
- *
- */
- private function lookupIndexFromEntityId($entityId, $set) {
-
- assert('is_string($entityId)');
- assert('isset($set)');
-
- $metadataSet = $this->getMetadataSet($set);
-
- /* Check for hostname. */
- $currenthost = \SimpleSAML\Utils\HTTP::getSelfHost(); // sp.example.org
- if(strpos($currenthost, ":") !== FALSE) {
- $currenthostdecomposed = explode(":", $currenthost);
- $currenthost = $currenthostdecomposed[0];
- }
-
- foreach($metadataSet AS $index => $entry) {
- if ($index === $entityId) return $index;
- if ($entry['entityid'] === $entityId) {
- if ($entry['host'] === '__DEFAULT__' || $entry['host'] === $currenthost) return $index;
- }
- }
-
- return NULL;
- }
-
- /**
- * This function retrieves metadata for the given entity id in the given set of metadata.
- * It will return NULL if it is unable to locate the metadata.
- *
- * This class implements this function using the getMetadataSet-function. A subclass should
- * override this function if it doesn't implement the getMetadataSet function, or if the
- * implementation of getMetadataSet is slow.
- *
- * @param $index The entityId or metaindex we are looking up.
- * @param $set The set we are looking for metadata in.
- * @return An associative array with metadata for the given entity, or NULL if we are unable to
- * locate the entity.
- */
- public function getMetaData($index, $set) {
-
- assert('is_string($index)');
- assert('isset($set)');
-
- $metadataSet = $this->getMetadataSet($set);
-
- if(array_key_exists($index, $metadataSet))
- return $metadataSet[$index];
-
- $indexlookup = $this->lookupIndexFromEntityId($index, $set);
- if (isset($indexlookup) && array_key_exists($indexlookup, $metadataSet))
- return $metadataSet[$indexlookup];
-
- return NULL;
- }
+abstract class SimpleSAML_Metadata_MetaDataStorageSource
+{
+
+
+ /**
+ * Parse array with metadata sources.
+ *
+ * This function accepts an array with metadata sources, and returns an array with
+ * each metadata source as an object.
+ *
+ * @param array $sourcesConfig Array with metadata source configuration.
+ *
+ * @return array Parsed metadata configuration.
+ *
+ * @throws Exception If something is wrong in the configuration.
+ */
+ public static function parseSources($sourcesConfig)
+ {
+ assert('is_array($sourcesConfig)');
+
+ $sources = array();
+
+ foreach ($sourcesConfig as $sourceConfig) {
+ if (!is_array($sourceConfig)) {
+ throw new Exception("Found an element in metadata source configuration which wasn't an array.");
+ }
+
+ $sources[] = self::getSource($sourceConfig);
+ }
+
+ return $sources;
+ }
+
+
+ /**
+ * This function creates a metadata source based on the given configuration.
+ * The type of source is based on the 'type' parameter in the configuration.
+ * The default type is 'flatfile'.
+ *
+ * @param array $sourceConfig Associative array with the configuration for this metadata source.
+ *
+ * @return mixed An instance of a metadata source with the given configuration.
+ *
+ * @throws Exception If the metadata source type is invalid.
+ */
+ public static function getSource($sourceConfig)
+ {
+ assert(is_array($sourceConfig));
+
+ if (array_key_exists('type', $sourceConfig)) {
+ $type = $sourceConfig['type'];
+ } else {
+ $type = 'flatfile';
+ }
+
+ switch ($type) {
+ case 'flatfile':
+ return new SimpleSAML_Metadata_MetaDataStorageHandlerFlatFile($sourceConfig);
+ case 'xml':
+ return new SimpleSAML_Metadata_MetaDataStorageHandlerXML($sourceConfig);
+ case 'serialize':
+ return new SimpleSAML_Metadata_MetaDataStorageHandlerSerialize($sourceConfig);
+ case 'mdx':
+ return new SimpleSAML_Metadata_MetaDataStorageHandlerMDX($sourceConfig);
+ case 'pdo':
+ return new SimpleSAML_Metadata_MetaDataStorageHandlerPdo($sourceConfig);
+ default:
+ throw new Exception('Invalid metadata source type: "'.$type.'".');
+ }
+ }
+
+
+ /**
+ * This function attempts to generate an associative array with metadata for all entities in the
+ * given set. The key of the array is the entity id.
+ *
+ * A subclass should override this function if it is able to easily generate this list.
+ *
+ * @param string $set The set we want to list metadata for.
+ *
+ * @return array An associative array with all entities in the given set, or an empty array if we are
+ * unable to generate this list.
+ */
+ public function getMetadataSet($set)
+ {
+ return array();
+ }
+
+
+ /**
+ * This function resolves an host/path combination to an entity id.
+ *
+ * This class implements this function using the getMetadataSet-function. A subclass should
+ * override this function if it doesn't implement the getMetadataSet function, or if the
+ * implementation of getMetadataSet is slow.
+ *
+ * @param string $hostPath The host/path combination we are looking up.
+ * @param string $set Which set of metadata we are looking it up in.
+ * @param string $type Do you want to return the metaindex or the entityID. [entityid|metaindex]
+ *
+ * @return string|null An entity id which matches the given host/path combination, or NULL if
+ * we are unable to locate one which matches.
+ */
+ public function getEntityIdFromHostPath($hostPath, $set, $type = 'entityid')
+ {
+
+ $metadataSet = $this->getMetadataSet($set);
+ if ($metadataSet === null) {
+ // this metadata source does not have this metadata set
+ return null;
+ }
+
+ foreach ($metadataSet as $index => $entry) {
+
+ if (!array_key_exists('host', $entry)) {
+ continue;
+ }
+
+ if ($hostPath === $entry['host']) {
+ if ($type === 'entityid') {
+ return $entry['entityid'];
+ } else {
+ return $index;
+ }
+ }
+ }
+
+ // no entries matched, we should return null
+ return null;
+ }
+
+
+ /**
+ * This function will go through all the metadata, and check the hint.cidr
+ * parameter, which defines a network space (ip range) for each remote entry.
+ * This function returns the entityID for any of the entities that have an
+ * IP range which the IP falls within.
+ *
+ * @param string $set Which set of metadata we are looking it up in.
+ * @param string $ip IP address
+ * @param string $type Do you want to return the metaindex or the entityID. [entityid|metaindex]
+ *
+ * @return string The entity id of a entity which have a CIDR hint where the provided
+ * IP address match.
+ */
+ public function getPreferredEntityIdFromCIDRhint($set, $ip, $type = 'entityid')
+ {
+
+ $metadataSet = $this->getMetadataSet($set);
+
+ foreach ($metadataSet as $index => $entry) {
+
+ if (!array_key_exists('hint.cidr', $entry)) {
+ continue;
+ }
+ if (!is_array($entry['hint.cidr'])) {
+ continue;
+ }
+
+ foreach ($entry['hint.cidr'] as $hint_entry) {
+ if (SimpleSAML\Utils\Net::ipCIDRcheck($hint_entry, $ip)) {
+ if ($type === 'entityid') {
+ return $entry['entityid'];
+ } else {
+ return $index;
+ }
+ }
+ }
+ }
+
+ // no entries matched, we should return null
+ return null;
+ }
+
+
+ /*
+ *
+ */
+ private function lookupIndexFromEntityId($entityId, $set)
+ {
+ assert('is_string($entityId)');
+ assert('isset($set)');
+
+ $metadataSet = $this->getMetadataSet($set);
+
+ // check for hostname
+ $currenthost = \SimpleSAML\Utils\HTTP::getSelfHost(); // sp.example.org
+ if (strpos($currenthost, ":") !== false) {
+ $currenthostdecomposed = explode(":", $currenthost);
+ $currenthost = $currenthostdecomposed[0];
+ }
+
+ foreach ($metadataSet as $index => $entry) {
+ if ($index === $entityId) {
+ return $index;
+ }
+ if ($entry['entityid'] === $entityId) {
+ if ($entry['host'] === '__DEFAULT__' || $entry['host'] === $currenthost) {
+ return $index;
+ }
+ }
+ }
+
+ return null;
+ }
+
+
+ /**
+ * This function retrieves metadata for the given entity id in the given set of metadata.
+ * It will return NULL if it is unable to locate the metadata.
+ *
+ * This class implements this function using the getMetadataSet-function. A subclass should
+ * override this function if it doesn't implement the getMetadataSet function, or if the
+ * implementation of getMetadataSet is slow.
+ *
+ * @param string $index The entityId or metaindex we are looking up.
+ * @param string $set The set we are looking for metadata in.
+ *
+ * @return array An associative array with metadata for the given entity, or NULL if we are unable to
+ * locate the entity.
+ */
+ public function getMetaData($index, $set)
+ {
+
+ assert('is_string($index)');
+ assert('isset($set)');
+
+ $metadataSet = $this->getMetadataSet($set);
+
+ if (array_key_exists($index, $metadataSet)) {
+ return $metadataSet[$index];
+ }
+
+ $indexlookup = $this->lookupIndexFromEntityId($index, $set);
+ if (isset($indexlookup) && array_key_exists($indexlookup, $metadataSet)) {
+ return $metadataSet[$indexlookup];
+ }
+
+ return null;
+ }
}
diff --git a/lib/SimpleSAML/Metadata/SAMLBuilder.php b/lib/SimpleSAML/Metadata/SAMLBuilder.php
index 703d28c..29e296c 100644
--- a/lib/SimpleSAML/Metadata/SAMLBuilder.php
+++ b/lib/SimpleSAML/Metadata/SAMLBuilder.php
@@ -1,5 +1,6 @@
<?php
+
/**
* Class for generating SAML 2.0 metadata from simpleSAMLphp metadata arrays.
*
@@ -7,716 +8,761 @@
*
* @package simpleSAMLphp
*/
-class SimpleSAML_Metadata_SAMLBuilder {
+class SimpleSAML_Metadata_SAMLBuilder
+{
+ /**
+ * The EntityDescriptor we are building.
+ *
+ * @var SAML2_XML_md_EntityDescriptor
+ */
+ private $entityDescriptor;
- /**
- * The EntityDescriptor we are building.
- */
- private $entityDescriptor;
-
-
- private $maxCache = NULL;
- private $maxDuration = NULL;
-
- /**
- * Initialize the builder.
- *
- * @param string $entityId The entity id of the entity.
- */
- public function __construct($entityId, $maxCache = NULL, $maxDuration = NULL) {
- assert('is_string($entityId)');
-
- $this->maxCache = $maxCache;
- $this->maxDuration = $maxDuration;
-
- $this->entityDescriptor = new SAML2_XML_md_EntityDescriptor();
- $this->entityDescriptor->entityID = $entityId;
- }
-
- private function setExpiration($metadata) {
-
- if (array_key_exists('expire', $metadata)) {
- if ($metadata['expire'] - time() < $this->maxDuration)
- $this->maxDuration = $metadata['expire'] - time();
- }
-
- if ($this->maxCache !== NULL)
- $this->entityDescriptor->cacheDuration = 'PT' . $this->maxCache . 'S';
- if ($this->maxDuration !== NULL)
- $this->entityDescriptor->validUntil = time() + $this->maxDuration;
- }
-
-
- /**
- * Retrieve the EntityDescriptor.
- *
- * Retrieve the EntityDescriptor element which is generated for this entity.
- * @return DOMElement The EntityDescriptor element for this entity.
- */
- public function getEntityDescriptor() {
-
- $xml = $this->entityDescriptor->toXML();
- $xml->ownerDocument->appendChild($xml);
-
- return $xml;
- }
-
-
- /**
- * Retrieve the EntityDescriptor as text.
- *
- * This function serializes this EntityDescriptor, and returns it as text.
- *
- * @param bool $formatted Whether the returned EntityDescriptor should be
- * formatted first.
- * @return string The serialized EntityDescriptor.
- */
- public function getEntityDescriptorText($formatted = TRUE) {
- assert('is_bool($formatted)');
-
- $xml = $this->getEntityDescriptor();
- if ($formatted) {
- SimpleSAML\Utils\XML::formatDOMElement($xml);
- }
-
- return $xml->ownerDocument->saveXML();
- }
-
- public function addSecurityTokenServiceType($metadata) {
- assert('is_array($metadata)');
- assert('isset($metadata["entityid"])');
- assert('isset($metadata["metadata-set"])');
-
- $metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
- $defaultEndpoint = $metadata->getDefaultEndpoint('SingleSignOnService');
- $e = new sspmod_adfs_SAML2_XML_fed_SecurityTokenServiceType();
- $e->Location = $defaultEndpoint['Location'];
-
- $this->addCertificate($e, $metadata);
-
- $this->entityDescriptor->RoleDescriptor[] = $e;
- }
-
- /**
- * @param SimpleSAML_Configuration $metadata Metadata.
- * @param $e Reference to the element where the Extensions element should be included.
- */
- private function addExtensions(SimpleSAML_Configuration $metadata, SAML2_XML_md_RoleDescriptor $e) {
-
- if ($metadata->hasValue('tags')) {
- $a = new SAML2_XML_saml_Attribute();
- $a->Name = 'tags';
- foreach ($metadata->getArray('tags') as $tag) {
- $a->AttributeValue[] = new SAML2_XML_saml_AttributeValue($tag);
- }
- $e->Extensions[] = $a;
- }
-
- if ($metadata->hasValue('hint.cidr')) {
- $a = new SAML2_XML_saml_Attribute();
- $a->Name = 'hint.cidr';
- foreach ($metadata->getArray('hint.cidr') as $hint) {
- $a->AttributeValue[] = new SAML2_XML_saml_AttributeValue($hint);
- }
- $e->Extensions[] = $a;
- }
-
- if ($metadata->hasValue('scope')) {
- foreach ($metadata->getArray('scope') as $scopetext) {
- $s = new SAML2_XML_shibmd_Scope();
- $s->scope = $scopetext;
- // Check whether $ ^ ( ) * | \ are in a scope -> assume regex.
- if (1 === preg_match('/[\$\^\)\(\*\|\\\\]/', $scopetext)) {
- $s->regexp = TRUE;
- } else {
- $s->regexp = FALSE;
- }
- $e->Extensions[] = $s;
- }
- }
-
- if ($metadata->hasValue('EntityAttributes')) {
- $ea = new SAML2_XML_mdattr_EntityAttributes();
- foreach ($metadata->getArray('EntityAttributes') as $attributeName => $attributeValues) {
- $a = new SAML2_XML_saml_Attribute();
- $a->Name = $attributeName;
- $a->NameFormat = 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri';
-
- // Attribute names that is not URI is prefixed as this: '{nameformat}name'
- if (preg_match('/^\{(.*?)\}(.*)$/', $attributeName, $matches)) {
- $a->Name = $matches[2];
- $nameFormat = $matches[1];
- if ($nameFormat !== SAML2_Const::NAMEFORMAT_UNSPECIFIED) {
- $a->NameFormat = $nameFormat;
- }
- }
- foreach ($attributeValues as $attributeValue) {
- $a->AttributeValue[] = new SAML2_XML_saml_AttributeValue($attributeValue);
- }
- $ea->children[] = $a;
- }
- $this->entityDescriptor->Extensions[] = $ea;
- }
-
- if ($metadata->hasValue('RegistrationInfo')) {
- $ri = new SAML2_XML_mdrpi_RegistrationInfo();
- foreach ($metadata->getArray('RegistrationInfo') as $riName => $riValues) {
- switch ($riName) {
- case 'authority':
- $ri->registrationAuthority = $riValues;
- break;
- case 'instant':
- $ri->registrationInstant = SAML2_Utils::xsDateTimeToTimestamp($riValues);
- break;
- case 'policies':
- $ri->RegistrationPolicy = $riValues;
- break;
- }
- }
- $this->entityDescriptor->Extensions[] = $ri;
-
- }
-
- if ($metadata->hasValue('UIInfo')) {
- $ui = new SAML2_XML_mdui_UIInfo();
- foreach ($metadata->getArray('UIInfo') as $uiName => $uiValues) {
- switch ($uiName) {
- case 'DisplayName':
- $ui->DisplayName = $uiValues;
- break;
- case 'Description':
- $ui->Description = $uiValues;
- break;
- case 'InformationURL':
- $ui->InformationURL = $uiValues;
- break;
- case 'PrivacyStatementURL':
- $ui->PrivacyStatementURL = $uiValues;
- break;
- case 'Keywords':
- foreach ($uiValues as $lang => $keywords) {
- $uiItem = new SAML2_XML_mdui_Keywords();
- $uiItem->lang = $lang;
- $uiItem->Keywords = $keywords;
- $ui->Keywords[] = $uiItem;
- }
- break;
- case 'Logo':
- foreach ($uiValues as $logo) {
- $uiItem = new SAML2_XML_mdui_Logo();
- $uiItem->url = $logo['url'];
- $uiItem->width = $logo['width'];
- $uiItem->height = $logo['height'];
- if (isset($logo['lang'])) {
- $uiItem->lang = $logo['lang'];
- }
- $ui->Logo[] = $uiItem;
- }
- break;
- }
- }
- $e->Extensions[] = $ui;
- }
-
- if ($metadata->hasValue('DiscoHints')) {
- $dh = new SAML2_XML_mdui_DiscoHints();
- foreach ($metadata->getArray('DiscoHints') as $dhName => $dhValues) {
- switch ($dhName) {
- case 'IPHint':
- $dh->IPHint = $dhValues;
- break;
- case 'DomainHint':
- $dh->DomainHint = $dhValues;
- break;
- case 'GeolocationHint':
- $dh->GeolocationHint = $dhValues;
- break;
- }
- }
- $e->Extensions[] = $dh;
- }
- }
-
-
- /**
- * Add Organization element.
- *
- * This function adds an organization element to the metadata.
- *
- * @param array $orgName An array with the localized OrganizatioName.
- * @param array $orgDisplayName An array with the localized OrganizatioDisplayName.
- * @param array $orgURL An array with the localized OrganizatioURL.
- */
- public function addOrganization(array $orgName, array $orgDisplayName, array $orgURL) {
-
- $org = new SAML2_XML_md_Organization();
-
- $org->OrganizationName = $orgName;
- $org->OrganizationDisplayName = $orgDisplayName;
- $org->OrganizationURL = $orgURL;
-
- $this->entityDescriptor->Organization = $org;
- }
-
-
- /**
- * Add organization element based on metadata array.
- *
- * @param array $metadata The metadata we should extract the organization information from.
- */
- public function addOrganizationInfo(array $metadata) {
-
- if (
- empty($metadata['OrganizationName']) ||
- empty($metadata['OrganizationDisplayName']) ||
- empty($metadata['OrganizationURL'])
- ) {
- /* Empty or incomplete organization information. */
- return;
- }
-
- $orgName = SimpleSAML\Utils\Arrays::arrayize($metadata['OrganizationName'], 'en');
- $orgDisplayName = SimpleSAML\Utils\Arrays::arrayize($metadata['OrganizationDisplayName'], 'en');
- $orgURL = SimpleSAML\Utils\Arrays::arrayize($metadata['OrganizationURL'], 'en');
-
- $this->addOrganization($orgName, $orgDisplayName, $orgURL);
- }
-
-
- /**
- * Add endpoint list to metadata.
- *
- * @param array $endpoints The endpoints.
- * @param bool $indexed Whether the endpoints should be indexed.
- * @return array Array of endpoint objects.
- */
- private static function createEndpoints(array $endpoints, $indexed) {
- assert('is_bool($indexed)');
-
- $ret = array();
-
- foreach ($endpoints as &$ep) {
- if ($indexed) {
- $t = new SAML2_XML_md_IndexedEndpointType();
- } else {
- $t = new SAML2_XML_md_EndpointType();
- }
-
- $t->Binding = $ep['Binding'];
- $t->Location = $ep['Location'];
- if (isset($ep['ResponseLocation'])) {
- $t->ResponseLocation = $ep['ResponseLocation'];
- }
- if (isset($ep['hoksso:ProtocolBinding'])) {
- $t->setAttributeNS(SAML2_Const::NS_HOK, 'hoksso:ProtocolBinding', SAML2_Const::BINDING_HTTP_REDIRECT);
- }
-
- if ($indexed) {
- if (!isset($ep['index'])) {
- /* Find the maximum index. */
- $maxIndex = -1;
- foreach ($endpoints as $ep) {
- if (!isset($ep['index'])) {
- continue;
- }
-
- if ($ep['index'] > $maxIndex) {
- $maxIndex = $ep['index'];
- }
- }
-
- $ep['index'] = $maxIndex + 1;
- }
-
- $t->index = $ep['index'];
- }
-
- $ret[] = $t;
- }
-
- return $ret;
- }
-
-
- /**
- * Add an AttributeConsumingService element to the metadata.
- *
- * @param DOMElement $spDesc The SPSSODescriptor element.
- * @param SimpleSAML_Configuration $metadata The metadata.
- */
- private function addAttributeConsumingService(SAML2_XML_md_SPSSODescriptor $spDesc, SimpleSAML_Configuration $metadata) {
- $attributes = $metadata->getArray('attributes', array());
- $name = $metadata->getLocalizedString('name', NULL);
-
- if ($name === NULL || count($attributes) == 0) {
- /* We cannot add an AttributeConsumingService without name and attributes. */
- return;
- }
-
- $attributesrequired = $metadata->getArray('attributes.required', array());
-
- /*
- * Add an AttributeConsumingService element with information as name and description and list
- * of requested attributes
- */
- $attributeconsumer = new SAML2_XML_md_AttributeConsumingService();
-
- $attributeconsumer->index = 0;
-
- $attributeconsumer->ServiceName = $name;
- $attributeconsumer->ServiceDescription = $metadata->getLocalizedString('description', array());
-
- $nameFormat = $metadata->getString('attributes.NameFormat', SAML2_Const::NAMEFORMAT_UNSPECIFIED);
- foreach ($attributes as $friendlyName => $attribute) {
- $t = new SAML2_XML_md_RequestedAttribute();
- $t->Name = $attribute;
- if (!is_int($friendlyName)) {
- $t->FriendlyName = $friendlyName;
- }
- if ($nameFormat !== SAML2_Const::NAMEFORMAT_UNSPECIFIED) {
- $t->NameFormat = $nameFormat;
- }
- if (in_array($attribute, $attributesrequired)) {
- $t->isRequired = true;
- }
- $attributeconsumer->RequestedAttribute[] = $t;
- }
-
- $spDesc->AttributeConsumingService[] = $attributeconsumer;
- }
-
-
- /**
- * Add metadata set for entity.
- *
- * This function is used to add a metadata array to the entity.
- *
- * @param string $set The metadata set this metadata comes from.
- * @param array $metadata The metadata.
- */
- public function addMetadata($set, $metadata) {
- assert('is_string($set)');
- assert('is_array($metadata)');
-
- $this->setExpiration($metadata);
-
- switch ($set) {
- case 'saml20-sp-remote':
- $this->addMetadataSP20($metadata);
- break;
- case 'saml20-idp-remote':
- $this->addMetadataIdP20($metadata);
- break;
- case 'shib13-sp-remote':
- $this->addMetadataSP11($metadata);
- break;
- case 'shib13-idp-remote':
- $this->addMetadataIdP11($metadata);
- break;
- case 'attributeauthority-remote':
- $this->addAttributeAuthority($metadata);
- break;
- default:
- SimpleSAML_Logger::warning('Unable to generate metadata for unknown type \'' . $set . '\'.');
- }
-
- }
-
- /**
- * Add SAML 2.0 SP metadata.
- *
- * @param array $metadata The metadata.
- * @param array $protocols The protocols supported.
- */
- public function addMetadataSP20($metadata, $protocols = array(SAML2_Const::NS_SAMLP)) {
- assert('is_array($metadata)');
- assert('is_array($protocols)');
- assert('isset($metadata["entityid"])');
- assert('isset($metadata["metadata-set"])');
-
- $metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
-
- $e = new SAML2_XML_md_SPSSODescriptor();
- $e->protocolSupportEnumeration = $protocols;
-
- if ($metadata->hasValue('saml20.sign.assertion')) {
- $e->WantAssertionsSigned = $metadata->getBoolean('saml20.sign.assertion');
- }
-
- if ($metadata->hasValue('redirect.validate')) {
- $e->AuthnRequestsSigned = $metadata->getBoolean('redirect.validate');
- } elseif ($metadata->hasValue('validate.authnrequest')) {
- $e->AuthnRequestsSigned = $metadata->getBoolean('validate.authnrequest');
- }
-
- $this->addExtensions($metadata, $e);
-
- $this->addCertificate($e, $metadata);
-
- $e->SingleLogoutService = self::createEndpoints($metadata->getEndpoints('SingleLogoutService'), FALSE);
-
- $e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
-
- $endpoints = $metadata->getEndpoints('AssertionConsumerService');
- foreach ($metadata->getArrayizeString('AssertionConsumerService.artifact', array()) as $acs) {
- $endpoints[] = array(
- 'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact',
- 'Location' => $acs,
- );
- }
- $e->AssertionConsumerService = self::createEndpoints($endpoints, TRUE);
-
- $this->addAttributeConsumingService($e, $metadata);
-
- $this->entityDescriptor->RoleDescriptor[] = $e;
-
- foreach ($metadata->getArray('contacts', array()) as $contact) {
- if (array_key_exists('contactType', $contact) && array_key_exists('emailAddress', $contact)) {
- $this->addContact($contact['contactType'], \SimpleSAML\Utils\Config\Metadata::getContact($contact));
- }
- }
-
- }
-
-
- /**
- * Add SAML 2.0 IdP metadata.
- *
- * @param array $metadata The metadata.
- */
- public function addMetadataIdP20($metadata) {
- assert('is_array($metadata)');
- assert('isset($metadata["entityid"])');
- assert('isset($metadata["metadata-set"])');
-
- $metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
-
- $e = new SAML2_XML_md_IDPSSODescriptor();
- $e->protocolSupportEnumeration[] = 'urn:oasis:names:tc:SAML:2.0:protocol';
-
- if ($metadata->hasValue('sign.authnrequest')) {
- $e->WantAuthnRequestsSigned = $metadata->getBoolean('sign.authnrequest');
- } elseif ($metadata->hasValue('redirect.sign')) {
- $e->WantAuthnRequestsSigned = $metadata->getBoolean('redirect.sign');
- }
-
- $this->addExtensions($metadata, $e);
- $this->addCertificate($e, $metadata);
+ /**
+ * The maximum time in seconds the metadata should be cached.
+ *
+ * @var int|null
+ */
+ private $maxCache = null;
- if ($metadata->hasValue('ArtifactResolutionService')){
- $e->ArtifactResolutionService = self::createEndpoints($metadata->getEndpoints('ArtifactResolutionService'), TRUE);
- }
- $e->SingleLogoutService = self::createEndpoints($metadata->getEndpoints('SingleLogoutService'), FALSE);
+ /**
+ * The maximum time in seconds since the current time that this metadata should be considered valid.
+ *
+ * @var int|null
+ */
+ private $maxDuration = null;
- $e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
- $e->SingleSignOnService = self::createEndpoints($metadata->getEndpoints('SingleSignOnService'), FALSE);
+ /**
+ * Initialize the SAML builder.
+ *
+ * @param string $entityId The entity id of the entity.
+ * @param double|null $maxCache The maximum time in seconds the metadata should be cached. Defaults to null
+ * @param double|null $maxDuration The maximum time in seconds this metadata should be considered valid. Defaults
+ * to null.
+ */
+ public function __construct($entityId, $maxCache = null, $maxDuration = null)
+ {
+ assert('is_string($entityId)');
+
+ $this->maxCache = $maxCache;
+ $this->maxDuration = $maxDuration;
+
+ $this->entityDescriptor = new SAML2_XML_md_EntityDescriptor();
+ $this->entityDescriptor->entityID = $entityId;
+ }
+
+
+ private function setExpiration($metadata)
+ {
+ if (array_key_exists('expire', $metadata)) {
+ if ($metadata['expire'] - time() < $this->maxDuration) {
+ $this->maxDuration = $metadata['expire'] - time();
+ }
+ }
+
+ if ($this->maxCache !== null) {
+ $this->entityDescriptor->cacheDuration = 'PT'.$this->maxCache.'S';
+ }
+ if ($this->maxDuration !== null) {
+ $this->entityDescriptor->validUntil = time() + $this->maxDuration;
+ }
+ }
+
+
+ /**
+ * Retrieve the EntityDescriptor element which is generated for this entity.
+ *
+ * @return DOMElement The EntityDescriptor element of this entity.
+ */
+ public function getEntityDescriptor()
+ {
+ $xml = $this->entityDescriptor->toXML();
+ $xml->ownerDocument->appendChild($xml);
- $this->entityDescriptor->RoleDescriptor[] = $e;
+ return $xml;
+ }
- foreach ($metadata->getArray('contacts', array()) as $contact) {
- if (array_key_exists('contactType', $contact) && array_key_exists('emailAddress', $contact)) {
- $this->addContact($contact['contactType'], \SimpleSAML\Utils\Config\Metadata::getContact($contact));
- }
- }
- }
+ /**
+ * Retrieve the EntityDescriptor as text.
+ *
+ * This function serializes this EntityDescriptor, and returns it as text.
+ *
+ * @param bool $formatted Whether the returned EntityDescriptor should be formatted first.
+ *
+ * @return string The serialized EntityDescriptor.
+ */
+ public function getEntityDescriptorText($formatted = true)
+ {
+ assert('is_bool($formatted)');
+ $xml = $this->getEntityDescriptor();
+ if ($formatted) {
+ SimpleSAML\Utils\XML::formatDOMElement($xml);
+ }
- /**
- * Add SAML 1.1 SP metadata.
- *
- * @param array $metadata The metadata.
- */
- public function addMetadataSP11($metadata) {
- assert('is_array($metadata)');
- assert('isset($metadata["entityid"])');
- assert('isset($metadata["metadata-set"])');
+ return $xml->ownerDocument->saveXML();
+ }
- $metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
- $e = new SAML2_XML_md_SPSSODescriptor();
- $e->protocolSupportEnumeration[] = 'urn:oasis:names:tc:SAML:1.1:protocol';
+ /**
+ * Add a SecurityTokenServiceType for ADFS metadata.
+ *
+ * @param array $metadata The metadata with the information about the SecurityTokenServiceType.
+ */
+ public function addSecurityTokenServiceType($metadata)
+ {
+ assert('is_array($metadata)');
+ assert('isset($metadata["entityid"])');
+ assert('isset($metadata["metadata-set"])');
- $this->addCertificate($e, $metadata);
+ $metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
+ $defaultEndpoint = $metadata->getDefaultEndpoint('SingleSignOnService');
+ $e = new sspmod_adfs_SAML2_XML_fed_SecurityTokenServiceType();
+ $e->Location = $defaultEndpoint['Location'];
- $e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
+ $this->addCertificate($e, $metadata);
- $endpoints = $metadata->getEndpoints('AssertionConsumerService');
- foreach ($metadata->getArrayizeString('AssertionConsumerService.artifact', array()) as $acs) {
- $endpoints[] = array(
- 'Binding' => 'urn:oasis:names:tc:SAML:1.0:profiles:artifact-01',
- 'Location' => $acs,
- );
- }
- $e->AssertionConsumerService = self::createEndpoints($endpoints, TRUE);
+ $this->entityDescriptor->RoleDescriptor[] = $e;
+ }
- $this->addAttributeConsumingService($e, $metadata);
- $this->entityDescriptor->RoleDescriptor[] = $e;
- }
+ /**
+ * Add extensions to the metadata.
+ *
+ * @param SimpleSAML_Configuration $metadata The metadata to get extensions from.
+ * @param SAML2_XML_md_RoleDescriptor $e Reference to the element where the Extensions element should be included.
+ */
+ private function addExtensions(SimpleSAML_Configuration $metadata, SAML2_XML_md_RoleDescriptor $e)
+ {
+ if ($metadata->hasValue('tags')) {
+ $a = new SAML2_XML_saml_Attribute();
+ $a->Name = 'tags';
+ foreach ($metadata->getArray('tags') as $tag) {
+ $a->AttributeValue[] = new SAML2_XML_saml_AttributeValue($tag);
+ }
+ $e->Extensions[] = $a;
+ }
+
+ if ($metadata->hasValue('hint.cidr')) {
+ $a = new SAML2_XML_saml_Attribute();
+ $a->Name = 'hint.cidr';
+ foreach ($metadata->getArray('hint.cidr') as $hint) {
+ $a->AttributeValue[] = new SAML2_XML_saml_AttributeValue($hint);
+ }
+ $e->Extensions[] = $a;
+ }
+
+ if ($metadata->hasValue('scope')) {
+ foreach ($metadata->getArray('scope') as $scopetext) {
+ $s = new SAML2_XML_shibmd_Scope();
+ $s->scope = $scopetext;
+ // Check whether $ ^ ( ) * | \ are in a scope -> assume regex.
+ if (1 === preg_match('/[\$\^\)\(\*\|\\\\]/', $scopetext)) {
+ $s->regexp = true;
+ } else {
+ $s->regexp = false;
+ }
+ $e->Extensions[] = $s;
+ }
+ }
+
+ if ($metadata->hasValue('EntityAttributes')) {
+ $ea = new SAML2_XML_mdattr_EntityAttributes();
+ foreach ($metadata->getArray('EntityAttributes') as $attributeName => $attributeValues) {
+ $a = new SAML2_XML_saml_Attribute();
+ $a->Name = $attributeName;
+ $a->NameFormat = 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri';
+
+ // Attribute names that is not URI is prefixed as this: '{nameformat}name'
+ if (preg_match('/^\{(.*?)\}(.*)$/', $attributeName, $matches)) {
+ $a->Name = $matches[2];
+ $nameFormat = $matches[1];
+ if ($nameFormat !== SAML2_Const::NAMEFORMAT_UNSPECIFIED) {
+ $a->NameFormat = $nameFormat;
+ }
+ }
+ foreach ($attributeValues as $attributeValue) {
+ $a->AttributeValue[] = new SAML2_XML_saml_AttributeValue($attributeValue);
+ }
+ $ea->children[] = $a;
+ }
+ $this->entityDescriptor->Extensions[] = $ea;
+ }
+
+ if ($metadata->hasValue('RegistrationInfo')) {
+ $ri = new SAML2_XML_mdrpi_RegistrationInfo();
+ foreach ($metadata->getArray('RegistrationInfo') as $riName => $riValues) {
+ switch ($riName) {
+ case 'authority':
+ $ri->registrationAuthority = $riValues;
+ break;
+ case 'instant':
+ $ri->registrationInstant = SAML2_Utils::xsDateTimeToTimestamp($riValues);
+ break;
+ case 'policies':
+ $ri->RegistrationPolicy = $riValues;
+ break;
+ }
+ }
+ $this->entityDescriptor->Extensions[] = $ri;
+ }
+
+ if ($metadata->hasValue('UIInfo')) {
+ $ui = new SAML2_XML_mdui_UIInfo();
+ foreach ($metadata->getArray('UIInfo') as $uiName => $uiValues) {
+ switch ($uiName) {
+ case 'DisplayName':
+ $ui->DisplayName = $uiValues;
+ break;
+ case 'Description':
+ $ui->Description = $uiValues;
+ break;
+ case 'InformationURL':
+ $ui->InformationURL = $uiValues;
+ break;
+ case 'PrivacyStatementURL':
+ $ui->PrivacyStatementURL = $uiValues;
+ break;
+ case 'Keywords':
+ foreach ($uiValues as $lang => $keywords) {
+ $uiItem = new SAML2_XML_mdui_Keywords();
+ $uiItem->lang = $lang;
+ $uiItem->Keywords = $keywords;
+ $ui->Keywords[] = $uiItem;
+ }
+ break;
+ case 'Logo':
+ foreach ($uiValues as $logo) {
+ $uiItem = new SAML2_XML_mdui_Logo();
+ $uiItem->url = $logo['url'];
+ $uiItem->width = $logo['width'];
+ $uiItem->height = $logo['height'];
+ if (isset($logo['lang'])) {
+ $uiItem->lang = $logo['lang'];
+ }
+ $ui->Logo[] = $uiItem;
+ }
+ break;
+ }
+ }
+ $e->Extensions[] = $ui;
+ }
+
+ if ($metadata->hasValue('DiscoHints')) {
+ $dh = new SAML2_XML_mdui_DiscoHints();
+ foreach ($metadata->getArray('DiscoHints') as $dhName => $dhValues) {
+ switch ($dhName) {
+ case 'IPHint':
+ $dh->IPHint = $dhValues;
+ break;
+ case 'DomainHint':
+ $dh->DomainHint = $dhValues;
+ break;
+ case 'GeolocationHint':
+ $dh->GeolocationHint = $dhValues;
+ break;
+ }
+ }
+ $e->Extensions[] = $dh;
+ }
+ }
+
+
+ /**
+ * Add an Organization element based on data passed as parameters
+ *
+ * @param array $orgName An array with the localized OrganizationName.
+ * @param array $orgDisplayName An array with the localized OrganizationDisplayName.
+ * @param array $orgURL An array with the localized OrganizationURL.
+ */
+ public function addOrganization(array $orgName, array $orgDisplayName, array $orgURL)
+ {
+ $org = new SAML2_XML_md_Organization();
+ $org->OrganizationName = $orgName;
+ $org->OrganizationDisplayName = $orgDisplayName;
+ $org->OrganizationURL = $orgURL;
- /**
- * Add SAML 1.1 IdP metadata.
- *
- * @param array $metadata The metadata.
- */
- public function addMetadataIdP11($metadata) {
- assert('is_array($metadata)');
- assert('isset($metadata["entityid"])');
- assert('isset($metadata["metadata-set"])');
+ $this->entityDescriptor->Organization = $org;
+ }
- $metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
- $e = new SAML2_XML_md_IDPSSODescriptor();
- $e->protocolSupportEnumeration[] = 'urn:oasis:names:tc:SAML:1.1:protocol';
- $e->protocolSupportEnumeration[] = 'urn:mace:shibboleth:1.0';
+ /**
+ * Add an Organization element based on metadata array.
+ *
+ * @param array $metadata The metadata we should extract the organization information from.
+ */
+ public function addOrganizationInfo(array $metadata)
+ {
+ if (
+ empty($metadata['OrganizationName']) ||
+ empty($metadata['OrganizationDisplayName']) ||
+ empty($metadata['OrganizationURL'])
+ ) {
+ // empty or incomplete organization information
+ return;
+ }
+
+ $orgName = SimpleSAML\Utils\Arrays::arrayize($metadata['OrganizationName'], 'en');
+ $orgDisplayName = SimpleSAML\Utils\Arrays::arrayize($metadata['OrganizationDisplayName'], 'en');
+ $orgURL = SimpleSAML\Utils\Arrays::arrayize($metadata['OrganizationURL'], 'en');
+
+ $this->addOrganization($orgName, $orgDisplayName, $orgURL);
+ }
+
+
+ /**
+ * Add a list of endpoints to metadata.
+ *
+ * @param array $endpoints The endpoints.
+ * @param bool $indexed Whether the endpoints should be indexed.
+ *
+ * @return array An array of endpoint objects, either SAML2_XML_md_EndpointType or SAML2_XML_md_IndexedEndpointType.
+ */
+ private static function createEndpoints(array $endpoints, $indexed)
+ {
+ assert('is_bool($indexed)');
+
+ $ret = array();
+
+ foreach ($endpoints as &$ep) {
+ if ($indexed) {
+ $t = new SAML2_XML_md_IndexedEndpointType();
+ } else {
+ $t = new SAML2_XML_md_EndpointType();
+ }
+
+ $t->Binding = $ep['Binding'];
+ $t->Location = $ep['Location'];
+ if (isset($ep['ResponseLocation'])) {
+ $t->ResponseLocation = $ep['ResponseLocation'];
+ }
+ if (isset($ep['hoksso:ProtocolBinding'])) {
+ $t->setAttributeNS(SAML2_Const::NS_HOK, 'hoksso:ProtocolBinding', SAML2_Const::BINDING_HTTP_REDIRECT);
+ }
+
+ if ($indexed) {
+ if (!isset($ep['index'])) {
+ /* Find the maximum index. */
+ $maxIndex = -1;
+ foreach ($endpoints as $ep) {
+ if (!isset($ep['index'])) {
+ continue;
+ }
+
+ if ($ep['index'] > $maxIndex) {
+ $maxIndex = $ep['index'];
+ }
+ }
+
+ $ep['index'] = $maxIndex + 1;
+ }
+
+ $t->index = $ep['index'];
+ }
+
+ $ret[] = $t;
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * Add an AttributeConsumingService element to the metadata.
+ *
+ * @param SAML2_XML_md_SPSSODescriptor $spDesc The SPSSODescriptor element.
+ * @param SimpleSAML_Configuration $metadata The metadata.
+ */
+ private function addAttributeConsumingService(
+ SAML2_XML_md_SPSSODescriptor $spDesc,
+ SimpleSAML_Configuration $metadata
+ ) {
+ $attributes = $metadata->getArray('attributes', array());
+ $name = $metadata->getLocalizedString('name', null);
+
+ if ($name === null || count($attributes) == 0) {
+ // we cannot add an AttributeConsumingService without name and attributes
+ return;
+ }
+
+ $attributesrequired = $metadata->getArray('attributes.required', array());
+
+ /*
+ * Add an AttributeConsumingService element with information as name and description and list
+ * of requested attributes
+ */
+ $attributeconsumer = new SAML2_XML_md_AttributeConsumingService();
+
+ $attributeconsumer->index = 0;
+
+ $attributeconsumer->ServiceName = $name;
+ $attributeconsumer->ServiceDescription = $metadata->getLocalizedString('description', array());
+
+ $nameFormat = $metadata->getString('attributes.NameFormat', SAML2_Const::NAMEFORMAT_UNSPECIFIED);
+ foreach ($attributes as $friendlyName => $attribute) {
+ $t = new SAML2_XML_md_RequestedAttribute();
+ $t->Name = $attribute;
+ if (!is_int($friendlyName)) {
+ $t->FriendlyName = $friendlyName;
+ }
+ if ($nameFormat !== SAML2_Const::NAMEFORMAT_UNSPECIFIED) {
+ $t->NameFormat = $nameFormat;
+ }
+ if (in_array($attribute, $attributesrequired)) {
+ $t->isRequired = true;
+ }
+ $attributeconsumer->RequestedAttribute[] = $t;
+ }
+
+ $spDesc->AttributeConsumingService[] = $attributeconsumer;
+ }
+
+
+ /**
+ * Add a specific type of metadata to an entity.
+ *
+ * @param string $set The metadata set this metadata comes from.
+ * @param array $metadata The metadata.
+ */
+ public function addMetadata($set, $metadata)
+ {
+ assert('is_string($set)');
+ assert('is_array($metadata)');
+
+ $this->setExpiration($metadata);
+
+ switch ($set) {
+ case 'saml20-sp-remote':
+ $this->addMetadataSP20($metadata);
+ break;
+ case 'saml20-idp-remote':
+ $this->addMetadataIdP20($metadata);
+ break;
+ case 'shib13-sp-remote':
+ $this->addMetadataSP11($metadata);
+ break;
+ case 'shib13-idp-remote':
+ $this->addMetadataIdP11($metadata);
+ break;
+ case 'attributeauthority-remote':
+ $this->addAttributeAuthority($metadata);
+ break;
+ default:
+ SimpleSAML_Logger::warning('Unable to generate metadata for unknown type \''.$set.'\'.');
+ }
+ }
+
+
+ /**
+ * Add SAML 2.0 SP metadata.
+ *
+ * @param array $metadata The metadata.
+ * @param array $protocols The protocols supported. Defaults to SAML2_Const::NS_SAMLP.
+ */
+ public function addMetadataSP20($metadata, $protocols = array(SAML2_Const::NS_SAMLP))
+ {
+ assert('is_array($metadata)');
+ assert('is_array($protocols)');
+ assert('isset($metadata["entityid"])');
+ assert('isset($metadata["metadata-set"])');
+
+ $metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
+
+ $e = new SAML2_XML_md_SPSSODescriptor();
+ $e->protocolSupportEnumeration = $protocols;
+
+ if ($metadata->hasValue('saml20.sign.assertion')) {
+ $e->WantAssertionsSigned = $metadata->getBoolean('saml20.sign.assertion');
+ }
+
+ if ($metadata->hasValue('redirect.validate')) {
+ $e->AuthnRequestsSigned = $metadata->getBoolean('redirect.validate');
+ } elseif ($metadata->hasValue('validate.authnrequest')) {
+ $e->AuthnRequestsSigned = $metadata->getBoolean('validate.authnrequest');
+ }
+
+ $this->addExtensions($metadata, $e);
+
+ $this->addCertificate($e, $metadata);
+
+ $e->SingleLogoutService = self::createEndpoints($metadata->getEndpoints('SingleLogoutService'), false);
- $this->addCertificate($e, $metadata);
+ $e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
- $e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
+ $endpoints = $metadata->getEndpoints('AssertionConsumerService');
+ foreach ($metadata->getArrayizeString('AssertionConsumerService.artifact', array()) as $acs) {
+ $endpoints[] = array(
+ 'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact',
+ 'Location' => $acs,
+ );
+ }
+ $e->AssertionConsumerService = self::createEndpoints($endpoints, true);
- $e->SingleSignOnService = self::createEndpoints($metadata->getEndpoints('SingleSignOnService'), FALSE);
+ $this->addAttributeConsumingService($e, $metadata);
- $this->entityDescriptor->RoleDescriptor[] = $e;
- }
+ $this->entityDescriptor->RoleDescriptor[] = $e;
+ foreach ($metadata->getArray('contacts', array()) as $contact) {
+ if (array_key_exists('contactType', $contact) && array_key_exists('emailAddress', $contact)) {
+ $this->addContact($contact['contactType'], \SimpleSAML\Utils\Config\Metadata::getContact($contact));
+ }
+ }
+ }
- /**
- * Add a AttributeAuthorityDescriptor.
- *
- * @param array $metadata The AttributeAuthorityDescriptor, in the format returned by SAMLParser.
- */
- public function addAttributeAuthority(array $metadata) {
- assert('is_array($metadata)');
- assert('isset($metadata["entityid"])');
- assert('isset($metadata["metadata-set"])');
- $metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
+ /**
+ * Add metadata of a SAML 2.0 identity provider.
+ *
+ * @param array $metadata The metadata.
+ */
+ public function addMetadataIdP20($metadata)
+ {
+ assert('is_array($metadata)');
+ assert('isset($metadata["entityid"])');
+ assert('isset($metadata["metadata-set"])');
+
+ $metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
+
+ $e = new SAML2_XML_md_IDPSSODescriptor();
+ $e->protocolSupportEnumeration[] = 'urn:oasis:names:tc:SAML:2.0:protocol';
+
+ if ($metadata->hasValue('sign.authnrequest')) {
+ $e->WantAuthnRequestsSigned = $metadata->getBoolean('sign.authnrequest');
+ } elseif ($metadata->hasValue('redirect.sign')) {
+ $e->WantAuthnRequestsSigned = $metadata->getBoolean('redirect.sign');
+ }
- $e = new SAML2_XML_md_AttributeAuthorityDescriptor();
- $e->protocolSupportEnumeration = $metadata->getArray('protocols', array());
+ $this->addExtensions($metadata, $e);
- $this->addExtensions($metadata, $e);
- $this->addCertificate($e, $metadata);
+ $this->addCertificate($e, $metadata);
- $e->AttributeService = self::createEndpoints($metadata->getEndpoints('AttributeService'), FALSE);
- $e->AssertionIDRequestService = self::createEndpoints($metadata->getEndpoints('AssertionIDRequestService'), FALSE);
+ if ($metadata->hasValue('ArtifactResolutionService')) {
+ $e->ArtifactResolutionService = self::createEndpoints(
+ $metadata->getEndpoints('ArtifactResolutionService'),
+ true
+ );
+ }
- $e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
+ $e->SingleLogoutService = self::createEndpoints($metadata->getEndpoints('SingleLogoutService'), false);
- $this->entityDescriptor->RoleDescriptor[] = $e;
- }
+ $e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
+ $e->SingleSignOnService = self::createEndpoints($metadata->getEndpoints('SingleSignOnService'), false);
- /**
- * Add contact information.
- *
- * Accepts a contact type, and a contact array that must be previously sanitized.
- *
- * @param string $type The type of contact. Deprecated.
- * @param array $details The details about the contact.
- *
- * @todo Change the signature to remove $type.
- * @todo Remove the capability to pass a name and parse it inside the method.
+ $this->entityDescriptor->RoleDescriptor[] = $e;
+
+ foreach ($metadata->getArray('contacts', array()) as $contact) {
+ if (array_key_exists('contactType', $contact) && array_key_exists('emailAddress', $contact)) {
+ $this->addContact($contact['contactType'], \SimpleSAML\Utils\Config\Metadata::getContact($contact));
+ }
+ }
+ }
+
+
+ /**
+ * Add metadata of a SAML 1.1 service provider.
*
- * @deprecated This function will change its signature and no longer parse a 'name' element.
- */
- public function addContact($type, $details) {
- assert('is_string($type)');
- assert('is_array($details)');
- assert('in_array($type, array("technical", "support", "administrative", "billing", "other"), TRUE)');
-
- // TODO: remove this check as soon as getContact() is called always before calling this function.
- $details = \SimpleSAML\Utils\Config\Metadata::getContact($details);
-
- $e = new SAML2_XML_md_ContactPerson();
- $e->contactType = $type;
-
- if (isset($details['company'])) {
- $e->Company = $details['company'];
- }
- if (isset($details['givenName'])) {
- $e->GivenName = $details['givenName'];
- }
- if (isset($details['surName'])) {
- $e->SurName = $details['surName'];
- }
-
- if (isset($details['emailAddress'])) {
- $eas = $details['emailAddress'];
- if (!is_array($eas)) {
- $eas = array($eas);
- }
- foreach ($eas as $ea) {
- $e->EmailAddress[] = $ea;
- }
- }
-
- if (isset($details['telephoneNumber'])) {
- $tlfNrs = $details['telephoneNumber'];
- if (!is_array($tlfNrs)) {
- $tlfNrs = array($tlfNrs);
- }
- foreach ($tlfNrs as $tlfNr) {
- $e->TelephoneNumber[] = $tlfNr;
- }
- }
-
- $this->entityDescriptor->ContactPerson[] = $e;
- }
-
-
- /**
- * Add a KeyDescriptor with an X509 certificate.
- *
- * @param SAML2_XML_md_RoleDescriptor $rd The RoleDescriptor the certificate should be added to.
- * @param string $use The value of the use-attribute.
- * @param string $x509data The certificate data.
- */
- private function addX509KeyDescriptor(SAML2_XML_md_RoleDescriptor $rd, $use, $x509data) {
- assert('in_array($use, array("encryption", "signing"), TRUE)');
- assert('is_string($x509data)');
-
- $keyDescriptor = SAML2_Utils::createKeyDescriptor($x509data);
- $keyDescriptor->use = $use;
- $rd->KeyDescriptor[] = $keyDescriptor;
- }
-
-
- /**
- * Add certificate.
- *
- * Helper function for adding a certificate to the metadata.
- *
- * @param SAML2_XML_md_RoleDescriptor $rd The RoleDescriptor the certificate should be added to.
- * @param SimpleSAML_Configuration $metadata The metadata for the entity.
- */
- private function addCertificate(SAML2_XML_md_RoleDescriptor $rd, SimpleSAML_Configuration $metadata) {
-
- $keys = $metadata->getPublicKeys();
- if ($keys !== NULL) {
- foreach ($keys as $key) {
- if ($key['type'] !== 'X509Certificate') {
- continue;
- }
- if (!isset($key['signing']) || $key['signing'] === TRUE) {
- $this->addX509KeyDescriptor($rd, 'signing', $key['X509Certificate']);
- }
- if (!isset($key['encryption']) || $key['encryption'] === TRUE) {
- $this->addX509KeyDescriptor($rd, 'encryption', $key['X509Certificate']);
- }
- }
- }
-
- if ($metadata->hasValue('https.certData')) {
- $this->addX509KeyDescriptor($rd, 'signing', $metadata->getString('https.certData'));
- }
- }
+ * @param array $metadata The metadata.
+ */
+ public function addMetadataSP11($metadata)
+ {
+ assert('is_array($metadata)');
+ assert('isset($metadata["entityid"])');
+ assert('isset($metadata["metadata-set"])');
+
+ $metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
+ $e = new SAML2_XML_md_SPSSODescriptor();
+ $e->protocolSupportEnumeration[] = 'urn:oasis:names:tc:SAML:1.1:protocol';
+
+ $this->addCertificate($e, $metadata);
+
+ $e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
+
+ $endpoints = $metadata->getEndpoints('AssertionConsumerService');
+ foreach ($metadata->getArrayizeString('AssertionConsumerService.artifact', array()) as $acs) {
+ $endpoints[] = array(
+ 'Binding' => 'urn:oasis:names:tc:SAML:1.0:profiles:artifact-01',
+ 'Location' => $acs,
+ );
+ }
+ $e->AssertionConsumerService = self::createEndpoints($endpoints, true);
+
+ $this->addAttributeConsumingService($e, $metadata);
+
+ $this->entityDescriptor->RoleDescriptor[] = $e;
+ }
+
+
+ /**
+ * Add metadata of a SAML 1.1 identity provider.
+ *
+ * @param array $metadata The metadata.
+ */
+ public function addMetadataIdP11($metadata)
+ {
+ assert('is_array($metadata)');
+ assert('isset($metadata["entityid"])');
+ assert('isset($metadata["metadata-set"])');
+
+ $metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
+
+ $e = new SAML2_XML_md_IDPSSODescriptor();
+ $e->protocolSupportEnumeration[] = 'urn:oasis:names:tc:SAML:1.1:protocol';
+ $e->protocolSupportEnumeration[] = 'urn:mace:shibboleth:1.0';
+
+ $this->addCertificate($e, $metadata);
+
+ $e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
+
+ $e->SingleSignOnService = self::createEndpoints($metadata->getEndpoints('SingleSignOnService'), false);
+
+ $this->entityDescriptor->RoleDescriptor[] = $e;
+ }
+
+
+ /**
+ * Add metadata of a SAML attribute authority.
+ *
+ * @param array $metadata The AttributeAuthorityDescriptor, in the format returned by
+ * SimpleSAML_Metadata_SAMLParser.
+ */
+ public function addAttributeAuthority(array $metadata)
+ {
+ assert('is_array($metadata)');
+ assert('isset($metadata["entityid"])');
+ assert('isset($metadata["metadata-set"])');
+
+ $metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
+
+ $e = new SAML2_XML_md_AttributeAuthorityDescriptor();
+ $e->protocolSupportEnumeration = $metadata->getArray('protocols', array());
+
+ $this->addExtensions($metadata, $e);
+ $this->addCertificate($e, $metadata);
+
+ $e->AttributeService = self::createEndpoints($metadata->getEndpoints('AttributeService'), false);
+ $e->AssertionIDRequestService = self::createEndpoints(
+ $metadata->getEndpoints('AssertionIDRequestService'),
+ false
+ );
+
+ $e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
+
+ $this->entityDescriptor->RoleDescriptor[] = $e;
+ }
+
+
+ /**
+ * Add contact information.
+ *
+ * Accepts a contact type, and a contact array that must be previously sanitized.
+ *
+ * WARNING: This function will change its signature and no longer parse a 'name' element.
+ *
+ * @param string $type The type of contact. Deprecated.
+ * @param array $details The details about the contact.
+ *
+ * @todo Change the signature to remove $type.
+ * @todo Remove the capability to pass a name and parse it inside the method.
+ */
+ public function addContact($type, $details)
+ {
+ assert('is_string($type)');
+ assert('is_array($details)');
+ assert('in_array($type, array("technical", "support", "administrative", "billing", "other"), TRUE)');
+
+ // TODO: remove this check as soon as getContact() is called always before calling this function.
+ $details = \SimpleSAML\Utils\Config\Metadata::getContact($details);
+
+ $e = new SAML2_XML_md_ContactPerson();
+ $e->contactType = $type;
+
+ if (isset($details['company'])) {
+ $e->Company = $details['company'];
+ }
+ if (isset($details['givenName'])) {
+ $e->GivenName = $details['givenName'];
+ }
+ if (isset($details['surName'])) {
+ $e->SurName = $details['surName'];
+ }
+
+ if (isset($details['emailAddress'])) {
+ $eas = $details['emailAddress'];
+ if (!is_array($eas)) {
+ $eas = array($eas);
+ }
+ foreach ($eas as $ea) {
+ $e->EmailAddress[] = $ea;
+ }
+ }
+
+ if (isset($details['telephoneNumber'])) {
+ $tlfNrs = $details['telephoneNumber'];
+ if (!is_array($tlfNrs)) {
+ $tlfNrs = array($tlfNrs);
+ }
+ foreach ($tlfNrs as $tlfNr) {
+ $e->TelephoneNumber[] = $tlfNr;
+ }
+ }
+
+ $this->entityDescriptor->ContactPerson[] = $e;
+ }
+
+
+ /**
+ * Add a KeyDescriptor with an X509 certificate.
+ *
+ * @param SAML2_XML_md_RoleDescriptor $rd The RoleDescriptor the certificate should be added to.
+ * @param string $use The value of the 'use' attribute.
+ * @param string $x509data The certificate data.
+ */
+ private function addX509KeyDescriptor(SAML2_XML_md_RoleDescriptor $rd, $use, $x509data)
+ {
+ assert('in_array($use, array("encryption", "signing"), TRUE)');
+ assert('is_string($x509data)');
+
+ $keyDescriptor = SAML2_Utils::createKeyDescriptor($x509data);
+ $keyDescriptor->use = $use;
+ $rd->KeyDescriptor[] = $keyDescriptor;
+ }
+
+
+ /**
+ * Add a certificate.
+ *
+ * Helper function for adding a certificate to the metadata.
+ *
+ * @param SAML2_XML_md_RoleDescriptor $rd The RoleDescriptor the certificate should be added to.
+ * @param SimpleSAML_Configuration $metadata The metadata of the entity.
+ */
+ private function addCertificate(SAML2_XML_md_RoleDescriptor $rd, SimpleSAML_Configuration $metadata)
+ {
+ $keys = $metadata->getPublicKeys();
+ if ($keys !== null) {
+ foreach ($keys as $key) {
+ if ($key['type'] !== 'X509Certificate') {
+ continue;
+ }
+ if (!isset($key['signing']) || $key['signing'] === true) {
+ $this->addX509KeyDescriptor($rd, 'signing', $key['X509Certificate']);
+ }
+ if (!isset($key['encryption']) || $key['encryption'] === true) {
+ $this->addX509KeyDescriptor($rd, 'encryption', $key['X509Certificate']);
+ }
+ }
+ }
+
+ if ($metadata->hasValue('https.certData')) {
+ $this->addX509KeyDescriptor($rd, 'signing', $metadata->getString('https.certData'));
+ }
+ }
}
diff --git a/lib/SimpleSAML/Metadata/SAMLParser.php b/lib/SimpleSAML/Metadata/SAMLParser.php
index 424e243..2e52614 100644
--- a/lib/SimpleSAML/Metadata/SAMLParser.php
+++ b/lib/SimpleSAML/Metadata/SAMLParser.php
@@ -1,5 +1,6 @@
<?php
+
/**
* This is class for parsing of SAML 1.x and SAML 2.0 metadata.
*
@@ -11,1364 +12,1451 @@
* parseDescriptorsFile, parseDescriptorsString or parseDescriptorsElement methods. These functions will return
* an array of SAMLParser elements where each element represents an EntityDescriptor-element.
*/
-class SimpleSAML_Metadata_SAMLParser {
-
- /**
- * This is the list of SAML 1.x protocols.
- */
- private static $SAML1xProtocols = array(
- 'urn:oasis:names:tc:SAML:1.0:protocol',
- 'urn:oasis:names:tc:SAML:1.1:protocol',
- );
-
-
- /**
- * This is the list with the SAML 2.0 protocol.
- */
- private static $SAML20Protocols = array(
- 'urn:oasis:names:tc:SAML:2.0:protocol',
- );
-
-
- /**
- * This is the entity id we find in the metadata.
- */
- private $entityId;
-
-
- /**
- * This is an array with the processed SPSSODescriptor elements we have found in this
- * metadata file.
- * Each element in the array is an associative array with the elements from parseSSODescriptor and:
- * - 'AssertionConsumerService': Array with the SP's assertion consumer services.
- * Each assertion consumer service is stored as an associative array with the
- * elements that parseGenericEndpoint returns.
- */
- private $spDescriptors;
-
-
- /**
- * This is an array with the processed IDPSSODescriptor elements we have found.
- * Each element in the array is an associative array with the elements from parseSSODescriptor and:
- * - 'SingleSignOnService': Array with the IdP's single signon service endpoints. Each endpoint is stored
- * as an associative array with the elements that parseGenericEndpoint returns.
- */
- private $idpDescriptors;
-
-
- /**
- * List of attribute authorities we have found.
- *
- * @var array
- */
- private $attributeAuthorityDescriptors = array();
-
-
- /**
- * This is an associative array with the organization name for this entity. The key of
- * the associative array is the language code, while the value is a string with the
- * organization name.
- */
- private $organizationName = array();
-
-
- /**
- * This is an associative array with the organization display name for this entity. The key of
- * the associative array is the language code, while the value is a string with the
- * organization display name.
- */
- private $organizationDisplayName = array();
-
-
- /**
- * This is an associative array with the organization URI for this entity. The key of
- * the associative array is the language code, while the value is the URI.
- */
- private $organizationURL = array();
-
-
- /**
- * This is an array of the Contact Persons of this entity.
- */
- private $contacts = array();
-
-
- private $scopes;
- private $entityAttributes;
- private $attributes;
- private $tags;
- private $uiInfo;
- private $discoHints;
-
-
- /**
- * This is an array of elements that may be used to validate this element.
- *
- * @var array
- */
- private $validators = array();
-
-
- /**
- * The original EntityDescriptor element for this entity, as a base64 encoded string.
- */
- private $entityDescriptor;
-
-
- /**
- * This is the constructor for the SAMLParser class.
- *
- * @param SAML2_XML_md_EntityDescriptor $entityElement The EntityDescriptor.
- * @param int|NULL $maxExpireTime The unix timestamp for when this entity should expire, or NULL if unknwon.
- * @param array $validators An array of parent elements that may validate this element.
- */
- private function __construct(SAML2_XML_md_EntityDescriptor $entityElement, $maxExpireTime, array $validators = array()) {
- assert('is_null($maxExpireTime) || is_int($maxExpireTime)');
-
- $this->spDescriptors = array();
- $this->idpDescriptors = array();
-
- $e = $entityElement->toXML();
- $e = $e->ownerDocument->saveXML($e);
- $this->entityDescriptor = base64_encode($e);
- $this->entityId = $entityElement->entityID;
-
- $expireTime = self::getExpireTime($entityElement, $maxExpireTime);
-
- $this->validators = $validators;
- $this->validators[] = $entityElement;
-
-
- /* Process Extensions element, if it exists. */
- $ext = self::processExtensions($entityElement);
- $this->scopes = $ext['scope'];
- $this->tags = $ext['tags'];
- $this->entityAttributes = $ext['EntityAttributes'];
-
- /* Look over the RoleDescriptors. */
- foreach ($entityElement->RoleDescriptor as $child) {
-
- if ($child instanceof SAML2_XML_md_SPSSODescriptor) {
- $this->processSPSSODescriptor($child, $expireTime);
- } elseif ($child instanceof SAML2_XML_md_IDPSSODescriptor) {
- $this->processIDPSSODescriptor($child, $expireTime);
- } elseif ($child instanceof SAML2_XML_md_AttributeAuthorityDescriptor) {
- $this->processAttributeAuthorityDescriptor($child, $expireTime);
- }
- }
-
- if ($entityElement->Organization) {
- $this->processOrganization($entityElement->Organization);
- }
-
- if(!empty($entityElement->ContactPerson)) {
- foreach($entityElement->ContactPerson as $contact) {
- $this->processContactPerson($contact);
- }
- }
- }
-
-
- /**
- * This function parses a file which contains XML encoded metadata.
- *
- * @param $file The path to the file which contains the metadata.
- * @return An instance of this class with the metadata loaded.
- */
- public static function parseFile($file) {
- $doc = new DOMDocument();
-
- $data = \SimpleSAML\Utils\HTTP::fetch($file);
-
- $res = $doc->loadXML($data);
- if($res !== TRUE) {
- throw new Exception('Failed to read XML from file: ' . $file);
- }
-
- return self::parseDocument($doc);
- }
-
-
- /**
- * This function parses a string which contains XML encoded metadata.
- *
- * @param $metadata A string which contains XML encoded metadata.
- * @return An instance of this class with the metadata loaded.
- */
- public static function parseString($metadata) {
- $doc = new DOMDocument();
-
- $res = $doc->loadXML($metadata);
- if($res !== TRUE) {
- throw new Exception('Failed to parse XML string.');
- }
-
- return self::parseDocument($doc);
- }
-
-
- /**
- * This function parses a DOMDocument which is assumed to contain a single EntityDescriptor element.
- *
- * @param $document The DOMDocument which contains the EntityDescriptor element.
- * @return An instance of this class with the metadata loaded.
- */
- public static function parseDocument($document) {
- assert('$document instanceof DOMDocument');
-
- $entityElement = self::findEntityDescriptor($document);
-
- return self::parseElement($entityElement);
- }
-
-
- /**
- * This function parses a SAML2_XML_md_EntityDescriptor object which represents a EntityDescriptor element.
- *
- * @param $entityElement A SAML2_XML_md_EntityDescriptor object which represents a EntityDescriptor element.
- * @return An instance of this class with the metadata loaded.
- */
- public static function parseElement($entityElement) {
- assert('$entityElement instanceof SAML2_XML_md_EntityDescriptor');
-
- return new SimpleSAML_Metadata_SAMLParser($entityElement, NULL);
- }
-
-
- /**
- * This function parses a file where the root node is either an EntityDescriptor element or an
- * EntitiesDescriptor element. In both cases it will return an associative array of SAMLParser instances. If
- * the file contains a single EntityDescriptorElement, then the array will contain a single SAMLParser
- * instance.
- *
- * @param $file The path to the file which contains the EntityDescriptor or EntitiesDescriptor element.
- * @return An array of SAMLParser instances.
- */
- public static function parseDescriptorsFile($file) {
-
- if ($file === NULL) throw new Exception('Cannot open file NULL. File name not specified.');
-
- $data = \SimpleSAML\Utils\HTTP::fetch($file);
-
- $doc = new DOMDocument();
- $res = $doc->loadXML($data);
- if($res !== TRUE) {
- throw new Exception('Failed to read XML from file: ' . $file);
- }
- if ($doc->documentElement === NULL) throw new Exception('Opened file is not an XML document: ' . $file);
-
- return self::parseDescriptorsElement($doc->documentElement);
- }
-
-
- /**
- * This function parses a string with XML data. The root node of the XML data is expected to be either an
- * EntityDescriptor element or an EntitiesDescriptor element. It will return an associative array of
- * SAMLParser instances.
- *
- * @param $string The string with XML data.
- * @return An associative array of SAMLParser instances. The key of the array will be the entity id.
- */
- public static function parseDescriptorsString($string) {
-
- $doc = new DOMDocument();
-
- $res = $doc->loadXML($string);
- if($res !== TRUE) {
- throw new Exception('Failed to parse XML string.');
- }
-
- return self::parseDescriptorsElement($doc->documentElement);
- }
-
-
- /**
- * This function parses a DOMElement which represents either an EntityDescriptor element or an
- * EntitiesDescriptor element. It will return an associative array of SAMLParser instances in both cases.
- *
- * @param DOMElement|NULL $element The DOMElement which contains the EntityDescriptor element or the EntitiesDescriptor element.
- * @return An associative array of SAMLParser instances. The key of the array will be the entity id.
- */
- public static function parseDescriptorsElement(DOMElement $element = NULL) {
-
- if($element === NULL) {
- throw new Exception('Document was empty.');
- }
-
- assert('$element instanceof DOMElement');
-
- if (SimpleSAML\Utils\XML::isDOMElementOfType($element, 'EntityDescriptor', '@md') === TRUE) {
- return self::processDescriptorsElement(new SAML2_XML_md_EntityDescriptor($element));
- } elseif (SimpleSAML\Utils\XML::isDOMElementOfType($element, 'EntitiesDescriptor', '@md') === TRUE) {
- return self::processDescriptorsElement(new SAML2_XML_md_EntitiesDescriptor($element));
- } else {
- throw new Exception('Unexpected root node: [' . $element->namespaceURI . ']:' .
- $element->localName);
- }
-
- return $ret;
- }
-
-
- /**
- *
- * @param SAML2_XML_md_EntityDescriptor|SAML2_XML_md_EntitiesDescriptor $element The element we should process.
- * @param int|NULL $maxExpireTime The maximum expiration time of the entitites.
- * @param array $validators The parent-elements that may be signed.
- * @return array Array of SAMLParser instances.
- */
- private static function processDescriptorsElement($element, $maxExpireTime = NULL, array $validators = array()) {
- assert('is_null($maxExpireTime) || is_int($maxExpireTime)');
-
- if ($element instanceof SAML2_XML_md_EntityDescriptor) {
- $ret = new SimpleSAML_Metadata_SAMLParser($element, $maxExpireTime, $validators);
- return array($ret->getEntityId() => $ret);
- }
-
- assert('$element instanceof SAML2_XML_md_EntitiesDescriptor');
-
-
- $expTime = self::getExpireTime($element, $maxExpireTime);
-
- $validators[] = $element;
-
- $ret = array();
- foreach ($element->children as $child) {
- $ret += self::processDescriptorsElement($child, $expTime, $validators);
- }
-
- return $ret;
- }
-
-
- /**
- * Determine how long a given element can be cached.
- *
- * This function looks for the 'validUntil' attribute to determine
- * how long a given XML-element is valid. It returns this as a unix timestamp.
- *
- * @param mixed $element The element we should determine the expiry time of.
- * @param int|NULL $maxExpireTime The maximum expiration time.
- * @return int The unix timestamp for when the element should expire. Will be NULL if no
- * limit is set for the element.
- */
- private static function getExpireTime($element, $maxExpireTime) {
- /* validUntil may be NULL */
- $expire = $element->validUntil;
-
- if ( $maxExpireTime !== NULL && ($expire === NULL || $maxExpireTime < $expire) ) {
- $expire = $maxExpireTime;
- }
-
- return $expire;
- }
-
-
- /**
- * This function returns the entity id of this parsed entity.
- *
- * @return The entity id of this parsed entity.
- */
- public function getEntityId() {
- return $this->entityId;
- }
-
-
- private function getMetadataCommon() {
- $ret = array();
- $ret['entityid'] = $this->entityId;
- $ret['entityDescriptor'] = $this->entityDescriptor;
-
-
- /*
- * Add organizational metadata
- */
- if (!empty($this->organizationName)) {
- $ret['description'] = $this->organizationName;
- $ret['OrganizationName'] = $this->organizationName;
- }
- if (!empty($this->organizationDisplayName)) {
- $ret['name'] = $this->organizationDisplayName;
- $ret['OrganizationDisplayName'] = $this->organizationDisplayName;
- }
- if (!empty($this->organizationURL)) {
- $ret['url'] = $this->organizationURL;
- $ret['OrganizationURL'] = $this->organizationURL;
- }
-
- /*
- * Add contact metadata
- */
- $ret['contacts'] = $this->contacts;
-
- return $ret;
- }
-
-
- /**
- * Add data parsed from extensions to metadata.
- *
- * @param array &$metadata The metadata that should be updated.
- * @param array $roleDescriptor The parsed role desciptor.
- */
- private function addExtensions(array &$metadata, array $roleDescriptor) {
- assert('array_key_exists("scope", $roleDescriptor)');
- assert('array_key_exists("tags", $roleDescriptor)');
-
- $scopes = array_merge($this->scopes, array_diff($roleDescriptor['scope'], $this->scopes));
- if (!empty($scopes)) {
- $metadata['scope'] = $scopes;
- }
-
- $tags = array_merge($this->tags, array_diff($roleDescriptor['tags'], $this->tags));
- if (!empty($tags)) {
- $metadata['tags'] = $tags;
- }
-
- if (!empty($this->entityAttributes)) {
- $metadata['EntityAttributes'] = $this->entityAttributes;
- }
-
- if (!empty($roleDescriptor['UIInfo'])) {
- $metadata['UIInfo'] = $roleDescriptor['UIInfo'];
- }
-
- if (!empty($roleDescriptor['DiscoHints'])) {
- $metadata['DiscoHints'] = $roleDescriptor['DiscoHints'];
- }
- }
-
-
- /**
- * This function returns the metadata for SAML 1.x SPs in the format simpleSAMLphp expects.
- * This is an associative array with the following fields:
- * - 'entityid': The entity id of the entity described in the metadata.
- * - 'AssertionConsumerService': String with the URL of the assertion consumer service which supports
- * the browser-post binding.
- * - 'certData': X509Certificate for entity (if present).
- *
- * Metadata must be loaded with one of the parse functions before this function can be called.
- *
- * @return Associative array with metadata or NULL if we are unable to generate metadata for a SAML 1.x SP.
- */
- public function getMetadata1xSP() {
-
- $ret = $this->getMetadataCommon();
- $ret['metadata-set'] = 'shib13-sp-remote';
-
-
- /* Find SP information which supports one of the SAML 1.x protocols. */
- $spd = $this->getSPDescriptors(self::$SAML1xProtocols);
- if(count($spd) === 0) {
- return NULL;
- }
-
- /* We currently only look at the first SPDescriptor which supports SAML 1.x. */
- $spd = $spd[0];
-
- /* Add expire time to metadata. */
- if (array_key_exists('expire', $spd)) {
- $ret['expire'] = $spd['expire'];
- }
-
- /* Find the assertion consumer service endpoints. */
- $ret['AssertionConsumerService'] = $spd['AssertionConsumerService'];
-
- /* Add the list of attributes the SP should receive. */
- if (array_key_exists('attributes', $spd)) {
- $ret['attributes'] = $spd['attributes'];
- }
- if (array_key_exists('attributes.required', $spd)) {
- $ret['attributes.required'] = $spd['attributes.required'];
- }
- if (array_key_exists('attributes.NameFormat', $spd)) {
- $ret['attributes.NameFormat'] = $spd['attributes.NameFormat'];
- }
-
- /* Add name & description. */
- if (array_key_exists('name', $spd)) {
- $ret['name'] = $spd['name'];
- }
- if (array_key_exists('description', $spd)) {
- $ret['description'] = $spd['description'];
- }
-
- /* Add public keys. */
- if (!empty($spd['keys'])) {
- $ret['keys'] = $spd['keys'];
- }
-
- /* Add extensions. */
- $this->addExtensions($ret, $spd);
-
- // prioritize mdui:DisplayName as the name if available
- if (!empty($ret['UIInfo']['DisplayName'])) {
- $ret['name'] = $ret['UIInfo']['DisplayName'];
- }
-
- return $ret;
- }
-
-
- /**
- * This function returns the metadata for SAML 1.x IdPs in the format simpleSAMLphp expects.
- * This is an associative array with the following fields:
- * - 'entityid': The entity id of the entity described in the metadata.
- * - 'name': Autogenerated name for this entity. Currently set to the entity id.
- * - 'SingleSignOnService': String with the URL of the SSO service which supports the redirect binding.
- * - 'SingleLogoutService': String with the URL where we should send logout requests/responses.
- * - 'certData': X509Certificate for entity (if present).
- * - 'certFingerprint': Fingerprint of the X509Certificate from the metadata.
- *
- * Metadata must be loaded with one of the parse functions before this function can be called.
- *
- * @return Associative array with metadata or NULL if we are unable to generate metadata for a SAML 1.x IdP.
- */
- public function getMetadata1xIdP() {
-
- $ret = $this->getMetadataCommon();
- $ret['metadata-set'] = 'shib13-idp-remote';
-
- /* Find IdP information which supports the SAML 1.x protocol. */
- $idp = $this->getIdPDescriptors(self::$SAML1xProtocols);
- if(count($idp) === 0) {
- return NULL;
- }
-
- /* We currently only look at the first IDP descriptor which supports SAML 1.x. */
- $idp = $idp[0];
-
- /* Add expire time to metadata. */
- if (array_key_exists('expire', $idp)) {
- $ret['expire'] = $idp['expire'];
- }
-
- /* Find the SSO service endpoints. */
- $ret['SingleSignOnService'] = $idp['SingleSignOnService'];
-
- /* Find the ArtifactResolutionService endpoint. */
- $ret['ArtifactResolutionService'] = $idp['ArtifactResolutionService'];
-
- /* Add public keys. */
- if (!empty($idp['keys'])) {
- $ret['keys'] = $idp['keys'];
- }
-
- /* Add extensions. */
- $this->addExtensions($ret, $idp);
-
- // prioritize mdui:DisplayName as the name if available
- if (!empty($ret['UIInfo']['DisplayName'])) {
- $ret['name'] = $ret['UIInfo']['DisplayName'];
- }
-
- return $ret;
- }
-
-
- /**
- * This function returns the metadata for SAML 2.0 SPs in the format simpleSAMLphp expects.
- * This is an associative array with the following fields:
- * - 'entityid': The entity id of the entity described in the metadata.
- * - 'AssertionConsumerService': String with the URL of the assertion consumer service which supports
- * the browser-post binding.
- * - 'SingleLogoutService': String with the URL where we should send logout requests/responses.
- * - 'NameIDFormat': The name ID format this SP expects. This may be unset.
- * - 'certData': X509Certificate for entity (if present).
- *
- * Metadata must be loaded with one of the parse functions before this function can be called.
- *
- * @return Associative array with metadata or NULL if we are unable to generate metadata for a SAML 2.x SP.
- */
- public function getMetadata20SP() {
-
- $ret = $this->getMetadataCommon();
- $ret['metadata-set'] = 'saml20-sp-remote';
-
-
- /* Find SP information which supports the SAML 2.0 protocol. */
- $spd = $this->getSPDescriptors(self::$SAML20Protocols);
- if(count($spd) === 0) {
- return NULL;
- }
-
- /* We currently only look at the first SPDescriptor which supports SAML 2.0. */
- $spd = $spd[0];
-
- /* Add expire time to metadata. */
- if (array_key_exists('expire', $spd)) {
- $ret['expire'] = $spd['expire'];
- }
-
- /* Find the assertion consumer service endpoints. */
- $ret['AssertionConsumerService'] = $spd['AssertionConsumerService'];
-
-
- /* Find the single logout service endpoint. */
- $ret['SingleLogoutService'] = $spd['SingleLogoutService'];
-
-
- /* Find the NameIDFormat. This may not exists. */
- if(count($spd['nameIDFormats']) > 0) {
- /* simpleSAMLphp currently only supports a single NameIDFormat pr. SP. We use the first one. */
- $ret['NameIDFormat'] = $spd['nameIDFormats'][0];
- }
-
- /* Add the list of attributes the SP should receive. */
- if (array_key_exists('attributes', $spd)) {
- $ret['attributes'] = $spd['attributes'];
- }
- if (array_key_exists('attributes.required', $spd)) {
- $ret['attributes.required'] = $spd['attributes.required'];
- }
- if (array_key_exists('attributes.NameFormat', $spd)) {
- $ret['attributes.NameFormat'] = $spd['attributes.NameFormat'];
- }
-
- /* Add name & description. */
- if (array_key_exists('name', $spd)) {
- $ret['name'] = $spd['name'];
- }
- if (array_key_exists('description', $spd)) {
- $ret['description'] = $spd['description'];
- }
-
- /* Add public keys. */
- if (!empty($spd['keys'])) {
- $ret['keys'] = $spd['keys'];
- }
-
- /* Add validate.authnrequest. */
- if (array_key_exists('AuthnRequestsSigned', $spd)) {
- $ret['validate.authnrequest'] = $spd['AuthnRequestsSigned'];
- }
-
- /* Add saml20.sign.assertion. */
- if (array_key_exists('WantAssertionsSigned', $spd)) {
- $ret['saml20.sign.assertion'] = $spd['WantAssertionsSigned'];
- }
-
- /* Add extensions. */
- $this->addExtensions($ret, $spd);
-
- // prioritize mdui:DisplayName as the name if available
- if (!empty($ret['UIInfo']['DisplayName'])) {
- $ret['name'] = $ret['UIInfo']['DisplayName'];
- }
-
- return $ret;
- }
-
-
- /**
- * This function returns the metadata for SAML 2.0 IdPs in the format simpleSAMLphp expects.
- * This is an associative array with the following fields:
- * - 'entityid': The entity id of the entity described in the metadata.
- * - 'name': Autogenerated name for this entity. Currently set to the entity id.
- * - 'SingleSignOnService': String with the URL of the SSO service which supports the redirect binding.
- * - 'SingleLogoutService': String with the URL where we should send logout requests(/responses).
- * - 'SingleLogoutServiceResponse': String where we should send logout responses (if this is different from
- * the 'SingleLogoutService' endpoint.
- * - 'NameIDFormats': The name ID formats this IdP supports.
- * - 'certData': X509Certificate for entity (if present).
- * - 'certFingerprint': Fingerprint of the X509Certificate from the metadata.
- *
- * Metadata must be loaded with one of the parse functions before this function can be called.
- *
- * @return Associative array with metadata or NULL if we are unable to generate metadata for a SAML 2.0 IdP.
- */
- public function getMetadata20IdP() {
-
- $ret = $this->getMetadataCommon();
- $ret['metadata-set'] = 'saml20-idp-remote';
-
-
- /* Find IdP information which supports the SAML 2.0 protocol. */
- $idp = $this->getIdPDescriptors(self::$SAML20Protocols);
- if(count($idp) === 0) {
- return NULL;
- }
-
- /* We currently only look at the first IDP descriptor which supports SAML 2.0. */
- $idp = $idp[0];
-
- /* Add expire time to metadata. */
- if (array_key_exists('expire', $idp)) {
- $ret['expire'] = $idp['expire'];
- }
-
- /* Enable redirect.sign if WantAuthnRequestsSigned is enabled. */
- if ($idp['WantAuthnRequestsSigned']) {
- $ret['sign.authnrequest'] = TRUE;
- }
-
- /* Find the SSO service endpoint. */
- $ret['SingleSignOnService'] = $idp['SingleSignOnService'];
-
-
- /* Find the single logout service endpoint. */
- $ret['SingleLogoutService'] = $idp['SingleLogoutService'];
-
- /* Find the ArtifactResolutionService endpoint. */
- $ret['ArtifactResolutionService'] = $idp['ArtifactResolutionService'];
-
- /* Add supported nameIDFormats */
- $ret['NameIDFormats'] = $idp['nameIDFormats'];
-
- /* Add public keys. */
- if (!empty($idp['keys'])) {
- $ret['keys'] = $idp['keys'];
- }
-
- /* Add extensions. */
- $this->addExtensions($ret, $idp);
-
- // prioritize mdui:DisplayName as the name if available
- if (!empty($ret['UIInfo']['DisplayName'])) {
- $ret['name'] = $ret['UIInfo']['DisplayName'];
- }
-
- return $ret;
- }
-
-
- /**
- * Retrieve AttributeAuthorities from the metadata.
- *
- * @return array Array of AttributeAuthorityDescriptor entries.
- */
- public function getAttributeAuthorities() {
-
- return $this->attributeAuthorityDescriptors;
- }
-
-
- /**
- * Parse a RoleDescriptorType element.
- *
- * The returned associative array has the following elements:
- * - 'protocols': Array with the protocols supported.
- * - 'expire': Timestamp for when this descriptor expires.
- * - 'keys': Array of associative arrays with the elements from parseKeyDescriptor.
- *
- * @param SAML2_XML_md_RoleDescriptor $element The element we should extract metadata from.
- * @param int|NULL $expireTime The unix timestamp for when this element should expire, or
- * NULL if unknwon.
- * @return Associative array with metadata we have extracted from this element.
- */
- private static function parseRoleDescriptorType(SAML2_XML_md_RoleDescriptor $element, $expireTime) {
- assert('is_null($expireTime) || is_int($expireTime)');
-
- $ret = array();
-
- $expireTime = self::getExpireTime($element, $expireTime);
-
- if ($expireTime !== NULL) {
- /* We have got an expire timestamp, either from this element, or one of the
- * parent elements.
- */
- $ret['expire'] = $expireTime;
- }
-
- $ret['protocols'] = $element->protocolSupportEnumeration;
-
- /* Process KeyDescriptor elements. */
- $ret['keys'] = array();
- foreach ($element->KeyDescriptor as $kd) {
- $key = self::parseKeyDescriptor($kd);
- if($key !== NULL) {
- $ret['keys'][] = $key;
- }
- }
-
- $ext = self::processExtensions($element);
- $ret['scope'] = $ext['scope'];
- $ret['tags'] = $ext['tags'];
- $ret['EntityAttributes'] = $ext['EntityAttributes'];
- $ret['UIInfo'] = $ext['UIInfo'];
- $ret['DiscoHints'] = $ext['DiscoHints'];
-
- return $ret;
- }
-
-
- /**
- * This function extracts metadata from a SSODescriptor element.
- *
- * The returned associative array has the following elements:
- * - 'protocols': Array with the protocols this SSODescriptor supports.
- * - 'SingleLogoutService': Array with the single logout service endpoints. Each endpoint is stored
- * as an associative array with the elements that parseGenericEndpoint returns.
- * - 'nameIDFormats': The NameIDFormats supported by this SSODescriptor. This may be an empty array.
- * - 'keys': Array of associative arrays with the elements from parseKeyDescriptor:
- *
- * @param SAML2_XML_md_SSODescriptorType $element The element we should extract metadata from.
- * @param int|NULL $expireTime The unix timestamp for when this element should expire, or
- * NULL if unknwon.
- * @return Associative array with metadata we have extracted from this element.
- */
- private static function parseSSODescriptor(SAML2_XML_md_SSODescriptorType $element, $expireTime) {
- assert('is_null($expireTime) || is_int($expireTime)');
-
- $sd = self::parseRoleDescriptorType($element, $expireTime);
-
- /* Find all SingleLogoutService elements. */
- $sd['SingleLogoutService'] = self::extractEndpoints($element->SingleLogoutService);
-
- /* Find all ArtifactResolutionService elements. */
- $sd['ArtifactResolutionService'] = self::extractEndpoints($element->ArtifactResolutionService);
-
-
- /* Process NameIDFormat elements. */
- $sd['nameIDFormats'] = $element->NameIDFormat;
-
- return $sd;
- }
-
-
- /**
- * This function extracts metadata from a SPSSODescriptor element.
- *
- * @param SAML2_XML_md_SPSSODescriptor $element The element which should be parsed.
- * @param int|NULL $expireTime The unix timestamp for when this element should expire, or
- * NULL if unknwon.
- */
- private function processSPSSODescriptor(SAML2_XML_md_SPSSODescriptor $element, $expireTime) {
- assert('is_null($expireTime) || is_int($expireTime)');
-
- $sp = self::parseSSODescriptor($element, $expireTime);
-
- /* Find all AssertionConsumerService elements. */
- $sp['AssertionConsumerService'] = self::extractEndpoints($element->AssertionConsumerService);
-
- /* Find all the attributes and SP name... */
- $attcs = $element->AttributeConsumingService;
- if (count($attcs) > 0) {
- self::parseAttributeConsumerService($attcs[0], $sp);
- }
-
- /* Check AuthnRequestsSigned */
- if ($element->AuthnRequestsSigned !== NULL) {
- $sp['AuthnRequestsSigned'] = $element->AuthnRequestsSigned;
- }
-
- /* Check WantAssertionsSigned */
- if ($element->WantAssertionsSigned !== NULL) {
- $sp['WantAssertionsSigned'] = $element->WantAssertionsSigned;
- }
-
- $this->spDescriptors[] = $sp;
- }
-
-
- /**
- * This function extracts metadata from a IDPSSODescriptor element.
- *
- * @param SAML2_XML_md_IDPSSODescriptor $element The element which should be parsed.
- * @param int|NULL $expireTime The unix timestamp for when this element should expire, or
- * NULL if unknwon.
- */
- private function processIDPSSODescriptor(SAML2_XML_md_IDPSSODescriptor $element, $expireTime) {
- assert('is_null($expireTime) || is_int($expireTime)');
-
- $idp = self::parseSSODescriptor($element, $expireTime);
-
- /* Find all SingleSignOnService elements. */
- $idp['SingleSignOnService'] = self::extractEndpoints($element->SingleSignOnService);
-
- if ($element->WantAuthnRequestsSigned) {
- $idp['WantAuthnRequestsSigned'] = TRUE;
- } else {
- $idp['WantAuthnRequestsSigned'] = FALSE;
- }
-
- $this->idpDescriptors[] = $idp;
- }
-
-
- /**
- * This function extracts metadata from a AttributeAuthorityDescriptor element.
- *
- * @param SAML2_XML_md_AttributeAuthorityDescriptor $element The element which should be parsed.
- * @param int|NULL $expireTime The unix timestamp for when this element should expire, or
- * NULL if unknwon.
- */
- private function processAttributeAuthorityDescriptor(SAML2_XML_md_AttributeAuthorityDescriptor $element, $expireTime) {
- assert('is_null($expireTime) || is_int($expireTime)');
-
- $aad = self::parseRoleDescriptorType($element, $expireTime);
- $aad['entityid'] = $this->entityId;
- $aad['metadata-set'] = 'attributeauthority-remote';
-
- $aad['AttributeService'] = self::extractEndpoints($element->AttributeService);
- $aad['AssertionIDRequestService'] = self::extractEndpoints($element->AssertionIDRequestService);
- $aad['NameIDFormat'] = $element->NameIDFormat;
-
- $this->attributeAuthorityDescriptors[] = $aad;
- }
-
-
- /**
- * Parse an Extensions element.
- *
- * @param mixed $element The element which contains the Extensions element.
- */
- private static function processExtensions($element) {
-
- $ret = array(
- 'scope' => array(),
- 'tags' => array(),
- 'EntityAttributes' => array(),
- 'UIInfo' => array(),
- 'DiscoHints' => array(),
- );
-
- foreach ($element->Extensions as $e) {
-
- if ($e instanceof SAML2_XML_shibmd_Scope) {
- $ret['scope'][] = $e->scope;
- continue;
- }
-
- // Entity Attributes are only allowed at entity level extensions
- // and not at RoleDescriptor level
- if ($element instanceof SAML2_XML_md_EntityDescriptor) {
-
- if ($e instanceof SAML2_XML_mdattr_EntityAttributes && !empty($e->children)) {
-
- foreach($e->children AS $attr) {
-
- // Only saml:Attribute are currently supported here. The specifications also allows
- // saml:Assertions, which more complex processing.
- if ($attr instanceof SAML2_XML_saml_Attribute) {
- if (empty($attr->Name) || empty($attr->AttributeValue)) continue;
-
- // Attribute names that is not URI is prefixed as this: '{nameformat}name'
- $name = $attr->Name;
- if(empty($attr->NameFormat)) {
- $name = '{' . SAML2_Const::NAMEFORMAT_UNSPECIFIED . '}' . $attr->Name;
- } elseif ($attr->NameFormat !== 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri') {
- $name = '{' . $attr->NameFormat . '}' . $attr->Name;
- }
-
- $values = array();
- foreach($attr->AttributeValue AS $attrvalue) {
- $values[] = $attrvalue->getString();
- }
-
- $ret['EntityAttributes'][$name] = $values;
- }
- }
- }
- }
-
- // UIInfo elements are only allowed at RoleDescriptor level extensions
- if ($element instanceof SAML2_XML_md_RoleDescriptor) {
-
- if ($e instanceof SAML2_XML_mdui_UIInfo) {
-
- $ret['UIInfo']['DisplayName'] = $e->DisplayName;
- $ret['UIInfo']['Description'] = $e->Description;
- $ret['UIInfo']['InformationURL'] = $e->InformationURL;
- $ret['UIInfo']['PrivacyStatementURL'] = $e->PrivacyStatementURL;
-
- foreach($e->Keywords as $uiItem) {
- if (!($uiItem instanceof SAML2_XML_mdui_Keywords)
- || empty($uiItem->Keywords)
- || empty($uiItem->lang))
- continue;
- $ret['UIInfo']['Keywords'][$uiItem->lang] = $uiItem->Keywords;
- }
- foreach($e->Logo as $uiItem) {
- if (!($uiItem instanceof SAML2_XML_mdui_Logo)
- || empty($uiItem->url)
- || empty($uiItem->height)
- || empty($uiItem->width))
- continue;
- $logo = array(
- 'url' => $uiItem->url,
- 'height' => $uiItem->height,
- 'width' => $uiItem->width,
- );
- if (!empty($uiItem->Lang)) {
- $logo['lang'] = $uiItem->lang;
- }
- $ret['UIInfo']['Logo'][] = $logo;
- }
- }
- }
-
- // DiscoHints elements are only allowed at IDPSSODescriptor level extensions
- if ($element instanceof SAML2_XML_md_IDPSSODescriptor) {
-
- if ($e instanceof SAML2_XML_mdui_DiscoHints) {
- $ret['DiscoHints']['IPHint'] = $e->IPHint;
- $ret['DiscoHints']['DomainHint'] = $e->DomainHint;
- $ret['DiscoHints']['GeolocationHint'] = $e->GeolocationHint;
- }
- }
-
-
- if (!($e instanceof SAML2_XML_Chunk)) {
- continue;
- }
-
-
- if ($e->localName === 'Attribute' && $e->namespaceURI === SAML2_Const::NS_SAML) {
- $attribute = $e->getXML();
-
- $name = $attribute->getAttribute('Name');
- $values = array_map(
- array('SimpleSAML\Utils\XML', 'getDOMText'),
+class SimpleSAML_Metadata_SAMLParser
+{
+
+ /**
+ * This is the list of SAML 1.x protocols.
+ *
+ * @var string[]
+ */
+ private static $SAML1xProtocols = array(
+ 'urn:oasis:names:tc:SAML:1.0:protocol',
+ 'urn:oasis:names:tc:SAML:1.1:protocol',
+ );
+
+
+ /**
+ * This is the list with the SAML 2.0 protocol.
+ *
+ * @var string[]
+ */
+ private static $SAML20Protocols = array(
+ 'urn:oasis:names:tc:SAML:2.0:protocol',
+ );
+
+
+ /**
+ * This is the entity id we find in the metadata.
+ *
+ * @var string
+ */
+ private $entityId;
+
+
+ /**
+ * This is an array with the processed SPSSODescriptor elements we have found in this
+ * metadata file.
+ * Each element in the array is an associative array with the elements from parseSSODescriptor and:
+ * - 'AssertionConsumerService': Array with the SP's assertion consumer services.
+ * Each assertion consumer service is stored as an associative array with the
+ * elements that parseGenericEndpoint returns.
+ *
+ * @var array[]
+ */
+ private $spDescriptors;
+
+
+ /**
+ * This is an array with the processed IDPSSODescriptor elements we have found.
+ * Each element in the array is an associative array with the elements from parseSSODescriptor and:
+ * - 'SingleSignOnService': Array with the IdP's single sign on service endpoints. Each endpoint is stored
+ * as an associative array with the elements that parseGenericEndpoint returns.
+ *
+ * @var array[]
+ */
+ private $idpDescriptors;
+
+
+ /**
+ * List of attribute authorities we have found.
+ *
+ * @var array
+ */
+ private $attributeAuthorityDescriptors = array();
+
+
+ /**
+ * This is an associative array with the organization name for this entity. The key of
+ * the associative array is the language code, while the value is a string with the
+ * organization name.
+ *
+ * @var string[]
+ */
+ private $organizationName = array();
+
+
+ /**
+ * This is an associative array with the organization display name for this entity. The key of
+ * the associative array is the language code, while the value is a string with the
+ * organization display name.
+ *
+ * @var string[]
+ */
+ private $organizationDisplayName = array();
+
+
+ /**
+ * This is an associative array with the organization URI for this entity. The key of
+ * the associative array is the language code, while the value is the URI.
+ *
+ * @var string[]
+ */
+ private $organizationURL = array();
+
+
+ /**
+ * This is an array of the Contact Persons of this entity.
+ *
+ * @var array[]
+ */
+ private $contacts = array();
+
+
+ /**
+ * @var array
+ */
+ private $scopes;
+
+
+ /**
+ * @var array
+ */
+ private $entityAttributes;
+
+
+ /**
+ * @var array
+ */
+ private $tags;
+
+
+ /**
+ * This is an array of elements that may be used to validate this element.
+ *
+ * @var SAML2_SignedElementHelper[]
+ */
+ private $validators = array();
+
+
+ /**
+ * The original EntityDescriptor element for this entity, as a base64 encoded string.
+ *
+ * @var string
+ */
+ private $entityDescriptor;
+
+
+ /**
+ * This is the constructor for the SAMLParser class.
+ *
+ * @param SAML2_XML_md_EntityDescriptor $entityElement The EntityDescriptor.
+ * @param int|NULL $maxExpireTime The unix timestamp for when this entity should expire, or
+ * NULL if unknown.
+ * @param array $validators An array of parent elements that may validate this element.
+ */
+ private function __construct(
+ SAML2_XML_md_EntityDescriptor $entityElement,
+ $maxExpireTime,
+ array $validators = array()
+ ) {
+ assert('is_null($maxExpireTime) || is_int($maxExpireTime)');
+
+ $this->spDescriptors = array();
+ $this->idpDescriptors = array();
+
+ $e = $entityElement->toXML();
+ $e = $e->ownerDocument->saveXML($e);
+ $this->entityDescriptor = base64_encode($e);
+ $this->entityId = $entityElement->entityID;
+
+ $expireTime = self::getExpireTime($entityElement, $maxExpireTime);
+
+ $this->validators = $validators;
+ $this->validators[] = $entityElement;
+
+ // process Extensions element, if it exists
+ $ext = self::processExtensions($entityElement);
+ $this->scopes = $ext['scope'];
+ $this->tags = $ext['tags'];
+ $this->entityAttributes = $ext['EntityAttributes'];
+
+ // look over the RoleDescriptors
+ foreach ($entityElement->RoleDescriptor as $child) {
+
+ if ($child instanceof SAML2_XML_md_SPSSODescriptor) {
+ $this->processSPSSODescriptor($child, $expireTime);
+ } elseif ($child instanceof SAML2_XML_md_IDPSSODescriptor) {
+ $this->processIDPSSODescriptor($child, $expireTime);
+ } elseif ($child instanceof SAML2_XML_md_AttributeAuthorityDescriptor) {
+ $this->processAttributeAuthorityDescriptor($child, $expireTime);
+ }
+ }
+
+ if ($entityElement->Organization) {
+ $this->processOrganization($entityElement->Organization);
+ }
+
+ if (!empty($entityElement->ContactPerson)) {
+ foreach ($entityElement->ContactPerson as $contact) {
+ $this->processContactPerson($contact);
+ }
+ }
+ }
+
+
+ /**
+ * This function parses a file which contains XML encoded metadata.
+ *
+ * @param string $file The path to the file which contains the metadata.
+ *
+ * @return SimpleSAML_Metadata_SAMLParser An instance of this class with the metadata loaded.
+ * @throws Exception If the file does not parse as XML.
+ */
+ public static function parseFile($file)
+ {
+ $doc = new DOMDocument();
+
+ $data = \SimpleSAML\Utils\HTTP::fetch($file);
+
+ $res = $doc->loadXML($data);
+ if ($res !== true) {
+ throw new Exception('Failed to read XML from file: '.$file);
+ }
+
+ return self::parseDocument($doc);
+ }
+
+
+ /**
+ * This function parses a string which contains XML encoded metadata.
+ *
+ * @param string $metadata A string which contains XML encoded metadata.
+ *
+ * @return SimpleSAML_Metadata_SAMLParser An instance of this class with the metadata loaded.
+ * @throws Exception If the string does not parse as XML.
+ */
+ public static function parseString($metadata)
+ {
+ $doc = new DOMDocument();
+
+ $res = $doc->loadXML($metadata);
+ if ($res !== true) {
+ throw new Exception('Failed to parse XML string.');
+ }
+
+ return self::parseDocument($doc);
+ }
+
+
+ /**
+ * This function parses a DOMDocument which is assumed to contain a single EntityDescriptor element.
+ *
+ * @param DOMDocument $document The DOMDocument which contains the EntityDescriptor element.
+ *
+ * @return SimpleSAML_Metadata_SAMLParser An instance of this class with the metadata loaded.
+ */
+ public static function parseDocument($document)
+ {
+ assert('$document instanceof DOMDocument');
+
+ $entityElement = self::findEntityDescriptor($document);
+
+ return self::parseElement($entityElement);
+ }
+
+
+ /**
+ * This function parses a SAML2_XML_md_EntityDescriptor object which represents a EntityDescriptor element.
+ *
+ * @param SAML2_XML_md_EntityDescriptor $entityElement A SAML2_XML_md_EntityDescriptor object which represents a
+ * EntityDescriptor element.
+ *
+ * @return SimpleSAML_Metadata_SAMLParser An instance of this class with the metadata loaded.
+ */
+ public static function parseElement($entityElement)
+ {
+ assert('$entityElement instanceof SAML2_XML_md_EntityDescriptor');
+
+ return new SimpleSAML_Metadata_SAMLParser($entityElement, null);
+ }
+
+
+ /**
+ * This function parses a file where the root node is either an EntityDescriptor element or an
+ * EntitiesDescriptor element. In both cases it will return an associative array of SAMLParser instances. If
+ * the file contains a single EntityDescriptorElement, then the array will contain a single SAMLParser
+ * instance.
+ *
+ * @param string $file The path to the file which contains the EntityDescriptor or EntitiesDescriptor element.
+ *
+ * @return SimpleSAML_Metadata_SAMLParser[] An array of SAMLParser instances.
+ * @throws Exception If the file does not parse as XML.
+ */
+ public static function parseDescriptorsFile($file)
+ {
+
+ if ($file === null) {
+ throw new Exception('Cannot open file NULL. File name not specified.');
+ }
+
+ $data = \SimpleSAML\Utils\HTTP::fetch($file);
+
+ $doc = new DOMDocument();
+ $res = $doc->loadXML($data);
+ if ($res !== true) {
+ throw new Exception('Failed to read XML from file: '.$file);
+ }
+ if ($doc->documentElement === null) {
+ throw new Exception('Opened file is not an XML document: '.$file);
+ }
+
+ return self::parseDescriptorsElement($doc->documentElement);
+ }
+
+
+ /**
+ * This function parses a string with XML data. The root node of the XML data is expected to be either an
+ * EntityDescriptor element or an EntitiesDescriptor element. It will return an associative array of
+ * SAMLParser instances.
+ *
+ * @param string $string The string with XML data.
+ *
+ * @return SimpleSAML_Metadata_SAMLParser[] An associative array of SAMLParser instances. The key of the array will
+ * be the entity id.
+ * @throws Exception If the string does not parse as XML.
+ */
+ public static function parseDescriptorsString($string)
+ {
+ $doc = new DOMDocument();
+
+ $res = $doc->loadXML($string);
+ if ($res !== true) {
+ throw new Exception('Failed to parse XML string.');
+ }
+
+ return self::parseDescriptorsElement($doc->documentElement);
+ }
+
+
+ /**
+ * This function parses a DOMElement which represents either an EntityDescriptor element or an
+ * EntitiesDescriptor element. It will return an associative array of SAMLParser instances in both cases.
+ *
+ * @param DOMElement|NULL $element The DOMElement which contains the EntityDescriptor element or the
+ * EntitiesDescriptor element.
+ *
+ * @return SimpleSAML_Metadata_SAMLParser[] An associative array of SAMLParser instances. The key of the array will
+ * be the entity id.
+ * @throws Exception if the document is empty or the root is an unexpected node.
+ */
+ public static function parseDescriptorsElement(DOMElement $element = null)
+ {
+ if ($element === null) {
+ throw new Exception('Document was empty.');
+ }
+
+ assert('$element instanceof DOMElement');
+
+ if (SimpleSAML\Utils\XML::isDOMElementOfType($element, 'EntityDescriptor', '@md') === true) {
+ return self::processDescriptorsElement(new SAML2_XML_md_EntityDescriptor($element));
+ } elseif (SimpleSAML\Utils\XML::isDOMElementOfType($element, 'EntitiesDescriptor', '@md') === true) {
+ return self::processDescriptorsElement(new SAML2_XML_md_EntitiesDescriptor($element));
+ } else {
+ throw new Exception('Unexpected root node: ['.$element->namespaceURI.']:'.$element->localName);
+ }
+ }
+
+
+ /**
+ *
+ * @param SAML2_XML_md_EntityDescriptor|SAML2_XML_md_EntitiesDescriptor $element The element we should process.
+ * @param int|NULL $maxExpireTime The maximum expiration time
+ * of the entities.
+ * @param array $validators The parent-elements that may be
+ * signed.
+ *
+ * @return SimpleSAML_Metadata_SAMLParser[] Array of SAMLParser instances.
+ */
+ private static function processDescriptorsElement($element, $maxExpireTime = null, array $validators = array())
+ {
+ assert('is_null($maxExpireTime) || is_int($maxExpireTime)');
+
+ if ($element instanceof SAML2_XML_md_EntityDescriptor) {
+ $ret = new SimpleSAML_Metadata_SAMLParser($element, $maxExpireTime, $validators);
+ $ret = array($ret->getEntityId() => $ret);
+ /** @var SimpleSAML_Metadata_SAMLParser[] $ret */
+ return $ret;
+ }
+
+ assert('$element instanceof SAML2_XML_md_EntitiesDescriptor');
+
+ $expTime = self::getExpireTime($element, $maxExpireTime);
+
+ $validators[] = $element;
+
+ $ret = array();
+ foreach ($element->children as $child) {
+ $ret += self::processDescriptorsElement($child, $expTime, $validators);
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * Determine how long a given element can be cached.
+ *
+ * This function looks for the 'validUntil' attribute to determine
+ * how long a given XML-element is valid. It returns this as a unix timestamp.
+ *
+ * @param mixed $element The element we should determine the expiry time of.
+ * @param int|NULL $maxExpireTime The maximum expiration time.
+ *
+ * @return int The unix timestamp for when the element should expire. Will be NULL if no
+ * limit is set for the element.
+ */
+ private static function getExpireTime($element, $maxExpireTime)
+ {
+ // validUntil may be null
+ $expire = $element->validUntil;
+
+ if ($maxExpireTime !== null && ($expire === null || $maxExpireTime < $expire)) {
+ $expire = $maxExpireTime;
+ }
+
+ return $expire;
+ }
+
+
+ /**
+ * This function returns the entity id of this parsed entity.
+ *
+ * @return string The entity id of this parsed entity.
+ */
+ public function getEntityId()
+ {
+ return $this->entityId;
+ }
+
+
+ private function getMetadataCommon()
+ {
+ $ret = array();
+ $ret['entityid'] = $this->entityId;
+ $ret['entityDescriptor'] = $this->entityDescriptor;
+
+ // add organizational metadata
+ if (!empty($this->organizationName)) {
+ $ret['description'] = $this->organizationName;
+ $ret['OrganizationName'] = $this->organizationName;
+ }
+ if (!empty($this->organizationDisplayName)) {
+ $ret['name'] = $this->organizationDisplayName;
+ $ret['OrganizationDisplayName'] = $this->organizationDisplayName;
+ }
+ if (!empty($this->organizationURL)) {
+ $ret['url'] = $this->organizationURL;
+ $ret['OrganizationURL'] = $this->organizationURL;
+ }
+
+ //add contact metadata
+ $ret['contacts'] = $this->contacts;
+
+ return $ret;
+ }
+
+
+ /**
+ * Add data parsed from extensions to metadata.
+ *
+ * @param array &$metadata The metadata that should be updated.
+ * @param array $roleDescriptor The parsed role descriptor.
+ */
+ private function addExtensions(array &$metadata, array $roleDescriptor)
+ {
+ assert('array_key_exists("scope", $roleDescriptor)');
+ assert('array_key_exists("tags", $roleDescriptor)');
+
+ $scopes = array_merge($this->scopes, array_diff($roleDescriptor['scope'], $this->scopes));
+ if (!empty($scopes)) {
+ $metadata['scope'] = $scopes;
+ }
+
+ $tags = array_merge($this->tags, array_diff($roleDescriptor['tags'], $this->tags));
+ if (!empty($tags)) {
+ $metadata['tags'] = $tags;
+ }
+
+ if (!empty($this->entityAttributes)) {
+ $metadata['EntityAttributes'] = $this->entityAttributes;
+
+ // check for entity categories
+ if (SimpleSAML\Utils\Config\Metadata::isHiddenFromDiscovery($metadata)) {
+ $metadata['hide.from.discovery'] = true;
+ }
+ }
+
+ if (!empty($roleDescriptor['UIInfo'])) {
+ $metadata['UIInfo'] = $roleDescriptor['UIInfo'];
+ }
+
+ if (!empty($roleDescriptor['DiscoHints'])) {
+ $metadata['DiscoHints'] = $roleDescriptor['DiscoHints'];
+ }
+ }
+
+
+ /**
+ * This function returns the metadata for SAML 1.x SPs in the format SimpleSAMLphp expects.
+ * This is an associative array with the following fields:
+ * - 'entityid': The entity id of the entity described in the metadata.
+ * - 'AssertionConsumerService': String with the URL of the assertion consumer service which supports
+ * the browser-post binding.
+ * - 'certData': X509Certificate for entity (if present).
+ *
+ * Metadata must be loaded with one of the parse functions before this function can be called.
+ *
+ * @return array An associative array with metadata or NULL if we are unable to generate metadata for a SAML 1.x SP.
+ */
+ public function getMetadata1xSP()
+ {
+ $ret = $this->getMetadataCommon();
+ $ret['metadata-set'] = 'shib13-sp-remote';
+
+
+ // find SP information which supports one of the SAML 1.x protocols
+ $spd = $this->getSPDescriptors(self::$SAML1xProtocols);
+ if (count($spd) === 0) {
+ return null;
+ }
+
+ // we currently only look at the first SPDescriptor which supports SAML 1.x
+ $spd = $spd[0];
+
+ // add expire time to metadata
+ if (array_key_exists('expire', $spd)) {
+ $ret['expire'] = $spd['expire'];
+ }
+
+ // find the assertion consumer service endpoints
+ $ret['AssertionConsumerService'] = $spd['AssertionConsumerService'];
+
+ // add the list of attributes the SP should receive
+ if (array_key_exists('attributes', $spd)) {
+ $ret['attributes'] = $spd['attributes'];
+ }
+ if (array_key_exists('attributes.required', $spd)) {
+ $ret['attributes.required'] = $spd['attributes.required'];
+ }
+ if (array_key_exists('attributes.NameFormat', $spd)) {
+ $ret['attributes.NameFormat'] = $spd['attributes.NameFormat'];
+ }
+
+ // add name & description
+ if (array_key_exists('name', $spd)) {
+ $ret['name'] = $spd['name'];
+ }
+ if (array_key_exists('description', $spd)) {
+ $ret['description'] = $spd['description'];
+ }
+
+ // add public keys
+ if (!empty($spd['keys'])) {
+ $ret['keys'] = $spd['keys'];
+ }
+
+ // add extensions
+ $this->addExtensions($ret, $spd);
+
+ // prioritize mdui:DisplayName as the name if available
+ if (!empty($ret['UIInfo']['DisplayName'])) {
+ $ret['name'] = $ret['UIInfo']['DisplayName'];
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * This function returns the metadata for SAML 1.x IdPs in the format simpleSAMLphp expects.
+ * This is an associative array with the following fields:
+ * - 'entityid': The entity id of the entity described in the metadata.
+ * - 'name': Auto generated name for this entity. Currently set to the entity id.
+ * - 'SingleSignOnService': String with the URL of the SSO service which supports the redirect binding.
+ * - 'SingleLogoutService': String with the URL where we should send logout requests/responses.
+ * - 'certData': X509Certificate for entity (if present).
+ * - 'certFingerprint': Fingerprint of the X509Certificate from the metadata.
+ *
+ * Metadata must be loaded with one of the parse functions before this function can be called.
+ *
+ * @return array An associative array with metadata or NULL if we are unable to generate metadata for a SAML 1.x
+ * IdP.
+ */
+ public function getMetadata1xIdP()
+ {
+ $ret = $this->getMetadataCommon();
+ $ret['metadata-set'] = 'shib13-idp-remote';
+
+ // find IdP information which supports the SAML 1.x protocol
+ $idp = $this->getIdPDescriptors(self::$SAML1xProtocols);
+ if (count($idp) === 0) {
+ return null;
+ }
+
+ // we currently only look at the first IDP descriptor which supports SAML 1.x
+ $idp = $idp[0];
+
+ // fdd expire time to metadata
+ if (array_key_exists('expire', $idp)) {
+ $ret['expire'] = $idp['expire'];
+ }
+
+ // find the SSO service endpoints
+ $ret['SingleSignOnService'] = $idp['SingleSignOnService'];
+
+ // find the ArtifactResolutionService endpoint
+ $ret['ArtifactResolutionService'] = $idp['ArtifactResolutionService'];
+
+ // add public keys
+ if (!empty($idp['keys'])) {
+ $ret['keys'] = $idp['keys'];
+ }
+
+ // add extensions
+ $this->addExtensions($ret, $idp);
+
+ // prioritize mdui:DisplayName as the name if available
+ if (!empty($ret['UIInfo']['DisplayName'])) {
+ $ret['name'] = $ret['UIInfo']['DisplayName'];
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * This function returns the metadata for SAML 2.0 SPs in the format simpleSAMLphp expects.
+ * This is an associative array with the following fields:
+ * - 'entityid': The entity id of the entity described in the metadata.
+ * - 'AssertionConsumerService': String with the URL of the assertion consumer service which supports
+ * the browser-post binding.
+ * - 'SingleLogoutService': String with the URL where we should send logout requests/responses.
+ * - 'NameIDFormat': The name ID format this SP expects. This may be unset.
+ * - 'certData': X509Certificate for entity (if present).
+ *
+ * Metadata must be loaded with one of the parse functions before this function can be called.
+ *
+ * @return array An associative array with metadata or NULL if we are unable to generate metadata for a SAML 2.x SP.
+ */
+ public function getMetadata20SP()
+ {
+ $ret = $this->getMetadataCommon();
+ $ret['metadata-set'] = 'saml20-sp-remote';
+
+ // find SP information which supports the SAML 2.0 protocol
+ $spd = $this->getSPDescriptors(self::$SAML20Protocols);
+ if (count($spd) === 0) {
+ return null;
+ }
+
+ // we currently only look at the first SPDescriptor which supports SAML 2.0
+ $spd = $spd[0];
+
+ // add expire time to metadata
+ if (array_key_exists('expire', $spd)) {
+ $ret['expire'] = $spd['expire'];
+ }
+
+ // find the assertion consumer service endpoints
+ $ret['AssertionConsumerService'] = $spd['AssertionConsumerService'];
+
+
+ // find the single logout service endpoint
+ $ret['SingleLogoutService'] = $spd['SingleLogoutService'];
+
+
+ // find the NameIDFormat. This may not exist
+ if (count($spd['nameIDFormats']) > 0) {
+ // SimpleSAMLphp currently only supports a single NameIDFormat pr. SP. We use the first one
+ $ret['NameIDFormat'] = $spd['nameIDFormats'][0];
+ }
+
+ // add the list of attributes the SP should receive
+ if (array_key_exists('attributes', $spd)) {
+ $ret['attributes'] = $spd['attributes'];
+ }
+ if (array_key_exists('attributes.required', $spd)) {
+ $ret['attributes.required'] = $spd['attributes.required'];
+ }
+ if (array_key_exists('attributes.NameFormat', $spd)) {
+ $ret['attributes.NameFormat'] = $spd['attributes.NameFormat'];
+ }
+
+ // add name & description
+ if (array_key_exists('name', $spd)) {
+ $ret['name'] = $spd['name'];
+ }
+ if (array_key_exists('description', $spd)) {
+ $ret['description'] = $spd['description'];
+ }
+
+ // add public keys
+ if (!empty($spd['keys'])) {
+ $ret['keys'] = $spd['keys'];
+ }
+
+ // add validate.authnrequest
+ if (array_key_exists('AuthnRequestsSigned', $spd)) {
+ $ret['validate.authnrequest'] = $spd['AuthnRequestsSigned'];
+ }
+
+ // add saml20.sign.assertion
+ if (array_key_exists('WantAssertionsSigned', $spd)) {
+ $ret['saml20.sign.assertion'] = $spd['WantAssertionsSigned'];
+ }
+
+ // add extensions
+ $this->addExtensions($ret, $spd);
+
+ // prioritize mdui:DisplayName as the name if available
+ if (!empty($ret['UIInfo']['DisplayName'])) {
+ $ret['name'] = $ret['UIInfo']['DisplayName'];
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * This function returns the metadata for SAML 2.0 IdPs in the format simpleSAMLphp expects.
+ * This is an associative array with the following fields:
+ * - 'entityid': The entity id of the entity described in the metadata.
+ * - 'name': Auto generated name for this entity. Currently set to the entity id.
+ * - 'SingleSignOnService': String with the URL of the SSO service which supports the redirect binding.
+ * - 'SingleLogoutService': String with the URL where we should send logout requests(/responses).
+ * - 'SingleLogoutServiceResponse': String where we should send logout responses (if this is different from
+ * the 'SingleLogoutService' endpoint.
+ * - 'NameIDFormats': The name ID formats this IdP supports.
+ * - 'certData': X509Certificate for entity (if present).
+ * - 'certFingerprint': Fingerprint of the X509Certificate from the metadata.
+ *
+ * Metadata must be loaded with one of the parse functions before this function can be called.
+ *
+ * @return array An associative array with metadata or NULL if we are unable to generate metadata for a SAML 2.0
+ * IdP.
+ */
+ public function getMetadata20IdP()
+ {
+ $ret = $this->getMetadataCommon();
+ $ret['metadata-set'] = 'saml20-idp-remote';
+
+ // find IdP information which supports the SAML 2.0 protocol
+ $idp = $this->getIdPDescriptors(self::$SAML20Protocols);
+ if (count($idp) === 0) {
+ return null;
+ }
+
+ // we currently only look at the first IDP descriptor which supports SAML 2.0
+ $idp = $idp[0];
+
+ // add expire time to metadata
+ if (array_key_exists('expire', $idp)) {
+ $ret['expire'] = $idp['expire'];
+ }
+
+ // enable redirect.sign if WantAuthnRequestsSigned is enabled
+ if ($idp['WantAuthnRequestsSigned']) {
+ $ret['sign.authnrequest'] = true;
+ }
+
+ // find the SSO service endpoint
+ $ret['SingleSignOnService'] = $idp['SingleSignOnService'];
+
+ // find the single logout service endpoint
+ $ret['SingleLogoutService'] = $idp['SingleLogoutService'];
+
+ // find the ArtifactResolutionService endpoint
+ $ret['ArtifactResolutionService'] = $idp['ArtifactResolutionService'];
+
+ // add supported nameIDFormats
+ $ret['NameIDFormats'] = $idp['nameIDFormats'];
+
+ // add public keys
+ if (!empty($idp['keys'])) {
+ $ret['keys'] = $idp['keys'];
+ }
+
+ // add extensions
+ $this->addExtensions($ret, $idp);
+
+ // prioritize mdui:DisplayName as the name if available
+ if (!empty($ret['UIInfo']['DisplayName'])) {
+ $ret['name'] = $ret['UIInfo']['DisplayName'];
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * Retrieve AttributeAuthorities from the metadata.
+ *
+ * @return array Array of AttributeAuthorityDescriptor entries.
+ */
+ public function getAttributeAuthorities()
+ {
+ return $this->attributeAuthorityDescriptors;
+ }
+
+
+ /**
+ * Parse a RoleDescriptorType element.
+ *
+ * The returned associative array has the following elements:
+ * - 'protocols': Array with the protocols supported.
+ * - 'expire': Timestamp for when this descriptor expires.
+ * - 'keys': Array of associative arrays with the elements from parseKeyDescriptor.
+ *
+ * @param SAML2_XML_md_RoleDescriptor $element The element we should extract metadata from.
+ * @param int|NULL $expireTime The unix timestamp for when this element should expire, or
+ * NULL if unknown.
+ *
+ * @return array An associative array with metadata we have extracted from this element.
+ */
+ private static function parseRoleDescriptorType(SAML2_XML_md_RoleDescriptor $element, $expireTime)
+ {
+ assert('is_null($expireTime) || is_int($expireTime)');
+
+ $ret = array();
+
+ $expireTime = self::getExpireTime($element, $expireTime);
+
+ if ($expireTime !== null) {
+ // we got an expired timestamp, either from this element or one of the parent elements
+ $ret['expire'] = $expireTime;
+ }
+
+ $ret['protocols'] = $element->protocolSupportEnumeration;
+
+ // process KeyDescriptor elements
+ $ret['keys'] = array();
+ foreach ($element->KeyDescriptor as $kd) {
+ $key = self::parseKeyDescriptor($kd);
+ if ($key !== null) {
+ $ret['keys'][] = $key;
+ }
+ }
+
+ $ext = self::processExtensions($element);
+ $ret['scope'] = $ext['scope'];
+ $ret['tags'] = $ext['tags'];
+ $ret['EntityAttributes'] = $ext['EntityAttributes'];
+ $ret['UIInfo'] = $ext['UIInfo'];
+ $ret['DiscoHints'] = $ext['DiscoHints'];
+
+ return $ret;
+ }
+
+
+ /**
+ * This function extracts metadata from a SSODescriptor element.
+ *
+ * The returned associative array has the following elements:
+ * - 'protocols': Array with the protocols this SSODescriptor supports.
+ * - 'SingleLogoutService': Array with the single logout service endpoints. Each endpoint is stored
+ * as an associative array with the elements that parseGenericEndpoint returns.
+ * - 'nameIDFormats': The NameIDFormats supported by this SSODescriptor. This may be an empty array.
+ * - 'keys': Array of associative arrays with the elements from parseKeyDescriptor:
+ *
+ * @param SAML2_XML_md_SSODescriptorType $element The element we should extract metadata from.
+ * @param int|NULL $expireTime The unix timestamp for when this element should expire, or
+ * NULL if unknown.
+ *
+ * @return array An associative array with metadata we have extracted from this element.
+ */
+ private static function parseSSODescriptor(SAML2_XML_md_SSODescriptorType $element, $expireTime)
+ {
+ assert('is_null($expireTime) || is_int($expireTime)');
+
+ $sd = self::parseRoleDescriptorType($element, $expireTime);
+
+ // find all SingleLogoutService elements
+ $sd['SingleLogoutService'] = self::extractEndpoints($element->SingleLogoutService);
+
+ // find all ArtifactResolutionService elements
+ $sd['ArtifactResolutionService'] = self::extractEndpoints($element->ArtifactResolutionService);
+
+
+ // process NameIDFormat elements
+ $sd['nameIDFormats'] = $element->NameIDFormat;
+
+ return $sd;
+ }
+
+
+ /**
+ * This function extracts metadata from a SPSSODescriptor element.
+ *
+ * @param SAML2_XML_md_SPSSODescriptor $element The element which should be parsed.
+ * @param int|NULL $expireTime The unix timestamp for when this element should expire, or
+ * NULL if unknown.
+ */
+ private function processSPSSODescriptor(SAML2_XML_md_SPSSODescriptor $element, $expireTime)
+ {
+ assert('is_null($expireTime) || is_int($expireTime)');
+
+ $sp = self::parseSSODescriptor($element, $expireTime);
+
+ // find all AssertionConsumerService elements
+ $sp['AssertionConsumerService'] = self::extractEndpoints($element->AssertionConsumerService);
+
+ // find all the attributes and SP name...
+ $attcs = $element->AttributeConsumingService;
+ if (count($attcs) > 0) {
+ self::parseAttributeConsumerService($attcs[0], $sp);
+ }
+
+ // check AuthnRequestsSigned
+ if ($element->AuthnRequestsSigned !== null) {
+ $sp['AuthnRequestsSigned'] = $element->AuthnRequestsSigned;
+ }
+
+ // check WantAssertionsSigned
+ if ($element->WantAssertionsSigned !== null) {
+ $sp['WantAssertionsSigned'] = $element->WantAssertionsSigned;
+ }
+
+ $this->spDescriptors[] = $sp;
+ }
+
+
+ /**
+ * This function extracts metadata from a IDPSSODescriptor element.
+ *
+ * @param SAML2_XML_md_IDPSSODescriptor $element The element which should be parsed.
+ * @param int|NULL $expireTime The unix timestamp for when this element should expire, or
+ * NULL if unknown.
+ */
+ private function processIDPSSODescriptor(SAML2_XML_md_IDPSSODescriptor $element, $expireTime)
+ {
+ assert('is_null($expireTime) || is_int($expireTime)');
+
+ $idp = self::parseSSODescriptor($element, $expireTime);
+
+ // find all SingleSignOnService elements
+ $idp['SingleSignOnService'] = self::extractEndpoints($element->SingleSignOnService);
+
+ if ($element->WantAuthnRequestsSigned) {
+ $idp['WantAuthnRequestsSigned'] = true;
+ } else {
+ $idp['WantAuthnRequestsSigned'] = false;
+ }
+
+ $this->idpDescriptors[] = $idp;
+ }
+
+
+ /**
+ * This function extracts metadata from a AttributeAuthorityDescriptor element.
+ *
+ * @param SAML2_XML_md_AttributeAuthorityDescriptor $element The element which should be parsed.
+ * @param int|NULL $expireTime The unix timestamp for when this element should
+ * expire, or NULL if unknown.
+ */
+ private function processAttributeAuthorityDescriptor(
+ SAML2_XML_md_AttributeAuthorityDescriptor $element,
+ $expireTime
+ ) {
+ assert('is_null($expireTime) || is_int($expireTime)');
+
+ $aad = self::parseRoleDescriptorType($element, $expireTime);
+ $aad['entityid'] = $this->entityId;
+ $aad['metadata-set'] = 'attributeauthority-remote';
+
+ $aad['AttributeService'] = self::extractEndpoints($element->AttributeService);
+ $aad['AssertionIDRequestService'] = self::extractEndpoints($element->AssertionIDRequestService);
+ $aad['NameIDFormat'] = $element->NameIDFormat;
+
+ $this->attributeAuthorityDescriptors[] = $aad;
+ }
+
+
+ /**
+ * Parse an Extensions element.
+ *
+ * @param mixed $element The element which contains the Extensions element.
+ *
+ * @return array An associative array with the extensions parsed.
+ */
+ private static function processExtensions($element)
+ {
+ $ret = array(
+ 'scope' => array(),
+ 'tags' => array(),
+ 'EntityAttributes' => array(),
+ 'UIInfo' => array(),
+ 'DiscoHints' => array(),
+ );
+
+ foreach ($element->Extensions as $e) {
+
+ if ($e instanceof SAML2_XML_shibmd_Scope) {
+ $ret['scope'][] = $e->scope;
+ continue;
+ }
+
+ // Entity Attributes are only allowed at entity level extensions and not at RoleDescriptor level
+ if ($element instanceof SAML2_XML_md_EntityDescriptor) {
+ if ($e instanceof SAML2_XML_mdattr_EntityAttributes && !empty($e->children)) {
+ foreach ($e->children as $attr) {
+ // only saml:Attribute are currently supported here. The specifications also allows
+ // saml:Assertions, which more complex processing.
+ if ($attr instanceof SAML2_XML_saml_Attribute) {
+ if (empty($attr->Name) || empty($attr->AttributeValue)) {
+ continue;
+ }
+
+ // attribute names that is not URI is prefixed as this: '{nameformat}name'
+ $name = $attr->Name;
+ if (empty($attr->NameFormat)) {
+ $name = '{'.SAML2_Const::NAMEFORMAT_UNSPECIFIED.'}'.$attr->Name;
+ } elseif ($attr->NameFormat !== 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri') {
+ $name = '{'.$attr->NameFormat.'}'.$attr->Name;
+ }
+
+ $values = array();
+ foreach ($attr->AttributeValue as $attrvalue) {
+ $values[] = $attrvalue->getString();
+ }
+
+ $ret['EntityAttributes'][$name] = $values;
+ }
+ }
+ }
+ }
+
+ // UIInfo elements are only allowed at RoleDescriptor level extensions
+ if ($element instanceof SAML2_XML_md_RoleDescriptor) {
+ if ($e instanceof SAML2_XML_mdui_UIInfo) {
+
+ $ret['UIInfo']['DisplayName'] = $e->DisplayName;
+ $ret['UIInfo']['Description'] = $e->Description;
+ $ret['UIInfo']['InformationURL'] = $e->InformationURL;
+ $ret['UIInfo']['PrivacyStatementURL'] = $e->PrivacyStatementURL;
+
+ foreach ($e->Keywords as $uiItem) {
+ if (!($uiItem instanceof SAML2_XML_mdui_Keywords)
+ || empty($uiItem->Keywords)
+ || empty($uiItem->lang)
+ ) {
+ continue;
+ }
+ $ret['UIInfo']['Keywords'][$uiItem->lang] = $uiItem->Keywords;
+ }
+ foreach ($e->Logo as $uiItem) {
+ if (!($uiItem instanceof SAML2_XML_mdui_Logo)
+ || empty($uiItem->url)
+ || empty($uiItem->height)
+ || empty($uiItem->width)
+ ) {
+ continue;
+ }
+ $logo = array(
+ 'url' => $uiItem->url,
+ 'height' => $uiItem->height,
+ 'width' => $uiItem->width,
+ );
+ if (!empty($uiItem->lang)) {
+ $logo['lang'] = $uiItem->lang;
+ }
+ $ret['UIInfo']['Logo'][] = $logo;
+ }
+ }
+ }
+
+ // DiscoHints elements are only allowed at IDPSSODescriptor level extensions
+ if ($element instanceof SAML2_XML_md_IDPSSODescriptor) {
+
+ if ($e instanceof SAML2_XML_mdui_DiscoHints) {
+ $ret['DiscoHints']['IPHint'] = $e->IPHint;
+ $ret['DiscoHints']['DomainHint'] = $e->DomainHint;
+ $ret['DiscoHints']['GeolocationHint'] = $e->GeolocationHint;
+ }
+ }
+
+ if (!($e instanceof SAML2_XML_Chunk)) {
+ continue;
+ }
+
+ if ($e->localName === 'Attribute' && $e->namespaceURI === SAML2_Const::NS_SAML) {
+ $attribute = $e->getXML();
+
+ $name = $attribute->getAttribute('Name');
+ $values = array_map(
+ array('SimpleSAML\Utils\XML', 'getDOMText'),
SimpleSAML\Utils\XML::getDOMChildren($attribute, 'AttributeValue', '@saml2')
- );
-
- if ($name === 'tags') {
- foreach ($values as $tagname) {
- if (!empty($tagname)) {
- $ret['tags'][] = $tagname;
- }
- }
- }
- }
- }
- return $ret;
- }
-
-
- /**
- * Parse and process a Organization element.
- *
- * @param SAML2_XML_md_Organization $element The Organization element.
- */
- private function processOrganization(SAML2_XML_md_Organization $element) {
-
- $this->organizationName = $element->OrganizationName;
- $this->organizationDisplayName = $element->OrganizationDisplayName;
- $this->organizationURL = $element->OrganizationURL;
- }
-
- /**
- * Parse and process a ContactPerson element.
- *
- * @param SAML2_XML_md_ContactPerson $element The ContactPerson element.
- */
-
- private function processContactPerson(SAML2_XML_md_ContactPerson $element) {
-
- $contactPerson = array();
- if(!empty($element->contactType)) {
- $contactPerson['contactType'] = $element->contactType;
- }
- if(!empty($element->Company)) {
- $contactPerson['company'] = $element->Company;
- }
- if(!empty($element->GivenName)) {
- $contactPerson['givenName'] = $element->GivenName;
- }
- if(!empty($element->SurName)) {
- $contactPerson['surName'] = $element->SurName;
- }
- if(!empty($element->EmailAddress)) {
- $contactPerson['emailAddress'] = $element->EmailAddress;
- }
- if(!empty($element->TelephoneNumber)) {
- $contactPerson['telephoneNumber'] = $element->TelephoneNumber;
- }
- if(!empty($contactPerson)) {
- $this->contacts[] = $contactPerson;
- }
- }
-
-
- /**
- * This function parses AttributeConsumerService elements.
- */
- private static function parseAttributeConsumerService(SAML2_XML_md_AttributeConsumingService $element, &$sp) {
- assert('is_array($sp)');
-
- $sp['name'] = $element->ServiceName;
- $sp['description'] = $element->ServiceDescription;
-
- $format = NULL;
- $sp['attributes'] = array();
- $sp['attributes.required'] = array();
- foreach ($element->RequestedAttribute AS $child) {
- $attrname = $child->Name;
- $sp['attributes'][] = $attrname;
-
- if ($child->isRequired) {
- $sp['attributes.required'][] = $attrname;
- }
-
- if ($child->isRequired !== NULL && $child->isRequired === TRUE) {
- $sp['attributes.required'][] = $attrname;
- }
-
- if ($child->NameFormat !== NULL) {
- $attrformat = $child->NameFormat;
- } else {
- $attrformat = SAML2_Const::NAMEFORMAT_UNSPECIFIED;
- }
-
- if ($format === NULL) {
- $format = $attrformat;
- } elseif ($format !== $attrformat) {
- $format = SAML2_Const::NAMEFORMAT_UNSPECIFIED;
- }
- }
-
- if (empty($sp['attributes'])) {
- /*
- * Really an invalid configuration - all AttributeConsumingServices
- * should have one or more attributes.
- */
- unset($sp['attributes']);
- }
- if (empty($sp['attributes.required'])) {
- unset($sp['attributes.required']);
- }
-
- if ($format !== SAML2_Const::NAMEFORMAT_UNSPECIFIED && $format !== NULL) {
- $sp['attributes.NameFormat'] = $format;
- }
- }
-
-
- /**
- * This function is a generic endpoint element parser.
- *
- * The returned associative array has the following elements:
- * - 'Binding': The binding this endpoint uses.
- * - 'Location': The URL to this endpoint.
- * - 'ResponseLocation': The URL where responses should be sent. This may not exist.
- * - 'index': The index of this endpoint. This attribute is only for indexed endpoints.
- * - 'isDefault': Whether this endpoint is the default endpoint for this type. This attribute may not exist.
- *
- * @param $element The element which should be parsed.
- * @return Associative array with the data we have extracted from the element.
- */
- private static function parseGenericEndpoint(SAML2_XML_md_EndpointType $element) {
-
- $ep = array();
-
- $ep['Binding'] = $element->Binding;
- $ep['Location'] = $element->Location;
-
- if ($element->ResponseLocation !== NULL) {
- $ep['ResponseLocation'] = $element->ResponseLocation;
- }
-
- if ($element instanceof SAML2_XML_md_IndexedEndpointType) {
- $ep['index'] = $element->index;
-
- if ($element->isDefault !== NULL) {
- $ep['isDefault'] = $element->isDefault;
- }
- }
-
- return $ep;
- }
-
-
- /**
- * Extract generic endpoints.
- *
- * @param array $endpoints The endpoints we should parse.
- * @return array Array of parsed endpoints.
- */
- private static function extractEndpoints(array $endpoints) {
-
- $ret = array();
- foreach ($endpoints as $ep) {
- $ret[] = self::parseGenericEndpoint($ep);
- }
-
- return $ret;
- }
-
-
- /**
- * This function parses a KeyDescriptor element. It currently only supports keys with a single
- * X509 certificate.
- *
- * The associative array for a key can contain:
- * - 'encryption': Indicates wheter this key can be used for encryption.
- * - 'signing': Indicates wheter this key can be used for signing.
- * - 'type: The type of the key. 'X509Certificate' is the only key type we support.
- * - 'X509Certificate': The contents of the first X509Certificate element (if the type is 'X509Certificate ').
- *
- * @param SAML2_XML_md_KeyDescriptor $kd The KeyDescriptor element.
- * @return Associative array describing the key, or NULL if this is an unsupported key.
- */
- private static function parseKeyDescriptor(SAML2_XML_md_KeyDescriptor $kd) {
-
- $r = array();
-
- if ($kd->use === 'encryption') {
- $r['encryption'] = TRUE;
- $r['signing'] = FALSE;
- } elseif($kd->use === 'signing') {
- $r['encryption'] = FALSE;
- $r['signing'] = TRUE;
- } else {
- $r['encryption'] = TRUE;
- $r['signing'] = TRUE;
- }
-
- $keyInfo = $kd->KeyInfo;
-
- foreach ($keyInfo->info as $i) {
- if ($i instanceof SAML2_XML_ds_X509Data) {
- foreach ($i->data as $d) {
- if ($d instanceof SAML2_XML_ds_X509Certificate) {
- $r['type'] = 'X509Certificate';
- $r['X509Certificate'] = $d->certificate;
- return $r;
- }
- }
- }
- }
-
- return NULL;
- }
-
-
- /**
- * This function finds SP descriptors which supports one of the given protocols.
- *
- * @param $protocols Array with the protocols we accept.
- * @return Array with SP descriptors which supports one of the given protocols.
- */
- private function getSPDescriptors($protocols) {
- assert('is_array($protocols)');
-
- $ret = array();
-
- foreach($this->spDescriptors as $spd) {
- $sharedProtocols = array_intersect($protocols, $spd['protocols']);
- if(count($sharedProtocols) > 0) {
- $ret[] = $spd;
- }
- }
-
- return $ret;
- }
-
-
- /**
- * This function finds IdP descriptors which supports one of the given protocols.
- *
- * @param $protocols Array with the protocols we accept.
- * @return Array with IdP descriptors which supports one of the given protocols.
- */
- private function getIdPDescriptors($protocols) {
- assert('is_array($protocols)');
-
- $ret = array();
-
- foreach($this->idpDescriptors as $idpd) {
- $sharedProtocols = array_intersect($protocols, $idpd['protocols']);
- if(count($sharedProtocols) > 0) {
- $ret[] = $idpd;
- }
- }
-
- return $ret;
- }
-
-
- /**
- * This function locates the EntityDescriptor node in a DOMDocument. This node should
- * be the first (and only) node in the document.
- *
- * This function will throw an exception if it is unable to locate the node.
- *
- * @param $doc The DOMDocument where we should find the EntityDescriptor node.
- * @return The DOMEntity which represents the EntityDescriptor.
- */
- private static function findEntityDescriptor($doc) {
-
- assert('$doc instanceof DOMDocument');
-
- /* Find the EntityDescriptor DOMElement. This should be the first (and only) child of the
- * DOMDocument.
- */
- $ed = $doc->documentElement;
-
- if($ed === NULL) {
- throw new Exception('Failed to load SAML metadata from empty XML document.');
- }
-
- if (SimpleSAML\Utils\XML::isDOMElementOfType($ed, 'EntityDescriptor', '@md') === FALSE) {
- throw new Exception('Expected first element in the metadata document to be an EntityDescriptor element.');
- }
-
- return new SAML2_XML_md_EntityDescriptor($ed);
- }
-
-
- /**
- * If this EntityDescriptor was signed this function use the public key to check the signature.
- *
- * @param $certificates One ore more certificates with the public key. This makes it possible
- * to do a key rollover.
- * @return TRUE if it is possible to check the signature with the certificate, FALSE otherwise.
- */
- public function validateSignature($certificates) {
- foreach ($certificates as $cert) {
- assert('is_string($cert)');
- $certFile = \SimpleSAML\Utils\Config::getCertPath($cert);
- if (!file_exists($certFile)) {
- throw new Exception('Could not find certificate file [' . $certFile . '], which is needed to validate signature');
- }
- $certData = file_get_contents($certFile);
-
- foreach ($this->validators as $validator) {
- $key = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type'=>'public'));
- $key->loadKey($certData);
- try {
- if ($validator->validate($key)) {
- return TRUE;
- }
- } catch (Exception $e) {
- /* This certificate does not sign this element. */
- }
- }
- }
- SimpleSAML_Logger::debug('Could not validate signature');
- return FALSE;
- }
-
-
- /**
- * This function checks if this EntityDescriptor was signed with a certificate with the
- * given fingerprint.
- *
- * @param $fingerprint Fingerprint of the certificate which should have been used to sign this
- * EntityDescriptor.
- * @return TRUE if it was signed with the certificate with the given fingerprint, FALSE otherwise.
- */
- public function validateFingerprint($fingerprint) {
- assert('is_string($fingerprint)');
-
- $fingerprint = strtolower(str_replace(":", "", $fingerprint));
-
- $candidates = array();
- foreach ($this->validators as $validator) {
- foreach ($validator->getValidatingCertificates() as $cert) {
-
- $fp = strtolower(sha1(base64_decode($cert)));
- $candidates[] = $fp;
- if ($fp === $fingerprint) {
- return TRUE;
- }
- }
-
-
- }
- SimpleSAML_Logger::debug('Fingerprint was [' . $fingerprint . '] not one of [' . join(', ', $candidates). ']');
- return FALSE;
- }
-
+ );
+
+ if ($name === 'tags') {
+ foreach ($values as $tagname) {
+ if (!empty($tagname)) {
+ $ret['tags'][] = $tagname;
+ }
+ }
+ }
+ }
+ }
+ return $ret;
+ }
+
+
+ /**
+ * Parse and process a Organization element.
+ *
+ * @param SAML2_XML_md_Organization $element The Organization element.
+ */
+ private function processOrganization(SAML2_XML_md_Organization $element)
+ {
+ $this->organizationName = $element->OrganizationName;
+ $this->organizationDisplayName = $element->OrganizationDisplayName;
+ $this->organizationURL = $element->OrganizationURL;
+ }
+
+
+ /**
+ * Parse and process a ContactPerson element.
+ *
+ * @param SAML2_XML_md_ContactPerson $element The ContactPerson element.
+ */
+
+ private function processContactPerson(SAML2_XML_md_ContactPerson $element)
+ {
+ $contactPerson = array();
+ if (!empty($element->contactType)) {
+ $contactPerson['contactType'] = $element->contactType;
+ }
+ if (!empty($element->Company)) {
+ $contactPerson['company'] = $element->Company;
+ }
+ if (!empty($element->GivenName)) {
+ $contactPerson['givenName'] = $element->GivenName;
+ }
+ if (!empty($element->SurName)) {
+ $contactPerson['surName'] = $element->SurName;
+ }
+ if (!empty($element->EmailAddress)) {
+ $contactPerson['emailAddress'] = $element->EmailAddress;
+ }
+ if (!empty($element->TelephoneNumber)) {
+ $contactPerson['telephoneNumber'] = $element->TelephoneNumber;
+ }
+ if (!empty($contactPerson)) {
+ $this->contacts[] = $contactPerson;
+ }
+ }
+
+
+ /**
+ * This function parses AttributeConsumerService elements.
+ *
+ * @param SAML2_XML_md_AttributeConsumingService $element The AttributeConsumingService to parse.
+ * @param array $sp The array with the SP's metadata.
+ */
+ private static function parseAttributeConsumerService(SAML2_XML_md_AttributeConsumingService $element, &$sp)
+ {
+ assert('is_array($sp)');
+
+ $sp['name'] = $element->ServiceName;
+ $sp['description'] = $element->ServiceDescription;
+
+ $format = null;
+ $sp['attributes'] = array();
+ $sp['attributes.required'] = array();
+ foreach ($element->RequestedAttribute as $child) {
+ $attrname = $child->Name;
+ $sp['attributes'][] = $attrname;
+
+ if ($child->isRequired) {
+ $sp['attributes.required'][] = $attrname;
+ }
+
+ if ($child->isRequired !== null && $child->isRequired === true) {
+ $sp['attributes.required'][] = $attrname;
+ }
+
+ if ($child->NameFormat !== null) {
+ $attrformat = $child->NameFormat;
+ } else {
+ $attrformat = SAML2_Const::NAMEFORMAT_UNSPECIFIED;
+ }
+
+ if ($format === null) {
+ $format = $attrformat;
+ } elseif ($format !== $attrformat) {
+ $format = SAML2_Const::NAMEFORMAT_UNSPECIFIED;
+ }
+ }
+
+ if (empty($sp['attributes'])) {
+ // a really invalid configuration: all AttributeConsumingServices should have one or more attributes
+ unset($sp['attributes']);
+ }
+ if (empty($sp['attributes.required'])) {
+ unset($sp['attributes.required']);
+ }
+
+ if ($format !== SAML2_Const::NAMEFORMAT_UNSPECIFIED && $format !== null) {
+ $sp['attributes.NameFormat'] = $format;
+ }
+ }
+
+
+ /**
+ * This function is a generic endpoint element parser.
+ *
+ * The returned associative array has the following elements:
+ * - 'Binding': The binding this endpoint uses.
+ * - 'Location': The URL to this endpoint.
+ * - 'ResponseLocation': The URL where responses should be sent. This may not exist.
+ * - 'index': The index of this endpoint. This attribute is only for indexed endpoints.
+ * - 'isDefault': Whether this endpoint is the default endpoint for this type. This attribute may not exist.
+ *
+ * @param SAML2_XML_md_EndpointType $element The element which should be parsed.
+ *
+ * @return array An associative array with the data we have extracted from the element.
+ */
+ private static function parseGenericEndpoint(SAML2_XML_md_EndpointType $element)
+ {
+ $ep = array();
+
+ $ep['Binding'] = $element->Binding;
+ $ep['Location'] = $element->Location;
+
+ if ($element->ResponseLocation !== null) {
+ $ep['ResponseLocation'] = $element->ResponseLocation;
+ }
+
+ if ($element instanceof SAML2_XML_md_IndexedEndpointType) {
+ $ep['index'] = $element->index;
+
+ if ($element->isDefault !== null) {
+ $ep['isDefault'] = $element->isDefault;
+ }
+ }
+
+ return $ep;
+ }
+
+
+ /**
+ * Extract generic endpoints.
+ *
+ * @param array $endpoints The endpoints we should parse.
+ *
+ * @return array Array of parsed endpoints.
+ */
+ private static function extractEndpoints(array $endpoints)
+ {
+ $ret = array();
+ foreach ($endpoints as $ep) {
+ $ret[] = self::parseGenericEndpoint($ep);
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * This function parses a KeyDescriptor element. It currently only supports keys with a single
+ * X509 certificate.
+ *
+ * The associative array for a key can contain:
+ * - 'encryption': Indicates whether this key can be used for encryption.
+ * - 'signing': Indicates whether this key can be used for signing.
+ * - 'type: The type of the key. 'X509Certificate' is the only key type we support.
+ * - 'X509Certificate': The contents of the first X509Certificate element (if the type is 'X509Certificate ').
+ *
+ * @param SAML2_XML_md_KeyDescriptor $kd The KeyDescriptor element.
+ *
+ * @return array|null An associative array describing the key, or null if this is an unsupported key.
+ */
+ private static function parseKeyDescriptor(SAML2_XML_md_KeyDescriptor $kd)
+ {
+ $r = array();
+
+ if ($kd->use === 'encryption') {
+ $r['encryption'] = true;
+ $r['signing'] = false;
+ } elseif ($kd->use === 'signing') {
+ $r['encryption'] = false;
+ $r['signing'] = true;
+ } else {
+ $r['encryption'] = true;
+ $r['signing'] = true;
+ }
+
+ $keyInfo = $kd->KeyInfo;
+
+ foreach ($keyInfo->info as $i) {
+ if ($i instanceof SAML2_XML_ds_X509Data) {
+ foreach ($i->data as $d) {
+ if ($d instanceof SAML2_XML_ds_X509Certificate) {
+ $r['type'] = 'X509Certificate';
+ $r['X509Certificate'] = $d->certificate;
+ return $r;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+
+ /**
+ * This function finds SP descriptors which supports one of the given protocols.
+ *
+ * @param $protocols Array with the protocols we accept.
+ *
+ * @return Array with SP descriptors which supports one of the given protocols.
+ */
+ private function getSPDescriptors($protocols)
+ {
+ assert('is_array($protocols)');
+
+ $ret = array();
+
+ foreach ($this->spDescriptors as $spd) {
+ $sharedProtocols = array_intersect($protocols, $spd['protocols']);
+ if (count($sharedProtocols) > 0) {
+ $ret[] = $spd;
+ }
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * This function finds IdP descriptors which supports one of the given protocols.
+ *
+ * @param $protocols Array with the protocols we accept.
+ *
+ * @return Array with IdP descriptors which supports one of the given protocols.
+ */
+ private function getIdPDescriptors($protocols)
+ {
+ assert('is_array($protocols)');
+
+ $ret = array();
+
+ foreach ($this->idpDescriptors as $idpd) {
+ $sharedProtocols = array_intersect($protocols, $idpd['protocols']);
+ if (count($sharedProtocols) > 0) {
+ $ret[] = $idpd;
+ }
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * This function locates the EntityDescriptor node in a DOMDocument. This node should
+ * be the first (and only) node in the document.
+ *
+ * This function will throw an exception if it is unable to locate the node.
+ *
+ * @param DOMDocument $doc The DOMDocument where we should find the EntityDescriptor node.
+ *
+ * @return SAML2_XML_md_EntityDescriptor The DOMEntity which represents the EntityDescriptor.
+ * @throws Exception If the document is empty or the first element is not an EntityDescriptor element.
+ */
+ private static function findEntityDescriptor($doc)
+ {
+ assert('$doc instanceof DOMDocument');
+
+ // find the EntityDescriptor DOMElement. This should be the first (and only) child of the DOMDocument
+ $ed = $doc->documentElement;
+
+ if ($ed === null) {
+ throw new Exception('Failed to load SAML metadata from empty XML document.');
+ }
+
+ if (SimpleSAML\Utils\XML::isDOMElementOfType($ed, 'EntityDescriptor', '@md') === false) {
+ throw new Exception('Expected first element in the metadata document to be an EntityDescriptor element.');
+ }
+
+ return new SAML2_XML_md_EntityDescriptor($ed);
+ }
+
+
+ /**
+ * If this EntityDescriptor was signed this function use the public key to check the signature.
+ *
+ * @param array $certificates One ore more certificates with the public key. This makes it possible
+ * to do a key rollover.
+ *
+ * @return boolean True if it is possible to check the signature with the certificate, false otherwise.
+ * @throws Exception If the certificate file cannot be found.
+ */
+ public function validateSignature($certificates)
+ {
+ foreach ($certificates as $cert) {
+ assert('is_string($cert)');
+ $certFile = \SimpleSAML\Utils\Config::getCertPath($cert);
+ if (!file_exists($certFile)) {
+ throw new Exception(
+ 'Could not find certificate file ['.$certFile.'], which is needed to validate signature'
+ );
+ }
+ $certData = file_get_contents($certFile);
+
+ foreach ($this->validators as $validator) {
+ $key = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type' => 'public'));
+ $key->loadKey($certData);
+ try {
+ if ($validator->validate($key)) {
+ return true;
+ }
+ } catch (Exception $e) {
+ // this certificate did not sign this element, skip
+ }
+ }
+ }
+ SimpleSAML_Logger::debug('Could not validate signature');
+ return false;
+ }
+
+
+ /**
+ * This function checks if this EntityDescriptor was signed with a certificate with the
+ * given fingerprint.
+ *
+ * @param string $fingerprint Fingerprint of the certificate which should have been used to sign this
+ * EntityDescriptor.
+ *
+ * @return boolean True if it was signed with the certificate with the given fingerprint, false otherwise.
+ */
+ public function validateFingerprint($fingerprint)
+ {
+ assert('is_string($fingerprint)');
+
+ $fingerprint = strtolower(str_replace(":", "", $fingerprint));
+
+ $candidates = array();
+ foreach ($this->validators as $validator) {
+ foreach ($validator->getValidatingCertificates() as $cert) {
+
+ $fp = strtolower(sha1(base64_decode($cert)));
+ $candidates[] = $fp;
+ if ($fp === $fingerprint) {
+ return true;
+ }
+ }
+ }
+ SimpleSAML_Logger::debug('Fingerprint was ['.$fingerprint.'] not one of ['.join(', ', $candidates).']');
+ return false;
+ }
}
diff --git a/lib/SimpleSAML/Metadata/Signer.php b/lib/SimpleSAML/Metadata/Signer.php
index a53201b..225050f 100644
--- a/lib/SimpleSAML/Metadata/Signer.php
+++ b/lib/SimpleSAML/Metadata/Signer.php
@@ -1,199 +1,225 @@
<?php
+
/**
* This class implements a helper function for signing of metadata.
*
* @author Olav Morken, UNINETT AS.
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-class SimpleSAML_Metadata_Signer {
-
- /**
- * This functions finds what key & certificate files should be used to sign the metadata
- * for the given entity.
- *
- * @param $config Our SimpleSAML_Configuration instance.
- * @param $entityMetadata The metadata of the entity.
- * @param $type A string which describes the type entity this is, e.g. 'SAML 2 IdP' or 'Shib 1.3 SP'.
- * @return An associative array with the keys 'privatekey', 'certificate', and optionally 'privatekey_pass'.
- */
- private static function findKeyCert($config, $entityMetadata, $type) {
-
- /* First we look for metadata.privatekey and metadata.certificate in the metadata. */
- if(array_key_exists('metadata.sign.privatekey', $entityMetadata)
- || array_key_exists('metadata.sign.certificate', $entityMetadata)) {
-
- if(!array_key_exists('metadata.sign.privatekey', $entityMetadata)
- || !array_key_exists('metadata.sign.certificate', $entityMetadata)) {
-
- throw new Exception('Missing either the "metadata.sign.privatekey" or the' .
- ' "metadata.sign.certificate" configuration option in the metadata for' .
- ' the ' . $type . ' "' . $entityMetadata['entityid'] . '". If one of' .
- ' these options is specified, then the other must also be specified.');
- }
-
- $ret = array(
- 'privatekey' => $entityMetadata['metadata.sign.privatekey'],
- 'certificate' => $entityMetadata['metadata.sign.certificate']
- );
-
- if(array_key_exists('metadata.sign.privatekey_pass', $entityMetadata)) {
- $ret['privatekey_pass'] = $entityMetadata['metadata.sign.privatekey_pass'];
- }
-
- return $ret;
- }
-
- /* Then we look for default values in the global configuration. */
- $privatekey = $config->getString('metadata.sign.privatekey', NULL);
- $certificate = $config->getString('metadata.sign.certificate', NULL);
- if($privatekey !== NULL || $certificate !== NULL) {
- if($privatekey === NULL || $certificate === NULL) {
- throw new Exception('Missing either the "metadata.sign.privatekey" or the' .
- ' "metadata.sign.certificate" configuration option in the global' .
- ' configuration. If one of these options is specified, then the other'.
- ' must also be specified.');
- }
- $ret = array('privatekey' => $privatekey, 'certificate' => $certificate);
-
- $privatekey_pass = $config->getString('metadata.sign.privatekey_pass', NULL);
- if($privatekey_pass !== NULL) {
- $ret['privatekey_pass'] = $privatekey_pass;
- }
-
- return $ret;
- }
-
- /* As a last resort we attempt to use the privatekey and certificate option from the metadata. */
- if(array_key_exists('privatekey', $entityMetadata)
- || array_key_exists('certificate', $entityMetadata)) {
-
- if(!array_key_exists('privatekey', $entityMetadata)
- || !array_key_exists('certificate', $entityMetadata)) {
- throw new Exception('Both the "privatekey" and the "certificate" option must' .
- ' be set in the metadata for the ' . $type .' "' .
- $entityMetadata['entityid'] . '" before it is possible to sign metadata' .
- ' from this entity.');
- }
-
- $ret = array(
- 'privatekey' => $entityMetadata['privatekey'],
- 'certificate' => $entityMetadata['certificate']
- );
-
- if(array_key_exists('privatekey_pass', $entityMetadata)) {
- $ret['privatekey_pass'] = $entityMetadata['privatekey_pass'];
- }
-
- return $ret;
- }
-
- throw new Exception('Could not find what key & certificate should be used to sign the metadata' .
- ' for the ' . $type . ' "' . $entityMetadata['entityid'] . '".');
- }
-
-
- /**
- * Determine whether metadata signing is enabled for the given metadata.
- *
- * @param $config Our SimpleSAML_Configuration instance.
- * @param $entityMetadata The metadata of the entity.
- * @param $type A string which describes the type entity this is, e.g. 'SAML 2 IdP' or 'Shib 1.3 SP'.
- */
- private static function isMetadataSigningEnabled($config, $entityMetadata, $type) {
-
- /* First check the metadata for the entity. */
- if(array_key_exists('metadata.sign.enable', $entityMetadata)) {
- if(!is_bool($entityMetadata['metadata.sign.enable'])) {
- throw new Exception(
- 'Invalid value for the "metadata.sign.enable" configuration option for' .
- ' the ' . $type .' "' . $entityMetadata['entityid'] . '". This option' .
- ' should be a boolean.');
- }
-
- return $entityMetadata['metadata.sign.enable'];
- }
-
- $enabled = $config->getBoolean('metadata.sign.enable', FALSE);
-
- return $enabled;
- }
-
-
- /**
- * Signs the given metadata if metadata signing is enabled.
- *
- * @param $metadataString A string with the metadata.
- * @param $entityMetadata The metadata of the entity.
- * @param $type A string which describes the type entity this is, e.g. 'SAML 2 IdP' or 'Shib 1.3 SP'.
- * @return The $metadataString with the signature embedded.
- */
- public static function sign($metadataString, $entityMetadata, $type) {
-
- $config = SimpleSAML_Configuration::getInstance();
-
- /* Check if metadata signing is enabled. */
- if (!self::isMetadataSigningEnabled($config, $entityMetadata, $type)) {
- return $metadataString;
- }
-
-
- /* Find the key & certificate which should be used to sign the metadata. */
-
- $keyCertFiles = self::findKeyCert($config, $entityMetadata, $type);
-
- $keyFile = \SimpleSAML\Utils\Config::getCertPath($keyCertFiles['privatekey']);
- if (!file_exists($keyFile)) {
- throw new Exception('Could not find private key file [' . $keyFile . '], which is needed to sign the metadata');
- }
- $keyData = file_get_contents($keyFile);
-
- $certFile = \SimpleSAML\Utils\Config::getCertPath($keyCertFiles['certificate']);
- if (!file_exists($certFile)) {
- throw new Exception('Could not find certificate file [' . $certFile . '], which is needed to sign the metadata');
- }
- $certData = file_get_contents($certFile);
-
-
- /* Convert the metadata to a DOM tree. */
- $xml = new DOMDocument();
- if(!$xml->loadXML($metadataString)) {
- throw new Exception('Error parsing self-generated metadata.');
- }
-
- /* Load the private key. */
- $objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type' => 'private'));
- if(array_key_exists('privatekey_pass', $keyCertFiles)) {
- $objKey->passphrase = $keyCertFiles['privatekey_pass'];
- }
- $objKey->loadKey($keyData, FALSE);
-
- /* Get the EntityDescriptor node we should sign. */
- $rootNode = $xml->firstChild;
-
- /* Sign the metadata with our private key. */
- if ($type == 'ADFS IdP') {
- $objXMLSecDSig = new sspmod_adfs_XMLSecurityDSig($metadataString);
- } else {
- $objXMLSecDSig = new XMLSecurityDSig();
+class SimpleSAML_Metadata_Signer
+{
+
+ /**
+ * This functions finds what key & certificate files should be used to sign the metadata
+ * for the given entity.
+ *
+ * @param SimpleSAML_Configuration $config Our SimpleSAML_Configuration instance.
+ * @param array $entityMetadata The metadata of the entity.
+ * @param string $type A string which describes the type entity this is, e.g. 'SAML 2 IdP' or
+ * 'Shib 1.3 SP'.
+ *
+ * @return array An associative array with the keys 'privatekey', 'certificate', and optionally 'privatekey_pass'.
+ * @throws Exception If the key and certificate used to sign is unknown.
+ */
+ private static function findKeyCert($config, $entityMetadata, $type)
+ {
+ // first we look for metadata.privatekey and metadata.certificate in the metadata
+ if (array_key_exists('metadata.sign.privatekey', $entityMetadata)
+ || array_key_exists('metadata.sign.certificate', $entityMetadata)
+ ) {
+
+ if (!array_key_exists('metadata.sign.privatekey', $entityMetadata)
+ || !array_key_exists('metadata.sign.certificate', $entityMetadata)
+ ) {
+
+ throw new Exception(
+ 'Missing either the "metadata.sign.privatekey" or the'.
+ ' "metadata.sign.certificate" configuration option in the metadata for'.
+ ' the '.$type.' "'.$entityMetadata['entityid'].'". If one of'.
+ ' these options is specified, then the other must also be specified.'
+ );
+ }
+
+ $ret = array(
+ 'privatekey' => $entityMetadata['metadata.sign.privatekey'],
+ 'certificate' => $entityMetadata['metadata.sign.certificate']
+ );
+
+ if (array_key_exists('metadata.sign.privatekey_pass', $entityMetadata)) {
+ $ret['privatekey_pass'] = $entityMetadata['metadata.sign.privatekey_pass'];
+ }
+
+ return $ret;
+ }
+
+ // then we look for default values in the global configuration
+ $privatekey = $config->getString('metadata.sign.privatekey', null);
+ $certificate = $config->getString('metadata.sign.certificate', null);
+ if ($privatekey !== null || $certificate !== null) {
+ if ($privatekey === null || $certificate === null) {
+ throw new Exception(
+ 'Missing either the "metadata.sign.privatekey" or the'.
+ ' "metadata.sign.certificate" configuration option in the global'.
+ ' configuration. If one of these options is specified, then the other'.
+ ' must also be specified.'
+ );
+ }
+ $ret = array('privatekey' => $privatekey, 'certificate' => $certificate);
+
+ $privatekey_pass = $config->getString('metadata.sign.privatekey_pass', null);
+ if ($privatekey_pass !== null) {
+ $ret['privatekey_pass'] = $privatekey_pass;
+ }
+
+ return $ret;
+ }
+
+ // as a last resort we attempt to use the privatekey and certificate option from the metadata
+ if (array_key_exists('privatekey', $entityMetadata)
+ || array_key_exists('certificate', $entityMetadata)
+ ) {
+
+ if (!array_key_exists('privatekey', $entityMetadata)
+ || !array_key_exists('certificate', $entityMetadata)
+ ) {
+ throw new Exception(
+ 'Both the "privatekey" and the "certificate" option must'.
+ ' be set in the metadata for the '.$type.' "'.
+ $entityMetadata['entityid'].'" before it is possible to sign metadata'.
+ ' from this entity.'
+ );
+ }
+
+ $ret = array(
+ 'privatekey' => $entityMetadata['privatekey'],
+ 'certificate' => $entityMetadata['certificate']
+ );
+
+ if (array_key_exists('privatekey_pass', $entityMetadata)) {
+ $ret['privatekey_pass'] = $entityMetadata['privatekey_pass'];
+ }
+
+ return $ret;
}
- $objXMLSecDSig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N);
+ throw new Exception(
+ 'Could not find what key & certificate should be used to sign the metadata'.
+ ' for the '.$type.' "'.$entityMetadata['entityid'].'".'
+ );
+ }
+
+
+ /**
+ * Determine whether metadata signing is enabled for the given metadata.
+ *
+ * @param SimpleSAML_Configuration $config Our SimpleSAML_Configuration instance.
+ * @param array $entityMetadata The metadata of the entity.
+ * @param string $type A string which describes the type entity this is, e.g. 'SAML 2 IdP' or
+ * 'Shib 1.3 SP'.
+ *
+ * @return boolean True if metadata signing is enabled, false otherwise.
+ * @throws Exception If the value of the 'metadata.sign.enable' option is not a boolean.
+ */
+ private static function isMetadataSigningEnabled($config, $entityMetadata, $type)
+ {
+ // first check the metadata for the entity
+ if (array_key_exists('metadata.sign.enable', $entityMetadata)) {
+ if (!is_bool($entityMetadata['metadata.sign.enable'])) {
+ throw new Exception(
+ 'Invalid value for the "metadata.sign.enable" configuration option for'.
+ ' the '.$type.' "'.$entityMetadata['entityid'].'". This option'.
+ ' should be a boolean.'
+ );
+ }
+
+ return $entityMetadata['metadata.sign.enable'];
+ }
+
+ $enabled = $config->getBoolean('metadata.sign.enable', false);
+
+ return $enabled;
+ }
+
+
+ /**
+ * Signs the given metadata if metadata signing is enabled.
+ *
+ * @param string $metadataString A string with the metadata.
+ * @param array $entityMetadata The metadata of the entity.
+ * @param string $type A string which describes the type entity this is, e.g. 'SAML 2 IdP' or 'Shib 1.3 SP'.
+ *
+ * @return string The $metadataString with the signature embedded.
+ * @throws Exception If the certificate or private key cannot be loaded, or the metadata doesn't parse properly.
+ */
+ public static function sign($metadataString, $entityMetadata, $type)
+ {
+ $config = SimpleSAML_Configuration::getInstance();
+
+ // check if metadata signing is enabled
+ if (!self::isMetadataSigningEnabled($config, $entityMetadata, $type)) {
+ return $metadataString;
+ }
+
+ // find the key & certificate which should be used to sign the metadata
+ $keyCertFiles = self::findKeyCert($config, $entityMetadata, $type);
+
+ $keyFile = \SimpleSAML\Utils\Config::getCertPath($keyCertFiles['privatekey']);
+ if (!file_exists($keyFile)) {
+ throw new Exception('Could not find private key file ['.$keyFile.'], which is needed to sign the metadata');
+ }
+ $keyData = file_get_contents($keyFile);
+
+ $certFile = \SimpleSAML\Utils\Config::getCertPath($keyCertFiles['certificate']);
+ if (!file_exists($certFile)) {
+ throw new Exception(
+ 'Could not find certificate file ['.$certFile.'], which is needed to sign the metadata'
+ );
+ }
+ $certData = file_get_contents($certFile);
+
+
+ // convert the metadata to a DOM tree
+ $xml = new DOMDocument();
+ if (!$xml->loadXML($metadataString)) {
+ throw new Exception('Error parsing self-generated metadata.');
+ }
+
+ // load the private key
+ $objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type' => 'private'));
+ if (array_key_exists('privatekey_pass', $keyCertFiles)) {
+ $objKey->passphrase = $keyCertFiles['privatekey_pass'];
+ }
+ $objKey->loadKey($keyData, false);
+
+ // get the EntityDescriptor node we should sign
+ $rootNode = $xml->firstChild;
+
+ // sign the metadata with our private key
+ if ($type == 'ADFS IdP') {
+ $objXMLSecDSig = new sspmod_adfs_XMLSecurityDSig($metadataString);
+ } else {
+ $objXMLSecDSig = new XMLSecurityDSig();
+ }
- $objXMLSecDSig->addReferenceList(array($rootNode), XMLSecurityDSig::SHA1,
- array('http://www.w3.org/2000/09/xmldsig#enveloped-signature', XMLSecurityDSig::EXC_C14N),
- array('id_name' => 'ID'));
+ $objXMLSecDSig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N);
- $objXMLSecDSig->sign($objKey);
+ $objXMLSecDSig->addReferenceList(
+ array($rootNode),
+ XMLSecurityDSig::SHA1,
+ array('http://www.w3.org/2000/09/xmldsig#enveloped-signature', XMLSecurityDSig::EXC_C14N),
+ array('id_name' => 'ID')
+ );
- /* Add the certificate to the signature. */
- $objXMLSecDSig->add509Cert($certData, true);
+ $objXMLSecDSig->sign($objKey);
- /* Add the signature to the metadata. */
- $objXMLSecDSig->insertSignature($rootNode, $rootNode->firstChild);
+ // add the certificate to the signature
+ $objXMLSecDSig->add509Cert($certData, true);
- /* Return the DOM tree as a string. */
- return $xml->saveXML();
- }
+ // add the signature to the metadata
+ $objXMLSecDSig->insertSignature($rootNode, $rootNode->firstChild);
+ // return the DOM tree as a string
+ return $xml->saveXML();
+ }
}
diff --git a/lib/SimpleSAML/Module.php b/lib/SimpleSAML/Module.php
index 0620d60..4868c56 100644
--- a/lib/SimpleSAML/Module.php
+++ b/lib/SimpleSAML/Module.php
@@ -1,198 +1,220 @@
-<?php
+<?php
+
/**
* Helper class for accessing information about modules.
*
* @author Olav Morken, UNINETT AS.
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-class SimpleSAML_Module {
-
-
- /**
- * Retrieve the base directory for a module.
- *
- * The returned path name will be an absoulte path.
- *
- * @param string $module Name of the module
- * @return string The base directory of a module.
- */
- public static function getModuleDir($module) {
- $baseDir = dirname(dirname(dirname(__FILE__))) . '/modules';
- $moduleDir = $baseDir . '/' . $module;
-
- return $moduleDir;
- }
-
-
- /**
- * Determine whether a module is enabled.
- *
- * Will return FALSE if the given module doesn't exists.
- *
- * @param string $module Name of the module
- * @return bool TRUE if the given module is enabled, FALSE if not.
- */
- public static function isModuleEnabled($module) {
-
- $moduleDir = self::getModuleDir($module);
-
- if(!is_dir($moduleDir)) {
- return FALSE;
- }
-
- $globalConfig = SimpleSAML_Configuration::getOptionalConfig();
- $moduleEnable = $globalConfig->getArray('module.enable', array());
-
- if(isset($moduleEnable[$module])) {
- if(is_bool($moduleEnable[$module]) === TRUE) {
- return $moduleEnable[$module];
- }
-
- throw new Exception("Invalid module.enable value for for the module $module");
- }
-
- if (assert_options(ASSERT_ACTIVE) && !file_exists($moduleDir . '/default-enable') && !file_exists($moduleDir . '/default-disable')) {
- SimpleSAML_Logger::error("Missing default-enable or default-disable file for the module $module");
- }
-
- if(file_exists($moduleDir . '/enable')) {
- return TRUE;
- }
-
- if(!file_exists($moduleDir . '/disable') && file_exists($moduleDir . '/default-enable')) {
- return TRUE;
- }
-
- return FALSE;
- }
-
-
- /**
- * Get available modules.
- *
- * @return array One string for each module.
- */
- public static function getModules() {
-
- $path = self::getModuleDir('.');
-
- $dh = opendir($path);
- if($dh === FALSE) {
- throw new Exception('Unable to open module directory "' . $path . '".');
- }
-
- $modules = array();
-
- while( ($f = readdir($dh)) !== FALSE) {
- if($f[0] === '.') {
- continue;
- }
-
- if(!is_dir($path . '/' . $f)) {
- continue;
- }
-
- $modules[] = $f;
- }
-
- closedir($dh);
-
- return $modules;
- }
-
-
- /**
- * Resolve module class.
- *
- * This function takes a string on the form "<module>:<class>" and converts it to a class
- * name. It can also check that the given class is a subclass of a specific class. The
- * resolved classname will be "sspmod_<module>_<$type>_<class>.
- *
- * It is also possible to specify a full classname instead of <module>:<class>.
- *
- * An exception will be thrown if the class can't be resolved.
- *
- * @param string $id The string we should resolve.
- * @param string $type The type of the class.
- * @param string|NULL $subclass The class should be a subclass of this class. Optional.
- * @return string The classname.
- */
- public static function resolveClass($id, $type, $subclass = NULL) {
- assert('is_string($id)');
- assert('is_string($type)');
- assert('is_string($subclass) || is_null($subclass)');
-
- $tmp = explode(':', $id, 2);
- if (count($tmp) === 1) {
- $className = $tmp[0];
- } else {
- $className = 'sspmod_' . $tmp[0] . '_' . $type . '_' . $tmp[1];
- }
-
- if (!class_exists($className)) {
- throw new Exception('Could not resolve \'' . $id .
- '\': No class named \'' . $className . '\'.');
- } elseif ($subclass !== NULL && !is_subclass_of($className, $subclass)) {
- throw new Exception('Could not resolve \'' . $id . '\': The class \'' .
- $className . '\' isn\'t a subclass of \'' . $subclass . '\'.');
- }
-
- return $className;
- }
-
-
- /**
- * Get absolute URL to a specified module resource.
- *
- * This function creates an absolute URL to a resource stored under ".../modules/<module>/www/".
- *
- * @param string $resource Resource path, on the form "<module name>/<resource>"
- * @param array $parameters Extra parameters which should be added to the URL. Optional.
- * @return string The absolute URL to the given resource.
- */
- public static function getModuleURL($resource, array $parameters = array()) {
- assert('is_string($resource)');
- assert('$resource[0] !== "/"');
-
- $url = \SimpleSAML\Utils\HTTP::getBaseURL() . 'module.php/' . $resource;
- if (!empty($parameters)) {
- $url = \SimpleSAML\Utils\HTTP::addURLParameters($url, $parameters);
- }
- return $url;
- }
-
-
- /**
- * Call a hook in all enabled modules.
- *
- * This function iterates over all enabled modules and calls a hook in each module.
- *
- * @param string $hook The name of the hook.
- * @param mixed &$data The data which should be passed to each hook. Will be passed as a reference.
- */
- public static function callHooks($hook, &$data = NULL) {
- assert('is_string($hook)');
-
- $modules = self::getModules();
- sort($modules);
- foreach ($modules as $module) {
- if (!self::isModuleEnabled($module)) {
- continue;
- }
-
- $hookfile = self::getModuleDir($module) . '/hooks/hook_' . $hook . '.php';
- if (!file_exists($hookfile)) {
- continue;
- }
-
- require_once($hookfile);
-
- $hookfunc = $module . '_hook_' . $hook;
- assert('is_callable($hookfunc)');
-
- $hookfunc($data);
- }
- }
-
+class SimpleSAML_Module
+{
+
+
+ /**
+ * Retrieve the base directory for a module.
+ *
+ * The returned path name will be an absolute path.
+ *
+ * @param string $module Name of the module
+ *
+ * @return string The base directory of a module.
+ */
+ public static function getModuleDir($module)
+ {
+ $baseDir = dirname(dirname(dirname(__FILE__))).'/modules';
+ $moduleDir = $baseDir.'/'.$module;
+
+ return $moduleDir;
+ }
+
+
+ /**
+ * Determine whether a module is enabled.
+ *
+ * Will return false if the given module doesn't exists.
+ *
+ * @param string $module Name of the module
+ *
+ * @return bool True if the given module is enabled, false otherwise.
+ *
+ * @throws Exception If module.enable is set and is not boolean.
+ */
+ public static function isModuleEnabled($module)
+ {
+
+ $moduleDir = self::getModuleDir($module);
+
+ if (!is_dir($moduleDir)) {
+ return false;
+ }
+
+ $globalConfig = SimpleSAML_Configuration::getOptionalConfig();
+ $moduleEnable = $globalConfig->getArray('module.enable', array());
+
+ if (isset($moduleEnable[$module])) {
+ if (is_bool($moduleEnable[$module]) === true) {
+ return $moduleEnable[$module];
+ }
+
+ throw new Exception("Invalid module.enable value for for the module $module");
+ }
+
+ if (assert_options(ASSERT_ACTIVE) &&
+ !file_exists($moduleDir.'/default-enable') &&
+ !file_exists($moduleDir.'/default-disable')
+ ) {
+ SimpleSAML_Logger::error("Missing default-enable or default-disable file for the module $module");
+ }
+
+ if (file_exists($moduleDir.'/enable')) {
+ return true;
+ }
+
+ if (!file_exists($moduleDir.'/disable') && file_exists($moduleDir.'/default-enable')) {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Get available modules.
+ *
+ * @return array One string for each module.
+ *
+ * @throws Exception If we cannot open the module's directory.
+ */
+ public static function getModules()
+ {
+
+ $path = self::getModuleDir('.');
+
+ $dh = opendir($path);
+ if ($dh === false) {
+ throw new Exception('Unable to open module directory "'.$path.'".');
+ }
+
+ $modules = array();
+
+ while (($f = readdir($dh)) !== false) {
+ if ($f[0] === '.') {
+ continue;
+ }
+
+ if (!is_dir($path.'/'.$f)) {
+ continue;
+ }
+
+ $modules[] = $f;
+ }
+
+ closedir($dh);
+
+ return $modules;
+ }
+
+
+ /**
+ * Resolve module class.
+ *
+ * This function takes a string on the form "<module>:<class>" and converts it to a class
+ * name. It can also check that the given class is a subclass of a specific class. The
+ * resolved classname will be "sspmod_<module>_<$type>_<class>.
+ *
+ * It is also possible to specify a full classname instead of <module>:<class>.
+ *
+ * An exception will be thrown if the class can't be resolved.
+ *
+ * @param string $id The string we should resolve.
+ * @param string $type The type of the class.
+ * @param string|null $subclass The class should be a subclass of this class. Optional.
+ *
+ * @return string The classname.
+ *
+ * @throws Exception If the class cannot be resolved.
+ */
+ public static function resolveClass($id, $type, $subclass = null)
+ {
+ assert('is_string($id)');
+ assert('is_string($type)');
+ assert('is_string($subclass) || is_null($subclass)');
+
+ $tmp = explode(':', $id, 2);
+ if (count($tmp) === 1) {
+ $className = $tmp[0];
+ } else {
+ $className = 'sspmod_'.$tmp[0].'_'.$type.'_'.$tmp[1];
+ }
+
+ if (!class_exists($className)) {
+ throw new Exception(
+ 'Could not resolve \''.$id.'\': No class named \''.$className.'\'.'
+ );
+ } elseif ($subclass !== null && !is_subclass_of($className, $subclass)) {
+ throw new Exception(
+ 'Could not resolve \''.$id.'\': The class \''.$className.'\' isn\'t a subclass of \''.$subclass.'\'.'
+ );
+ }
+
+ return $className;
+ }
+
+
+ /**
+ * Get absolute URL to a specified module resource.
+ *
+ * This function creates an absolute URL to a resource stored under ".../modules/<module>/www/".
+ *
+ * @param string $resource Resource path, on the form "<module name>/<resource>"
+ * @param array $parameters Extra parameters which should be added to the URL. Optional.
+ *
+ * @return string The absolute URL to the given resource.
+ */
+ public static function getModuleURL($resource, array $parameters = array())
+ {
+ assert('is_string($resource)');
+ assert('$resource[0] !== "/"');
+
+ $url = \SimpleSAML\Utils\HTTP::getBaseURL().'module.php/'.$resource;
+ if (!empty($parameters)) {
+ $url = \SimpleSAML\Utils\HTTP::addURLParameters($url, $parameters);
+ }
+ return $url;
+ }
+
+
+ /**
+ * Call a hook in all enabled modules.
+ *
+ * This function iterates over all enabled modules and calls a hook in each module.
+ *
+ * @param string $hook The name of the hook.
+ * @param mixed &$data The data which should be passed to each hook. Will be passed as a reference.
+ */
+ public static function callHooks($hook, &$data = null)
+ {
+ assert('is_string($hook)');
+
+ $modules = self::getModules();
+ sort($modules);
+ foreach ($modules as $module) {
+ if (!self::isModuleEnabled($module)) {
+ continue;
+ }
+
+ $hookfile = self::getModuleDir($module).'/hooks/hook_'.$hook.'.php';
+ if (!file_exists($hookfile)) {
+ continue;
+ }
+
+ require_once($hookfile);
+
+ $hookfunc = $module.'_hook_'.$hook;
+ assert('is_callable($hookfunc)');
+
+ $hookfunc($data);
+ }
+ }
}
diff --git a/lib/SimpleSAML/Session.php b/lib/SimpleSAML/Session.php
index 42f73b8..b13a64d 100644
--- a/lib/SimpleSAML/Session.php
+++ b/lib/SimpleSAML/Session.php
@@ -57,7 +57,7 @@ class SimpleSAML_Session
* This is used in the debug logs and error messages to easily track more information
* about what went wrong.
*
- * @var int
+ * @var string|int
*/
private $trackid = 0;
@@ -195,7 +195,8 @@ class SimpleSAML_Session
*
* @param string|null $sessionId The session we should load, or null to load the current session.
*
- * @return The session which is stored in the session handler, or null if the session wasn't found.
+ * @return SimpleSAML_Session The session that is stored in the session handler, or null if the session wasn't
+ * found.
*/
public static function getSession($sessionId = null)
{
@@ -225,8 +226,10 @@ class SimpleSAML_Session
$globalConfig = SimpleSAML_Configuration::getInstance();
if ($session->authToken !== null) {
- $authTokenCookieName = $globalConfig->getString('session.authtoken.cookiename',
- 'SimpleSAMLAuthToken');
+ $authTokenCookieName = $globalConfig->getString(
+ 'session.authtoken.cookiename',
+ 'SimpleSAMLAuthToken'
+ );
if (!isset($_COOKIE[$authTokenCookieName])) {
SimpleSAML_Logger::warning('Missing AuthToken cookie.');
return null;
@@ -417,8 +420,10 @@ class SimpleSAML_Session
$this->setRememberMeExpire();
} else {
- $sessionHandler->setCookie($globalConfig->getString('session.authtoken.cookiename',
- 'SimpleSAMLAuthToken'), $this->authToken);
+ $sessionHandler->setCookie(
+ $globalConfig->getString('session.authtoken.cookiename', 'SimpleSAMLAuthToken'),
+ $this->authToken
+ );
}
}
@@ -471,8 +476,10 @@ class SimpleSAML_Session
$classname = $handler[0];
$functionname = $handler[1];
- throw new Exception('Logout handler is not a vaild function: '.$classname.'::'.
- $functionname);
+ throw new Exception(
+ 'Logout handler is not a vaild function: '.$classname.'::'.
+ $functionname
+ );
}
// call the logout handler
@@ -496,8 +503,10 @@ class SimpleSAML_Session
assert('is_string($authority)');
if (!isset($this->authData[$authority])) {
- SimpleSAML_Logger::debug('Session: '.var_export($authority, true).
- ' not valid because we are not authenticated.');
+ SimpleSAML_Logger::debug(
+ 'Session: '.var_export($authority, true).
+ ' not valid because we are not authenticated.'
+ );
return false;
}
@@ -514,7 +523,7 @@ class SimpleSAML_Session
/**
* Update session cookies.
*
- * @param array The parameters for the cookies.
+ * @param array $params The parameters for the cookies.
*/
public function updateSessionCookies($params = null)
{
@@ -526,8 +535,11 @@ class SimpleSAML_Session
if ($this->authToken !== null) {
$globalConfig = SimpleSAML_Configuration::getInstance();
- $sessionHandler->setCookie($globalConfig->getString('session.authtoken.cookiename',
- 'SimpleSAMLAuthToken'), $this->authToken, $params);
+ $sessionHandler->setCookie(
+ $globalConfig->getString('session.authtoken.cookiename', 'SimpleSAMLAuthToken'),
+ $this->authToken,
+ $params
+ );
}
}
@@ -568,8 +580,10 @@ class SimpleSAML_Session
$logout_handler = array($classname, $functionname);
if (!is_callable($logout_handler)) {
- throw new Exception('Logout handler is not a vaild function: '.$classname.'::'.
- $functionname);
+ throw new Exception(
+ 'Logout handler is not a vaild function: '.$classname.'::'.
+ $functionname
+ );
}
$this->authData[$authority]['LogoutHandlers'][] = $logout_handler;
@@ -628,14 +642,15 @@ class SimpleSAML_Session
if ($timeout === null) {
// use the default timeout
-
$configuration = SimpleSAML_Configuration::getInstance();
$timeout = $configuration->getInteger('session.datastore.timeout', null);
if ($timeout !== null) {
if ($timeout <= 0) {
- throw new Exception('The value of the session.datastore.timeout'.
- ' configuration option should be a positive integer.');
+ throw new Exception(
+ 'The value of the session.datastore.timeout'.
+ ' configuration option should be a positive integer.'
+ );
}
}
}
diff --git a/lib/SimpleSAML/SessionHandler.php b/lib/SimpleSAML/SessionHandler.php
index 9586d56..debfba0 100644
--- a/lib/SimpleSAML/SessionHandler.php
+++ b/lib/SimpleSAML/SessionHandler.php
@@ -1,5 +1,6 @@
<?php
+
/**
* This file is part of SimpleSAMLphp. See the file COPYING in the
* root of the distribution for licence information.
@@ -9,153 +10,165 @@
* the class method getSessionHandler().
*
* @author Olav Morken, UNINETT AS. <andreas.solberg@uninett.no>
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-abstract class SimpleSAML_SessionHandler {
-
-
- /* This static variable contains a reference to the current
- * instance of the session handler. This variable will be NULL if
- * we haven't instantiated a session handler yet.
- */
- private static $sessionHandler = NULL;
-
-
-
- /* This function retrieves the current instance of the session handler.
- * The session handler will be instantiated if this is the first call
- * to this fuunction.
- *
- * Returns:
- * The current session handler.
- */
- public static function getSessionHandler() {
- if(self::$sessionHandler === NULL) {
- self::createSessionHandler();
- }
-
- return self::$sessionHandler;
- }
-
-
- /* This constructor is included in case it is needed in the the
- * future. Including it now allows us to write parent::__construct() in
- * the subclasses of this class.
- */
- protected function __construct() {
- }
-
-
- /**
- * Create and set new session id.
- *
- * @return string The new session id.
- */
- abstract public function newSessionId();
-
-
- /**
- * Retrieve the session id of saved in the session cookie.
- *
- * @return string The session id saved in the cookie.
- */
- abstract public function getCookieSessionId();
-
-
- /**
- * Retrieve the session cookie name.
- *
- * @return string The session cookie name.
- */
- abstract public function getSessionCookieName();
-
-
- /**
- * Save the session.
- *
- * @param SimpleSAML_Session $session The session object we should save.
- */
- abstract public function saveSession(SimpleSAML_Session $session);
-
-
- /**
- * Load the session.
- *
- * @param string|NULL $sessionId The ID of the session we should load, or NULL to use the default.
- * @return SimpleSAML_Session|NULL The session object, or NULL if it doesn't exist.
- */
- abstract public function loadSession($sessionId = NULL);
-
-
- /**
- * Initialize the session handler.
- *
- * This function creates an instance of the session handler which is
- * selected in the 'session.handler' configuration directive. If no
- * session handler is selected, then we will fall back to the default
- * PHP session handler.
- */
- private static function createSessionHandler() {
-
- $store = SimpleSAML_Store::getInstance();
- if ($store === FALSE) {
- self::$sessionHandler = new SimpleSAML_SessionHandlerPHP();
- } else {
- self::$sessionHandler = new SimpleSAML_SessionHandlerStore($store);
- }
- }
-
-
- /**
- * Check whether the session cookie is set.
- *
- * This function will only return FALSE if is is certain that the cookie isn't set.
- *
- * @return bool TRUE if it was set, FALSE if not.
- */
- public function hasSessionCookie() {
-
- return TRUE;
- }
-
-
- /**
- * Get the cookie parameters that should be used for session cookies.
- *
- * @return array
- * @link http://www.php.net/manual/en/function.session-get-cookie-params.php
- */
- public function getCookieParams() {
-
- $config = SimpleSAML_Configuration::getInstance();
-
- return array(
- 'lifetime' => $config->getInteger('session.cookie.lifetime', 0),
- 'path' => $config->getString('session.cookie.path', '/'),
- 'domain' => $config->getString('session.cookie.domain', NULL),
- 'secure' => $config->getBoolean('session.cookie.secure', FALSE),
- 'httponly' => TRUE,
- );
- }
-
-
- /**
- * Set a session cookie.
- *
- * @param string $name The name of the session cookie.
- * @param string|NULL $value The value of the cookie. Set to NULL to delete the cookie.
- */
- public function setCookie($name, $value, array $params = NULL) {
- assert('is_string($name)');
- assert('is_string($value) || is_null($value)');
-
- if ($params !== NULL) {
- $params = array_merge($this->getCookieParams(), $params);
- } else {
- $params = $this->getCookieParams();
- }
-
- \SimpleSAML\Utils\HTTP::setCookie($name, $value, $params);
- }
-
+abstract class SimpleSAML_SessionHandler
+{
+
+
+ /**
+ * This static variable contains a reference to the current
+ * instance of the session handler. This variable will be NULL if
+ * we haven't instantiated a session handler yet.
+ *
+ * @var SimpleSAML_SessionHandler
+ */
+ private static $sessionHandler = null;
+
+
+ /**
+ * This function retrieves the current instance of the session handler.
+ * The session handler will be instantiated if this is the first call
+ * to this function.
+ *
+ * @return SimpleSAML_SessionHandler The current session handler.
+ */
+ public static function getSessionHandler()
+ {
+ if (self::$sessionHandler === null) {
+ self::createSessionHandler();
+ }
+
+ return self::$sessionHandler;
+ }
+
+
+ /**
+ * This constructor is included in case it is needed in the the
+ * future. Including it now allows us to write parent::__construct() in
+ * the subclasses of this class.
+ */
+ protected function __construct()
+ {
+ }
+
+
+ /**
+ * Create and set new session id.
+ *
+ * @return string The new session id.
+ */
+ abstract public function newSessionId();
+
+
+ /**
+ * Retrieve the session id of saved in the session cookie.
+ *
+ * @return string The session id saved in the cookie.
+ */
+ abstract public function getCookieSessionId();
+
+
+ /**
+ * Retrieve the session cookie name.
+ *
+ * @return string The session cookie name.
+ */
+ abstract public function getSessionCookieName();
+
+
+ /**
+ * Save the session.
+ *
+ * @param SimpleSAML_Session $session The session object we should save.
+ */
+ abstract public function saveSession(SimpleSAML_Session $session);
+
+
+ /**
+ * Load the session.
+ *
+ * @param string|NULL $sessionId The ID of the session we should load, or null to use the default.
+ *
+ * @return SimpleSAML_Session|null The session object, or null if it doesn't exist.
+ */
+ abstract public function loadSession($sessionId = null);
+
+
+ /**
+ * Initialize the session handler.
+ *
+ * This function creates an instance of the session handler which is
+ * selected in the 'session.handler' configuration directive. If no
+ * session handler is selected, then we will fall back to the default
+ * PHP session handler.
+ */
+ private static function createSessionHandler()
+ {
+
+ $store = SimpleSAML_Store::getInstance();
+ if ($store === false) {
+ self::$sessionHandler = new SimpleSAML_SessionHandlerPHP();
+ } else {
+ /** @var SimpleSAML_Store $store At this point, $store can only be an object */
+ self::$sessionHandler = new SimpleSAML_SessionHandlerStore($store);
+ }
+ }
+
+
+ /**
+ * Check whether the session cookie is set.
+ *
+ * This function will only return false if is is certain that the cookie isn't set.
+ *
+ * @return bool True if it was set, false if not.
+ */
+ public function hasSessionCookie()
+ {
+
+ return true;
+ }
+
+
+ /**
+ * Get the cookie parameters that should be used for session cookies.
+ *
+ * @return array An array with the cookie parameters.
+ * @link http://www.php.net/manual/en/function.session-get-cookie-params.php
+ */
+ public function getCookieParams()
+ {
+
+ $config = SimpleSAML_Configuration::getInstance();
+
+ return array(
+ 'lifetime' => $config->getInteger('session.cookie.lifetime', 0),
+ 'path' => $config->getString('session.cookie.path', '/'),
+ 'domain' => $config->getString('session.cookie.domain', null),
+ 'secure' => $config->getBoolean('session.cookie.secure', false),
+ 'httponly' => true,
+ );
+ }
+
+
+ /**
+ * Set a session cookie.
+ *
+ * @param string $name The name of the session cookie.
+ * @param string|null $value The value of the cookie. Set to null to delete the cookie.
+ * @param array|null $params Additional params to use for the session cookie.
+ */
+ public function setCookie($name, $value, array $params = null)
+ {
+ assert('is_string($name)');
+ assert('is_string($value) || is_null($value)');
+
+ if ($params !== null) {
+ $params = array_merge($this->getCookieParams(), $params);
+ } else {
+ $params = $this->getCookieParams();
+ }
+
+ \SimpleSAML\Utils\HTTP::setCookie($name, $value, $params);
+ }
}
diff --git a/lib/SimpleSAML/SessionHandlerCookie.php b/lib/SimpleSAML/SessionHandlerCookie.php
index 9e47a8a..f0b56cc 100644
--- a/lib/SimpleSAML/SessionHandlerCookie.php
+++ b/lib/SimpleSAML/SessionHandlerCookie.php
@@ -1,139 +1,145 @@
<?php
+
/**
- * This file is part of SimpleSAMLphp. See the file COPYING in the
- * root of the distribution for licence information.
+ * This file is part of SimpleSAMLphp. See the file COPYING in the root of the distribution for licence information.
*
- * This file defines a base class for session handlers that need to store
- * the session id in a cookie. It takes care of storing and retrieving the
- * session id.
+ * This file defines a base class for session handlers that need to store the session id in a cookie. It takes care of
+ * storing and retrieving the session id.
*
* @author Olav Morken, UNINETT AS. <andreas.solberg@uninett.no>
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
* @abstract
*/
-abstract class SimpleSAML_SessionHandlerCookie
-extends SimpleSAML_SessionHandler {
-
- /* This variable contains the current session id. */
- private $session_id = NULL;
-
-
- /* This variable contains the session cookie name. */
- protected $cookie_name;
-
-
- /* This constructor initializes the session id based on what
- * we receive in a cookie. We create a new session id and set
- * a cookie with this id if we don't have a session id.
- */
- protected function __construct() {
- /* Call the constructor in the base class in case it should
- * become necessary in the future.
- */
- parent::__construct();
-
- $config = SimpleSAML_Configuration::getInstance();
- $this->cookie_name = $config->getString('session.cookie.name', 'SimpleSAMLSessionID');
- }
-
-
- /**
- * Create and set new session id.
- *
- * @return string The new session id.
- */
- public function newSessionId() {
- $this->session_id = self::createSessionID();
- SimpleSAML_Session::createSession($this->session_id);
- $this->setCookie($this->cookie_name, $this->session_id);
-
- return $this->session_id;
- }
-
-
- /**
- * Retrieve the session id of saved in the session cookie.
- *
- * @return string The session id saved in the cookie.
- */
- public function getCookieSessionId() {
- if ($this->session_id === NULL) {
- if(self::hasSessionCookie()) {
- /* Attempt to retrieve the session id from the cookie. */
- $this->session_id = $_COOKIE[$this->cookie_name];
- }
-
- /* Check if we have a valid session id. */
- if(!self::isValidSessionID($this->session_id)) {
- /* We don't have a valid session. Create a new session id. */
- return self::newSessionId();
- }
- }
-
- return $this->session_id;
- }
-
-
- /**
- * Retrieve the session cookie name.
- *
- * @return string The session cookie name.
- */
- public function getSessionCookieName() {
-
- return $this->cookie_name;
- }
-
-
- /* This static function creates a session id. A session id consists
- * of 32 random hexadecimal characters.
- *
- * Returns:
- * A random session id.
- */
- private static function createSessionID() {
- return bin2hex(openssl_random_pseudo_bytes(16));
- }
-
-
- /* This static function validates a session id. A session id is valid
- * if it only consists of characters which are allowed in a session id
- * and it is the correct length.
- *
- * Parameters:
- * $session_id The session id we should validate.
- *
- * Returns:
- * TRUE if this session id is valid, FALSE if not.
- */
- private static function isValidSessionID($session_id) {
- if(!is_string($session_id)) {
- return FALSE;
- }
-
- if(strlen($session_id) != 32) {
- return FALSE;
- }
-
- if(preg_match('/[^0-9a-f]/', $session_id)) {
- return FALSE;
- }
-
- return TRUE;
- }
-
-
- /**
- * Check whether the session cookie is set.
- *
- * This function will only return FALSE if is is certain that the cookie isn't set.
- *
- * @return bool TRUE if it was set, FALSE if not.
- */
- public function hasSessionCookie() {
-
- return array_key_exists($this->cookie_name, $_COOKIE);
- }
-
+abstract class SimpleSAML_SessionHandlerCookie extends SimpleSAML_SessionHandler
+{
+
+ /**
+ * This variable contains the current session id.
+ *
+ * @var string|null
+ */
+ private $session_id = null;
+
+
+ /**
+ * This variable contains the session cookie name.
+ *
+ * @var string
+ */
+ protected $cookie_name;
+
+
+ /**
+ * This constructor initializes the session id based on what we receive in a cookie. We create a new session id and
+ * set a cookie with this id if we don't have a session id.
+ */
+ protected function __construct()
+ {
+ // call the constructor in the base class in case it should become necessary in the future
+ parent::__construct();
+
+ $config = SimpleSAML_Configuration::getInstance();
+ $this->cookie_name = $config->getString('session.cookie.name', 'SimpleSAMLSessionID');
+ }
+
+
+ /**
+ * Create and set new session id.
+ *
+ * @return string The new session id.
+ */
+ public function newSessionId()
+ {
+ $this->session_id = self::createSessionID();
+ SimpleSAML_Session::createSession($this->session_id);
+ $this->setCookie($this->cookie_name, $this->session_id);
+
+ return $this->session_id;
+ }
+
+
+ /**
+ * Retrieve the session id of saved in the session cookie.
+ *
+ * @return string The session id saved in the cookie.
+ */
+ public function getCookieSessionId()
+ {
+ if ($this->session_id === null) {
+ if (self::hasSessionCookie()) {
+ // attempt to retrieve the session id from the cookie
+ $this->session_id = $_COOKIE[$this->cookie_name];
+ }
+
+ // check if we have a valid session id
+ if (!self::isValidSessionID($this->session_id)) {
+ // we don't have a valid session. Create a new session id
+ return self::newSessionId();
+ }
+ }
+
+ return $this->session_id;
+ }
+
+
+ /**
+ * Retrieve the session cookie name.
+ *
+ * @return string The session cookie name.
+ */
+ public function getSessionCookieName()
+ {
+ return $this->cookie_name;
+ }
+
+
+ /**
+ * This static function creates a session id. A session id consists of 32 random hexadecimal characters.
+ *
+ * @return string A random session id.
+ */
+ private static function createSessionID()
+ {
+ return bin2hex(openssl_random_pseudo_bytes(16));
+ }
+
+
+ /**
+ * This static function validates a session id. A session id is valid if it only consists of characters which are
+ * allowed in a session id and it is the correct length.
+ *
+ * @param string $session_id The session ID we should validate.
+ *
+ * @return boolean True if this session ID is valid, false otherwise.
+ */
+ private static function isValidSessionID($session_id)
+ {
+ if (!is_string($session_id)) {
+ return false;
+ }
+
+ if (strlen($session_id) != 32) {
+ return false;
+ }
+
+ if (preg_match('/[^0-9a-f]/', $session_id)) {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Check whether the session cookie is set.
+ *
+ * This function will only return false if is is certain that the cookie isn't set.
+ *
+ * @return boolean True if it was set, false otherwise.
+ */
+ public function hasSessionCookie()
+ {
+ return array_key_exists($this->cookie_name, $_COOKIE);
+ }
}
diff --git a/lib/SimpleSAML/SessionHandlerPHP.php b/lib/SimpleSAML/SessionHandlerPHP.php
index 4f05274..7bc7a17 100644
--- a/lib/SimpleSAML/SessionHandlerPHP.php
+++ b/lib/SimpleSAML/SessionHandlerPHP.php
@@ -1,214 +1,240 @@
<?php
+
/**
- * This file is part of SimpleSAMLphp. See the file COPYING in the
- * root of the distribution for licence information.
+ * This file is part of SimpleSAMLphp. See the file COPYING in the root of the distribution for licence information.
*
- * This file defines a session handler which uses the default php
- * session handler for storage.
+ * This file defines a session handler which uses the default php session handler for storage.
*
* @author Olav Morken, UNINETT AS. <andreas.solberg@uninett.no>
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-class SimpleSAML_SessionHandlerPHP extends SimpleSAML_SessionHandler {
-
- /* This variable contains the session cookie name. */
- protected $cookie_name;
-
-
- /* Initialize the PHP session handling. This constructor is protected
- * because it should only be called from
- * SimpleSAML_SessionHandler::createSessionHandler(...).
- */
- protected function __construct() {
-
- /* Call the parent constructor in case it should become
- * necessary in the future.
- */
- parent::__construct();
-
- /* Initialize the php session handling.
- *
- * If session_id() returns a blank string, then we need
- * to call session start. Otherwise the session is already
- * started, and we should avoid calling session_start().
- */
- if(session_id() === '') {
- $config = SimpleSAML_Configuration::getInstance();
-
- $params = $this->getCookieParams();
-
- session_set_cookie_params($params['lifetime'], $params['path'], $params['domain'], $params['secure'], $params['httponly']);
-
- $this->cookie_name = $config->getString('session.phpsession.cookiename', NULL);
- if (!empty($this->cookie_name)) {
- session_name($this->cookie_name);
- } else {
- $this->cookie_name = session_name();
- }
-
- $savepath = $config->getString('session.phpsession.savepath', NULL);
- if(!empty($savepath)) {
- session_save_path($savepath);
- }
- }
- }
-
-
- /**
- * Create and set new session id.
- *
- * @return string The new session id.
- */
- public function newSessionId() {
- $session_cookie_params = session_get_cookie_params();
-
- if ($session_cookie_params['secure'] && !\SimpleSAML\Utils\HTTP::isHTTPS()) {
- throw new SimpleSAML_Error_Exception('Session start with secure cookie not allowed on http.');
- }
-
- if (headers_sent()) {
- throw new SimpleSAML_Error_Exception('Cannot create new session - headers already sent.');
- }
-
- /* Generate new (secure) session id. */
- $sessionId = bin2hex(openssl_random_pseudo_bytes(16));
- SimpleSAML_Session::createSession($sessionId);
-
- if (session_id() !== '') {
- /* Session already started, close it. */
- session_write_close();
- }
-
- session_id($sessionId);
- session_start();
-
- return session_id();
- }
-
-
- /**
- * Retrieve the session id of saved in the session cookie.
- *
- * @return string The session id saved in the cookie.
- */
- public function getCookieSessionId() {
- if(session_id() === '') {
- if(!self::hasSessionCookie()) {
- return self::newSessionId();
- }
-
- $session_cookie_params = session_get_cookie_params();
-
- if ($session_cookie_params['secure'] && !\SimpleSAML\Utils\HTTP::isHTTPS()) {
- throw new SimpleSAML_Error_Exception('Session start with secure cookie not allowed on http.');
- }
-
- session_start();
- }
-
- return session_id();
- }
-
-
- /**
- * Retrieve the session cookie name.
- *
- * @return string The session cookie name.
- */
- public function getSessionCookieName() {
-
- return $this->cookie_name;
- }
-
-
- /**
- * Save the current session to the PHP session array.
- *
- * @param SimpleSAML_Session $session The session object we should save.
- */
- public function saveSession(SimpleSAML_Session $session) {
-
- $_SESSION['SimpleSAMLphp_SESSION'] = serialize($session);
- }
-
-
- /**
- * Load the session from the PHP session array.
- *
- * @param string|NULL $sessionId The ID of the session we should load, or NULL to use the default.
- * @return SimpleSAML_Session|NULL The session object, or NULL if it doesn't exist.
- */
- public function loadSession($sessionId = NULL) {
- assert('is_string($sessionId) || is_null($sessionId)');
-
- if ($sessionId !== NULL) {
- if (session_id() === '') {
- /* session not initiated with getCookieSessionId(), start session without setting cookie */
- $ret = ini_set('session.use_cookies', '0');
- if ($ret === FALSE) {
- throw new SimpleSAML_Error_Exception('Disabling PHP option session.use_cookies failed.');
- }
-
- session_id($sessionId);
- session_start();
- } elseif ($sessionId !== session_id()) {
- throw new SimpleSAML_Error_Exception('Cannot load PHP session with a specific ID.');
- }
- } elseif (session_id() === '') {
- $sessionId = self::getCookieSessionId();
- }
-
- if (!isset($_SESSION['SimpleSAMLphp_SESSION'])) {
- return NULL;
- }
-
- $session = $_SESSION['SimpleSAMLphp_SESSION'];
- assert('is_string($session)');
-
- $session = unserialize($session);
- assert('$session instanceof SimpleSAML_Session');
-
- return $session;
- }
-
-
- /**
- * Check whether the session cookie is set.
- *
- * This function will only return FALSE if is is certain that the cookie isn't set.
- *
- * @return bool TRUE if it was set, FALSE if not.
- */
- public function hasSessionCookie() {
-
- return array_key_exists($this->cookie_name, $_COOKIE);
- }
-
-
- /**
- * Get the cookie parameters that should be used for session cookies.
- *
- * This function contains some adjustments from the default to provide backwards-compatibility.
- *
- * @return array
- * @link http://www.php.net/manual/en/function.session-get-cookie-params.php
- */
- public function getCookieParams() {
-
- $config = SimpleSAML_Configuration::getInstance();
-
- $ret = parent::getCookieParams();
-
- if ($config->hasValue('session.phpsession.limitedpath') && $config->hasValue('session.cookie.path')) {
- throw new SimpleSAML_Error_Exception('You cannot set both the session.phpsession.limitedpath and session.cookie.path options.');
- } elseif ($config->hasValue('session.phpsession.limitedpath')) {
- $ret['path'] = $config->getBoolean('session.phpsession.limitedpath', FALSE) ? '/' . $config->getBaseURL() : '/';
- }
-
- $ret['httponly'] = $config->getBoolean('session.phpsession.httponly', TRUE);
-
- return $ret;
- }
-
+class SimpleSAML_SessionHandlerPHP extends SimpleSAML_SessionHandler
+{
+
+ /**
+ * This variable contains the session cookie name.
+ *
+ * @var string
+ */
+ protected $cookie_name;
+
+
+ /**
+ * Initialize the PHP session handling. This constructor is protected because it should only be called from
+ * SimpleSAML_SessionHandler::createSessionHandler(...).
+ */
+ protected function __construct()
+ {
+ // call the parent constructor in case it should become necessary in the future
+ parent::__construct();
+
+ /* Initialize the php session handling.
+ *
+ * If session_id() returns a blank string, then we need to call session start. Otherwise the session is already
+ * started, and we should avoid calling session_start().
+ */
+ if (session_id() === '') {
+ $config = SimpleSAML_Configuration::getInstance();
+
+ $params = $this->getCookieParams();
+
+ session_set_cookie_params(
+ $params['lifetime'],
+ $params['path'],
+ $params['domain'],
+ $params['secure'],
+ $params['httponly']
+ );
+
+ $this->cookie_name = $config->getString('session.phpsession.cookiename', null);
+ if (!empty($this->cookie_name)) {
+ session_name($this->cookie_name);
+ } else {
+ $this->cookie_name = session_name();
+ }
+
+ $savepath = $config->getString('session.phpsession.savepath', null);
+ if (!empty($savepath)) {
+ session_save_path($savepath);
+ }
+ }
+ }
+
+
+ /**
+ * Create and set new session id.
+ *
+ * @return string The new session id.
+ *
+ * @throws SimpleSAML_Error_Exception If the cookie is marked as secure but we are not using HTTPS, or the headers
+ * were already sent and therefore we cannot set the cookie.
+ */
+ public function newSessionId()
+ {
+ $session_cookie_params = session_get_cookie_params();
+
+ if ($session_cookie_params['secure'] && !\SimpleSAML\Utils\HTTP::isHTTPS()) {
+ throw new SimpleSAML_Error_Exception('Session start with secure cookie not allowed on http.');
+ }
+
+ if (headers_sent()) {
+ throw new SimpleSAML_Error_Exception('Cannot create new session - headers already sent.');
+ }
+
+ // generate new (secure) session id
+ $sessionId = bin2hex(openssl_random_pseudo_bytes(16));
+ SimpleSAML_Session::createSession($sessionId);
+
+ if (session_id() !== '') {
+ // session already started, close it
+ session_write_close();
+ }
+
+ session_id($sessionId);
+ session_start();
+
+ return session_id();
+ }
+
+
+ /**
+ * Retrieve the session id of saved in the session cookie.
+ *
+ * @return string The session id saved in the cookie.
+ *
+ * @throws SimpleSAML_Error_Exception If the cookie is marked as secure but we are not using HTTPS.
+ */
+ public function getCookieSessionId()
+ {
+ if (session_id() === '') {
+ if (!self::hasSessionCookie()) {
+ return self::newSessionId();
+ }
+
+ $session_cookie_params = session_get_cookie_params();
+
+ if ($session_cookie_params['secure'] && !\SimpleSAML\Utils\HTTP::isHTTPS()) {
+ throw new SimpleSAML_Error_Exception('Session start with secure cookie not allowed on http.');
+ }
+
+ session_start();
+ }
+
+ return session_id();
+ }
+
+
+ /**
+ * Retrieve the session cookie name.
+ *
+ * @return string The session cookie name.
+ */
+ public function getSessionCookieName()
+ {
+ return $this->cookie_name;
+ }
+
+
+ /**
+ * Save the current session to the PHP session array.
+ *
+ * @param SimpleSAML_Session $session The session object we should save.
+ */
+ public function saveSession(SimpleSAML_Session $session)
+ {
+ $_SESSION['SimpleSAMLphp_SESSION'] = serialize($session);
+ }
+
+
+ /**
+ * Load the session from the PHP session array.
+ *
+ * @param string|null $sessionId The ID of the session we should load, or null to use the default.
+ *
+ * @return SimpleSAML_Session|null The session object, or null if it doesn't exist.
+ *
+ * @throws SimpleSAML_Error_Exception If it wasn't possible to disable session cookies or we are trying to load a
+ * PHP session with a specific identifier and it doesn't match with the current session identifier.
+ */
+ public function loadSession($sessionId = null)
+ {
+ assert('is_string($sessionId) || is_null($sessionId)');
+
+ if ($sessionId !== null) {
+ if (session_id() === '') {
+ // session not initiated with getCookieSessionId(), start session without setting cookie
+ $ret = ini_set('session.use_cookies', '0');
+ if ($ret === false) {
+ throw new SimpleSAML_Error_Exception('Disabling PHP option session.use_cookies failed.');
+ }
+
+ session_id($sessionId);
+ session_start();
+ } elseif ($sessionId !== session_id()) {
+ throw new SimpleSAML_Error_Exception('Cannot load PHP session with a specific ID.');
+ }
+ } elseif (session_id() === '') {
+ self::getCookieSessionId();
+ }
+
+ if (!isset($_SESSION['SimpleSAMLphp_SESSION'])) {
+ return null;
+ }
+
+ $session = $_SESSION['SimpleSAMLphp_SESSION'];
+ assert('is_string($session)');
+
+ $session = unserialize($session);
+ assert('$session instanceof SimpleSAML_Session');
+
+ return $session;
+ }
+
+
+ /**
+ * Check whether the session cookie is set.
+ *
+ * This function will only return false if is is certain that the cookie isn't set.
+ *
+ * @return boolean True if it was set, false otherwise.
+ */
+ public function hasSessionCookie()
+ {
+ return array_key_exists($this->cookie_name, $_COOKIE);
+ }
+
+
+ /**
+ * Get the cookie parameters that should be used for session cookies.
+ *
+ * This function contains some adjustments from the default to provide backwards-compatibility.
+ *
+ * @return array The cookie parameters for our sessions.
+ * @link http://www.php.net/manual/en/function.session-get-cookie-params.php
+ *
+ * @throws SimpleSAML_Error_Exception If both 'session.phpsession.limitedpath' and 'session.cookie.path' options
+ * are set at the same time in the configuration.
+ */
+ public function getCookieParams()
+ {
+ $config = SimpleSAML_Configuration::getInstance();
+
+ $ret = parent::getCookieParams();
+
+ if ($config->hasValue('session.phpsession.limitedpath') && $config->hasValue('session.cookie.path')) {
+ throw new SimpleSAML_Error_Exception(
+ 'You cannot set both the session.phpsession.limitedpath and session.cookie.path options.'
+ );
+ } elseif ($config->hasValue('session.phpsession.limitedpath')) {
+ $ret['path'] = $config->getBoolean(
+ 'session.phpsession.limitedpath',
+ false
+ ) ? '/'.$config->getBaseURL() : '/';
+ }
+
+ $ret['httponly'] = $config->getBoolean('session.phpsession.httponly', true);
+
+ return $ret;
+ }
}
diff --git a/lib/SimpleSAML/SessionHandlerStore.php b/lib/SimpleSAML/SessionHandlerStore.php
index 02ccf6c..ecf7154 100644
--- a/lib/SimpleSAML/SessionHandlerStore.php
+++ b/lib/SimpleSAML/SessionHandlerStore.php
@@ -1,64 +1,75 @@
<?php
+
/**
- * Session storage in the datastore.
+ * Session storage in the data store.
*
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-class SimpleSAML_SessionHandlerStore extends SimpleSAML_SessionHandlerCookie {
+class SimpleSAML_SessionHandlerStore extends SimpleSAML_SessionHandlerCookie
+{
+
+ /**
+ * The data store we save the session to.
+ *
+ * @var SimpleSAML_Store
+ */
+ private $store;
- /**
- * The datastore we save the session to.
- */
- private $store;
- /**
- * Initialize the session handlerstore.
- */
- protected function __construct(SimpleSAML_Store $store) {
- parent::__construct();
+ /**
+ * Initialize the session.
+ *
+ * @param SimpleSAML_Store $store The store to use.
+ */
+ protected function __construct(SimpleSAML_Store $store)
+ {
+ parent::__construct();
- $this->store = $store;
- }
+ $this->store = $store;
+ }
- /**
- * Load the session from the datastore.
- *
- * @param string|NULL $sessionId The ID of the session we should load, or NULL to use the default.
- * @return SimpleSAML_Session|NULL The session object, or NULL if it doesn't exist.
- */
- public function loadSession($sessionId = NULL) {
- assert('is_string($sessionId) || is_null($sessionId)');
+ /**
+ * Load a session from the data store.
+ *
+ * @param string|null $sessionId The ID of the session we should load, or null to use the default.
+ *
+ * @return SimpleSAML_Session|null The session object, or null if it doesn't exist.
+ */
+ public function loadSession($sessionId = null)
+ {
+ assert('is_string($sessionId) || is_null($sessionId)');
- if ($sessionId === NULL) {
- $sessionId = $this->getCookieSessionId();
- }
+ if ($sessionId === null) {
+ $sessionId = $this->getCookieSessionId();
+ }
- $session = $this->store->get('session', $sessionId);
- if ($session !== NULL) {
- assert('$session instanceof SimpleSAML_Session');
- return $session;
- }
+ $session = $this->store->get('session', $sessionId);
+ if ($session !== null) {
+ assert('$session instanceof SimpleSAML_Session');
+ return $session;
+ }
- return NULL;
- }
+ return null;
+ }
- /**
- * Save the current session to the datastore.
- *
- * @param SimpleSAML_Session $session The session object we should save.
- */
- public function saveSession(SimpleSAML_Session $session) {
+ /**
+ * Save a session to the data store.
+ *
+ * @param SimpleSAML_Session $session The session object we should save.
+ */
+ public function saveSession(SimpleSAML_Session $session)
+ {
- $sessionId = $session->getSessionId();
+ $sessionId = $session->getSessionId();
- $config = SimpleSAML_Configuration::getInstance();
- $sessionDuration = $config->getInteger('session.duration', 8*60*60);
- $expire = time() + $sessionDuration;
+ $config = SimpleSAML_Configuration::getInstance();
+ $sessionDuration = $config->getInteger('session.duration', 8 * 60 * 60);
+ $expire = time() + $sessionDuration;
- $this->store->set('session', $sessionId, $session, $expire);
- }
+ $this->store->set('session', $sessionId, $session, $expire);
+ }
}
diff --git a/lib/SimpleSAML/Stats.php b/lib/SimpleSAML/Stats.php
index acaf1d8..42bd72c 100644
--- a/lib/SimpleSAML/Stats.php
+++ b/lib/SimpleSAML/Stats.php
@@ -1,91 +1,101 @@
<?php
+
/**
* Statistics handler class.
*
* This class is responsible for taking a statistics event and logging it.
*
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-class SimpleSAML_Stats {
-
- /**
- * Whether this class is initialized.
- * @var boolean
- */
- private static $initialized = FALSE;
-
-
- /**
- * The statistics output callbacks.
- * @var array
- */
- private static $outputs = NULL;
-
-
- /**
- * Create an output from a configuration object.
- *
- * @param SimpleSAML_Configuration $config The configuration object.
- * @return
- */
- private static function createOutput(SimpleSAML_Configuration $config) {
- $cls = $config->getString('class');
- $cls = SimpleSAML_Module::resolveClass($cls, 'Stats_Output', 'SimpleSAML_Stats_Output');
-
- $output = new $cls($config);
- return $output;
- }
-
-
- /**
- * Initialize the outputs.
- */
- private static function initOutputs() {
-
- $config = SimpleSAML_Configuration::getInstance();
- $outputCfgs = $config->getConfigList('statistics.out', array());
-
- self::$outputs = array();
- foreach ($outputCfgs as $cfg) {
- self::$outputs[] = self::createOutput($cfg);
- }
- }
-
-
- /**
- * Notify about an event.
- *
- * @param string $event The event.
- * @param array $data Event data. Optional.
- */
- public static function log($event, array $data = array()) {
- assert('is_string($event)');
- assert('!isset($data["op"])');
- assert('!isset($data["time"])');
- assert('!isset($data["_id"])');
-
- if (!self::$initialized) {
- self::initOutputs();
- self::$initialized = TRUE;
- }
-
- if (empty(self::$outputs)) {
- /* Not enabled. */
- return;
- }
-
- $data['op'] = $event;
- $data['time'] = microtime(TRUE);
-
- /* The ID generation is designed to cluster IDs related in time close together. */
- $int_t = (int)$data['time'];
- $hd = openssl_random_pseudo_bytes(16);
- $data['_id'] = sprintf('%016x%s', $int_t, bin2hex($hd));
-
- foreach (self::$outputs as $out) {
- $out->emit($data);
- }
- }
+class SimpleSAML_Stats
+{
+
+ /**
+ * Whether this class is initialized.
+ *
+ * @var boolean
+ */
+ private static $initialized = false;
+
+
+ /**
+ * The statistics output callbacks.
+ *
+ * @var array
+ */
+ private static $outputs = null;
+
+
+ /**
+ * Create an output from a configuration object.
+ *
+ * @param SimpleSAML_Configuration $config The configuration object.
+ *
+ * @return mixed A new instance of the configured class.
+ */
+ private static function createOutput(SimpleSAML_Configuration $config)
+ {
+ $cls = $config->getString('class');
+ $cls = SimpleSAML_Module::resolveClass($cls, 'Stats_Output', 'SimpleSAML_Stats_Output');
+
+ $output = new $cls($config);
+ return $output;
+ }
+
+
+ /**
+ * Initialize the outputs.
+ */
+ private static function initOutputs()
+ {
+
+ $config = SimpleSAML_Configuration::getInstance();
+ $outputCfgs = $config->getConfigList('statistics.out', array());
+
+ self::$outputs = array();
+ foreach ($outputCfgs as $cfg) {
+ self::$outputs[] = self::createOutput($cfg);
+ }
+ }
+
+
+ /**
+ * Notify about an event.
+ *
+ * @param string $event The event.
+ * @param array $data Event data. Optional.
+ *
+ * @return void|boolean False if output is not enabled, void otherwise.
+ */
+ public static function log($event, array $data = array())
+ {
+ assert('is_string($event)');
+ assert('!isset($data["op"])');
+ assert('!isset($data["time"])');
+ assert('!isset($data["_id"])');
+
+ if (!self::$initialized) {
+ self::initOutputs();
+ self::$initialized = true;
+ }
+
+ if (empty(self::$outputs)) {
+ // not enabled
+ return;
+ }
+
+ $data['op'] = $event;
+ $data['time'] = microtime(true);
+
+ // the ID generation is designed to cluster IDs related in time close together
+ $int_t = (int) $data['time'];
+ $hd = openssl_random_pseudo_bytes(16);
+ $data['_id'] = sprintf('%016x%s', $int_t, bin2hex($hd));
+
+ foreach (self::$outputs as $out) {
+ $out->emit($data);
+ }
+ }
}
diff --git a/lib/SimpleSAML/Stats/Output.php b/lib/SimpleSAML/Stats/Output.php
index b11c7a9..8b019c8 100644
--- a/lib/SimpleSAML/Stats/Output.php
+++ b/lib/SimpleSAML/Stats/Output.php
@@ -1,27 +1,29 @@
<?php
+
/**
* Interface for statistics outputs.
*
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-abstract class SimpleSAML_Stats_Output {
-
- /**
- * Initialize the output.
- *
- * @param SimpleSAML_Configuration $config The configuration for this output.
- */
- public function __construct(SimpleSAML_Configuration $config) {
- /* Do nothing by default. */
- }
+abstract class SimpleSAML_Stats_Output
+{
+ /**
+ * Initialize the output.
+ *
+ * @param SimpleSAML_Configuration $config The configuration for this output.
+ */
+ public function __construct(SimpleSAML_Configuration $config)
+ {
+ // do nothing by default
+ }
- /**
- * Write a stats event.
- *
- * @param array $data The event.
- */
- abstract public function emit(array $data);
+ /**
+ * Write a stats event.
+ *
+ * @param array $data The event.
+ */
+ abstract public function emit(array $data);
}
diff --git a/lib/SimpleSAML/Store.php b/lib/SimpleSAML/Store.php
index 2ea922d..3d638d8 100644
--- a/lib/SimpleSAML/Store.php
+++ b/lib/SimpleSAML/Store.php
@@ -1,88 +1,91 @@
<?php
+
/**
- * Base class for datastores.
+ * Base class for data stores.
*
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-abstract class SimpleSAML_Store {
-
- /**
- * Our singleton instance.
- *
- * This is FALSE if the datastore isn't enabled, and NULL
- * if we haven't attempted to initialize it.
- *
- * @var SimpleSAML_Store|FALSE|NULL
- */
- private static $instance;
-
-
- /**
- * Retrieve our singleton instance.
- *
- * @return SimpleSAML_Store|FALSE The datastore, or FALSE if it isn't enabled.
- */
- public static function getInstance() {
-
- if (self::$instance !== NULL) {
- return self::$instance;
- }
-
- $config = SimpleSAML_Configuration::getInstance();
- $storeType = $config->getString('store.type', NULL);
- if ($storeType === NULL) {
- $storeType = $config->getString('session.handler', 'phpsession');
- }
-
- switch ($storeType) {
- case 'phpsession':
- /* We cannot support advanced features with the PHP session store. */
- self::$instance = FALSE;
- break;
- case 'memcache':
- self::$instance = new SimpleSAML_Store_Memcache();
- break;
- case 'sql':
- self::$instance = new SimpleSAML_Store_SQL();
- break;
- default:
- /* Datastore from module. */
- $className = SimpleSAML_Module::resolveClass($storeType, 'Store', 'SimpleSAML_Store');
- self::$instance = new $className();
- }
-
- return self::$instance;
- }
-
-
- /**
- * Retrieve a value from the datastore.
- *
- * @param string $type The datatype.
- * @param string $key The key.
- * @return mixed|NULL The value.
- */
- abstract public function get($type, $key);
-
-
- /**
- * Save a value to the datastore.
- *
- * @param string $type The datatype.
- * @param string $key The key.
- * @param mixed $value The value.
- * @param int|NULL $expire The expiration time (unix timestamp), or NULL if it never expires.
- */
- abstract public function set($type, $key, $value, $expire = NULL);
-
-
- /**
- * Delete a value from the datastore.
- *
- * @param string $type The datatype.
- * @param string $key The key.
- */
- abstract public function delete($type, $key);
+abstract class SimpleSAML_Store
+{
+
+ /**
+ * Our singleton instance.
+ *
+ * This is false if the data store isn't enabled, and null if we haven't attempted to initialize it.
+ *
+ * @var SimpleSAML_Store|false|null
+ */
+ private static $instance;
+
+
+ /**
+ * Retrieve our singleton instance.
+ *
+ * @return SimpleSAML_Store|false The data store, or false if it isn't enabled.
+ */
+ public static function getInstance()
+ {
+
+ if (self::$instance !== null) {
+ return self::$instance;
+ }
+
+ $config = SimpleSAML_Configuration::getInstance();
+ $storeType = $config->getString('store.type', null);
+ if ($storeType === null) {
+ $storeType = $config->getString('session.handler', 'phpsession');
+ }
+
+ switch ($storeType) {
+ case 'phpsession':
+ // we cannot support advanced features with the PHP session store
+ self::$instance = false;
+ break;
+ case 'memcache':
+ self::$instance = new SimpleSAML_Store_Memcache();
+ break;
+ case 'sql':
+ self::$instance = new SimpleSAML_Store_SQL();
+ break;
+ default:
+ // datastore from module
+ $className = SimpleSAML_Module::resolveClass($storeType, 'Store', 'SimpleSAML_Store');
+ self::$instance = new $className();
+ }
+
+ return self::$instance;
+ }
+
+
+ /**
+ * Retrieve a value from the data store.
+ *
+ * @param string $type The data type.
+ * @param string $key The key.
+ *
+ * @return mixed|null The value.
+ */
+ abstract public function get($type, $key);
+
+
+ /**
+ * Save a value to the data store.
+ *
+ * @param string $type The data type.
+ * @param string $key The key.
+ * @param mixed $value The value.
+ * @param int|null $expire The expiration time (unix timestamp), or null if it never expires.
+ */
+ abstract public function set($type, $key, $value, $expire = null);
+
+
+ /**
+ * Delete a value from the data store.
+ *
+ * @param string $type The data type.
+ * @param string $key The key.
+ */
+ abstract public function delete($type, $key);
}
diff --git a/lib/SimpleSAML/Utilities.php b/lib/SimpleSAML/Utilities.php
index 8cf4d93..0313330 100644
--- a/lib/SimpleSAML/Utilities.php
+++ b/lib/SimpleSAML/Utilities.php
@@ -1,645 +1,729 @@
<?php
+
/**
* Misc static functions that is used several places.in example parsing and id generation.
*
* @author Andreas Åkre Solberg, UNINETT AS. <andreas.solberg@uninett.no>
* @package simpleSAMLphp
*/
-class SimpleSAML_Utilities {
-
- /**
- * List of log levels.
- *
- * This list is used to restore the log levels after some log levels are disabled.
- *
- * @var array
- */
- private static $logLevelStack = array();
-
-
- /**
- * The current mask of disabled log levels.
- *
- * Note: This mask is not directly related to the PHP error reporting level.
- *
- * @var int
- */
- public static $logMask = 0;
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getSelfHost() instead.
- */
- public static function getSelfHost() {
- return \SimpleSAML\Utils\HTTP::getSelfHost();
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getSelfURLHost() instead.
- */
- public static function selfURLhost() {
- return \SimpleSAML\Utils\HTTP::getSelfURLHost();
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::isHTTPS() instead.
- */
- public static function isHTTPS() {
- return \SimpleSAML\Utils\HTTP::isHTTPS();
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getSelfURLNoQuery() instead.
- */
- public static function selfURLNoQuery() {
- return \SimpleSAML\Utils\HTTP::getSelfURLNoQuery();
-
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getSelfHostWithPath() instead.
- */
- public static function getSelfHostWithPath() {
- return \SimpleSAML\Utils\HTTP::getSelfHostWithPath();
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getFirstPathElement() instead.
- */
- public static function getFirstPathElement($trailingslash = true) {
- return \SimpleSAML\Utils\HTTP::getFirstPathElement($trailingslash);
- }
+class SimpleSAML_Utilities
+{
+
+ /**
+ * List of log levels.
+ *
+ * This list is used to restore the log levels after some log levels are disabled.
+ *
+ * @var array
+ */
+ private static $logLevelStack = array();
+
+
+ /**
+ * The current mask of disabled log levels.
+ *
+ * Note: This mask is not directly related to the PHP error reporting level.
+ *
+ * @var int
+ */
+ public static $logMask = 0;
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getSelfHost() instead.
+ */
+ public static function getSelfHost()
+ {
+ return \SimpleSAML\Utils\HTTP::getSelfHost();
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getSelfURLHost() instead.
+ */
+ public static function selfURLhost()
+ {
+ return \SimpleSAML\Utils\HTTP::getSelfURLHost();
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::isHTTPS() instead.
+ */
+ public static function isHTTPS()
+ {
+ return \SimpleSAML\Utils\HTTP::isHTTPS();
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getSelfURLNoQuery()
+ * instead.
+ */
+ public static function selfURLNoQuery()
+ {
+ return \SimpleSAML\Utils\HTTP::getSelfURLNoQuery();
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getSelfHostWithPath()
+ * instead.
+ */
+ public static function getSelfHostWithPath()
+ {
+ return \SimpleSAML\Utils\HTTP::getSelfHostWithPath();
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getFirstPathElement()
+ * instead.
+ */
+ public static function getFirstPathElement($trailingslash = true)
+ {
+ return \SimpleSAML\Utils\HTTP::getFirstPathElement($trailingslash);
+ }
/**
* @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getSelfURL() instead.
*/
- public static function selfURL() {
- return \SimpleSAML\Utils\HTTP::getSelfURL();
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getBaseURL() instead.
- */
- public static function getBaseURL() {
- return \SimpleSAML\Utils\HTTP::getBaseURL();
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::addURLParameters() instead.
- */
- public static function addURLparameter($url, $parameters) {
- return \SimpleSAML\Utils\HTTP::addURLParameters($url, $parameters);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use \SimpleSAML\Utils\HTTP::checkURLAllowed() instead.
- */
- public static function checkURLAllowed($url, array $trustedSites = NULL) {
- return \SimpleSAML\Utils\HTTP::checkURLAllowed($url, $trustedSites);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML_Auth_State::parseStateID() instead.
- */
- public static function parseStateID($stateId) {
- return SimpleSAML_Auth_State::parseStateID($stateId);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0.
- */
- public static function checkDateConditions($start=NULL, $end=NULL) {
- $currentTime = time();
-
- if (!empty($start)) {
- $startTime = SAML2_Utils::xsDateTimeToTimestamp($start);
- /* Allow for a 10 minute difference in Time */
- if (($startTime < 0) || (($startTime - 600) > $currentTime)) {
- return FALSE;
- }
- }
- if (!empty($end)) {
- $endTime = SAML2_Utils::xsDateTimeToTimestamp($end);
- if (($endTime < 0) || ($endTime <= $currentTime)) {
- return FALSE;
- }
- }
- return TRUE;
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Random::generateID() instead.
- */
- public static function generateID() {
- return SimpleSAML\Utils\Random::generateID();
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use \SimpleSAML\Utils\Time::generateTimestamp() instead.
- */
- public static function generateTimestamp($instant = NULL) {
- return SimpleSAML\Utils\Time::generateTimestamp($instant);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use \SimpleSAML\Utils\Time::parseDuration() instead.
- */
- public static function parseDuration($duration, $timestamp = NULL) {
- return SimpleSAML\Utils\Time::parseDuration($duration, $timestamp);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please raise a SimpleSAML_Error_Error exception instead.
- */
- public static function fatalError($trackId = 'na', $errorCode = null, Exception $e = null) {
- throw new SimpleSAML_Error_Error($errorCode, $e);
- }
-
-
- /**
- * @deprecated This method will be removed in version 2.0. Use SimpleSAML\Utils\Net::ipCIDRcheck() instead.
- */
- static function ipCIDRcheck($cidr, $ip = null) {
- return SimpleSAML\Utils\Net::ipCIDRcheck($cidr, $ip);
- }
-
- private static function _doRedirect($url, $parameters = array()) {
- assert('is_string($url)');
- assert('!empty($url)');
- assert('is_array($parameters)');
-
- if (!empty($parameters)) {
- $url = self::addURLparameter($url, $parameters);
- }
-
- /* Set the HTTP result code. This is either 303 See Other or
- * 302 Found. HTTP 303 See Other is sent if the HTTP version
- * is HTTP/1.1 and the request type was a POST request.
- */
- if ($_SERVER['SERVER_PROTOCOL'] === 'HTTP/1.1' &&
- $_SERVER['REQUEST_METHOD'] === 'POST') {
- $code = 303;
- } else {
- $code = 302;
- }
-
- if (strlen($url) > 2048) {
- SimpleSAML_Logger::warning('Redirecting to a URL longer than 2048 bytes.');
- }
-
- /* Set the location header. */
- header('Location: ' . $url, TRUE, $code);
-
- /* Disable caching of this response. */
- header('Pragma: no-cache');
- header('Cache-Control: no-cache, must-revalidate');
-
- /* Show a minimal web page with a clickable link to the URL. */
- echo '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
- echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"' .
- ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' . "\n";
- echo '<html xmlns="http://www.w3.org/1999/xhtml">';
- echo '<head>
+ public static function selfURL()
+ {
+ return \SimpleSAML\Utils\HTTP::getSelfURL();
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getBaseURL() instead.
+ */
+ public static function getBaseURL()
+ {
+ return \SimpleSAML\Utils\HTTP::getBaseURL();
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::addURLParameters() instead.
+ */
+ public static function addURLparameter($url, $parameters)
+ {
+ return \SimpleSAML\Utils\HTTP::addURLParameters($url, $parameters);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use \SimpleSAML\Utils\HTTP::checkURLAllowed() instead.
+ */
+ public static function checkURLAllowed($url, array $trustedSites = null)
+ {
+ return \SimpleSAML\Utils\HTTP::checkURLAllowed($url, $trustedSites);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML_Auth_State::parseStateID() instead.
+ */
+ public static function parseStateID($stateId)
+ {
+ return SimpleSAML_Auth_State::parseStateID($stateId);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0.
+ */
+ public static function checkDateConditions($start = null, $end = null)
+ {
+ $currentTime = time();
+
+ if (!empty($start)) {
+ $startTime = SAML2_Utils::xsDateTimeToTimestamp($start);
+ /* Allow for a 10 minute difference in Time */
+ if (($startTime < 0) || (($startTime - 600) > $currentTime)) {
+ return false;
+ }
+ }
+ if (!empty($end)) {
+ $endTime = SAML2_Utils::xsDateTimeToTimestamp($end);
+ if (($endTime < 0) || ($endTime <= $currentTime)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Random::generateID() instead.
+ */
+ public static function generateID()
+ {
+ return SimpleSAML\Utils\Random::generateID();
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use \SimpleSAML\Utils\Time::generateTimestamp()
+ * instead.
+ */
+ public static function generateTimestamp($instant = null)
+ {
+ return SimpleSAML\Utils\Time::generateTimestamp($instant);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use \SimpleSAML\Utils\Time::parseDuration() instead.
+ */
+ public static function parseDuration($duration, $timestamp = null)
+ {
+ return SimpleSAML\Utils\Time::parseDuration($duration, $timestamp);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please raise a SimpleSAML_Error_Error exception instead.
+ */
+ public static function fatalError($trackId = 'na', $errorCode = null, Exception $e = null)
+ {
+ throw new SimpleSAML_Error_Error($errorCode, $e);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in version 2.0. Use SimpleSAML\Utils\Net::ipCIDRcheck() instead.
+ */
+ public static function ipCIDRcheck($cidr, $ip = null)
+ {
+ return SimpleSAML\Utils\Net::ipCIDRcheck($cidr, $ip);
+ }
+
+
+ private static function _doRedirect($url, $parameters = array())
+ {
+ assert('is_string($url)');
+ assert('!empty($url)');
+ assert('is_array($parameters)');
+
+ if (!empty($parameters)) {
+ $url = self::addURLparameter($url, $parameters);
+ }
+
+ /* Set the HTTP result code. This is either 303 See Other or
+ * 302 Found. HTTP 303 See Other is sent if the HTTP version
+ * is HTTP/1.1 and the request type was a POST request.
+ */
+ if ($_SERVER['SERVER_PROTOCOL'] === 'HTTP/1.1' &&
+ $_SERVER['REQUEST_METHOD'] === 'POST'
+ ) {
+ $code = 303;
+ } else {
+ $code = 302;
+ }
+
+ if (strlen($url) > 2048) {
+ SimpleSAML_Logger::warning('Redirecting to a URL longer than 2048 bytes.');
+ }
+
+ /* Set the location header. */
+ header('Location: '.$url, true, $code);
+
+ /* Disable caching of this response. */
+ header('Pragma: no-cache');
+ header('Cache-Control: no-cache, must-revalidate');
+
+ /* Show a minimal web page with a clickable link to the URL. */
+ echo '<?xml version="1.0" encoding="UTF-8"?>'."\n";
+ echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"'.
+ ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'."\n";
+ echo '<html xmlns="http://www.w3.org/1999/xhtml">';
+ echo '<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Redirect</title>
</head>';
- echo '<body>';
- echo '<h1>Redirect</h1>';
- echo '<p>';
- echo 'You were redirected to: ';
- echo '<a id="redirlink" href="' .
- htmlspecialchars($url) . '">' . htmlspecialchars($url) . '</a>';
- echo '<script type="text/javascript">document.getElementById("redirlink").focus();</script>';
- echo '</p>';
- echo '</body>';
- echo '</html>';
-
- /* End script execution. */
- exit;
- }
-
-
- /**
- * @deprecated 1.12.0 This method will be removed from the API. Instead, use the redirectTrustedURL() or
- * redirectUntrustedURL() functions accordingly.
- */
- public static function redirect($url, $parameters = array(), $allowed_redirect_hosts = NULL) {
- assert('is_string($url)');
- assert('strlen($url) > 0');
- assert('is_array($parameters)');
-
- if ($allowed_redirect_hosts !== NULL) {
- $url = self::checkURLAllowed($url, $allowed_redirect_hosts);
- } else {
- $url = self::normalizeURL($url);
- }
- self::_doRedirect($url, $parameters);
- }
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::redirectTrustedURL() instead.
- */
- public static function redirectTrustedURL($url, $parameters = array()) {
- \SimpleSAML\Utils\HTTP::redirectTrustedURL($url, $parameters);
- }
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::redirectUntrustedURL() instead.
- */
- public static function redirectUntrustedURL($url, $parameters = array()) {
- \SimpleSAML\Utils\HTTP::redirectUntrustedURL($url, $parameters);
- }
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Arrays::transpose() instead.
- */
- public static function transposeArray($in) {
- return SimpleSAML\Utils\Arrays::transpose($in);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::isDOMElementOfType() instead.
- */
- public static function isDOMElementOfType(DOMNode $element, $name, $nsURI) {
- return SimpleSAML\Utils\XML::isDOMElementOfType($element, $name, $nsURI);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::getDOMChildren() instead.
- */
- public static function getDOMChildren(DOMElement $element, $localName, $namespaceURI) {
- return SimpleSAML\Utils\XML::getDOMChildren($element, $localName, $namespaceURI);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::getDOMText() instead.
- */
- public static function getDOMText($element) {
- return SimpleSAML\Utils\XML::getDOMText($element);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getAcceptLanguage() instead.
- */
- public static function getAcceptLanguage() {
- return \SimpleSAML\Utils\HTTP::getAcceptLanguage();
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::isValid() instead.
- */
- public static function validateXML($xml, $schema) {
- $result = \SimpleSAML\Utils\XML::isValid($xml, $schema);
- return ($result === true) ? '' : $result;
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::checkSAMLMessage() instead.
- */
- public static function validateXMLDocument($message, $type) {
- \SimpleSAML\Utils\XML::checkSAMLMessage($message, $type);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use openssl_random_pseudo_bytes() instead.
- */
- public static function generateRandomBytes($length) {
- assert('is_int($length)');
-
- return openssl_random_pseudo_bytes($length);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use bin2hex() instead.
- */
- public static function stringToHex($bytes) {
- $ret = '';
- for($i = 0; $i < strlen($bytes); $i++) {
- $ret .= sprintf('%02x', ord($bytes[$i]));
- }
- return $ret;
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\System::resolvePath() instead.
- */
- public static function resolvePath($path, $base = NULL) {
- return \SimpleSAML\Utils\System::resolvePath($path, $base);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::resolveURL() instead.
- */
- public static function resolveURL($url, $base = NULL) {
- return \SimpleSAML\Utils\HTTP::resolveURL($url, $base);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::normalizeURL() instead.
- */
- public static function normalizeURL($url) {
- return \SimpleSAML\Utils\HTTP::normalizeURL($url);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::parseQueryString() instead.
- */
- public static function parseQueryString($query_string) {
- return \SimpleSAML\Utils\HTTP::parseQueryString($query_string);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use
- * SimpleSAML\Utils\Arrays::normalizeAttributesArray() instead.
- */
- public static function parseAttributes($attributes) {
- return SimpleSAML\Utils\Arrays::normalizeAttributesArray($attributes);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Config::getSecretSalt() instead.
- */
- public static function getSecretSalt() {
- return SimpleSAML\Utils\Config::getSecretSalt();
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please call error_get_last() directly.
- */
- public static function getLastError() {
-
- if (!function_exists('error_get_last')) {
- return '[Cannot get error message]';
- }
-
- $error = error_get_last();
- if ($error === NULL) {
- return '[No error message found]';
- }
-
- return $error['message'];
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Config::getCertPath() instead.
- */
- public static function resolveCert($path) {
- return \SimpleSAML\Utils\Config::getCertPath($path);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Crypto::loadPublicKey() instead.
- */
- public static function loadPublicKey(SimpleSAML_Configuration $metadata, $required = FALSE, $prefix = '') {
- return SimpleSAML\Utils\Crypto::loadPublicKey($metadata, $required, $prefix);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Crypto::loadPrivateKey() instead.
- */
- public static function loadPrivateKey(SimpleSAML_Configuration $metadata, $required = FALSE, $prefix = '') {
- return SimpleSAML\Utils\Crypto::loadPrivateKey($metadata, $required, $prefix);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::formatDOMElement() instead.
- */
- public static function formatDOMElement(DOMElement $root, $indentBase = '') {
- SimpleSAML\Utils\XML::formatDOMElement($root, $indentBase);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::formatXMLString() instead.
- */
- public static function formatXMLString($xml, $indentBase = '') {
- return SimpleSAML\Utils\XML::formatXMLString($xml, $indentBase);
- }
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Arrays::arrayize() instead.
- */
- public static function arrayize($data, $index = 0) {
- return SimpleSAML\Utils\Arrays::arrayize($data, $index);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Auth::isAdmin() instead.
- */
- public static function isAdmin() {
- return SimpleSAML\Utils\Auth::isAdmin();
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Auth::getAdminLoginURL instead();
- */
- public static function getAdminLoginURL($returnTo = NULL) {
- return SimpleSAML\Utils\Auth::getAdminLoginURL($returnTo);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Auth::requireAdmin() instead.
- */
- public static function requireAdmin() {
- \SimpleSAML\Utils\Auth::requireAdmin();
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::submitPOSTData() instead.
- */
- public static function postRedirect($destination, $post) {
- \SimpleSAML\Utils\HTTP::submitPOSTData($destination, $post);
- }
-
- /**
- * @deprecated This method will be removed in SSP 2.0. PLease use SimpleSAML\Utils\HTTP::getPOSTRedirectURL() instead.
- */
- public static function createPostRedirectLink($destination, $post) {
- return \SimpleSAML\Utils\HTTP::getPOSTRedirectURL($destination, $post);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getPOSTRedirectURL() instead.
- */
- public static function createHttpPostRedirectLink($destination, $post) {
- assert('is_string($destination)');
- assert('is_array($post)');
-
- $postId = SimpleSAML\Utils\Random::generateID();
- $postData = array(
- 'post' => $post,
- 'url' => $destination,
- );
+ echo '<body>';
+ echo '<h1>Redirect</h1>';
+ echo '<p>';
+ echo 'You were redirected to: ';
+ echo '<a id="redirlink" href="'.
+ htmlspecialchars($url).'">'.htmlspecialchars($url).'</a>';
+ echo '<script type="text/javascript">document.getElementById("redirlink").focus();</script>';
+ echo '</p>';
+ echo '</body>';
+ echo '</html>';
+
+ /* End script execution. */
+ exit;
+ }
+
+
+ /**
+ * @deprecated 1.12.0 This method will be removed from the API. Instead, use the redirectTrustedURL() or
+ * redirectUntrustedURL() functions accordingly.
+ */
+ public static function redirect($url, $parameters = array(), $allowed_redirect_hosts = null)
+ {
+ assert('is_string($url)');
+ assert('strlen($url) > 0');
+ assert('is_array($parameters)');
+
+ if ($allowed_redirect_hosts !== null) {
+ $url = self::checkURLAllowed($url, $allowed_redirect_hosts);
+ } else {
+ $url = self::normalizeURL($url);
+ }
+ self::_doRedirect($url, $parameters);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::redirectTrustedURL()
+ * instead.
+ */
+ public static function redirectTrustedURL($url, $parameters = array())
+ {
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL($url, $parameters);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::redirectUntrustedURL()
+ * instead.
+ */
+ public static function redirectUntrustedURL($url, $parameters = array())
+ {
+ \SimpleSAML\Utils\HTTP::redirectUntrustedURL($url, $parameters);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Arrays::transpose() instead.
+ */
+ public static function transposeArray($in)
+ {
+ return SimpleSAML\Utils\Arrays::transpose($in);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::isDOMElementOfType()
+ * instead.
+ */
+ public static function isDOMElementOfType(DOMNode $element, $name, $nsURI)
+ {
+ return SimpleSAML\Utils\XML::isDOMElementOfType($element, $name, $nsURI);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::getDOMChildren() instead.
+ */
+ public static function getDOMChildren(DOMElement $element, $localName, $namespaceURI)
+ {
+ return SimpleSAML\Utils\XML::getDOMChildren($element, $localName, $namespaceURI);
+ }
- $session = SimpleSAML_Session::getSessionFromRequest();
- $session->setData('core_postdatalink', $postId, $postData);
-
- $redirInfo = base64_encode(SimpleSAML\Utils\Crypto::aesEncrypt($session->getSessionId() . ':' . $postId));
-
- $url = SimpleSAML_Module::getModuleURL('core/postredirect.php', array('RedirInfo' => $redirInfo));
- $url = preg_replace("#^https:#", "http:", $url);
-
- return $url;
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0.
- */
- public static function validateCA($certificate, $caFile) {
- SimpleSAML_XML_Validator::validateCertificate($certificate, $caFile);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Time::initTimezone() instead.
- */
- public static function initTimezone() {
- \SimpleSAML\Utils\Time::initTimezone();
- }
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\System::writeFile() instead.
- */
- public static function writeFile($filename, $data, $mode=0600) {
- \SimpleSAML\Utils\System::writeFile($filename, $data, $mode);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\System::getTempDir instead.
- */
- public static function getTempDir() {
- return SimpleSAML\Utils\System::getTempDir();
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0.
- */
- public static function maskErrors($mask) {
- assert('is_int($mask)');
-
- $currentEnabled = error_reporting();
- self::$logLevelStack[] = array($currentEnabled, self::$logMask);
-
- $currentEnabled &= ~$mask;
- error_reporting($currentEnabled);
- self::$logMask |= $mask;
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0.
- */
- public static function popErrorMask() {
- $lastMask = array_pop(self::$logLevelStack);
- error_reporting($lastMask[0]);
- self::$logMask = $lastMask[1];
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Config\Metadata::getDefaultEndpoint() instead.
- */
- public static function getDefaultEndpoint(array $endpoints, array $bindings = NULL) {
- return \SimpleSAML\Utils\Config\Metadata::getDefaultEndpoint($endpoints, $bindings);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::checkSessionCookie() instead.
- */
- public static function checkCookie($retryURL = NULL) {
- \SimpleSAML\Utils\HTTP::checkSessionCookie($retryURL);
- }
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::getDOMText() instead.
+ */
+ public static function getDOMText($element)
+ {
+ return SimpleSAML\Utils\XML::getDOMText($element);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getAcceptLanguage()
+ * instead.
+ */
+ public static function getAcceptLanguage()
+ {
+ return \SimpleSAML\Utils\HTTP::getAcceptLanguage();
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::isValid() instead.
+ */
+ public static function validateXML($xml, $schema)
+ {
+ $result = \SimpleSAML\Utils\XML::isValid($xml, $schema);
+ return ($result === true) ? '' : $result;
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::checkSAMLMessage() instead.
+ */
+ public static function validateXMLDocument($message, $type)
+ {
+ \SimpleSAML\Utils\XML::checkSAMLMessage($message, $type);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use openssl_random_pseudo_bytes() instead.
+ */
+ public static function generateRandomBytes($length)
+ {
+ assert('is_int($length)');
+
+ return openssl_random_pseudo_bytes($length);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use bin2hex() instead.
+ */
+ public static function stringToHex($bytes)
+ {
+ $ret = '';
+ for ($i = 0; $i < strlen($bytes); $i++) {
+ $ret .= sprintf('%02x', ord($bytes[$i]));
+ }
+ return $ret;
+ }
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::debugSAMLMessage() instead.
- */
- public static function debugMessage($message, $type) {
- \SimpleSAML\Utils\XML::debugSAMLMessage($message, $type);
- }
-
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\System::resolvePath() instead.
+ */
+ public static function resolvePath($path, $base = null)
+ {
+ return \SimpleSAML\Utils\System::resolvePath($path, $base);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::resolveURL() instead.
+ */
+ public static function resolveURL($url, $base = null)
+ {
+ return \SimpleSAML\Utils\HTTP::resolveURL($url, $base);
+ }
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::fetch() instead.
- */
- public static function fetch($path, $context = array(), $getHeaders = FALSE) {
- return \SimpleSAML\Utils\HTTP::fetch($path, $context, $getHeaders);
- }
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Crypto::aesEncrypt() instead.
- */
- public static function aesEncrypt($clear) {
- return SimpleSAML\Utils\Crypto::aesEncrypt($clear);
- }
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::normalizeURL() instead.
+ */
+ public static function normalizeURL($url)
+ {
+ return \SimpleSAML\Utils\HTTP::normalizeURL($url);
+ }
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Crypto::aesDecrypt() instead.
- */
- public static function aesDecrypt($encData) {
- return SimpleSAML\Utils\Crypto::aesDecrypt($encData);
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\System::getOS() instead.
- */
- public static function isWindowsOS() {
- return SimpleSAML\Utils\System::getOS() === SimpleSAML\Utils\System::WINDOWS;
- }
-
-
- /**
- * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::setCookie() instead.
- */
- public static function setCookie($name, $value, array $params = NULL, $throw = TRUE) {
- \SimpleSAML\Utils\HTTP::setCookie($name, $value, $params, $throw);
- }
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::parseQueryString() instead.
+ */
+ public static function parseQueryString($query_string)
+ {
+ return \SimpleSAML\Utils\HTTP::parseQueryString($query_string);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use
+ * SimpleSAML\Utils\Arrays::normalizeAttributesArray() instead.
+ */
+ public static function parseAttributes($attributes)
+ {
+ return SimpleSAML\Utils\Arrays::normalizeAttributesArray($attributes);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Config::getSecretSalt() instead.
+ */
+ public static function getSecretSalt()
+ {
+ return SimpleSAML\Utils\Config::getSecretSalt();
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please call error_get_last() directly.
+ */
+ public static function getLastError()
+ {
+
+ if (!function_exists('error_get_last')) {
+ return '[Cannot get error message]';
+ }
+
+ $error = error_get_last();
+ if ($error === null) {
+ return '[No error message found]';
+ }
+
+ return $error['message'];
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Config::getCertPath() instead.
+ */
+ public static function resolveCert($path)
+ {
+ return \SimpleSAML\Utils\Config::getCertPath($path);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Crypto::loadPublicKey() instead.
+ */
+ public static function loadPublicKey(SimpleSAML_Configuration $metadata, $required = false, $prefix = '')
+ {
+ return SimpleSAML\Utils\Crypto::loadPublicKey($metadata, $required, $prefix);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Crypto::loadPrivateKey() instead.
+ */
+ public static function loadPrivateKey(SimpleSAML_Configuration $metadata, $required = false, $prefix = '')
+ {
+ return SimpleSAML\Utils\Crypto::loadPrivateKey($metadata, $required, $prefix);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::formatDOMElement() instead.
+ */
+ public static function formatDOMElement(DOMElement $root, $indentBase = '')
+ {
+ SimpleSAML\Utils\XML::formatDOMElement($root, $indentBase);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::formatXMLString() instead.
+ */
+ public static function formatXMLString($xml, $indentBase = '')
+ {
+ return SimpleSAML\Utils\XML::formatXMLString($xml, $indentBase);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Arrays::arrayize() instead.
+ */
+ public static function arrayize($data, $index = 0)
+ {
+ return SimpleSAML\Utils\Arrays::arrayize($data, $index);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Auth::isAdmin() instead.
+ */
+ public static function isAdmin()
+ {
+ return SimpleSAML\Utils\Auth::isAdmin();
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Auth::getAdminLoginURL instead();
+ */
+ public static function getAdminLoginURL($returnTo = null)
+ {
+ return SimpleSAML\Utils\Auth::getAdminLoginURL($returnTo);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Auth::requireAdmin() instead.
+ */
+ public static function requireAdmin()
+ {
+ \SimpleSAML\Utils\Auth::requireAdmin();
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::submitPOSTData() instead.
+ */
+ public static function postRedirect($destination, $post)
+ {
+ \SimpleSAML\Utils\HTTP::submitPOSTData($destination, $post);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. PLease use SimpleSAML\Utils\HTTP::getPOSTRedirectURL()
+ * instead.
+ */
+ public static function createPostRedirectLink($destination, $post)
+ {
+ return \SimpleSAML\Utils\HTTP::getPOSTRedirectURL($destination, $post);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getPOSTRedirectURL()
+ * instead.
+ */
+ public static function createHttpPostRedirectLink($destination, $post)
+ {
+ assert('is_string($destination)');
+ assert('is_array($post)');
+
+ $postId = SimpleSAML\Utils\Random::generateID();
+ $postData = array(
+ 'post' => $post,
+ 'url' => $destination,
+ );
+
+ $session = SimpleSAML_Session::getSessionFromRequest();
+ $session->setData('core_postdatalink', $postId, $postData);
+
+ $redirInfo = base64_encode(SimpleSAML\Utils\Crypto::aesEncrypt($session->getSessionId().':'.$postId));
+
+ $url = SimpleSAML_Module::getModuleURL('core/postredirect.php', array('RedirInfo' => $redirInfo));
+ $url = preg_replace("#^https:#", "http:", $url);
+
+ return $url;
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0.
+ */
+ public static function validateCA($certificate, $caFile)
+ {
+ SimpleSAML_XML_Validator::validateCertificate($certificate, $caFile);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Time::initTimezone() instead.
+ */
+ public static function initTimezone()
+ {
+ \SimpleSAML\Utils\Time::initTimezone();
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\System::writeFile() instead.
+ */
+ public static function writeFile($filename, $data, $mode = 0600)
+ {
+ \SimpleSAML\Utils\System::writeFile($filename, $data, $mode);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\System::getTempDir instead.
+ */
+ public static function getTempDir()
+ {
+ return SimpleSAML\Utils\System::getTempDir();
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0.
+ */
+ public static function maskErrors($mask)
+ {
+ assert('is_int($mask)');
+
+ $currentEnabled = error_reporting();
+ self::$logLevelStack[] = array($currentEnabled, self::$logMask);
+
+ $currentEnabled &= ~$mask;
+ error_reporting($currentEnabled);
+ self::$logMask |= $mask;
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0.
+ */
+ public static function popErrorMask()
+ {
+ $lastMask = array_pop(self::$logLevelStack);
+ error_reporting($lastMask[0]);
+ self::$logMask = $lastMask[1];
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use
+ * SimpleSAML\Utils\Config\Metadata::getDefaultEndpoint() instead.
+ */
+ public static function getDefaultEndpoint(array $endpoints, array $bindings = null)
+ {
+ return \SimpleSAML\Utils\Config\Metadata::getDefaultEndpoint($endpoints, $bindings);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::checkSessionCookie()
+ * instead.
+ */
+ public static function checkCookie($retryURL = null)
+ {
+ \SimpleSAML\Utils\HTTP::checkSessionCookie($retryURL);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::debugSAMLMessage() instead.
+ */
+ public static function debugMessage($message, $type)
+ {
+ \SimpleSAML\Utils\XML::debugSAMLMessage($message, $type);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::fetch() instead.
+ */
+ public static function fetch($path, $context = array(), $getHeaders = false)
+ {
+ return \SimpleSAML\Utils\HTTP::fetch($path, $context, $getHeaders);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Crypto::aesEncrypt() instead.
+ */
+ public static function aesEncrypt($clear)
+ {
+ return SimpleSAML\Utils\Crypto::aesEncrypt($clear);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Crypto::aesDecrypt() instead.
+ */
+ public static function aesDecrypt($encData)
+ {
+ return SimpleSAML\Utils\Crypto::aesDecrypt($encData);
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\System::getOS() instead.
+ */
+ public static function isWindowsOS()
+ {
+ return SimpleSAML\Utils\System::getOS() === SimpleSAML\Utils\System::WINDOWS;
+ }
+
+
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::setCookie() instead.
+ */
+ public static function setCookie($name, $value, array $params = null, $throw = true)
+ {
+ \SimpleSAML\Utils\HTTP::setCookie($name, $value, $params, $throw);
+ }
}
diff --git a/lib/SimpleSAML/Utils/Config.php b/lib/SimpleSAML/Utils/Config.php
index afbc208..c181ba9 100644
--- a/lib/SimpleSAML/Utils/Config.php
+++ b/lib/SimpleSAML/Utils/Config.php
@@ -59,10 +59,10 @@ class Config
/**
* Returns the path to the config dir
*
- * If the SIMPLSAMLPHP_CONFIG_DIR environment variable has been set, it takes precedence
- * over the default $simplesamldir/config directory.
+ * If the SIMPLESAMLPHP_CONFIG_DIR environment variable has been set, it takes precedence over the default
+ * $simplesamldir/config directory.
*
- * @return string
+ * @return string The path to the configuration directory.
*/
public static function getConfigDir()
{
@@ -70,6 +70,8 @@ class Config
$configDirEnv = getenv('SIMPLESAMLPHP_CONFIG_DIR');
if ($configDirEnv !== false) {
if (!is_dir($configDirEnv)) {
+ // this error is fatal, make sure the user can read it at least
+ header('Content-Type: text/plain');
throw new \InvalidArgumentException(
sprintf(
'Config directory specified by environment variable SIMPLESAMLPHP_CONFIG_DIR is not a ' .
diff --git a/lib/SimpleSAML/Utils/Config/Metadata.php b/lib/SimpleSAML/Utils/Config/Metadata.php
index 82c47ba..9ec4720 100644
--- a/lib/SimpleSAML/Utils/Config/Metadata.php
+++ b/lib/SimpleSAML/Utils/Config/Metadata.php
@@ -11,6 +11,22 @@ class Metadata
{
/**
+ * The string that identities Entity Categories.
+ *
+ * @var string
+ */
+ public static $ENTITY_CATEGORY = 'http://macedir.org/entity-category';
+
+
+ /**
+ * The string the identifies the REFEDS "Hide From Discovery" Entity Category.
+ *
+ * @var string
+ */
+ public static $HIDE_FROM_DISCOVERY = 'http://refeds.org/category/hide-from-discovery';
+
+
+ /**
* @var array The valid configuration options for a contact configuration array.
* @see "Metadata for the OASIS Security Assertion Markup Language (SAML) V2.0", section 2.3.2.2.
*/
@@ -72,7 +88,7 @@ class Metadata
* @return array An array holding valid contact configuration options. If a key 'name' was part of the input array,
* it will try to decompose the name into its parts, and place the parts into givenName and surName, if those are
* missing.
- * @throws \InvalidArgumentException If $contact is neither a string nor null, or the contact does not conform to
+ * @throws \InvalidArgumentException If $contact is neither an array nor null, or the contact does not conform to
* valid configuration rules for contacts.
*/
public static function getContact($contact)
@@ -157,7 +173,9 @@ class Metadata
if (empty($contact['telephoneNumber']) ||
!(is_string($contact['telephoneNumber']) || is_array($contact['telephoneNumber']))
) {
- throw new \InvalidArgumentException('"telephoneNumber" must be a string or an array and cannot be empty.');
+ throw new \InvalidArgumentException(
+ '"telephoneNumber" must be a string or an array and cannot be empty.'
+ );
}
if (is_array($contact['telephoneNumber'])) {
foreach ($contact['telephoneNumber'] as $address) {
@@ -223,4 +241,27 @@ class Metadata
*/
return $firstAllowed;
}
+
+
+ /**
+ * Determine if an entity should be hidden in the discovery service.
+ *
+ * This method searches for the "Hide From Discovery" REFEDS Entity Category, and tells if the entity should be
+ * hidden or not depending on it.
+ *
+ * @see https://refeds.org/category/hide-from-discovery
+ *
+ * @param array $metadata An associative array with the metadata representing an entity.
+ *
+ * @return boolean True if the entity should be hidden, false otherwise.
+ */
+ public static function isHiddenFromDiscovery($metadata)
+ {
+ if (array_key_exists(self::$ENTITY_CATEGORY, $metadata['EntityAttributes'])) {
+ if (in_array(self::$HIDE_FROM_DISCOVERY, $metadata['EntityAttributes'][self::$ENTITY_CATEGORY])) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/lib/SimpleSAML/Utils/Crypto.php b/lib/SimpleSAML/Utils/Crypto.php
index e09bbfe..269ed1b 100644
--- a/lib/SimpleSAML/Utils/Crypto.php
+++ b/lib/SimpleSAML/Utils/Crypto.php
@@ -11,88 +11,102 @@ class Crypto
{
/**
- * Decrypt data using AES and the system-wide secret salt as key.
+ * Decrypt data using AES-256-CBC and the key provided as a parameter.
*
- * @param string $ciphertext The encrypted data to decrypt.
+ * @param string $ciphertext The IV and the encrypted data, concatenated.
+ * @param string $secret The secret to use to decrypt the data.
*
* @return string The decrypted data.
* @htorws \InvalidArgumentException If $ciphertext is not a string.
- * @throws \SimpleSAML_Error_Exception If the mcrypt module is not loaded.
+ * @throws \SimpleSAML_Error_Exception If the openssl module is not loaded.
*
- * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no>
- * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ * @see \SimpleSAML\Utils\Crypto::aesDecrypt()
*/
- public static function aesDecrypt($ciphertext)
+ private static function _aesDecrypt($ciphertext, $secret)
{
if (!is_string($ciphertext)) {
throw new \InvalidArgumentException('Input parameter "$ciphertext" must be a string.');
}
- if (!function_exists("mcrypt_encrypt")) {
- throw new \SimpleSAML_Error_Exception("The mcrypt PHP module is not loaded.");
+ if (!function_exists("openssl_decrypt")) {
+ throw new \SimpleSAML_Error_Exception("The openssl PHP module is not loaded.");
}
- $enc = MCRYPT_RIJNDAEL_256;
- $mode = MCRYPT_MODE_CBC;
-
- $ivSize = mcrypt_get_iv_size($enc, $mode);
- $keySize = mcrypt_get_key_size($enc, $mode);
-
- $key = hash('sha256', Config::getSecretSalt(), true);
- $key = substr($key, 0, $keySize);
+ $raw = defined('OPENSSL_RAW_DATA') ? OPENSSL_RAW_DATA : true;
+ $key = openssl_digest($secret, 'sha256');
+ $method = 'AES-256-CBC';
+ $ivSize = 16;
+ $iv = substr($ciphertext, 0, $ivSize);
+ $data = substr($ciphertext, $ivSize);
- $iv = substr($ciphertext, 0, $ivSize);
- $data = substr($ciphertext, $ivSize);
-
- $clear = mcrypt_decrypt($enc, $key, $data, $mode, $iv);
+ return openssl_decrypt($data, $method, $key, $raw, $iv);
+ }
- $len = strlen($clear);
- $numpad = ord($clear[$len - 1]);
- $clear = substr($clear, 0, $len - $numpad);
- return $clear;
+ /**
+ * Decrypt data using AES-256-CBC and the system-wide secret salt as key.
+ *
+ * @param string $ciphertext The IV used and the encrypted data, concatenated.
+ *
+ * @return string The decrypted data.
+ * @htorws \InvalidArgumentException If $ciphertext is not a string.
+ * @throws \SimpleSAML_Error_Exception If the openssl module is not loaded.
+ *
+ * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no>
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ public static function aesDecrypt($ciphertext)
+ {
+ return self::_aesDecrypt($ciphertext, Config::getSecretSalt());
}
/**
- * Encrypt data using AES and the system-wide secret salt as key.
+ * Encrypt data using AES-256-CBC and the key provided as a parameter.
*
* @param string $data The data to encrypt.
+ * @param string $secret The secret to use to encrypt the data.
*
- * @return string The encrypted data and IV.
+ * @return string The IV and encrypted data concatenated.
* @throws \InvalidArgumentException If $data is not a string.
- * @throws \SimpleSAML_Error_Exception If the mcrypt module is not loaded.
+ * @throws \SimpleSAML_Error_Exception If the openssl module is not loaded.
*
- * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no>
- * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ * @see \SimpleSAML\Utils\Crypto::aesEncrypt()
*/
- public static function aesEncrypt($data)
+ private static function _aesEncrypt($data, $secret)
{
if (!is_string($data)) {
throw new \InvalidArgumentException('Input parameter "$data" must be a string.');
}
- if (!function_exists("mcrypt_encrypt")) {
- throw new \SimpleSAML_Error_Exception('The mcrypt PHP module is not loaded.');
- }
-
- $enc = MCRYPT_RIJNDAEL_256;
- $mode = MCRYPT_MODE_CBC;
-
- $blockSize = mcrypt_get_block_size($enc, $mode);
- $ivSize = mcrypt_get_iv_size($enc, $mode);
- $keySize = mcrypt_get_key_size($enc, $mode);
- $key = hash('sha256', Config::getSecretSalt(), true);
- $key = substr($key, 0, $keySize);
+ if (!function_exists("openssl_encrypt")) {
+ throw new \SimpleSAML_Error_Exception('The openssl PHP module is not loaded.');
+ }
- $len = strlen($data);
- $numpad = $blockSize - ($len % $blockSize);
- $data = str_pad($data, $len + $numpad, chr($numpad));
+ $raw = defined('OPENSSL_RAW_DATA') ? OPENSSL_RAW_DATA : true;
+ $key = openssl_digest($secret, 'sha256');
+ $method = 'AES-256-CBC';
+ $ivSize = 16;
+ $iv = substr($key, 0, $ivSize);
- $iv = openssl_random_pseudo_bytes($ivSize);
+ return $iv.openssl_encrypt($data, $method, $key, $raw, $iv);
+ }
- $data = mcrypt_encrypt($enc, $key, $data, $mode, $iv);
- return $iv.$data;
+ /**
+ * Encrypt data using AES-256-CBC and the system-wide secret salt as key.
+ *
+ * @param string $data The data to encrypt.
+ *
+ * @return string The IV and encrypted data concatenated.
+ * @throws \InvalidArgumentException If $data is not a string.
+ * @throws \SimpleSAML_Error_Exception If the openssl module is not loaded.
+ *
+ * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no>
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ public static function aesEncrypt($data)
+ {
+ return self::_aesEncrypt($data, Config::getSecretSalt());
}
@@ -270,7 +284,7 @@ class Crypto
}
// hash w/ salt
- if (!$salt) { // no salt provided, generate one
+ if ($salt === null) { // no salt provided, generate one
// default 8 byte salt, but 4 byte for LDAP SHA1 hashes
$bytes = ($algorithm == 'SSHA1') ? 4 : 8;
$salt = openssl_random_pseudo_bytes($bytes);
diff --git a/lib/SimpleSAML/Utils/Net.php b/lib/SimpleSAML/Utils/Net.php
index 4cf2c83..1ae6ced 100644
--- a/lib/SimpleSAML/Utils/Net.php
+++ b/lib/SimpleSAML/Utils/Net.php
@@ -23,7 +23,7 @@ class Net
* @author Brook Schofield, GÉANT
* @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
*/
- static function ipCIDRcheck($cidr, $ip = null)
+ public static function ipCIDRcheck($cidr, $ip = null)
{
if ($ip === null) {
$ip = $_SERVER['REMOTE_ADDR'];
@@ -34,6 +34,8 @@ class Net
list ($net, $mask) = explode('/', $cidr);
+ $ip_ip = array();
+ $ip_net = array();
if (strstr($ip, ':') || strstr($net, ':')) {
// Validate IPv6 with inet_pton, convert to hex with bin2hex
// then store as a long with hexdec
diff --git a/lib/SimpleSAML/Utils/System.php b/lib/SimpleSAML/Utils/System.php
index 8889251..6a9ec0e 100644
--- a/lib/SimpleSAML/Utils/System.php
+++ b/lib/SimpleSAML/Utils/System.php
@@ -114,10 +114,8 @@ class System
$base = $config->getBaseDir();
}
- // remove trailing slashes from $base
- while (substr($base, -1) === '/') {
- $base = substr($base, 0, -1);
- }
+ // remove trailing slashes
+ $base = rtrim($base, '/');
// check for absolute path
if (substr($path, 0, 1) === '/') {
diff --git a/lib/SimpleSAML/XHTML/IdPDisco.php b/lib/SimpleSAML/XHTML/IdPDisco.php
index 8b084f3..065e2e5 100644
--- a/lib/SimpleSAML/XHTML/IdPDisco.php
+++ b/lib/SimpleSAML/XHTML/IdPDisco.php
@@ -1,5 +1,6 @@
<?php
+
/**
* This class implements a generic IdP discovery service, for use in various IdP
* discovery service pages. This should reduce code duplication.
@@ -7,523 +8,591 @@
* Experimental support added for Extended IdP Metadata Discovery Protocol by Andreas 2008-08-28
* More information: http://rnd.feide.no/content/extended-identity-provider-discovery-service-protocol
*
+ * @author Jaime Pérez <jaime.perez@uninett.no>, UNINETT AS.
* @author Olav Morken, UNINETT AS.
* @author Andreas Åkre Solberg <andreas@uninett.no>, UNINETT AS.
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-class SimpleSAML_XHTML_IdPDisco {
-
- /**
- * An instance of the configuration class.
- */
- protected $config;
-
- /**
- * The identifier of this discovery service.
- *
- * @var string
- */
- protected $instance;
-
-
- /**
- * An instance of the metadata handler, which will allow us to fetch metadata about IdPs.
- */
- protected $metadata;
-
-
- /**
- * The users session.
- */
- protected $session;
-
-
- /**
- * The metadata sets we find allowed entities in, in prioritized order.
- *
- * @var array
- */
- protected $metadataSets;
-
-
- /**
- * The entity id of the SP which accesses this IdP discovery service.
- */
- protected $spEntityId;
-
- /**
- * HTTP parameter from the request, indicating whether the discovery service
- * can interact with the user or not.
- */
- protected $isPassive;
-
- /**
- * The SP request to set the IdPentityID...
- */
- protected $setIdPentityID = NULL;
-
-
- /**
- * The name of the query parameter which should contain the users choice of IdP.
- * This option default to 'entityID' for Shibboleth compatibility.
- */
- protected $returnIdParam;
-
- /**
- * The list of scoped idp's. The intersection between the metadata idpList
- * and scopedIDPList (given as a $_GET IDPList[] parameter) is presented to
- * the user. If the intersection is empty the metadata idpList is used.
- */
- protected $scopedIDPList = array();
-
- /**
- * The URL the user should be redirected to after choosing an IdP.
- */
- protected $returnURL;
-
-
- /**
- * Initializes this discovery service.
- *
- * The constructor does the parsing of the request. If this is an invalid request, it will
- * throw an exception.
- *
- * @param array $metadataSets Array with metadata sets we find remote entities in.
- * @param string $instance The name of this instance of the discovery service.
- */
- public function __construct(array $metadataSets, $instance) {
- assert('is_string($instance)');
-
- /* Initialize standard classes. */
- $this->config = SimpleSAML_Configuration::getInstance();
- $this->metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
- $this->session = SimpleSAML_Session::getSessionFromRequest();
- $this->instance = $instance;
- $this->metadataSets = $metadataSets;
-
- $this->log('Accessing discovery service.');
-
-
- /* Standard discovery service parameters. */
-
- if(!array_key_exists('entityID', $_GET)) {
- throw new Exception('Missing parameter: entityID');
- } else {
- $this->spEntityId = $_GET['entityID'];
- }
-
- if(!array_key_exists('returnIDParam', $_GET)) {
- $this->returnIdParam = 'entityID';
- } else {
- $this->returnIdParam = $_GET['returnIDParam'];
- }
-
- $this->log('returnIdParam initially set to [' . $this->returnIdParam . ']');
-
- if(!array_key_exists('return', $_GET)) {
- throw new Exception('Missing parameter: return');
- } else {
- $this->returnURL = \SimpleSAML\Utils\HTTP::checkURLAllowed($_GET['return']);
- }
-
- $this->isPassive = FALSE;
- if (array_key_exists('isPassive', $_GET)) {
- if ($_GET['isPassive'] === 'true') $this->isPassive = TRUE;
- }
- $this->log('isPassive initially set to [' . ($this->isPassive ? 'TRUE' : 'FALSE' ) . ']');
-
- if (array_key_exists('IdPentityID', $_GET)) {
- $this->setIdPentityID = $_GET['IdPentityID'];
- } else {
- $this->setIdPentityID = NULL;
- }
-
- if (array_key_exists('IDPList', $_REQUEST)) {
- $this->scopedIDPList = $_REQUEST['IDPList'];
- }
-
- }
-
-
- /**
- * Log a message.
- *
- * This is an helper function for logging messages. It will prefix the messages with our
- * discovery service type.
- *
- * @param $message The message which should be logged.
- */
- protected function log($message) {
- SimpleSAML_Logger::info('idpDisco.' . $this->instance . ': ' . $message);
- }
-
-
- /**
- * Retrieve cookie with the given name.
- *
- * This function will retrieve a cookie with the given name for the current discovery
- * service type.
- *
- * @param $name The name of the cookie.
- * @return The value of the cookie with the given name, or NULL if no cookie with that name exists.
- */
- protected function getCookie($name) {
- $prefixedName = 'idpdisco_' . $this->instance . '_' . $name;
- if(array_key_exists($prefixedName, $_COOKIE)) {
- return $_COOKIE[$prefixedName];
- } else {
- return NULL;
- }
- }
-
-
- /**
- * Save cookie with the given name and value.
- *
- * This function will save a cookie with the given name and value for the current discovery
- * service type.
- *
- * @param $name The name of the cookie.
- * @param $value The value of the cookie.
- */
- protected function setCookie($name, $value) {
- $prefixedName = 'idpdisco_' . $this->instance . '_' . $name;
-
- $params = array(
- /* We save the cookies for 90 days. */
- 'lifetime' => (60*60*24*90),
- /* The base path for cookies. This should be the installation directory for simpleSAMLphp. */
- 'path' => ('/' . $this->config->getBaseUrl()),
- 'httponly' => FALSE,
- );
-
- \SimpleSAML\Utils\HTTP::setCookie($prefixedName, $value, $params, FALSE);
- }
-
-
- /**
- * Validates the given IdP entity id.
- *
- * Takes a string with the IdP entity id, and returns the entity id if it is valid, or
- * NULL if not.
- *
- * @param $idp The entity id we want to validate. This can be NULL, in which case we will return NULL.
- * @return The entity id if it is valid, NULL if not.
- */
- protected function validateIdP($idp) {
- if($idp === NULL) {
- return NULL;
- }
-
- if(!$this->config->getBoolean('idpdisco.validate', TRUE)) {
- return $idp;
- }
-
- foreach ($this->metadataSets AS $metadataSet) {
- try {
- $this->metadata->getMetaData($idp, $metadataSet);
- return $idp;
- } catch(Exception $e) { }
- }
-
- $this->log('Unable to validate IdP entity id [' . $idp . '].');
- /* The entity id wasn't valid. */
- return NULL;
- }
-
-
- /**
- * Retrieve the users choice of IdP.
- *
- * This function finds out which IdP the user has manually chosen, if any.
- *
- * @return The entity id of the IdP the user has chosen, or NULL if the user has made no choice.
- */
- protected function getSelectedIdP() {
-
-
- /* Parameter set from the Extended IdP Metadata Discovery Service Protocol,
- * indicating that the user prefers this IdP.
- */
- if ($this->setIdPentityID) {
- return $this->validateIdP($this->setIdPentityID);
- }
-
- /* User has clicked on a link, or selected the IdP from a dropdown list. */
- if(array_key_exists('idpentityid', $_GET)) {
- return $this->validateIdP($_GET['idpentityid']);
- }
-
- /* Search for the IdP selection from the form used by the links view.
- * This form uses a name which equals idp_<entityid>, so we search for that.
- *
- * Unfortunately, php replaces periods in the name with underscores, and there
- * is no reliable way to get them back. Therefore we do some quick and dirty
- * parsing of the query string.
- */
- $qstr = $_SERVER['QUERY_STRING'];
- $matches = array();
- if(preg_match('/(?:^|&)idp_([^=]+)=/', $qstr, $matches)) {
- return $this->validateIdP(urldecode($matches[1]));
- }
-
- /* No IdP chosen. */
- return NULL;
- }
-
-
- /**
- * Retrieve the users saved choice of IdP.
- *
- * @return The entity id of the IdP the user has saved, or NULL if the user hasn't saved any choice.
- */
- protected function getSavedIdP() {
- if(!$this->config->getBoolean('idpdisco.enableremember', FALSE)) {
- /* Saving of IdP choices is disabled. */
- return NULL;
- }
-
- if($this->getCookie('remember') === '1') {
- $this->log('Return previously saved IdP because of remember cookie set to 1');
- return $this->getPreviousIdP();
- }
-
- if( $this->isPassive) {
- $this->log('Return previously saved IdP because of isPassive');
- return $this->getPreviousIdP();
- }
-
- return NULL;
- }
-
-
- /**
- * Retrieve the previous IdP the user used.
- *
- * @return The entity id of the previous IdP the user used, or NULL if this is the first time.
- */
- protected function getPreviousIdP() {
- return $this->validateIdP($this->getCookie('lastidp'));
- }
-
-
- /**
- * Retrieve a recommended IdP based on the IP address of the client.
- *
- * @return string|NULL The entity ID of the IdP if one is found, or NULL if not.
- */
- protected function getFromCIDRhint() {
-
- foreach ($this->metadataSets as $metadataSet) {
- $idp = $this->metadata->getPreferredEntityIdFromCIDRhint($metadataSet, $_SERVER['REMOTE_ADDR']);
- if (!empty($idp)) {
- return $idp;
- }
- }
-
- return NULL;
- }
-
-
- /**
- * Try to determine which IdP the user should most likely use.
- *
- * This function will first look at the previous IdP the user has chosen. If the user
- * hasn't chosen an IdP before, it will look at the IP address.
- *
- * @return The entity id of the IdP the user should most likely use.
- */
- protected function getRecommendedIdP() {
-
- $idp = $this->getPreviousIdP();
- if($idp !== NULL) {
- $this->log('Preferred IdP from previous use [' . $idp . '].');
- return $idp;
- }
-
- $idp = $this->getFromCIDRhint();
-
- if(!empty($idp)) {
- $this->log('Preferred IdP from CIDR hint [' . $idp . '].');
- return $idp;
- }
-
- return NULL;
- }
-
-
- /**
- * Save the current IdP choice to a cookie.
- *
- * @param string $idp The entityID of the IdP.
- */
- protected function setPreviousIdP($idp) {
- assert('is_string($idp)');
-
- $this->log('Choice made [' . $idp . '] Setting cookie.');
- $this->setCookie('lastidp', $idp);
- }
-
-
- /**
- * Determine whether the choice of IdP should be saved.
- *
- * @return TRUE if the choice should be saved, FALSE if not.
- */
- protected function saveIdP() {
- if(!$this->config->getBoolean('idpdisco.enableremember', FALSE)) {
- /* Saving of IdP choices is disabled. */
- return FALSE;
- }
-
- if(array_key_exists('remember', $_GET)) {
- return TRUE;
- }
- }
-
-
- /**
- * Determine which IdP the user should go to, if any.
- *
- * @return The entity id of the IdP the user should be sent to, or NULL if the user
- * should choose.
- */
- protected function getTargetIdP() {
-
- /* First, check if the user has chosen an IdP. */
- $idp = $this->getSelectedIdP();
- if($idp !== NULL) {
- /* The user selected this IdP. Save the choice in a cookie. */
- $this->setPreviousIdP($idp);
-
- if($this->saveIdP()) {
- $this->setCookie('remember', '1');
- } else {
- $this->setCookie('remember', '0');
- }
-
- return $idp;
- }
-
- $this->log('getSelectedIdP() returned NULL');
-
- /* Check if the user has saved an choice earlier. */
- $idp = $this->getSavedIdP();
- if($idp !== NULL) {
- $this->log('Using saved choice [' . $idp . '].');
- return $idp;
- }
-
- /* The user has made no choice. */
- return NULL;
- }
-
-
- /**
- * Retrieve the list of IdPs which are stored in the metadata.
- *
- * @return array Array with entityid=>metadata mappings.
- */
- protected function getIdPList() {
-
- $idpList = array();
- foreach ($this->metadataSets AS $metadataSet) {
- $newList = $this->metadata->getList($metadataSet);
- /*
- * Note that we merge the entities in reverse order. This ensuers
- * that it is the entity in the first metadata set that "wins" if
- * two metadata sets have the same entity.
- */
- $idpList = array_merge($newList, $idpList);
- }
-
- return $idpList;
- }
-
- /**
- * Return the list of scoped idp
- *
- * @return array Array of idp entities
- */
- protected function getScopedIDPList() {
- return $this->scopedIDPList;
- }
-
- /**
- * Handles a request to this discovery service.
- *
- * The IdP disco parameters should be set before calling this function.
- */
- public function handleRequest() {
-
- $idp = $this->getTargetIdp();
- if($idp !== NULL) {
-
- $extDiscoveryStorage = $this->config->getString('idpdisco.extDiscoveryStorage', NULL);
- if ($extDiscoveryStorage !== NULL) {
- $this->log('Choice made [' . $idp . '] (Forwarding to external discovery storage)');
- \SimpleSAML\Utils\HTTP::redirectTrustedURL($extDiscoveryStorage, array(
- 'entityID' => $this->spEntityId,
- 'IdPentityID' => $idp,
- 'returnIDParam' => $this->returnIdParam,
- 'isPassive' => 'true',
- 'return' => $this->returnURL
- ));
-
- } else {
- $this->log('Choice made [' . $idp . '] (Redirecting the user back. returnIDParam=' . $this->returnIdParam . ')');
- \SimpleSAML\Utils\HTTP::redirectTrustedURL($this->returnURL, array($this->returnIdParam => $idp));
- }
-
- return;
- }
-
- if ($this->isPassive) {
- $this->log('Choice not made. (Redirecting the user back without answer)');
- \SimpleSAML\Utils\HTTP::redirectTrustedURL($this->returnURL);
- return;
- }
-
- /* No choice made. Show discovery service page. */
-
- $idpList = $this->getIdPList();
- $preferredIdP = $this->getRecommendedIdP();
-
- $idpintersection = array_intersect(array_keys($idpList), $this->getScopedIDPList());
- if (sizeof($idpintersection) > 0) {
- $idpList = array_intersect_key($idpList, array_fill_keys($idpintersection, NULL));
- }
-
- $idpintersection = array_values($idpintersection);
-
- if(sizeof($idpintersection) == 1) {
- $this->log('Choice made [' . $idpintersection[0] . '] (Redirecting the user back. returnIDParam=' . $this->returnIdParam . ')');
- \SimpleSAML\Utils\HTTP::redirectTrustedURL($this->returnURL, array($this->returnIdParam => $idpintersection[0]));
- }
-
- /*
- * Make use of an XHTML template to present the select IdP choice to the user.
- * Currently the supported options is either a drop down menu or a list view.
- */
- switch($this->config->getString('idpdisco.layout', 'links')) {
- case 'dropdown':
- $templateFile = 'selectidp-dropdown.php';
- break;
- case 'links':
- $templateFile = 'selectidp-links.php';
- break;
- default:
- throw new Exception('Invalid value for the \'idpdisco.layout\' option.');
- }
-
- $t = new SimpleSAML_XHTML_Template($this->config, $templateFile, 'disco');
- $t->data['idplist'] = $idpList;
- $t->data['preferredidp'] = $preferredIdP;
- $t->data['return'] = $this->returnURL;
- $t->data['returnIDParam'] = $this->returnIdParam;
- $t->data['entityID'] = $this->spEntityId;
- $t->data['urlpattern'] = htmlspecialchars(\SimpleSAML\Utils\HTTP::getSelfURLNoQuery());
- $t->data['rememberenabled'] = $this->config->getBoolean('idpdisco.enableremember', FALSE);
- $t->show();
- }
+class SimpleSAML_XHTML_IdPDisco
+{
+
+ /**
+ * An instance of the configuration class.
+ *
+ * @var SimpleSAML_Configuration
+ */
+ protected $config;
+
+ /**
+ * The identifier of this discovery service.
+ *
+ * @var string
+ */
+ protected $instance;
+
+
+ /**
+ * An instance of the metadata handler, which will allow us to fetch metadata about IdPs.
+ *
+ * @var SimpleSAML_Metadata_MetaDataStorageHandler
+ */
+ protected $metadata;
+
+
+ /**
+ * The users session.
+ *
+ * @var SimpleSAML_Session
+ */
+ protected $session;
+
+
+ /**
+ * The metadata sets we find allowed entities in, in prioritized order.
+ *
+ * @var array
+ */
+ protected $metadataSets;
+
+
+ /**
+ * The entity id of the SP which accesses this IdP discovery service.
+ *
+ * @var string
+ */
+ protected $spEntityId;
+
+ /**
+ * HTTP parameter from the request, indicating whether the discovery service
+ * can interact with the user or not.
+ *
+ * @var boolean
+ */
+ protected $isPassive;
+
+ /**
+ * The SP request to set the IdPentityID...
+ *
+ * @var string|null
+ */
+ protected $setIdPentityID = null;
+
+
+ /**
+ * The name of the query parameter which should contain the users choice of IdP.
+ * This option default to 'entityID' for Shibboleth compatibility.
+ *
+ * @var string
+ */
+ protected $returnIdParam;
+
+ /**
+ * The list of scoped idp's. The intersection between the metadata idpList
+ * and scopedIDPList (given as a $_GET IDPList[] parameter) is presented to
+ * the user. If the intersection is empty the metadata idpList is used.
+ *
+ * @var array
+ */
+ protected $scopedIDPList = array();
+
+ /**
+ * The URL the user should be redirected to after choosing an IdP.
+ *
+ * @var string
+ */
+ protected $returnURL;
+
+
+ /**
+ * Initializes this discovery service.
+ *
+ * The constructor does the parsing of the request. If this is an invalid request, it will throw an exception.
+ *
+ * @param array $metadataSets Array with metadata sets we find remote entities in.
+ * @param string $instance The name of this instance of the discovery service.
+ *
+ * @throws Exception If the request is invalid.
+ */
+ public function __construct(array $metadataSets, $instance)
+ {
+ assert('is_string($instance)');
+
+ // initialize standard classes
+ $this->config = SimpleSAML_Configuration::getInstance();
+ $this->metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+ $this->session = SimpleSAML_Session::getSessionFromRequest();
+ $this->instance = $instance;
+ $this->metadataSets = $metadataSets;
+
+ $this->log('Accessing discovery service.');
+
+ // standard discovery service parameters
+ if (!array_key_exists('entityID', $_GET)) {
+ throw new Exception('Missing parameter: entityID');
+ } else {
+ $this->spEntityId = $_GET['entityID'];
+ }
+
+ if (!array_key_exists('returnIDParam', $_GET)) {
+ $this->returnIdParam = 'entityID';
+ } else {
+ $this->returnIdParam = $_GET['returnIDParam'];
+ }
+
+ $this->log('returnIdParam initially set to ['.$this->returnIdParam.']');
+
+ if (!array_key_exists('return', $_GET)) {
+ throw new Exception('Missing parameter: return');
+ } else {
+ $this->returnURL = \SimpleSAML\Utils\HTTP::checkURLAllowed($_GET['return']);
+ }
+
+ $this->isPassive = false;
+ if (array_key_exists('isPassive', $_GET)) {
+ if ($_GET['isPassive'] === 'true') {
+ $this->isPassive = true;
+ }
+ }
+ $this->log('isPassive initially set to ['.($this->isPassive ? 'TRUE' : 'FALSE').']');
+
+ if (array_key_exists('IdPentityID', $_GET)) {
+ $this->setIdPentityID = $_GET['IdPentityID'];
+ }
+
+ if (array_key_exists('IDPList', $_REQUEST)) {
+ $this->scopedIDPList = $_REQUEST['IDPList'];
+ }
+ }
+
+
+ /**
+ * Log a message.
+ *
+ * This is an helper function for logging messages. It will prefix the messages with our
+ * discovery service type.
+ *
+ * @param string $message The message which should be logged.
+ */
+ protected function log($message)
+ {
+ SimpleSAML_Logger::info('idpDisco.'.$this->instance.': '.$message);
+ }
+
+
+ /**
+ * Retrieve cookie with the given name.
+ *
+ * This function will retrieve a cookie with the given name for the current discovery
+ * service type.
+ *
+ * @param string $name The name of the cookie.
+ *
+ * @return string The value of the cookie with the given name, or null if no cookie with that name exists.
+ */
+ protected function getCookie($name)
+ {
+ $prefixedName = 'idpdisco_'.$this->instance.'_'.$name;
+ if (array_key_exists($prefixedName, $_COOKIE)) {
+ return $_COOKIE[$prefixedName];
+ } else {
+ return null;
+ }
+ }
+
+
+ /**
+ * Save cookie with the given name and value.
+ *
+ * This function will save a cookie with the given name and value for the current discovery
+ * service type.
+ *
+ * @param string $name The name of the cookie.
+ * @param string $value The value of the cookie.
+ */
+ protected function setCookie($name, $value)
+ {
+ $prefixedName = 'idpdisco_'.$this->instance.'_'.$name;
+
+ $params = array(
+ // we save the cookies for 90 days
+ 'lifetime' => (60 * 60 * 24 * 90),
+ // the base path for cookies. This should be the installation directory for SimpleSAMLphp
+ 'path' => ('/'.$this->config->getBaseUrl()),
+ 'httponly' => false,
+ );
+
+ \SimpleSAML\Utils\HTTP::setCookie($prefixedName, $value, $params, false);
+ }
+
+
+ /**
+ * Validates the given IdP entity id.
+ *
+ * Takes a string with the IdP entity id, and returns the entity id if it is valid, or
+ * null if not.
+ *
+ * @param string|null $idp The entity id we want to validate. This can be null, in which case we will return null.
+ *
+ * @return string|null The entity id if it is valid, null if not.
+ */
+ protected function validateIdP($idp)
+ {
+ if ($idp === null) {
+ return null;
+ }
+
+ if (!$this->config->getBoolean('idpdisco.validate', true)) {
+ return $idp;
+ }
+
+ foreach ($this->metadataSets as $metadataSet) {
+ try {
+ $this->metadata->getMetaData($idp, $metadataSet);
+ return $idp;
+ } catch (Exception $e) {
+ // continue
+ }
+ }
+
+ $this->log('Unable to validate IdP entity id ['.$idp.'].');
+
+ // the entity id wasn't valid
+ return null;
+ }
+
+
+ /**
+ * Retrieve the users choice of IdP.
+ *
+ * This function finds out which IdP the user has manually chosen, if any.
+ *
+ * @return string The entity id of the IdP the user has chosen, or null if the user has made no choice.
+ */
+ protected function getSelectedIdP()
+ {
+ /* Parameter set from the Extended IdP Metadata Discovery Service Protocol, indicating that the user prefers
+ * this IdP.
+ */
+ if (!empty($this->setIdPentityID)) {
+ return $this->validateIdP($this->setIdPentityID);
+ }
+
+ // user has clicked on a link, or selected the IdP from a drop-down list
+ if (array_key_exists('idpentityid', $_GET)) {
+ return $this->validateIdP($_GET['idpentityid']);
+ }
+
+ /* Search for the IdP selection from the form used by the links view. This form uses a name which equals
+ * idp_<entityid>, so we search for that.
+ *
+ * Unfortunately, php replaces periods in the name with underscores, and there is no reliable way to get them
+ * back. Therefore we do some quick and dirty parsing of the query string.
+ */
+ $qstr = $_SERVER['QUERY_STRING'];
+ $matches = array();
+ if (preg_match('/(?:^|&)idp_([^=]+)=/', $qstr, $matches)) {
+ return $this->validateIdP(urldecode($matches[1]));
+ }
+
+ // no IdP chosen
+ return null;
+ }
+
+
+ /**
+ * Retrieve the users saved choice of IdP.
+ *
+ * @return string The entity id of the IdP the user has saved, or null if the user hasn't saved any choice.
+ */
+ protected function getSavedIdP()
+ {
+ if (!$this->config->getBoolean('idpdisco.enableremember', false)) {
+ // saving of IdP choices is disabled
+ return null;
+ }
+
+ if ($this->getCookie('remember') === '1') {
+ $this->log('Return previously saved IdP because of remember cookie set to 1');
+ return $this->getPreviousIdP();
+ }
+
+ if ($this->isPassive) {
+ $this->log('Return previously saved IdP because of isPassive');
+ return $this->getPreviousIdP();
+ }
+
+ return null;
+ }
+
+
+ /**
+ * Retrieve the previous IdP the user used.
+ *
+ * @return string The entity id of the previous IdP the user used, or null if this is the first time.
+ */
+ protected function getPreviousIdP()
+ {
+ return $this->validateIdP($this->getCookie('lastidp'));
+ }
+
+
+ /**
+ * Retrieve a recommended IdP based on the IP address of the client.
+ *
+ * @return string|null The entity ID of the IdP if one is found, or null if not.
+ */
+ protected function getFromCIDRhint()
+ {
+ foreach ($this->metadataSets as $metadataSet) {
+ $idp = $this->metadata->getPreferredEntityIdFromCIDRhint($metadataSet, $_SERVER['REMOTE_ADDR']);
+ if (!empty($idp)) {
+ return $idp;
+ }
+ }
+
+ return null;
+ }
+
+
+ /**
+ * Try to determine which IdP the user should most likely use.
+ *
+ * This function will first look at the previous IdP the user has chosen. If the user
+ * hasn't chosen an IdP before, it will look at the IP address.
+ *
+ * @return string The entity id of the IdP the user should most likely use.
+ */
+ protected function getRecommendedIdP()
+ {
+ $idp = $this->getPreviousIdP();
+ if ($idp !== null) {
+ $this->log('Preferred IdP from previous use ['.$idp.'].');
+ return $idp;
+ }
+
+ $idp = $this->getFromCIDRhint();
+
+ if (!empty($idp)) {
+ $this->log('Preferred IdP from CIDR hint ['.$idp.'].');
+ return $idp;
+ }
+
+ return null;
+ }
+
+
+ /**
+ * Save the current IdP choice to a cookie.
+ *
+ * @param string $idp The entityID of the IdP.
+ */
+ protected function setPreviousIdP($idp)
+ {
+ assert('is_string($idp)');
+
+ $this->log('Choice made ['.$idp.'] Setting cookie.');
+ $this->setCookie('lastidp', $idp);
+ }
+
+
+ /**
+ * Determine whether the choice of IdP should be saved.
+ *
+ * @return boolean True if the choice should be saved, false otherwise.
+ */
+ protected function saveIdP()
+ {
+ if (!$this->config->getBoolean('idpdisco.enableremember', false)) {
+ // saving of IdP choices is disabled
+ return false;
+ }
+
+ if (array_key_exists('remember', $_GET)) {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Determine which IdP the user should go to, if any.
+ *
+ * @return string The entity id of the IdP the user should be sent to, or null if the user should choose.
+ */
+ protected function getTargetIdP()
+ {
+ // first, check if the user has chosen an IdP
+ $idp = $this->getSelectedIdP();
+ if ($idp !== null) {
+ // the user selected this IdP. Save the choice in a cookie
+ $this->setPreviousIdP($idp);
+
+ if ($this->saveIdP()) {
+ $this->setCookie('remember', '1');
+ } else {
+ $this->setCookie('remember', '0');
+ }
+
+ return $idp;
+ }
+
+ $this->log('getSelectedIdP() returned null');
+
+ // check if the user has saved an choice earlier
+ $idp = $this->getSavedIdP();
+ if ($idp !== null) {
+ $this->log('Using saved choice ['.$idp.'].');
+ return $idp;
+ }
+
+ // the user has made no choice
+ return null;
+ }
+
+
+ /**
+ * Retrieve the list of IdPs which are stored in the metadata.
+ *
+ * @return array An array with entityid => metadata mappings.
+ */
+ protected function getIdPList()
+ {
+ $idpList = array();
+ foreach ($this->metadataSets as $metadataSet) {
+ $newList = $this->metadata->getList($metadataSet);
+ /*
+ * Note that we merge the entities in reverse order. This ensures that it is the entity in the first
+ * metadata set that "wins" if two metadata sets have the same entity.
+ */
+ $idpList = array_merge($newList, $idpList);
+ }
+
+ return $idpList;
+ }
+
+
+ /**
+ * Return the list of scoped idp
+ *
+ * @return array An array of IdP entities
+ */
+ protected function getScopedIDPList()
+ {
+ return $this->scopedIDPList;
+ }
+
+
+ /**
+ * Filter the list of IdPs.
+ *
+ * This method returns the IdPs that comply with the following conditions:
+ * - The IdP does not have the 'hide.from.discovery' configuration option.
+ *
+ * @param array $list An associative array containing metadata for the IdPs to apply the filtering to.
+ *
+ * @return array An associative array containing metadata for the IdPs that were not filtered out.
+ */
+ protected function filterList($list)
+ {
+ foreach ($list as $entity => $metadata) {
+ if (array_key_exists('hide.from.discovery', $metadata) && $metadata['hide.from.discovery'] === true) {
+ unset($list[$entity]);
+ }
+ }
+ return $list;
+ }
+
+
+ /**
+ * Check if an IdP is set or if the request is passive, and redirect accordingly.
+ *
+ * @return void If there is no IdP targeted and this is not a passive request.
+ */
+ protected function start()
+ {
+ $idp = $this->getTargetIdp();
+ if ($idp !== null) {
+ $extDiscoveryStorage = $this->config->getString('idpdisco.extDiscoveryStorage', null);
+ if ($extDiscoveryStorage !== null) {
+ $this->log('Choice made ['.$idp.'] (Forwarding to external discovery storage)');
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL($extDiscoveryStorage, array(
+ 'entityID' => $this->spEntityId,
+ 'IdPentityID' => $idp,
+ 'returnIDParam' => $this->returnIdParam,
+ 'isPassive' => 'true',
+ 'return' => $this->returnURL
+ ));
+ } else {
+ $this->log(
+ 'Choice made ['.$idp.'] (Redirecting the user back. returnIDParam='.$this->returnIdParam.')'
+ );
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL($this->returnURL, array($this->returnIdParam => $idp));
+ }
+ }
+
+ if ($this->isPassive) {
+ $this->log('Choice not made. (Redirecting the user back without answer)');
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL($this->returnURL);
+ }
+ }
+
+
+ /**
+ * Handles a request to this discovery service.
+ *
+ * The IdP disco parameters should be set before calling this function.
+ */
+ public function handleRequest()
+ {
+ $this->start();
+
+ // no choice made. Show discovery service page
+ $idpList = $this->getIdPList();
+ $idpList = $this->filterList($idpList);
+ $preferredIdP = $this->getRecommendedIdP();
+
+ $idpintersection = array_intersect(array_keys($idpList), $this->getScopedIDPList());
+ if (sizeof($idpintersection) > 0) {
+ $idpList = array_intersect_key($idpList, array_fill_keys($idpintersection, null));
+ }
+
+ $idpintersection = array_values($idpintersection);
+
+ if (sizeof($idpintersection) == 1) {
+ $this->log(
+ 'Choice made ['.$idpintersection[0].'] (Redirecting the user back. returnIDParam='.
+ $this->returnIdParam.')'
+ );
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL(
+ $this->returnURL,
+ array($this->returnIdParam => $idpintersection[0])
+ );
+ }
+
+ /*
+ * Make use of an XHTML template to present the select IdP choice to the user. Currently the supported options
+ * is either a drop down menu or a list view.
+ */
+ switch ($this->config->getString('idpdisco.layout', 'links')) {
+ case 'dropdown':
+ $templateFile = 'selectidp-dropdown.php';
+ break;
+ case 'links':
+ $templateFile = 'selectidp-links.php';
+ break;
+ default:
+ throw new Exception('Invalid value for the \'idpdisco.layout\' option.');
+ }
+
+ $t = new SimpleSAML_XHTML_Template($this->config, $templateFile, 'disco');
+ $t->data['idplist'] = $idpList;
+ $t->data['preferredidp'] = $preferredIdP;
+ $t->data['return'] = $this->returnURL;
+ $t->data['returnIDParam'] = $this->returnIdParam;
+ $t->data['entityID'] = $this->spEntityId;
+ $t->data['urlpattern'] = htmlspecialchars(\SimpleSAML\Utils\HTTP::getSelfURLNoQuery());
+ $t->data['rememberenabled'] = $this->config->getBoolean('idpdisco.enableremember', false);
+ $t->show();
+ }
}
diff --git a/lib/SimpleSAML/XHTML/Template.php b/lib/SimpleSAML/XHTML/Template.php
index 68275de..f0ecda8 100644
--- a/lib/SimpleSAML/XHTML/Template.php
+++ b/lib/SimpleSAML/XHTML/Template.php
@@ -8,710 +8,704 @@
*/
class SimpleSAML_XHTML_Template {
- /**
- * This is the default language map. It is used to map languages codes from the user agent to
- * other language codes.
- */
- private static $defaultLanguageMap = array('nb' => 'no');
-
-
- private $configuration = null;
- private $template = 'default.php';
- private $availableLanguages = array('en');
- private $language = null;
-
- private $langtext = array();
-
- public $data = null;
-
-
- /**
- * Associative array of dictionaries.
- */
- private $dictionaries = array();
-
-
- /**
- * The default dictionary.
- */
- private $defaultDictionary = NULL;
-
-
- /**
- * HTTP GET language parameter name.
- */
- private $languageParameterName = 'language';
-
-
- /**
- * Constructor
- *
- * @param $configuration Configuration object
- * @param $template Which template file to load
- * @param $defaultDictionary The default dictionary where tags will come from.
- */
- function __construct(SimpleSAML_Configuration $configuration, $template, $defaultDictionary = NULL) {
- $this->configuration = $configuration;
- $this->template = $template;
-
- $this->data['baseurlpath'] = $this->configuration->getBaseURL();
-
- $this->availableLanguages = $this->configuration->getArray('language.available', array('en'));
-
- $this->languageParameterName = $this->configuration->getString('language.parameter.name', 'language');
- if (isset($_GET[$this->languageParameterName])) {
- $this->setLanguage($_GET[$this->languageParameterName], $this->configuration->getBoolean('language.parameter.setcookie', TRUE));
- }
-
- if($defaultDictionary !== NULL && substr($defaultDictionary, -4) === '.php') {
- /* For backwards compatibility - print warning. */
- $backtrace = debug_backtrace();
- $where = $backtrace[0]['file'] . ':' . $backtrace[0]['line'];
- SimpleSAML_Logger::warning('Deprecated use of new SimpleSAML_Template(...) at ' . $where .
- '. The last parameter is now a dictionary name, which should not end in ".php".');
-
- $this->defaultDictionary = substr($defaultDictionary, 0, -4);
- } else {
- $this->defaultDictionary = $defaultDictionary;
- }
- }
-
- /**
- * setLanguage() will set a cookie for the user's browser to remember what language
- * was selected
- *
- * @param $language Language code for the language to set.
- */
- public function setLanguage($language, $setLanguageCookie = TRUE) {
- $language = strtolower($language);
- if (in_array($language, $this->availableLanguages, TRUE)) {
- $this->language = $language;
- if ($setLanguageCookie === TRUE) {
- SimpleSAML_XHTML_Template::setLanguageCookie($language);
- }
- }
- }
-
- /**
- * getLanguage() will return the language selected by the user, or the default language
- * This function first looks for a cached language code,
- * then checks for a language cookie,
- * then it tries to calculate the preferred language from HTTP headers.
- * Last it returns the default language.
- */
- public function getLanguage() {
-
- // Language is set in object
- if (isset($this->language)) {
- return $this->language;
- }
-
- // Run custom getLanguage function if defined
- $customFunction = $this->configuration->getArray('language.get_language_function', NULL);
- if (isset($customFunction)) {
- assert('is_callable($customFunction)');
- $customLanguage = call_user_func($customFunction, $this);
- if ($customLanguage !== NULL && $customLanguage !== FALSE) {
- return $customLanguage;
- }
- }
-
- // Language is provided in a stored COOKIE
- $languageCookie = SimpleSAML_XHTML_Template::getLanguageCookie();
- if ($languageCookie !== NULL) {
- $this->language = $languageCookie;
- return $languageCookie;
- }
-
- /* Check if we can find a good language from the Accept-Language http header. */
- $httpLanguage = $this->getHTTPLanguage();
- if ($httpLanguage !== NULL) {
- return $httpLanguage;
- }
-
- // Language is not set, and we get the default language from the configuration.
- return $this->getDefaultLanguage();
- }
-
-
- /**
- * This function gets the prefered language for the user based on the Accept-Language http header.
- *
- * @return The prefered language based on the Accept-Language http header, or NULL if none of the
- * languages in the header were available.
- */
- private function getHTTPLanguage() {
- $languageScore = \SimpleSAML\Utils\HTTP::getAcceptLanguage();
-
- /* For now we only use the default language map. We may use a configurable language map
- * in the future.
- */
- $languageMap = self::$defaultLanguageMap;
-
- /* Find the available language with the best score. */
- $bestLanguage = NULL;
- $bestScore = -1.0;
-
- foreach($languageScore as $language => $score) {
-
- /* Apply the language map to the language code. */
- if(array_key_exists($language, $languageMap)) {
- $language = $languageMap[$language];
- }
-
- if(!in_array($language, $this->availableLanguages, TRUE)) {
- /* Skip this language - we don't have it. */
- continue;
- }
-
- /* Some user agents use very limited precicion of the quality value, but order the
- * elements in descending order. Therefore we rely on the order of the output from
- * getAcceptLanguage() matching the order of the languages in the header when two
- * languages have the same quality.
- */
- if($score > $bestScore) {
- $bestLanguage = $language;
- $bestScore = $score;
- }
- }
-
- return $bestLanguage;
- }
-
-
- /**
- * Returns the language default (from configuration)
- */
- private function getDefaultLanguage() {
- return $this->configuration->getString('language.default', 'en');
- }
-
- /**
- * Returns a list of all available languages.
- */
- private function getLanguageList() {
- $thisLang = $this->getLanguage();
- $lang = array();
- foreach ($this->availableLanguages AS $nl) {
- $lang[$nl] = ($nl == $thisLang);
- }
- return $lang;
- }
-
- /**
- * Return TRUE if language is Right-to-Left.
- */
- private function isLanguageRTL() {
- $rtlLanguages = $this->configuration->getArray('language.rtl', array());
- $thisLang = $this->getLanguage();
- if (in_array($thisLang, $rtlLanguages)) {
- return TRUE;
- }
- return FALSE;
- }
-
- /**
- * Includs a file relative to the template base directory.
- * This function can be used to include headers and footers etc.
- *
- */
- private function includeAtTemplateBase($file) {
- $data = $this->data;
-
- $filename = $this->findTemplatePath($file);
-
- include($filename);
- }
-
-
- /**
- * Retrieve a dictionary.
- *
- * This function retrieves a dictionary with the given name.
- *
- * @param $name The name of the dictionary, as the filename in the dictionary directory,
- * without the '.php'-ending.
- * @return An associative array with the dictionary.
- */
- private function getDictionary($name) {
- assert('is_string($name)');
-
- if(!array_key_exists($name, $this->dictionaries)) {
- $sepPos = strpos($name, ':');
- if($sepPos !== FALSE) {
- $module = substr($name, 0, $sepPos);
- $fileName = substr($name, $sepPos + 1);
- $dictDir = SimpleSAML_Module::getModuleDir($module) . '/dictionaries/';
- } else {
- $dictDir = $this->configuration->getPathValue('dictionarydir', 'dictionaries/');
- $fileName = $name;
- }
-
- $this->dictionaries[$name] = $this->readDictionaryFile($dictDir . $fileName);
- }
-
- return $this->dictionaries[$name];
- }
-
-
- /**
- * Retrieve a tag.
- *
- * This function retrieves a tag as an array with language => string mappings.
- *
- * @param $tag The tag name. The tag name can also be on the form '{<dictionary>:<tag>}', to retrieve
- * a tag from the specific dictionary.
- * @return As associative array with language => string mappings, or NULL if the tag wasn't found.
- */
- public function getTag($tag) {
- assert('is_string($tag)');
-
- /* First check translations loaded by the includeInlineTranslation and includeLanguageFile methods. */
- if(array_key_exists($tag, $this->langtext)) {
- return $this->langtext[$tag];
- }
-
- /* Check whether we should use the default dictionary or a dictionary specified in the tag. */
- if(substr($tag, 0, 1) === '{' && preg_match('/^{((?:\w+:)?\w+?):(.*)}$/D', $tag, $matches)) {
- $dictionary = $matches[1];
- $tag = $matches[2];
- } else {
- $dictionary = $this->defaultDictionary;
- if($dictionary === NULL) {
- /* We don't have any dictionary to load the tag from. */
- return NULL;
- }
- }
-
- $dictionary = $this->getDictionary($dictionary);
- if(!array_key_exists($tag, $dictionary)) {
- return NULL;
- }
-
- return $dictionary[$tag];
- }
-
-
- /**
- * Retrieve the preferred translation of a given text.
- *
- * @param $translations The translations, as an associative array with language => text mappings.
- * @return The preferred translation.
- */
- public function getTranslation($translations) {
- assert('is_array($translations)');
-
- /* Look up translation of tag in the selected language. */
- $selected_language = $this->getLanguage();
- if (array_key_exists($selected_language, $translations)) {
- return $translations[$selected_language];
- }
-
- /* Look up translation of tag in the default language. */
- $default_language = $this->getDefaultLanguage();
- if(array_key_exists($default_language, $translations)) {
- return $translations[$default_language];
- }
-
- /* Check for english translation. */
- if(array_key_exists('en', $translations)) {
- return $translations['en'];
- }
-
- /* Pick the first translation available. */
- if(count($translations) > 0) {
- $languages = array_keys($translations);
- return $translations[$languages[0]];
- }
-
- /* We don't have anything to return. */
- throw new Exception('Nothing to return from translation.');
- }
-
-
- /**
- * Translate a attribute name.
- *
- * @param string $name The attribute name.
- * @return string The translated attribute name, or the original attribute name if no translation was found.
- */
- public function getAttributeTranslation($name) {
-
- /* Normalize attribute name. */
- $normName = strtolower($name);
- $normName = str_replace(":", "_", $normName);
-
- /* Check for an extra dictionary. */
- $extraDict = $this->configuration->getString('attributes.extradictionary', NULL);
- if ($extraDict !== NULL) {
- $dict = $this->getDictionary($extraDict);
- if (array_key_exists($normName, $dict)) {
- return $this->getTranslation($dict[$normName]);
- }
- }
-
- /* Search the default attribute dictionary. */
- $dict = $this->getDictionary('attributes');
- if (array_key_exists('attribute_' . $normName, $dict)) {
- return $this->getTranslation($dict['attribute_' . $normName]);
- }
-
- /* No translations found. */
- return $name;
- }
-
-
- /**
- * Translate a tag into the current language, with a fallback to english.
- *
- * This function is used to look up a translation tag in dictionaries, and return the
- * translation into the current language. If no translation into the current language can be
- * found, english will be tried, and if that fails, placeholder text will be returned.
- *
- * An array can be passed as the tag. In that case, the array will be assumed to be on the
- * form (language => text), and will be used as the source of translations.
- *
- * This function can also do replacements into the translated tag. It will search the
- * translated tag for the keys provided in $replacements, and replace any found occurances
- * with the value of the key.
- *
- * @param string|array $tag A tag name for the translation which should be looked up, or an
- * array with (language => text) mappings.
- * @param array $replacements An associative array of keys that should be replaced with
- * values in the translated string.
- * @return string The translated tag, or a placeholder value if the tag wasn't found.
- */
- public function t($tag, $replacements = array(), $fallbackdefault = true, $oldreplacements = array(), $striptags = FALSE) {
- if(!is_array($replacements)) {
-
- /* Old style call to t(...). Print warning to log. */
- $backtrace = debug_backtrace();
- $where = $backtrace[0]['file'] . ':' . $backtrace[0]['line'];
- SimpleSAML_Logger::warning('Deprecated use of SimpleSAML_Template::t(...) at ' . $where .
- '. Please update the code to use the new style of parameters.');
-
- /* For backwards compatibility. */
- if(!$replacements && $this->getTag($tag) === NULL) {
- SimpleSAML_Logger::warning('Code which uses $fallbackdefault === FALSE shouls be' .
- ' updated to use the getTag-method instead.');
- return NULL;
- }
-
- $replacements = $oldreplacements;
- }
-
- if(is_array($tag)) {
- $tagData = $tag;
- } else {
- $tagData = $this->getTag($tag);
- if($tagData === NULL) {
- /* Tag not found. */
- SimpleSAML_Logger::info('Template: Looking up [' . $tag . ']: not translated at all.');
- return $this->t_not_translated($tag, $fallbackdefault);
- }
- }
-
- $translated = $this->getTranslation($tagData);
-
- foreach ($replacements as $k => $v) {
- /* try to translate if no replacement is given */
- if ($v == NULL) $v = $this->t($k);
- $translated = str_replace($k, $v, $translated);
- }
- return $translated;
- }
-
- /**
- * Return the string that should be used when no translation was found.
- *
- * @param $tag A name tag of the string that should be returned.
- * @param $fallbacktag If set to TRUE and string was not found in any languages, return
- * the tag it self. If FALSE return NULL.
- */
- private function t_not_translated($tag, $fallbacktag) {
- if ($fallbacktag) {
- return 'not translated (' . $tag . ')';
- } else {
- return $tag;
- }
- }
-
-
- /**
- * You can include translation inline instead of putting translation
- * in dictionaries. This function is reccomended to only be used from dynamic
- * data, or when the translation is already provided from an external source, as
- * a database or in metadata.
- *
- * @param $tag The tag that has a translation
- * @param $translation The translation array
- */
- public function includeInlineTranslation($tag, $translation) {
-
- if (is_string($translation)) {
- $translation = array('en' => $translation);
- } elseif (!is_array($translation)) {
- throw new Exception("Inline translation should be string or array. Is " . gettype($translation) . " now!");
- }
-
- SimpleSAML_Logger::debug('Template: Adding inline language translation for tag [' . $tag . ']');
- $this->langtext[$tag] = $translation;
- }
-
- /**
- * Include language file from the dictionaries directory.
- *
- * @param $file File name of dictionary to include
- * @param $otherConfig Optionally provide a different configuration object than
- * the one provided in the constructor to be used to find the dictionary directory.
- * This enables the possiblity of combining dictionaries inside simpleSAMLphp
- * distribution with external dictionaries.
- */
- public function includeLanguageFile($file, $otherConfig = null) {
-
- $filebase = null;
- if (!empty($otherConfig)) {
- $filebase = $otherConfig->getPathValue('dictionarydir', 'dictionaries/');
- } else {
- $filebase = $this->configuration->getPathValue('dictionarydir', 'dictionaries/');
- }
-
-
- $lang = $this->readDictionaryFile($filebase . $file);
- SimpleSAML_Logger::debug('Template: Merging language array. Loading [' . $file . ']');
- $this->langtext = array_merge($this->langtext, $lang);
- }
-
-
- /**
- * Read a dictionary file in json format.
- *
- * @param string $filename The absolute path to the dictionary file, minus the .definition.json ending.
- * @return array The translation array from the file.
- */
- private function readDictionaryJSON($filename) {
- $definitionFile = $filename . '.definition.json';
- assert('file_exists($definitionFile)');
-
- $fileContent = file_get_contents($definitionFile);
- $lang = json_decode($fileContent, TRUE);
-
- if (empty($lang)) {
- SimpleSAML_Logger::error('Invalid dictionary definition file [' . $definitionFile . ']');
- return array();
- }
-
- $translationFile = $filename . '.translation.json';
- if (file_exists($translationFile)) {
- $fileContent = file_get_contents($translationFile);
- $moreTrans = json_decode($fileContent, TRUE);
- if (!empty($moreTrans)) {
- $lang = self::lang_merge($lang, $moreTrans);
- }
- }
-
- return $lang;
- }
-
-
- /**
- * Read a dictionary file in PHP format.
- *
- * @param string $filename The absolute path to the dictionary file.
- * @return array The translation array from the file.
- */
- private function readDictionaryPHP($filename) {
- $phpFile = $filename . '.php';
- assert('file_exists($phpFile)');
-
- $lang = NULL;
- include($phpFile);
- if (isset($lang)) {
- return $lang;
- }
-
- return array();
- }
-
-
- /**
- * Read a dictionary file.
- *
- * @param $filename The absolute path to the dictionary file.
- * @return The translation array which was found in the dictionary file.
- */
- private function readDictionaryFile($filename) {
- assert('is_string($filename)');
-
- SimpleSAML_Logger::debug('Template: Reading [' . $filename . ']');
-
- $jsonFile = $filename . '.definition.json';
- if (file_exists($jsonFile)) {
- return $this->readDictionaryJSON($filename);
- }
-
-
- $phpFile = $filename . '.php';
- if (file_exists($phpFile)) {
- return $this->readDictionaryPHP($filename);
- }
-
- SimpleSAML_Logger::error($_SERVER['PHP_SELF'].' - Template: Could not find template file [' . $this->template . '] at [' . $filename . ']');
- return array();
- }
-
-
- // Merge two translation arrays.
- public static function lang_merge($def, $lang) {
- foreach($def AS $key => $value) {
- if (array_key_exists($key, $lang))
- $def[$key] = array_merge($value, $lang[$key]);
- }
- return $def;
- }
-
-
- /**
- * Show the template to the user.
- */
- public function show() {
-
- $filename = $this->findTemplatePath($this->template);
- require($filename);
- }
-
-
- /**
- * Find template path.
- *
- * This function locates the given template based on the template name.
- * It will first search for the template in the current theme directory, and
- * then the default theme.
- *
- * The template name may be on the form <module name>:<template path>, in which case
- * it will search for the template file in the given module.
- *
- * An error will be thrown if the template file couldn't be found.
- *
- * @param string $template The relative path from the theme directory to the template file.
- * @return string The absolute path to the template file.
- */
- private function findTemplatePath($template) {
- assert('is_string($template)');
-
- $tmp = explode(':', $template, 2);
- if (count($tmp) === 2) {
- $templateModule = $tmp[0];
- $templateName = $tmp[1];
- } else {
- $templateModule = 'default';
- $templateName = $tmp[0];
- }
-
- $tmp = explode(':', $this->configuration->getString('theme.use', 'default'), 2);
- if (count($tmp) === 2) {
- $themeModule = $tmp[0];
- $themeName = $tmp[1];
- } else {
- $themeModule = NULL;
- $themeName = $tmp[0];
- }
-
-
- /* First check the current theme. */
- if ($themeModule !== NULL) {
- /* .../module/<themeModule>/themes/<themeName>/<templateModule>/<templateName> */
-
- $filename = SimpleSAML_Module::getModuleDir($themeModule) . '/themes/' . $themeName . '/' . $templateModule . '/' . $templateName;
-
- } elseif ($templateModule !== 'default') {
- /* .../module/<templateModule>/templates/<themeName>/<templateName> */
- $filename = SimpleSAML_Module::getModuleDir($templateModule) . '/templates/' . $templateName;
-
- } else {
- /* .../templates/<theme>/<templateName> */
- $filename = $this->configuration->getPathValue('templatedir', 'templates/') . $templateName;
- }
-
- if (file_exists($filename)) {
- return $filename;
- }
-
-
- /* Not found in current theme. */
- SimpleSAML_Logger::debug($_SERVER['PHP_SELF'].' - Template: Could not find template file [' .
- $template . '] at [' . $filename . '] - now trying the base template');
-
-
- /* Try default theme. */
- if ($templateModule !== 'default') {
- /* .../module/<templateModule>/templates/<templateName> */
- $filename = SimpleSAML_Module::getModuleDir($templateModule) . '/templates/' . $templateName;
-
- } else {
- /* .../templates/<templateName> */
- $filename = $this->configuration->getPathValue('templatedir', 'templates/') . '/' . $templateName;
- }
-
- if (file_exists($filename)) {
- return $filename;
- }
-
-
- /* Not found in default template - log error and throw exception. */
- $error = 'Template: Could not find template file [' . $template . '] at [' . $filename . ']';
- SimpleSAML_Logger::critical($_SERVER['PHP_SELF'] . ' - ' . $error);
-
- throw new Exception($error);
- }
-
-
- /**
- * Retrieve the user-selected language from a cookie.
- *
- * @return string|NULL The language, or NULL if unset.
- */
- public static function getLanguageCookie() {
- $config = SimpleSAML_Configuration::getInstance();
- $availableLanguages = $config->getArray('language.available', array('en'));
- $name = $config->getString('language.cookie.name', 'language');
-
- if (isset($_COOKIE[$name])) {
- $language = strtolower((string)$_COOKIE[$name]);
- if (in_array($language, $availableLanguages, TRUE)) {
- return $language;
- }
- }
-
- return NULL;
- }
-
-
- /**
- * Set the user-selected language in a cookie.
- *
- * @param string $language The language.
- */
- public static function setLanguageCookie($language) {
- assert('is_string($language)');
-
- $language = strtolower($language);
- $config = SimpleSAML_Configuration::getInstance();
- $availableLanguages = $config->getArray('language.available', array('en'));
-
- if (!in_array($language, $availableLanguages, TRUE) || headers_sent()) {
- return;
- }
-
- $name = $config->getString('language.cookie.name', 'language');
- $params = array(
- 'lifetime' => ($config->getInteger('language.cookie.lifetime', 60*60*24*900)),
- 'domain' => ($config->getString('language.cookie.domain', NULL)),
- 'path' => ($config->getString('language.cookie.path', '/')),
- 'httponly' => FALSE,
- );
+ /**
+ * This is the default language map. It is used to map languages codes from the user agent to
+ * other language codes.
+ */
+ private static $defaultLanguageMap = array('nb' => 'no');
+
+
+ private $configuration = null;
+ private $template = 'default.php';
+ private $availableLanguages = array('en');
+ private $language = null;
+
+ private $langtext = array();
+
+ public $data = null;
+
+
+ /**
+ * Associative array of dictionaries.
+ */
+ private $dictionaries = array();
+
+
+ /**
+ * The default dictionary.
+ */
+ private $defaultDictionary = NULL;
+
+
+ /**
+ * HTTP GET language parameter name.
+ */
+ private $languageParameterName = 'language';
+
+
+ /**
+ * Constructor
+ *
+ * @param $configuration Configuration object
+ * @param $template Which template file to load
+ * @param $defaultDictionary The default dictionary where tags will come from.
+ */
+ function __construct(SimpleSAML_Configuration $configuration, $template, $defaultDictionary = NULL) {
+ $this->configuration = $configuration;
+ $this->template = $template;
+
+ $this->data['baseurlpath'] = $this->configuration->getBaseURL();
+
+ $this->availableLanguages = $this->configuration->getArray('language.available', array('en'));
+
+ $this->languageParameterName = $this->configuration->getString('language.parameter.name', 'language');
+ if (isset($_GET[$this->languageParameterName])) {
+ $this->setLanguage($_GET[$this->languageParameterName], $this->configuration->getBoolean('language.parameter.setcookie', TRUE));
+ }
+
+ if($defaultDictionary !== NULL && substr($defaultDictionary, -4) === '.php') {
+ /* For backwards compatibility - print warning. */
+ $backtrace = debug_backtrace();
+ $where = $backtrace[0]['file'] . ':' . $backtrace[0]['line'];
+ SimpleSAML_Logger::warning('Deprecated use of new SimpleSAML_Template(...) at ' . $where .
+ '. The last parameter is now a dictionary name, which should not end in ".php".');
+
+ $this->defaultDictionary = substr($defaultDictionary, 0, -4);
+ } else {
+ $this->defaultDictionary = $defaultDictionary;
+ }
+ }
+
+
+ /**
+ * setLanguage() will set a cookie for the user's browser to remember what language
+ * was selected
+ *
+ * @param $language Language code for the language to set.
+ */
+ public function setLanguage($language, $setLanguageCookie = TRUE) {
+ $language = strtolower($language);
+ if (in_array($language, $this->availableLanguages, TRUE)) {
+ $this->language = $language;
+ if ($setLanguageCookie === TRUE) {
+ SimpleSAML_XHTML_Template::setLanguageCookie($language);
+ }
+ }
+ }
+
+ /**
+ * getLanguage() will return the language selected by the user, or the default language
+ * This function first looks for a cached language code,
+ * then checks for a language cookie,
+ * then it tries to calculate the preferred language from HTTP headers.
+ * Last it returns the default language.
+ */
+ public function getLanguage() {
+
+ // Language is set in object
+ if (isset($this->language)) {
+ return $this->language;
+ }
+
+ // Run custom getLanguage function if defined
+ $customFunction = $this->configuration->getArray('language.get_language_function', NULL);
+ if (isset($customFunction)) {
+ assert('is_callable($customFunction)');
+ $customLanguage = call_user_func($customFunction, $this);
+ if ($customLanguage !== NULL && $customLanguage !== FALSE) {
+ return $customLanguage;
+ }
+ }
+
+ // Language is provided in a stored COOKIE
+ $languageCookie = SimpleSAML_XHTML_Template::getLanguageCookie();
+ if ($languageCookie !== NULL) {
+ $this->language = $languageCookie;
+ return $languageCookie;
+ }
+
+ /* Check if we can find a good language from the Accept-Language http header. */
+ $httpLanguage = $this->getHTTPLanguage();
+ if ($httpLanguage !== NULL) {
+ return $httpLanguage;
+ }
+
+ // Language is not set, and we get the default language from the configuration.
+ return $this->getDefaultLanguage();
+ }
+
+
+ /**
+ * This function gets the prefered language for the user based on the Accept-Language http header.
+ *
+ * @return The prefered language based on the Accept-Language http header, or NULL if none of the
+ * languages in the header were available.
+ */
+ private function getHTTPLanguage() {
+ $languageScore = \SimpleSAML\Utils\HTTP::getAcceptLanguage();
+
+ /* For now we only use the default language map. We may use a configurable language map
+ * in the future.
+ */
+ $languageMap = self::$defaultLanguageMap;
+
+ /* Find the available language with the best score. */
+ $bestLanguage = NULL;
+ $bestScore = -1.0;
+
+ foreach($languageScore as $language => $score) {
+
+ /* Apply the language map to the language code. */
+ if(array_key_exists($language, $languageMap)) {
+ $language = $languageMap[$language];
+ }
+
+ if(!in_array($language, $this->availableLanguages, TRUE)) {
+ /* Skip this language - we don't have it. */
+ continue;
+ }
+
+ /* Some user agents use very limited precicion of the quality value, but order the
+ * elements in descending order. Therefore we rely on the order of the output from
+ * getAcceptLanguage() matching the order of the languages in the header when two
+ * languages have the same quality.
+ */
+ if($score > $bestScore) {
+ $bestLanguage = $language;
+ $bestScore = $score;
+ }
+ }
+
+ return $bestLanguage;
+ }
+
+
+ /**
+ * Returns the language default (from configuration)
+ */
+ private function getDefaultLanguage() {
+ return $this->configuration->getString('language.default', 'en');
+ }
+
+ /**
+ * Returns a list of all available languages.
+ */
+ private function getLanguageList() {
+ $thisLang = $this->getLanguage();
+ $lang = array();
+ foreach ($this->availableLanguages AS $nl) {
+ $lang[$nl] = ($nl == $thisLang);
+ }
+ return $lang;
+ }
+
+ /**
+ * Return TRUE if language is Right-to-Left.
+ */
+ private function isLanguageRTL() {
+ $rtlLanguages = $this->configuration->getArray('language.rtl', array());
+ $thisLang = $this->getLanguage();
+ if (in_array($thisLang, $rtlLanguages)) {
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ /**
+ * Includs a file relative to the template base directory.
+ * This function can be used to include headers and footers etc.
+ *
+ */
+ private function includeAtTemplateBase($file) {
+ $data = $this->data;
+
+ $filename = $this->findTemplatePath($file);
+
+ include($filename);
+ }
+
+
+ /**
+ * Retrieve a dictionary.
+ *
+ * This function retrieves a dictionary with the given name.
+ *
+ * @param $name The name of the dictionary, as the filename in the dictionary directory,
+ * without the '.php'-ending.
+ * @return An associative array with the dictionary.
+ */
+ private function getDictionary($name) {
+ assert('is_string($name)');
+
+ if(!array_key_exists($name, $this->dictionaries)) {
+ $sepPos = strpos($name, ':');
+ if($sepPos !== FALSE) {
+ $module = substr($name, 0, $sepPos);
+ $fileName = substr($name, $sepPos + 1);
+ $dictDir = SimpleSAML_Module::getModuleDir($module) . '/dictionaries/';
+ } else {
+ $dictDir = $this->configuration->getPathValue('dictionarydir', 'dictionaries/');
+ $fileName = $name;
+ }
+ $this->dictionaries[$name] = $this->readDictionaryFile($dictDir . $fileName);
+ }
+
+ return $this->dictionaries[$name];
+ }
+
+
+ /**
+ * Retrieve a tag.
+ *
+ * This function retrieves a tag as an array with language => string mappings.
+ *
+ * @param $tag The tag name. The tag name can also be on the form '{<dictionary>:<tag>}', to retrieve
+ * a tag from the specific dictionary.
+ * @return As associative array with language => string mappings, or NULL if the tag wasn't found.
+ */
+ public function getTag($tag) {
+ assert('is_string($tag)');
+
+ /* First check translations loaded by the includeInlineTranslation and includeLanguageFile methods. */
+ if(array_key_exists($tag, $this->langtext)) {
+ return $this->langtext[$tag];
+ }
+
+ /* Check whether we should use the default dictionary or a dictionary specified in the tag. */
+ if(substr($tag, 0, 1) === '{' && preg_match('/^{((?:\w+:)?\w+?):(.*)}$/D', $tag, $matches)) {
+ $dictionary = $matches[1];
+ $tag = $matches[2];
+ } else {
+ $dictionary = $this->defaultDictionary;
+ if($dictionary === NULL) {
+ /* We don't have any dictionary to load the tag from. */
+ return NULL;
+ }
+ }
+
+ $dictionary = $this->getDictionary($dictionary);
+ if(!array_key_exists($tag, $dictionary)) {
+ return NULL;
+ }
+
+ return $dictionary[$tag];
+ }
+
+
+ /**
+ * Retrieve the preferred translation of a given text.
+ *
+ * @param $translations The translations, as an associative array with language => text mappings.
+ * @return The preferred translation.
+ */
+ public function getTranslation($translations) {
+ assert('is_array($translations)');
+
+ /* Look up translation of tag in the selected language. */
+ $selected_language = $this->getLanguage();
+ if (array_key_exists($selected_language, $translations)) {
+ return $translations[$selected_language];
+ }
+
+ /* Look up translation of tag in the default language. */
+ $default_language = $this->getDefaultLanguage();
+ if(array_key_exists($default_language, $translations)) {
+ return $translations[$default_language];
+ }
+
+ /* Check for english translation. */
+ if(array_key_exists('en', $translations)) {
+ return $translations['en'];
+ }
+
+ /* Pick the first translation available. */
+ if(count($translations) > 0) {
+ $languages = array_keys($translations);
+ return $translations[$languages[0]];
+ }
+
+ /* We don't have anything to return. */
+ throw new Exception('Nothing to return from translation.');
+ }
+
+
+ /**
+ * Translate a attribute name.
+ *
+ * @param string $name The attribute name.
+ * @return string The translated attribute name, or the original attribute name if no translation was found.
+ */
+ public function getAttributeTranslation($name) {
+
+ /* Normalize attribute name. */
+ $normName = strtolower($name);
+ $normName = str_replace(":", "_", $normName);
+
+ /* Check for an extra dictionary. */
+ $extraDict = $this->configuration->getString('attributes.extradictionary', NULL);
+ if ($extraDict !== NULL) {
+ $dict = $this->getDictionary($extraDict);
+ if (array_key_exists($normName, $dict)) {
+ return $this->getTranslation($dict[$normName]);
+ }
+ }
+
+ /* Search the default attribute dictionary. */
+ $dict = $this->getDictionary('attributes');
+ if (array_key_exists('attribute_' . $normName, $dict)) {
+ return $this->getTranslation($dict['attribute_' . $normName]);
+ }
+
+ /* No translations found. */
+ return $name;
+ }
+
+
+ /**
+ * Translate a tag into the current language, with a fallback to english.
+ *
+ * This function is used to look up a translation tag in dictionaries, and return the
+ * translation into the current language. If no translation into the current language can be
+ * found, english will be tried, and if that fails, placeholder text will be returned.
+ *
+ * An array can be passed as the tag. In that case, the array will be assumed to be on the
+ * form (language => text), and will be used as the source of translations.
+ *
+ * This function can also do replacements into the translated tag. It will search the
+ * translated tag for the keys provided in $replacements, and replace any found occurances
+ * with the value of the key.
+ *
+ * @param string|array $tag A tag name for the translation which should be looked up, or an
+ * array with (language => text) mappings.
+ * @param array $replacements An associative array of keys that should be replaced with
+ * values in the translated string.
+ * @return string The translated tag, or a placeholder value if the tag wasn't found.
+ */
+ public function t($tag, $replacements = array(), $fallbackdefault = true, $oldreplacements = array(), $striptags = FALSE) {
+ if(!is_array($replacements)) {
+
+ /* Old style call to t(...). Print warning to log. */
+ $backtrace = debug_backtrace();
+ $where = $backtrace[0]['file'] . ':' . $backtrace[0]['line'];
+ SimpleSAML_Logger::warning('Deprecated use of SimpleSAML_Template::t(...) at ' . $where .
+ '. Please update the code to use the new style of parameters.');
+
+ /* For backwards compatibility. */
+ if(!$replacements && $this->getTag($tag) === NULL) {
+ SimpleSAML_Logger::warning('Code which uses $fallbackdefault === FALSE shouls be' .
+ ' updated to use the getTag-method instead.');
+ return NULL;
+ }
+
+ $replacements = $oldreplacements;
+ }
+
+ if(is_array($tag)) {
+ $tagData = $tag;
+ } else {
+ $tagData = $this->getTag($tag);
+ if($tagData === NULL) {
+ /* Tag not found. */
+ SimpleSAML_Logger::info('Template: Looking up [' . $tag . ']: not translated at all.');
+ return $this->t_not_translated($tag, $fallbackdefault);
+ }
+ }
+
+ $translated = $this->getTranslation($tagData);
+
+ foreach ($replacements as $k => $v) {
+ /* try to translate if no replacement is given */
+ if ($v == NULL) $v = $this->t($k);
+ $translated = str_replace($k, $v, $translated);
+ }
+ return $translated;
+ }
+
+
+ /**
+ * Return the string that should be used when no translation was found.
+ *
+ * @param $tag A name tag of the string that should be returned.
+ * @param $fallbacktag If set to TRUE and string was not found in any languages, return
+ * the tag it self. If FALSE return NULL.
+ */
+ private function t_not_translated($tag, $fallbacktag) {
+ if ($fallbacktag) {
+ return 'not translated (' . $tag . ')';
+ } else {
+ return $tag;
+ }
+ }
+
+
+ /**
+ * You can include translation inline instead of putting translation
+ * in dictionaries. This function is reccomended to only be used from dynamic
+ * data, or when the translation is already provided from an external source, as
+ * a database or in metadata.
+ *
+ * @param $tag The tag that has a translation
+ * @param $translation The translation array
+ */
+ public function includeInlineTranslation($tag, $translation) {
+ if (is_string($translation)) {
+ $translation = array('en' => $translation);
+ } elseif (!is_array($translation)) {
+ throw new Exception("Inline translation should be string or array. Is " . gettype($translation) . " now!");
+ }
+ SimpleSAML_Logger::debug('Template: Adding inline language translation for tag [' . $tag . ']');
+ $this->langtext[$tag] = $translation;
+ }
+
+ /**
+ * Include language file from the dictionaries directory.
+ *
+ * @param $file File name of dictionary to include
+ * @param $otherConfig Optionally provide a different configuration object than
+ * the one provided in the constructor to be used to find the dictionary directory.
+ * This enables the possiblity of combining dictionaries inside simpleSAMLphp
+ * distribution with external dictionaries.
+ */
+ public function includeLanguageFile($file, $otherConfig = null) {
+ $filebase = null;
+ if (!empty($otherConfig)) {
+ $filebase = $otherConfig->getPathValue('dictionarydir', 'dictionaries/');
+ } else {
+ $filebase = $this->configuration->getPathValue('dictionarydir', 'dictionaries/');
+ }
+
+ $lang = $this->readDictionaryFile($filebase . $file);
+ SimpleSAML_Logger::debug('Template: Merging language array. Loading [' . $file . ']');
+ $this->langtext = array_merge($this->langtext, $lang);
+ }
+
+
+ /**
+ * Read a dictionary file in json format.
+ *
+ * @param string $filename The absolute path to the dictionary file, minus the .definition.json ending.
+ * @return array The translation array from the file.
+ */
+ private function readDictionaryJSON($filename) {
+ $definitionFile = $filename . '.definition.json';
+ assert('file_exists($definitionFile)');
+
+ $fileContent = file_get_contents($definitionFile);
+ $lang = json_decode($fileContent, TRUE);
+
+ if (empty($lang)) {
+ SimpleSAML_Logger::error('Invalid dictionary definition file [' . $definitionFile . ']');
+ return array();
+ }
+
+ $translationFile = $filename . '.translation.json';
+ if (file_exists($translationFile)) {
+ $fileContent = file_get_contents($translationFile);
+ $moreTrans = json_decode($fileContent, TRUE);
+ if (!empty($moreTrans)) {
+ $lang = self::lang_merge($lang, $moreTrans);
+ }
+ }
+
+ return $lang;
+ }
+
+
+ /**
+ * Read a dictionary file in PHP format.
+ *
+ * @param string $filename The absolute path to the dictionary file.
+ * @return array The translation array from the file.
+ */
+ private function readDictionaryPHP($filename) {
+ $phpFile = $filename . '.php';
+ assert('file_exists($phpFile)');
+
+ $lang = NULL;
+ include($phpFile);
+ if (isset($lang)) {
+ return $lang;
+ }
+
+ return array();
+ }
+
+
+ /**
+ * Read a dictionary file.
+ *
+ * @param $filename The absolute path to the dictionary file.
+ * @return The translation array which was found in the dictionary file.
+ */
+ private function readDictionaryFile($filename) {
+ assert('is_string($filename)');
+
+ SimpleSAML_Logger::debug('Template: Reading [' . $filename . ']');
+
+ $jsonFile = $filename . '.definition.json';
+ if (file_exists($jsonFile)) {
+ return $this->readDictionaryJSON($filename);
+ }
+
+
+ $phpFile = $filename . '.php';
+ if (file_exists($phpFile)) {
+ return $this->readDictionaryPHP($filename);
+ }
+
+ SimpleSAML_Logger::error($_SERVER['PHP_SELF'].' - Template: Could not find template file [' . $this->template . '] at [' . $filename . ']');
+ return array();
+ }
+
+
+ // Merge two translation arrays.
+ public static function lang_merge($def, $lang) {
+ foreach($def AS $key => $value) {
+ if (array_key_exists($key, $lang))
+ $def[$key] = array_merge($value, $lang[$key]);
+ }
+ return $def;
+ }
+
+
+ /**
+ * Show the template to the user.
+ */
+ public function show() {
+
+ $filename = $this->findTemplatePath($this->template);
+ require($filename);
+ }
+
+
+ /**
+ * Find template path.
+ *
+ * This function locates the given template based on the template name.
+ * It will first search for the template in the current theme directory, and
+ * then the default theme.
+ *
+ * The template name may be on the form <module name>:<template path>, in which case
+ * it will search for the template file in the given module.
+ *
+ * An error will be thrown if the template file couldn't be found.
+ *
+ * @param string $template The relative path from the theme directory to the template file.
+ * @return string The absolute path to the template file.
+ */
+ private function findTemplatePath($template) {
+ assert('is_string($template)');
+
+ $tmp = explode(':', $template, 2);
+ if (count($tmp) === 2) {
+ $templateModule = $tmp[0];
+ $templateName = $tmp[1];
+ } else {
+ $templateModule = 'default';
+ $templateName = $tmp[0];
+ }
+
+ $tmp = explode(':', $this->configuration->getString('theme.use', 'default'), 2);
+ if (count($tmp) === 2) {
+ $themeModule = $tmp[0];
+ $themeName = $tmp[1];
+ } else {
+ $themeModule = NULL;
+ $themeName = $tmp[0];
+ }
+
+
+ /* First check the current theme. */
+ if ($themeModule !== NULL) {
+ /* .../module/<themeModule>/themes/<themeName>/<templateModule>/<templateName> */
+
+ $filename = SimpleSAML_Module::getModuleDir($themeModule) . '/themes/' . $themeName . '/' . $templateModule . '/' . $templateName;
+ } elseif ($templateModule !== 'default') {
+ /* .../module/<templateModule>/templates/<themeName>/<templateName> */
+ $filename = SimpleSAML_Module::getModuleDir($templateModule) . '/templates/' . $templateName;
+ } else {
+ /* .../templates/<theme>/<templateName> */
+ $filename = $this->configuration->getPathValue('templatedir', 'templates/') . $templateName;
+ }
+
+ if (file_exists($filename)) {
+ return $filename;
+ }
+
+
+ /* Not found in current theme. */
+ SimpleSAML_Logger::debug($_SERVER['PHP_SELF'].' - Template: Could not find template file [' .
+ $template . '] at [' . $filename . '] - now trying the base template');
+
+
+ /* Try default theme. */
+ if ($templateModule !== 'default') {
+ /* .../module/<templateModule>/templates/<templateName> */
+ $filename = SimpleSAML_Module::getModuleDir($templateModule) . '/templates/' . $templateName;
+ } else {
+ /* .../templates/<templateName> */
+ $filename = $this->configuration->getPathValue('templatedir', 'templates/') . '/' . $templateName;
+ }
+
+ if (file_exists($filename)) {
+ return $filename;
+ }
+
+
+ /* Not found in default template - log error and throw exception. */
+ $error = 'Template: Could not find template file [' . $template . '] at [' . $filename . ']';
+ SimpleSAML_Logger::critical($_SERVER['PHP_SELF'] . ' - ' . $error);
+
+ throw new Exception($error);
+ }
+
+
+ /**
+ * Retrieve the user-selected language from a cookie.
+ *
+ * @return string|NULL The language, or NULL if unset.
+ */
+ public static function getLanguageCookie() {
+ $config = SimpleSAML_Configuration::getInstance();
+ $availableLanguages = $config->getArray('language.available', array('en'));
+ $name = $config->getString('language.cookie.name', 'language');
+
+ if (isset($_COOKIE[$name])) {
+ $language = strtolower((string)$_COOKIE[$name]);
+ if (in_array($language, $availableLanguages, TRUE)) {
+ return $language;
+ }
+ }
+
+ return NULL;
+ }
+
+
+ /**
+ * Set the user-selected language in a cookie.
+ *
+ * @param string $language The language.
+ */
+ public static function setLanguageCookie($language) {
+ assert('is_string($language)');
+
+ $language = strtolower($language);
+ $config = SimpleSAML_Configuration::getInstance();
+ $availableLanguages = $config->getArray('language.available', array('en'));
+
+ if (!in_array($language, $availableLanguages, TRUE) || headers_sent()) {
+ return;
+ }
+
+ $name = $config->getString('language.cookie.name', 'language');
+ $params = array(
+ 'lifetime' => ($config->getInteger('language.cookie.lifetime', 60*60*24*900)),
+ 'domain' => ($config->getString('language.cookie.domain', NULL)),
+ 'path' => ($config->getString('language.cookie.path', '/')),
+ 'httponly' => FALSE,
+ );
\SimpleSAML\Utils\HTTP::setCookie($name, $language, $params, FALSE);
- }
+ }
}
diff --git a/lib/_autoload.php b/lib/_autoload.php
index bbe3681..03b5ec1 100644
--- a/lib/_autoload.php
+++ b/lib/_autoload.php
@@ -1,21 +1,20 @@
<?php
/**
- * This file is a backwards compatible autoloader for simpleSAMLphp.
+ * This file is a backwards compatible autoloader for SimpleSAMLphp.
* Loads the Composer autoloader.
*
* @author Olav Morken, UNINETT AS.
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
// SSP is loaded as a separate project
-if (file_exists(dirname(dirname(__FILE__)) . '/vendor/autoload.php')) {
- require_once dirname(dirname(__FILE__)) . '/vendor/autoload.php';
-}
-// SSP is loaded as a library.
-else if (file_exists(dirname(dirname(__FILE__)) . '/../../autoload.php')) {
- require_once dirname(dirname(__FILE__)) . '/../../autoload.php';
-}
-else {
- throw new Exception('Unable to load Composer autoloader');
+if (file_exists(dirname(dirname(__FILE__)).'/vendor/autoload.php')) {
+ require_once dirname(dirname(__FILE__)).'/vendor/autoload.php';
+} else { // SSP is loaded as a library.
+ if (file_exists(dirname(dirname(__FILE__)).'/../../autoload.php')) {
+ require_once dirname(dirname(__FILE__)).'/../../autoload.php';
+ } else {
+ throw new Exception('Unable to load Composer autoloader');
+ }
}
diff --git a/lib/_autoload_modules.php b/lib/_autoload_modules.php
index fcbc110..c4c85e4 100644
--- a/lib/_autoload_modules.php
+++ b/lib/_autoload_modules.php
@@ -1,14 +1,14 @@
<?php
/**
- * This file implements a autoloader for simpleSAMLphp modules.
+ * This file implements a autoloader for SimpleSAMLphp modules.
*
* @author Boy Baukema, SURFnet
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
/**
- * Autoload function for simpleSAMLphp modules.
+ * Autoload function for SimpleSAMLphp modules.
*
* @param string $className Name of the class.
*/
diff --git a/log/_placeholder.php b/log/_placeholder.php
deleted file mode 100644
index 67c9ac0..0000000
--- a/log/_placeholder.php
+++ /dev/null
@@ -1,2 +0,0 @@
-<?php
-/* this file can be deleted */
diff --git a/modules/aselect/default-disable b/modules/aselect/default-disable
deleted file mode 100644
index fa0bd82..0000000
--- a/modules/aselect/default-disable
+++ /dev/null
@@ -1,3 +0,0 @@
-This file indicates that the default state of this module
-is disabled. To enable, create a file named enable in the
-same directory as this file.
diff --git a/modules/aselect/docs/aselect.txt b/modules/aselect/docs/aselect.txt
deleted file mode 100644
index c411cf2..0000000
--- a/modules/aselect/docs/aselect.txt
+++ /dev/null
@@ -1,49 +0,0 @@
-A-Select module for simpleSAMLphp
----------------------------------
-
-This module allows one to use an A-Select server as authentication
-source for simpleSAMLphp.
-
-The module supports the A-Select protocol, including signing of
-requests. Not supported is A-Select Cross.
-
-Usage:
-
-Enable the module if not already enabled:
-$ touch modules/aselect/enabled
-
-In config/authsources.php, configure your A-Selectserver as an
-authentication source. The following is an example for a source
-named 'aselect':
-
- 'aselect' => array(
- 'aselect:aselect',
- 'app_id' => 'simplesamlphp',
- 'server_id' => 'sso.example.com',
- 'server_url' => 'https://test.sso.example.com/server',
- 'private_key' => 'file:///etc/ssl/private/aselect.key',
- 'add_default_attributes' => FALSE
- ),
-
-The parameters:
-- app_id: the application I for simpleSAMLphp as configured in
- your A-Select server;
-- server_id: the A-Select server ID as configured in your
- A-Select server;
-- server_url: the URL for your A-Selectserver, usually ends in
- '/server/.
-- private_key: the key you want to use for signing requests.
- If you're really sure you do not want request signing, you
- can set this option to a null value.
-- add_default_attributes: true to add default attributes
- (uid and organization) to resulting attributes, false
- to never do this, and null to do this only when no
- attributes are returned.
-Options 'serverurl' and 'serverid' (without underscore) are
-supported for backwards compatibility.
-
-Author: Wessel Dankers <wsl@uvt.nl>
-
-Copyright: © 2011,2012 Tilburg University (http://www.tilburguniversity.edu)
-
-License: LGPL version 2.1
diff --git a/modules/aselect/lib/Auth/Source/aselect.php b/modules/aselect/lib/Auth/Source/aselect.php
deleted file mode 100644
index b8e115e..0000000
--- a/modules/aselect/lib/Auth/Source/aselect.php
+++ /dev/null
@@ -1,207 +0,0 @@
-<?php
-
-/**
- * Authentication module which acts as an A-Select client
- *
- * @author Wessel Dankers, Tilburg University
- */
-class sspmod_aselect_Auth_Source_aselect extends SimpleSAML_Auth_Source {
- private $app_id = 'simplesamlphp';
- private $server_id;
- private $server_url;
- private $private_key;
- private $add_default_attributes;
-
- /**
- * Constructor for this authentication source.
- *
- * @param array $info Information about this authentication source.
- * @param array $config Configuration.
- */
- public function __construct($info, $config) {
- /* Call the parent constructor first, as required by the interface. */
- parent::__construct($info, $config);
-
- $cfg = SimpleSAML_Configuration::loadFromArray($config,
- 'Authentication source ' . var_export($this->authId, true));
-
- $cfg->getValueValidate('type', array('app'), 'app');
- $this->app_id = $cfg->getString('app_id');
- $this->private_key = $cfg->getString('private_key', null);
-
- // accept these arguments with '_' for consistency
- // accept these arguments without '_' for backwards compatibility
- $this->server_id = $cfg->getString('serverid', null);
- if($this->server_id === null)
- $this->server_id = $cfg->getString('server_id');
-
- $this->server_url = $cfg->getString('serverurl', null);
- if($this->server_url === null)
- $this->server_url = $cfg->getString('server_url');
-
- $this->add_default_attributes = $cfg->getBoolean('add_default_attributes', null);
- }
-
- /**
- * Initiate authentication.
- *
- * @param array &$state Information about the current authentication.
- */
- public function authenticate(&$state) {
- $state['aselect::authid'] = $this->authId;
- $state['aselect::add_default_attributes'] = $this->add_default_attributes;
- $id = SimpleSAML_Auth_State::saveState($state, 'aselect:login', true);
-
- try {
- $app_url = SimpleSAML_Module::getModuleURL('aselect/credentials.php', array('ssp_state' => $id));
- $as_url = $this->request_authentication($app_url);
-
- \SimpleSAML\Utils\HTTP::redirectTrustedURL($as_url);
- } catch(Exception $e) {
- // attach the exception to the state
- SimpleSAML_Auth_State::throwException($state, $e);
- }
- }
-
- /**
- * Sign a string using the configured private key
- *
- * @param string $str The string to calculate a signature for
- */
- private function base64_signature($str) {
- $key = openssl_pkey_get_private($this->private_key);
- if($key === false)
- throw new SimpleSAML_Error_Exception("Unable to load private key: ".openssl_error_string());
- if(!openssl_sign($str, $sig, $key))
- throw new SimpleSAML_Error_Exception("Unable to create signature: ".openssl_error_string());
- openssl_pkey_free($key);
- return base64_encode($sig);
- }
-
- /**
- * Parse a base64 encoded attribute blob. Can't use parse_str() because it
- * may contain multi-valued attributes.
- *
- * @param string $base64 The base64 string to decode.
- */
- private static function decode_attributes($base64) {
- $blob = base64_decode($base64, true);
- if($blob === false)
- throw new SimpleSAML_Error_Exception("Attributes parameter base64 malformed");
- $pairs = explode('&', $blob);
- $ret = array();
- foreach($pairs as $pair) {
- $keyval = explode('=', $pair, 2);
- if(count($keyval) < 2)
- throw new SimpleSAML_Error_Exception("Missing value in attributes parameter");
- $key = urldecode($keyval[0]);
- $val = urldecode($keyval[1]);
- $ret[$key][] = $val;
- }
- return $ret;
- }
-
- /**
- * Default options for curl invocations.
- */
- private static $curl_options = array(
- CURLOPT_BINARYTRANSFER => true,
- CURLOPT_FAILONERROR => true,
- CURLOPT_RETURNTRANSFER => true,
- CURLOPT_CONNECTTIMEOUT => 1,
- CURLOPT_TIMEOUT => 5,
- CURLOPT_USERAGENT => "simpleSAMLphp",
- );
-
- /**
- * Create a (possibly signed) URL to contact the A-Select server.
- *
- * @param string $request The name of the request (authenticate / verify_credentials).
- * @param array $parameters The parameters to pass for this request.
- */
- private function create_aselect_url($request, $parameters) {
- $parameters['request'] = $request;
- $parameters['a-select-server'] = $this->server_id;
- if(!is_null($this->private_key)) {
- $signable = '';
- foreach(array('a-select-server', 'app_id', 'app_url', 'aselect_credentials', 'rid') as $p)
- if(array_key_exists($p, $parameters))
- $signable .= $parameters[$p];
- $parameters['signature'] = $this->base64_signature($signable);
- }
- return \SimpleSAML\Utils\HTTP::addURLParameters($this->server_url, $parameters);
- }
-
- /**
- * Contact the A-Select server and return the result as an associative array.
- *
- * @param string $request The name of the request (authenticate / verify_credentials).
- * @param array $parameters The parameters to pass for this request.
- */
- private function call_aselect($request, $parameters) {
- $url = $this->create_aselect_url($request, $parameters);
-
- $curl = curl_init($url);
- if($curl === false)
- throw new SimpleSAML_Error_Exception("Unable to create CURL handle");
-
- if(!curl_setopt_array($curl, self::$curl_options))
- throw new SimpleSAML_Error_Exception("Unable to set CURL options: ".curl_error($curl));
-
- $str = curl_exec($curl);
- $err = curl_error($curl);
-
- curl_close($curl);
-
- if($str === false)
- throw new SimpleSAML_Error_Exception("Unable to retrieve URL: $err");
-
- parse_str($str, $res);
-
- // message is only available with some A-Select server implementations
- if($res['result_code'] != '0000')
- if(array_key_exists('message', $res))
- throw new SimpleSAML_Error_Exception("Unable to contact SSO service: result_code=".$res['result_code']." message=".$res['message']);
- else
- throw new SimpleSAML_Error_Exception("Unable to contact SSO service: result_code=".$res['result_code']);
- unset($res['result_code']);
-
- return $res;
- }
-
- /**
- * Initiate authentication. Returns a URL to redirect the user to.
- *
- * @param string $app_url The SSP URL to return to after authenticating (similar to an ACS).
- */
- public function request_authentication($app_url) {
- $res = $this->call_aselect('authenticate',
- array('app_id' => $this->app_id, 'app_url' => $app_url));
-
- $as_url = $res['as_url'];
- unset($res['as_url']);
-
- return \SimpleSAML\Utils\HTTP::addURLParameters($as_url, $res);
- }
-
- /**
- * Verify the credentials upon return from the A-Select server. Returns an associative array
- * with the information given by the A-Select server. Any attributes are pre-parsed.
- *
- * @param string $server_id The A-Select server ID as passed by the client
- * @param string $credentials The credentials as passed by the client
- * @param string $rid The request ID as passed by the client
- */
- public function verify_credentials($server_id, $credentials, $rid) {
- if($server_id != $this->server_id)
- throw new SimpleSAML_Error_Exception("Acquired server ID ($server_id) does not match configured server ID ($this->server_id)");
-
- $res = $this->call_aselect('verify_credentials',
- array('aselect_credentials' => $credentials, 'rid' => $rid));
-
- if(array_key_exists('attributes', $res))
- $res['attributes'] = self::decode_attributes($res['attributes']);
-
- return $res;
- }
-}
diff --git a/modules/aselect/www/credentials.php b/modules/aselect/www/credentials.php
deleted file mode 100644
index 6271a48..0000000
--- a/modules/aselect/www/credentials.php
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-
-/**
- * Check the credentials that the user got from the A-Select server.
- * This function is called after the user returns from the A-Select server.
- *
- * @author Wessel Dankers, Tilburg University
- */
-if (!array_key_exists('ssp_state', $_REQUEST)) {
- throw new SimpleSAML_Error_Exception("Missing ssp_state parameter");
-}
-$state = SimpleSAML_Auth_State::loadState($_REQUEST['ssp_state'], 'aselect:login');
-
-if (!array_key_exists('a-select-server', $_REQUEST)) {
- SimpleSAML_Auth_State::throwException($state, new SimpleSAML_Error_Exception("Missing a-select-server parameter"));
-}
-$server_id = $_REQUEST['a-select-server'];
-
-if (!array_key_exists('aselect_credentials', $_REQUEST)) {
- SimpleSAML_Auth_State::throwException($state,
- new SimpleSAML_Error_Exception("Missing aselect_credentials parameter"));
-}
-$credentials = $_REQUEST['aselect_credentials'];
-
-if (!array_key_exists('rid', $_REQUEST)) {
- SimpleSAML_Auth_State::throwException($state, new SimpleSAML_Error_Exception("Missing rid parameter"));
-}
-$rid = $_REQUEST['rid'];
-
-try {
- if (!array_key_exists('aselect::authid', $state)) {
- throw new SimpleSAML_Error_Exception("ASelect authentication source missing in state");
- }
- $authid = $state['aselect::authid'];
- $aselect = SimpleSAML_Auth_Source::getById($authid);
- if (is_null($aselect)) {
- throw new SimpleSAML_Error_Exception("Could not find authentication source with id $authid");
- }
- $creds = $aselect->verify_credentials($server_id, $credentials, $rid);
-
- if ($state['aselect::add_default_attributes'] === true) {
- // Add default attributes
- $state['Attributes'] = array('uid' => array($creds['uid']), 'organization' => array($creds['organization']));
- if (array_key_exists('attributes', $creds)) {
- $state['Attributes'] = array_merge($state['Attributes'], $creds['attributes']);
- }
- } elseif ($state['aselect::add_default_attributes'] === false) {
- // Do not add default attributes
- if (array_key_exists('attributes', $creds)) {
- $state['Attributes'] = $creds['attributes'];
- } else {
- $state['Attributes'] = array();
- }
- } else {
- // Legacy behaviour: add default attributes if no attributes are returned
- if (array_key_exists('attributes', $creds)) {
- $state['Attributes'] = $creds['attributes'];
- } else {
- $state['Attributes'] = array('uid' => array($creds['uid']), 'organization' => array($creds['organization']));
- }
- }
-} catch (Exception $e) {
- SimpleSAML_Auth_State::throwException($state, $e);
-}
-
-SimpleSAML_Auth_Source::completeAuth($state);
-SimpleSAML_Auth_State::throwException($state, new SimpleSAML_Error_Exception("Internal error in A-Select component"));
diff --git a/modules/authX509/docs/authX509.txt b/modules/authX509/docs/authX509.txt
index 0f3d126..0878616 100644
--- a/modules/authX509/docs/authX509.txt
+++ b/modules/authX509/docs/authX509.txt
@@ -29,8 +29,8 @@ this later).
If your server or your client (or both!) have TLS renegotiation disabled
as a workaround for CVE-2009-3555, then the configuration directive above
-must not appear in a <Directory>, <Location>, or in a name-based
-<VirtualHost>. You can only use them server-wide, or in <VirtualHost>s
+must not appear in a &lt;Directory&gt;, &lt;Location&gt;, or in a name-based
+&lt;VirtualHost&gt;. You can only use them server-wide, or in &lt;VirtualHost&gt;s
with different IP address/port combinations.
diff --git a/modules/autotest/default-disable b/modules/autotest/default-disable
deleted file mode 100644
index fa0bd82..0000000
--- a/modules/autotest/default-disable
+++ /dev/null
@@ -1,3 +0,0 @@
-This file indicates that the default state of this module
-is disabled. To enable, create a file named enable in the
-same directory as this file.
diff --git a/modules/autotest/docs/test.txt b/modules/autotest/docs/test.txt
deleted file mode 100644
index 5dc0bfc..0000000
--- a/modules/autotest/docs/test.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-`autotest` module
-=================
-
-This module provides an interface to do automatic testing of authentication sources.
-To enable it, create a file named "`enable`" in `modules/autotest`.
-
-It provides three webpages:
-
-- `https://.../simplesaml/module.php/autotest/login.php`
-- `https://.../simplesaml/module.php/autotest/logout.php`
-- `https://.../simplesaml/module.php/autotest/attributes.php`
-
-All the webpages have a mandatory paremeter 'SourceID', which is the name of the authentication source.
-
-On success, the web pages print a single line with "OK".
-The attributes page will also list all the attributes of the user.
-On error they set the HTTP status code to 500 Internal Server Error, print a line with "ERROR" and then any information about the error.
-
-Note that you still have to parse the login pages to extract the parameters in the login form.
diff --git a/modules/autotest/www/attributes.php b/modules/autotest/www/attributes.php
deleted file mode 100644
index e50a474..0000000
--- a/modules/autotest/www/attributes.php
+++ /dev/null
@@ -1,31 +0,0 @@
-<?php
-
-try {
- if (!isset($_GET['SourceID'])) {
- throw new SimpleSAML_Error_BadRequest('Missing SourceID parameter');
- }
- $sourceId = $_GET['SourceID'];
-
- $as = new SimpleSAML_Auth_Simple($sourceId);
-
- if (!$as->isAuthenticated()) {
- throw new SimpleSAML_Error_Exception('Not authenticated.');
- }
-
- $attributes = $as->getAttributes();
-
- header('Content-Type: text/plain; charset=utf-8');
- echo("OK\n");
- foreach ($attributes as $name => $values) {
- echo("$name\n");
- foreach ($values as $value) {
- echo("\t$value\n");
- }
- }
-
-} catch (Exception $e) {
- header('HTTP/1.0 500 Internal Server Error');
- header('Content-Type: text/plain; charset=utf-8');
- echo("ERROR\n");
- echo($e->getMessage() . "\n");
-}
diff --git a/modules/autotest/www/login.php b/modules/autotest/www/login.php
deleted file mode 100644
index da26db5..0000000
--- a/modules/autotest/www/login.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-
-try {
- if (!isset($_GET['SourceID'])) {
- throw new SimpleSAML_Error_BadRequest('Missing SourceID parameter');
- }
- $sourceId = $_GET['SourceID'];
-
- $as = new SimpleSAML_Auth_Simple($sourceId);
-
- $as->requireAuth();
-
- header('Content-Type: text/plain; charset=utf-8');
- echo("OK\n");
-} catch (Exception $e) {
- header('HTTP/1.0 500 Internal Server Error');
- header('Content-Type: text/plain; charset=utf-8');
- echo("ERROR\n");
- echo($e->getMessage() . "\n");
-}
diff --git a/modules/autotest/www/logout.php b/modules/autotest/www/logout.php
deleted file mode 100644
index 81f50d7..0000000
--- a/modules/autotest/www/logout.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-try {
- if (!isset($_GET['SourceID'])) {
- throw new SimpleSAML_Error_BadRequest('Missing SourceID parameter');
- }
- $sourceId = $_GET['SourceID'];
-
- $as = new SimpleSAML_Auth_Simple($sourceId);
-
- if ($as->isAuthenticated()) {
- $as->logout();
- }
-
-
- header('Content-Type: text/plain; charset=utf-8');
- echo("OK\n");
-
-} catch (Exception $e) {
- header('HTTP/1.0 500 Internal Server Error');
- header('Content-Type: text/plain; charset=utf-8');
- echo("ERROR\n");
- echo($e->getMessage() . "\n");
-}
diff --git a/modules/consentAdmin/www/consentAdmin.php b/modules/consentAdmin/www/consentAdmin.php
index fd75bc1..ae8b30b 100644
--- a/modules/consentAdmin/www/consentAdmin.php
+++ b/modules/consentAdmin/www/consentAdmin.php
@@ -186,9 +186,9 @@ if ($action !== null && $sp_entityid !== null) {
}
}
// init template to enable translation of status messages
- $et = new SimpleSAML_XHTML_Template($config, 'consentAdmin:consentadminajax.php', 'consentAdmin:consentadmin');
- $et->data['res'] = $res;
- $et->show();
+ $template = new SimpleSAML_XHTML_Template($config, 'consentAdmin:consentadminajax.php', 'consentAdmin:consentadmin');
+ $template->data['res'] = $res;
+ $template->show();
exit;
}
@@ -204,9 +204,9 @@ foreach ($user_consent_list as $c) {
$template_sp_content = array();
// Init template
-$et = new SimpleSAML_XHTML_Template($config, 'consentAdmin:consentadmin.php', 'consentAdmin:consentadmin');
-$sp_empty_name = $et->getTag('sp_empty_name');
-$sp_empty_description = $et->getTag('sp_empty_description');
+$template = new SimpleSAML_XHTML_Template($config, 'consentAdmin:consentadmin.php', 'consentAdmin:consentadmin');
+$sp_empty_name = $template->translator->getTag('sp_empty_name');
+$sp_empty_description = $template->translator->getTag('sp_empty_description');
// Process consents for all SP
foreach ($all_sp_metadata as $sp_entityid => $sp_values) {
@@ -267,7 +267,7 @@ foreach ($all_sp_metadata as $sp_entityid => $sp_values) {
);
}
-$et->data['header'] = 'Consent Administration';
-$et->data['spList'] = $sp_list;
-$et->data['showDescription'] = $cA_config->getValue('showDescription');
-$et->show();
+$template->data['header'] = 'Consent Administration';
+$template->data['spList'] = $sp_list;
+$template->data['showDescription'] = $cA_config->getValue('showDescription');
+$template->show();
diff --git a/modules/consentSimpleAdmin/config-templates/module_consentSimpleAdmin.php b/modules/consentSimpleAdmin/config-templates/module_consentSimpleAdmin.php
deleted file mode 100644
index ee4ebe3..0000000
--- a/modules/consentSimpleAdmin/config-templates/module_consentSimpleAdmin.php
+++ /dev/null
@@ -1,16 +0,0 @@
-<?php
-
-
-$config = array(
-
- 'store' => array(
- 'consent:Database',
- 'dsn' => 'pgsql:host=sql.uninett.no;dbname=andreas_consent',
- 'username' => 'simplesaml',
- 'password' => 'xxxx',
- ),
-
- 'auth' => 'example-static',
- 'userid' => 'eduPersonPrincipalName',
-
-);
diff --git a/modules/consentSimpleAdmin/default-disable b/modules/consentSimpleAdmin/default-disable
deleted file mode 100644
index fa0bd82..0000000
--- a/modules/consentSimpleAdmin/default-disable
+++ /dev/null
@@ -1,3 +0,0 @@
-This file indicates that the default state of this module
-is disabled. To enable, create a file named enable in the
-same directory as this file.
diff --git a/modules/consentSimpleAdmin/dictionaries/consentsimpleadmin.definition.json b/modules/consentSimpleAdmin/dictionaries/consentsimpleadmin.definition.json
deleted file mode 100644
index 482f857..0000000
--- a/modules/consentSimpleAdmin/dictionaries/consentsimpleadmin.definition.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "header": {
- "en": "Consent withdrawal"
- },
- "granted": {
- "en": "You have earlier granted %NO% consents to %OF% different services."
- },
- "info": {
- "en": "If you withdraw all consents given, you will be asked again each time you visit a new service, whether or not you would like to accept that a given set of personal information are transferred."
- },
- "withdraw": {
- "en": "Withdraw all consent given"
- },
- "headerstats": {
- "en": "Consent Storage Statistics"
- },
- "statusers": {
- "en": "%NO% unique users have given consent."
- },
- "statservices": {
- "en": "Consent is given to %NO% unique services."
- },
- "stattotal": {
- "en": "Consent storage contains %NO% entries."
- }
-}
diff --git a/modules/consentSimpleAdmin/dictionaries/consentsimpleadmin.translation.json b/modules/consentSimpleAdmin/dictionaries/consentsimpleadmin.translation.json
deleted file mode 100644
index 0476942..0000000
--- a/modules/consentSimpleAdmin/dictionaries/consentsimpleadmin.translation.json
+++ /dev/null
@@ -1,242 +0,0 @@
-{
- "header": {
- "no": "Tilbaketrekning av samtykke",
- "sv": "\u00c5tertagande av samtycke",
- "es": "Retirar consentimiento",
- "de": "R\u00fcckzug der Genehmigung",
- "nl": "Toestemming intrekken",
- "sl": "Preklic privolitve",
- "da": "Samtykke tilbagekaldelse",
- "hu": "Hozz\u00e1j\u00e1rul\u00e1s visszavon\u00e1sa",
- "pt": "Remover Consentimento",
- "eu": "Onespena kendu",
- "tr": "Onaydan vazge\u00e7me",
- "hr": "Povla\u010denje odobrenja",
- "fr": "R\u00e9vocation du consentement",
- "it": "Ritirare il consenso",
- "lt": "Leidimo at\u0161aukimas",
- "ja": "\u627f\u8a8d\u3092\u53d6\u308a\u6d88\u3059",
- "zh-tw": "\u540c\u610f\u64a4\u92b7",
- "nn": "Tilbaketrekking av samtykke",
- "et": "N\u00f5usoleku tagasiv\u00f5tmine",
- "he": "\u05d4\u05e1\u05e8\u05ea \u05d4\u05e1\u05db\u05de\u05d4",
- "zh": "\u64a4\u9500\u8bb8\u53ef",
- "ar": "\u0627\u0644\u063a\u0627\u0621 \u0627\u0644\u0645\u0648\u0627\u0641\u0642\u0629",
- "lv": "Noteikumu anul\u0113\u0161ana",
- "id": "Penarikan consent",
- "sr": "Povla\u010denje odobrenja",
- "ro": "Retragerea consim\u021b\u0103m\u00e2ntului",
- "ru": "\u041e\u0442\u043e\u0437\u0432\u0430\u0442\u044c \u0441\u043e\u0433\u043b\u0430\u0441\u0438\u0435",
- "cs": "Zru\u0161it povolen\u00ed"
- },
- "granted": {
- "no": "Du har tidligere gitt samtykke %NO% ganger til %OF% forskjellige tjenester.",
- "sv": "Du har tidigare godk\u00e4nt %NO% samtycken till %OF% olika tj\u00e4nster.",
- "es": "Anteriormente permiti\u00f3 %NO% consentimientos para %OF% servicios diferentes.",
- "de": "Sie haben bisher %NO% Genehmigungen an %OF% verschiedene Services erteilt.",
- "nl": "Je hebt eerder %NO% toestemmingen gegeven aan %OF% verschillende diensten.",
- "sl": "Potrdili ste %NO% privolitev za %OF% razli\u010dnih storitev.",
- "da": "Du har tidligere givet %NO% samtykker til %OF% forskellige services.",
- "hu": "%NO% db. hozz\u00e1j\u00e1rul\u00e1st adott %OF% k\u00fcl\u00f6nb\u00f6z\u0151 szolg\u00e1ltat\u00e1snak.",
- "pt": "Voc\u00ea tem %NO% consentimentos definidos anteriormente para %OF% servi\u00e7os diferentes.",
- "tr": "Daha \u00f6nce %NO% onaylar\u0131n\u0131 %OF% farkl\u0131 servisler i\u00e7in garanti ettiniz.",
- "hr": "Do sada ste dali %NO% odobrenja za %OF% razli\u010ditih servisa.",
- "fr": "Vous aviez pr\u00e9c\u00e9demment donn\u00e9 %NO% consentements \u00e0 %OF% services diff\u00e9rents.",
- "it": "Hai precedentemente dato %NO% consensi a %OF% diversi servizi.",
- "lt": "J\u016bs anks\u010diau suteik\u0117te %NO% ledimus %OF% \u012fvairioms paslaugoms.",
- "ja": "\u3042\u306a\u305f\u306f %OF% \u306e\u7570\u306a\u308b\u30b5\u30fc\u30d3\u30b9\u306b %NO% \u306e\u627f\u8a8d\u304c\u3042\u308a\u307e\u3059\u3002",
- "zh-tw": "\u60a8\u5148\u524d\u6388\u4e88 %NO% \u500b\u8a31\u53ef\u81f3 %OF% \u500b\u4e0d\u540c\u670d\u52d9\u3002",
- "nn": "Du har tidlegare gjeve %NO% samtykke til %OF% ulike tenester.",
- "et": "Sa oled varem andnud %NO% n\u00f5usolekut %OF% erinevale teenusele.",
- "he": "\u05db\u05d1\u05e8 \u05d4\u05e2\u05e0\u05e7\u05ea %NO% \u05d4\u05e1\u05db\u05de\u05d5\u05ea \u05dc %OF% \u05e9\u05d9\u05e8\u05d5\u05ea\u05d9\u05dd \u05e9\u05d5\u05e0\u05d9\u05dd",
- "zh": "\u4f60\u5728\u5148\u524d\u6388\u6743\u4e86%NO%\u8bb8\u53ef\u7ed9%OF%\u4e0d\u540c\u7684\u670d\u52a1",
- "ar": "\u0644\u0642\u062f \u0642\u0645\u062a \u0628\u0625\u0639\u0637\u0627\u0621 \u066a\u0639\u062f\u062f\u066a \u0645\u0648\u0627\u0641\u0642\u0629 \u0644 \u066a\u0644\u066a \u0627\u0644\u062e\u062f\u0645\u0627\u062a \u0627\u0644\u0645\u062e\u062a\u0644\u0641\u0629",
- "lv": "J\u016bs esat piekritis %NO% noteikumiem %OF% da\u017e\u0101diem servisiem.",
- "id": "Sebelumnya anda telah memberikan memberikan hak akses sejumlah %NO% consent kepada %OF% layanan berbeda.",
- "sr": "Do sada ste dali %NO% odobrenja za %OF% razli\u010ditih servisa.",
- "ro": "A\u021bi acordat mai devreme %NO% consim\u021b\u0103minte pentru %OF% servicii diferite.",
- "ru": "\u0412\u044b \u0440\u0430\u043d\u0435\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u043b\u0438 \u0441\u043e\u0433\u043b\u0430\u0441\u0438\u0435 %NO% \u0434\u043b\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 %OF% \u0441\u043b\u0443\u0436\u0431.",
- "cs": "D\u0159\u00edve jste ud\u011blili %NO% povolen\u00ed %OF% r\u016fzn\u00fdm slu\u017eb\u00e1m.",
- "eu": "Lehenago %NO% onespen onartu dituzu %OF% zerbitzu ezberdinentzat."
- },
- "info": {
- "no": "Dersom du trekker tilbake alle tidligere gitte samtykker, s\u00e5 vil du hver gang du bes\u00f8ker en tjeneste p\u00e5 nytt bli spurt om du aksepterer at en gitt liste med personlig informasjon blir overf\u00f8rt til tjenesten.",
- "sv": "Om du drar tillbaka alla samtycken som du godk\u00e4nt kommer du \u00e5ter bli tillfr\u00e5gad n\u00e4r du bes\u00f6ker tj\u00e4nsterna f\u00f6r f\u00f6rsta g\u00e5ngen oberoende om du vill eller inte vill godk\u00e4nna att personuppgifter \u00f6verf\u00f6rs.",
- "es": "Si retira todos los consentimientos concedidos, se le preguntar\u00e1 de nuevo cada vez que visite un nuevo servicio, independientemente de que acepte o rechace que un conjunto de datos personales sean transferidos.",
- "de": "Wenn Sie alle Genehmigungen zur\u00fcckziehen, m\u00fcssen Sie beim erneuten Besuch eines Services, wieder die Genehmigung erteilen, dass Ihre personenbezogenen Daten \u00fcbertragen werden d\u00fcrfen.",
- "nl": "Als je alle gegeven toestemmingen intrekt, zal je elke keer dat je een nieuwe dienst bezoekt opnieuw gevraagd worden of je een gegeven set persoonsgegevens wilt vrijgeven.",
- "sl": "V primeru preklica vseh privolitev, boste ob dostopu do storitev ponovno pozvani k privolitvi posredovanja osebnih podatkov storitvi.",
- "da": "Hvis du tilbagekalder alle dine samtykker, s\u00e5 vil du blive spurgt om at afgive samtykke hver gang du tilg\u00e5r en ny tjeneste.",
- "hu": "Ha visszavonja az \u00f6sszes hozz\u00e1j\u00e1rul\u00e1st, akkor minden alkalommal, amikor egy \u00faj szolg\u00e1ltat\u00e1st k\u00edv\u00e1n ig\u00e9nybe venni, hozz\u00e1 kell j\u00e1rulnia szem\u00e9lyes adatainak kiad\u00e1s\u00e1hoz.",
- "pt": "Se remover os consentimentos definidos, ir-lhe-\u00e1 ser perguntado se aceita o envio de informa\u00e7\u00e3o pessoal de cada vez que aceder a um novo servi\u00e7o.",
- "tr": "Verilen t\u00fcm onaylamalardan vazge\u00e7erseniz, verilen bir ki\u015fisel bilgi k\u00fcmesinin aktar\u0131lmas\u0131n\u0131 kabul etmeyi isteseniz istemeseniz de, yeni bir servise her girdi\u011finizde tekrar sorgulanacaks\u0131n\u0131z",
- "hr": "Ako povu\u010dete sva dosada\u0161nja odobrenja, svaki put kad pristupite novom servisu aplikacija \u0107e od vas tra\u017eiti odobrenje za isporu\u010divanje odgovaraju\u0107eg skupa osobnih podataka.",
- "fr": "Si vous r\u00e9voquez tous les consentements donn\u00e9s, il vous sera demand\u00e9 \u00e0 chaque acc\u00e8s sur un nouveau service si vous acceptez que vos donn\u00e9es personnelles soient transmises ou non.",
- "it": "Se ritiri tutti i consensi forniti, ogni volta che visiterai un nuovo servizio, ti verr\u00e0 chiesto se vorrai trasferire un insieme di informazioni personali.",
- "lt": "Jei at\u0161auksime visus suteiktus leidimus, kiekvien\u0105 kart\u0105 kai lankysite nauj\u0105 paslaug\u0105 v\u0117l b\u016bsite klausiamas, ar sutinkate, kad b\u016bt\u0173 perduodamas duotasis asmenin\u0117s informacijos rinkinys.",
- "ja": "\u4e0e\u3048\u3089\u308c\u305f\u627f\u8a8d\u3092\u5168\u3066\u53d6\u308a\u6d88\u3059\u5834\u5408\u3001\u500b\u4eba\u60c5\u5831\u306e\u8ee2\u9001\u8a31\u53ef\u306b\u95a2\u308f\u3089\u305a\u3001\u65b0\u3057\u3044\u30b5\u30fc\u30d3\u30b9\u306b\u8a2a\u308c\u305f\u969b\u306b\u6bce\u56de\u5c0b\u306d\u3089\u308c\u307e\u3059\u3002",
- "zh-tw": "\u5982\u679c\u4f60\u64a4\u92b7\u6240\u6709\u8f49\u79fb\u8a31\u53ef\uff0c\u4e4b\u5f8c\u6bcf\u6b21\u8a2a\u554f\u4e00\u500b\u65b0\u670d\u52d9\u6642\uff0c\u60a8\u90fd\u6703\u88ab\u518d\u6b21\u8a62\u554f\u662f\u5426\u540c\u610f\u500b\u4eba\u8cc7\u6599\u5c07\u88ab\u50b3\u9001\u7684\u8f49\u79fb\u8a31\u53ef\u3002",
- "nn": "Dersom du trekk tilbake samtykke, blir du spurd ved neste innlogging om du er villig til \u00e5 overf\u00f8ra informasjon til tenesta. Dette skjer f\u00f8r innlogging og inneber ikkje at tenesta ser informasjon, men du f\u00e5r vist fram kva som blir overf\u00f8rt.",
- "et": "Kui v\u00f5tad k\u00f5ik antud n\u00f5usolekud tagasi, siis k\u00fcsitakse sult igat teenust k\u00fclastades uuesti, kas soovid isikuandmete hulka edastada.",
- "he": "\u05d0\u05dd \u05ea\u05e1\u05d9\u05e8 \u05d0\u05ea \u05db\u05dc \u05d4\u05d4\u05e1\u05db\u05de\u05d5\u05ea \u05e9\u05e0\u05ea\u05ea, \u05d0\u05d6\u05d9 \u05ea\u05e9\u05d0\u05dc \u05e9\u05d5\u05d1 \u05db\u05dc \u05e4\u05e2\u05dd \u05d1\u05e9\u05ea\u05d1\u05e7\u05e8 \u05d1\u05e9\u05d9\u05e8\u05d5\u05ea \u05d7\u05d3\u05e9, \u05d4\u05d0\u05dd \u05d0\u05ea\u05d4 \u05de\u05e1\u05db\u05d9\u05dd \u05d0\u05d5 \u05dc\u05d0 \u05e9\u05de\u05d9\u05d3\u05e2 \u05d0\u05d9\u05e9\u05d9 \u05de\u05e1\u05d5\u05d9\u05d9\u05dd \u05d9\u05d5\u05e2\u05d1\u05e8.",
- "zh": "\u5982\u679c\u4f60\u64a4\u9500\u8bb8\u53ef\uff0c\u4f60\u5c06\u6bcf\u6b21\u8bbf\u95ee\u4e00\u4e2a\u65b0\u670d\u52a1\u65f6\u90fd\u5c06\u88ab\u8be2\u95ee\uff1a\u662f\u5426\u613f\u610f\u63a5\u53d7\u5c06\u4f60\u7684\u4e2a\u4eba\u4fe1\u606f\u53d1\u9001\u51fa\u53bb",
- "ar": "\u0627\u0630\u0627 \u0642\u0645\u062a \u0628\u0625\u0644\u063a\u0627\u0621 \u062c\u0645\u064a\u0639 \u0627\u0644\u0645\u0648\u0627\u0641\u0642\u0627\u062a \u0627\u0644\u0645\u0645\u0646\u0648\u062d\u0629 \u0633\u064a\u062a\u0645 \u0633\u0624\u0627\u0644\u0643 \u0643\u0644\u0645\u0627 \u062f\u062e\u0644\u062a \u0644\u062e\u062f\u0645\u0629 \u062c\u062f\u064a\u062f\u0629 \u0639\u0646 \u0642\u0628\u0648\u0644\u0643 \u0627\u0648 \u0631\u0641\u0636\u0643 \u0644\u062a\u062d\u0648\u064a\u0644 \u0628\u0639\u0636 \u0645\u0639\u0644\u0648\u0645\u0627\u062a\u0643 \u0627\u0644\u0634\u062e\u0635\u064a\u0629",
- "lv": "Ja nepiekr\u012btat visiem noteikumiem, katru reizi Jums tiks jaut\u0101ts, vai piekr\u012btat, ka person\u012bg\u0101 inform\u0101cija tiks p\u0101rraid\u012bta pa t\u012bklu.",
- "id": "Jika anda menarik semua consent yang diberikan, anda akan ditanya lagi setiap kalu anda mengunjungi sebuah layanan baru, apakah anda ingin atau tidak jika sekumpulan informasi personal itu ditransfer.",
- "sr": "Ako povu\u010dete sva dosada\u0161nja odobrenja, svaki put kad pristupite novom servisu aplikacija \u0107e od vas tra\u017eiti odobrenje za isporu\u010divanje odgovaraju\u0107eg skupa li\u010dnih podataka.",
- "ro": "Dac\u0103 anula\u021bi toate consim\u021b\u0103mintele acordate, de fiecare dat\u0103 c\u00e2nd accesa\u021bi un serviciu nou ve\u021bi fi \u00eentrebat dac\u0103 sunte\u021bi de acord ca o parte din informa\u021biile personale s\u0103 fie transferat\u0103.",
- "ru": "\u0415\u0441\u043b\u0438 \u0432\u044b \u043e\u0442\u0437\u044b\u0432\u0430\u0435\u0442\u0435 \u0432\u0441\u0435 \u0440\u0430\u043d\u0435\u0435 \u0432\u044b\u0434\u0430\u043d\u043d\u044b\u0435 \u0441\u043e\u0433\u043b\u0430\u0441\u0438\u044f, \u0442\u043e \u0432\u0430\u0441 \u0431\u0443\u0434\u0443\u0442 \u043e\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044c \u043e \u0441\u043e\u0433\u043b\u0430\u0441\u0438\u0438 \u0432\u0441\u044f\u043a\u0438\u0439 \u0440\u0430\u0437 \u043f\u0440\u0438 \u0432\u0430\u0448\u0435\u043c \u0434\u043e\u0441\u0442\u0443\u043f\u0435 \u043a \u043d\u043e\u0432\u043e\u0439 \u0441\u043b\u0443\u0436\u0431\u0435, \u0432\u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043f\u0440\u0438\u043d\u044f\u0442\u0438\u044f \u0443\u0441\u043b\u043e\u0432\u0438\u044f \u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0435 \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u043d\u0430\u0431\u043e\u0440\u0430 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445.",
- "cs": "Pokud zru\u0161\u00edte ve\u0161ker\u00e1 ud\u011blen\u00e1 povolen\u00ed, budete p\u0159i ka\u017ed\u00e9 n\u00e1v\u0161t\u011bv\u011b nov\u00e9 slu\u017eby dot\u00e1z\u00e1ni, zda souhlas\u00edte \u010di nesouhlas\u00edte s p\u0159enosem ur\u010dit\u00e9 skupiny osobn\u00edch informac\u00ed.",
- "eu": "Emandako onespen guztiak kentzen badituzu,zerbitzu berri bat bisitatzen duzun bakoitzean berriro galdetuko dizugu, datu multzo bat transferitua izatea onartu edo baztertzen duzun alde batera utzita."
- },
- "withdraw": {
- "no": "Trekk tilbake alle tidligere gitte samtykker",
- "sv": "Dra tillbaka alla godk\u00e4nda samtycken",
- "es": "Retirar todos los consentimientos concedidos",
- "de": "Alle Genehmigungen zur\u00fcckziehen",
- "nl": "Trek alle gegeven toestemming in",
- "sl": "Prekli\u010di vse privolitve.",
- "da": "Tilbagekald alle samtykker",
- "hu": "Az \u00f6sszes hozz\u00e1j\u00e1rul\u00e1s visszavon\u00e1sa",
- "pt": "Remover todos os Consentimentos definidos",
- "eu": "Emandako onespen guztiak kendu",
- "tr": "Verilen t\u00fcm onaylardan vazge\u00e7",
- "hr": "Povuci sva odobrenja",
- "fr": "R\u00e9voquer tous les consentements donn\u00e9s",
- "it": "Ritira tutti i consensi forniti",
- "lt": "At\u0161aukti visus suteiktus leidimus",
- "ja": "\u5168\u3066\u306e\u627f\u8a8d\u3092\u53d6\u308a\u6d88\u3059",
- "zh-tw": "\u64a4\u92b7\u6240\u6709\u8f49\u79fb\u8a31\u53ef",
- "nn": "Trekk tilbake alle samtykke",
- "et": "V\u00f5ta k\u00f5ik n\u00f5usolekud tagasi",
- "he": "\u05d4\u05e1\u05e8 \u05d0\u05ea \u05db\u05dc \u05d4\u05d4\u05e1\u05db\u05de\u05d5\u05ea \u05e9\u05e0\u05d9\u05ea\u05e0\u05d5",
- "zh": "\u64a4\u9500\u6240\u6709\u8bb8\u53ef",
- "ar": "\u0627\u0644\u063a \u0643\u0644 \u0627\u0644\u0645\u0648\u0627\u0641\u0642\u0627\u062a \u0627\u0644\u0645\u0645\u0646\u0648\u062d\u0629",
- "lv": "Nepiekrist visiem dotajiem noteikumiem",
- "id": "Tarik semua consent yang diberikan",
- "sr": "Povuci sva odobrenja",
- "ro": "Retrage toate consim\u021b\u0103mintele",
- "ru": "\u0412\u044b\u0434\u0430\u043d \u043e\u0442\u043a\u0430\u0437 \u043e\u0442 \u0432\u0441\u0435\u0445 \u0440\u0430\u043d\u0435\u0435 \u0432\u044b\u0434\u0430\u043d\u043d\u044b\u0445 \u0441\u043e\u0433\u043b\u0430\u0441\u0438\u0439",
- "cs": "Zru\u0161it ve\u0161ker\u00e1 ud\u011blen\u00e1 povolen\u00ed"
- },
- "headerstats": {
- "no": "Samtykke statistikk",
- "sv": "Statistik f\u00f6r lagret \u00f6ver samtycken",
- "es": "Estad\u00edsticas de consentimientos almacenados",
- "de": "Statistik \u00fcber Ihre Genehmigungen",
- "nl": "Statistiek",
- "sl": "Statistika privolitev",
- "da": "Samtykkelager statestik",
- "hu": "Hozz\u00e1j\u00e1rul\u00e1s t\u00e1rol\u00f3 statisztik\u00e1k",
- "pt": "Estat\u00edsticas do armazenamento de Consentimentos",
- "tr": "Onay Depolama \u0130statistikleri",
- "hr": "Statistike odobrenja",
- "fr": "Statistiques du d\u00e9p\u00f4t de consentement",
- "it": "Statistiche sui consensi memorizzati",
- "lt": "Leidim\u0173 Saugyklos Statistika",
- "ja": "\u627f\u8a8d\u30b9\u30c8\u30ec\u30fc\u30b8\u7d71\u8a08",
- "zh-tw": "\u8a31\u53ef\u5132\u5b58\u7d71\u8a08",
- "nn": "Statistikk for samtykke",
- "et": "N\u00f5usolekute salvestamise statistika",
- "he": "\u05e1\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d5\u05ea \u05e2\u05d1\u05d5\u05e8 \u05de\u05d0\u05d2\u05e8 \u05d4\u05d4\u05e1\u05db\u05de\u05d5\u05ea",
- "zh": "\u9759\u6001\u5b58\u50a8\u8bb8\u53ef",
- "ar": "\u0627\u062d\u0635\u0627\u0626\u064a\u0627\u062a \u0627\u0644\u0645\u0648\u0627\u0641\u0642\u0627\u062a \u0627\u0644\u0645\u062e\u062a\u0632\u0646\u0629",
- "lv": "Noteikumu glab\u0101tuves statistika",
- "id": "Statistik Penyimpanan Consent",
- "sr": "Statistike odobrenja",
- "ro": "Statistici despre stocarea consim\u021b\u0103mintelor",
- "ru": "\u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0441\u043e\u0433\u043b\u0430\u0441\u0438\u0439",
- "cs": "Statistika ulo\u017een\u00fdch povolen\u00ed",
- "eu": "Biltegiratutako onespenen estatistikak"
- },
- "stattotal": {
- "no": "Samtykkelageret inneholder %NO% innslag.",
- "sv": "Lagret inneh\u00e5ller %NO% samtycken.",
- "es": "El almac\u00e9n de consentimientos tiene %NO% entradas.",
- "de": "Sie haben %NO% Genehmigungen erteilt",
- "nl": "Je toestemmingsopslag bevat %NO% items.",
- "sl": "Privolitvena stran vsebuje %NO% zapisov.",
- "da": "Samtykkelager indeholder %NO% samtykker.",
- "hu": "A hozz\u00e1j\u00e1rul\u00e1s t\u00e1rol\u00f3 %NO% bejegyz\u00e9st tartalmaz.",
- "pt": "O armazenamento de consentimentos cont\u00e9m %NO% entradas.",
- "tr": "Onay depolama %NO% elemanlar\u0131 i\u00e7ermektedir",
- "hr": "Arhiva odobrenja sadr\u017ei %NO% zapisa.",
- "fr": "Le d\u00e9p\u00f4t de consentements contient %NO% \u00e9l\u00e9ments. ",
- "it": "Immagazzinati consensi per %NO% voci.",
- "lt": "Leidim\u0173 saugyklose yra %NO% \u012fra\u0161\u0173.",
- "ja": "\u627f\u8a8d\u30b9\u30c8\u30ec\u30fc\u30b8\u306f %NO% \u30a8\u30f3\u30c8\u30ea\u3042\u308a\u307e\u3059\u3002",
- "zh-tw": "\u540c\u610f\u5132\u5b58\u5305\u542b %NO% \u9805\u76ee\u3002",
- "nn": "Samtykkelageret har %NO% innslag.",
- "et": "Salvestatud on %NO% n\u00f5usolekut.",
- "he": "\u05de\u05d0\u05d2\u05e8 \u05d4\u05d4\u05e1\u05db\u05de\u05d5\u05ea \u05de\u05db\u05d9\u05dc %NO% \u05e8\u05e9\u05d5\u05de\u05d5\u05ea.",
- "zh": "\u8bb8\u53ef\u5b58\u50a8\u5305\u542b %NO% \u5b9e\u4f53",
- "ar": "\u0645\u0633\u062a\u0648\u062f\u0639 \u0627\u0644\u0645\u0648\u0627\u0641\u0642\u0627\u062a \u064a\u062d\u062a\u0648\u064a \u0639\u0644\u064a \u066a\u0639\u062f\u062f\u066a \u0627\u062f\u062e\u0627\u0644",
- "lv": "Noteikumu glab\u0101tava satur %NO% vien\u012bbas.",
- "id": "Penyimpan consent mengandung %NO% entri.",
- "sr": "Arhiva odobrenja sadr\u017ei %NO% zapisa.",
- "ro": "Zona de stocare a consim\u021b\u0103mintelor con\u021bine %NO% intr\u0103ri.",
- "ru": "\u0425\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0441\u043e\u0433\u043b\u0430\u0441\u0438\u0439 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 %NO% \u0437\u0430\u043f\u0438\u0441\u0435\u0439.",
- "cs": "\u00dalo\u017ei\u0161t\u011b povolen\u00ed obsahuje %NO% z\u00e1znam\u016f.",
- "eu": "Onespenen biltegiak %NO% sarrera ditu."
- },
- "statusers": {
- "no": "%NO% unike brukere har avgitt samtykke.",
- "sv": "%NO% unika anv\u00e4ndare har l\u00e4mnat samtycken.",
- "es": "%NO% usuarios han dado su consentimiento.",
- "de": "%NO% verschiedene Benutzer haben ihre Genehmigung erteilt",
- "nl": "%NO% unieke gebruikers hebben toestemming verleend.",
- "sl": "Privolitev je dalo %NO% razli\u010dnih uporabnikov.",
- "da": "%NO% unikke brugere har givet samtykke",
- "hu": "%NO% k\u00fcl\u00f6nb\u00f6z\u0151 felhaszn\u00e1l\u00f3 adott hozz\u00e1j\u00e1rul\u00e1st.",
- "pt": "%NO\" utilizador distintos deram o seu consentimento.",
- "tr": "%NO% biricik (unique) kullan\u0131c\u0131lar onay verdi.",
- "hr": "%NO% pojedina\u010dnih korisnika je dalo odobrenje.",
- "fr": "%NO% usagers distincts ont donn\u00e9 un consentement.",
- "it": "%NO% distinti utenti hanno dato il consenso.",
- "lt": "%NO% unikali\u0173 naudotoj\u0173 suteik\u0117 leidim\u0105.",
- "ja": "%NO% \u306e\u30e6\u30cb\u30fc\u30af\u30e6\u30fc\u30b6\u30fc\u304b\u3089\u627f\u8a8d\u3092\u4e0e\u3048\u3089\u308c\u3066\u3044\u307e\u3059\u3002",
- "zh-tw": "%NO% \u500b\u4e0d\u540c\u4f7f\u7528\u8005\u64c1\u6709\u8f49\u79fb\u8a31\u53ef\u3002",
- "nn": "%NO% unike brukarar har gjeve samtykke.",
- "et": "%NO% unikaalset kasutajat on andnud n\u00f5usolekuid.",
- "he": "%NO% \u05de\u05e9\u05ea\u05de\u05e9\u05d9\u05dd \u05e9\u05d5\u05e0\u05d9\u05dd \u05e0\u05ea\u05e0\u05d5 \u05d4\u05e1\u05db\u05de\u05d5\u05ea",
- "zh": "\u552f\u4e00\u7684%NO%\u7528\u6237\u7ed9\u4e88\u4e86\u8bb8\u53ef",
- "ar": "\u066a\u0639\u062f\u062f\u066a \u0645\u0633\u062a\u062e\u062f\u0645 \u0641\u0631\u064a\u062f \u0627\u0639\u0637\u064a \u0645\u0648\u0627\u0641\u0642\u062a\u0647",
- "lv": "%NO% lietot\u0101ju piekritu\u0161i noteikumoem.",
- "id": "Sejumlah %NO% user telah diberikan consent.",
- "sr": "%NO% pojedina\u010dnih korisnika je dalo odobrenje.",
- "ro": "Un num\u0103r de %NO% utilizatori au fost de acord.",
- "ru": "%NO% \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0434\u0430\u043b\u0438 \u0441\u043e\u0433\u043b\u0430\u0441\u0438\u0435.",
- "cs": "%NO% unik\u00e1tn\u00edch u\u017eivatel\u016f ud\u011blilo povolen\u00ed.",
- "eu": "%NO% erabiltzailek bere onespena eman dute."
- },
- "statservices": {
- "no": "Samtykke er avgitt til %NO% unike tjenester.",
- "sv": "Samtycken \u00e4r l\u00e4mnade f\u00f6r %NO% unika tj\u00e4nster.",
- "es": "Consentimiento dado a %NO% servicios diferentes.",
- "de": "Genehmigung an %NO% Dienste erteilt",
- "nl": "Toestemming is verleend aan %NO% unieke diensten.",
- "sl": "%NO% razli\u010dnih storitev je dobilo privolitev.",
- "da": "Samtykke er givet til %NO% unikke services.",
- "hu": "%NO% k\u00fcl\u00f6nb\u00f6z\u0151 szolg\u00e1ltat\u00e1shoz adtak hozz\u00e1j\u00e1rul\u00e1st.",
- "pt": "Est\u00e3o definidos consentimentos para %NO% servi\u00e7os distintos.",
- "tr": "Onay %NO% biricik (unique) servislere veriliyor.",
- "hr": "Odobrenje je dano za %NO% razli\u010ditih servisa.",
- "fr": "Un consentement a \u00e9t\u00e9 donn\u00e9 \u00e0 %NO% services diff\u00e9rents.",
- "it": "Il consenso \u00e8 stato dato a %NO% servizi distinti.",
- "lt": "Leidimas suteiktas %NO% unikalioms paslaugoms.",
- "ja": "%NO% \u306e\u30e6\u30cb\u30fc\u30af\u30b5\u30fc\u30d3\u30b9\u304b\u3089\u627f\u8a8d\u3092\u4e0e\u3048\u3089\u308c\u3066\u3044\u307e\u3059\u3002",
- "zh-tw": "\u8f49\u79fb\u8a31\u53ef\u7d66\u4e88 %NO% \u500b\u4e0d\u540c\u670d\u52d9\u3002",
- "nn": "Samtykke er gjeve til %NO% unike tenester",
- "et": "N\u00f5usolekuid on antud %NO% unikaalsele teenusele.",
- "he": "\u05d4\u05d4\u05e1\u05db\u05de\u05d4 \u05e0\u05d9\u05ea\u05e0\u05d4 \u05e2\u05d1\u05d5\u05e8 %NO% \u05e9\u05d9\u05e8\u05d5\u05ea\u05d9\u05dd \u05e9\u05d5\u05e0\u05d9\u05dd",
- "zh": "\u8bb8\u53ef\u662f\u7ed9 %NO% \u552f\u4e00\u670d\u52a1",
- "ar": "\u062a\u0645 \u0625\u0639\u0637\u0627\u0621 \u0645\u0648\u0627\u0641\u0642\u0629 \u0644\u066a\u0639\u062f\u062f\u066a \u0645\u062e\u062f\u0645 \u0641\u0631\u064a\u062f",
- "lv": "%NO% servisiem ir lieto\u0161anas noteikumi.",
- "id": "Consent diberikan kepada %NO% layanan.",
- "sr": "Odobrenje je dano za %NO% razli\u010ditih servisa.",
- "ro": "Consim\u021b\u0103m\u00e2ntul a fost acordat pentru %NO% servicii.",
- "ru": "\u0421\u043e\u0433\u043b\u0430\u0441\u0438\u0435 \u0434\u0430\u043d\u043e %NO% \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c.",
- "cs": "Povolen\u00ed ud\u011bleno %NO% unik\u00e1tn\u00edm slu\u017eb\u00e1m.",
- "eu": "Onespena %NO% zerbitzu ezberdinei emana."
- }
-}
diff --git a/modules/consentSimpleAdmin/hooks/hook_frontpage.php b/modules/consentSimpleAdmin/hooks/hook_frontpage.php
deleted file mode 100644
index 0c3658c..0000000
--- a/modules/consentSimpleAdmin/hooks/hook_frontpage.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-/**
- * Hook to add the simple consenet admin module to the frontpage.
- *
- * @param array &$links The links on the frontpage, split into sections.
- */
-function consentSimpleAdmin_hook_frontpage(&$links) {
- assert('is_array($links)');
- assert('array_key_exists("links", $links)');
-
- $links['config'][] = array(
- 'href' => SimpleSAML_Module::getModuleURL('consentSimpleAdmin/consentAdmin.php'),
- 'text' => '{consentSimpleAdmin:consentsimpleadmin:header}',
- );
- $links['config'][] = array(
- 'href' => SimpleSAML_Module::getModuleURL('consentSimpleAdmin/consentStats.php'),
- 'text' => '{consentSimpleAdmin:consentsimpleadmin:headerstats}',
- );
-
-}
diff --git a/modules/consentSimpleAdmin/hooks/hook_sanitycheck.php b/modules/consentSimpleAdmin/hooks/hook_sanitycheck.php
deleted file mode 100644
index 5f829c8..0000000
--- a/modules/consentSimpleAdmin/hooks/hook_sanitycheck.php
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-/**
- *
- * @param array &$hookinfo hookinfo
- */
-function consentSimpleAdmin_hook_sanitycheck(&$hookinfo) {
- assert('is_array($hookinfo)');
- assert('array_key_exists("errors", $hookinfo)');
- assert('array_key_exists("info", $hookinfo)');
-
- try {
- $consentconfig = SimpleSAML_Configuration::getConfig('module_consentSimpleAdmin.php');
-
- // Parse consent config
- $consent_storage = sspmod_consent_Store::parseStoreConfig($consentconfig->getValue('store'));
-
- if (!is_callable(array($consent_storage, 'selftest'))) {
- /* Doesn't support a selftest. */
- return;
- }
- $testres = $consent_storage->selftest();
- if ($testres) {
- $hookinfo['info'][] = '[consentSimpleAdmin] Consent Storage selftest OK.';
- } else {
- $hookinfo['errors'][] = '[consentSimpleAdmin] Consent Storage selftest failed.';
- }
-
- } catch (Exception $e) {
- $hookinfo['errors'][] = '[consentSimpleAdmin] Error connecting to storage: ' . $e->getMessage();
- }
-
-}
diff --git a/modules/consentSimpleAdmin/templates/consentadmin.php b/modules/consentSimpleAdmin/templates/consentadmin.php
deleted file mode 100644
index a19f6df..0000000
--- a/modules/consentSimpleAdmin/templates/consentadmin.php
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-$this->data['header'] = $this->t('{consentSimpleAdmin:consentsimpleadmin:header}');
-$this->includeAtTemplateBase('includes/header.php');
-
-?>
-
-
-
-<p><?php
-
-echo '<p>' . $this->t('{consentSimpleAdmin:consentsimpleadmin:granted}', array(
- '%NO%' => (string)$this->data['consents'],
- '%OF%' => (string)$this->data['consentServices'],
-)) . '</p>';
-
-
-echo '<p>' . $this->t('{consentSimpleAdmin:consentsimpleadmin:info}') . '</p>';
-
-
-?></p>
-
-<!-- <p>You have granted <?php echo $this->data['consents']; ?> consents to <?php echo $this->data['consentServices']; ?> different services.</p>
-
-<p>If you withdraw all consents given, you will be asked again each time you visit a new service, whether or not you would like to accept that a given set of attributes are transferred.</p> -->
-
-<form method="get" action="consentAdmin.php">
-
- <input type="submit" name="withdraw" value="<?php echo $this->t('{consentSimpleAdmin:consentsimpleadmin:withdraw}'); ?>" />
-
-</form>
-<!-- Withdraw all consent given -->
-
-<?php $this->includeAtTemplateBase('includes/footer.php');
diff --git a/modules/consentSimpleAdmin/templates/consentstats.php b/modules/consentSimpleAdmin/templates/consentstats.php
deleted file mode 100644
index 1c80940..0000000
--- a/modules/consentSimpleAdmin/templates/consentstats.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-
-$this->data['header'] = $this->t('{consentSimpleAdmin:consentsimpleadmin:headerstats}');
-$this->includeAtTemplateBase('includes/header.php');
-
-?>
-
-
-<p><?php
-
-echo '<p>' . $this->t('{consentSimpleAdmin:consentsimpleadmin:stattotal}', array('%NO%' => $this->data['stats']['total'])) . '</p>';
-echo '<p>' . $this->t('{consentSimpleAdmin:consentsimpleadmin:statusers}', array('%NO%' => $this->data['stats']['users'])) . '</p>';
-echo '<p>' . $this->t('{consentSimpleAdmin:consentsimpleadmin:statservices}', array('%NO%' => $this->data['stats']['services'])) . '</p>';
-
-
-
-?></p>
-
-
-<?php $this->includeAtTemplateBase('includes/footer.php');
diff --git a/modules/consentSimpleAdmin/www/consentAdmin.php b/modules/consentSimpleAdmin/www/consentAdmin.php
deleted file mode 100644
index 1c576e9..0000000
--- a/modules/consentSimpleAdmin/www/consentAdmin.php
+++ /dev/null
@@ -1,87 +0,0 @@
-<?php
-/*
- * consentSimpleAdmin - Simple Consent administration module
- *
- * This module is a simplification of the danish consent administration module.
- *
- * @author Andreas Åkre Solberg <andreas.solberg@uninett.no>
- * @author Mads Freek - WAYF
- * @author Jacob Christiansen - WAYF
- * @package SimpleSAMLphp
- */
-
-
-// Get config object
-$config = SimpleSAML_Configuration::getInstance();
-$consentconfig = SimpleSAML_Configuration::getConfig('module_consentSimpleAdmin.php');
-
-$as = $consentconfig->getValue('auth');
-$as = new SimpleSAML_Auth_Simple($as);
-$as->requireAuth();
-
-// Get all attributes
-$attributes = $as->getAttributes();
-
-
-// Get user ID
-$userid_attributename = $consentconfig->getValue('userid', 'eduPersonPrincipalName');
-if (empty($attributes[$userid_attributename])) {
- throw new Exception('Could not generate useridentifier for storing consent. Attribute ['.
- $userid_attributename.'] was not available.');
-}
-
-$userid = $attributes[$userid_attributename][0];
-
-// Get metadata storage handler
-$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
-
-// Get IdP id and metadata
-if ($as->getAuthData('saml:sp:IdP') !== null) {
- // From a remote idp (as bridge)
- $idp_entityid = $as->getAuthData('saml:sp:IdP');
- $idp_metadata = $metadata->getMetaData($idp_entityid, 'saml20-idp-remote');
-} else {
- // from the local idp
- $idp_entityid = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted');
- $idp_metadata = $metadata->getMetaData($idp_entityid, 'saml20-idp-hosted');
-}
-
-SimpleSAML_Logger::debug('consentAdmin: IdP is ['.$idp_entityid.']');
-
-$source = $idp_metadata['metadata-set'].'|'.$idp_entityid;
-
-
-// Parse consent config
-$consent_storage = sspmod_consent_Store::parseStoreConfig($consentconfig->getValue('store'));
-
-// Calc correct user ID hash
-$hashed_user_id = sspmod_consent_Auth_Process_Consent::getHashedUserID($userid, $source);
-
-
-// Check if button with withdraw all consent was clicked.
-if (array_key_exists('withdraw', $_REQUEST)) {
-
- SimpleSAML_Logger::info('consentAdmin: UserID ['.$hashed_user_id.'] has requested to withdraw all consents given...');
-
- $consent_storage->deleteAllConsents($hashed_user_id);
-}
-
-
-// Get all consents for user
-$user_consent_list = $consent_storage->getConsents($hashed_user_id);
-
-$consentServices = array();
-foreach ($user_consent_list AS $c) {
- $consentServices[$c[1]] = 1;
-}
-
-SimpleSAML_Logger::debug('consentAdmin: no of consents ['.count($user_consent_list).'] no of services ['.count($consentServices).']');
-
-// Init template
-$t = new SimpleSAML_XHTML_Template($config, 'consentSimpleAdmin:consentadmin.php');
-
-$t->data['consentServices'] = count($consentServices);
-$t->data['consents'] = count($user_consent_list);
-
-
-$t->show();
diff --git a/modules/consentSimpleAdmin/www/consentStats.php b/modules/consentSimpleAdmin/www/consentStats.php
deleted file mode 100644
index a9339c0..0000000
--- a/modules/consentSimpleAdmin/www/consentStats.php
+++ /dev/null
@@ -1,29 +0,0 @@
-<?php
-/*
- * consentSimpleAdmin - Simple Consent administration module
- *
- * shows statistics.
- *
- * @author Andreas Åkre Solberg <andreas.solberg@uninett.no>
- * @package simpleSAMLphp
- */
-
-
-// Get config object
-$config = SimpleSAML_Configuration::getInstance();
-$consentconfig = SimpleSAML_Configuration::getConfig('module_consentSimpleAdmin.php');
-
-
-// Parse consent config
-$consent_storage = sspmod_consent_Store::parseStoreConfig($consentconfig->getValue('store'));
-
-// Get all consents for user
-$stats = $consent_storage->getStatistics();
-
-// Init template
-$t = new SimpleSAML_XHTML_Template($config, 'consentSimpleAdmin:consentstats.php');
-
-$t->data['stats'] = $stats;
-
-
-$t->show();
diff --git a/modules/core/docs/authproc_php.txt b/modules/core/docs/authproc_php.txt
index 7fc4a0d..9e0b7bc 100644
--- a/modules/core/docs/authproc_php.txt
+++ b/modules/core/docs/authproc_php.txt
@@ -10,10 +10,13 @@ Parameters
: This is the name of the filter.
It must be `'core:PHP'`.
+`function`
+: The PHP function that should be run, defined as an anonymous function with one parameter called `$attributes`.
+ This is an associative array with the user's attributes, and can be modified to add or remove them.
+
`code`
-: The PHP code that should be run.
- This PHP code will have one variable available - `$attributes`.
- This is an associative array of attributes, and can be modified to add or remove attributes.
+: **Deprecated**
+ If you are using this option, please migrate your code to an anonymous function defined in the `function` option.
Examples
--------
@@ -22,15 +25,15 @@ Add the `mail` attribute based on the user's `uid` attribute:
10 => array(
'class' => 'core:PHP',
- 'code' => '
- if (empty($attributes["uid"])) {
- throw new Exception("Missing uid attribute.");
+ 'function' => function (&$attributes) {
+ if (empty($attributes['uid'])) {
+ throw new Exception('Missing uid attribute.');
}
- $uid = $attributes["uid"][0];
- $mail = $uid . "@example.net";
- $attributes["mail"] = array($mail);
- ',
+ $uid = $attributes['uid'][0];
+ $mail = $uid.'@example.net';
+ $attributes['mail'] = array($mail);
+ },
),
@@ -38,9 +41,9 @@ Create a random number variable:
10 => array(
'class' => 'core:PHP',
- 'code' => '
- $attributes["random"] = array(
+ 'code' => function (&$attributes) {
+ $attributes['random'] = array(
(string)rand(),
);
- ',
+ },
),
diff --git a/modules/core/lib/Auth/Process/PHP.php b/modules/core/lib/Auth/Process/PHP.php
index cecf989..e54af28 100644
--- a/modules/core/lib/Auth/Process/PHP.php
+++ b/modules/core/lib/Auth/Process/PHP.php
@@ -1,50 +1,70 @@
<?php
+
/**
* Attribute filter for running arbitrary PHP code.
*
* @package simpleSAMLphp
*/
-class sspmod_core_Auth_Process_PHP extends SimpleSAML_Auth_ProcessingFilter {
-
- /**
- * The PHP code that should be run.
- *
- * @var string
- */
- private $code;
-
-
- /**
- * Initialize this filter, parse configuration
- *
- * @param array $config Configuration information about this filter.
- * @param mixed $reserved For future use.
- */
- public function __construct($config, $reserved) {
- parent::__construct($config, $reserved);
-
- assert('is_array($config)');
-
- if (!isset($config['code'])) {
- throw new SimpleSAML_Error_Exception($this->authId . ': Missing required \'code\' option.');
- }
-
- $this->code = (string)$config['code'];
- }
-
-
- /**
- * Apply the PHP code to the attribtes.
- *
- * @param array &$request The current request
- */
- public function process(&$request) {
- assert('is_array($request)');
- assert('array_key_exists("Attributes", $request)');
-
- $function = create_function('&$attributes', $this->code);
- $function($request['Attributes']);
- }
+class sspmod_core_Auth_Process_PHP extends SimpleSAML_Auth_ProcessingFilter
+{
+
+ /**
+ * The PHP code that should be run.
+ *
+ * @var string
+ */
+ private $code;
+
+ /**
+ * @var callable
+ */
+ private $function = null;
+
+
+ /**
+ * Initialize this filter, parse configuration
+ *
+ * @param array $config Configuration information about this filter.
+ * @param mixed $reserved For future use.
+ */
+ public function __construct($config, $reserved)
+ {
+ parent::__construct($config, $reserved);
+
+ assert('is_array($config)');
+
+ if (isset($config['function'])) {
+ $this->function = $config['function'];
+ } else { // TODO: remove this branch after removing the 'code' option.
+ if (!isset($config['code'])) {
+ throw new SimpleSAML_Error_Exception("core:PHP: Neither 'function' nor 'code' options defined.");
+ }
+ SimpleSAML_Logger::warning(
+ "Deprecated 'code' configuration option in PHP authentication processing filter."
+ );
+ $this->code = (string) $config['code'];
+ }
+ }
+
+
+ /**
+ * Apply the PHP code to the attributes.
+ *
+ * @param array &$request The current request
+ */
+ public function process(&$request)
+ {
+ assert('is_array($request)');
+ assert('array_key_exists("Attributes", $request)');
+
+ if ($this->function) {
+ $function = $this->function;
+ $function($request['Attributes']);
+ } else { // TODO: remove this branch after removing the 'code' option.
+ $function = create_function('&$attributes', $this->code);
+ $function($request['Attributes']);
+ }
+ }
}
diff --git a/modules/core/www/frontpage_config.php b/modules/core/www/frontpage_config.php
index 74c8c61..d04beaf 100644
--- a/modules/core/www/frontpage_config.php
+++ b/modules/core/www/frontpage_config.php
@@ -85,7 +85,7 @@ $functionchecks = array(
'simplexml_import_dom' => array('required', 'SimpleXML'),
'dom_import_simplexml' => array('required', 'XML DOM'),
'preg_match' => array('required', 'RegEx support'),
- 'mcrypt_module_open'=> array('required', 'MCrypt'),
+ 'mcrypt_module_open'=> array('optional', 'MCrypt'),
'mysql_connect' => array('optional', 'MySQL support'),
);
if (SimpleSAML_Module::isModuleEnabled('ldap')) {
diff --git a/modules/core/www/postredirect.php b/modules/core/www/postredirect.php
index 3daa0cd..cd124ef 100644
--- a/modules/core/www/postredirect.php
+++ b/modules/core/www/postredirect.php
@@ -45,8 +45,8 @@ assert('array_key_exists("url", $postData)');
assert('array_key_exists("post", $postData)');
$config = SimpleSAML_Configuration::getInstance();
-$p = new SimpleSAML_XHTML_Template($config, 'post.php');
-$p->data['destination'] = $postData['url'];
-$p->data['post'] = $postData['post'];
-$p->show();
+$template = new SimpleSAML_XHTML_Template($config, 'post.php');
+$template->data['destination'] = $postData['url'];
+$template->data['post'] = $postData['post'];
+$template->show();
exit(0);
diff --git a/modules/discojuice/config-templates/discojuice.php b/modules/discojuice/config-templates/discojuice.php
deleted file mode 100644
index 19e5198..0000000
--- a/modules/discojuice/config-templates/discojuice.php
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-
-/**
- * This is the configuration file for the DiscoJuice.
- */
-
-$config = array(
-
- // A human readable name describing the Service Provider
- 'name' => 'Service',
-
- /* A set of prepared metadata feeds from discojuice.org
- * You may visit
- * https://static.discojuice.org/feeds/
- *
- * to review the available feed identifiers.
- * You may choose to not use any of the provider feed, by setting this to an
- * empty array: array()
- */
- 'feeds' => array('edugain'),
-
- /*
- * You may provide additional feeds
- */
- 'additionalFeeds' => array(
- ),
-
- /*
- * If you set this value to true, the module will contact discojuice.org to read and write cookies.
- * If you enable this, you will also need to get your host accepted in the access control list of
- * discojuice.org
- *
- * The response URL of your service, similar to:
- *
- * https://sp.example.org/simplesaml/module.php/discojuice/response.html
- *
- * will need to be registered at discojuice.org. If your response URL is already registered in the metadata
- * of one of the federation feeds at discojuice.org, you should already have access.
- */
- 'enableCentralStorage' => false,
-
-); \ No newline at end of file
diff --git a/modules/discojuice/default-disable b/modules/discojuice/default-disable
deleted file mode 100644
index e69de29..0000000
--- a/modules/discojuice/default-disable
+++ /dev/null
diff --git a/modules/discojuice/templates/central.tpl.php b/modules/discojuice/templates/central.tpl.php
deleted file mode 100644
index 0695b67..0000000
--- a/modules/discojuice/templates/central.tpl.php
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-
-header('P3P:CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"');
-
-?><!DOCTYPE html>
-<html lang="en">
-<head>
- <meta charset="utf-8" />
- <title>Select Your Login Provider</title>
-
- <!-- JQuery hosted by Google -->
- <script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js" type="text/javascript"></script>
-
- <!-- DiscoJuice hosted by UNINETT at discojuice.org -->
- <script type="text/javascript" src="https://engine.discojuice.org/discojuice-stable.min.js"></script>
- <link rel="stylesheet" type="text/css" href="https://static.discojuice.org/css/discojuice.css" />
-
- <style type="text/css">
- body {
- text-align: center;
- }
- div.discojuice {
- text-align: left;
- position: relative;
- width: 600px;
- margin-right: auto;
- margin-left: auto;
- }
- </style>
-
- <script type="text/javascript">
-
-<?php
-
- echo '
- $("document").ready(function() {
- var djc = DiscoJuice.Hosted.getConfig(' .
- json_encode($this->data['hostedConfig'][0]) . "," .
- json_encode($this->data['hostedConfig'][1]) . "," .
- json_encode($this->data['hostedConfig'][2]) . "," .
- json_encode($this->data['hostedConfig'][3]) . "," .
- json_encode($this->data['hostedConfig'][4]) .
- ');';
-
- if (!$this->data['enableCentralStorage']) {
- echo " delete djc.disco;\n";
- }
- if (!empty($this->data['additionalFeeds'])) {
- foreach($this->data['additionalFeeds'] AS $feed) {
- echo " djc.metadata.push(" . json_encode($feed) . ");\n";
- }
- }
-
- echo " djc.always = true;\n";
-
- echo '
- $("a.signin").DiscoJuice(djc);
- });
- ';
-
-?>
-
-
-
- </script>
-
-
-
-</head>
-<body style="background: #ccc">
-
- <p style="display: none; text-align: right"><a class="signin" href="/">signin</a></p>
-
-</body>
-</html>
-
-
diff --git a/modules/discojuice/www/central.php b/modules/discojuice/www/central.php
deleted file mode 100644
index 9404fd5..0000000
--- a/modules/discojuice/www/central.php
+++ /dev/null
@@ -1,49 +0,0 @@
-<?php
-
-if (empty($_REQUEST['entityID'])) throw new Exception('Missing parameter [entityID]');
-if (empty($_REQUEST['return'])) throw new Exception('Missing parameter [return]');
-
-
-$djconfig = SimpleSAML_Configuration::getOptionalConfig('discojuice.php');
-$config = SimpleSAML_Configuration::getInstance();
-
-// EntityID
-$entityid = $_REQUEST['entityID'];
-
-// Return to...
-$returnidparam = !empty($_REQUEST['returnIDParam']) ? $_REQUEST['returnIDParam'] : 'entityID';
-$href = \SimpleSAML\Utils\HTTP::addURLParameters(
- $_REQUEST['return'],
- array($returnidparam => '')
-);
-
-
-$hostedConfig = array(
- // Name of service
- $djconfig->getString('name', 'Service'),
-
- $entityid,
-
- // Url to response
- SimpleSAML_Module::getModuleURL('discojuice/response.html'),
-
- // Set of feeds to subscribe to.
- $djconfig->getArray('feeds', array('edugain')),
-
- $href
-);
-
-/*
- "a.signin", "Teest Demooo",
- "https://example.org/saml2/entityid",
- "' . SimpleSAML_Module::getModuleURL('discojuice/discojuice/discojuiceDiscoveryResponse.html') . '", ["kalmar"], "http://example.org/login?idp="
-*/
-
-$t = new SimpleSAML_XHTML_Template($config, 'discojuice:central.tpl.php');
-$t->data['hostedConfig'] = $hostedConfig;
-$t->data['enableCentralStorage'] = $djconfig->getBoolean('enableCentralStorage', true);
-$t->data['additionalFeeds'] = $djconfig->getArray('additionalFeeds', null);
-$t->show();
-
-
-
diff --git a/modules/discojuice/www/response.html b/modules/discojuice/www/response.html
deleted file mode 100644
index 3067d41..0000000
--- a/modules/discojuice/www/response.html
+++ /dev/null
@@ -1,60 +0,0 @@
-<!DOCTYPE html>
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
- <META HTTP-EQUIV="CACHE-CONTROL" CONTENT="NO-CACHE">
- <META HTTP-EQUIV="PRAGMA" CONTENT="NO-CACHE">
- <title>IdP Discovery Response Receiver</title>
-
- <script type="text/javascript">
-
-function parseURL(url) {
- var a = document.createElement('a');
- a.href = url;
- return a.hostname;
-}
-
-function receive() {
- var urlParams = {},
- cid = null;
- (function () {
- var e,
- a = /\+/g, // Regex for replacing addition symbol with a space
- r = /([^&;=]+)=?([^&;]*)/g,
- d = function (s) { return decodeURIComponent(s.replace(a, " ")); },
- q = window.location.search.substring(1);
-
- while (e = r.exec(q))
- urlParams[d(e[1])] = d(e[2]);
- })();
-
- if (urlParams.cid) cid = urlParams.cid;
-
- var sender = parseURL(document.referrer);
-
- // Received a specific entity ID from the storage.
- if (urlParams.entityID) {
- window.parent.DiscoJuice.Utils.log('ResponseLocation: Response from discovery service [' + sender + ']: ' + urlParams.entityID + ' subID: ' + urlParams.subID);
- window.parent.DiscoJuice.Control.discoResponse(sender, urlParams.entityID, urlParams.subID, cid);
-
- // Received a textual error from the storage, to show in the debug log.
- } else if (urlParams['error']) {
- window.parent.DiscoJuice.Control.discoResponseError(cid,
- "Error from IdP Discovery Service [" + sender + "]: " + urlParams.error);
-
- // Did not receive a response parameter. This probably means that the Disco storage did not have a stored preference
- // for the user. Consequently: no error.
- } else {
- window.parent.DiscoJuice.Utils.log('No valid response parameters. cid[' + cid + ']');
- window.parent.DiscoJuice.Control.discoResponseError(cid);
- }
-
-}
-
- </script>
-</head>
-
-<body onload="receive();">
-
-</body>
-</html> \ No newline at end of file
diff --git a/modules/discopower/lib/PowerIdPDisco.php b/modules/discopower/lib/PowerIdPDisco.php
index 44f4813..18a5b8e 100644
--- a/modules/discopower/lib/PowerIdPDisco.php
+++ b/modules/discopower/lib/PowerIdPDisco.php
@@ -1,339 +1,363 @@
<?php
+
/**
- * This class implements a generic IdP discovery service, for use in various IdP
- * discovery service pages. This should reduce code duplication.
+ * This class implements a generic IdP discovery service, for use in various IdP discovery service pages. This should
+ * reduce code duplication.
*
- * This module extends the basic IdP disco handler, and add features like filtering
- * and tabs.
+ * This module extends the basic IdP disco handler, and add features like filtering and tabs.
*
* @author Andreas Åkre Solberg <andreas@uninett.no>, UNINETT AS.
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-class sspmod_discopower_PowerIdPDisco extends SimpleSAML_XHTML_IdPDisco {
-
- private $discoconfig;
-
-
- /**
- * The domain to use when saving common domain cookies.
- * This is NULL if support for common domain cookies is disabled.
- *
- * @var string|NULL
- */
- private $cdcDomain;
-
-
- /**
- * The lifetime of the CDC cookie, in seconds.
- * If set to NULL, it will only be valid until the browser is closed.
- *
- * @var int|NULL
- */
- private $cdcLifetime;
-
-
- /**
- * Initializes this discovery service.
- *
- * The constructor does the parsing of the request. If this is an invalid request, it will
- * throw an exception.
- *
- * @param array $metadataSets Array with metadata sets we find remote entities in.
- * @param string $instance The name of this instance of the discovery service.
- */
- public function __construct(array $metadataSets, $instance) {
-
- parent::__construct($metadataSets, $instance);
-
- $this->discoconfig = SimpleSAML_Configuration::getConfig('module_discopower.php');
-
- $this->cdcDomain = $this->discoconfig->getString('cdc.domain', NULL);
- if ($this->cdcDomain !== NULL && $this->cdcDomain[0] !== '.') {
- /* Ensure that the CDC domain starts with a dot ('.') as required by the spec. */
- $this->cdcDomain = '.' . $this->cdcDomain;
- }
-
- $this->cdcLifetime = $this->discoconfig->getInteger('cdc.lifetime', NULL);
- }
-
-
- /**
- * Log a message.
- *
- * This is an helper function for logging messages. It will prefix the messages with our
- * discovery service type.
- *
- * @param $message The message which should be logged.
- */
- protected function log($message) {
- SimpleSAML_Logger::info('PowerIdPDisco.' . $this->instance . ': ' . $message);
- }
-
-
- /**
- * Compare two entities.
- *
- * This function is used to sort the entity list. It sorts based on english name,
- * and will always put IdP's with names configured before those with only an
- * entityID.
- *
- * @param array $a The metadata of the first entity.
- * @param array $b The metadata of the second entity.
- * @return int How $a compares to $b.
- */
- public static function mcmp(array $a, array $b) {
- if (isset($a['name']['en']) && isset($b['name']['en'])) {
- return strcasecmp($a['name']['en'], $b['name']['en']);
- } elseif (isset($a['name']['en'])) {
- return -1; /* Place name before entity ID. */
- } elseif (isset($b['name']['en'])) {
- return 1; /* Place entity ID after name. */
- } else {
- return strcasecmp($a['entityid'], $b['entityid']);
- }
- }
-
-
- /*
- * This function will structure the idp list in a hierarchy based upon the tags.
- */
- protected function idplistStructured($list) {
- $slist = array();
-
- $order = $this->discoconfig->getValue('taborder');
- if (is_array($order)) {
- foreach($order AS $oe) {
- $slist[$oe] = array();
- }
- }
-
- $enableTabs = $this->discoconfig->getValue('tabs', NULL);
-
- foreach($list AS $key => $val) {
- $tags = array('misc');
- if (array_key_exists('tags', $val)) {
- $tags = $val['tags'];
- }
- foreach ($tags AS $tag) {
- if (!empty($enableTabs) && !in_array($tag, $enableTabs)) continue;
- $slist[$tag][$key] = $val;
- }
- }
-
- foreach($slist AS $tab => $tbslist) {
- uasort($slist[$tab], array('sspmod_discopower_PowerIdPDisco', 'mcmp'));
- }
-
- return $slist;
- }
-
- private function processFilter($filter, $entry, $default = TRUE) {
- if (in_array($entry['entityid'], $filter['entities.include'] )) return TRUE;
- if (in_array($entry['entityid'], $filter['entities.exclude'] )) return FALSE;
-
- if (array_key_exists('tags', $entry)) {
- foreach ($filter['tags.include'] AS $fe) {
- if (in_array($fe, $entry['tags'])) return TRUE;
- }
- foreach ($filter['tags.exclude'] AS $fe) {
- if (in_array($fe, $entry['tags'])) return FALSE;
- }
- }
- return $default;
- }
-
- protected function filterList($list) {
-
- try {
- $spmd = $this->metadata->getMetaData($this->spEntityId, 'saml20-sp-remote');
- } catch(Exception $e) {
- return $list;
- }
-
- if (!isset($spmd)) return $list;
- if (!array_key_exists('discopower.filter', $spmd)) return $list;
- $filter = $spmd['discopower.filter'];
-
- if (!array_key_exists('entities.include', $filter)) $filter['entities.include'] = array();
- if (!array_key_exists('entities.exclude', $filter)) $filter['entities.exclude'] = array();
- if (!array_key_exists('tags.include', $filter)) $filter['tags.include'] = array();
- if (!array_key_exists('tags.exclude', $filter)) $filter['tags.exclude'] = array();
-
- $defaultrule = TRUE;
- if ( array_key_exists('entities.include', $spmd['discopower.filter'] ) ||
- array_key_exists('tags.include', $spmd['discopower.filter'])) {
-
- $defaultrule = FALSE;
- }
-
- $returnlist = array();
- foreach ($list AS $key => $entry) {
- if ($this->processFilter($filter, $entry, $defaultrule)) {
- $returnlist[$key] = $entry;
- }
- }
- return $returnlist;
-
- }
-
-
- /**
- * Handles a request to this discovery service.
- *
- * The IdP disco parameters should be set before calling this function.
- */
- public function handleRequest() {
-
- $idp = $this->getTargetIdp();
- if($idp !== NULL) {
-
- $extDiscoveryStorage = $this->config->getString('idpdisco.extDiscoveryStorage',NULL);
- if ($extDiscoveryStorage !== NULL) {
- $this->log('Choice made [' . $idp . '] (Forwarding to external discovery storage)');
- \SimpleSAML\Utils\HTTP::redirectTrustedURL($extDiscoveryStorage, array(
- 'entityID' => $this->spEntityId,
- 'IdPentityID' => $idp,
- 'returnIDParam' => $this->returnIdParam,
- 'isPassive' => 'true',
- 'return' => $this->returnURL
- ));
-
- } else {
- $this->log('Choice made [' . $idp . '] (Redirecting the user back. returnIDParam=' . $this->returnIdParam . ')');
- \SimpleSAML\Utils\HTTP::redirectTrustedURL($this->returnURL, array($this->returnIdParam => $idp));
- }
-
- return;
- }
-
- if ($this->isPassive) {
- $this->log('Choice not made. (Redirecting the user back without answer)');
- \SimpleSAML\Utils\HTTP::redirectTrustedURL($this->returnURL);
- return;
- }
-
- /* No choice made. Show discovery service page. */
- $idpList = $this->getIdPList();
- $idpList = $this->idplistStructured($this->filterList($idpList));
- $preferredIdP = $this->getRecommendedIdP();
-
- $t = new SimpleSAML_XHTML_Template($this->config, 'discopower:disco-tpl.php', 'disco');
- $t->data['idplist'] = $idpList;
- $t->data['preferredidp'] = $preferredIdP;
- $t->data['return'] = $this->returnURL;
- $t->data['returnIDParam'] = $this->returnIdParam;
- $t->data['entityID'] = $this->spEntityId;
- $t->data['urlpattern'] = htmlspecialchars(\SimpleSAML\Utils\HTTP::getSelfURLNoQuery());
- $t->data['rememberenabled'] = $this->config->getBoolean('idpdisco.enableremember', FALSE);
- $t->data['rememberchecked'] = $this->config->getBoolean('idpdisco.rememberchecked', FALSE);
- $t->data['defaulttab'] = $this->discoconfig->getValue('defaulttab', 0);
- $t->data['score'] = $this->discoconfig->getValue('score', 'quicksilver');
- $t->show();
- }
-
-
- /**
- * Get the IdP entities saved in the common domain cookie.
- *
- * @return array List of IdP entities.
- */
- private function getCDC() {
-
- if (!isset($_COOKIE['_saml_idp'])) {
- return array();
- }
-
- $ret = (string)$_COOKIE['_saml_idp'];
- $ret = explode(' ', $ret);
- foreach ($ret as &$idp) {
- $idp = base64_decode($idp);
- if ($idp === FALSE) {
- /* Not properly base64 encoded. */
- return array();
- }
- }
-
- return $ret;
- }
-
-
- /**
- * Save the current IdP choice to a cookie.
- *
- * This function overrides the corresponding function in the parent class,
- * to add support for common domain cookie.
- *
- * @param string $idp The entityID of the IdP.
- */
- protected function setPreviousIdP($idp) {
- assert('is_string($idp)');
-
- if ($this->cdcDomain === NULL) {
- parent::setPreviousIdP($idp);
- return;
- }
-
- $list = $this->getCDC();
-
- $prevIndex = array_search($idp, $list, TRUE);
- if ($prevIndex !== FALSE) {
- unset($list[$prevIndex]);
- }
- $list[] = $idp;
-
- foreach ($list as &$value) {
- $value = base64_encode($value);
- }
- $newCookie = implode(' ', $list);
-
- while (strlen($newCookie) > 4000) {
- /* The cookie is too long. Remove the oldest elements until it is short enough. */
- $tmp = explode(' ', $newCookie, 2);
- if (count($tmp) === 1) {
- /*
- * We are left with a single entityID whose base64
- * representation is too long to fit in a cookie.
- */
- break;
- }
- $newCookie = $tmp[1];
- }
-
- $params = array(
- 'lifetime' => $this->cdcLifetime,
- 'domain' => $this->cdcDomain,
- 'secure' => TRUE,
- 'httponly' => FALSE,
- );
- \SimpleSAML\Utils\HTTP::setCookie('_saml_idp', $newCookie, $params, FALSE);
- }
-
-
- /**
- * Retrieve the previous IdP the user used.
- *
- * This function overrides the corresponding function in the parent class,
- * to add support for common domain cookie.
- *
- * @return string|NULL The entity id of the previous IdP the user used, or NULL if this is the first time.
- */
- protected function getPreviousIdP() {
-
- if ($this->cdcDomain === NULL) {
- return parent::getPreviousIdP();
- }
-
- $prevIdPs = $this->getCDC();
- while (count($prevIdPs) > 0) {
- $idp = array_pop($prevIdPs);
- $idp = $this->validateIdP($idp);
- if ($idp !== NULL) {
- return $idp;
- }
- }
-
- return NULL;
- }
-
+class sspmod_discopower_PowerIdPDisco extends SimpleSAML_XHTML_IdPDisco
+{
+
+
+ /**
+ * The configuration for this instance.
+ *
+ * @var SimpleSAML_Configuration
+ */
+ private $discoconfig;
+
+
+ /**
+ * The domain to use when saving common domain cookies. This is null if support for common domain cookies is
+ * disabled.
+ *
+ * @var string|null
+ */
+ private $cdcDomain;
+
+
+ /**
+ * The lifetime of the CDC cookie, in seconds. If set to null, it will only be valid until the browser is closed.
+ *
+ * @var int|null
+ */
+ private $cdcLifetime;
+
+
+ /**
+ * Initializes this discovery service.
+ *
+ * The constructor does the parsing of the request. If this is an invalid request, it will throw an exception.
+ *
+ * @param array $metadataSets Array with metadata sets we find remote entities in.
+ * @param string $instance The name of this instance of the discovery service.
+ */
+ public function __construct(array $metadataSets, $instance)
+ {
+ parent::__construct($metadataSets, $instance);
+
+ $this->discoconfig = SimpleSAML_Configuration::getConfig('module_discopower.php');
+
+ $this->cdcDomain = $this->discoconfig->getString('cdc.domain', null);
+ if ($this->cdcDomain !== null && $this->cdcDomain[0] !== '.') {
+ // ensure that the CDC domain starts with a dot ('.') as required by the spec
+ $this->cdcDomain = '.'.$this->cdcDomain;
+ }
+
+ $this->cdcLifetime = $this->discoconfig->getInteger('cdc.lifetime', null);
+ }
+
+
+ /**
+ * Log a message.
+ *
+ * This is an helper function for logging messages. It will prefix the messages with our discovery service type.
+ *
+ * @param string $message The message which should be logged.
+ */
+ protected function log($message)
+ {
+ SimpleSAML_Logger::info('PowerIdPDisco.'.$this->instance.': '.$message);
+ }
+
+
+ /**
+ * Compare two entities.
+ *
+ * This function is used to sort the entity list. It sorts based on english name, and will always put IdP's with
+ * names configured before those with only an entityID.
+ *
+ * @param array $a The metadata of the first entity.
+ * @param array $b The metadata of the second entity.
+ *
+ * @return int How $a compares to $b.
+ */
+ public static function mcmp(array $a, array $b)
+ {
+ if (isset($a['name']['en']) && isset($b['name']['en'])) {
+ return strcasecmp($a['name']['en'], $b['name']['en']);
+ } elseif (isset($a['name']['en'])) {
+ return -1; // place name before entity ID
+ } elseif (isset($b['name']['en'])) {
+ return 1; // Place entity ID after name
+ } else {
+ return strcasecmp($a['entityid'], $b['entityid']);
+ }
+ }
+
+
+ /**
+ * Structure the list of IdPs in a hierarchy based upon the tags.
+ *
+ * @param array $list A list of IdPs.
+ *
+ * @return array The list of IdPs structured accordingly.
+ */
+ protected function idplistStructured($list)
+ {
+ $slist = array();
+
+ $order = $this->discoconfig->getValue('taborder');
+ if (is_array($order)) {
+ foreach ($order as $oe) {
+ $slist[$oe] = array();
+ }
+ }
+
+ $enableTabs = $this->discoconfig->getValue('tabs', null);
+
+ foreach ($list as $key => $val) {
+ $tags = array('misc');
+ if (array_key_exists('tags', $val)) {
+ $tags = $val['tags'];
+ }
+ foreach ($tags as $tag) {
+ if (!empty($enableTabs) && !in_array($tag, $enableTabs)) {
+ continue;
+ }
+ $slist[$tag][$key] = $val;
+ }
+ }
+
+ foreach ($slist as $tab => $tbslist) {
+ uasort($slist[$tab], array('sspmod_discopower_PowerIdPDisco', 'mcmp'));
+ }
+
+ return $slist;
+ }
+
+
+ /**
+ * Do the actual filtering according the rules defined.
+ *
+ * @param array $filter A set of rules regarding filtering.
+ * @param array $entry An entry to be evaluated by the filters.
+ * @param boolean $default What to do in case the entity does not match any rules. Defaults to true.
+ *
+ * @return boolean True if the entity should be kept, false if it should be discarded according to the filters.
+ */
+ private function processFilter($filter, $entry, $default = true)
+ {
+ if (in_array($entry['entityid'], $filter['entities.include'])) {
+ return true;
+ }
+ if (in_array($entry['entityid'], $filter['entities.exclude'])) {
+ return false;
+ }
+
+ if (array_key_exists('tags', $entry)) {
+ foreach ($filter['tags.include'] as $fe) {
+ if (in_array($fe, $entry['tags'])) {
+ return true;
+ }
+ }
+ foreach ($filter['tags.exclude'] as $fe) {
+ if (in_array($fe, $entry['tags'])) {
+ return false;
+ }
+ }
+ }
+ return $default;
+ }
+
+
+ /**
+ * Filter a list of entities according to any filters defined in the parent class, plus discopower configuration
+ * options regarding filtering.
+ *
+ * @param array $list A list of entities to filter.
+ *
+ * @return array The list in $list after filtering entities.
+ */
+ protected function filterList($list)
+ {
+ parent::filterList($list);
+
+ try {
+ $spmd = $this->metadata->getMetaData($this->spEntityId, 'saml20-sp-remote');
+ } catch (Exception $e) {
+ return $list;
+ }
+
+ if (!isset($spmd)) {
+ return $list;
+ }
+ if (!array_key_exists('discopower.filter', $spmd)) {
+ return $list;
+ }
+ $filter = $spmd['discopower.filter'];
+
+ if (!array_key_exists('entities.include', $filter)) {
+ $filter['entities.include'] = array();
+ }
+ if (!array_key_exists('entities.exclude', $filter)) {
+ $filter['entities.exclude'] = array();
+ }
+ if (!array_key_exists('tags.include', $filter)) {
+ $filter['tags.include'] = array();
+ }
+ if (!array_key_exists('tags.exclude', $filter)) {
+ $filter['tags.exclude'] = array();
+ }
+
+ $defaultrule = true;
+ if (array_key_exists('entities.include', $spmd['discopower.filter']) ||
+ array_key_exists('tags.include', $spmd['discopower.filter'])
+ ) {
+
+ $defaultrule = false;
+ }
+
+ $returnlist = array();
+ foreach ($list as $key => $entry) {
+ if ($this->processFilter($filter, $entry, $defaultrule)) {
+ $returnlist[$key] = $entry;
+ }
+ }
+ return $returnlist;
+ }
+
+
+ /**
+ * Handles a request to this discovery service.
+ *
+ * The IdP disco parameters should be set before calling this function.
+ */
+ public function handleRequest()
+ {
+ $this->start();
+
+ // no choice made. Show discovery service page
+ $idpList = $this->getIdPList();
+ $idpList = $this->idplistStructured($this->filterList($idpList));
+ $preferredIdP = $this->getRecommendedIdP();
+
+ $t = new SimpleSAML_XHTML_Template($this->config, 'discopower:disco-tpl.php', 'disco');
+ $t->data['idplist'] = $idpList;
+ $t->data['preferredidp'] = $preferredIdP;
+ $t->data['return'] = $this->returnURL;
+ $t->data['returnIDParam'] = $this->returnIdParam;
+ $t->data['entityID'] = $this->spEntityId;
+ $t->data['urlpattern'] = htmlspecialchars(\SimpleSAML\Utils\HTTP::getSelfURLNoQuery());
+ $t->data['rememberenabled'] = $this->config->getBoolean('idpdisco.enableremember', false);
+ $t->data['rememberchecked'] = $this->config->getBoolean('idpdisco.rememberchecked', false);
+ $t->data['defaulttab'] = $this->discoconfig->getValue('defaulttab', 0);
+ $t->data['score'] = $this->discoconfig->getValue('score', 'quicksilver');
+ $t->show();
+ }
+
+
+ /**
+ * Get the IdP entities saved in the common domain cookie.
+ *
+ * @return array List of IdP entities.
+ */
+ private function getCDC()
+ {
+ if (!isset($_COOKIE['_saml_idp'])) {
+ return array();
+ }
+
+ $ret = (string) $_COOKIE['_saml_idp'];
+ $ret = explode(' ', $ret);
+ foreach ($ret as &$idp) {
+ $idp = base64_decode($idp);
+ if ($idp === false) {
+ // not properly base64 encoded
+ return array();
+ }
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * Save the current IdP choice to a cookie.
+ *
+ * This function overrides the corresponding function in the parent class, to add support for common domain cookie.
+ *
+ * @param string $idp The entityID of the IdP.
+ */
+ protected function setPreviousIdP($idp)
+ {
+ assert('is_string($idp)');
+
+ if ($this->cdcDomain === null) {
+ parent::setPreviousIdP($idp);
+ return;
+ }
+
+ $list = $this->getCDC();
+
+ $prevIndex = array_search($idp, $list, true);
+ if ($prevIndex !== false) {
+ unset($list[$prevIndex]);
+ }
+ $list[] = $idp;
+
+ foreach ($list as &$value) {
+ $value = base64_encode($value);
+ }
+ $newCookie = implode(' ', $list);
+
+ while (strlen($newCookie) > 4000) {
+ // the cookie is too long. Remove the oldest elements until it is short enough
+ $tmp = explode(' ', $newCookie, 2);
+ if (count($tmp) === 1) {
+ // we are left with a single entityID whose base64 representation is too long to fit in a cookie.
+ break;
+ }
+ $newCookie = $tmp[1];
+ }
+
+ $params = array(
+ 'lifetime' => $this->cdcLifetime,
+ 'domain' => $this->cdcDomain,
+ 'secure' => true,
+ 'httponly' => false,
+ );
+ \SimpleSAML\Utils\HTTP::setCookie('_saml_idp', $newCookie, $params, false);
+ }
+
+
+ /**
+ * Retrieve the previous IdP the user used.
+ *
+ * This function overrides the corresponding function in the parent class, to add support for common domain cookie.
+ *
+ * @return string|null The entity id of the previous IdP the user used, or null if this is the first time.
+ */
+ protected function getPreviousIdP()
+ {
+ if ($this->cdcDomain === null) {
+ return parent::getPreviousIdP();
+ }
+
+ $prevIdPs = $this->getCDC();
+ while (count($prevIdPs) > 0) {
+ $idp = array_pop($prevIdPs);
+ $idp = $this->validateIdP($idp);
+ if ($idp !== null) {
+ return $idp;
+ }
+ }
+
+ return null;
+ }
}
diff --git a/modules/logpeek/config-templates/module_logpeek.php b/modules/logpeek/config-templates/module_logpeek.php
deleted file mode 100644
index 40d4693..0000000
--- a/modules/logpeek/config-templates/module_logpeek.php
+++ /dev/null
@@ -1,11 +0,0 @@
-<?php
-/*
- * Configuration for the module logpeek.
- */
-
-$config = array (
- 'logfile' => '/var/log/simplesamlphp.log',
- 'lines' => 1500,
- // Read block size. 8192 is max, limited by fread.
- 'blocksz' => 8192,
-);
diff --git a/modules/logpeek/default-disable b/modules/logpeek/default-disable
deleted file mode 100644
index fa0bd82..0000000
--- a/modules/logpeek/default-disable
+++ /dev/null
@@ -1,3 +0,0 @@
-This file indicates that the default state of this module
-is disabled. To enable, create a file named enable in the
-same directory as this file.
diff --git a/modules/logpeek/hooks/hook_frontpage.php b/modules/logpeek/hooks/hook_frontpage.php
deleted file mode 100644
index 6b850d3..0000000
--- a/modules/logpeek/hooks/hook_frontpage.php
+++ /dev/null
@@ -1,16 +0,0 @@
-<?php
-/**
- * Hook to add the logpeek module to the frontpage.
- *
- * @param array &$links The links on the frontpage, split into sections.
- */
-function logpeek_hook_frontpage(&$links) {
- assert('is_array($links)');
- assert('array_key_exists("links", $links)');
-
- $links['config'][] = array(
- 'href' => SimpleSAML_Module::getModuleURL('logpeek/'),
- 'text' => array('en' => 'SimpleSAMLphp logs access (Log peek)', 'no' => 'Vis simpleSAMLphp log'),
- );
-
-}
diff --git a/modules/logpeek/lib/File/reverseRead.php b/modules/logpeek/lib/File/reverseRead.php
deleted file mode 100644
index 501c0d2..0000000
--- a/modules/logpeek/lib/File/reverseRead.php
+++ /dev/null
@@ -1,220 +0,0 @@
-<?php
-/**
- * Functionatility for line by line reverse reading of a file. It is done by blockwise
- * fetching the file from the end and putting the lines into an array.
- *
- * @author Thomas Graff<thomas.graff@uninett.no>
- *
- */
-class sspmod_logpeek_File_reverseRead{
- // 8192 is max number of octets limited by fread.
- private $blockSize;
- private $blockStart;
- private $fileHandle;
- // fileSize may be changed after initial file size check
- private $fileSize;
- private $fileMtime;
- // Array containing file lines
- private $content;
- // Leftover before first complete line
- private $remainder;
- // Count read lines from the end
- private $readPointer;
-
- /**
- * File is checked and file handle to file is opend. But no data is read
- * from the file.
- *
- * @param string $fileUrl Path and filename to file to be read
- * @param int $blockSize File read block size in byte
- * @return bool Success
- */
- public function __construct($fileUrl, $blockSize = 8192){
- if(!is_readable($fileUrl)){
- return FALSE;
- }
-
- $this->blockSize = $blockSize;
- $this->content = array();
- $this->remainder = '';
- $this->readPointer = 0;
-
- $fileInfo = stat($fileUrl);
- $this->fileSize = $this->blockStart = $fileInfo['size'];
- $this->fileMtime = $fileInfo['mtime'];
-
- if($this->fileSize > 0){
- $this->fileHandle = fopen($fileUrl, 'rb');
- return TRUE;
- }else{
- return FALSE;
- }
- }
-
-
- public function __destruct(){
- if(is_resource($this->fileHandle)){
- fclose($this->fileHandle);
- }
- }
-
- /**
- * Fetch chunk of data from file.
- * Each time this function is called, will it fetch a chunk
- * of data from the file. It starts from the end of the file
- * and work towards the beginning of the file.
- *
- * @return string buffer with datablock.
- * Will return bool FALSE when there is no more data to get.
- */
- private function readChunk(){
- $splits = $this->blockSize;
-
- $this->blockStart -= $splits;
- if($this->blockStart < 0){
- $splits += $this->blockStart;
- $this->blockStart = 0;
- }
-
- // Return false if nothing more to read
- if($splits === 0){
- return FALSE;
- }
-
- fseek($this->fileHandle, $this->blockStart, SEEK_SET);
- $buff = fread($this->fileHandle, $splits);
-
- return $buff;
- }
-
- /**
- * Get one line of data from the file, starting from the end of the file.
- *
- * @return string One line of data from the file.
- * Bool FALSE when there is no more data to get.
- */
- public function getPreviousLine(){
- if(count($this->content) === 0 || $this->readPointer < 1){
-
- do {
- $buff = $this->readChunk();
-
- if($buff !== FALSE){
- $eolPos = strpos($buff, "\n");
- }else{
- // Empty buffer, no more to read.
- if(strlen($this->remainder) > 0){
- $buff = $this->remainder;
- $this->remainder = '';
- // Exit from while-loop
- break;
- }else{
- // Remainder also empty.
- return FALSE;
- }
- }
-
- if($eolPos === FALSE){
- // No eol found. Make buffer head of remainder and empty buffer.
- $this->remainder = $buff . $this->remainder;
- $buff = '';
- }elseif($eolPos !== 0){
- // eol found.
- $buff .= $this->remainder;
- $this->remainder = substr($buff, 0, $eolPos);
- $buff = substr($buff, $eolPos+1);
- }elseif($eolPos === 0){
- $buff .= $this->remainder;
- $buff = substr($buff, 1);
- $this->remainder = '';
- }
-
- }while(($buff !== FALSE) && ($eolPos === FALSE));
-
- $this->content = explode("\n", $buff);
- $this->readPointer = count($this->content);
- }
-
- if(count($this->content) > 0){
- return $this->content[--$this->readPointer];
- }else{
- return FALSE;
- }
- }
-
-
- private function cutHead(&$haystack, $needle, $exit){
- $pos = 0;
- $cnt = 0;
- // Holder på inntill antall ønskede linjer eller vi ikke finner flere linjer
- while($cnt < $exit && ($pos = strpos($haystack, $needle, $pos)) !==false ){
- $pos++;
- $cnt++;
- }
- return ($pos === false) ? false : substr($haystack, $pos, strlen($haystack));
- }
-
-
- // FIXME: This function hawe som error, do not use before auditing and testing
- public function getTail($lines = 10){
- $this->blockStart = $this->fileSize;
- $buff1 = Array();
- $lastLines = array();
-
- while($this->blockStart){
- $buff = $this->readChunk();
- if(!$buff)break;
-
- $lines -= substr_count($buff, "\n");
-
- if($lines <= 0)
- {
- $buff1[] = $this->cutHead($buff, "\n", abs($lines)+1);
- break;
- }
- $buff1[] = $buff;
- }
-
- for($i = count($buff1); $i >= 0; $i--){
- $lastLines = array_merge($lastLines, explode("\n", $buff1[$i]));
- }
-
- return $lastLines;
- }
-
-
- private function getLineAtPost($pos){
- if($pos < 0 || $pos > $this->fileSize){
- return FALSE;
- }
-
- $seeker = $pos;
- fseek($this->fileHandle, $seeker, SEEK_SET);
- while($seeker > 0 && fgetc($this->fileHandle) !== "\n"){
- fseek($this->fileHandle, --$seeker, SEEK_SET);
- }
-
- return rtrim(fgets($this->fileHandle));
- }
-
-
- public function getFirstLine(){
- return $this->getLineAtPost(0);
- }
-
-
- public function getLastLine(){
- return $this->getLineAtPost($this->fileSize-2);
- }
-
-
- public function getFileSize(){
- return $this->fileSize;
- }
-
-
- public function getFileMtime(){
- return $this->fileMtime;
- }
-
-}
diff --git a/modules/logpeek/lib/Syslog/parseLine.php b/modules/logpeek/lib/Syslog/parseLine.php
deleted file mode 100644
index e2d56f9..0000000
--- a/modules/logpeek/lib/Syslog/parseLine.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-class sspmod_logpeek_Syslog_parseLine{
-
-
- public static function isOlderThan($time, $logLine){
- return true;
- }
-
- public static function getUnixTime($logLine, $year = NULL){
- // I can read month and day and time from the file.
- // but I will assum year is current year retured by time().
- // Unless month and day in the file is bigger than current month and day,
- // I will then asume prevous year.
- // A better approach would be to get the year from last modification time (mtime) of the
- // file this record is taken from. But that require knowledge about the file.
- if(!$year){
- $now = getdate();
- $year = (int)$now['year'];
- }
- list($month, $day, $hour, $minute, $second) = sscanf($logLine, "%s %d %d:%d:%d ");
- $time = sprintf("%d %s %d %d:%d:%d", $day, $month, $year, $hour, $minute, $second);
- return strtotime($time);
- }
-}
diff --git a/modules/logpeek/templates/logpeek.php b/modules/logpeek/templates/logpeek.php
deleted file mode 100644
index 2b25e28..0000000
--- a/modules/logpeek/templates/logpeek.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-$this->data['header'] = 'Log peek';
-$this->includeAtTemplateBase('includes/header.php');
-?>
-
-<h2>SimpleSAMLphp logs (admin utility)</h2>
-
-<form method="get" action="?">
- <table>
- <tr><th><label for="start">First entry in logfile</label></th><td id="star"><?php echo $this->data['timestart']; ?></td></tr>
- <tr><th><label for="end">Last entry in logfile</label></th><td id="end"><?php echo $this->data['endtime']; ?></td></tr>
- <tr><th><label for="size">Logfile size</label></th><td id="size"><?php echo $this->data['filesize']; ?></td></tr>
- <tr><th><label for="tag">Tag id for search</label></th><td><input type="text" name="tag" id="tag" value="<?php echo $this->data['trackid']; ?>" /></td></tr>
- <tr><th><input type="submit" value="Search log" /></th><td></td></tr>
- </table>
-</form>
-
-<pre style="background: #eee; border: 1px solid #666; padding: 1em; margin: .4em; overflow: scroll">
-<?php
-if (!empty($this->data['results'])) {
- foreach($this->data['results'] AS $line) {
- echo htmlspecialchars($line) . "\n";
- }
-}
-?>
-</pre>
-<?php $this->includeAtTemplateBase('includes/footer.php');
diff --git a/modules/logpeek/www/index.php b/modules/logpeek/www/index.php
deleted file mode 100644
index a0d8152..0000000
--- a/modules/logpeek/www/index.php
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-function logFilter($objFile, $tag, $cut){
- if (!preg_match('/^[a-f0-9]{10}$/D', $tag)) throw new Exception('Invalid search tag');
-
- $i = 0;
- $results = array();
- $line = $objFile->getPreviousLine();
- while($line !== FALSE && ($i++ < $cut)){
- if(strstr($line, '[' . $tag . ']')){
- $results[] = $line;
- }
- $line = $objFile->getPreviousLine();
- }
- $results[] = 'Searched ' . $i . ' lines backward. ' . count($results) . ' lines found.';
- $results = array_reverse($results);
- return $results;
-}
-
-
-$config = SimpleSAML_Configuration::getInstance();
-$session = SimpleSAML_Session::getSessionFromRequest();
-
-SimpleSAML\Utils\Auth::requireAdmin();
-
-$logpeekconfig = SimpleSAML_Configuration::getConfig('module_logpeek.php');
-$logfile = $logpeekconfig->getValue('logfile', '/var/simplesamlphp.log');
-$blockSize = $logpeekconfig->getValue('blocksz', 8192);
-
-$myLog = new sspmod_logpeek_File_reverseRead($logfile, $blockSize);
-
-
-$results = NULL;
-if (isset($_REQUEST['tag'])) {
- $results = logFilter($myLog, $_REQUEST['tag'], $logpeekconfig->getValue('lines', 500));
-}
-
-
-$fileModYear = date("Y", $myLog->getFileMtime());
-$firstLine = $myLog->getFirstLine();
-$firstTimeEpoch = sspmod_logpeek_Syslog_parseLine::getUnixTime($firstLine, $fileModYear);
-$lastLine = $myLog->getLastLine();
-$lastTimeEpoch = sspmod_logpeek_Syslog_parseLine::getUnixTime($lastLine, $fileModYear);
-$fileSize = $myLog->getFileSize();
-
-$t = new SimpleSAML_XHTML_Template($config, 'logpeek:logpeek.php');
-$t->data['results'] = $results;
-$t->data['trackid'] = $session->getTrackID();
-$t->data['timestart'] = date(DATE_RFC822, $firstTimeEpoch);
-$t->data['endtime'] = date(DATE_RFC822, $lastTimeEpoch);
-$t->data['filesize'] = $fileSize;
-
-$t->show();
diff --git a/modules/metaedit/config-template/module_metaedit.php b/modules/metaedit/config-template/module_metaedit.php
deleted file mode 100644
index 696880e..0000000
--- a/modules/metaedit/config-template/module_metaedit.php
+++ /dev/null
@@ -1,11 +0,0 @@
-<?php
-/*
- * The configuration of simpleSAMLphp statistics package
- */
-
-$config = array (
- 'metahandlerConfig' => array('directory' => 'metadata/metaedit'),
- 'auth' => 'saml2',
- 'useridattr' => 'eduPersonPrincipalName',
-);
-
diff --git a/modules/metaedit/default-disable b/modules/metaedit/default-disable
deleted file mode 100644
index fa0bd82..0000000
--- a/modules/metaedit/default-disable
+++ /dev/null
@@ -1,3 +0,0 @@
-This file indicates that the default state of this module
-is disabled. To enable, create a file named enable in the
-same directory as this file.
diff --git a/modules/metaedit/hooks/hook_frontpage.php b/modules/metaedit/hooks/hook_frontpage.php
deleted file mode 100644
index f62fcf8..0000000
--- a/modules/metaedit/hooks/hook_frontpage.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-/**
- * Hook to add the modinfo module to the frontpage.
- *
- * @param array &$links The links on the frontpage, split into sections.
- */
-function metaedit_hook_frontpage(&$links) {
- assert('is_array($links)');
- assert('array_key_exists("links", $links)');
-
- $links['federation']['metaedit'] = array(
- 'href' => SimpleSAML_Module::getModuleURL('metaedit/index.php'),
- 'text' => array('en' => 'Metadata registry', 'no' => 'Metadata registrering'),
- 'shorttext' => array('en' => 'Metadata registry', 'no' => 'Metadata registrering'),
- );
-
-}
diff --git a/modules/metaedit/lib/MetaEditor.php b/modules/metaedit/lib/MetaEditor.php
deleted file mode 100644
index e2d2d6e..0000000
--- a/modules/metaedit/lib/MetaEditor.php
+++ /dev/null
@@ -1,177 +0,0 @@
-<?php
-
-/**
- * Editor for metadata
- *
- * @author Andreas Åkre Solberg <andreas@uninett.no>, UNINETT AS.
- * @package simpleSAMLphp
- */
-class sspmod_metaedit_MetaEditor {
-
-
- protected function getStandardField($request, &$metadata, $key) {
- if (array_key_exists('field_' . $key, $request)) {
- $metadata[$key] = $request['field_' . $key];
- } else {
- if (isset($metadata[$key])) unset($metadata[$key]);
- }
- }
-
- protected function getEndpointField($request, &$metadata, $key, $binding, $indexed) {
- if (array_key_exists('field_' . $key, $request)) {
- $e = array(
- 'Binding' => $binding,
- 'Location' => $request['field_' . $key]
- );
- if ($indexed) {
- $e['index'] = 0;
- }
- $metadata[$key] = array($e);
- } else {
- if (isset($metadata[$key])) unset($metadata[$key]);
- }
- }
-
- public function formToMeta($request, $metadata = array(), $override = NULL) {
- $this->getStandardField($request, $metadata, 'entityid');
- $this->getStandardField($request, $metadata, 'name');
- $this->getStandardField($request, $metadata, 'description');
- $this->getEndpointField($request, $metadata, 'AssertionConsumerService', SAML2_Const::BINDING_HTTP_POST, TRUE);
- $this->getEndpointField($request, $metadata, 'SingleLogoutService', SAML2_Const::BINDING_HTTP_REDIRECT, FALSE);
- $metadata['updated'] = time();
-
- if ($override) {
- foreach($override AS $key => $value) {
- $metadata[$key] = $value;
- }
- }
-
- return $metadata;
- }
-
- protected function requireStandardField($request, $key) {
- if (!array_key_exists('field_' . $key, $request))
- throw new Exception('Required field [' . $key . '] was missing.');
- if (empty($request['field_' . $key]))
- throw new Exception('Required field [' . $key . '] was empty.');
- }
-
- public function checkForm($request) {
- $this->requireStandardField($request, 'entityid');
- $this->requireStandardField($request, 'name');
- }
-
-
- protected function header($name) {
- return '<tr ><td>&nbsp;</td><td class="header">' . $name . '</td></tr>';
-
- }
-
- protected function readonlyDateField($metadata, $key, $name) {
- $value = '<span style="color: #aaa">Not set</a>';
- if (array_key_exists($key, $metadata))
- $value = date('j. F Y, G:i', $metadata[$key]);
- return '<tr>
- <td class="name">' . $name . '</td>
- <td class="data">' . $value . '</td></tr>';
-
- }
-
- protected function readonlyField($metadata, $key, $name) {
- $value = '';
- if (array_key_exists($key, $metadata))
- $value = $metadata[$key];
- return '<tr>
- <td class="name">' . $name . '</td>
- <td class="data">' . htmlspecialchars($value) . '</td></tr>';
-
- }
-
- protected function hiddenField($key, $value) {
- return '<input type="hidden" name="' . $key . '" value="' . htmlspecialchars($value) . '" />';
- }
-
- protected function flattenLanguageField(&$metadata, $key) {
- if (array_key_exists($key, $metadata)) {
- if (is_array($metadata[$key])) {
- if (isset($metadata[$key]['en'])) {
- $metadata[$key] = $metadata[$key]['en'];
- } else {
- unset($metadata[$key]);
- }
- }
- }
- }
-
- protected function standardField($metadata, $key, $name, $textarea = FALSE) {
- $value = '';
- if (array_key_exists($key, $metadata)) {
- $value = htmlspecialchars($metadata[$key]);
- }
- if ($textarea) {
- return '<tr><td class="name">' . $name . '</td><td class="data">
- <textarea name="field_' . $key . '" rows="5" cols="50">' . $value . '</textarea></td></tr>';
-
- } else {
- return '<tr><td class="name">' . $name . '</td><td class="data">
- <input type="text" size="60" name="field_' . $key . '" value="' . $value . '" /></td></tr>';
-
- }
- }
-
- protected function endpointField($metadata, $key, $name, $textarea = FALSE) {
- $value = '';
- if (array_key_exists($key, $metadata)) {
- if (is_array($metadata[$key])) {
- $value = htmlspecialchars($metadata[$key][0]['Location']);
- } else {
- $value = htmlspecialchars($metadata[$key]);
- }
-
- }
-
- if ($textarea) {
- return '<tr><td class="name">' . $name . '</td><td class="data">
- <textarea name="field_' . $key . '" rows="5" cols="50">' . $value . '</textarea></td></tr>';
-
- } else {
- return '<tr><td class="name">' . $name . '</td><td class="data">
- <input type="text" size="60" name="field_' . $key . '" value="' . $value . '" /></td></tr>';
-
- }
- }
-
- public function metaToForm($metadata) {
- $this->flattenLanguageField($metadata, 'name');
- $this->flattenLanguageField($metadata, 'description');
- return '<form action="edit.php" method="post">' .
-
- (array_key_exists('entityid', $metadata) ?
- $this->hiddenField('was-entityid', $metadata['entityid']) :
- '') .
-
- '<div id="tabdiv">' .
- '<ul>' .
- '<li><a href="#basic">Name and descrition</a></li>' .
- '<li><a href="#saml">SAML 2.0</a></li>' .
- '</ul>' .
- '<div id="basic"><table class="formtable">' .
- $this->standardField($metadata, 'entityid', 'EntityID') .
- $this->standardField($metadata, 'name', 'Name of service') .
- $this->standardField($metadata, 'description', 'Description of service', TRUE) .
- $this->readonlyField($metadata, 'owner', 'Owner') .
- $this->readonlyDateField($metadata, 'updated', 'Last updated') .
- $this->readonlyDateField($metadata, 'expire', 'Expire') .
-
- '</table></div><div id="saml"><table class="formtable">' .
- $this->endpointField($metadata, 'AssertionConsumerService', 'AssertionConsumerService endpoint') .
- $this->endpointField($metadata, 'SingleLogoutService', 'SingleLogoutService endpoint') .
- '</table></div>' .
- '</div>' .
- '<input type="submit" name="submit" value="Save" style="margin-top: 5px" />' .
- '</form>';
- }
-
-}
-
-
diff --git a/modules/metaedit/templates/formedit.php b/modules/metaedit/templates/formedit.php
deleted file mode 100644
index a90a924..0000000
--- a/modules/metaedit/templates/formedit.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-$this->data['jquery'] = array('version' => '1.6', 'core' => TRUE, 'ui' => TRUE, 'css' => TRUE);
-$this->data['head'] = '<link rel="stylesheet" type="text/css" href="/' . $this->data['baseurlpath'] . 'module.php/metaedit/resources/style.css" />' . "\n";
-$this->data['head'] .= '<script type="text/javascript">
-$(document).ready(function() {
- $("#tabdiv").tabs();
-});
-</script>';
-
-$this->includeAtTemplateBase('includes/header.php');
-
-
-echo('<h1>Metadata Editor</h1>');
-
-echo($this->data['form']);
-
-echo('<p style="float: right"><a href="index.php">Return to entity listing <strong>without saving...</strong></a></p>');
-
-$this->includeAtTemplateBase('includes/footer.php');
-
diff --git a/modules/metaedit/templates/metalist.php b/modules/metaedit/templates/metalist.php
deleted file mode 100644
index 04b39cd..0000000
--- a/modules/metaedit/templates/metalist.php
+++ /dev/null
@@ -1,49 +0,0 @@
-<?php
-
-$this->data['jquery'] = array('version' => '1.6', 'core' => TRUE, 'ui' => TRUE, 'css' => TRUE);
-$this->data['head'] = '<link rel="stylesheet" type="text/css" href="/' . $this->data['baseurlpath'] . 'module.php/metaedit/resources/style.css" />' . "\n";
-$this->includeAtTemplateBase('includes/header.php');
-
-
-echo('<h1>Metadata Registry</h1>');
-
-echo('<p>Here you can register new SAML entities. You are successfully logged in as ' . htmlspecialchars($this->data['userid']) . '</p>');
-
-echo('<h2>Your entries</h2>');
-echo('<table class="metalist" style="width: 100%">');
-$i = 0; $rows = array('odd', 'even');
-foreach($this->data['metadata']['mine'] AS $md ) {
- $i++;
- echo('<tr class="' . $rows[$i % 2] . '">
- <td>' . htmlspecialchars($md['name']) . '</td>
- <td><tt>' . htmlspecialchars($md['entityid']) . '</tt></td>
- <td>
- <a href="edit.php?entityid=' . urlencode($md['entityid']) . '">edit</a>
- <a href="index.php?delete=' . urlencode($md['entityid']) . '">delete</a>
- </td></tr>');
-}
-if ($i == 0) {
- echo('<tr><td colspan="3">No entries registered</td></tr>');
-}
-echo('</table>');
-
-echo('<p><a href="edit.php">Add new entity</a> | <a href="xmlimport.php">Add from SAML 2.0 XML metadata</a></p>');
-
-echo('<h2>Other entries</h2>');
-echo('<table class="metalist" style="width: 100%">');
-$i = 0; $rows = array('odd', 'even');
-foreach($this->data['metadata']['others'] AS $md ) {
- $i++;
- echo('<tr class="' . $rows[$i % 2] . '">
- <td>' . htmlspecialchars($md['name']) . '</td>
- <td><tt>' . htmlspecialchars($md['entityid']) . '</tt></td>
- <td>' . (isset($md['owner']) ? htmlspecialchars($md['owner']) : 'No owner') . '
- </td></tr>');
-}
-if ($i == 0) {
- echo('<tr><td colspan="3">No entries registered</td></tr>');
-}
-echo('</table>');
-
-$this->includeAtTemplateBase('includes/footer.php');
-
diff --git a/modules/metaedit/templates/saved.php b/modules/metaedit/templates/saved.php
deleted file mode 100644
index f9b10ba..0000000
--- a/modules/metaedit/templates/saved.php
+++ /dev/null
@@ -1,15 +0,0 @@
-<?php
-
-
-
-$this->includeAtTemplateBase('includes/header.php');
-
-
-echo('<h1>Metadata successfully saved</h1>');
-
-echo('<p><a href="index.php">Go back to metadata registry listing</a></p>');
-
-
-
-$this->includeAtTemplateBase('includes/footer.php');
-
diff --git a/modules/metaedit/templates/xmlimport.tpl.php b/modules/metaedit/templates/xmlimport.tpl.php
deleted file mode 100644
index 68f046c..0000000
--- a/modules/metaedit/templates/xmlimport.tpl.php
+++ /dev/null
@@ -1,16 +0,0 @@
-<?php
-$this->includeAtTemplateBase('includes/header.php');
-
-echo('<h1>Import SAML 2.0 XML Metadata</h1>');
-
-echo('<form method="post" action="edit.php">');
-echo('<p>Paste in SAML 2.0 XML Metadata for the entity that you would like to add.</p>');
-echo('<textarea style="height: 200px; width: 90%; border: 1px solid #aaa;" cols="50" rows="5" name="xmlmetadata"></textarea>');
-echo('<input type="submit" style="margin-top: .5em" name="metasubmit" value="Import metadata" />');
-echo('</form>');
-
-
-echo('<p style="float: right"><a href="index.php">Return to entity listing</a></p>');
-
-$this->includeAtTemplateBase('includes/footer.php');
-
diff --git a/modules/metaedit/www/edit.php b/modules/metaedit/www/edit.php
deleted file mode 100644
index 8a89c2b..0000000
--- a/modules/metaedit/www/edit.php
+++ /dev/null
@@ -1,81 +0,0 @@
-<?php
-
-/* Load simpleSAMLphp, configuration and metadata */
-$config = SimpleSAML_Configuration::getInstance();
-$metaconfig = SimpleSAML_Configuration::getConfig('module_metaedit.php');
-
-$mdh = new SimpleSAML_Metadata_MetaDataStorageHandlerSerialize($metaconfig->getValue('metahandlerConfig', NULL));
-
-$authsource = $metaconfig->getValue('auth', 'login-admin');
-$useridattr = $metaconfig->getValue('useridattr', 'eduPersonPrincipalName');
-
-$as = new SimpleSAML_Auth_Simple($authsource);
-$as->requireAuth();
-$attributes = $as->getAttributes();
-// Check if userid exists
-if (!isset($attributes[$useridattr]))
- throw new Exception('User ID is missing');
-$userid = $attributes[$useridattr][0];
-
-function requireOwnership($metadata, $userid) {
- if (!isset($metadata['owner']))
- throw new Exception('Metadata has no owner. Which means no one is granted access, not even you.');
- if ($metadata['owner'] !== $userid)
- throw new Exception('Metadata has an owner that is not equal to your userid, hence you are not granted access.');
-}
-
-
-if (array_key_exists('entityid', $_REQUEST)) {
- $metadata = $mdh->getMetadata($_REQUEST['entityid'], 'saml20-sp-remote');
- requireOwnership($metadata, $userid);
-} elseif(array_key_exists('xmlmetadata', $_REQUEST)) {
-
- $xmldata = $_REQUEST['xmlmetadata'];
- \SimpleSAML\Utils\XML::checkSAMLMessage($xmldata, 'saml-meta');
- $entities = SimpleSAML_Metadata_SAMLParser::parseDescriptorsString($xmldata);
- $entity = array_pop($entities);
- $metadata = $entity->getMetadata20SP();
-
- /* Trim metadata endpoint arrays. */
- $metadata['AssertionConsumerService'] = array(\SimpleSAML\Utils\Config\Metadata::getDefaultEndpoint($metadata['AssertionConsumerService'], array(SAML2_Const::BINDING_HTTP_POST)));
- $metadata['SingleLogoutService'] = array(\SimpleSAML\Utils\Config\Metadata::getDefaultEndpoint($metadata['SingleLogoutService'], array(SAML2_Const::BINDING_HTTP_REDIRECT)));
-
-} else {
- $metadata = array(
- 'owner' => $userid,
- );
-}
-
-
-$editor = new sspmod_metaedit_MetaEditor();
-
-
-if (isset($_POST['submit'])) {
- $editor->checkForm($_POST);
- $metadata = $editor->formToMeta($_POST, array(), array('owner' => $userid));
-
- if (isset($_REQUEST['was-entityid']) && $_REQUEST['was-entityid'] !== $metadata['entityid']) {
- $premetadata = $mdh->getMetadata($_REQUEST['was-entityid'], 'saml20-sp-remote');
- requireOwnership($premetadata, $userid);
- $mdh->deleteMetadata($_REQUEST['was-entityid'], 'saml20-sp-remote');
- }
-
- $testmetadata = NULL;
- try {
- $testmetadata = $mdh->getMetadata($metadata['entityid'], 'saml20-sp-remote');
- } catch(Exception $e) {}
- if ($testmetadata) requireOwnership($testmetadata, $userid);
-
- $mdh->saveMetadata($metadata['entityid'], 'saml20-sp-remote', $metadata);
-
- $template = new SimpleSAML_XHTML_Template($config, 'metaedit:saved.php');
- $template->show();
- exit;
-}
-
-$form = $editor->metaToForm($metadata);
-
-$template = new SimpleSAML_XHTML_Template($config, 'metaedit:formedit.php');
-$template->data['form'] = $form;
-$template->show();
-
diff --git a/modules/metaedit/www/index.php b/modules/metaedit/www/index.php
deleted file mode 100644
index f2e8d76..0000000
--- a/modules/metaedit/www/index.php
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-
-/* Load simpleSAMLphp, configuration and metadata */
-$config = SimpleSAML_Configuration::getInstance();
-$metaconfig = SimpleSAML_Configuration::getConfig('module_metaedit.php');
-
-$mdh = new SimpleSAML_Metadata_MetaDataStorageHandlerSerialize($metaconfig->getValue('metahandlerConfig', NULL));
-
-$authsource = $metaconfig->getValue('auth', 'login-admin');
-$useridattr = $metaconfig->getValue('useridattr', 'eduPersonPrincipalName');
-
-$as = new SimpleSAML_Auth_Simple($authsource);
-$as->requireAuth();
-$attributes = $as->getAttributes();
-// Check if userid exists
-if (!isset($attributes[$useridattr]))
- throw new Exception('User ID is missing');
-$userid = $attributes[$useridattr][0];
-
-function requireOwnership($metadata, $userid) {
- if (!isset($metadata['owner']))
- throw new Exception('Metadata has no owner. Which means no one is granted access, not even you.');
- if ($metadata['owner'] !== $userid)
- throw new Exception('Metadata has an owner that is not equal to your userid, hence you are not granted access.');
-}
-
-
-if (isset($_REQUEST['delete'])) {
- $premetadata = $mdh->getMetadata($_REQUEST['delete'], 'saml20-sp-remote');
- requireOwnership($premetadata, $userid);
- $mdh->deleteMetadata($_REQUEST['delete'], 'saml20-sp-remote');
-}
-
-
-$list = $mdh->getMetadataSet('saml20-sp-remote');
-
-$slist = array('mine' => array(), 'others' => array());
-foreach($list AS $listitem) {
- if (array_key_exists('owner', $listitem)) {
- if ($listitem['owner'] === $userid) {
- $slist['mine'][] = $listitem; continue;
- }
- }
- $slist['others'][] = $listitem;
-}
-
-
-$template = new SimpleSAML_XHTML_Template($config, 'metaedit:metalist.php');
-$template->data['metadata'] = $slist;
-$template->data['userid'] = $userid;
-$template->show();
diff --git a/modules/metaedit/www/resources/style.css b/modules/metaedit/www/resources/style.css
deleted file mode 100644
index 1240db0..0000000
--- a/modules/metaedit/www/resources/style.css
+++ /dev/null
@@ -1,37 +0,0 @@
-table.formtable {
- width: 100%;
-}
-table.formtable tr td.name {
- text-align: right;
- vertical-align: top;
- padding-right: .6em;
-}
-table.formtable tr td.value {
- text-align: left;
- padding: 0px;
-}
-table.formtable tr td.header {
- padding-left: 5px;
- padding-top: 8px;
- font-weight: bold;
- font-size: 110%;
-}
-
-table.formtable tr td input,table.formtable tr td textarea {
- width: 90%;
- border: 1px solid #bbb;
- margin: 2px 5px;
- padding: 2px 4px;
-}
-
-
-table.metalist {
- border: 1px solid #aaa;
- border-collapse: collapse;
-}
-table.metalist tr td {
- padding: 2px 5px;
-}
-table.metalist tr.even td {
- background: #e5e5e5;
-} \ No newline at end of file
diff --git a/modules/metaedit/www/xmlimport.php b/modules/metaedit/www/xmlimport.php
deleted file mode 100644
index 12799af..0000000
--- a/modules/metaedit/www/xmlimport.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-
-/* Load simpleSAMLphp, configuration and metadata */
-$config = SimpleSAML_Configuration::getInstance();
-$session = SimpleSAML_Session::getSessionFromRequest();
-
-$template = new SimpleSAML_XHTML_Template($config, 'metaedit:xmlimport.tpl.php');
-$template->show();
diff --git a/modules/metarefresh/config-templates/config-metarefresh.php b/modules/metarefresh/config-templates/config-metarefresh.php
index 8da56eb..377c514 100644
--- a/modules/metarefresh/config-templates/config-metarefresh.php
+++ b/modules/metarefresh/config-templates/config-metarefresh.php
@@ -51,6 +51,20 @@ $config = array(
51 => array('class' => 'core:AttributeMap', 'oid2name'),
),
),
+
+ /*
+ * The sets of entities to load, any combination of:
+ * - 'saml20-idp-remote'
+ * - 'saml20-sp-remote'
+ * - 'shib13-idp-remote'
+ * - 'shib13-sp-remote'
+ * - 'attributeauthority-remote'
+ *
+ * All of them will be used by default.
+ *
+ * This option takes precedence over the same option per metadata set.
+ */
+ //'types' => array(),
),
),
'expireAfter' => 60*60*24*4, // Maximum 4 days cache time.
@@ -61,6 +75,19 @@ $config = array(
* Can be 'flatfile' or 'serialize'. 'flatfile' is the default.
*/
'outputFormat' => 'flatfile',
+
+
+ /*
+ * The sets of entities to load, any combination of:
+ * - 'saml20-idp-remote'
+ * - 'saml20-sp-remote'
+ * - 'shib13-idp-remote'
+ * - 'shib13-sp-remote'
+ * - 'attributeauthority-remote'
+ *
+ * All of them will be used by default.
+ */
+ //'types' => array(),
),
),
);
diff --git a/modules/metarefresh/hooks/hook_cron.php b/modules/metarefresh/hooks/hook_cron.php
index f1a0512..9f18ffe 100644
--- a/modules/metarefresh/hooks/hook_cron.php
+++ b/modules/metarefresh/hooks/hook_cron.php
@@ -48,8 +48,25 @@ function metarefresh_hook_cron(&$croninfo) {
$whitelist = $mconfig->getArray('whitelist', array());
$conditionalGET = $mconfig->getBoolean('conditionalGET', FALSE);
+ // get global type filters
+ $available_types = array(
+ 'saml20-idp-remote',
+ 'saml20-sp-remote',
+ 'shib13-idp-remote',
+ 'shib13-sp-remote',
+ 'attributeauthority-remote'
+ );
+ $set_types = $set->getArrayize('types', $available_types);
+
foreach($set->getArray('sources') AS $source) {
+ // filter metadata by type of entity
+ if (isset($source['types'])) {
+ $metaloader->setTypes($source['types']);
+ } else {
+ $metaloader->setTypes($set_types);
+ }
+
# Merge global and src specific blacklists
if(isset($source['blacklist'])) {
$source['blacklist'] = array_unique(array_merge($source['blacklist'], $blacklist));
diff --git a/modules/metarefresh/lib/MetaLoader.php b/modules/metarefresh/lib/MetaLoader.php
index 3f09817..91f505e 100644
--- a/modules/metarefresh/lib/MetaLoader.php
+++ b/modules/metarefresh/lib/MetaLoader.php
@@ -11,8 +11,13 @@ class sspmod_metarefresh_MetaLoader {
private $oldMetadataSrc;
private $stateFile;
private $changed;
- private static $types = array('saml20-idp-remote', 'saml20-sp-remote',
- 'shib13-idp-remote', 'shib13-sp-remote', 'attributeauthority-remote');
+ private $types = array(
+ 'saml20-idp-remote',
+ 'saml20-sp-remote',
+ 'shib13-idp-remote',
+ 'shib13-sp-remote',
+ 'attributeauthority-remote'
+ );
/**
@@ -37,6 +42,33 @@ class sspmod_metarefresh_MetaLoader {
}
+
+ /**
+ * Get the types of entities that will be loaded.
+ *
+ * @return array The entity types allowed.
+ */
+ public function getTypes()
+ {
+ return $this->types;
+ }
+
+
+ /**
+ * Set the types of entities that will be loaded.
+ *
+ * @param string|array $types Either a string with the name of one single type allowed, or an array with a list of
+ * types. Pass an empty array to reset to all types of entities.
+ */
+ public function setTypes($types)
+ {
+ if (!is_array($types)) {
+ $types = array($types);
+ }
+ $this->types = $types;
+ }
+
+
/**
* This function processes a SAML metadata file.
*
@@ -176,7 +208,7 @@ class sspmod_metarefresh_MetaLoader {
private function addCachedMetadata($source) {
if(isset($this->oldMetadataSrc)) {
- foreach(self::$types as $type) {
+ foreach($this->types as $type) {
foreach($this->oldMetadataSrc->getMetadataSet($type) as $entity) {
if(array_key_exists('metarefresh:src', $entity)) {
if($entity['metarefresh:src'] == $source['src']) {
@@ -369,7 +401,7 @@ class sspmod_metarefresh_MetaLoader {
}
}
- foreach(self::$types as $type) {
+ foreach($this->types as $type) {
$filename = $outputDir . '/' . $type . '.php';
diff --git a/modules/metarefresh/www/fetch.php b/modules/metarefresh/www/fetch.php
index 322c344..5f8cb28 100644
--- a/modules/metarefresh/www/fetch.php
+++ b/modules/metarefresh/www/fetch.php
@@ -30,8 +30,25 @@ foreach ($sets AS $setkey => $set) {
$blacklist = $mconfig->getArray('blacklist', array());
$whitelist = $mconfig->getArray('whitelist', array());
+ // get global type filters
+ $available_types = array(
+ 'saml20-idp-remote',
+ 'saml20-sp-remote',
+ 'shib13-idp-remote',
+ 'shib13-sp-remote',
+ 'attributeauthority-remote'
+ );
+ $set_types = $set->getArrayize('types', $available_types);
+
foreach($set->getArray('sources') AS $source) {
+ // filter metadata by type of entity
+ if (isset($source['types'])) {
+ $metaloader->setTypes($source['types']);
+ } else {
+ $metaloader->setTypes($set_types);
+ }
+
# Merge global and src specific blacklists
if(isset($source['blacklist'])) {
$source['blacklist'] = array_unique(array_merge($source['blacklist'], $blacklist));
diff --git a/modules/saml/lib/SP/LogoutStore.php b/modules/saml/lib/SP/LogoutStore.php
index fa78cdf..b471f60 100644
--- a/modules/saml/lib/SP/LogoutStore.php
+++ b/modules/saml/lib/SP/LogoutStore.php
@@ -125,7 +125,7 @@ class sspmod_saml_SP_LogoutStore {
/**
* Retrieve all session IDs from a key-value store.
*
- * @param SimpleSAML_Store_SQL $store The datastore.
+ * @param SimpleSAML_Store $store The datastore.
* @param string $authId The authsource ID.
* @param string $nameId The hash of the users NameID.
* @param array $sessionIndexes The session indexes.
@@ -234,6 +234,7 @@ class sspmod_saml_SP_LogoutStore {
/* We cannot fetch all sessions without a SQL store. */
return FALSE;
} else {
+ /** @var SimpleSAML_Store $sessions At this point the store cannot be false */
$sessions = self::getSessionsStore($store, $authId, $strNameId, $sessionIndexes);
}
diff --git a/modules/saml/www/sp/saml2-acs.php b/modules/saml/www/sp/saml2-acs.php
index 68751e3..624f7cc 100644
--- a/modules/saml/www/sp/saml2-acs.php
+++ b/modules/saml/www/sp/saml2-acs.php
@@ -25,161 +25,177 @@ try {
}
if ($b instanceof SAML2_HTTPArtifact) {
- $b->setSPMetadata($spMetadata);
+ $b->setSPMetadata($spMetadata);
}
$response = $b->receive();
if (!($response instanceof SAML2_Response)) {
- throw new SimpleSAML_Error_BadRequest('Invalid message received to AssertionConsumerService endpoint.');
+ throw new SimpleSAML_Error_BadRequest('Invalid message received to AssertionConsumerService endpoint.');
}
$idp = $response->getIssuer();
-if ($idp === NULL) {
- /* No Issuer in the response. Look for an unencrypted assertion with an issuer. */
- foreach ($response->getAssertions() as $a) {
- if ($a instanceof SAML2_Assertion) {
- /* We found an unencrypted assertion - there should be an issuer here. */
- $idp = $a->getIssuer();
- break;
- }
- }
- if ($idp === NULL) {
- /* No issuer found in the assertions. */
- throw new Exception('Missing <saml:Issuer> in message delivered to AssertionConsumerService.');
- }
+if ($idp === null) {
+ // no Issuer in the response. Look for an unencrypted assertion with an issuer
+ foreach ($response->getAssertions() as $a) {
+ if ($a instanceof SAML2_Assertion) {
+ // we found an unencrypted assertion, there should be an issuer here
+ $idp = $a->getIssuer();
+ break;
+ }
+ }
+ if ($idp === null) {
+ // no issuer found in the assertions
+ throw new Exception('Missing <saml:Issuer> in message delivered to AssertionConsumerService.');
+ }
}
$session = SimpleSAML_Session::getSessionFromRequest();
$prevAuth = $session->getAuthData($sourceId, 'saml:sp:prevAuth');
-if ($prevAuth !== NULL && $prevAuth['id'] === $response->getId() && $prevAuth['issuer'] === $idp) {
- /* OK, it looks like this message has the same issuer
- * and ID as the SP session we already have active. We
- * therefore assume that the user has somehow triggered
- * a resend of the message.
- * In that case we may as well just redo the previous redirect
- * instead of displaying a confusing error message.
- */
- SimpleSAML_Logger::info('Duplicate SAML 2 response detected - ignoring the response and redirecting the user to the correct page.');
- \SimpleSAML\Utils\HTTP::redirectTrustedURL($prevAuth['redirect']);
+if ($prevAuth !== null && $prevAuth['id'] === $response->getId() && $prevAuth['issuer'] === $idp) {
+ /* OK, it looks like this message has the same issuer
+ * and ID as the SP session we already have active. We
+ * therefore assume that the user has somehow triggered
+ * a resend of the message.
+ * In that case we may as well just redo the previous redirect
+ * instead of displaying a confusing error message.
+ */
+ SimpleSAML_Logger::info(
+ 'Duplicate SAML 2 response detected - ignoring the response and redirecting the user to the correct page.'
+ );
+ if (isset($prevAuth['redirect'])) {
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL($prevAuth['redirect']);
+ }
+
+ SimpleSAML_Logger::info('No RelayState or ReturnURL available, cannot redirect.');
+ throw new SimpleSAML_Error_Exception('Duplicate assertion received.');
}
$idpMetadata = array();
$stateId = $response->getInResponseTo();
if (!empty($stateId)) {
- /* This is a response to a request we sent earlier. */
- $state = SimpleSAML_Auth_State::loadState($stateId, 'saml:sp:sso');
-
- /* Check that the authentication source is correct. */
- assert('array_key_exists("saml:sp:AuthId", $state)');
- if ($state['saml:sp:AuthId'] !== $sourceId) {
- throw new SimpleSAML_Error_Exception('The authentication source id in the URL does not match the authentication source which sent the request.');
- }
-
- /* Check that the issuer is the one we are expecting. */
- assert('array_key_exists("ExpectedIssuer", $state)');
- if ($state['ExpectedIssuer'] !== $idp) {
- $idpMetadata = $source->getIdPMetadata($idp);
- $idplist = $idpMetadata->getArrayize('IDPList', array());
- if (!in_array($state['ExpectedIssuer'], $idplist)) {
- throw new SimpleSAML_Error_Exception('The issuer of the response does not match to the identity provider we sent the request to.');
- }
- }
+ // this is a response to a request we sent earlier
+ $state = SimpleSAML_Auth_State::loadState($stateId, 'saml:sp:sso');
+
+ // check that the authentication source is correct
+ assert('array_key_exists("saml:sp:AuthId", $state)');
+ if ($state['saml:sp:AuthId'] !== $sourceId) {
+ throw new SimpleSAML_Error_Exception(
+ 'The authentication source id in the URL does not match the authentication source which sent the request.'
+ );
+ }
+
+ // check that the issuer is the one we are expecting
+ assert('array_key_exists("ExpectedIssuer", $state)');
+ if ($state['ExpectedIssuer'] !== $idp) {
+ $idpMetadata = $source->getIdPMetadata($idp);
+ $idplist = $idpMetadata->getArrayize('IDPList', array());
+ if (!in_array($state['ExpectedIssuer'], $idplist)) {
+ throw new SimpleSAML_Error_Exception(
+ 'The issuer of the response does not match to the identity provider we sent the request to.'
+ );
+ }
+ }
} else {
- /* This is an unsolicited response. */
- $state = array(
- 'saml:sp:isUnsolicited' => TRUE,
- 'saml:sp:AuthId' => $sourceId,
- 'saml:sp:RelayState' => \SimpleSAML\Utils\HTTP::checkURLAllowed($response->getRelayState()),
- );
+ // this is an unsolicited response
+ $state = array(
+ 'saml:sp:isUnsolicited' => true,
+ 'saml:sp:AuthId' => $sourceId,
+ 'saml:sp:RelayState' => \SimpleSAML\Utils\HTTP::checkURLAllowed(
+ $spMetadata->getString(
+ 'RelayState',
+ $response->getRelayState()
+ )
+ ),
+ );
}
-SimpleSAML_Logger::debug('Received SAML2 Response from ' . var_export($idp, TRUE) . '.');
+SimpleSAML_Logger::debug('Received SAML2 Response from '.var_export($idp, true).'.');
if (empty($idpMetadata)) {
- $idpMetadata = $source->getIdPmetadata($idp);
+ $idpMetadata = $source->getIdPmetadata($idp);
}
try {
- $assertions = sspmod_saml_Message::processResponse($spMetadata, $idpMetadata, $response);
+ $assertions = sspmod_saml_Message::processResponse($spMetadata, $idpMetadata, $response);
} catch (sspmod_saml_Error $e) {
- /* The status of the response wasn't "success". */
- $e = $e->toException();
- SimpleSAML_Auth_State::throwException($state, $e);
+ // the status of the response wasn't "success"
+ $e = $e->toException();
+ SimpleSAML_Auth_State::throwException($state, $e);
}
-$authenticatingAuthority = NULL;
-$nameId = NULL;
-$sessionIndex = NULL;
-$expire = NULL;
+$authenticatingAuthority = null;
+$nameId = null;
+$sessionIndex = null;
+$expire = null;
$attributes = array();
-$foundAuthnStatement = FALSE;
+$foundAuthnStatement = false;
foreach ($assertions as $assertion) {
- /* Check for duplicate assertion (replay attack). */
- $store = SimpleSAML_Store::getInstance();
- if ($store !== FALSE) {
- $aID = $assertion->getId();
- if ($store->get('saml.AssertionReceived', $aID) !== NULL) {
- $e = new SimpleSAML_Error_Exception('Received duplicate assertion.');
- SimpleSAML_Auth_State::throwException($state, $e);
- }
-
- $notOnOrAfter = $assertion->getNotOnOrAfter();
- if ($notOnOrAfter === NULL) {
- $notOnOrAfter = time() + 24*60*60;
- } else {
- $notOnOrAfter += 60; /* We allow 60 seconds clock skew, so add it here also. */
- }
-
- $store->set('saml.AssertionReceived', $aID, TRUE, $notOnOrAfter);
- }
-
-
- if ($authenticatingAuthority === NULL) {
- $authenticatingAuthority = $assertion->getAuthenticatingAuthority();
- }
- if ($nameId === NULL) {
- $nameId = $assertion->getNameId();
- }
- if ($sessionIndex === NULL) {
- $sessionIndex = $assertion->getSessionIndex();
- }
- if ($expire === NULL) {
- $expire = $assertion->getSessionNotOnOrAfter();
- }
-
- $attributes = array_merge($attributes, $assertion->getAttributes());
-
- if ($assertion->getAuthnInstant() !== NULL) {
- /* Assertion contains AuthnStatement, since AuthnInstant is a required attribute. */
- $foundAuthnStatement = TRUE;
- }
+ // check for duplicate assertion (replay attack)
+ $store = SimpleSAML_Store::getInstance();
+ if ($store !== false) {
+ $aID = $assertion->getId();
+ if ($store->get('saml.AssertionReceived', $aID) !== null) {
+ $e = new SimpleSAML_Error_Exception('Received duplicate assertion.');
+ SimpleSAML_Auth_State::throwException($state, $e);
+ }
+
+ $notOnOrAfter = $assertion->getNotOnOrAfter();
+ if ($notOnOrAfter === null) {
+ $notOnOrAfter = time() + 24 * 60 * 60;
+ } else {
+ $notOnOrAfter += 60; // we allow 60 seconds clock skew, so add it here also
+ }
+
+ $store->set('saml.AssertionReceived', $aID, true, $notOnOrAfter);
+ }
+
+
+ if ($authenticatingAuthority === null) {
+ $authenticatingAuthority = $assertion->getAuthenticatingAuthority();
+ }
+ if ($nameId === null) {
+ $nameId = $assertion->getNameId();
+ }
+ if ($sessionIndex === null) {
+ $sessionIndex = $assertion->getSessionIndex();
+ }
+ if ($expire === null) {
+ $expire = $assertion->getSessionNotOnOrAfter();
+ }
+
+ $attributes = array_merge($attributes, $assertion->getAttributes());
+
+ if ($assertion->getAuthnInstant() !== null) {
+ // assertion contains AuthnStatement, since AuthnInstant is a required attribute
+ $foundAuthnStatement = true;
+ }
}
if (!$foundAuthnStatement) {
- $e = new SimpleSAML_Error_Exception('No AuthnStatement found in assertion(s).');
- SimpleSAML_Auth_State::throwException($state, $e);
+ $e = new SimpleSAML_Error_Exception('No AuthnStatement found in assertion(s).');
+ SimpleSAML_Auth_State::throwException($state, $e);
}
-if ($expire !== NULL) {
- $logoutExpire = $expire;
+if ($expire !== null) {
+ $logoutExpire = $expire;
} else {
- /* Just expire the logout associtaion 24 hours into the future. */
- $logoutExpire = time() + 24*60*60;
+ // just expire the logout association 24 hours into the future
+ $logoutExpire = time() + 24 * 60 * 60;
}
-/* Register this session in the logout store. */
+// register this session in the logout store
sspmod_saml_SP_LogoutStore::addSession($sourceId, $nameId, $sessionIndex, $logoutExpire);
-/* We need to save the NameID and SessionIndex for logout. */
+// we need to save the NameID and SessionIndex for logout
$logoutState = array(
- 'saml:logout:Type' => 'saml2',
- 'saml:logout:IdP' => $idp,
- 'saml:logout:NameID' => $nameId,
- 'saml:logout:SessionIndex' => $sessionIndex,
- );
+ 'saml:logout:Type' => 'saml2',
+ 'saml:logout:IdP' => $idp,
+ 'saml:logout:NameID' => $nameId,
+ 'saml:logout:SessionIndex' => $sessionIndex,
+);
$state['LogoutState'] = $logoutState;
$state['saml:AuthenticatingAuthority'] = $authenticatingAuthority;
$state['saml:AuthenticatingAuthority'][] = $idp;
@@ -192,21 +208,21 @@ $state['PersistentAuthData'][] = 'saml:sp:SessionIndex';
$state['saml:sp:AuthnContext'] = $assertion->getAuthnContext();
$state['PersistentAuthData'][] = 'saml:sp:AuthnContext';
-if ($expire !== NULL) {
- $state['Expire'] = $expire;
+if ($expire !== null) {
+ $state['Expire'] = $expire;
}
+// note some information about the authentication, in case we receive the same response again
+$state['saml:sp:prevAuth'] = array(
+ 'id' => $response->getId(),
+ 'issuer' => $idp,
+);
if (isset($state['SimpleSAML_Auth_Default.ReturnURL'])) {
- /* Just note some information about the authentication, in case we receive the
- * same response again.
- */
- $state['saml:sp:prevAuth'] = array(
- 'id' => $response->getId(),
- 'issuer' => $idp,
- 'redirect' => $state['SimpleSAML_Auth_Default.ReturnURL'],
- );
- $state['PersistentAuthData'][] = 'saml:sp:prevAuth';
+ $state['saml:sp:prevAuth']['redirect'] = $state['SimpleSAML_Auth_Default.ReturnURL'];
+} elseif (isset($state['saml:sp:RelayState'])) {
+ $state['saml:sp:prevAuth']['redirect'] = $state['saml:sp:RelayState'];
}
+$state['PersistentAuthData'][] = 'saml:sp:prevAuth';
$source->handleResponse($state, $idp, $attributes);
assert('FALSE');
diff --git a/modules/saml2debug/default-disable b/modules/saml2debug/default-disable
deleted file mode 100644
index fa0bd82..0000000
--- a/modules/saml2debug/default-disable
+++ /dev/null
@@ -1,3 +0,0 @@
-This file indicates that the default state of this module
-is disabled. To enable, create a file named enable in the
-same directory as this file.
diff --git a/modules/saml2debug/hooks/hook_frontpage.php b/modules/saml2debug/hooks/hook_frontpage.php
deleted file mode 100644
index dfc913f..0000000
--- a/modules/saml2debug/hooks/hook_frontpage.php
+++ /dev/null
@@ -1,16 +0,0 @@
-<?php
-/**
- * Hook to add the simple consenet admin module to the frontpage.
- *
- * @param array &$links The links on the frontpage, split into sections.
- */
-function saml2debug_hook_frontpage(&$links) {
- assert('is_array($links)');
- assert('array_key_exists("links", $links)');
-
- $links['federation'][] = array(
- 'href' => SimpleSAML_Module::getModuleURL('saml2debug/debug.php'),
- 'text' => array('en' => 'SAML 2.0 Debugger'),
- );
-
-}
diff --git a/modules/saml2debug/templates/debug.tpl.php b/modules/saml2debug/templates/debug.tpl.php
deleted file mode 100644
index 05ce4aa..0000000
--- a/modules/saml2debug/templates/debug.tpl.php
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-
-$this->data['jquery'] = array('version' => '1.6', 'core' => TRUE, 'ui' => TRUE, 'css' => TRUE);
-$this->data['head'] = '<script type="text/javascript">
-
-$(document).ready(function() {
- $("#tabdiv").tabs({ selected: ' . $this->data['activeTab'] . ' });
-});
-</script>';
-
-$this->data['header'] = 'SAML 2.0 Debugger';
-$this->includeAtTemplateBase('includes/header.php');
-?>
-
-
-
-<div id="tabdiv">
-<ul>
- <li><a href="#decode">Decode</a></li>
- <li><a href="#encode">Encode</a></li>
-</ul>
-
-
-<div id="decode">
-
- <p>Paste in a SAML message encoded with the HTTP-POST or HTTP-REDIRECT encoding. You
- can both use the full URL that you copied from LiveHTTPHeaders, or you can paste in
- only the SAMLRequest or SAMLResponse parameter. It will be automatically detected
- whether you post a URL or the value it self and whether you post a HTTP-REDIRECT or
- HTTP-POST encoded value. enjoy!</p>
-
- <form method="post" action="debug.php">
- <textarea style="width: 95%; border: 1px solid #999; font-family: monospace" cols="50" rows="10" name="encoded"><?php echo $this->data['encoded']; ?></textarea>
- <p><input type="submit" name="decode" value="Decode SAML message »" /></p>
- </form>
-
-</div> <!-- #redirect -->
-
-<div id="encode">
-
- <p>Type in the SAML Message below, and select which binding to use.</p>
-
- <form method="post" action="debug.php">
- <textarea style="width: 95%; border: 1px solid #999" cols="50" rows="20" name="decoded"><?php echo $this->data['decoded']; ?></textarea>
-
- <div style="margin: 1em">
- Use this binding:
- <select name="binding">
- <option value="redirect">HTTP-REDIRECT</option>
- <option value="post">HTTP-POST</option>
- </select>
- </div>
-
- <p><input type="submit" name="decode" value="« Encode SAML message" /></p>
- </form>
-
-</div> <!-- #redirect -->
-
-
-
-
-</div> <!-- #tabdiv -->
-
-
-
-
-
-<?php $this->includeAtTemplateBase('includes/footer.php');
diff --git a/modules/saml2debug/www/debug.php b/modules/saml2debug/www/debug.php
deleted file mode 100644
index 4f83925..0000000
--- a/modules/saml2debug/www/debug.php
+++ /dev/null
@@ -1,63 +0,0 @@
-<?php
-
-$config = SimpleSAML_Configuration::getInstance();
-
-
-function getValue($raw) {
-
- $val = $raw;
-
- $url = parse_url($raw, PHP_URL_QUERY);
- if (!empty($url)) $val = $url;
-
- $arr = array();
- $query = parse_str($val, $arr);
-
- if (array_key_exists('SAMLResponse', $arr)) return $arr['SAMLResponse'];
- if (array_key_exists('SAMLRequest', $arr)) return $arr['SAMLRequest'];
- if (array_key_exists('LogoutRequest', $arr)) return $arr['LogoutRequest'];
- if (array_key_exists('LogoutResponse', $arr)) return $arr['LogoutResponse'];
-
- return rawurldecode(stripslashes($val));
-}
-
-function decode($raw) {
- $message = getValue($raw);
-
- $base64decoded = base64_decode($message);
- $gzinflated = gzinflate($base64decoded);
- if ($gzinflated != FALSE) {
- $base64decoded = $gzinflated;
- }
- $decoded = htmlspecialchars($base64decoded);
- return $decoded;
-}
-
-function encode($message) {
- if (!array_key_exists('binding', $_REQUEST)) throw new Exception('missing binding');
- if ($_REQUEST['binding'] === 'redirect') {
- return urlencode(base64_encode(gzdeflate(stripslashes($message))));
- } else {
- return base64_encode(stripslashes($message));
- }
-}
-
-
-$decoded = '';
-$encoded = 'fZJNT%2BMwEIbvSPwHy%2Fd8tMvHympSdUGISuwS0cCBm%2BtMUwfbk%2FU4zfLvSVMq2Euv45n3fd7xzOb%2FrGE78KTRZXwSp5yBU1hpV2f8ubyLfvJ5fn42I2lNKxZd2Lon%2BNsBBTZMOhLjQ8Y77wRK0iSctEAiKLFa%2FH4Q0zgVrceACg1ny9uMy7rCdaM2%2Bs0BWrtppK2UAdeoVjW2ruq1bevGImcvR6zpHmtJ1MHSUZAuDKU0vY7Si2h6VU5%2BiMuJuLx65az4dPql3SHBKaz1oYnEfVkWUfG4KkeBna7A%2Fxm6M14j1gZihZazBRH4MODcoKPOgl%2BB32kFz08PGd%2BG0JJIkr7v46%2BhRCaEpod17DCRivYZCkmkd4N28B3wfNyrGKP5bws9DS6PKDz%2FMpsl36Tyz%2F%2Fax1jeFmi0emcLY7C%2F8SDD0Z7dobcynHbbV3QVbcZW0TlqQemNhoqzJD%2B4%2Fn8Yw7l8AA%3D%3D';
-
-$activeTab = 0;
-
-if (array_key_exists('encoded', $_REQUEST)) {
- $decoded = decode($_REQUEST['encoded']);
- $activeTab = 1;
-}
-if (array_key_exists('decoded', $_REQUEST)) {
- $encoded = encode($_REQUEST['decoded']);
-}
-
-$t = new SimpleSAML_XHTML_Template($config, 'saml2debug:debug.tpl.php');
-$t->data['encoded'] = $encoded;
-$t->data['decoded'] = $decoded;
-$t->data['activeTab'] = $activeTab;
-$t->show();
diff --git a/modules/statistics/lib/StatDataset.php b/modules/statistics/lib/StatDataset.php
index fab589d..4beb34f 100644
--- a/modules/statistics/lib/StatDataset.php
+++ b/modules/statistics/lib/StatDataset.php
@@ -1,310 +1,328 @@
<?php
-/*
+
+
+/**
* @author Andreas Åkre Solberg <andreas.solberg@uninett.no>
* @package simpleSAMLphp
*/
-class sspmod_statistics_StatDataset {
-
- protected $statconfig;
- protected $ruleconfig;
- protected $timeresconfig;
- protected $ruleid;
-
- protected $fileslot;
- protected $timeres;
-
- protected $delimiter;
- protected $results;
- protected $summary;
- protected $max;
-
- protected $datehandlerFile;
- protected $datehandlerTick;
-
- /**
- * Constructor
- */
- public function __construct($statconfig, $ruleconfig, $ruleid, $timeres, $fileslot) {
- assert('$statconfig instanceof SimpleSAML_Configuration');
- assert('$ruleconfig instanceof SimpleSAML_Configuration');
- $this->statconfig = $statconfig;
- $this->ruleconfig = $ruleconfig;
-
- $timeresconfigs = $statconfig->getConfigItem('timeres');
- $this->timeresconfig = $timeresconfigs->getConfigItem($timeres);
-
- $this->ruleid = $ruleid;
- $this->fileslot = $fileslot;
- $this->timeres = $timeres;
-
-
- $this->delimiter = '_';
- $this->max = 0;
-
- $this->datehandlerTick = new sspmod_statistics_DateHandler($this->statconfig->getValue('offset', 0));
- if ($this->timeresconfig->getValue('customDateHandler', 'default') === 'month') {
- $this->datehandlerFile = new sspmod_statistics_DateHandlerMonth(0);
- } else {
- $datehandlerFile = $this->datehandlerTick;
- }
-
-
- $this->loadData();
-
-
- }
-
- public function getFileSlot() {
- return $this->fileslot;
- }
-
- public function getTimeRes() {
- return $this->timeres;
- }
-
- public function setDelimiter($delimiter = '_') {
- if (empty($delimiter)) $delimiter = '_';
- $this->delimiter = $delimiter;
- }
- public function getDelimiter() {
- if ($this->delimiter === '_') return NULL;
- return $this->delimiter;
- }
-
- public function calculateMax() {
-
- /*
- * Get rule specific configuration from the configuration file.
- */
- $slotsize = $this->ruleconfig->getValue('slot');
- $dateformat_period = $this->timeresconfig->getValue('dateformat-period');
- $dateformat_intra = $this->timeresconfig->getValue('dateformat-intra');
-
-
- $maxvalue = 0; $maxvaluetime = NULL;
- foreach($this->results AS $slot => &$res) {
- if (!array_key_exists($this->delimiter, $res)) $res[$this->delimiter] = 0;
- if ($res[$this->delimiter] > $maxvalue) {
- $maxvaluetime = $this->datehandlerTick->prettyDateSlot($slot, $slotsize, $dateformat_intra);
- }
- $maxvalue = max($res[$this->delimiter],$maxvalue);
- }
- $this->max = sspmod_statistics_Graph_GoogleCharts::roof($maxvalue);
- }
-
- public function getDebugData() {
- $debugdata = array();
-
- $slotsize = $this->timeresconfig->getValue('slot');
- $dateformat_period = $this->timeresconfig->getValue('dateformat-period');
- $dateformat_intra = $this->timeresconfig->getValue('dateformat-intra');
-
- foreach($this->results AS $slot => &$res) {
- $debugdata[$slot] = array($this->datehandlerTick->prettyDateSlot($slot, $slotsize, $dateformat_intra), $res[$this->delimiter] );
- }
- return $debugdata;
- }
-
- public function aggregateSummary() {
-
- /**
- * Aggregate summary table from dataset. To be used in the table view.
- */
- $this->summary = array();
- foreach($this->results AS $slot => $res) {
- foreach ($res AS $key => $value) {
- if (array_key_exists($key, $this->summary)) {
- $this->summary[$key] += $value;
- } else {
- $this->summary[$key] = $value;
- }
- }
- }
- asort($this->summary);
- $this->summary = array_reverse($this->summary, TRUE);
- }
-
- public function getTopDelimiters() {
- /*
- * Create a list of delimiter keys that has the highest total summary in this period.
- */
- $topdelimiters = array();
- $maxdelimiters = 4; $i = 0;
- foreach($this->summary AS $key => $value) {
- if ($key !== '_')
- $topdelimiters[] = $key;
- if ($i++ >= $maxdelimiters) break;
- }
- return $topdelimiters;
-
- }
-
- public function availDelimiters() {
- $availDelimiters = array();
- foreach($this->summary AS $key => $value) {
- if ($key !== '_')
- $topdelimiters[] = $key;
- $availdelimiters[$key] = 1;
- }
- return array_keys($availdelimiters);
- }
-
- public function getPieData() {
-
- $piedata = array(); $sum = 0;
- $topdelimiters = $this->getTopDelimiters();
-
- foreach($topdelimiters AS $td) {
- $sum += $this->summary[$td];
- $piedata[] = number_format(100*$this->summary[$td] / $this->summary['_'], 2);
- }
- $piedata[] = number_format(100 - 100*($sum /$this->summary['_']), 2);
- return $piedata;
- }
-
- public function getMax() {
- return $this->max;
- }
-
- public function getSummary() {
- return $this->summary;
- }
-
- public function getResults() {
- return $this->results;
- }
-
- public function getAxis() {
- $slotsize = $this->timeresconfig->getValue('slot');
- $dateformat_period = $this->timeresconfig->getValue('dateformat-period');
- $dateformat_intra = $this->timeresconfig->getValue('dateformat-intra');
- $axislabelint = $this->timeresconfig->getValue('axislabelint');
-
-
- $axis = array();
- $axispos = array();
- $xentries = count($this->results);
- $lastslot = 0; $i = 0;
-
- foreach($this->results AS $slot => $res) {
-
- // check if there should be an axis here...
- if ( $slot % $axislabelint == 0) {
- $axis[] = $this->datehandlerTick->prettyDateSlot($slot, $slotsize, $dateformat_intra);
- $axispos[] = (($i)/($xentries-1));
- }
- $lastslot = $slot;
- $i++;
- }
-
- $axis[] = $this->datehandlerTick->prettyDateSlot($lastslot+1, $slotsize, $dateformat_intra);
-
- return array('axis' => $axis, 'axispos' => $axispos);
- }
-
-
- /*
- * Walk through dataset to get percent values from max into dataset[].
- */
- public function getPercentValues() {
-
-
- $slotsize = $this->timeresconfig->getValue('slot');
- $dateformat_period = $this->timeresconfig->getValue('dateformat-period');
- $dateformat_intra = $this->timeresconfig->getValue('dateformat-intra');
- $axislabelint = $this->timeresconfig->getValue('axislabelint');
-
- $xentries = count($this->results);
- $lastslot = 0; $i = 0;
-
-
- $dataset = array();
- foreach($this->results AS $slot => $res) {
- if (array_key_exists($this->delimiter, $res)) {
- if ($res[$this->delimiter] === NULL) {
- $dataset[] = -1;
- } else {
- $dataset[] = number_format(100*$res[$this->delimiter] / $this->max, 2);
- }
- } else {
- $dataset[] = '0';
- }
- $lastslot = $slot;
- $i++;
- }
-
- return $dataset;
- }
-
-
-
- public function getDelimiterPresentation() {
- $config = SimpleSAML_Configuration::getInstance();
- $t = new SimpleSAML_XHTML_Template($config, 'statistics:statistics-tpl.php');
-
- $availdelimiters = $this->availDelimiters();
-
-
-
- /*
- * Create a delimiter presentation filter for this rule...
- */
- if ($this->ruleconfig->hasValue('fieldPresentation')) {
- $fieldpresConfig = $this->ruleconfig->getConfigItem('fieldPresentation');
- $classname = SimpleSAML_Module::resolveClass($fieldpresConfig->getValue('class'), 'Statistics_FieldPresentation');
- if (!class_exists($classname))
- throw new Exception('Could not find field presentation plugin [' . $classname . ']: No class found');
- $presentationHandler = new $classname($availdelimiters, $fieldpresConfig->getValue('config'), $t);
-
- return $presentationHandler->getPresentation();
- }
-
- return array();
- }
-
- public function getDelimiterPresentationPie() {
- $topdelimiters = $this->getTopDelimiters();
- $delimiterPresentation = $this->getDelimiterPresentation();
-
- $pieaxis = array();
- foreach($topdelimiters AS $key) {
- $keyName = $key;
- if(array_key_exists($key, $delimiterPresentation)) $keyName = $delimiterPresentation[$key];
- $pieaxis[] = $keyName;
- }
- $pieaxis[] = 'Others';
- return $pieaxis;
- }
-
-
-
- public function loadData() {
-
- $statdir = $this->statconfig->getValue('statdir');
- $resarray = array();
- $rules = SimpleSAML\Utils\Arrays::arrayize($this->ruleid);
- foreach($rules AS $rule) {
- // Get file and extract results.
- $resultFileName = $statdir . '/' . $rule . '-' . $this->timeres . '-'. $this->fileslot . '.stat';
- if (!file_exists($resultFileName))
- throw new Exception('Aggregated statitics file [' . $resultFileName . '] not found.');
- if (!is_readable($resultFileName))
- throw new Exception('Could not read statitics file [' . $resultFileName . ']. Bad file permissions?');
- $resultfile = file_get_contents($resultFileName);
- $newres = unserialize($resultfile);
- if (empty($newres))
- throw new Exception('Aggregated statistics in file [' . $resultFileName . '] was empty.');
- $resarray[] = $newres;
- }
-
- $combined = $resarray[0];
- if(count($resarray) > 1) {
- for($i = 1; $i < count($resarray); $i++) {
- $combined = $this->combine($combined, $resarray[$i]);
- }
- }
- $this->results = $combined;
- }
+class sspmod_statistics_StatDataset
+{
+
+ protected $statconfig;
+ protected $ruleconfig;
+ protected $timeresconfig;
+ protected $ruleid;
+
+ protected $fileslot;
+ protected $timeres;
+
+ protected $delimiter;
+ protected $results;
+ protected $summary;
+ protected $max;
+
+ protected $datehandlerFile;
+ protected $datehandlerTick;
+
+
+ /**
+ * Constructor
+ */
+ public function __construct($statconfig, $ruleconfig, $ruleid, $timeres, $fileslot)
+ {
+ assert('$statconfig instanceof SimpleSAML_Configuration');
+ assert('$ruleconfig instanceof SimpleSAML_Configuration');
+ $this->statconfig = $statconfig;
+ $this->ruleconfig = $ruleconfig;
+
+ $timeresconfigs = $statconfig->getConfigItem('timeres');
+ $this->timeresconfig = $timeresconfigs->getConfigItem($timeres);
+
+ $this->ruleid = $ruleid;
+ $this->fileslot = $fileslot;
+ $this->timeres = $timeres;
+
+ $this->delimiter = '_';
+ $this->max = 0;
+
+ $this->datehandlerTick = new sspmod_statistics_DateHandler($this->statconfig->getValue('offset', 0));
+ if ($this->timeresconfig->getValue('customDateHandler', 'default') === 'month') {
+ $this->datehandlerFile = new sspmod_statistics_DateHandlerMonth(0);
+ } else {
+ $this->datehandlerFile = $this->datehandlerTick;
+ }
+
+ $this->loadData();
+ }
+
+
+ public function getFileSlot()
+ {
+ return $this->fileslot;
+ }
+
+
+ public function getTimeRes()
+ {
+ return $this->timeres;
+ }
+
+
+ public function setDelimiter($delimiter = '_')
+ {
+ if (empty($delimiter)) {
+ $delimiter = '_';
+ }
+ $this->delimiter = $delimiter;
+ }
+
+
+ public function getDelimiter()
+ {
+ if ($this->delimiter === '_') {
+ return null;
+ }
+ return $this->delimiter;
+ }
+
+
+ public function calculateMax()
+ {
+ $maxvalue = 0;
+ foreach ($this->results as $slot => &$res) {
+ if (!array_key_exists($this->delimiter, $res)) {
+ $res[$this->delimiter] = 0;
+ }
+ $maxvalue = max($res[$this->delimiter], $maxvalue);
+ }
+ $this->max = sspmod_statistics_Graph_GoogleCharts::roof($maxvalue);
+ }
+
+
+ public function getDebugData()
+ {
+ $debugdata = array();
+
+ $slotsize = $this->timeresconfig->getValue('slot');
+ $dateformat_intra = $this->timeresconfig->getValue('dateformat-intra');
+
+ foreach ($this->results as $slot => &$res) {
+ $debugdata[$slot] = array(
+ $this->datehandlerTick->prettyDateSlot($slot, $slotsize, $dateformat_intra),
+ $res[$this->delimiter]
+ );
+ }
+ return $debugdata;
+ }
+
+
+ public function aggregateSummary()
+ {
+ // aggregate summary table from dataset. To be used in the table view
+ $this->summary = array();
+ foreach ($this->results as $slot => $res) {
+ foreach ($res as $key => $value) {
+ if (array_key_exists($key, $this->summary)) {
+ $this->summary[$key] += $value;
+ } else {
+ $this->summary[$key] = $value;
+ }
+ }
+ }
+ asort($this->summary);
+ $this->summary = array_reverse($this->summary, true);
+ }
+
+
+ public function getTopDelimiters()
+ {
+ // create a list of delimiter keys that has the highest total summary in this period
+ $topdelimiters = array();
+ $maxdelimiters = 4;
+ $i = 0;
+ foreach ($this->summary as $key => $value) {
+ if ($key !== '_') {
+ $topdelimiters[] = $key;
+ }
+ if ($i++ >= $maxdelimiters) {
+ break;
+ }
+ }
+ return $topdelimiters;
+ }
+
+
+ public function availDelimiters()
+ {
+ $availDelimiters = array();
+ foreach ($this->summary as $key => $value) {
+ $availDelimiters[$key] = 1;
+ }
+ return array_keys($availDelimiters);
+ }
+
+
+ public function getPieData()
+ {
+ $piedata = array();
+ $sum = 0;
+ $topdelimiters = $this->getTopDelimiters();
+
+ foreach ($topdelimiters as $td) {
+ $sum += $this->summary[$td];
+ $piedata[] = number_format(100 * $this->summary[$td] / $this->summary['_'], 2);
+ }
+ $piedata[] = number_format(100 - 100 * ($sum / $this->summary['_']), 2);
+ return $piedata;
+ }
+
+
+ public function getMax()
+ {
+ return $this->max;
+ }
+
+
+ public function getSummary()
+ {
+ return $this->summary;
+ }
+
+
+ public function getResults()
+ {
+ return $this->results;
+ }
+
+
+ public function getAxis()
+ {
+ $slotsize = $this->timeresconfig->getValue('slot');
+ $dateformat_period = $this->timeresconfig->getValue('dateformat-period');
+ $dateformat_intra = $this->timeresconfig->getValue('dateformat-intra');
+ $axislabelint = $this->timeresconfig->getValue('axislabelint');
+
+ $axis = array();
+ $axispos = array();
+ $xentries = count($this->results);
+ $lastslot = 0;
+ $i = 0;
+
+ foreach ($this->results as $slot => $res) {
+ // check if there should be an axis here...
+ if ($slot % $axislabelint == 0) {
+ $axis[] = $this->datehandlerTick->prettyDateSlot($slot, $slotsize, $dateformat_intra);
+ $axispos[] = (($i) / ($xentries - 1));
+ }
+ $lastslot = $slot;
+ $i++;
+ }
+
+ $axis[] = $this->datehandlerTick->prettyDateSlot($lastslot + 1, $slotsize, $dateformat_intra);
+
+ return array('axis' => $axis, 'axispos' => $axispos);
+ }
+
+
+ /*
+ * Walk through dataset to get percent values from max into dataset[].
+ */
+ public function getPercentValues()
+ {
+ $i = 0;
+ $dataset = array();
+ foreach ($this->results as $slot => $res) {
+ if (array_key_exists($this->delimiter, $res)) {
+ if ($res[$this->delimiter] === null) {
+ $dataset[] = -1;
+ } else {
+ $dataset[] = number_format(100 * $res[$this->delimiter] / $this->max, 2);
+ }
+ } else {
+ $dataset[] = '0';
+ }
+ $i++;
+ }
+
+ return $dataset;
+ }
+
+
+ public function getDelimiterPresentation()
+ {
+ $config = SimpleSAML_Configuration::getInstance();
+ $t = new SimpleSAML_XHTML_Template($config, 'statistics:statistics-tpl.php');
+
+ $availdelimiters = $this->availDelimiters();
+
+ // create a delimiter presentation filter for this rule...
+ if ($this->ruleconfig->hasValue('fieldPresentation')) {
+ $fieldpresConfig = $this->ruleconfig->getConfigItem('fieldPresentation');
+ $classname = SimpleSAML_Module::resolveClass(
+ $fieldpresConfig->getValue('class'),
+ 'Statistics_FieldPresentation'
+ );
+ if (!class_exists($classname)) {
+ throw new Exception('Could not find field presentation plugin ['.$classname.']: No class found');
+ }
+ $presentationHandler = new $classname($availdelimiters, $fieldpresConfig->getValue('config'), $t);
+
+ return $presentationHandler->getPresentation();
+ }
+
+ return array();
+ }
+
+
+ public function getDelimiterPresentationPie()
+ {
+ $topdelimiters = $this->getTopDelimiters();
+ $delimiterPresentation = $this->getDelimiterPresentation();
+
+ $pieaxis = array();
+ foreach ($topdelimiters as $key) {
+ $keyName = $key;
+ if (array_key_exists($key, $delimiterPresentation)) {
+ $keyName = $delimiterPresentation[$key];
+ }
+ $pieaxis[] = $keyName;
+ }
+ $pieaxis[] = 'Others';
+ return $pieaxis;
+ }
+
+
+ public function loadData()
+ {
+ $statdir = $this->statconfig->getValue('statdir');
+ $resarray = array();
+ $rules = SimpleSAML\Utils\Arrays::arrayize($this->ruleid);
+ foreach ($rules as $rule) {
+ // Get file and extract results.
+ $resultFileName = $statdir.'/'.$rule.'-'.$this->timeres.'-'.$this->fileslot.'.stat';
+ if (!file_exists($resultFileName)) {
+ throw new Exception('Aggregated statitics file ['.$resultFileName.'] not found.');
+ }
+ if (!is_readable($resultFileName)) {
+ throw new Exception('Could not read statitics file ['.$resultFileName.']. Bad file permissions?');
+ }
+ $resultfile = file_get_contents($resultFileName);
+ $newres = unserialize($resultfile);
+ if (empty($newres)) {
+ throw new Exception('Aggregated statistics in file ['.$resultFileName.'] was empty.');
+ }
+ $resarray[] = $newres;
+ }
+
+ $combined = $resarray[0];
+ $count = count($resarray);
+ if ($count > 1) {
+ for ($i = 1; $i < $count; $i++) {
+ $combined = $this->combine($combined, $resarray[$i]);
+ }
+ }
+ $this->results = $combined;
+ }
}
diff --git a/tests/lib/SimpleSAML/DatabaseTest.php b/tests/lib/SimpleSAML/DatabaseTest.php
new file mode 100644
index 0000000..8f09b91
--- /dev/null
+++ b/tests/lib/SimpleSAML/DatabaseTest.php
@@ -0,0 +1,297 @@
+<?php
+
+
+/**
+ * This test ensures that the SimpleSAML_Database class can properly
+ * query a database.
+ *
+ * It currently uses sqlite to test, but an alternate config.php file
+ * should be created for test cases to ensure that it will work
+ * in an environment.
+ *
+ * @author Tyler Antonio, University of Alberta. <tantonio@ualberta.ca>
+ * @package simpleSAMLphp
+ */
+class SimpleSAML_DatabaseTest extends PHPUnit_Framework_TestCase
+{
+
+ /**
+ * @var SimpleSAML_Configuration
+ */
+ protected $config;
+
+ /**
+ * @var SimpleSAML\Database
+ */
+ protected $db;
+
+
+ /**
+ * Make protected functions available for testing
+ *
+ * @param string $getMethod The method to get.
+ * @requires PHP 5.3.2
+ *
+ * @return mixed The method itself.
+ */
+ protected static function getMethod($getMethod)
+ {
+ $class = new ReflectionClass('SimpleSAML\Database');
+ $method = $class->getMethod($getMethod);
+ $method->setAccessible(true);
+ return $method;
+ }
+
+
+ /**
+ * @covers SimpleSAML\Database::getInstance
+ * @covers SimpleSAML\Database::generateInstanceId
+ * @covers SimpleSAML\Database::__construct
+ * @covers SimpleSAML\Database::connect
+ */
+ public function setUp()
+ {
+ $config = array(
+ 'database.dsn' => 'sqlite::memory:',
+ 'database.username' => null,
+ 'database.password' => null,
+ 'database.prefix' => 'phpunit_',
+ 'database.persistent' => true,
+ 'database.slaves' => array(),
+ );
+
+ $this->config = new SimpleSAML_Configuration($config, "test/SimpleSAML/DatabaseTest.php");
+
+ // Ensure that we have a functional configuration class.
+ $this->assertInstanceOf('SimpleSAML_Configuration', $this->config);
+ $this->assertEquals($config['database.dsn'], $this->config->getString('database.dsn'));
+
+ $this->db = SimpleSAML\Database::getInstance($this->config);
+
+ // Ensure that we have a functional database class.
+ $this->assertInstanceOf('SimpleSAML\Database', $this->db);
+ }
+
+
+ /**
+ * @covers SimpleSAML\Database::getInstance
+ * @covers SimpleSAML\Database::generateInstanceId
+ * @covers SimpleSAML\Database::__construct
+ * @covers SimpleSAML\Database::connect
+ * @expectedException Exception
+ * @test
+ */
+ public function connectionFailure()
+ {
+ $config = array(
+ 'database.dsn' => 'mysql:host=localhost;dbname=saml',
+ 'database.username' => 'notauser',
+ 'database.password' => 'notausersinvalidpassword',
+ 'database.prefix' => 'phpunit_',
+ 'database.persistent' => true,
+ 'database.slaves' => array(),
+ );
+
+ $this->config = new SimpleSAML_Configuration($config, "test/SimpleSAML/DatabaseTest.php");
+ $db = SimpleSAML\Database::getInstance($this->config);
+ }
+
+
+ /**
+ * @covers SimpleSAML\Database::getInstance
+ * @covers SimpleSAML\Database::generateInstanceId
+ * @covers SimpleSAML\Database::__construct
+ * @covers SimpleSAML\Database::connect
+ * @test
+ */
+ public function instances()
+ {
+ $config = array(
+ 'database.dsn' => 'sqlite::memory:',
+ 'database.username' => null,
+ 'database.password' => null,
+ 'database.prefix' => 'phpunit_',
+ 'database.persistent' => true,
+ 'database.slaves' => array(),
+ );
+ $config2 = array(
+ 'database.dsn' => 'sqlite::memory:',
+ 'database.username' => null,
+ 'database.password' => null,
+ 'database.prefix' => 'phpunit2_',
+ 'database.persistent' => true,
+ 'database.slaves' => array(),
+ );
+
+ $config1 = new SimpleSAML_Configuration($config, "test/SimpleSAML/DatabaseTest.php");
+ $config2 = new SimpleSAML_Configuration($config2, "test/SimpleSAML/DatabaseTest.php");
+ $config3 = new SimpleSAML_Configuration($config, "test/SimpleSAML/DatabaseTest.php");
+
+ $db1 = SimpleSAML\Database::getInstance($config1);
+ $db2 = SimpleSAML\Database::getInstance($config2);
+ $db3 = SimpleSAML\Database::getInstance($config3);
+
+ $generateInstanceId = self::getMethod('generateInstanceId');
+
+ $instance1 = $generateInstanceId->invokeArgs($db1, array($config1));
+ $instance2 = $generateInstanceId->invokeArgs($db2, array($config2));
+ $instance3 = $generateInstanceId->invokeArgs($db3, array($config3));
+
+ // Assert that $instance1 and $instance2 have different instance ids
+ $this->assertNotEquals(
+ $instance1,
+ $instance2,
+ "Database instances should be different, but returned the same id"
+ );
+ // Assert that $instance1 and $instance3 have identical instance ids
+ $this->assertEquals(
+ $instance1,
+ $instance3,
+ "Database instances should have the same id, but returned different id"
+ );
+
+ // Assert that $db1 and $db2 are different instances
+ $this->assertNotEquals(
+ spl_object_hash($db1),
+ spl_object_hash($db2),
+ "Database instances should be different, but returned the same spl_object_hash"
+ );
+ // Assert that $db1 and $db3 are identical instances
+ $this->assertEquals(
+ spl_object_hash($db1),
+ spl_object_hash($db3),
+ "Database instances should be the same, but returned different spl_object_hash"
+ );
+ }
+
+
+ /**
+ * @covers SimpleSAML\Database::getInstance
+ * @covers SimpleSAML\Database::generateInstanceId
+ * @covers SimpleSAML\Database::__construct
+ * @covers SimpleSAML\Database::connect
+ * @covers SimpleSAML\Database::getSlave
+ * @test
+ */
+ public function slaves()
+ {
+ $getSlave = self::getMethod('getSlave');
+
+ $master = spl_object_hash(PHPUnit_Framework_Assert::readAttribute($this->db, 'dbMaster'));
+ $slave = spl_object_hash($getSlave->invokeArgs($this->db, array()));
+
+ $this->assertTrue(($master == $slave), "getSlave should have returned the master database object");
+
+ $config = array(
+ 'database.dsn' => 'sqlite::memory:',
+ 'database.username' => null,
+ 'database.password' => null,
+ 'database.prefix' => 'phpunit_',
+ 'database.persistent' => true,
+ 'database.slaves' => array(
+ array(
+ 'dsn' => 'sqlite::memory:',
+ 'username' => null,
+ 'password' => null,
+ ),
+ ),
+ );
+
+ $sspConfiguration = new SimpleSAML_Configuration($config, "test/SimpleSAML/DatabaseTest.php");
+ $msdb = SimpleSAML\Database::getInstance($sspConfiguration);
+
+ $slaves = PHPUnit_Framework_Assert::readAttribute($msdb, 'dbSlaves');
+ $gotSlave = spl_object_hash($getSlave->invokeArgs($msdb, array()));
+
+ $this->assertEquals(
+ spl_object_hash($slaves[0]),
+ $gotSlave,
+ "getSlave should have returned a slave database object"
+ );
+ }
+
+
+ /**
+ * @covers SimpleSAML\Database::applyPrefix
+ * @test
+ */
+ public function prefix()
+ {
+ $prefix = $this->config->getString('database.prefix');
+ $table = "saml20_idp_hosted";
+ $pftable = $this->db->applyPrefix($table);
+
+ $this->assertEquals($prefix.$table, $pftable, "Did not properly apply the table prefix");
+ }
+
+
+ /**
+ * @covers SimpleSAML\Database::write
+ * @covers SimpleSAML\Database::read
+ * @covers SimpleSAML\Database::exec
+ * @covers SimpleSAML\Database::query
+ * @test
+ */
+ public function querying()
+ {
+ $table = $this->db->applyPrefix("sspdbt");
+ $this->assertEquals($this->config->getString('database.prefix')."sspdbt", $table);
+
+ $this->db->write(
+ "CREATE TABLE IF NOT EXISTS $table (ssp_key INT(16) NOT NULL, ssp_value TEXT NOT NULL)",
+ false
+ );
+
+ $query1 = $this->db->read("SELECT * FROM $table");
+ $this->assertEquals(0, $query1->fetch(), "Table $table is not empty when it should be.");
+
+ $ssp_key = time();
+ $ssp_value = md5(rand(0, 10000));
+ $stmt = $this->db->write(
+ "INSERT INTO $table (ssp_key, ssp_value) VALUES (:ssp_key, :ssp_value)",
+ array('ssp_key' => array($ssp_key, PDO::PARAM_INT), 'ssp_value' => $ssp_value)
+ );
+ $this->assertEquals(1, $stmt->rowCount(), "Could not insert data into $table.");
+
+ $query2 = $this->db->read("SELECT * FROM $table WHERE ssp_key = :ssp_key", array('ssp_key' => $ssp_key));
+ $data = $query2->fetch();
+ $this->assertEquals($data['ssp_value'], $ssp_value, "Inserted data doesn't match what is in the database");
+ }
+
+
+ /**
+ * @covers SimpleSAML\Database::read
+ * @covers SimpleSAML\Database::query
+ * @expectedException Exception
+ * @test
+ */
+ public function readFailure()
+ {
+ $table = $this->db->applyPrefix("sspdbt");
+ $this->assertEquals($this->config->getString('database.prefix')."sspdbt", $table);
+
+ $this->db->read("SELECT * FROM $table");
+ }
+
+
+ /**
+ * @covers SimpleSAML\Database::write
+ * @covers SimpleSAML\Database::exec
+ * @expectedException Exception
+ * @test
+ */
+ public function noSuchTable()
+ {
+ $this->db->write("DROP TABLE phpunit_nonexistent", false);
+ }
+
+
+ public function tearDown()
+ {
+ $table = $this->db->applyPrefix("sspdbt");
+ $this->db->write("DROP TABLE IF EXISTS $table", false);
+
+ unset($this->config);
+ unset($this->db);
+ }
+}
diff --git a/tests/lib/SimpleSAML/Utils/ConfigTest.php b/tests/lib/SimpleSAML/Utils/ConfigTest.php
index 556e5d0..2fdc080 100644
--- a/tests/lib/SimpleSAML/Utils/ConfigTest.php
+++ b/tests/lib/SimpleSAML/Utils/ConfigTest.php
@@ -44,6 +44,6 @@ class Utils_ConfigTest extends PHPUnit_Framework_TestCase
'Given: "' . $invalidDir . '"'
);
- $configDir = \SimpleSAML\Utils\Config::getConfigDir();
+ \SimpleSAML\Utils\Config::getConfigDir();
}
}
diff --git a/tests/lib/SimpleSAML/Utils/CryptoTest.php b/tests/lib/SimpleSAML/Utils/CryptoTest.php
new file mode 100644
index 0000000..e2f0312
--- /dev/null
+++ b/tests/lib/SimpleSAML/Utils/CryptoTest.php
@@ -0,0 +1,78 @@
+<?php
+
+
+/**
+ * Tests for SimpleSAML\Utils\Crypto.
+ */
+class CryptoTest extends PHPUnit_Framework_TestCase
+{
+
+ /**
+ * Test invalid input provided to the aesDecrypt() method.
+ *
+ * @expectedException InvalidArgumentException
+ */
+ public function testAesDecryptBadInput()
+ {
+ $m = new ReflectionMethod('\SimpleSAML\Utils\Crypto', '_aesDecrypt');
+ $m->setAccessible(true);
+
+ $m->invokeArgs(null, array(array(), 'SECRET'));
+ }
+
+
+ /**
+ * Test invalid input provided to the aesEncrypt() method.
+ *
+ * @expectedException InvalidArgumentException
+ */
+ public function testAesEncryptBadInput()
+ {
+ $m = new ReflectionMethod('\SimpleSAML\Utils\Crypto', '_aesEncrypt');
+ $m->setAccessible(true);
+
+ $m->invokeArgs(null, array(array(), 'SECRET'));
+ }
+
+
+ /**
+ * Test that aesDecrypt() works properly, being able to decrypt some previously known (and correct)
+ * ciphertext.
+ */
+ public function testAesDecrypt()
+ {
+ if (!extension_loaded('openssl')) {
+ $this->setExpectedException('\SimpleSAML_Error_Exception');
+ }
+
+ $secret = 'SUPER_SECRET_SALT';
+ $m = new ReflectionMethod('\SimpleSAML\Utils\Crypto', '_aesDecrypt');
+ $m->setAccessible(true);
+
+ $plaintext = 'SUPER_SECRET_TEXT';
+ $ciphertext = 'NmRkODJlZGE2OTA3YTYwMm9En+KAReUk2z7Xi/b3c39kF/c1n6Vdj/zNARQt+UHU';
+ $this->assertEquals($plaintext, $m->invokeArgs(null, array(base64_decode($ciphertext), $secret)));
+ }
+
+
+ /**
+ * Test that aesEncrypt() produces ciphertexts that aesDecrypt() can decrypt.
+ */
+ public function testAesEncrypt()
+ {
+ if (!extension_loaded('openssl')) {
+ $this->setExpectedException('\SimpleSAML_Error_Exception');
+ }
+
+ $secret = 'SUPER_SECRET_SALT';
+ $e = new ReflectionMethod('\SimpleSAML\Utils\Crypto', '_aesEncrypt');
+ $d = new ReflectionMethod('\SimpleSAML\Utils\Crypto', '_aesDecrypt');
+ $e->setAccessible(true);
+ $d->setAccessible(true);
+
+ $original_plaintext = 'SUPER_SECRET_TEXT';
+ $ciphertext = $e->invokeArgs(null, array($original_plaintext, $secret));
+ $decrypted_plaintext = $d->invokeArgs(null, array($ciphertext, $secret));
+ $this->assertEquals($original_plaintext, $decrypted_plaintext);
+ }
+}
diff --git a/tests/modules/core/lib/Auth/Process/PHPTest.php b/tests/modules/core/lib/Auth/Process/PHPTest.php
new file mode 100644
index 0000000..66c5523
--- /dev/null
+++ b/tests/modules/core/lib/Auth/Process/PHPTest.php
@@ -0,0 +1,103 @@
+<?php
+
+
+/**
+ * Test for the core:PHP filter.
+ */
+class Test_Core_Auth_Process_PHP extends PHPUnit_Framework_TestCase
+{
+
+ /**
+ * Helper function to run the filter with a given configuration.
+ *
+ * @param array $config The filter configuration.
+ * @param array $request The request state.
+ *
+ * @return array The state array after processing.
+ */
+ private static function processFilter(array $config, array $request)
+ {
+ $filter = new sspmod_core_Auth_Process_PHP($config, null);
+ @$filter->process($request);
+ return $request;
+ }
+
+
+ /**
+ * Test the configuration of the filter.
+ *
+ * @expectedException SimpleSAML_Error_Exception
+ */
+ public function testInvalidConfiguration()
+ {
+ $config = array();
+ new sspmod_core_Auth_Process_PHP($config, null);
+ }
+
+
+
+ /**
+ * Check that defining a function works as expected.
+ */
+ public function testFunctionDefined()
+ {
+ $config = array(
+ 'function' => function (&$attributes) {
+ $attributes['key'] = 'value';
+ },
+ );
+ $request = array('Attributes' => array());
+ $expected = array(
+ 'Attributes' => array(
+ 'key' => 'value',
+ ),
+ );
+
+ $this->assertEquals($expected, $this->processFilter($config, $request));
+ }
+
+
+ /**
+ * Check that defining the code works as expected.
+ */
+ public function testCodeDefined()
+ {
+ $config = array(
+ 'code' => '
+ $attributes["key"] = "value";
+ ',
+ );
+ $request = array('Attributes' => array());
+ $expected = array(
+ 'Attributes' => array(
+ 'key' => 'value',
+ ),
+ );
+
+ $this->assertEquals($expected, $this->processFilter($config, $request));
+ }
+
+
+ /**
+ * Check that when both the function and code are defined, only the function is executed.
+ */
+ public function testOptionsPrecedence()
+ {
+ $config = array(
+ 'function' => function (&$attributes) {
+ $attributes['who'] = 'function';
+ },
+ 'code' => '
+ $attributes["who"] = "code";
+ ',
+ );
+ $request = array('Attributes' => array());
+ $expected = array(
+ 'Attributes' => array(
+ 'who' => 'function',
+ ),
+ );
+
+ $this->assertEquals($expected, $this->processFilter($config, $request));
+ }
+}
diff --git a/www/_include.php b/www/_include.php
index 8b0ee6e..7b65e92 100644
--- a/www/_include.php
+++ b/www/_include.php
@@ -1,111 +1,96 @@
<?php
-/* Remove magic quotes. */
-if(get_magic_quotes_gpc()) {
- foreach(array('_GET', '_POST', '_COOKIE', '_REQUEST') as $a) {
- if (isset($$a) && is_array($$a)) {
- foreach($$a as &$v) {
- /* We don't use array-parameters anywhere.
- * Ignore any that may appear.
- */
- if(is_array($v)) {
- continue;
- }
- /* Unescape the string. */
- $v = stripslashes($v);
- }
- }
- }
-}
-if (get_magic_quotes_runtime()) {
- set_magic_quotes_runtime(FALSE);
+/**
+ * Disable magic quotes if they are enabled.
+ */
+function removeMagicQuotes()
+{
+ if (get_magic_quotes_gpc()) {
+ foreach (array('_GET', '_POST', '_COOKIE', '_REQUEST') as $a) {
+ if (isset($$a) && is_array($$a)) {
+ foreach ($$a as &$v) {
+ // we don't use array-parameters anywhere. Ignore any that may appear
+ if (is_array($v)) {
+ continue;
+ }
+ // unescape the string
+ $v = stripslashes($v);
+ }
+ }
+ }
+ }
+ if (get_magic_quotes_runtime()) {
+ set_magic_quotes_runtime(false);
+ }
}
-
-/* Initialize the autoloader. */
-require_once(dirname(dirname(__FILE__)) . '/lib/_autoload.php');
-
-/* Enable assertion handler for all pages. */
-SimpleSAML_Error_Assertion::installHandler();
-
-/* Show error page on unhandled exceptions. */
-function SimpleSAML_exception_handler(Exception $exception) {
-
- if ($exception instanceof SimpleSAML_Error_Error) {
- $exception->show();
- } else {
- $e = new SimpleSAML_Error_Error('UNHANDLEDEXCEPTION', $exception);
- $e->show();
- }
+if (version_compare(PHP_VERSION, '5.4.0', '<')) {
+ removeMagicQuotes();
}
-set_exception_handler('SimpleSAML_exception_handler');
-
-/* Log full backtrace on errors and warnings. */
-function SimpleSAML_error_handler($errno, $errstr, $errfile = NULL, $errline = 0, $errcontext = NULL) {
- if (!class_exists('SimpleSAML_Logger')) {
- /* We are probably logging a deprecation-warning during parsing.
- * Unfortunately, the autoloader is disabled at this point,
- * so we should stop here.
- *
- * See PHP bug: https://bugs.php.net/bug.php?id=47987
- */
- return FALSE;
- }
+// initialize the autoloader
+require_once(dirname(dirname(__FILE__)).'/lib/_autoload.php');
+// enable assertion handler for all pages
+SimpleSAML_Error_Assertion::installHandler();
- if ($errno & SimpleSAML_Utilities::$logMask || ! ($errno & error_reporting() )) {
- /* Masked error. */
- return FALSE;
- }
-
- static $limit = 5;
- $limit -= 1;
- if ($limit < 0) {
- /* We have reached the limit in the number of backtraces we will log. */
- return FALSE;
- }
-
- /* Show an error with a full backtrace. */
- $e = new SimpleSAML_Error_Exception('Error ' . $errno . ' - ' . $errstr);
- $e->logError();
-
- /* Resume normal error processing. */
- return FALSE;
+// show error page on unhandled exceptions
+function SimpleSAML_exception_handler(Exception $exception)
+{
+ if ($exception instanceof SimpleSAML_Error_Error) {
+ $exception->show();
+ } else {
+ $e = new SimpleSAML_Error_Error('UNHANDLEDEXCEPTION', $exception);
+ $e->show();
+ }
}
-set_error_handler('SimpleSAML_error_handler');
-/**
- * Class which should print a warning every time a reference to $SIMPLESAML_INCPREFIX is made.
- */
-class SimpleSAML_IncPrefixWarn {
+set_exception_handler('SimpleSAML_exception_handler');
- /**
- * Print a warning, as a call to this function means that $SIMPLESAML_INCPREFIX is referenced.
- *
- * @return A blank string.
- */
- function __toString() {
- $backtrace = debug_backtrace();
- $where = $backtrace[0]['file'] . ':' . $backtrace[0]['line'];
- error_log('Deprecated $SIMPLESAML_INCPREFIX still in use at ' . $where .
- '. The simpleSAMLphp library now uses an autoloader.');
- return '';
- }
+// log full backtrace on errors and warnings
+function SimpleSAML_error_handler($errno, $errstr, $errfile = null, $errline = 0, $errcontext = null)
+{
+ if (!class_exists('SimpleSAML_Logger')) {
+ /* We are probably logging a deprecation-warning during parsing. Unfortunately, the autoloader is disabled at
+ * this point, so we should stop here.
+ *
+ * See PHP bug: https://bugs.php.net/bug.php?id=47987
+ */
+ return false;
+ }
+
+ if ($errno & SimpleSAML_Utilities::$logMask || !($errno & error_reporting())) {
+ // masked error
+ return false;
+ }
+
+ static $limit = 5;
+ $limit -= 1;
+ if ($limit < 0) {
+ // we have reached the limit in the number of backtraces we will log
+ return false;
+ }
+
+ // show an error with a full backtrace
+ $e = new SimpleSAML_Error_Exception('Error '.$errno.' - '.$errstr);
+ $e->logError();
+
+ // resume normal error processing
+ return false;
}
-/* Set the $SIMPLESAML_INCPREFIX to a reference to the class. */
-$SIMPLESAML_INCPREFIX = new SimpleSAML_IncPrefixWarn();
+set_error_handler('SimpleSAML_error_handler');
$configdir = SimpleSAML\Utils\Config::getConfigDir();
-if (!file_exists($configdir . '/config.php')) {
- header('Content-Type: text/plain');
- echo("You have not yet created the simpleSAMLphp configuration files.\n");
- echo("See: https://simplesamlphp.org/docs/devel/simplesamlphp-install-repo\n");
- exit(1);
+if (!file_exists($configdir.'/config.php')) {
+ header('Content-Type: text/plain');
+ echo("You have not yet created the simpleSAMLphp configuration files.\n");
+ echo("See: https://simplesamlphp.org/docs/devel/simplesamlphp-install-repo\n");
+ exit(1);
}
-/* Set the timezone. */
+// set the timezone
SimpleSAML\Utils\Time::initTimezone();
-/* Disable XML external entity loading explicitly. */
+
+// disable XML external entity loading explicitly
libxml_disable_entity_loader();
diff --git a/www/admin/hostnames.php b/www/admin/hostnames.php
index 17e74e4..da7b09b 100644
--- a/www/admin/hostnames.php
+++ b/www/admin/hostnames.php
@@ -24,11 +24,11 @@ $attributes['Utilities_getSelfHostWithPath()'] = array(\SimpleSAML\Utils\HTTP::g
$attributes['Utilities_getFirstPathElement()'] = array(\SimpleSAML\Utils\HTTP::getFirstPathElement());
$attributes['Utilities_selfURL()'] = array(\SimpleSAML\Utils\HTTP::getSelfURL());
-$et = new SimpleSAML_XHTML_Template($config, 'hostnames.php');
+$template = new SimpleSAML_XHTML_Template($config, 'hostnames.php');
-$et->data['remaining'] = $session->getAuthData('admin', 'Expire') - time();
-$et->data['attributes'] = $attributes;
-$et->data['valid'] = 'na';
-$et->data['logout'] = null;
+$template->data['remaining'] = $session->getAuthData('admin', 'Expire') - time();
+$template->data['attributes'] = $attributes;
+$template->data['valid'] = 'na';
+$template->data['logout'] = null;
-$et->show();
+$template->show();
diff --git a/www/authmemcookie.php b/www/authmemcookie.php
index b6a1f92..02aedc4 100644
--- a/www/authmemcookie.php
+++ b/www/authmemcookie.php
@@ -7,93 +7,91 @@
* The configuration for this script is stored in config/authmemcookie.php.
*
* The file extra/auth_memcookie.conf contains an example of how Auth Memcookie can be configured
- * to use simpleSAMLphp.
+ * to use SimpleSAMLphp.
*/
require_once('_include.php');
try {
- /* Load simpleSAMLphp configuration. */
- $globalConfig = SimpleSAML_Configuration::getInstance();
-
- /* Check if this module is enabled. */
- if(!$globalConfig->getBoolean('enable.authmemcookie', FALSE)) {
- throw new SimpleSAML_Error_Error('NOACCESS');
- }
-
- /* Load Auth MemCookie configuration. */
- $amc = SimpleSAML_AuthMemCookie::getInstance();
-
- $sourceId = $amc->getAuthSource();
- $s = new SimpleSAML_Auth_Simple($sourceId);
-
- /* Check if the user is authorized. We attempt to authenticate the user if not. */
- $s->requireAuth();
-
- /* Generate session id and save it in a cookie. */
- $sessionID = SimpleSAML\Utils\Random::generateID();
-
- $cookieName = $amc->getCookieName();
-
- $sessionHandler = SimpleSAML_SessionHandler::getSessionHandler();
- $sessionHandler->setCookie($cookieName, $sessionID);
-
-
- /* Generate the authentication information. */
-
- $attributes = $s->getAttributes();
-
- $authData = array();
-
- /* Username. */
- $usernameAttr = $amc->getUsernameAttr();
- if(!array_key_exists($usernameAttr, $attributes)) {
- throw new Exception('The user doesn\'t have an attribute named \'' . $usernameAttr .
- '\'. This attribute is expected to contain the username.');
- }
- $authData['UserName'] = $attributes[$usernameAttr];
-
- /* Groups. */
- $groupsAttr = $amc->getGroupsAttr();
- if($groupsAttr !== NULL) {
- if(!array_key_exists($groupsAttr, $attributes)) {
- throw new Exception('The user doesn\'t have an attribute named \'' . $groupsAttr .
- '\'. This attribute is expected to contain the groups the user is a member of.');
- }
- $authData['Groups'] = $attributes[$groupsAttr];
- } else {
- $authData['Groups'] = array();
- }
-
- $authData['RemoteIP'] = $_SERVER['REMOTE_ADDR'];
-
- foreach($attributes as $n => $v) {
- $authData['ATTR_' . $n] = $v;
- }
-
-
- /* Store the authentication data in the memcache server. */
-
- $data = '';
- foreach($authData as $n => $v) {
- if(is_array($v)) {
- $v = implode(':', $v);
- }
-
- $data .= $n . '=' . $v . "\r\n";
- }
-
-
- $memcache = $amc->getMemcache();
- $expirationTime = $s->getAuthData('Expire');
- $memcache->set($sessionID, $data, 0, $expirationTime);
-
- /* Register logout handler. */
- $session = SimpleSAML_Session::getSessionFromRequest();
- $session->registerLogoutHandler($sourceId, 'SimpleSAML_AuthMemCookie', 'logoutHandler');
-
- /* Redirect the user back to this page to signal that the login is completed. */
- \SimpleSAML\Utils\HTTP::redirectTrustedURL(\SimpleSAML\Utils\HTTP::getSelfURL());
-} catch(Exception $e) {
- throw new SimpleSAML_Error_Error('CONFIG', $e);
+ // load SimpleSAMLphp configuration
+ $globalConfig = SimpleSAML_Configuration::getInstance();
+
+ // check if this module is enabled
+ if (!$globalConfig->getBoolean('enable.authmemcookie', false)) {
+ throw new SimpleSAML_Error_Error('NOACCESS');
+ }
+
+ // load Auth MemCookie configuration
+ $amc = SimpleSAML_AuthMemCookie::getInstance();
+
+ $sourceId = $amc->getAuthSource();
+ $s = new SimpleSAML_Auth_Simple($sourceId);
+
+ // check if the user is authorized. We attempt to authenticate the user if not
+ $s->requireAuth();
+
+ // generate session id and save it in a cookie
+ $sessionID = SimpleSAML\Utils\Random::generateID();
+
+ $cookieName = $amc->getCookieName();
+
+ $sessionHandler = SimpleSAML_SessionHandler::getSessionHandler();
+ $sessionHandler->setCookie($cookieName, $sessionID);
+
+ // generate the authentication information
+ $attributes = $s->getAttributes();
+
+ $authData = array();
+
+ // username
+ $usernameAttr = $amc->getUsernameAttr();
+ if (!array_key_exists($usernameAttr, $attributes)) {
+ throw new Exception(
+ "The user doesn't have an attribute named '".$usernameAttr.
+ "'. This attribute is expected to contain the username."
+ );
+ }
+ $authData['UserName'] = $attributes[$usernameAttr];
+
+ // groups
+ $groupsAttr = $amc->getGroupsAttr();
+ if ($groupsAttr !== null) {
+ if (!array_key_exists($groupsAttr, $attributes)) {
+ throw new Exception(
+ "The user doesn't have an attribute named '".$groupsAttr.
+ "'. This attribute is expected to contain the groups the user is a member of."
+ );
+ }
+ $authData['Groups'] = $attributes[$groupsAttr];
+ } else {
+ $authData['Groups'] = array();
+ }
+
+ $authData['RemoteIP'] = $_SERVER['REMOTE_ADDR'];
+
+ foreach ($attributes as $n => $v) {
+ $authData['ATTR_'.$n] = $v;
+ }
+
+ // store the authentication data in the memcache server
+ $data = '';
+ foreach ($authData as $n => $v) {
+ if (is_array($v)) {
+ $v = implode(':', $v);
+ }
+ $data .= $n.'='.$v."\r\n";
+ }
+
+ $memcache = $amc->getMemcache();
+ $expirationTime = $s->getAuthData('Expire');
+ $memcache->set($sessionID, $data, 0, $expirationTime);
+
+ // register logout handler
+ $session = SimpleSAML_Session::getSessionFromRequest();
+ $session->registerLogoutHandler($sourceId, 'SimpleSAML_AuthMemCookie', 'logoutHandler');
+
+ // redirect the user back to this page to signal that the login is completed
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL(\SimpleSAML\Utils\HTTP::getSelfURL());
+} catch (Exception $e) {
+ throw new SimpleSAML_Error_Error('CONFIG', $e);
}
diff --git a/www/errorreport.php b/www/errorreport.php
index 042f9a6..723b3e7 100644
--- a/www/errorreport.php
+++ b/www/errorreport.php
@@ -4,99 +4,117 @@ require_once('_include.php');
$config = SimpleSAML_Configuration::getInstance();
-/* This page will redirect to itself after processing a POST request and sending the email. */
-if($_SERVER['REQUEST_METHOD'] !== 'POST') {
- /* The message has been sent. Show error report page. */
+// this page will redirect to itself after processing a POST request and sending the email
+if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
+ // the message has been sent. Show error report page
- $t = new SimpleSAML_XHTML_Template($config, 'errorreport.php', 'errors');
- $t->show();
- exit;
+ $t = new SimpleSAML_XHTML_Template($config, 'errorreport.php', 'errors');
+ $t->show();
+ exit;
}
-$reportId = (string)$_REQUEST['reportId'];
-$email = (string)$_REQUEST['email'];
-$text = htmlspecialchars((string)$_REQUEST['text']);
+$reportId = (string) $_REQUEST['reportId'];
+$email = (string) $_REQUEST['email'];
+$text = htmlspecialchars((string) $_REQUEST['text']);
+$data = null;
try {
- $session = SimpleSAML_Session::getSessionFromRequest();
- $data = $session->getData('core:errorreport', $reportId);
+ $session = SimpleSAML_Session::getSessionFromRequest();
+ $data = $session->getData('core:errorreport', $reportId);
} catch (Exception $e) {
- SimpleSAML_Logger::error('Error loading error report data: ' . var_export($e->getMessage(), TRUE));
+ SimpleSAML_Logger::error('Error loading error report data: '.var_export($e->getMessage(), true));
}
-if ($data === NULL) {
- $data = array(
- 'exceptionMsg' => 'not set',
- 'exceptionTrace' => 'not set',
- 'reportId' => $reportId,
- 'trackId' => 'not set',
- 'url' => 'not set',
- 'version' => $config->getVersion(),
- 'referer' => 'not set',
- );
-
- if (isset($session)) {
- $data['trackId'] = $session->getTrackID();
- }
+if ($data === null) {
+ $data = array(
+ 'exceptionMsg' => 'not set',
+ 'exceptionTrace' => 'not set',
+ 'reportId' => $reportId,
+ 'trackId' => 'not set',
+ 'url' => 'not set',
+ 'version' => $config->getVersion(),
+ 'referer' => 'not set',
+ );
+
+ if (isset($session)) {
+ $data['trackId'] = $session->getTrackID();
+ }
}
foreach ($data as $k => $v) {
- $data[$k] = htmlspecialchars($v);
+ $data[$k] = htmlspecialchars($v);
}
-/* Build the email message. */
-
-$message = '<h1>SimpleSAMLphp Error Report</h1>
+// build the email message
+$message = <<<MESSAGE
+<h1>SimpleSAMLphp Error Report</h1>
<p>Message from user:</p>
-<div class="box" style="background: yellow; color: #888; border: 1px solid #999900; padding: .4em; margin: .5em">' . htmlspecialchars($text) . '</div>
+<div class="box" style="background: yellow; color: #888; border: 1px solid #999900; padding: .4em; margin: .5em">
+ %s
+</div>
-<p>Exception: <strong>' . $data['exceptionMsg'] . '</strong></p>
-<pre>' . $data['exceptionTrace'] . '</pre>
+<p>Exception: <strong>%s</strong></p>
+<pre>%s</pre>
<p>URL:</p>
-<pre><a href="' . $data['url'] . '">' . $data['url'] . '</a></pre>
+<pre><a href="%s">%s</a></pre>
<p>Host:</p>
-<pre>' . htmlspecialchars(php_uname('n')) . '</pre>
+<pre>%s</pre>
<p>Directory:</p>
-<pre>' . dirname(dirname(__FILE__)) . '</pre>
+<pre>%s</pre>
<p>Track ID:</p>
-<pre>' . $data['trackId'] . '</pre>
+<pre>%s</pre>
-<p>Version: <tt>' . $data['version'] . '</tt></p>
+<p>Version: <tt>%s</tt></p>
-<p>Report ID: <tt>' . $data['reportId'] . '</tt></p>
+<p>Report ID: <tt>%s</tt></p>
-<p>Referer: <tt>' . $data['referer'] . '</tt></p>
+<p>Referer: <tt>%s</tt></p>
<hr />
-<div class="footer">This message was sent using simpleSAMLphp. Visit the <a href="http://simplesamlphp.org/">simpleSAMLphp homepage</a>.</div>
-
-';
-
-
-/* Add the email address of the submitter as the Reply-To address. */
+<div class="footer">
+ This message was sent using SimpleSAMLphp. Visit the <a href="http://simplesamlphp.org/">SimpleSAMLphp homepage</a>.
+</div>
+MESSAGE;
+$message = sprintf(
+ $message,
+ htmlspecialchars($text),
+ $data['exceptionMsg'],
+ $data['exceptionTrace'],
+ $data['url'],
+ $data['url'],
+ htmlspecialchars(php_uname('n')),
+ dirname(dirname(__FILE__)),
+ $data['trackId'],
+ $data['version'],
+ $data['reportId'],
+ $data['referer']
+);
+
+// add the email address of the submitter as the Reply-To address
$email = trim($email);
-/* Check that it looks like a valid email address. */
-if (!preg_match('/\s/', $email) && strpos($email, '@') !== FALSE) {
- $replyto = $email;
- $from = $email;
+
+// check that it looks like a valid email address
+if (!preg_match('/\s/', $email) && strpos($email, '@') !== false) {
+ $replyto = $email;
+ $from = $email;
} else {
- $replyto = NULL;
- $from = 'no-reply@simplesamlphp.org';
+ $replyto = null;
+ $from = 'no-reply@simplesamlphp.org';
}
-/* Send the email. */
+// send the email
$toAddress = $config->getString('technicalcontact_email', 'na@example.org');
-if ($config->getBoolean('errorreporting', TRUE) && $toAddress !== 'na@example.org') {
- $email = new SimpleSAML_XHTML_EMail($toAddress, 'simpleSAMLphp error report', $from);
- $email->setBody($message);
- $email->send();
- SimpleSAML_Logger::error('Report with id ' . $reportId . ' sent to <' . $toAddress . '>.');
+if ($config->getBoolean('errorreporting', true) && $toAddress !== 'na@example.org') {
+ $email = new SimpleSAML_XHTML_EMail($toAddress, 'SimpleSAMLphp error report', $from);
+ $email->setBody($message);
+ $email->send();
+ SimpleSAML_Logger::error('Report with id '.$reportId.' sent to <'.$toAddress.'>.');
}
-/* Redirect the user back to this page to clear the POST request. */
+// redirect the user back to this page to clear the POST request
\SimpleSAML\Utils\HTTP::redirectTrustedURL(\SimpleSAML\Utils\HTTP::getSelfURLNoQuery());
diff --git a/www/logout.php b/www/logout.php
index c361b29..220449a 100644
--- a/www/logout.php
+++ b/www/logout.php
@@ -4,17 +4,17 @@ require_once('_include.php');
$config = SimpleSAML_Configuration::getInstance();
-if(array_key_exists('link_href', $_REQUEST)) {
- $link = (string)$_REQUEST['link_href'];
- $link = \SimpleSAML\Utils\HTTP::normalizeURL($link);
+if (array_key_exists('link_href', $_REQUEST)) {
+ $link = (string) $_REQUEST['link_href'];
+ $link = \SimpleSAML\Utils\HTTP::normalizeURL($link);
} else {
- $link = 'index.php';
+ $link = 'index.php';
}
-if(array_key_exists('link_text', $_REQUEST)) {
- $text = $_REQUEST['link_text'];
+if (array_key_exists('link_text', $_REQUEST)) {
+ $text = $_REQUEST['link_text'];
} else {
- $text = '{logout:default_link_text}';
+ $text = '{logout:default_link_text}';
}
$t = new SimpleSAML_XHTML_Template($config, 'logout.php');
@@ -22,5 +22,3 @@ $t->data['link'] = $link;
$t->data['text'] = $text;
$t->show();
exit();
-
-?> \ No newline at end of file
diff --git a/www/module.php b/www/module.php
index 406191f..66baa3d 100644
--- a/www/module.php
+++ b/www/module.php
@@ -6,179 +6,167 @@
* the RequestHandler in the module.
*
* @author Olav Morken, UNINETT AS.
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
require_once('_include.php');
-/* Index pages - filenames to attempt when accessing directories. */
+// index pages - file names to attempt when accessing directories
$indexFiles = array('index.php', 'index.html', 'index.htm', 'index.txt');
-/* MIME types - key is file extension, value is MIME type. */
+// MIME types - key is file extension, value is MIME type
$mimeTypes = array(
- 'bmp' => 'image/x-ms-bmp',
- 'css' => 'text/css',
- 'gif' => 'image/gif',
- 'htm' => 'text/html',
- 'html' => 'text/html',
- 'shtml' => 'text/html',
- 'ico' => 'image/vnd.microsoft.icon',
- 'jpe' => 'image/jpeg',
- 'jpeg' => 'image/jpeg',
- 'jpg' => 'image/jpeg',
- 'js' => 'text/javascript',
- 'pdf' => 'application/pdf',
- 'png' => 'image/png',
- 'svg' => 'image/svg+xml',
- 'svgz' => 'image/svg+xml',
- 'swf' => 'application/x-shockwave-flash',
- 'swfl' => 'application/x-shockwave-flash',
- 'txt' => 'text/plain',
- 'xht' => 'application/xhtml+xml',
- 'xhtml' => 'application/xhtml+xml',
- );
+ 'bmp' => 'image/x-ms-bmp',
+ 'css' => 'text/css',
+ 'gif' => 'image/gif',
+ 'htm' => 'text/html',
+ 'html' => 'text/html',
+ 'shtml' => 'text/html',
+ 'ico' => 'image/vnd.microsoft.icon',
+ 'jpe' => 'image/jpeg',
+ 'jpeg' => 'image/jpeg',
+ 'jpg' => 'image/jpeg',
+ 'js' => 'text/javascript',
+ 'pdf' => 'application/pdf',
+ 'png' => 'image/png',
+ 'svg' => 'image/svg+xml',
+ 'svgz' => 'image/svg+xml',
+ 'swf' => 'application/x-shockwave-flash',
+ 'swfl' => 'application/x-shockwave-flash',
+ 'txt' => 'text/plain',
+ 'xht' => 'application/xhtml+xml',
+ 'xhtml' => 'application/xhtml+xml',
+);
try {
- if (empty($_SERVER['PATH_INFO'])) {
- throw new SimpleSAML_Error_NotFound('No PATH_INFO to module.php');
- }
-
- $url = $_SERVER['PATH_INFO'];
- assert('substr($url, 0, 1) === "/"');
-
- /* Clear the PATH_INFO option, so that a script can detect whether it is called
- * with anything following the '.php'-ending.
- */
- unset($_SERVER['PATH_INFO']);
-
- $modEnd = strpos($url, '/', 1);
- if ($modEnd === FALSE) {
- /* The path must always be on the form /module/. */
- throw new SimpleSAML_Error_NotFound('The URL must at least contain a module name followed by a slash.');
- }
-
- $module = substr($url, 1, $modEnd - 1);
- $url = substr($url, $modEnd + 1);
- if ($url === FALSE) {
- $url = '';
- }
-
- if (!SimpleSAML_Module::isModuleEnabled($module)) {
- throw new SimpleSAML_Error_NotFound('The module \'' . $module .
- '\' was either not found, or wasn\'t enabled.');
- }
-
- /* Make sure that the request isn't suspicious (contains references to current
- * directory or parent directory or anything like that. Searching for './' in the
- * URL will detect both '../' and './'. Searching for '\' will detect attempts to
- * use Windows-style paths.
- */
- if (strpos($url, '\\') !== FALSE) {
- throw new SimpleSAML_Error_BadRequest('Requested URL contained a backslash.');
- } elseif (strpos($url, './') !== FALSE) {
- throw new SimpleSAML_Error_BadRequest('Requested URL contained \'./\'.');
- }
-
- $moduleDir = SimpleSAML_Module::getModuleDir($module) . '/www/';
-
- /* Check for '.php/' in the path, the presence of which indicates that another php-script
- * should handle the request.
- */
- for ($phpPos = strpos($url, '.php/'); $phpPos !== FALSE; $phpPos = strpos($url, '.php/', $phpPos + 1)) {
-
- $newURL = substr($url, 0, $phpPos + 4);
- $param = substr($url, $phpPos + 4);
-
- if (is_file($moduleDir . $newURL)) {
- /* $newPath points to a normal file. Point execution to that file, and
- * save the remainder of the path in PATH_INFO.
- */
- $url = $newURL;
- $_SERVER['PATH_INFO'] = $param;
- break;
- }
- }
-
- $path = $moduleDir . $url;
-
- if ($path[strlen($path)-1] === '/') {
- /* Path ends with a slash - directory reference. Attempt to find index file
- * in directory.
- */
- foreach ($indexFiles as $if) {
- if (file_exists($path . $if)) {
- $path .= $if;
- break;
- }
- }
- }
-
- if (is_dir($path)) {
- /* Path is a directory - maybe no index file was found in the previous step, or
- * maybe the path didn't end with a slash. Either way, we don't do directory
- * listings.
- */
- throw new SimpleSAML_Error_NotFound('Directory listing not available.');
- }
-
- if (!file_exists($path)) {
- /* File not found. */
- SimpleSAML_Logger::info('Could not find file \'' . $path . '\'.');
- throw new SimpleSAML_Error_NotFound('The URL wasn\'t found in the module.');
- }
-
- if (preg_match('#\.php$#D', $path)) {
- /* PHP file - attempt to run it. */
- $_SERVER['SCRIPT_NAME'] .= '/' . $module . '/' . $url;
- require($path);
- exit();
- }
-
- /* Some other file type - attempt to serve it. */
-
- /* Find MIME type for file, based on extension. */
- $contentType = NULL;
- if (preg_match('#\.([^/\.]+)$#D', $path, $type)) {
- $type = strtolower($type[1]);
- if (array_key_exists($type, $mimeTypes)) {
- $contentType = $mimeTypes[$type];
- }
- }
-
- if ($contentType === NULL) {
- /* We were unable to determine the MIME type from the file extension. Fall back
- * to mime_content_type (if it exists).
- */
- if (function_exists('mime_content_type')) {
- $contentType = mime_content_type($path);
- } else {
- /* mime_content_type doesn't exist. Return a default MIME type. */
- SimpleSAML_Logger::warning('Unable to determine mime content type of file: ' . $path);
- $contentType = 'application/octet-stream';
- }
- }
-
- $contentLength = sprintf('%u', filesize($path)); /* Force filesize to an unsigned number. */
-
- header('Content-Type: ' . $contentType);
- header('Content-Length: ' . $contentLength);
- header('Cache-Control: public,max-age=86400');
- header('Expires: ' . gmdate('D, j M Y H:i:s \G\M\T', time() + 10*60));
- header('Last-Modified: ' . gmdate('D, j M Y H:i:s \G\M\T', filemtime($path)));
-
- readfile($path);
- exit();
-
-} catch(SimpleSAML_Error_Error $e) {
-
- $e->show();
-
-} catch(Exception $e) {
-
- $e = new SimpleSAML_Error_Error('UNHANDLEDEXCEPTION', $e);
- $e->show();
-
+ if (empty($_SERVER['PATH_INFO'])) {
+ throw new SimpleSAML_Error_NotFound('No PATH_INFO to module.php');
+ }
+
+ $url = $_SERVER['PATH_INFO'];
+ assert('substr($url, 0, 1) === "/"');
+
+ /* clear the PATH_INFO option, so that a script can detect whether it is called with anything following the
+ *'.php'-ending.
+ */
+ unset($_SERVER['PATH_INFO']);
+
+ $modEnd = strpos($url, '/', 1);
+ if ($modEnd === false) {
+ // the path must always be on the form /module/
+ throw new SimpleSAML_Error_NotFound('The URL must at least contain a module name followed by a slash.');
+ }
+
+ $module = substr($url, 1, $modEnd - 1);
+ $url = substr($url, $modEnd + 1);
+ if ($url === false) {
+ $url = '';
+ }
+
+ if (!SimpleSAML_Module::isModuleEnabled($module)) {
+ throw new SimpleSAML_Error_NotFound('The module \''.$module.'\' was either not found, or wasn\'t enabled.');
+ }
+
+ /* Make sure that the request isn't suspicious (contains references to current directory or parent directory or
+ * anything like that. Searching for './' in the URL will detect both '../' and './'. Searching for '\' will detect
+ * attempts to use Windows-style paths.
+ */
+ if (strpos($url, '\\') !== false) {
+ throw new SimpleSAML_Error_BadRequest('Requested URL contained a backslash.');
+ } elseif (strpos($url, './') !== false) {
+ throw new SimpleSAML_Error_BadRequest('Requested URL contained \'./\'.');
+ }
+
+ $moduleDir = SimpleSAML_Module::getModuleDir($module).'/www/';
+
+ // check for '.php/' in the path, the presence of which indicates that another php-script should handle the request
+ for ($phpPos = strpos($url, '.php/'); $phpPos !== false; $phpPos = strpos($url, '.php/', $phpPos + 1)) {
+
+ $newURL = substr($url, 0, $phpPos + 4);
+ $param = substr($url, $phpPos + 4);
+
+ if (is_file($moduleDir.$newURL)) {
+ /* $newPath points to a normal file. Point execution to that file, and
+ * save the remainder of the path in PATH_INFO.
+ */
+ $url = $newURL;
+ $_SERVER['PATH_INFO'] = $param;
+ break;
+ }
+ }
+
+ $path = $moduleDir.$url;
+
+ if ($path[strlen($path) - 1] === '/') {
+ // path ends with a slash - directory reference. Attempt to find index file in directory
+ foreach ($indexFiles as $if) {
+ if (file_exists($path.$if)) {
+ $path .= $if;
+ break;
+ }
+ }
+ }
+
+ if (is_dir($path)) {
+ /* Path is a directory - maybe no index file was found in the previous step, or maybe the path didn't end with
+ * a slash. Either way, we don't do directory listings.
+ */
+ throw new SimpleSAML_Error_NotFound('Directory listing not available.');
+ }
+
+ if (!file_exists($path)) {
+ // file not found
+ SimpleSAML_Logger::info('Could not find file \''.$path.'\'.');
+ throw new SimpleSAML_Error_NotFound('The URL wasn\'t found in the module.');
+ }
+
+ if (preg_match('#\.php$#D', $path)) {
+ // PHP file - attempt to run it
+ $_SERVER['SCRIPT_NAME'] .= '/'.$module.'/'.$url;
+ require($path);
+ exit();
+ }
+
+ // some other file type - attempt to serve it
+
+ // find MIME type for file, based on extension
+ $contentType = null;
+ if (preg_match('#\.([^/\.]+)$#D', $path, $type)) {
+ $type = strtolower($type[1]);
+ if (array_key_exists($type, $mimeTypes)) {
+ $contentType = $mimeTypes[$type];
+ }
+ }
+
+ if ($contentType === null) {
+ /* We were unable to determine the MIME type from the file extension. Fall back to mime_content_type (if it
+ * exists).
+ */
+ if (function_exists('mime_content_type')) {
+ $contentType = mime_content_type($path);
+ } else {
+ // mime_content_type doesn't exist. Return a default MIME type
+ SimpleSAML_Logger::warning('Unable to determine mime content type of file: '.$path);
+ $contentType = 'application/octet-stream';
+ }
+ }
+
+ $contentLength = sprintf('%u', filesize($path)); // force filesize to an unsigned number
+
+ header('Content-Type: '.$contentType);
+ header('Content-Length: '.$contentLength);
+ header('Cache-Control: public,max-age=86400');
+ header('Expires: '.gmdate('D, j M Y H:i:s \G\M\T', time() + 10 * 60));
+ header('Last-Modified: '.gmdate('D, j M Y H:i:s \G\M\T', filemtime($path)));
+
+ readfile($path);
+ exit();
+} catch (SimpleSAML_Error_Error $e) {
+
+ $e->show();
+} catch (Exception $e) {
+
+ $e = new SimpleSAML_Error_Error('UNHANDLEDEXCEPTION', $e);
+ $e->show();
}
-
-?> \ No newline at end of file
diff --git a/www/saml2/idp/metadata.php b/www/saml2/idp/metadata.php
index 631865a..7784a11 100644
--- a/www/saml2/idp/metadata.php
+++ b/www/saml2/idp/metadata.php
@@ -2,215 +2,221 @@
require_once('../../_include.php');
-/* Load simpleSAMLphp, configuration and metadata */
+// load SimpleSAMLphp, configuration and metadata
$config = SimpleSAML_Configuration::getInstance();
$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
-if (!$config->getBoolean('enable.saml20-idp', false))
- throw new SimpleSAML_Error_Error('NOACCESS');
+if (!$config->getBoolean('enable.saml20-idp', false)) {
+ throw new SimpleSAML_Error_Error('NOACCESS');
+}
-/* Check if valid local session exists.. */
+// check if valid local session exists
if ($config->getBoolean('admin.protectmetadata', false)) {
SimpleSAML\Utils\Auth::requireAdmin();
}
-
try {
- $idpentityid = isset($_GET['idpentityid']) ? $_GET['idpentityid'] : $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted');
- $idpmeta = $metadata->getMetaDataConfig($idpentityid, 'saml20-idp-hosted');
-
- $availableCerts = array();
-
- $keys = array();
- $certInfo = SimpleSAML\Utils\Crypto::loadPublicKey($idpmeta, FALSE, 'new_');
- if ($certInfo !== NULL) {
- $availableCerts['new_idp.crt'] = $certInfo;
- $keys[] = array(
- 'type' => 'X509Certificate',
- 'signing' => TRUE,
- 'encryption' => TRUE,
- 'X509Certificate' => $certInfo['certData'],
- );
- $hasNewCert = TRUE;
- } else {
- $hasNewCert = FALSE;
- }
-
- $certInfo = SimpleSAML\Utils\Crypto::loadPublicKey($idpmeta, TRUE);
- $availableCerts['idp.crt'] = $certInfo;
- $keys[] = array(
- 'type' => 'X509Certificate',
- 'signing' => TRUE,
- 'encryption' => ($hasNewCert ? FALSE : TRUE),
- 'X509Certificate' => $certInfo['certData'],
- );
-
- if ($idpmeta->hasValue('https.certificate')) {
- $httpsCert = SimpleSAML\Utils\Crypto::loadPublicKey($idpmeta, TRUE, 'https.');
- assert('isset($httpsCert["certData"])');
- $availableCerts['https.crt'] = $httpsCert;
- $keys[] = array(
- 'type' => 'X509Certificate',
- 'signing' => TRUE,
- 'encryption' => FALSE,
- 'X509Certificate' => $httpsCert['certData'],
- );
- }
-
- $metaArray = array(
- 'metadata-set' => 'saml20-idp-remote',
- 'entityid' => $idpentityid,
- );
-
- $ssob = $metadata->getGenerated('SingleSignOnServiceBinding', 'saml20-idp-hosted');
- $slob = $metadata->getGenerated('SingleLogoutServiceBinding', 'saml20-idp-hosted');
- $ssol = $metadata->getGenerated('SingleSignOnService', 'saml20-idp-hosted');
- $slol = $metadata->getGenerated('SingleLogoutService', 'saml20-idp-hosted');
-
- if (is_array($ssob)) {
- foreach ($ssob as $binding) {
- $metaArray['SingleSignOnService'][] = array(
- 'Binding' => $binding,
- 'Location' => $ssol,
- );
- }
- } else {
- $metaArray['SingleSignOnService'][] = array(
- 'Binding' => $ssob,
- 'Location' => $ssol,
- );
- }
-
- if (is_array($slob)) {
- foreach ($slob as $binding) {
- $metaArray['SingleLogoutService'][] = array(
- 'Binding' => $binding,
- 'Location' => $slol,
- );
- }
- } else {
- $metaArray['SingleLogoutService'][] = array(
- 'Binding' => $slob,
- 'Location' => $slol,
- );
- }
-
- if (count($keys) === 1) {
- $metaArray['certData'] = $keys[0]['X509Certificate'];
- } else {
- $metaArray['keys'] = $keys;
- }
-
- if ($idpmeta->getBoolean('saml20.sendartifact', FALSE)) {
- /* Artifact sending enabled. */
- $metaArray['ArtifactResolutionService'][] = array(
- 'index' => 0,
- 'Location' => \SimpleSAML\Utils\HTTP::getBaseURL() . 'saml2/idp/ArtifactResolutionService.php',
- 'Binding' => SAML2_Const::BINDING_SOAP,
- );
- }
-
- if ($idpmeta->getBoolean('saml20.hok.assertion', FALSE)) {
- /* Prepend HoK SSO Service endpoint. */
- array_unshift($metaArray['SingleSignOnService'], array(
- 'hoksso:ProtocolBinding' => SAML2_Const::BINDING_HTTP_REDIRECT,
- 'Binding' => SAML2_Const::BINDING_HOK_SSO,
- 'Location' => \SimpleSAML\Utils\HTTP::getBaseURL() . 'saml2/idp/SSOService.php'));
- }
-
- $metaArray['NameIDFormat'] = $idpmeta->getString('NameIDFormat', 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient');
-
- if ($idpmeta->hasValue('OrganizationName')) {
- $metaArray['OrganizationName'] = $idpmeta->getLocalizedString('OrganizationName');
- $metaArray['OrganizationDisplayName'] = $idpmeta->getLocalizedString('OrganizationDisplayName', $metaArray['OrganizationName']);
-
- if (!$idpmeta->hasValue('OrganizationURL')) {
- throw new SimpleSAML_Error_Exception('If OrganizationName is set, OrganizationURL must also be set.');
- }
- $metaArray['OrganizationURL'] = $idpmeta->getLocalizedString('OrganizationURL');
- }
-
- if ($idpmeta->hasValue('scope')) {
- $metaArray['scope'] = $idpmeta->getArray('scope');
- }
-
- if ($idpmeta->hasValue('EntityAttributes')) {
- $metaArray['EntityAttributes'] = $idpmeta->getArray('EntityAttributes');
- }
-
- if ($idpmeta->hasValue('UIInfo')) {
- $metaArray['UIInfo'] = $idpmeta->getArray('UIInfo');
- }
-
- if ($idpmeta->hasValue('DiscoHints')) {
- $metaArray['DiscoHints'] = $idpmeta->getArray('DiscoHints');
- }
-
- if ($idpmeta->hasValue('RegistrationInfo')) {
- $metaArray['RegistrationInfo'] = $idpmeta->getArray('RegistrationInfo');
- }
-
- if ($idpmeta->hasValue('validate.authnrequest')) {
- $metaArray['sign.authnrequest'] = $idpmeta->getBoolean('validate.authnrequest');
- }
-
- if ($idpmeta->hasValue('redirect.validate')) {
- $metaArray['redirect.sign'] = $idpmeta->getBoolean('redirect.validate');
- }
-
- if ($idpmeta->hasValue('contacts')) {
- $contacts = $idpmeta->getArray('contacts');
- foreach ($contacts as $contact) {
- $metaArray['contacts'][] = \SimpleSAML\Utils\Config\Metadata::getContact($contact);
- }
- }
-
- $technicalContactEmail = $config->getString('technicalcontact_email', FALSE);
- if ($technicalContactEmail && $technicalContactEmail !== 'na@example.org') {
- $techcontact['emailAddress'] = $technicalContactEmail;
- $techcontact['name'] = $config->getString('technicalcontact_name', NULL);
- $techcontact['contactType'] = 'technical';
- $metaArray['contacts'][] = \SimpleSAML\Utils\Config\Metadata::getContact($techcontact);
- }
-
- $metaBuilder = new SimpleSAML_Metadata_SAMLBuilder($idpentityid);
- $metaBuilder->addMetadataIdP20($metaArray);
- $metaBuilder->addOrganizationInfo($metaArray);
-
- $metaxml = $metaBuilder->getEntityDescriptorText();
-
- $metaflat = '$metadata[' . var_export($idpentityid, TRUE) . '] = ' . var_export($metaArray, TRUE) . ';';
-
- /* Sign the metadata if enabled. */
- $metaxml = SimpleSAML_Metadata_Signer::sign($metaxml, $idpmeta->toArray(), 'SAML 2 IdP');
-
- if (array_key_exists('output', $_GET) && $_GET['output'] == 'xhtml') {
- $defaultidp = $config->getString('default-saml20-idp', NULL);
-
- $t = new SimpleSAML_XHTML_Template($config, 'metadata.php', 'admin');
-
- $t->data['available_certs'] = $availableCerts;
- $t->data['header'] = 'saml20-idp';
- $t->data['metaurl'] = \SimpleSAML\Utils\HTTP::getSelfURLNoQuery();
- $t->data['metadata'] = htmlspecialchars($metaxml);
- $t->data['metadataflat'] = htmlspecialchars($metaflat);
- $t->data['defaultidp'] = $defaultidp;
- $t->show();
-
- } else {
-
- header('Content-Type: application/xml');
-
- echo $metaxml;
- exit(0);
-
- }
-
-
-
-} catch(Exception $exception) {
-
- throw new SimpleSAML_Error_Error('METADATA', $exception);
-
+ $idpentityid = isset($_GET['idpentityid']) ?
+ $_GET['idpentityid'] :
+ $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted');
+ $idpmeta = $metadata->getMetaDataConfig($idpentityid, 'saml20-idp-hosted');
+
+ $availableCerts = array();
+
+ $keys = array();
+ $certInfo = SimpleSAML\Utils\Crypto::loadPublicKey($idpmeta, false, 'new_');
+ if ($certInfo !== null) {
+ $availableCerts['new_idp.crt'] = $certInfo;
+ $keys[] = array(
+ 'type' => 'X509Certificate',
+ 'signing' => true,
+ 'encryption' => true,
+ 'X509Certificate' => $certInfo['certData'],
+ );
+ $hasNewCert = true;
+ } else {
+ $hasNewCert = false;
+ }
+
+ $certInfo = SimpleSAML\Utils\Crypto::loadPublicKey($idpmeta, true);
+ $availableCerts['idp.crt'] = $certInfo;
+ $keys[] = array(
+ 'type' => 'X509Certificate',
+ 'signing' => true,
+ 'encryption' => ($hasNewCert ? false : true),
+ 'X509Certificate' => $certInfo['certData'],
+ );
+
+ if ($idpmeta->hasValue('https.certificate')) {
+ $httpsCert = SimpleSAML\Utils\Crypto::loadPublicKey($idpmeta, true, 'https.');
+ assert('isset($httpsCert["certData"])');
+ $availableCerts['https.crt'] = $httpsCert;
+ $keys[] = array(
+ 'type' => 'X509Certificate',
+ 'signing' => true,
+ 'encryption' => false,
+ 'X509Certificate' => $httpsCert['certData'],
+ );
+ }
+
+ $metaArray = array(
+ 'metadata-set' => 'saml20-idp-remote',
+ 'entityid' => $idpentityid,
+ );
+
+ $ssob = $metadata->getGenerated('SingleSignOnServiceBinding', 'saml20-idp-hosted');
+ $slob = $metadata->getGenerated('SingleLogoutServiceBinding', 'saml20-idp-hosted');
+ $ssol = $metadata->getGenerated('SingleSignOnService', 'saml20-idp-hosted');
+ $slol = $metadata->getGenerated('SingleLogoutService', 'saml20-idp-hosted');
+
+ if (is_array($ssob)) {
+ foreach ($ssob as $binding) {
+ $metaArray['SingleSignOnService'][] = array(
+ 'Binding' => $binding,
+ 'Location' => $ssol,
+ );
+ }
+ } else {
+ $metaArray['SingleSignOnService'][] = array(
+ 'Binding' => $ssob,
+ 'Location' => $ssol,
+ );
+ }
+
+ if (is_array($slob)) {
+ foreach ($slob as $binding) {
+ $metaArray['SingleLogoutService'][] = array(
+ 'Binding' => $binding,
+ 'Location' => $slol,
+ );
+ }
+ } else {
+ $metaArray['SingleLogoutService'][] = array(
+ 'Binding' => $slob,
+ 'Location' => $slol,
+ );
+ }
+
+ if (count($keys) === 1) {
+ $metaArray['certData'] = $keys[0]['X509Certificate'];
+ } else {
+ $metaArray['keys'] = $keys;
+ }
+
+ if ($idpmeta->getBoolean('saml20.sendartifact', false)) {
+ /* Artifact sending enabled. */
+ $metaArray['ArtifactResolutionService'][] = array(
+ 'index' => 0,
+ 'Location' => \SimpleSAML\Utils\HTTP::getBaseURL().'saml2/idp/ArtifactResolutionService.php',
+ 'Binding' => SAML2_Const::BINDING_SOAP,
+ );
+ }
+
+ if ($idpmeta->getBoolean('saml20.hok.assertion', false)) {
+ /* Prepend HoK SSO Service endpoint. */
+ array_unshift($metaArray['SingleSignOnService'], array(
+ 'hoksso:ProtocolBinding' => SAML2_Const::BINDING_HTTP_REDIRECT,
+ 'Binding' => SAML2_Const::BINDING_HOK_SSO,
+ 'Location' => \SimpleSAML\Utils\HTTP::getBaseURL().'saml2/idp/SSOService.php'
+ ));
+ }
+
+ $metaArray['NameIDFormat'] = $idpmeta->getString(
+ 'NameIDFormat',
+ 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient'
+ );
+
+ if ($idpmeta->hasValue('OrganizationName')) {
+ $metaArray['OrganizationName'] = $idpmeta->getLocalizedString('OrganizationName');
+ $metaArray['OrganizationDisplayName'] = $idpmeta->getLocalizedString(
+ 'OrganizationDisplayName',
+ $metaArray['OrganizationName']
+ );
+
+ if (!$idpmeta->hasValue('OrganizationURL')) {
+ throw new SimpleSAML_Error_Exception('If OrganizationName is set, OrganizationURL must also be set.');
+ }
+ $metaArray['OrganizationURL'] = $idpmeta->getLocalizedString('OrganizationURL');
+ }
+
+ if ($idpmeta->hasValue('scope')) {
+ $metaArray['scope'] = $idpmeta->getArray('scope');
+ }
+
+ if ($idpmeta->hasValue('EntityAttributes')) {
+ $metaArray['EntityAttributes'] = $idpmeta->getArray('EntityAttributes');
+
+ // check for entity categories
+ if (SimpleSAML\Utils\Config\Metadata::isHiddenFromDiscovery($metaArray)) {
+ $metaArray['hide.from.discovery'] = true;
+ }
+ }
+
+ if ($idpmeta->hasValue('UIInfo')) {
+ $metaArray['UIInfo'] = $idpmeta->getArray('UIInfo');
+ }
+
+ if ($idpmeta->hasValue('DiscoHints')) {
+ $metaArray['DiscoHints'] = $idpmeta->getArray('DiscoHints');
+ }
+
+ if ($idpmeta->hasValue('RegistrationInfo')) {
+ $metaArray['RegistrationInfo'] = $idpmeta->getArray('RegistrationInfo');
+ }
+
+ if ($idpmeta->hasValue('validate.authnrequest')) {
+ $metaArray['sign.authnrequest'] = $idpmeta->getBoolean('validate.authnrequest');
+ }
+
+ if ($idpmeta->hasValue('redirect.validate')) {
+ $metaArray['redirect.sign'] = $idpmeta->getBoolean('redirect.validate');
+ }
+
+ if ($idpmeta->hasValue('contacts')) {
+ $contacts = $idpmeta->getArray('contacts');
+ foreach ($contacts as $contact) {
+ $metaArray['contacts'][] = \SimpleSAML\Utils\Config\Metadata::getContact($contact);
+ }
+ }
+
+ $technicalContactEmail = $config->getString('technicalcontact_email', false);
+ if ($technicalContactEmail && $technicalContactEmail !== 'na@example.org') {
+ $techcontact['emailAddress'] = $technicalContactEmail;
+ $techcontact['name'] = $config->getString('technicalcontact_name', null);
+ $techcontact['contactType'] = 'technical';
+ $metaArray['contacts'][] = \SimpleSAML\Utils\Config\Metadata::getContact($techcontact);
+ }
+
+ $metaBuilder = new SimpleSAML_Metadata_SAMLBuilder($idpentityid);
+ $metaBuilder->addMetadataIdP20($metaArray);
+ $metaBuilder->addOrganizationInfo($metaArray);
+
+ $metaxml = $metaBuilder->getEntityDescriptorText();
+
+ $metaflat = '$metadata['.var_export($idpentityid, true).'] = '.var_export($metaArray, true).';';
+
+ // sign the metadata if enabled
+ $metaxml = SimpleSAML_Metadata_Signer::sign($metaxml, $idpmeta->toArray(), 'SAML 2 IdP');
+
+ if (array_key_exists('output', $_GET) && $_GET['output'] == 'xhtml') {
+ $defaultidp = $config->getString('default-saml20-idp', null);
+
+ $t = new SimpleSAML_XHTML_Template($config, 'metadata.php', 'admin');
+
+ $t->data['available_certs'] = $availableCerts;
+ $t->data['header'] = 'saml20-idp';
+ $t->data['metaurl'] = \SimpleSAML\Utils\HTTP::getSelfURLNoQuery();
+ $t->data['metadata'] = htmlspecialchars($metaxml);
+ $t->data['metadataflat'] = htmlspecialchars($metaflat);
+ $t->data['defaultidp'] = $defaultidp;
+ $t->show();
+ } else {
+
+ header('Content-Type: application/xml');
+
+ echo $metaxml;
+ exit(0);
+ }
+} catch (Exception $exception) {
+ throw new SimpleSAML_Error_Error('METADATA', $exception);
}
-