Skip to content

Commit

Permalink
ADD simplesamlphp extension support
Browse files Browse the repository at this point in the history
SimpleSAMLphp extension requires a SimpleSAMLphp SP application to be
run separately. It also expects the SimpleSAMLphp SP to be on the same
server and will import from the SP. To duplicate this, I'm running the
SP as a separate container but the codebase is in a shared volume that
is mounted in both the wiki and the SP container.

To make the shared volume work, the SP container initially has its
codebase in /var/www/simplesamlphp-base which will be copied into the
shared volume /var/www/simplesamlphp on startup.

There is a local IDP counterpart that is only used for dev testing. In
order to make setup easy, I've committed a set of certs of the SP in
docker/simplesamlphp/sp/cert/ which is mounted into the SP. Warning:
These certs are meant only for local dev and must not be used in any
kind of prod environment.

Check README for details on the SP's env vars and how to generate your
own cert/key.

Note that the SP is using an older version of SimpleSAMLphp that still
supports php7.4. Newer versions require php8 or above and cannot be used
with our current wiki build as its using php7.4.

The SimpleSAMLphp extension adds its own logout button. The default
logout button ends up being a non-functional duplicate. I think the
extension is supposed to remove this duplicate logout button but I get
the feeling the method they use only works for newer vector skin. I've
manually deleted the duplicate logout button.

Configuration of the SimpleSAMLphp extension is largely the same as it's
also a PluggableAuth module. The main difference is that we configure
the SAML attribute mapping there. Some of the attributes are used only
by UBCAuth.

Minor Mediawiki version upgrade 1.39.7 to 1.39.8.

Remove deprecated LDAP hooks in CustomHooks.php. I think the remaining
Caliper hook should only activate if the UBCAuth extension is enabled.
  • Loading branch information
ionparticle committed Jul 16, 2024
1 parent b5b9d2b commit 4e6cfcc
Show file tree
Hide file tree
Showing 22 changed files with 3,913 additions and 104 deletions.
37 changes: 3 additions & 34 deletions CustomHooks.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,8 @@
use IMSGlobal\Caliper\entities\agent\Person;
use CaliperExtension\caliper\CaliperSensor;

# If LDAP environment variables are defined, enable additional customization
if (getenv('LDAP_SERVER') || getenv('LDAP_BASE_DN') || getenv('LDAP_SEARCH_STRINGS') || getenv('LDAP_SEARCH_ATTRS')) {

// Remove the change password link from Preferences page.
// ref: https://stackoverflow.com/questions/16893589/prevent-users-from-changing-their-passwords-in-mediawiki
// note: many of the hooks mentioned in the stackoverflow post above have been deprecated
$wgHooks['GetPreferences'][] = 'RemovePasswordChangeLink';
function RemovePasswordChangeLink($user, &$preferences) {
unset($preferences['password']);
return true;
}

///////////////////////////////////////////////////////////////////////////////

$wgHooks['AuthChangeFormFields'][] = 'ChangeAuthFormFields';
function ChangeAuthFormFields($requests, $fieldInfo, &$formDescriptor, $action) {
global $wgCookiePrefix;

// Remove "local" domain option from login page
unset($formDescriptor['domain']['options']['local']);

// Remove username from cookies to avoid prefilling the field with wiki username.
// Users should authenticate with usernames in LDAP.
unset($_COOKIE[$wgCookiePrefix.'UserName']);

return true;
}

# if Caliper is setup, use a custom actor with puid from LDAP
if (filter_var( getenv( 'UBC_AUTH_ENABLED' ), FILTER_VALIDATE_BOOLEAN )) {
# if Caliper is setup, use a custom actor with puid
if (getenv('CALIPER_HOST') && getenv('CALIPER_API_KEY')) {
$wgHooks['SetCaliperActorObject'][] = 'SetCaliperActor';

Expand Down Expand Up @@ -74,8 +47,4 @@ function SetCaliperActor(&$actor, &$user) {
return true;
}
}
} // end customization for LDAP authentication

#####################
## End LDAP customization
#####################
}
26 changes: 11 additions & 15 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM php:7.4-apache

ENV WIKI_VERSION_MAJOR_MINOR=1.39
ENV WIKI_VERSION_BUGFIX=7
ENV WIKI_VERSION_BUGFIX=8
ENV WIKI_VERSION=$WIKI_VERSION_MAJOR_MINOR.$WIKI_VERSION_BUGFIX
ENV WIKI_VERSION_STR=1_39

Expand All @@ -18,8 +18,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
git \
imagemagick \
unzip \
vim.tiny \
vim \
libonig-dev \
# for simpleSAMLphp
libzip-dev \
# for TimedMediaHandler
ffmpeg \
&& rm -rf /var/lib/apt/lists/* \
Expand All @@ -28,16 +30,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
&& ln -s /usr/lib/x86_64-linux-gnu/liblber.so /usr/lib/liblber.so \
&& docker-php-source extract

# pcntl for Scribunto
RUN docker-php-ext-install -j$(nproc) mbstring xml intl mysqli ldap pcntl opcache calendar \
&& docker-php-ext-configure gd --with-freetype=/usr/include/ --with-jpeg=/usr/include/ \
&& docker-php-ext-install -j$(nproc) gd \
&& docker-php-source delete \
&& pecl install imagick-3.4.3 \
&& pecl install redis \
&& docker-php-ext-enable imagick mysqli redis \
&& a2enmod rewrite \
&& rm -rf /tmp/pear
# install php extensions
# pcntl for Scribunto, zip for SimpleSAMLphp
COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/
RUN install-php-extensions mbstring xml intl mysqli ldap pcntl opcache calendar zip imagick redis memcached

RUN a2enmod rewrite

WORKDIR /var/www/html

Expand All @@ -61,13 +59,11 @@ COPY robots.txt /var/www/html/robots.txt

# composer won't load plugins if we don't explicitly allow executing as root
ENV COMPOSER_ALLOW_SUPERUSER=1
# FIXME temp hack to use lastest composer 1.x. composer 2.x version will break wikimedia/composer-merge-plugin
RUN curl -L https://getcomposer.org/installer | php \
#RUN curl -L https://getcomposer.org/composer-1.phar --output composer.phar \
&& php composer.phar install --no-dev

RUN EXTS=`curl https://extdist.wmflabs.org/dist/extensions/ | awk 'BEGIN { FS = "\"" } ; {print $2}'` \
&& for i in SmiteSpam VisualEditor Scribunto LiquidThreads Cite WikiEditor LDAPProvider PluggableAuth LDAPAuthentication2 ParserFunctions TemplateData InputBox Widgets Variables RightFunctions PageInCat CategoryTree LabeledSectionTransclusion UserPageEditProtection Quiz Collection DeleteBatch LinkTarget HitCounters Math 3D MultimediaViewer TimedMediaHandler; do \
&& for i in SmiteSpam VisualEditor Scribunto LiquidThreads Cite WikiEditor LDAPProvider PluggableAuth LDAPAuthentication2 ParserFunctions TemplateData InputBox Widgets Variables RightFunctions PageInCat CategoryTree LabeledSectionTransclusion UserPageEditProtection Quiz Collection DeleteBatch LinkTarget HitCounters Math 3D MultimediaViewer TimedMediaHandler SimpleSAMLphp; do \
FILENAME=`echo "$EXTS" | grep ^${i}-REL${WIKI_VERSION_STR}`; \
echo "Installing https://extdist.wmflabs.org/dist/extensions/$FILENAME"; \
curl -Ls https://extdist.wmflabs.org/dist/extensions/$FILENAME | tar xz -C /var/www/html/extensions; \
Expand Down
55 changes: 55 additions & 0 deletions LocalSettings.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,13 @@ function loadenv($envName, $default = "") {
ini_set( 'display_errors', 1 );
$wgShowExceptionDetails = true;
$wgCacheDirectory = false;
$wgCachePages = false;
$wgDebugLogFile = "/tmp/mw-debug-{$wgDBname}.log";
$wgDebugLogGroups = [
"SimpleSAMLphp" => "/tmp/mw-debug-SimpleSAMLphp.log",
"PluggableAuth" => "/tmp/mw-debug-PluggableAuth.log",
"UBCAuth" => "/tmp/mw-debug-UBCAuth.log",
];
}

## Shared memory settings
Expand Down Expand Up @@ -617,6 +623,55 @@ function loadenv($envName, $default = "") {
}
}

if (filter_var( getenv( 'SIMPLESAMLPHP_ENABLED' ), FILTER_VALIDATE_BOOLEAN )) {
wfLoadExtension( 'PluggableAuth' );
wfLoadExtension( 'SimpleSAMLphp' );
$wgSimpleSAMLphp_InstallDir = '/var/www/simplesamlphp';
$wgPluggableAuth_EnableLocalLogin = false;

$wgPluggableAuth_Config['CWL Log In'] = [
'plugin' => 'SimpleSAMLphp',
'data' => [
'authSourceId' => 'wiki-sp',
# standardized attributes with commonly used OIDs
# uid attribute, which in Shib is the CWL login name
'usernameAttribute' => 'urn:oid:0.9.2342.19200300.100.1.1',
# displayName attribute, aka preferred name
'realNameAttribute' => 'urn:oid:2.16.840.1.113730.3.1.241',
# mail attribute, email address
'emailAttribute' => 'urn:oid:0.9.2342.19200300.100.1.3',
# eduPersonAffiliation, an array of (staff, student, faculty, etc)
'eduPersonAffiliationAttribute' => 'urn:oid:1.3.6.1.4.1.5923.1.1.1.1',
# non-standard attributes, uncertain OIDs
# ubc's puid
'puidAttribute' => 'ubcEduCwlPuid',
]
];

# disable local wiki account creation page
$wgGroupPermissions['*']['createaccount'] = false;
# allow auto creation
$wgGroupPermissions['*']['autocreateaccount'] = true;

# disable password resets entirely
# ref: https://www.mediawiki.org/wiki/Manual:$wgPasswordResetRoutes
$wgPasswordResetRoutes = array(
'username' => false,
'email' => false,
);

# enable local properties so users can edit their real name and email
# ref: https://www.mediawiki.org/wiki/Extension:PluggableAuth
$wgPluggableAuth_EnableLocalProperties = true;

if ( filter_var( getenv( 'UBC_AUTH_ENABLED' ), FILTER_VALIDATE_BOOLEAN ) ) {
wfLoadExtension( 'UBCAuth' );
$wgSimpleSAMLphp_MandatoryUserInfoProviders['username'] = [
'class' => 'MediaWiki\Extension\UBCAuth\UsernameInfoProvider'
];
}
}


if (getenv('MEDIAWIKI_EXTENSIONS') && strpos(getenv('MEDIAWIKI_EXTENSIONS'), 'Scribunto') !== false) {
# Scribunto
Expand Down
49 changes: 48 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,54 @@ docker-compose down
docker-compose up -d
```

## Adding new LDAP users
### Setting up SAML2 auth using the SimpleSAMLphp Extension.

The dev docker compose configuration expects the wiki to be located at
`wiki.docker` and the IDP to be located at `idp.docker`. This can be easily
configured by editing the hosts file and mapping both `wiki.docker` and
`idp.docker` to `127.0.0.1`.

#### Generate Key & Cert

**WARNING** the certs provided in `docker/simplesamlphp/sp/cert/` must NOT be
used in any kind of prod environment. They are only there for easier setup of
the docker compose dev environment.

To generate your own cert and key:

```bash
openssl req -newkey rsa:3072 -new -x509 -days 3652 -nodes -out wiki-sp.crt -keyout wiki-sp.pem
```

The key and cert can replace the ones in `docker/simplesamlphp/sp/cert/` which
will get mounted into the simplesamlphp container's
/var/www/simplesamlphp/cert/ directory.

#### SP Required Environment Variables

Deployment of the SimpleSAMLphp SP is required if you want to use the
SimpleSAMLphp extension. Note the IDP provided in docker compose is only for
development purposes.

The SP pulls metadata from the target IDP's metadata URL.

Required SP environment variables:

* SIMPLESAMLPHP_SECRET_SALT - Cryptographically secured random string used for salting purposes.
* SIMPLESAMLPHP_ADMIN_PASSWORD - Password for the default admin user.
* SIMPLESAMLPHP_MEMCACHED_SERVER - SimpleSAMLphp's SP cannot use the cookie cache as the wiki side SimpleSAMLphp extension will conflict with it. So we need to use a separate cache. For this purpose, we can just use the same Memcached server that the wiki uses.
* SIMPLESAMLPHP_TRUSTED_DOMAIN - Enter the wiki's domain here so that the SP knows it is safe.
* SIMPLESAMLPHP_BASEURL - Base URL for the SP. The SP needs to share the same domain as the wiki (or you run into cookie domain issues), so the base URL should be a path under the wiki domain.
* SIMPLESAMLPHP_SP_ENTITY_ID - The identifier that the SP uses to identify itself
* SIMPLESAMLPHP_IDP_ENTITY_ID - The target IDP's identifier.
* SIMPLESAMLPHP_IDP_METADATA_URL - URL where we can get the IDP's metadata.
* SIMPLESAMLPHP_CRON_SECRET - Random alphanumeric string for cron security.

Optional SP environment variables:

* SIMPLESAMLPHP_DEV - This turns on dev mode which enables the admin interface at `<SIMPLESAMLPHP_BASEURL>/module.php/admin/`. It also allows SIMPLESAMLPHP_SECRET_SALT to default to 'secretsalt' if unset and SIMPLESAMLPHP_ADMIN_PASSWORD to default to 'admin' if unset.

### Adding new LDAP users

You can connect to the LDAP container using your preferred LDAP GUI using `localhost:1389` with login `cn=admin,dc=example,dc=org` and password `admin`.

Expand Down
Loading

0 comments on commit 4e6cfcc

Please sign in to comment.