Skip to content

Commit

Permalink
Merge pull request #55 from creative-commoners/pulls/4.0/no-undefined…
Browse files Browse the repository at this point in the history
…-var

PHP 7.2+ support
  • Loading branch information
dnsl48 committed Feb 25, 2020
2 parents 9a96d48 + 8a3cfc3 commit f9d98ac
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 88 deletions.
89 changes: 48 additions & 41 deletions docs/en/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@ The following values need to be defined in your `.env` file for **all** environm
| `REALME_CERT_DIR` | /sites/realme-dev/secure/certs | Directory where all certificates will reside. All certificates should be placed here. Needs to be readable (but ideally not writeable) by the web server user. |
| `REALME_SIGNING_CERT_FILENAME` | mts_saml_sp.pem | Name of the SAML secure signing certificate for the required environment. For MTS, this is provided by RealMe, and is available on the RealMe developers site. |

In addition to these, YML configuration is required to specify some values that should be consistently applied across
It is important to note that the file referred to by `REALME_SIGNING_CERT_FILENAME` is expected to be in [PEM format](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail), containing both the private key and the certificate (and optionally any intermediary certificates). If your files are not structured this way it can be easily created by e.g. `cat yoursite.crt yoursite.ca-bundle yoursite.key > yoursite.pem` provided each file has the appropirate `-----BEGIN *-----` and `-----END *-----` headers & footers.

In addition to these environment variables, YML configuration is required to specify some values that should be consistently applied across
environments. These are noted below.

Create a file in your project called for example `mysite/_config/realme.yml`. In this file, specify the following, with
Create a file in your project called for example `mysite/_config/realme.yml`. In this file, specify the following, with
appropriate values set. Examples are given below, but should be evaluated for your own application.

Note that the below configuration assumes that you are using the `SS_ENVIRONMENT_TYPE` const correctly on your
development, staging/test and production environments.
Note that the below configuration assumes that you are using the `SS_ENVIRONMENT_TYPE` const correctly on your
development, staging/test and production environments.

```yaml

Expand Down Expand Up @@ -44,12 +46,12 @@ SilverStripe\RealMe\RealMeService:
metadata_contact_support_company: "Your Company"
metadata_contact_support_firstnames: "Your"
metadata_contact_support_surname: "Name"

SilverStripe\RealMe\Authenticator\LoginForm:
service_name_1: "this website"
service_name_2: "this website"
service_name_3: "this website"

---
Name: realmetest
Only:
Expand All @@ -59,7 +61,7 @@ After:
---
SilverStripe\RealMe\RealMeService:
realme_env: 'ite'

---
Name: realmeprod
Only:
Expand All @@ -69,7 +71,7 @@ After:
---
SilverStripe\RealMe\RealMeService:
realme_env: 'prod'

```

The value you set for `realme_env` must be one of 'mts', 'ite' or 'prod'.
Expand All @@ -79,14 +81,14 @@ The value you set for `integration_type` must be one of 'login' or 'assert'.
The values you set for `sp_entity_ids` should conform to the RealMe standard for entity IDs. In summary, the
domain should be relevant to the agency, the first part of the path should be the privacy realm name, and
the second part of the path should be the service name.

The values for `service_name_1`, `service_name_2` and `service_name_3` should fit in these sentences:

* `service_name_1`: "To access the [online service], you need a RealMe login."
* `service_name_2`: "To log in to [this service] you need a RealMe login."
* `service_name_3`: "[This service] uses RealMe login to secure and protect your personal information."

**Note:** None of these are required for the assert form, as they are not used (it only uses organisation name, which is
**Note:** None of these are required for the assert form, as they are not used (it only uses organisation name, which is
taken from the `metadata_organisation_name` config value instead.

**Note:** the service name cannot be more than 10 characters in length, or the validation will fail.
Expand All @@ -110,18 +112,18 @@ If you are wanting to test SMS tokens on the ITE environment, further documentat

The RealMe system consists of three separate environments - MTS, ITE and Production.

In MTS, you confirm that your setup is correct, and you can correctly parse all the different types of messages that
In MTS, you confirm that your setup is correct, and you can correctly parse all the different types of messages that
RealMe may pass back to your application.

In ITE, which is equivalent to a pre-prod or staging environment, you confirm that your website will work correctly when
deployed to production, using your own secure certificates, and any custom configuration (e.g. `authn_context` values)
In ITE, which is equivalent to a pre-prod or staging environment, you confirm that your website will work correctly when
deployed to production, using your own secure certificates, and any custom configuration (e.g. `authn_context` values)
set.

In production, you allow real users to use RealMe for authentication.

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

The development environment is known as MTS. This environment is setup to allow testing of your code on your development
The development environment is known as 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.

- Review the documentation on the 'Try it out now' page on the [RealMe Developers site](https://developers.realme.govt.nz/try-it-out-now/).
Expand All @@ -141,16 +143,16 @@ environment. In this environment, RealMe provide all SSL certificates required t
- Either use the `$RealMeLoginForm` global template variable or add the `RealMeAuthenticator` and access `/Security/login`.
- Once authenticated, you can access user data from templates using `$RealMeUser` (e.g. `$RealMeUser.SPNameID`), or in a controller by using `RealMeService::currentRealMeUser()`.

If you are developing locally, note that the module enforces your environment to be configured for https. If you don't
have this setup by default, [ngrok](https://ngrok.com/download) is a nice, easy to use tool that provides this
functionality. You just run ngrok, and copy the https URL that it gives you - this will let you access your site
protected via https, however you will need to ensure you set the `SS_TRUSTED_PROXY_IPS` const in your
_ss_environment.php, e.g. `define('SS_TRUSTED_PROXY_IPS', '*');` so that we know that ngrok is trust-worthy and allowed
If you are developing locally, note that the module enforces your environment to be configured for https. If you don't
have this setup by default, [ngrok](https://ngrok.com/download) is a nice, easy to use tool that provides this
functionality. You just run ngrok, and copy the https URL that it gives you - this will let you access your site
protected via https, however you will need to ensure you set the `SS_TRUSTED_PROXY_IPS` const in your
_ss_environment.php, e.g. `define('SS_TRUSTED_PROXY_IPS', '*');` so that we know that ngrok is trust-worthy and allowed
to pass http traffic as https.

If you do this, ngrok will give you a random URL each time you start it, which means that you will need to change the
above YML configuration and re-integrate to MTS every time you restart ngrok. Alternatively, set this up on a
development server that has the capability to perform SSL communication natively. You can use self-signed certificates
If you do this, ngrok will give you a random URL each time you start it, which means that you will need to change the
above YML configuration and re-integrate to MTS every time you restart ngrok. Alternatively, set this up on a
development server that has the capability to perform SSL communication natively. You can use self-signed certificates
if required.

You should now be able to proceed to testing the standard login form, or [using the RealMe templates](templates.md).
Expand All @@ -170,11 +172,11 @@ You should now be able to proceed to testing the standard login form, or [using
- Follow the steps as for the ITE environment above, but creating an integration request for production rather than ITE.

## More complex environments
If you are working with multiple website environments (e.g. multiple test sites or similar), you will encounter issues
using the basic configuration system above, because you will want multiple different websites to point to different RealMe
integrations (e.g. 'test1' website points to MTS, while 'test2' and 'staging' websites both point to ITE). To do this,
each website installation needs a separate RealMe integration (that is, a different SP Entity ID). Once these are
configured with RealMe, you can switch them out by modifying configuration. For now (until a solution to #26 is built),
If you are working with multiple website environments (e.g. multiple test sites or similar), you will encounter issues
using the basic configuration system above, because you will want multiple different websites to point to different RealMe
integrations (e.g. 'test1' website points to MTS, while 'test2' and 'staging' websites both point to ITE). To do this,
each website installation needs a separate RealMe integration (that is, a different SP Entity ID). Once these are
configured with RealMe, you can switch them out by modifying configuration. For now (until a solution to #26 is built),
the best solution is to use `RealMeService::config()->set()` for SS4. **Note:** This can cause a significant performance impact in SilverStripe 4, as by default YML configuration is immutable, and this overrides this which is not ideal. Use this only if it's really necessary, and ensure that the majority of your environments are still configured via YML as normal.

In your app/\_config.php:
Expand Down Expand Up @@ -207,37 +209,42 @@ if ($changed) {
This will allow you, if necessary, to re-configure the module in real-time based on the website environment. Note that you will still need to deploy the correct private/public keypairs to the correct servers etc.

## Syncing Realme with SilverStripe members
After logging in the module can sync the attributes returned from RealMe (depending on your assertion type) and sync the
details with the appropriate members.

To setup syncing, you must have the `RealMeMemberExtension` enabled on Member (or subclass) and then tell the module to
sync with the database via the following configuration in realme.yml. You can also include
`login_member_after_authentication` which will automatically login a user (as a SilverStripe `Member` object) after
successful RealMe authentication.
After logging in the module can sync the attributes returned from RealMe (depending on your assertion type) and sync the
details with the appropriate members. This is not available for `assert` type authenticaiton as the unique identifier is
valid only for that session, meaning each time a user logged in they would have a new `Member` object created for them,
and any associated historic user activity would be lost to them.

To setup syncing, you **must** be using the `login` type of authentication and have the `RealMeMemberExtension` enabled
on `Member` (or a subclass of it) and then tell the module to sync with the database via the following configuration in
realme.yml. You can also include `login_member_after_authentication` which will automatically login a user (as a
Silverstripe `Member` object) after successful RealMe authentication.

```yaml
SilverStripe\Security\Member:
extensions:
- SilverStripe\RealMe\Extension\MemberExtension

SilverStripe\RealMe\RealMeService:
sync_with_local_member_database: true
login_member_after_authentication: true
```

Run a `dev/build` and after a valid RealMe login, a new member will be synced based on the RealMe FLT or FIT. If not
found, a new member will be created.
Run a `dev/build` to ensure the configuration changes are accounted for.

When a RealMe login completes with success, a new member will be synced based on the RealMe FLT. If no member matching the
FLT is found, a new member will be created. _Note this is not supported for `assert`, as the FIT is transient (changes each
time a member logs in)._

### UAT and production environments

The SAML signing security certificates must be purchased by the agency. If you are hosting on the Common Web Platform,
the [CWP Service desk](https://www.cwp.govt.nz/service-desk/new-request/) can help generating the certificate signing
request (CSR) and installing the certificate once purchased by the agency. More information on the requirements can be
The SAML signing security certificates must be purchased by the agency. If you are hosting on the Common Web Platform,
the [CWP Service desk](https://www.cwp.govt.nz/service-desk/new-request/) can help generating the certificate signing
request (CSR) and installing the certificate once purchased by the agency. More information on the requirements can be
found on the [RealMe developers site](https://developers.realme.govt.nz/how-realme-works/certificate-requirements/).

#### When you're hosting on CWP

For UAT and production environments, the above environment consts will be defined for you by CWP Operations once the
For UAT and production environments, the above environment consts will be defined for you by CWP Operations once the
certificates have been purchased and installed. [Create a Service Desk ticket](https://www.cwp.govt.nz/service-desk/new-request/)
to request the start of this process.

Expand Down
11 changes: 9 additions & 2 deletions src/Authenticator/LoginHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,14 @@ public function acs(HTTPRequest $request)
$session->set('RealMe.SessionData', serialize($authData));
$session->set('RealMe.OriginalResponse', $request->postVar('SAMLResponse'));

if (RealMeService::config()->get('sync_with_local_member_database') === true) {
$realMeServiceConfig = RealMeService::config();
if ($realMeServiceConfig->get('sync_with_local_member_database') === true) {
if ($realMeServiceConfig->get('integration_type') === RealMeService::TYPE_ASSERT) {
throw new RealMeException(
'NameID is transient for ASSERT - it cannot be used to identify a user between sessions.',
RealMeException::PERSISTING_TRANSIENT_ID
);
}
if (!Member::has_extension(MemberExtension::class)) {
throw new RealMeException(
'The RealMe MemberExtension should be used when syncing with a local database',
Expand All @@ -114,7 +121,7 @@ public function acs(HTTPRequest $request)
if (!$authData->getMember()->isInDb()) {
$authData->getMember()->write();
}
if (RealMeService::config()->get('login_member_after_authentication') === true) {
if ($realMeServiceConfig->get('login_member_after_authentication') === true) {
Injector::inst()->get(AuthenticationHandler::class)->login($authData->getMember());
}
}
Expand Down
1 change: 1 addition & 0 deletions src/Exception.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ class Exception extends BaseException
const MISSING_AUTHN_RESPONSE = 6;
const NOT_AUTHENTICATED = 7;
const MISSING_MEMBER_EXTENSION = 8;
const PERSISTING_TRANSIENT_ID = 9;
}

0 comments on commit f9d98ac

Please sign in to comment.