Skip to content

Commit 3eee672

Browse files
author
Boy Baukema
committed
Rewrite php-saml to use ZF naming conventions, PSR-0 structure, improve repo layout, add Metadata generation, improve tests
1 parent ef5e154 commit 3eee672

File tree

13 files changed

+417
-223
lines changed

13 files changed

+417
-223
lines changed

README

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
The files index.php, consume.php and settings.php are sample code to help
1+
The files in demo are sample code to help
22
demonstrate how this library should work. In order to use them, you can
33
unpack this library in your website directory.
44

demo/consume.php

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,32 @@
99

1010
error_reporting(E_ALL);
1111

12+
$settings = NULL;
1213
require 'settings.php';
1314

14-
require 'lib/onelogin/saml.php';
15-
16-
$samlResponse = new OneLogin_Saml_Response(saml_get_settings(), $_POST['SAMLResponse']);
15+
$samlResponse = new OneLogin_Saml_Response($settings, $_POST['SAMLResponse']);
1716

1817
try {
19-
if ($samlResponse->is_valid()) {
20-
echo "You are: " . $samlResponse->get_nameid();
18+
if ($samlResponse->isValid()) {
19+
echo 'You are: ' . $samlResponse->getNameId() . '<br>';
20+
$attributes = $samlResponse->getAttributes();
21+
if (!empty($attributes)) {
22+
echo 'You have the following attributes:<br>';
23+
echo '<table><thead><th>Name</th><th>Values</th></thead><tbody>';
24+
foreach ($attributes as $attributeName => $attributeValues) {
25+
echo '<tr><td>' . htmlentities($attributeName) . '</td><td><ul>';
26+
foreach ($attributeValues as $attributeValue) {
27+
echo '<li>' . htmlentities($attributeValue) . '</li>';
28+
}
29+
echo '</ul></td></tr>';
30+
}
31+
echo '</tbody></table>';
32+
}
2133
}
2234
else {
23-
echo "Invalid SAML response.";
35+
echo 'Invalid SAML response.';
2436
}
2537
}
2638
catch (Exception $e) {
27-
echo "Invalid SAML response: " . $e->getMessage();
39+
echo 'Invalid SAML response: ' . $e->getMessage();
2840
}

demo/index.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,10 @@
1010

1111
error_reporting(E_ALL);
1212

13+
$settings = NULL;
1314
require 'settings.php';
1415

15-
require 'lib/onelogin/saml.php';
16-
17-
$authrequest = new OneLogin_Saml_AuthRequest(saml_get_settings());
18-
$url = $authrequest->create();
16+
$authRequest = new OneLogin_Saml_AuthRequest($settings);
17+
$url = $authRequest->getRedirectUrl();
1918

2019
header("Location: $url");

demo/metadata.php

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
<?php
22
/**
3-
* Created by JetBrains PhpStorm.
4-
* User: boy
5-
* Date: 4/6/12
6-
* Time: 9:31 AM
7-
* To change this template use File | Settings | File Templates.
8-
*/
3+
* SAMPLE Code to demonstrate how to handle a SAML assertion response.
4+
*
5+
* Your IdP will usually want your metadata, you can use this code to generate it once,
6+
* or expose it on a URL so your IdP can check it periodically.
7+
*/
8+
9+
error_reporting(E_ALL);
10+
11+
$settings = NULL;
12+
require 'settings.php';
13+
14+
header('Content-Type: text/xml');
15+
16+
$samlMetadata = new OneLogin_Saml_Metadata($settings);
17+
echo $samlMetadata->getXml();

demo/settings.php

Lines changed: 27 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,38 @@
22
/**
33
* SAMPLE Code to demonstrate how provide SAML settings.
44
*
5-
* The settings are contained within a SamlSettings object. You need to
5+
* The settings are contained within a OneLogin_Saml_Settings object. You need to
66
* provide, at a minimum, the following things:
7-
* - idp_sso_target_url: This is the URL to forward to for auth requests.
7+
*
8+
* - idpSingleSignOnUrl
9+
* This is the URL to forward to for auth requests.
810
* It will be provided by your IdP.
9-
* - x509certificate: This is a certificate required to authenticate your
10-
* request. This certificate should be provided by your IdP.
11-
* - assertion_consumer_service_url: The URL that the IdP should redirect
12-
* to once the authorization is complete. You must provide this, and it
13-
* should point to the consume.php script or its equivalent.
11+
*
12+
* - idpPublicCertificate
13+
* This is a certificate required to authenticate your request.
14+
* This certificate should be provided by your IdP.
15+
*
16+
* - spReturnUrl
17+
* The URL that the IdP should redirect to once the authorization is complete.
18+
* You must provide this, and it should point to the consume.php script or its equivalent.
1419
*/
1520

16-
define('XMLSECLIB_DIR', './../src')
21+
define('XMLSECLIBS_DIR', './../ext/xmlseclibs/');
22+
require XMLSECLIBS_DIR . 'xmlseclibs.php';
1723

1824
define('ONELOGIN_SAML_DIR', './../src/OneLogin/Saml/');
1925
require ONELOGIN_SAML_DIR . 'AuthRequest.php';
2026
require ONELOGIN_SAML_DIR . 'Response.php';
2127
require ONELOGIN_SAML_DIR . 'Settings.php';
2228
require ONELOGIN_SAML_DIR . 'XmlSec.php';
2329

24-
/**
25-
* Return a SamlSettings object with user settings.
26-
*/
27-
function saml_get_settings()
28-
{
29-
// This function should be modified to return the SAML settings for the current user
30-
31-
$settings = new OneLogin_Saml_Settings();
30+
$settings = new OneLogin_Saml_Settings();
3231

33-
// When using Service Provider Initiated SSO (starting at index.php), this URL asks the IdP to authenticate the user.
34-
$settings->idp_sso_target_url = "https://app.onelogin.com/saml/signon/6171";
32+
// When using Service Provider Initiated SSO (starting at index.php), this URL asks the IdP to authenticate the user.
33+
$settings->idpSingleSignOnUrl = 'https://app.onelogin.com/saml/signon/6171';
3534

36-
// The certificate for the users account in the IdP
37-
$settings->x509certificate = <<<ENDCERTIFICATE
35+
// The certificate for the users account in the IdP
36+
$settings->idpPublicCertificate = <<<CERTIFICATE
3837
-----BEGIN CERTIFICATE-----
3938
MIIBrTCCAaGgAwIBAgIBATADBgEAMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApD
4039
YWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxv
@@ -47,17 +46,15 @@ function saml_get_settings()
4746
UhLFIXkGSCAIVfoR5S2ggdfpINKUWGsWS/lEzLNYMBkURXuVAgMBAAEwAwYBAAMB
4847
AA==
4948
-----END CERTIFICATE-----
50-
ENDCERTIFICATE;
51-
52-
// The URL where to the SAML Response/SAML Assertion will be posted
53-
$settings->assertion_consumer_service_url = "http://localhost/php-saml/consume.php";
49+
CERTIFICATE;
5450

55-
// Name of this application
56-
$settings->issuer = "php-saml";
51+
// The URL where to the SAML Response/SAML Assertion will be posted
52+
$settings->spReturnUrl = 'http://localhost/php-saml/consume.php';
5753

58-
// Tells the IdP to return the email address of the current user
59-
$settings->name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress";
54+
// Name of this application
55+
$settings->spIssuer = 'php-saml';
6056

57+
// Tells the IdP to return the email address of the current user
58+
$settings->requestedNameIdFormat = OneLogin_Saml_Settings::NAMEID_EMAIL_ADDRESS;
6159

62-
return $settings;
63-
}
60+
return $settings;

src/OneLogin/Saml/Metadata.php

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,58 @@
11
<?php
2+
23
/**
3-
* Created by JetBrains PhpStorm.
4-
* User: boy
5-
* Date: 4/6/12
6-
* Time: 9:22 AM
7-
* To change this template use File | Settings | File Templates.
8-
*/
4+
* Create SAML2 Metadata documents
5+
*/
6+
class OneLogin_Saml_Metadata
7+
{
8+
/**
9+
* How long should the metadata be valid?
10+
*/
11+
const VALIDITY_SECONDS = 604800; // 1 week
12+
13+
/**
14+
* Service settings
15+
* @var OneLogin_Saml_Settings
16+
*/
17+
private $_settings;
18+
19+
/**
20+
* Create a new Metadata document
21+
* @param OneLogin_Saml_Settings $settings
22+
*/
23+
public function __construct(OneLogin_Saml_Settings $settings)
24+
{
25+
$this->_settings = $settings;
26+
}
27+
28+
/**
29+
* @return string
30+
*/
31+
public function getXml()
32+
{
33+
$validUntil = $this->_getMetadataValidTimestamp();
34+
35+
return <<<METADATA_TEMPLATE
36+
<?xml version="1.0"?>
37+
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
38+
validUntil="$validUntil"
39+
entityID="{$this->_settings->spIssuer}">
40+
<md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
41+
<md:NameIDFormat>{$this->_settings->requestedNameIdFormat}</md:NameIDFormat>
42+
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
43+
Location="{$this->_settings->spReturnUrl}"
44+
index="1"/>
45+
</md:SPSSODescriptor>
46+
</md:EntityDescriptor>
47+
METADATA_TEMPLATE;
48+
}
49+
50+
private function _getMetadataValidTimestamp()
51+
{
52+
$timeZone = date_default_timezone_get();
53+
date_default_timezone_set('UTC');
54+
$time = strftime("%Y-%m-%dT%H:%M:%SZ", time() + self::VALIDITY_SECONDS);
55+
date_default_timezone_set($timeZone);
56+
return $time;
57+
}
58+
}

src/onelogin/saml/authrequest.php

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,76 @@
11
<?php
2+
23
/**
34
* Create a SAML authorization request.
45
*/
5-
class SamlAuthRequest
6+
class OneLogin_Saml_AuthRequest
67
{
8+
const ID_PREFIX = 'ONELOGIN';
9+
710
/**
811
* A SamlResponse class provided to the constructor.
12+
* @var OneLogin_Saml_Settings
913
*/
10-
private $settings;
14+
private $_settings;
1115

1216
/**
1317
* Construct the response object.
1418
*
15-
* @param SamlResponse $settings
19+
* @param OneLogin_Saml_Settings $settings
1620
* A SamlResponse settings object containing the necessary
1721
* x509 certicate to decode the XML.
1822
*/
19-
function __construct($settings)
23+
public function __construct(OneLogin_Saml_Settings $settings)
2024
{
21-
$this->settings = $settings;
25+
$this->_settings = $settings;
2226
}
2327

2428
/**
2529
* Generate the request.
2630
*
27-
* @return
28-
* A fully qualified URL that can be redirected to in order to process
29-
* the authorization request.
31+
* @return string A fully qualified URL that can be redirected to in order to process the authorization request.
3032
*/
31-
public function create()
33+
public function getRedirectUrl()
3234
{
33-
$id = $this->generateUniqueID(20);
34-
$issue_instant = $this->getTimestamp();
35+
$id = $this->_generateUniqueID();
36+
$issueInstant = $this->_getTimestamp();
3537

36-
$request =
37-
"<samlp:AuthnRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ID=\"$id\" Version=\"2.0\" IssueInstant=\"$issue_instant\" ProtocolBinding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" AssertionConsumerServiceURL=\"" . $this->settings->assertion_consumer_service_url . "\">" .
38-
"<saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">" . $this->settings->issuer . "</saml:Issuer>\n" .
39-
"<samlp:NameIDPolicy xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" Format=\"" . $this->settings->name_identifier_format . "\" AllowCreate=\"true\"></samlp:NameIDPolicy>\n" .
40-
"<samlp:RequestedAuthnContext xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" Comparison=\"exact\">" .
41-
"<saml:AuthnContextClassRef xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></samlp:RequestedAuthnContext>\n" .
42-
"</samlp:AuthnRequest>";
38+
$request = <<<AUTHNREQUEST
39+
<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
40+
ID="$id"
41+
Version="2.0"
42+
IssueInstant="$issueInstant"
43+
ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
44+
AssertionConsumerServiceURL="{$this->_settings->spReturnUrl}">
45+
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">{$this->_settings->spIssuer}</saml:Issuer>
46+
<samlp:NameIDPolicy xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
47+
Format="{$this->_settings->requestedNameIdFormat}"
48+
AllowCreate="true"></samlp:NameIDPolicy>
49+
<samlp:RequestedAuthnContext xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" Comparison="exact">
50+
<saml:AuthnContextClassRef xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
51+
>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
52+
</samlp:RequestedAuthnContext>
53+
</samlp:AuthnRequest>";
54+
AUTHNREQUEST;
4355

44-
$deflated_request = gzdeflate($request);
45-
$base64_request = base64_encode($deflated_request);
46-
$encoded_request = urlencode($base64_request);
56+
$deflatedRequest = gzdeflate($request);
57+
$base64Request = base64_encode($deflatedRequest);
58+
$encodedRequest = urlencode($base64Request);
4759

48-
return $this->settings->idp_sso_target_url . "?SAMLRequest=" . $encoded_request;
60+
return $this->_settings->idpSingleSignOnUrl . "?SAMLRequest=" . $encodedRequest;
4961
}
5062

51-
private function generateUniqueID($length)
63+
private function _generateUniqueID()
5264
{
53-
$chars = "abcdef0123456789";
54-
$chars_len = strlen($chars);
55-
$uniqueID = "";
56-
for ($i = 0; $i < $length; $i++)
57-
$uniqueID .= substr($chars, rand(0, 15), 1);
58-
return "_" . $uniqueID;
65+
return self::ID_PREFIX . sha1(uniqid(mt_rand(), TRUE));
5966
}
6067

61-
private function getTimestamp()
68+
private function _getTimestamp()
6269
{
70+
$defaultTimezone = date_default_timezone_get();
6371
date_default_timezone_set('UTC');
64-
return strftime("%Y-%m-%dT%H:%M:%SZ");
72+
$timestamp = strftime("%Y-%m-%dT%H:%M:%SZ");
73+
date_default_timezone_set($defaultTimezone);
74+
return $timestamp;
6575
}
66-
}
67-
68-
;
76+
}

0 commit comments

Comments
 (0)