ShibbolethSupport
Add Shibboleth based Single Sign-On authentication support for moinmoin logins .
Authentication support
strict session support
it is possible to implement with Apache and configured mod_shib module.
you have to use http authentiction modul with apache with shibboleth apache module configured. Example apache configuration snippet:
<Location /shibtest> AuthType shibboleth require valid-user </Location>
You have to install the following patch in order to overcome bug?? in version 1.7.x in the current http authentication module in site-packages/MoinMoin/auth/http.py
diff -ruN http.py.save http.py --- http.py.save 2008-09-30 10:56:58.000000000 +0200 +++ http.py 2008-11-13 10:46:34.000000000 +0100 @@ -56,7 +56,8 @@ elif not isinstance(request, request_cli.Request): env = request.env auth_type = env.get('AUTH_TYPE', '') - if auth_type in ['Basic', 'Digest', 'NTLM', 'Negotiate', ]: + + if auth_type in ['Basic', 'Digest', 'NTLM', 'Negotiate', '' ]: username = env.get('REMOTE_USER', '').decode(config.charset) if auth_type in ('NTLM', 'Negotiate', ): # converting to standard case so the user can even enter wrong case
You have to install the following patch in order to overcome bug?? in version 1.8.3 in the current http authentication module in site-packages/MoinMoin/auth/http.py
root@mignon# diff -ruN http.py.save http.py --- http.py.save 2009-05-26 17:13:00.000000000 +0200 +++ http.py 2009-05-26 17:13:58.000000000 +0200 @@ -60,7 +60,7 @@ elif not isinstance(request, request_cli.Request): env = request.env auth_type = env.get('AUTH_TYPE', '') - if auth_type in ['Basic', 'Digest', 'NTLM', 'Negotiate', ]: + if auth_type in ['Basic', 'Digest', 'NTLM', 'Negotiate', '']: username = env.get('REMOTE_USER', '').decode(config.charset) if auth_type in ('NTLM', 'Negotiate', ): # converting to standard case so the user can even enter wrong case
You have to install the following patch to support shibboleth authentication for shibboleth2 in version 1.8.3 in the current http authentication module in site-packages/MoinMoin/auth/http.py
root@mignon# diff -ruN http.py.save http.py --- http.py.save 2009-05-26 17:13:00.000000000 +0200 +++ http.py 2009-05-26 17:13:58.000000000 +0200 @@ -60,7 +60,7 @@ elif not isinstance(request, request_cli.Request): env = request.env auth_type = env.get('AUTH_TYPE', '') - if auth_type in ['Basic', 'Digest', 'NTLM', 'Negotiate', ]: + if auth_type in ['Basic', 'Digest', 'NTLM', 'Negotiate', 'shibboleth',]: username = env.get('REMOTE_USER', '').decode(config.charset) if auth_type in ('NTLM', 'Negotiate', ): # converting to standard case so the user can even enter wrong case
lazy session support
I have written a basic Shibboleth auth module with lazy authentication (users need to click the Login link):
from MoinMoin.auth import ContinueLogin, MultistageRedirectLogin from MoinMoin.auth.http import HTTPAuth class ShibbolethAuth(HTTPAuth): """ Authenticate with Shibboleth """ name = 'shibboleth' login_inputs = ['special_no_input'] def request(self, request, user_obj, **kw): try: # hack to make HTTPAuth work with Shibboleth if request.env.get('REMOTE_USER'): request.env['AUTH_TYPE'] = 'Basic' except AttributeError: pass return HTTPAuth.request(self, request, user_obj, **kw) def login(self, request, user_obj, **kw): if kw.get('multistage'): u, cont = self.request(request, user_obj, **kw) return ContinueLogin(u) else: shiburl = request.getQualifiedURL('/Shibboleth.sso/Login') return MultistageRedirectLogin(shiburl + '?target=%return')
I have tested it with MoinMoin 1.7.1. Simply paste it into your configuration file (or in a separate file and import it). It also works with non-lazy (strict) authentication.
Bug: You always get redirected to the front page after login.
I'm not experienced with Python or the MoinMoin codebase, so any suggestions are welcome. --PerOlofsson
Lazy Session, new version - Juli 2018
To clarify the concepts discussed on this page a bit more: The idea with lazy login is to open the Wiki for read-only access to the public but protect it with Shibboleth for write access. This this is different from the strict session stuff found above, which blocks all access to the Wiki without valid credentials.
I found the above lazy login code to be lacking Single Sign Out and not documented very cleanly. Also it was written for an older version of Moin. I therefore hacked together this. The code was tested it in our production environment and it *seems* to run fine, including Single Sign Out, working redirect back to the page we came from and update of user info (email, alias name). If I find any bugs in the future, I will update this page accordingly.
DerBachmannRocker, MHT
from MoinMoin.auth import * from werkzeug import redirect, abort, url_quote, url_quote_plus from MoinMoin import log logging = log.getLogger(__name__) #Configure Moin logging and set log level to DEBUG to see all messages! class mhtShibbolethAuth(GivenAuth): # Authenticate users with Shibboleth SP # Adapted and improved from: # https://moinmo.in/ShibbolethSupport # # Tested with Shibboleth SP v2.6.0 on Apache for Debian 8 # Moin 1.9.8 # # This requires Shibboleth SP (mod_shib), fully configured, to accept your users. # # To use this class # save mhtshibbolethlogin.py to /etc/moin # make executable: chmod a+rx /etc/moin/mhtshibbolethlogin.py # Put this in your mywiki.py # from mhtshibbolethlogin import mhtShibbolethAuth # auth = [mhtShibbolethAuth(autocreate=True)] # # Mapping of Shibboleth user attributes to Moin: # moin aliasname: displayName (urn:oid:2.16.840.1.113730.3.1.241) # moin email: mail # moin username: REMOTE_USER (configure in shibboleth2.xml which value gets put into REMOTE_USER) # ################################ #Hints for Apache configuration ################################ #Place these lines inside your virtual host configuration file # ## This uses lazy login. Do not protect root of Moin - but make sure Shibboleth is enabled ## https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPApacheConfig ## Warning. Use exactly this syntax, or else Shibboleth SP may never be invoked / session variables may stay empty. ## Require shibboleth is critical here. #<Location / > # AuthType Shibboleth # ShibRequestSetting requireSession false # Require shibboleth #</Location> ## Protect moin admin special pages with Shibboleth #<Location /SystemInfo > # ShibRequestSetting requireSession true # Require valid-user #</Location> #<Location /SystemAdmin > # ShibRequestSetting requireSession true # Require valid-user #</Location> # ###################################### #Hints for Shibboleth SP configuration ###################################### # This class will accept any Shibboleth user that is able to log in! # If you need to check for any user attributes (e.g. eduPersonScopedAffiliation, eduPersonPrincipalName) # please configure in shibboleth2.xml: # <ApplicationDefaults ... sessionHook="/Shibboleth.sso/AttrChecker" /> # AttrChecker elements can be used to check for required values before login is allowed # https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPHandler #<Handler type="AttributeChecker" Location="/AttrChecker" template="attrChecker.html" flushSession="true"> # <AND> # <!-- attribute-map.xml: id of attributes --> # <Rule require="displayName"/> # <RuleRegex require="eppn" caseSensitive="false">^.+@mh-trossingen\.de$</RuleRegex> # </AND> #</Handler> # # # name = 'shibboleth' logout_possible = True login_inputs = ['special_no_input'] #GivenAuth constructor + Shibboleth login/logout hooks def __init__(self, env_var=None, # environment variable we want to read (default: REMOTE_USER) user_name=None, # can be used to just give a specific user name to log in autocreate=False, # create/update the user profile for the auth. user strip_maildomain=False, # joe@example.org -> joe strip_windomain=False, # DOMAIN\joe -> joe titlecase=False, # joe doe -> Joe Doe remove_blanks=False, # Joe Doe -> JoeDoe coding=None, # for decoding REMOTE_USER correctly (default: auto) shibLoginHook="/Shibboleth.sso/Login", shibLogoutHook="/Shibboleth.sso/Logout", ): self.shibLoginHook = shibLoginHook self.shibLogoutHook = shibLogoutHook GivenAuth.__init__(self, env_var, user_name, autocreate, strip_maildomain, strip_windomain, titlecase, remove_blanks, coding ) def login(self, request, user_obj, **kw): if kw.get('multistage'): logging.debug("Returning from Shibboleth login page. Redirect back to Moin was successful.") #test if shibboleth login was successful if request.environ.get("Shib-Session-ID"): logging.debug("A Shibboleth session ID was found. Login successful. Returning user object.") u, cont = self.request(request, user_obj, **kw) u.aliasname = request.environ.get("displayName") u.email = request.environ.get("mail") if u and self.autocreate: logging.debug("calling create_or_update to autocreate user %r" % u.name) u.create_or_update(True) return LoginReturn (u, True, message=None, multistage=None, redirect_to=url_quote(request.values.get("wikiPageBeforeLogin"))) else: #this should never happen (?) logging.warning("No Shibboleth session ID found. Aborting.") return CancelLogin("Could not log in via Shibboleth. Please contact your Administrator.") #stolen from openid connector # openid is designed to work together with other auths. I did NOT test this functionality elif user_obj and user_obj.valid: return ContinueLogin(user_obj) else: #Request to Shibboleth idp. Pass ?target= parameter pointing back to this multistage login procedure return_to = get_multistage_continuation_url(request, self.name, extra_fields={"wikiPageBeforeLogin" : request.base_url}) #return URL needs to be escaped. Otherwise redirect back to Moin (and multistage) will not work correctly return_to = url_quote(return_to) logging.debug("shibboleth multistage return url: %s", return_to) shiburl = "%s?target=%s" %(request.getQualifiedURL(self.shibLoginHook).encode("utf-8"), return_to) #request to Shibboleth idp return MultistageRedirectLogin( shiburl ) def logout(self, request, user_obj, **kw): shiburl = request.getQualifiedURL(self.shibLogoutHook).encode("utf-8") logging.info("Logout: redirecting to %s" % shiburl) abort(redirect(shiburl)) return GivenAuth.logout(self, request, user_obj, **kw)
Discussion
- Which moin version ?
- See patches / code
- Why an empty auth_type string has to be added for the strict session method?
- shibboleth module is not using auth_type 'Basic, Digest etc.'.
So, to summarize, we need auth method '' for shibboleth v1 and 'shibboleth' for v2? And that's all we need to do on the moin side to start with basic support for it? Or do we need to wait for more code first?
For strict session support, you only need to add support for AUTH_TYPE "shibboleth" in HTTPAuth. For lazy session support (user needs to click login button), see my code above. --PerOlofsson