Skip to content

MWStake/ldap-demo

Repository files navigation

Configuring MediaWiki For LDAPGroups and Restricted Namespaces

This file

This file is intended to be record every configuration that is made in MediaWiki. It is meant to reside in ldap-demo/README.org and use org-tangle to extract the configuration files. “Tangling” is from Donald Knuth’s idea for literate programming.

How to use this file

Any time a part of the configuration needs to be changed, the following process should be followed:

  1. Edit the relevant parts of this file
  2. Tangle the file by opening it in emacs and, from within the file, typing “CTRL-C CTRL-v t” or, in the terms used by emacs, C-c C-v t.
  3. Check the resulting files into the git repository.

Initial setup

Requirements:

  • You have already set up MySQL and httpd
  • jq
  • moreutils (for sponge)
  • php cli

Basic Bare LocalSettings.php

  • git checkout with submodules
if [ ! -x mediawiki/.git ]; then
    git submodule add -f -b REL1_31 https://gerrit.wikimedia.org/r/p/mediaiwki/core mediawiki
else
    ( cd mediawiki && git fetch origin && git checkout REL1_31 )
fi
ls -l mediawiki/extensions
  • ran through basic installer, get LocalSettings.php:
<?php
# This file was automatically generated by the MediaWiki 1.27.4
# installer. If you make manual changes, please keep track in case you
# need to recreate them later.
#
# See includes/DefaultSettings.php for all configurable settings
# and their default values, but don't forget to make changes in _this_
# file, not there.
#
# Further documentation for configuration settings may be found at:
# https://www.mediawiki.org/wiki/Manual:Configuration_settings

# Protect against web entry
if ( !defined( 'MEDIAWIKI' ) ) {
	exit;
}

## Uncomment this to disable output compression
# $wgDisableOutputCompression = true;

$wgSitename = "LDAP Demo";
$wgMetaNamespace = "LDAP_Demo";

## The URL base path to the directory containing the wiki;
## defaults for all runtime URL paths are based off of this.
## For more information on customizing the URLs
## (like /w/index.php/Page_title to /wiki/Page_title) please see:
## https://www.mediawiki.org/wiki/Manual:Short_URL
$wgScriptPath = "/mediawiki";

## The protocol and server name to use in fully-qualified URLs
$wgServer = "http://debiankde.local";

## The URL path to static resources (images, scripts, etc.)
$wgResourceBasePath = $wgScriptPath;

## The URL path to the logo.  Make sure you change this from the default,
## or else you'll overwrite your logo when you upgrade!
$wgLogo = "$wgResourceBasePath/resources/assets/wiki.png";

## UPO means: this is also a user preference option

$wgEnableEmail = true;
$wgEnableUserEmail = true; # UPO

$wgEmergencyContact = "apache@debiankde.local";
$wgPasswordSender = "apache@debiankde.local";

$wgEnotifUserTalk = false; # UPO
$wgEnotifWatchlist = false; # UPO
$wgEmailAuthentication = true;

## Database settings
$wgDBtype = "mysql";
$wgDBserver = "10.5.5.1";
$wgDBname = "ldap-demo";
$wgDBuser = "wikiuser";
$wgDBpassword = "wikipass";

# MySQL specific settings
$wgDBprefix = "";

# MySQL table options to use during installation or update
$wgDBTableOptions = "ENGINE=InnoDB, DEFAULT CHARSET=binary";

# Experimental charset support for MySQL 5.0.
$wgDBmysql5 = false;

## Shared memory settings
$wgMainCacheType = CACHE_MEMCACHED;
$wgMemCachedServers = [ '10.5.5.1:11211' ];

## To enable image uploads, make sure the 'images' directory
## is writable, then set this to true:
$wgEnableUploads = true;
$wgUseImageMagick = true;
$wgImageMagickConvertCommand = "/usr/bin/convert";

# InstantCommons allows wiki to use images from https://commons.wikimedia.org
$wgUseInstantCommons = false;

## If you use ImageMagick (or any other shell command) on a
## Linux server, this will need to be set to the name of an
## available UTF-8 locale
$wgShellLocale = "en_US.utf8";

## Set $wgCacheDirectory to a writable directory on the web server
## to make your wiki go slightly faster. The directory should not
## be publically accessible from the web.
#$wgCacheDirectory = "$IP/cache";

# Site language code, should be one of the list in ./languages/data/Names.php
$wgLanguageCode = "en";

$wgSecretKey = "828af370037801add29264e58507376f4769244d6d99b624a607b96727a78aae";

# Changing this will log out all existing sessions.
$wgAuthenticationTokenVersion = "1";

# Site upgrade key. Must be set to a string (default provided) to turn on the
# web installer while LocalSettings.php is in place
$wgUpgradeKey = "cd79c75b8c0cb68d";

## For attaching licensing metadata to pages, and displaying an
## appropriate copyright notice / icon. GNU Free Documentation
## License and Creative Commons licenses are supported so far.
$wgRightsPage = ""; # Set to the title of a wiki page that describes your license/copyright
$wgRightsUrl = "";
$wgRightsText = "";
$wgRightsIcon = "";

# Path to the GNU diff3 utility. Used for conflict resolution.
$wgDiff3 = "/usr/bin/diff3";

# The following permissions were set based on your choice in the installer
$wgGroupPermissions['*']['createaccount'] = false;
$wgGroupPermissions['*']['edit'] = false;
$wgGroupPermissions['*']['read'] = false;

## Default skin: you can change the default skin. Use the internal symbolic
## names, ie 'vector', 'monobook':
$wgDefaultSkin = "vector";

# End of automatically generated settings.
# Add more configuration options below.
  • Add loader to LocalSettings:
wfLoadSkin( 'Vector' );
  • Symlink LocalSettings:
rm -f mediawiki/LocalSettings.php
ln -s ../LocalSettings.php mediawiki
ls -ld mediawiki/LocalSettings.php

Turn on debugging to get lots of errors

error_reporting( -1 );
ini_set( 'display_errors', 1 );
$wgShowSQLErrors = true;
$wgDebugDumpSql  = true;
$wgShowDBErrorBacktrace = true;
$wgShowExceptionDetails = true;
$wgDebugLogFile = "/var/log/mediawiki/debug.log";

Set up caching

$wgMainCacheType = CACHE_MEMCACHED;

Local LDAP server for testing

For this demonstration, we’ll set up a local OpenLDAP server and populate it with data from ForumSys’s LDAP test server.

Set up slapd

Note that the \025 here is C-u so that readline erases everything on the line.

#!/usr/bin/expect

spawn dpkg-reconfigure slapd -freadline
expect "Omit OpenLDAP server configuration?"
send "\025n\r"

expect "DNS domain name:"
send "\025example.com\r"

expect "Organization name:"
send "\025nodomain\r"

expect "Administrator password:"
send "password\r"

expect "Confirm password:"
send "password\r"

expect "Database backend to use:"
send "\0253\r"

expect "Do you want the database to be removed when slapd is purged?"
send "\025yes\r"

# done
expect eof

In order to make this idempotent, we erase all the data and reconfigure with the above script. We also install gosa-schema to get gender attributes.

sudo dpkg --purge slapd
sudo DEBIAN_FRONTEND=noninteractive apt -y install slapd gosa-schema
sudo service slapd stop
sudo rm -rf /var/lib/ldap
sudo expect slapd-config
sudo chown -R openldap:openldap /etc/ldap/slapd.d
sudo service slapd start

Get a database from the sample server

ldapsearch -w password -D "cn=read-only-admin,dc=example,dc=com" -b "dc=example,dc=com" -H ldap://ldap.forumsys.com -LLL | \
    grep -v ^userPassword:: | sed '/dn: dc=example,dc=com/,/^$/d' | sed '/dn: cn=admin,dc=example,dc=com/,/^$/d' | \
    sed 's,objectClass: inetOrgPerson,userPassword:: e1NIQX1XNnBoNU1tNVB6OEdnaVVMYlBnekczN21qOWc9\nobjectClass: inetOrgPerson,'

The following is the results of the above command saved here for later

dn: uid=newton,dc=example,dc=com
sn: Newton
userPassword:: e1NIQX1XNnBoNU1tNVB6OEdnaVVMYlBnekczN21qOWc9
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
uid: newton
mail: newton@ldap.forumsys.com
cn: Isaac Newton

dn: uid=einstein,dc=example,dc=com
userPassword:: e1NIQX1XNnBoNU1tNVB6OEdnaVVMYlBnekczN21qOWc9
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
cn: Albert Einstein
sn: Einstein
uid: einstein
mail: einstein@ldap.forumsys.com
telephoneNumber: 314-159-2653

dn: uid=tesla,dc=example,dc=com
userPassword:: e1NIQX1XNnBoNU1tNVB6OEdnaVVMYlBnekczN21qOWc9
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
objectClass: posixAccount
cn: Nikola Tesla
sn: Tesla
uid: tesla
mail: tesla@ldap.forumsys.com
uidNumber: 88888
gidNumber: 99999
homeDirectory: home

dn: uid=galieleo,dc=example,dc=com
userPassword:: e1NIQX1XNnBoNU1tNVB6OEdnaVVMYlBnekczN21qOWc9
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
cn: Galileo Galilei
sn: Galilei
uid: galieleo
mail: galieleo@ldap.forumsys.com

dn: uid=euler,dc=example,dc=com
userPassword:: e1NIQX1XNnBoNU1tNVB6OEdnaVVMYlBnekczN21qOWc9
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
uid: euler
sn: Euler
cn: Leonhard Euler
mail: euler@ldap.forumsys.com

dn: uid=gauss,dc=example,dc=com
userPassword:: e1NIQX1XNnBoNU1tNVB6OEdnaVVMYlBnekczN21qOWc9
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
cn: Carl Friedrich Gauss
sn: Gauss
uid: gauss
mail: gauss@ldap.forumsys.com

dn: uid=riemann,dc=example,dc=com
userPassword:: e1NIQX1XNnBoNU1tNVB6OEdnaVVMYlBnekczN21qOWc9
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
cn: Bernhard Riemann
sn: Riemann
uid: riemann
mail: riemann@ldap.forumsys.com

dn: uid=euclid,dc=example,dc=com
uid: euclid
userPassword:: e1NIQX1XNnBoNU1tNVB6OEdnaVVMYlBnekczN21qOWc9
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
cn: Euclid
sn: Euclid
mail: euclid@ldap.forumsys.com

dn: ou=mathematicians,dc=example,dc=com
uniqueMember: uid=euclid,dc=example,dc=com
uniqueMember: uid=riemann,dc=example,dc=com
uniqueMember: uid=euler,dc=example,dc=com
uniqueMember: uid=gauss,dc=example,dc=com
uniqueMember: uid=test,dc=example,dc=com
ou: mathematicians
cn: Mathematicians
objectClass: groupOfUniqueNames
objectClass: top

dn: ou=scientists,dc=example,dc=com
uniqueMember: uid=einstein,dc=example,dc=com
uniqueMember: uid=galieleo,dc=example,dc=com
uniqueMember: uid=tesla,dc=example,dc=com
uniqueMember: uid=newton,dc=example,dc=com
uniqueMember: uid=training,dc=example,dc=com
uniqueMember: uid=jmacy,dc=example,dc=com
ou: scientists
cn: Scientists
objectClass: groupOfUniqueNames
objectClass: top

dn: cn=read-only-admin,dc=example,dc=com
sn: Read Only Admin
cn: read-only-admin
userPassword:: e1NIQX1XNnBoNU1tNVB6OEdnaVVMYlBnekczN21qOWc9
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top

dn: ou=italians,ou=scientists,dc=example,dc=com
uniqueMember: uid=tesla,dc=example,dc=com
ou: italians
cn: Italians
objectClass: groupOfUniqueNames
objectClass: top

dn: uid=test,dc=example,dc=com
objectClass: posixAccount
objectClass: top
userPassword:: e1NIQX1XNnBoNU1tNVB6OEdnaVVMYlBnekczN21qOWc9
objectClass: inetOrgPerson
gidNumber: 0
givenName: Test
sn: Test
displayName: Test
uid: test
initials: TS
homeDirectory: home
cn: Test
uidNumber: 24601
o: Company

dn: ou=chemists,dc=example,dc=com
ou: chemists
objectClass: groupOfUniqueNames
objectClass: top
uniqueMember: uid=curie,dc=example,dc=com
uniqueMember: uid=boyle,dc=example,dc=com
uniqueMember: uid=nobel,dc=example,dc=com
uniqueMember: uid=pasteur,dc=example,dc=com
cn: Chemists

dn: uid=curie,dc=example,dc=com
uid: curie
userPassword:: e1NIQX1XNnBoNU1tNVB6OEdnaVVMYlBnekczN21qOWc9
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
cn: Marie Curie
sn: Curie
mail: curie@ldap.forumsys.com

dn: uid=nobel,dc=example,dc=com
uid: nobel
userPassword:: e1NIQX1XNnBoNU1tNVB6OEdnaVVMYlBnekczN21qOWc9
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
mail: nobel@ldap.forumsys.com
sn: Nobel
cn: Alfred Nobel

dn: uid=boyle,dc=example,dc=com
uid: boyle
userPassword:: e1NIQX1XNnBoNU1tNVB6OEdnaVVMYlBnekczN21qOWc9
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
cn: Robert Boyle
sn: Boyle
mail: boyle@ldap.forumsys.com
telephoneNumber: 999-867-5309

dn: uid=pasteur,dc=example,dc=com
userPassword:: e1NIQX1XNnBoNU1tNVB6OEdnaVVMYlBnekczN21qOWc9
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
sn: Pasteur
cn: Louis Pasteur
uid: pasteur
telephoneNumber: 602-214-4978
mail: pasteur@ldap.forumsys.com

dn: uid=nogroup,dc=example,dc=com
uid: nogroup
userPassword:: e1NIQX1XNnBoNU1tNVB6OEdnaVVMYlBnekczN21qOWc9
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
cn: No Group
mail: nogroup@ldap.forumsys.com
sn: Group

dn: uid=training,dc=example,dc=com
uid: training
userPassword:: e1NIQX1XNnBoNU1tNVB6OEdnaVVMYlBnekczN21qOWc9
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
cn: FS Training
sn: training
mail: training@forumsys.com
telephoneNumber: 888-111-2222

dn: uid=jmacy,dc=example,dc=com
uid: jmacy
telephoneNumber: 888-111-2222
sn: training
cn: FS Training
userPassword:: e1NIQX1XNnBoNU1tNVB6OEdnaVVMYlBnekczN21qOWc9
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
mail: jmacy-training@forumsys.com

The following command loads the above output into the ldap

ldapmodify -a -c -w password -D "cn=admin,dc=example,dc=com" -H ldap://localhost < ldap.ldif

Set up LDAP authentication

  • Check out LDAPProvider, LDAPAuthentication, LDAPUserInfo, LDAPGroups
for ext in LDAPProvider LDAPAuthentication LDAPUserInfo LDAPGroups; do
    dir=extensions/$ext
    if [ ! -d $dir ]; then
        git submodule add -f https://github.com/mwstake/mediawiki-extensions-$ext $dir
        git submodule update --init $dir
    else
        ( cd $dir && git checkout master && git pull origin )
    fi
done
dir=extensions/PluggableAuth
if [ ! -d $dir ]; then
    git submodule add -f https://github.com/wikimedia/mediawiki-extensions-PluggableAuth $dir
    git submodule update --init $dir
else
    ( cd $dir && git checkout master && git pull origin )
fi

Note that LDAPAuthentication depends on mediawiki/pluggable-auth, but it needs to be registered with Composer. See I532f9b4784106ac0cf40371f10601c4a8334412b.

{
	"require": {
		"mediawiki/ldap-provider": "dev-master",
		"mediawiki/ldap-user-info": "dev-master",
		"mediawiki/ldap-groups": "dev-master",
		"mediawiki/ldap-authentication": "dev-master"
	},
	"minimum-stability": "dev",
	"prefer-stable": false,
	"config": {
		"minimum-stability": "dev",
		"prefer-stable": false,
		"prefer": "source"
	},
	"repositories": [
        {
		"type": "vcs",
		"url": "../extensions/LDAPGroups"
	        },
        {
		"type": "vcs",
		"url": "../extensions/LDAPUserInfo"
	        },
        {
		"type": "vcs",
		"url": "../extensions/LDAPAuthentication"
	        },
        {
		"type": "vcs",
		"url": "../extensions/LDAPProvider"
	        },
        {
		"type": "vcs",
		"url": "../extensions/PluggableAuth"
	        }
        ]

}
  • Set up symlink for file
ln -s ../composer.local.json mediawiki
ls -dl composer.local.json mediawiki/composer.local.json
  • Run composer update
( cd mediawiki && composer update -vv -n --no-ansi  )
  • Enable extensions in LocalSettings.php:
wfLoadExtensions( [ 'LDAPProvider', 'LDAPAuthentication', 'LDAPUserInfo', 'LDAPGroups', 'PluggableAuth' ] );
  • Allow automatic creation of accounts

Note that you if you still have problems you may be blacklisted (debug log will show [authentication] MediaWiki\Auth\AuthManager::autoCreateUser: blacklisted in session). To fix, clear cookies.

$wgGroupPermissions['*']['autocreateaccount'] = true;
  • Update the schema
cd mediawiki && MW_INSTALL_PATH=`pwd` php maintenance/update.php

Make the mediawiki installation authenticate against our server

searchstring how we construct a user name to bind with searchattribute is the attribute that we look for once we have successfully logged in to get our user id. userbasedn is where to look for users usernameattribute, realnameattribute, and emailattribute are used for extracting the cooresponding user info from LDAP.

A search string will be constructed from searchattribute and userbasedn if searchstring is not provided.

The userinfo here is used by LDAPProvider, but should be in LDAPUserInfo.

Also, if no sections are found an error with directions should be printed.

{
	"LDAP Demo": {
		"connection": {
				"server": "10.5.5.1",
				"user": "cn=read-only-admin,dc=example,dc=com",
				"pass": "password",
				"basedn": "dc=example,dc=com",
				"userbasedn": "dc=example,dc=com",
				"searchattribute": "uid",
				"usernameattribute": "uid",
				"realnameattribute": "cn",
				"emailattribute": "mail"
		},
		"userinfo": {
			"attributes-map": {
				"email": "mail",
				"realname": "cn",
				"nickname": "uid",
				"language": "preferredlanguage"
			}
		}
	}
}
$LDAPProviderDomainConfigs = "$IP/../ldapprovider.json";

$LDAPProviderCacheTime = 300;
$LDAPProviderCacheType = CACHE_MEMCACHED;

# Following are only for testing and should be removed when this is done.
$LDAPProviderCacheTime = 1;
$LDAPProviderCacheType = CACHE_NONE;

Use our groups

OpenLDAP (at least in the schema we’ve loaded) uses groupOfUniqueNames and uniqueMember to handle groups.[fn:1] Tell LDAPProvider to use UniqueMember in the ldapprovider.json:
jq '.["LDAP Demo"]["groupsync"]["grouplookup"] = "UniqueMember"' ldapprovider.json | sponge ldapprovider.json
jq '.["LDAP Demo"]["groupsync"]["mapping"] = { "Mathematicians": "ou=mathematicians,dc=example,dc=com","Scientists": "ou=scientists,dc=example,dc=com","Italian scientists": "ou=italians,ou=scientists,dc=example,dc=com","Chemists": "ou=chemists,dc=example,dc=com"}' ldapprovider.json | sponge ldapprovider.json

You can do a UniqueMember search from the command line like this:

for user in einstein euclid pasteur tesla; do
    echo For $user
    echo =========
    ldapsearch -LLL -D cn=read-only-admin,dc=example,dc=com -w password -b dc=example,dc=com "(&(objectclass=groupOfUniqueNames)(uniqueMember=uid=$user,dc=example,dc=com))" dn
done

Limit users

  • Math namespace is only writable by mathematicians
  • Main namespace is writable by all users
  • Chemistry namespace is readable and writable only by chemists

Use NamespaceManager to manage namespaces

NamespaceManager is a MediaWiki extension which is intended to help you manage namespace configuration by putting all configuration in a seperate json file.

  • Add mediawiki/namespace-manager to composer.local.json
jq '.["require"]["mediawiki/namespace-manager"] = "dev-master"' composer.local.json | sponge composer.local.json
  • Run composer update
( cd mediawiki && composer update -vv -n --no-ansi  )
  • Load NamespaceManager and point to the namespace map file in LocalSettings.php
wfLoadExtension( "NamespaceManager" );
$NamespaceManagerMapFile = "$IP/../ns.json";
  • Create a namespace mapping file
{
	"globalAdmin": "sysop",
	"Math": {
		"constant": "NS_MATH",
		"id": 5000,
		"includable": false,
		"lockdown": [
			"move", "edit", "create", "createpage", "createtalk"
		],
		"group": "Mathematicians",
		"permission": "edit-math"
	},
	"Chemistry": {
		"constant": "NS_CHEMISTRY",
		"id": 5002,
		"includable": false,
		"lockdown": [
			"read", "move", "edit", "create", "createpage", "createtalk"
		],
		"group": "Chemists",
		"permission": "edit-chemistry"
	}
}

Add Lockdown so Namespaces can actually be hidden

  • Check it out as a submodule
dir=extensions/Lockdown
if [ ! -d $dir ]; then
    git submodule add -b REL1_31 -f ssh://gerrit.wikimedia.org:29418/mediawiki/$dir $dir
    git submodule update --init $dir
else
    ( cd $dir && git checkout REL1_31 && git pull origin )
fi
rm -f mediawiki/$dir
ln -s ../../$dir mediawiki/$dir
  • Call it from LocalSettings.php
wfLoadExtension( "Lockdown" );

Footnotes

[fn:1] See GroupOfUniqueNames vs groupOfNames and memberOf for information on different ways directory servers manage groups.

About

A demonstration wiki for the LDAP extensions

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages