Skip to content

Commit

Permalink
[MAJOR] v2 rewrite, removes SimpleSAMLphp for onelogin/php-saml (#19)
Browse files Browse the repository at this point in the history
Major refactor of silverstripe-realme module to create version 2.0.0

The module now uses the `onelogin/php-saml` SAML library rather than the much more bloated SimpleSAMLphp application.

* FEAT: Provide new `RealMeUser` class that encapsulates logon or assert user information.
* FEAT: Tidy up of RealMeSetupTask to match new metadata XML
* FEAT: Add assert login form
* FEAT: Update certificate check to test for \r and \n characters (e.g. on Windows systems)
* FEAT: Log error via SS_Log when auth errors are returned by onelogin
* FEAT: Switch to using setProxyVars, and only if TRUSTED_PROXY is set to true for security (so someone can't protocol-downgrade if they can bypass a fronting WAF, for example)
* FEAT: Set original SAMLResponse in session so it can be retrieved later
* FEAT: Add error messages to RealMe form if they exist
* FEAT: Add 'standard' error messages, and allow the ability to override by YML
* FEAT: Add noopener/noreferer to links that open in target="_blank"
* FEAT: Remove ability to set RealMeBackURL by _REQUEST, add ability to set 'RealMeErrorBackURL' separate to 'RealMeBackURL'
* FEAT: Add _ss_env detection around Authenticator::register_authenticator() to avoid issues with Deploynaut-based deploys
* FEAT: Merge changes from Terraformers team into PR#19 (#1)
* FEAT: Session naming convention modified to use dot notation e.g RealMe.SessionData
* FEAT: Redundancy checks added to RealMeUser::getFederatedIdentity()
* FEAT: RealMeService::getCertDir() modified to handle sub directories
* FEAT: Instances of getCertDir() use modified to follow new convention
* FEAT: Current real me user features moved away from Config service.
* FEAT: RealMeService now implements TemplateGlobalProvider
* FEAT: RealMeService::getUserData() functionality moved to RealMeService::user_data()
* FEAT: "RealMeUser" definition added to RealMeService::get_template_global_variables() array
* FEAT: RealMeDataExtension.php removed, and associated SiteTree/SiteConfig extension definitions in extensions.yml
* FEAT: RealMeAuthenticator::on_register() use of cache modified to test for availability of key in cache
* FEAT: RealMeUser: Modified isValid to check integration type, and check validity between login and assertion integrations (federated logins have more required attributes)
* FEAT: enforceLogin() is now split between authentication and error state -> refactored into new function for error processing & translation; clearLogin() now clears all realme session states (Ideally should be all namespaced).
* FEAT: Update to use the default styling (blue background) by default
* FEAT: Add ability to include a 'mini' login form, suitable for website header/footers
* FEAT: Add ability to specify whether the 'What's RealMe?' popup appears to the left or right under the form
* FEAT: Allow ability to override service name that is displayed
* FEAT: Properly fix the service names (there are three distinct values that are in slightly different contexts)
* FEAT: Add public accessors for info stored on RealMeFederatedIdentity

* FIX: Update RealMeLoginForm.ss so it doesn't need to be overwritten for every site
* FIX: Update requirement for onelogin/php-saml to hopefully resolve issues with signature verification failing
* FIX: Always use the absolute base URL, rather than the current page URL, for the RelayState. For strict SAML, RelayState must be < 80 bytes. We may need to revise this later, or not pass a RelayState for a domain is longer than 80 bytes
* FIX: Rename NameID (now called SPNameID) and added UserFederatedTag (either FLT or FIT)
* FIX: Fix DateOfBirth to return null if any element is missing (shouldn't occur for valid identities though)
* FIX: Update log message to include _errorReason
* FIX: Remove invalid springload link from RealMeAssertForm.ss template (copied from Shared Workspace)
* FIX: Update RealMe links to be https
* FIX: Update broken link in assert form
* FIX: Use RealMeBackURL rather than overriding the standard member login form's BackURL, as it can be cleared elsewhere accidentally
* FIX: Don't require all federated identity tags, better handle cases where values aren't present (e.g. BirthPlaceQuality)
* FIX: Application Name / Service ID part of the entity ID can be up 20 chars
* FIX: Update error message for NoAvailableIDP to remove the reference to igovt
* FIX: Remove leading/trailing whitespace from certificate contents before returning
* FIX: Update requirements per dhensby changes, and change .gitignore
* FIX: Update docblocks with changes suggested by Scrutinizer
* FIX: Remove 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' from message spec
* FIX: Optimise DBField creation in RealMeFederatedIdentity::getDateOfBirth()
* FIX: .gitignore removed because it didn't contain any entries relevant to project specifically
* FIX: Use of `sizeof($errors) > 0` replaced with `!empty($errors)` in RealMeService::enforceLogin()
* FIX: RealMeSecurityExtension: Removed unused constants and unused error handlers/translations
* FIX: RealMeService: fixed user_data function to return user
* FIX: Add NameIDFormat back (revert 97bf2ce), but make it work for both logon and assert
* FIX: Ensure we only redirect users if they aren't already being redirected (allowing application code to redirect users to a custom destination instead)
* FIX: Always send a specific NameIDFormat, depending on the integration type.
* FIX: RealMe (MTS at least, possible ITE and Prod) will fail oddly if the NameIDFormat provided in AuthN requests does not match the one supplied during metadata upload/submission. On MTS, this results in the AuthN request being successfully validated, but after submitting the resulting form, MTS fails with a HTTP 404 error rather than redirecting back to the issuer. This simple change ensures that the NameIDFormat we use when running dev/tasks/RealMeSetupTask matches the NameIDFormat that is used when submitting the AuthN request.
* FIX: Minor style tidyup
* FIX: Update comments in RealMeService to be more accurate for enforceLogin- Add tests for getAuth() and various overrides
* FIX: Fix issue with running tests via travis doesn't work because of missing env vars (force env var setting during tests)

* DOCS: Added @jakxnz to README because I squashed all his commits and lost his hard work. Sorry :(
* DOCS: Update installation documentation to remove references to SimpleSAMLphp
* DOCS: Update changelog a bit, add note to README, mark getSigningCertPassword() as deprecated, remove old config params
* DOCS: Installation instructions updated
* DOCS: Changelog updated
* DOCS: Readme updated
* DOCS: Revert change to installation.md to correct version - still need custom fork of module until changes can be made generic and merged upstream to onelogin/php-saml.
* DOCS: Update text to reference latest version of Login and Assert assets
* DOCS: Update code example to fix old arg
* DOCS: Remove reference to old `REALME_MUTUAL_CERT_FILENAME` const from documentation
* DOCS: Update README.md and configuration.md to include new instructions mainly using the RealMe Developers site
* DOCS: Remove the ssl-certs.md file as this is adequately covered now by the RealMe Developers site
* DOCS: Remove installation.md and migrate content to README; update module overrides

* TESTS: Remove bootstrap.php (only needed for SimpleSAMLphp), add parent calls to tests
* TESTS: Move temp certificate path into RealMeServiceTest method
* TESTS: Update travis to match requirements, only build on 5.4+ and SS 3.5
* TESTS: Test certificate contents moved to files in test directory
* TESTS: Only bother with PHP5.6 tests, 5.4 and 5.5 aren't officially supported and shouldn't be used anymore anyway
* TESTS: Switch to Codecov.io for coverage based on @robbieaverill suggestion
  • Loading branch information
madmatt committed May 1, 2018
1 parent 71a01ca commit b1cfcae
Show file tree
Hide file tree
Showing 42 changed files with 1,947 additions and 1,878 deletions.
5 changes: 0 additions & 5 deletions .gitignore

This file was deleted.

3 changes: 3 additions & 0 deletions .scrutinizer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ checks:

filter:
paths: [code/*, tests/*]

tools:
external_code_coverage: true
17 changes: 3 additions & 14 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,10 @@ sudo: false
language: php

php:
- 5.3
- 5.4
- 5.5
- 5.6

env:
- DB=MYSQL CORE_RELEASE=3.2

matrix:
include:
- php: 5.6
env: DB=MYSQL CORE_RELEASE=3
- php: 5.6
env: DB=MYSQL CORE_RELEASE=3.1
- php: 5.6
env: DB=PGSQL CORE_RELEASE=3.2
- DB=MYSQL CORE_RELEASE=3.6

before_script:
- composer self-update || true
Expand All @@ -30,4 +18,5 @@ before_script:
- composer install

script:
- vendor/bin/phpunit realme/tests/
- vendor/bin/phpunit --coverage-clover=coverage.clover realme/tests/
- bash <(curl -s https://codecov.io/bash) -f coverage.clover -R realme/
171 changes: 54 additions & 117 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,56 +9,79 @@ silverstripe-realme

Adds support to SilverStripe for authentication via [RealMe](https://www.realme.govt.nz/).

This module provides the foundation to support a quick integration for a SilverStripe application with RealMe as an
This module provides the foundation to support a quick integration for a SilverStripe application with RealMe as an
identity provider. This module requires extensive setup prior to being utilised effectively.

If integration with RealMe is wanted, it is best to get in touch with the RealMe team as early as possible. There are a
number of documents mentioned in this documentation that can only be found by accessing the RealMe Shared Workspace.
If integration with RealMe is wanted, it is best to get in touch with the RealMe team as early as possible. There are a
number of documents mentioned in this documentation that can only be found by accessing the RealMe Shared Workspace.
This can be accomplished by [getting in touch with the RealMe team](https://www.realme.govt.nz/realme-business/).

**Note:** Currently this module does not integrate with the `Member` functionality of SilverStripe. It is initially
intended to purely provide an authentication mechanism that can be extended by customers that require it. One such
extension would be to create standard SilverStripe `Member` records linked to a unique RealMe identifier, but that's
**Note:** Currently this module does not integrate with the `Member` functionality of SilverStripe. It is initially
intended to purely provide an authentication mechanism that can be extended by customers that require it. One such
extension would be to create standard SilverStripe `Member` records linked to a unique RealMe identifier, but that's
not currently built in.

## Work in progress
This module is a work in progress. It is generally considered stable, but should have a decent knowledge of RealMe or at
least standard SAML conventions in order to debug issues. Support is provided via the GitHub Issues for this module. If
you encounter any issues, please [open a new issue here](https://github.com/silverstripe/silverstripe-realme/issues).

## Requirements
This module doesn't have any specific requirements beyond those required by [SimpleSAMLphp](https://simplesamlphp.org):
the tool used to control authentication with the RealMe systems.
This module doesn't have any specific requirements beyond those required by
[onelogin/php-saml](https://github.com/onelogin/php-saml/blob/master/composer.json), the tool used to control
authentication with the RealMe systems.

These requirements are PHP 5.3, with the following required PHP extensions enabled: date, dom, hash, libxml, openssl,
pcre, SPL, zlib, and mcrypt php53-mcrypt
These requirements are PHP 5.6, with the following required PHP extensions enabled: date, dom, hash, libxml, openssl,
pcre, SPL, zlib, and mcrypt with the PHP bindings.

This module is designed to be run on a [CWP](https://www.cwp.govt.nz/) instance, and there are two sets of installation
This module is designed to be run on a [CWP](https://www.cwp.govt.nz/) instance, and there are two sets of installation
instructions - one for use on CWP, and one for generic use.

## Installation

See the [Installation section](docs/en/installation.md) for full details.
The module is best installed via Composer, by adding the below to your composer.json. For now, we need to specify a
custom version of the excellent onelogin/php-saml module to fix some XMLDSig validation errors with the RealMe XML
responses, hence the custom `repositories` section.

```
{
"require": {
"silverstripe/realme": "^2.0",
"onelogin/php-saml": "dev-fixes/realme-dsig-validation as 2.11.0"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/madmatt/php-saml.git"
}
]
}
```

Once installation is completed, configuration is required before this module will work - see below.

## Configuration of RealMe for your application

RealMe provide two testing environments and a production environment for you to integrate with. Access to these
environments is strictly controlled, and you must [contact the RealMe team](https://www.realme.govt.nz/realme-business/)
to gain access to the documentation required for these environments.
RealMe provide two testing environments and a production environment for you to integrate with. Access to these
environments is strictly controlled, and more information on these can be found on the [RealMe Developers site](https://developers.realme.govt.nz/how-to-integrate/).

See [configuration.md](docs/en/configuration.md) for environment and YML configuration required before the module can be
setup.

The configuration instructions above also steps you through setting up all three environments.
used.

## Providing RealMe login buttons

By default, the module integrates with the `Authenticator` class in SilverStripe, extending the standard SilverStripe
login form. If you want to provide your own separate login form just for RealMe, then the built-in templates can help
with this. They have been designed to integrate as cleanly as possible with SilverStripe templates, but it is up to you
By default, the module integrates with the `Authenticator` class in SilverStripe, extending the standard SilverStripe
login form. If you want to provide your own separate login form just for RealMe, then the built-in templates can help
with this. They have been designed to integrate as cleanly as possible with SilverStripe templates, but it is up to you
whether you use them, or roll your own.

See the [templates documentation](docs/en/templates.md) for more information on using or modifying these.

## Testing for authentication

The `RealMeService` service object allows you to inject authentication where-ever it is required. For example, let's
take a simple Controller that ensures that all users have a valid RealMe 'FLT' (a unique string that identifies a RealMe
The `RealMeService` service object allows you to inject authentication where-ever it is required. For example, let's
take a simple Controller that ensures that all users have a valid RealMe 'FLT' (a unique string that identifies a RealMe
account, but is not their username.

```php
Expand All @@ -67,111 +90,25 @@ class RealMeTestController extends Controller {
* @var RealMeService
*/
public $realMeService;

private static $dependencies = array(
'realMeService' => '%$RealMeService'
);

public function index() {
// enforceLogin will redirect the user to RealMe if they're not authenticated, or return true if they are
// authenticated with RealMe. It should only ever return 'false' if there was an initial error dealing with
// SimpleSAMLphp
// enforceLogin will redirect the user to RealMe if they're not authenticated, or return true if they are
// authenticated with RealMe. It should only ever return 'false' if there was an error initialising config
if($this->service->enforceLogin()) {
$userData = $this->service->getUserData();
printf("Congratulations, you're authenticated with a FLT of '%s'!", $userData->UserFlt);

printf("Congratulations, you're authenticated with a unique ID of '%s'!", $userData->SPNameID);
} else {
echo "There was an error while attempting to authenticate you.";
}
}
}
```

### MTS: [Messaging Test Environment](https://mts.realme.govt.nz/logon-mts/home)

The first environment is MTS. This environment is setup to allow testing of your code on your development environment.
In this environment, RealMe provide all SSL certificates required to communicate.

- Obtain access to RealMe and the Shared Workspace for MTS public/private development keys
- Fill out the "MTS checklist" available from the shared workspace and provide to the DIA RealMe team.
- Download 'Integration Bundle Login MTS' from the [RealMe Shared Workspace](https://see.govt.nz/realme/realme/Library/Forms/Library.aspx)
- Unpack the four certificates into the directory you've specified in `REALME_CERT_DIR` (ideally outside of your webroot)
- mts_mutual_ssl_idp.cer
- mts_mutual_ssl_sp.cer
- mts_mutual_ssl_sp.pem
- mts_saml_idp.cer
- mts_saml_sp.pem

- Run the RealMe build task to populate the configuration directories, metadata files, and authsources for MTS
```sake dev/tasks/RealMeSetupTask forEnv=mts```

#### MTS metadata example ####

```xml
<?xml version="1.0" encoding="UTF-8"?>
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" entityID="http://yourdomain.govt.nz/p-realm/s-name">
<SPSSODescriptor AuthnRequestsSigned="true"
WantAssertionsSigned="true"
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>
SSL certificate info
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</KeyDescriptor>
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</NameIDFormat>
<AssertionConsumerService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
Location="http://yourdomain.govt.nz/vendor/madmatt/simplesamlphp/www/module.php/saml/sp/saml2-acs.php/realme-mts" index="0"
isDefault="true">
</AssertionConsumerService>
</SPSSODescriptor>
<Organization>
<OrganizationName xml:lang="en-us">CWP Demo Organisation</OrganizationName>
<OrganizationDisplayName xml:lang="en-us">CWP Demo Organisation</OrganizationDisplayName>
<OrganizationURL xml:lang="en-us">http://yourdomain.govt.nz/</OrganizationURL>
</Organization>
<ContactPerson contactType="support">
<Company>SilverStripe</Company>
<GivenName>Jane</GivenName>
<SurName>Smith</SurName>
</ContactPerson>
</EntityDescriptor>
```

- Save the XML output from your task to an XML file, and upload this to the [MTS metadata upload](https://mts.realme.govt.nz/logon-mts/metadataupdate). Be sure to click continue and ok after uploading.

- include the session data realme/templates/Layout/RealMeSessionData.ss in your template, or reference session data
directly from any descendant of SiteTree $RealMeSessionData, or by using SiteConfig: SiteConfig::current_site_config()->RealMeSessionData();
- See the templates/Includes or templates/Layout directory for more information.

### ITE: Integration Test Environment

- Complete an integration to MTS.
- Obtain the ITE checklist from the RealMe shared document library and complete it.
- Publish your site to your CWP UAT environment with a working configuration for MTS and ITE
- Create a support ticket with [CWP Service desk](https://www.cwp.govt.nz/service-desk/new-request/) requesting access to ITE, and referencing information about your project, domain, and the ITE checklist

**Note** There will be charges associated with this, as operations will need generate and purchase the SSL certificates required for your domain, and provide them to DIA
To save time, both ITE and production certificates will be purchased at the same time.

If you wish do do this process yourself please see the [ssl-certs documentation](docs/en/ssl-certs.md)

### PROD: Production Environment

- Complete an integration to MTS and ITE.
- Obtain the Production checklist from the RealMe shared document library and complete it.
- Publish your site to your CWP UAT environment with a working configuration for MTS and ITE and a configuration for production
- Create a support ticket with [CWP Service desk](https://www.cwp.govt.nz/service-desk/new-request/) requesting access to production, and referencing information about your project, domain, and the production checklist

**Note** There will be charges associated with this, as operations will need generate and purchase the SSL certificates required for your domain, and provide them to DIA

If you wish do do this process yourself please see the [ssl-certs documentation](docs/en/ssl-certs.md)

## Known issues
The RelayState must be less than 80 bytes
## Appreciation

* Sincere thanks to Jackson (@jakxnz) for his work reviewing and updating pull requests.
6 changes: 5 additions & 1 deletion _config.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<?php
Authenticator::register_authenticator('RealMeAuthenticator');
// Only define RealMeAuthenticator module if at least one SS env var is set
if(defined('REALME_CERT_DIR')) {
Authenticator::register_authenticator('RealMeAuthenticator');
}


// defines the base directory, used by RealMeLoginForm to include javascript and css via Requirements
define('REALME_MODULE_PATH', basename(dirname(__FILE__)));
2 changes: 1 addition & 1 deletion _config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ RealMeLoginForm:
include_jquery: false
include_javascript: true
include_css: true
widget_theme: light
widget_theme: default
6 changes: 1 addition & 5 deletions _config/extensions.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
SiteTree:
extensions:
- RealMeDataExtension
- RealMeSiteTreeExtension
SiteConfig:
extensions:
- RealMeDataExtension
Security:
extensions:
- RealMeSecurityExtension
- RealMeSecurityExtension
34 changes: 0 additions & 34 deletions bootstrap.php

This file was deleted.

35 changes: 33 additions & 2 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,36 @@
# Changelog
# Change Log for silverstripe-realme

All notable changes to this project will be documented in this file.

This project adheres to [Semantic Versioning](http://semver.org/).
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [2.0.0] - 2018-05-01
- [Added]: RealMeService::currentRealMeUser() added for accessing the valid realme user from anywhere
- [Changed]: Module no longer uses the HTTP-Artifact binding, it now supports HTTP-Post binding only
- [Changed]: RealMeService now implements TemplateGlobalProvider
- [Changed]: RealMeService::getUserData() functionality moved to RealMeService::user_data()
- [Removed]: SiteTree::RealMeUser() use RealMeService::currentRealMeUser() instead
- [Removed]: RealMeDataExtension.php and associated SiteTree/SiteConfig extension definitions in extensions.yml
- [Removed]: RealMeSessionData.ss template helper (instead, use `$RealMeUser` directly from your templates)
- [Removed]: Dependency on convoluted SimpleSAMLphp module no longer necessary, now using onelogin/php-saml
- [Removed]: `REALME_MUTUAL_CERT_FILENAME`, `REALME_MUTUAL_CERT_PASSWORD`, `REALME_LOG_DIR`, `REALME_TEMP_DIR` constants
- [Deprecated]: `REALME_SIGNING_CERT_PASSWORD` should no longer be required, marked for removal


## [0.9.1] - 2016-04-28
- [Added]:
- [Changed]:
- [Deprecated]:
- [Security]:


## [0.9.0] - 2015-12-08
- [Added]: Initial release, utilising a forked & modified version of [SimpleSAMLphp](https://simplesamlphp.org/) to authenticate.


[Unreleased]: https://github.com/silverstripe/silverstripe-realme/compare/2.0.0...HEAD
[2.0.0]: https://github.com/silverstripe/silverstripe-realme/compare/1.0.0...2.0.0
[1.0.0]: https://github.com/silverstripe/silverstripe-realme/compare/0.9.1...1.0.0
[0.9.1]: https://github.com/silverstripe/silverstripe-realme/compare/0.9.0...0.9.1
[0.9.0]: Initial release
Loading

0 comments on commit b1cfcae

Please sign in to comment.