Commit 1638ee7e authored by haemmer's avatar haemmer
Browse files

Fixed #275 as well as some other minor bugs

parent 65b6ae1c
<?php
// WAYF Identity Provider Configuration file
// In the following you see some example entries of Identity Providers and
// Find below some example entries of Identity Providers, categories and
// cascaded WAYFs
// The keys of $IDProviders must correspond to the entityId of the
// Identity Providers or a unique value in case of a cascaded WAYF/DS or
// a category
// a category. In the case of a category, the key must correspond to the the
// Type value of Identity Provider entries.
// The sequence of IdPs and SPs play a role. No sorting is done.
// A general entry for an IdP can consist of the form:
// Type: [Optional] Some type that is used for the embedded wayf to hide
// or show certain categories. Default type will
// Type: [Optional] Type of the entry. Default type will
// be 'unknown' if not specified.
// Categories should have the type 'category'
// An entry for a cascaded WAYF that the user shall be
// redirected to should have the type 'wayf'
// Name: [Mandatory] Default name to display in drop-down list
// [en|it|fr||de|pt][Name]: [Optional] Display name in other languages
// SSO: [Mandatory] Should be the SAML1 SSO endpoint of the IdP
// Realm: [Optional] Kerberos Realm
// IP[]: [Optional] IP ranges of that organizations that can be used to guess
// a user's Identity Provider
// An entry for another WAYF that the user shall be redirected to should have:
// Type: 'wayf'
// Index: [Optional] An alphanumerical value that is used for sorting
// categories and Identity Provider in ascending order
// if the Identity Providers are parsed from metadata.
// This is only relevant if
// $includeLocalConfEntries = true
// A category entry can be used to group multiple IdP entries into a optgroup
// The category entries should look like:
......@@ -59,7 +64,7 @@ $IDProviders['aitta.funet.fi'] = array (
// Category
$IDProviders['vho'] = array (
'Type' => 'category',
'Name' => 'Virtual Home Organization',
'Name' => 'Virtual Home Organizations',
);
// An example of a configuration with multiple network blocks and multiple languages
......@@ -107,10 +112,10 @@ $IDProviders['other'] = array (
// Standard example with a Type that could be used to hide certain
// Identity Providers in the list of an embedded WAYF according to their type
$IDProviders['https://toba.switch.ch/idp/shibboleth'] = array(
$IDProviders['https://aai-logon.switch.ch/idp/shibboleth'] = array(
'Type' => 'other',
'Name' => 'SWITCH - Serving Swiss Universities',
'SSO' => 'https://toba.switch.ch/idp/profile/Shibboleth/SSO',
'SSO' => 'https://aai-logon.switch.ch/idp/profile/Shibboleth/SSO',
);
?>
......@@ -8,7 +8,23 @@ Bug reports/feature requests: https://forge.switch.ch/redmine/projects/wayf/issu
-------------------------------------------------------------------------------
Description:
The SWITCHwayf is an implementation of the Shibboleth WAYF and SAML2 Discovery Service protocol for use withing a Shibboleth architecture.
The SWITCHwayf is an implementation of the Shibboleth WAYF and SAML2 Discovery
Service protocol for use withing a Shibboleth architecture.
-------------------------------------------------------------------------------
Security Note (25. October 2010):
The current implementation of the Discovery Service protocol as defined in
http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-idp-discovery.pdf
states that the protocol creates opportunities for phishing attacks as do all
SSO protocols that make use of redirection. Therefore, the specification states
that an implementation should examine the 'return' parameter used in a
Discovery Service request and match it against the <idpdisc:DiscoveryResponse>
extension in SAML metadata. The implementation of the Discovery Service protocol
in the SWITCHwayf does NOT verify the return parameter because it only
optionally reads SAML metadata.
Thanks to Tom Scavo for making us aware of this issue.
-------------------------------------------------------------------------------
......@@ -29,6 +45,7 @@ Some of the features include
- HTML code generation for embedding the WAYF directly into a web page
- Support for remembering IdP selection permanently
- I18N support, currently language packs for en, de, it, fr and pt are included
- Support for templates
-------------------------------------------------------------------------------
......@@ -211,7 +228,7 @@ Credits for this feature go to Josh Howlett from Bristol University.
-------------------------------------------------------------------------------
Identity Provider configuration file format:
Configuration file format:
Checkout the file 'IDProvider.conf.php' for an example of the file format. It's
supposed to be mostly self-explanatory. Basically the file format is PHP code
that defines an array of arrays called $IDProviders.
......@@ -221,34 +238,64 @@ Identity Providers or a unique value in case of a cascaded WAYF/DS or
a category. The entityID must be a valid URI (URL or URN) where in the
case of a URN the last component must be a valid hostname.
The sequence of IdPs and SPs matters and no sorting is done.
A general entry for an IdP can consist of the form:
Type: [Optional] Some type that is used for the embedded wayf to hide
If metadata is not parsed from SAML2 metadta (using the setting
$useSAML2Metadata = false), the IdPs and category entries will be displayed
in order as defined in the configuration file and no sorting is done.
If metadata is used ($useSAML2Metadata = true) to generate the list of Identity
Providers, the Identity Providers will first be sorted according to their
type/category, their Index value (see below) and then alphabetically after their
(local) Name within the same type category.
If an IdP does not have a type, its category is 'unknown'.
If there exists a type for an IdP that does not have a corresponding category
it will be displayed in the category 'unknown' at the end of the drop down list
but will keep its type value (this is important for the Embedded Discovery
Service).
A general entry for an Identity Provider, a cascaded WAYF/DS or a category is
of the following form:
$IDProviders[#key#] = #entry#
#key# is a unique value that must correspond to the entity's entityID in case
the entry stands for an Identity Provider. For entries of Type category, the
#key# should be an identifier that corresponds to a 'Type' of an IdP.
#entry# is another hash array with the following keys:
['Type']: Optional Type that is used for the embedded wayf to hide
or show certain categories. Default type will
be 'unknown' if not specified.
Name: [Mandatory] Default name to display in drop-down list
[en|it|fr||de|pt][Name]: [Optional] Display name in other languages
SSO: [Mandatory] Should be the SAML1 SSO endpoint of the IdP
Realm: [Optional] Kerberos Realm
IP[]: [Optional] IP ranges of that organizations that can be used to
An entry for another WAYF/DS that the user shall be
redirected to should have ['Type'] ='wayf
The Type values 'category' and 'wayf' are reserved
words that are not allowed to be assigned to
entries for Identity Providers.
['Name']: Mandatory Default name to display in drop-down list
['en'|'it'|'fr'|'de'|'pt']['Name']:
Optional Display name in other languages
['SSO']: Mandatory Should be the SAML1 SSO endpoint of the IdP
['Realm']: Optional Kerberos Realm
['IP'][]: Optional IP ranges of that organizations that can be used to
guess a user's Identity Provider
['Index']: Optional An alphanumerical value that is used for sorting
categories and Identity Provider in ascending order
if the Identity Providers are parsed from metadata.
This is only relevant if
$includeLocalConfEntries = true
An entry for another WAYF that the user shall be redirected to should have:
Type: 'wayf'
A category entry can be used to group multiple IdP entries into a optgroup
The category entries should look like:
Name: [Mandatory] Default name to display in drop-down list
[en|it|fr||de|pt][Name]: [Optional] Display name in other languages
Type: 'category' Category type
As stated above, the sequence of entries is important. So, one is completely
flexible when it comes to ordering the category and IdP entries.
For category entries, only Type, (local) Name and Index are relevant.
-------------------------------------------------------------------------------
Changes:
1.14 - Sorting within categories works now correctly if SAML2 metadata is
used to generate Identity Provider drop-down list. Thanks to Prof.
Kazu Yamaji from NII for reporting this issue.
- Fixed a minor bug in templates.php that cause PHP warnings to show up
in case an invalid IdP was stored in the cookie.
- Fixed a bug affecting the Kerberos authentication.
Thanks to Robert Basch from MIT for reporting these bugs and for
submitting patches.
- Fixed a bug where hidden IdPs would still be shown in Embedded WAYF
1.13 - Added Favourite IdPs to Embedded WAYF
- Fixed a bug where the state of the "Remember for session" checkbox was
not remembered.
......
......@@ -4,7 +4,7 @@
******************************************************************************
SWITCH PHP WAYF,
Copyright 2009 SWITCH - Serving Swiss Universities
Version: 1.13
Version: 1.14
Contact: aai@switch.ch
Web site: http://www.switch.ch/aai/wayf
******************************************************************************
......@@ -14,7 +14,7 @@ Web site: http://www.switch.ch/aai/wayf
// Load general configuration and template file
/*------------------------------------------------*/
require_once('config.php');
require_once('config.test.php');
require_once('templates.php');
require_once('functions.php');
require_once('languages.php');
......@@ -386,6 +386,18 @@ if ($hintedCookieIdP != '-'){
$selectedIDP = '-';
}
/*------------------------------------------------*/
// Sort Identity Providers
/*------------------------------------------------*/
//if (isset($useSAML2Metadata) && $useSAML2Metadata){
if (true){
// Only automatically sort if list of Identity Provider is parsed
// from metadata instead of being manualy managed
sortIdentityProviders($IDProviders);
}
/*------------------------------------------------*/
// Draw WAYF
/*------------------------------------------------*/
......
......@@ -45,16 +45,20 @@ $SPCookieName = $cookieNamePrefix.'_saml_sp';
// Whether to show the checkbox to permanently remember a setting
$showPermanentSetting = false;
// Set to true in order to enable dynamic generation of the IdP list displayed
// Set to true in order to enable reading the Identity Provider from a SAML2
// metadata file defined below in $metadataFile
$useSAML2Metadata = false;
// Parsed metadata shall have precedence
// when conflicts between SAML2 metadata and local IDProvider.conf are detected.
// If ture parsed metadata shall have precedence if there are entries defined
// in metadata as well as the local IDProviders configuration file.
// Only relevant if $useSAML2Metadata is true
$SAML2MetaOverLocalConf = false;
// If includeLocalConfEntries parameter is set to true, mergeInfo() will also consider IDPs
// not listed in metadataIDProviders but defined in IDProviders file
// This is required if you need to add local exceptions over the federation metadata
// If includeLocalConfEntries parameter is set to true, Identity Providers
// not listed in metadata but defined in the local IDProviders file will also
// be displayed in the drop down list. This is required if you need to add
// local exceptions over the federation metadata
// Only relevant if $useSAML2Metadata is true
$includeLocalConfEntries = true;
// Whether to turn on Kerberos support for IdP preselection
......@@ -128,6 +132,6 @@ $WAYFLogFile = '/var/log/apache2/wayf.log';
// Development mode settings
//**************************
// If the development mode is activated, PHP errors and warnings will be displayed
$developmentMode = true;
$developmentMode = false;
?>
......@@ -128,7 +128,7 @@
-->
</style>
</head>
<body bgcolor="#ffffff" onLoad="if (document.IdPList && document.IdPList.Select) document.IdPList.Select.focus()">
<body bgcolor="#ffffff" onLoad="if (top != self) {top.location = self.location;};if (document.IdPList && document.IdPList.Select) document.IdPList.Select.focus()">
<script language="JavaScript" type="text/javascript">
<!--
function showConfirmation(){
......
......@@ -344,7 +344,7 @@ function getIPAdressHint() {
global $IDProviders;
foreach($IDProviders as $name => $idp) {
if (array_key_exists("IP", $idp)) {
if (is_array($idp) && array_key_exists("IP", $idp)) {
$clientIP = $_SERVER["REMOTE_ADDR"];
foreach( $idp["IP"] as $network ) {
......@@ -511,4 +511,90 @@ function convertToShibDSStructure($IDProviders){
}
/******************************************************************************/
// Sorts the IDProviders array
function sortIdentityProviders(&$IDProviders){
$sortedIDProviders = Array();
$sortedCategories = Array();
foreach ($IDProviders as $entityId => $IDProvider){
if (!is_array($IDProvider)){
// Remove any entries that are not arrays
unset($IDProviders[$entityId]);
} elseif ($IDProvider['Type'] == 'category'){
$sortedCategories[$entityId] = $IDProvider;
} else {
$sortedIDProviders[$entityId] = $IDProvider;
}
}
// Sort categories and IdPs
if (count($sortedCategories) > 1){
// Sort using index
uasort($sortedCategories, 'sortUsingTypeIndexAndName');
} else {
// Sort alphabetically using the key of a category
ksort($sortedCategories);
}
// Add category 'unknown' if not present
if (!isset($IDProviders['unknown'])){
$sortedCategories['unknown'] = array (
'Name' => 'Unknown',
'Type' => 'category',
);
}
// Sort Identity Providers
uasort($sortedIDProviders, 'sortUsingTypeIndexAndName');
$IDProviders = Array();
// Compose array
$showUnknownCategory = false;
while(list($categoryKey, $categoryValue) = each($sortedCategories)){
$IDProviders[$categoryKey] = $categoryValue;
// Loop through all IdPs
foreach ($sortedIDProviders as $IDProvidersPKey => $IDProvidersValue){
// Add IdP if its type matches the current category
if ($IDProvidersValue['Type'] == $categoryKey){
$IDProviders[$IDProvidersPKey] = $IDProvidersValue;
unset($sortedIDProviders[$IDProvidersPKey]);
}
// Add IdP if its type is 'unknown' or if there doesnt exist a category for its type
if ($categoryKey == 'unknown' || !isset($sortedCategories[$IDProvidersValue['Type']])){
$IDProviders[$IDProvidersPKey] = $IDProvidersValue;
unset($sortedIDProviders[$IDProvidersPKey]);
$showUnknownCategory = true;
}
}
}
// Check if unkown category is needed
if (!$showUnknownCategory){
unset($IDProviders['unknown']);
}
}
/******************************************************************************/
// Sorts two entries according to their Type, Index and (local) Name
function sortUsingTypeIndexAndName($a, $b){
global $language;
if ($a['Type'] != $b['Type']){
return strcmp($b['Type'], $a['Type']);
} elseif (isset($a['Index']) && isset($b['Index']) && $a['Index'] != $b['Index']){
return strcmp($a['Index'], $b['Index']);
} else {
// Sort using locale names
$localNameB = (isset($a[$language]['Name'])) ? $a[$language]['Name'] : $a['Name'];
$localNameA = (isset($b[$language]['Name'])) ? $b[$language]['Name'] : $b['Name'];
return strcmp($localNameB, $localNameA);
}
}
?>
......@@ -173,9 +173,6 @@ function parseMetadata($metadataFile, $defaultLanguage){
}
}
// Sort IdPs
uasort($metadataIDProviders, 'sortIdPs');
return $metadataIDProviders;
}
......@@ -216,12 +213,6 @@ function dumpFile($metadataIDPFile, $IDProviders){
}
}
/******************************************************************************/
// Sort IdP array using the default name of an IdP
function sortIdPs($a, $b){
return strcmp($a['Name'], $b['Name']);
}
/******************************************************************************/
// Function mergeInfo is used to create the effective $IDProviders array.
......@@ -233,33 +224,38 @@ function mergeInfo($IDProviders, $metadataIDProviders, $SAML2MetaOverLocalConf,
// If $includeLocalConfEntries parameter is set to true, mergeInfo() will also consider IDPs
// not listed in metadataIDProviders but defined in IDProviders file
// This is required if you need to add local exceptions over the federation metadata
$arrayOfIDPs = $metadataIDProviders;
$allIDPS = $metadataIDProviders;
$mergedArray = Array();
if ($includeLocalConfEntries) {
$arrayOfIDPs = array_merge($metadataIDProviders, $IDProviders);
uasort($arrayOfIDPs, 'sortIdPs');
$allIDPS = array_merge($metadataIDProviders, $IDProviders);
}
foreach ($arrayOfIDPs as $meta => $metaArray){
foreach ($allIDPS as $allIDPsKey => $allIDPsEntry){
if(isset($IDProviders[$allIDPsKey])){
// Entry exists also in local IDProviders.conf.php
if (isset($metadataIDProviders[$allIDPsKey]) && is_array($metadataIDProviders[$allIDPsKey])) {
// Skip IDPs
if (
!$SAML2MetaOverLocalConf &&
isset($IDProviders[$meta])
&& !is_array($IDProviders[$meta])
) { continue; }
// Remove IdP if there is a removal rule in local IDProviders.conf.php
if (!is_array($IDProviders[$allIDPsKey])){
unset($metadataIDProviders[$allIDPsKey]);
continue;
}
if(isset($IDProviders[$meta])){
if (isset($metadataIDProviders[$meta]) && is_array($metadataIDProviders[$meta])) {
// Entry exists in both IDProviders sources and is an array
if($SAML2MetaOverLocalConf){
$mergedArray[$meta] = array_merge($IDProviders[$meta], $metadataIDProviders[$meta]);
// Metadata entry overwrite local conf
$mergedArray[$allIDPsKey] = array_merge($IDProviders[$allIDPsKey], $metadataIDProviders[$allIDPsKey]);
} else {
$mergedArray[$meta] = $metadataIDProviders[$meta];
// Local conf overwrites metada entry
$mergedArray[$allIDPsKey] = array_merge($metadataIDProviders[$allIDPsKey], $IDProviders[$allIDPsKey]);
}
} else {
$mergedArray[$meta] = $IDProviders[$meta];
// Entry only exists in local IDProviders file
$mergedArray[$allIDPsKey] = $IDProviders[$allIDPsKey];
}
} else {
$mergedArray[$meta] = $metadataIDProviders[$meta];
// Entry doesnt exist in in local IDProviders.conf.php
$mergedArray[$allIDPsKey] = $metadataIDProviders[$allIDPsKey];
}
}
......
......@@ -226,19 +226,26 @@ function printEmbeddedWAYFScript(){
// Generate list of Identity Providers
$JSONIdPArray = array();
foreach ($IDProviders as $key => $value){
foreach ($IDProviders as $key => $IDProvider){
// Get IdP Name
if (isset($value[$language]['Name'])){
$IdPName = addslashes($value[$language]['Name']);
if (isset($IDProvider[$language]['Name'])){
$IdPName = addslashes($IDProvider[$language]['Name']);
} else {
$IdPName = addslashes($value['Name']);
$IdPName = addslashes($IDProvider['Name']);
}
// Set selected attribute
$selected = ($selectedIDP == $key) ? ' selected="selected"' : '' ;
$IdPType = isset($IDProviders[$key]['Type']) ? $IDProviders[$key]['Type'] : '';
// SSO
if (isset($IDProvider['SSO'])){
$IdPSSO = $IDProvider['SSO'];
} else {
$IdPSSO = '';
}
// Skip non-IdP entries
if ($IdPType == '' || $IdPType == 'category'){
continue;
......@@ -249,7 +256,7 @@ function printEmbeddedWAYFScript(){
"{$key}":{
type:"{$IdPType}",
name:"{$IdPName}",
SAML1SSOurl:"{$value['SSO']}"
SAML1SSOurl:"{$IdPSSO}"
}
ENTRY;
}
......@@ -858,20 +865,20 @@ SCRIPT;
// Generate drop-down list
$optgroup = '';
foreach ($IDProviders as $key => $value){
foreach ($IDProviders as $key => $IDProvider){
// Get IdP Name
if (isset($value[$language]['Name'])){
$IdPName = addslashes($value[$language]['Name']);
if (isset($IDProvider[$language]['Name'])){
$IdPName = addslashes($IDProvider[$language]['Name']);
} else {
$IdPName = addslashes($value['Name']);
$IdPName = addslashes($IDProvider['Name']);
}
// Figure out if entry is valid or a category
if (!isset($value['SSO'])){
if (!isset($IDProvider['SSO'])){
// Check if entry is a category
if (isset($value['Type']) && $value['Type'] == 'category'){
if (isset($IDProvider['Type']) && $IDProvider['Type'] == 'category'){
echo <<<SCRIPT
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment