Skip to content

Commit

Permalink
Changing the hard-coded instance to a looser implementation (#4)
Browse files Browse the repository at this point in the history
Now configured by SP_URLS env var, and it must be a full URL to a metadata XML file.
Added docekr-compose.yml to help with maintaining this image
Added a internal refresh link to allow the metadata to be refreshed.
  • Loading branch information
codyfinegan authored Feb 15, 2023
1 parent d5372cf commit 6130995
Show file tree
Hide file tree
Showing 10 changed files with 218 additions and 96 deletions.
2 changes: 2 additions & 0 deletions .env.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# SP_URLS should be a full URL to a metadata file
SP_URLS=http://example.com/my/metadata.php
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ server.pem
server.crt
simplesamlphp-*/
simplesamlphp-*.tar.gz
.env
29 changes: 26 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ RUN mkdir /app && \
rm -rf metadata


FROM php:8.0-apache-buster
FROM php:8.0-apache-buster as dev

COPY --from=node_builder /app/samlphp/ /var/www/html/

Expand All @@ -35,14 +35,37 @@ RUN sed -ri -e 's!/var/www/html!/var/www/html/www/!g' /etc/apache2/sites-availab
sed -ri -e 's!:80>!:${LISTEN_PORT}>!g' /etc/apache2/sites-available/*.conf && \
sed -ri -e "s!usenewui!:usenewui2!g" /var/www/html/www/index.php # Hack to prevent auto-redirect to login page


# Generate the internal certificate
RUN cd /var/www/html/cert && \
openssl req -subj /C=NZ/ST=Wellington/L=Wellington/O=Totara/OU=Development/CN=server \
-newkey rsa:3072 -new -x509 -days 3652 -nodes -out server.crt -keyout server.pem && \
chown www-data server.* && \
chown www-data /var/www/html/cache

# Expose PHP errors to the CLI
RUN cp "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini" && \
echo "log_errors = On\nerror_log = /dev/stderr" > "$PHP_INI_DIR/conf.d/error.ini"

# Override the core module so we can alter the theme
RUN sed -ri -e 's!core:show_metadata.tpl.php!show_metadata.tpl.php!g' /var/www/html/modules/core/www/show_metadata.php && \
sed -ri -e 's!core:frontpage_federation.tpl.php!frontpage_federation.tpl.php!g' /var/www/html/modules/core/www/frontpage_federation.php

RUN mkdir -p /var/www/html/modules/totara/locales/en/LC_MESSAGES && \
cp /var/www/html/modules/core/locales/en/LC_MESSAGES/core.po /var/www/html/modules/totara/locales/en/LC_MESSAGES/totara.po

FROM php:8.0-apache-buster as prod

ENV SIMPLESAMLPHP_CONFIG_DIR /var/www/config/
ENV LISTEN_PORT 8089

# Default expose port
EXPOSE 8089

RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"

COPY --from=dev /var/www/html /var/www/html
COPY --from=dev /etc/apache2/ /etc/apache2/

COPY config/ /var/www/config/
COPY metadata/ /var/www/html/metadata/
COPY modules/totara/ /var/www/html/modules/totara/
COPY modules/totara/themes/ /var/www/html/modules/totara/themes/
30 changes: 16 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ There are two environmental variables that need to be set in the Docker image fo

| Variable | Description |
|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| `TOTARA_URL` | The URLs of the Totara instance to mark as trusted. Eg: `http://my-totara-instance.local,http://another-site`. Accept multiple if seperated by a comma. |
| `SP_URLS` | The URLs of the Service Provider instances to register. Eg: `http://my-instance.local/path/to/metadata.php,http://another-site/path/to/metadata.php`. Accept multiple if seperated by a comma. **Must be the full URL to the metadata**. |
| `LISTEN_PORT` | The port used to access the service. Defaults to `8089`. |
| `SITE_TITLE` | Override the default site title, used when running multiple to tell them apart. |

Expand All @@ -22,23 +22,23 @@ There are two environmental variables that need to be set in the Docker image fo
docker pull totara/simple-saml-test:latest

# Start the service
docker run --rm -p 8089:8089 -e LISTEN_PORT=8089 -e TOTARA_URL={YOUR_TOTARA_URL} -it totara/simple-saml-test:latest
docker run --rm -p 8089:8089 -e LISTEN_PORT=8089 -e SP_URLS=http://{YOUR_SP_INSTANCE}/path/to/metadata.php -it totara/simple-saml-test:latest
```

Set `{YOUR_TOTARA_URL}` to the domain of your Totara instance.
Set `{YOUR_SP_INSTANCE}` to the domain of your service provider instance (typically a Totara instance but technically could work with any SAML site).

If your Totara instance domain isn't publicly resolvable (such as it's a test environment) you will need to teach the
If your domain isn't publicly resolvable (such as it's a test environment) you will need to teach the
Domain/IP to this docker image.

```shell
# Totara instance is running directly on the host machine
docker run --add-host={YOUR_TOTARA_URL}:host-gateway ... -it totara/simple-saml-test:latest
# Instance is running directly on the host machine
docker run --add-host={YOUR_SP_INSTANCE}:host-gateway ... -it totara/simple-saml-test:latest

# Totara instance is somewhere else, replace the domain & IP
docker run --add-host={YOUR_TOTARA_URL}:{IP_OF_SITE} ... -it totara/simple-saml-test:latest
# Instance is somewhere else, replace the domain & IP
docker run --add-host={YOUR_SP_INSTANCE}:{IP_OF_SITE} ... -it totara/simple-saml-test:latest
```

Once started, you can access the service via `http://localhost:{LISTEN_PORT}`.
Once started, you can access the service via `http://localhost:{LISTEN_PORT}` (defaults to 8089).

### Using Totara Docker Dev

Expand All @@ -59,15 +59,15 @@ services:
ports:
- "8089:8089"
environment:
- TOTARA_URL=http://${SAML_TOTARA_URL:-totara74}
- SP_URLS=${SAML_TOTARA_URLS}
- LISTEN_PORT=8089
- SITE_TITLE="Testing"
```
Edit your `.env` file and add the following line.

```dotenv
SAML_TOTARA_URL=totara74
SAML_TOTARA_URLS=http://totara74/path/to/sp/metadata.php
```

In it place the name of the Totara instance you're using to connect.
Expand All @@ -80,9 +80,11 @@ Try and access http://saml2:8089 and confirm you see the test environment.

*Important*: The URL that Totara and the URL that you access the site on via your browser must be the same.

The path to the metadata file depends on what SAML plugin you are using which is why it's not specified here.

## Developing This Image

* Fork this repo, create a new branch and make the change.
* Test your image by building it directly with `docker build -t test_saml2 .`
* Try running the image with the commands above, swapping out with the test image name.
* If it's all good, submit a pull request for the change.
* Test using the built-in docker image with docker-compose, you can run `docker-compose up --build dev` to run the dev version with the config/metadata/modules folders volumed in (real time changes).
* Once everything is all good, test with the prod version `docker-compose up --build prod`.
* If it is all good, submit a pull request for the change.
18 changes: 8 additions & 10 deletions config/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

$env = getenv('SAML_TRUSTED');
$trusted = $env ? [$env] : [];
$trusted = $env ? [$env] : null;
$title = getenv('SITE_HEADER') ?: 'SimpleSAML Test';

$config = [
Expand Down Expand Up @@ -203,7 +203,7 @@
* https://idp.example.org/ssp/, then
* http://idp.example.org/ssp/module.php/core/postredirect.php must be accessible.
*/
'enable.http_post' => false,
'enable.http_post' => true,

/*
* Set the allowed clock skew between encrypting/decrypting assertions
Expand Down Expand Up @@ -255,7 +255,7 @@
'debug' => [
'saml' => true,
'backtraces' => true,
'validatexml' => false,
'validatexml' => true,
],

/*
Expand All @@ -266,7 +266,7 @@
* the error to 'technicalcontact_email'.
*/
'showerrors' => true,
'errorreporting' => true,
'errorreporting' => false,

/*
* Custom error show function called from SimpleSAML\Error\Error::show.
Expand Down Expand Up @@ -295,8 +295,8 @@
* Options: [syslog,file,errorlog,stderr]
*
*/
'logging.level' => SimpleSAML\Logger::NOTICE,
'logging.handler' => 'syslog',
'logging.level' => SimpleSAML\Logger::ERR,
'logging.handler' => 'stderr',

/*
* Specify the format of the logs. Its use varies depending on the log handler used (for instance, you cannot
Expand Down Expand Up @@ -796,9 +796,7 @@
* Languages available, RTL languages, and what language is the default.
*/
'language.available' => [
'en', 'no', 'nn', 'se', 'da', 'de', 'sv', 'fi', 'es', 'ca', 'fr', 'it', 'nl', 'lb',
'cs', 'sl', 'lt', 'hr', 'hu', 'pl', 'pt', 'pt-br', 'tr', 'ja', 'zh', 'zh-tw', 'ru',
'et', 'he', 'id', 'sr', 'lv', 'ro', 'eu', 'el', 'af', 'zu', 'xh', 'st',
'en'
],
'language.rtl' => ['ar', 'dv', 'fa', 'ur', 'he'],
'language.default' => 'en',
Expand Down Expand Up @@ -901,7 +899,7 @@
* change. If you don't want to check the source templates for every request,
* set it to false.
*/
'template.auto_reload' => false,
'template.auto_reload' => true,

/*
* Set this option to true to indicate that your installation of SimpleSAMLphp
Expand Down
26 changes: 26 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
version: "3.7"

services:
dev:
build:
context: .
target: dev
ports:
- "8099:8099"
environment:
- LISTEN_PORT=8099
- SITE_HEADER=Dev SAML Image
- SP_URLS=$SP_URLS
volumes:
- ./metadata/:/var/www/html/metadata/
- ./config/:/var/www/config/
- ./modules/totara/themes/:/var/www/html/modules/totara/themes/

prod:
build: .
ports:
- "8098:8098"
environment:
- LISTEN_PORT=8098
- SITE_HEADER=Prod SAML Image
- SP_URLS=$SP_URLS
32 changes: 23 additions & 9 deletions metadata/saml20-sp-remote.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,19 @@
/*
* Example SimpleSAMLphp SAML 2.0 SP
*/
$totara_instance = getenv('TOTARA_URL');
$sp_instance = getenv('SP_URLS');

if (empty($totara_instance)) {
die('No TOTARA_URL environment variable was defined.');
if (empty($sp_instance)) {
echo 'No SP_URLS environment variable were defined.';
return;
}

$instances = [];
if (str_contains($totara_instance, ',')) {
$instances = explode(',', $totara_instance);
if (str_contains($sp_instance, ',')) {
$instances = explode(',', $sp_instance);
array_walk($instances, fn($instance) => trim($instance));
} else {
$instances[] = $totara_instance;
$instances[] = $sp_instance;
}

$config = \SimpleSAML\Configuration::getInstance();
Expand Down Expand Up @@ -57,11 +58,11 @@
return file_get_contents($filename);
};

$reset = isset($_GET['refresh_metadata']) && $_GET['refresh_metadata'] === 'y';
$refresh = isset($_GET['refresh_metadata']) && (in_array($_GET['refresh_metadata'], ['y', 'yes', 't', 'true', 1, '1'], true));

$metadata = [];
foreach ($instances as $totara_instance) {
$xml = $load_metadata($totara_instance . '/auth/saml2/sp/metadata.php', $reset);
foreach ($instances as $sp_instance) {
$xml = $load_metadata($sp_instance, $refresh);

// No data, silently ignore it.
if (!$xml) {
Expand All @@ -75,6 +76,13 @@
foreach ($entities as &$entity) {
$data = $entity->getMetadata20SP();

// Hack to get en-US behaving
foreach ($data as $key => $value) {
if (is_array($value) && isset($value['en-US'])) {
$data[$key]['en'] = $value['en-US'];
}
}

if (isset($data['entityDescriptor'])) {
unset($data['entityDescriptor']);
}
Expand All @@ -89,3 +97,9 @@

$metadata = array_merge($metadata, $output['saml20-sp-remote']);
}

if ($refresh) {
$new_url = str_replace('refresh_metadata=y', 'refreshed=done', $_SERVER['REQUEST_URI']);
header('Location: ' . $new_url);
exit;
}
60 changes: 0 additions & 60 deletions modules/totara/themes/simple/default/_header.twig
Original file line number Diff line number Diff line change
Expand Up @@ -25,66 +25,6 @@
<p><a href="/module.php/core/frontpage_welcome.php">Admin Backend</a></p>
</div>
</div>
{% if not hideLanguageBar %}
<div class="right">
<a href="" id="menuLink" class="menu-link hide-for-large">
<span class="fa fa-globe fa-2x" aria-hidden="true"></span>
</a>
</div>
<div id="languagebar" class="hide-for-large">
<div id="menu">
<div class="pure-menu">
<ul class="pure-menu-list">
{% for key, lang in languageBar %}
{% if key == currentLanguage %}
<li><a class="pure-menu-heading" href="#">{{ lang.name }}</a><li>
{% else %}
{% if lang.url %}
<li class="pure-menu-item"><a href="{{ lang.url }}
{%- if queryParams %}&{% endif %}
{%- for name, value in queryParams %}
{%- if not loop.first %}&{% endif %}
{%- if value %}{{ name }}={{ value }}{% else %}{{ name }}{% endif %}
{%- endfor %}" class="pure-menu-link">{{ lang.name }}</a></li>
{% endif %}
{% endif %}
{% endfor %}
</ul>
</div>
</div>
</div>
{% endif %}
<div class="right show-for-large">
<div class="v-center language-bar">
<form id="language-form" class="pure-form" method="get">
{% if not hideLanguageBar %}
<div id="languagebar">
{% for name, value in queryParams %}
{% if value %}
<input type="hidden" name="{{ name }}" value="{{ value }}" />
{% else %}
<input type="hidden" name="{{ name }}" />
{% endif %}
{% endfor %}
<select aria-label="{% trans %}Language{% endtrans %}" class="pure-input-1-4 language-menu" name="language" id="language-selector">
{% for key, lang in languageBar %}
{% if key == currentLanguage %}
<option value="{{ key }}" selected="selected">&#xf0ac; {{ lang.name }}</option>
{% else %}
<option value="{{ key }}">{{ lang.name }}</option>
{% endif %}
{% endfor %}
</select>
<noscript>
<button type="submit" class="pure-button">
<i class="fa fa-arrow-right"></i>
</button>
</noscript>
</div>
{% endif %}
</form>
</div>{# language bar #}
</div>{# show-for-large #}
</div>{# wrap #}
</div>{# header #}

Loading

0 comments on commit 6130995

Please sign in to comment.