# -*- coding: iso-8859-1 -*- """ MoinMoin - CAS authentication Jasig CAS (see http://www.jasig.org/cas) authentication module. @copyright: 2012 MoinMoin:RichardLiao @license: GNU GPL, see COPYING for details. """ import sys import time, re import urlparse import urllib, urllib2 from MoinMoin import log logging = log.getLogger(__name__) from MoinMoin.auth import BaseAuth from MoinMoin import user, wikiutil class PyCAS(object): """A class for working with a CAS server.""" def __init__(self, server_url, renew=False, login_path='/login', logout_path='/logout', validate_path='/validate', coding='utf-8'): self.server_url = server_url self.renew = renew self.login_path = login_path self.logout_path = logout_path self.validate_path = validate_path self.coding = coding def login_url(self, service): """Return the login URL for the given service.""" url = self.server_url + self.login_path + '?service=' + urllib.quote_plus(service) if self.renew: url += "&renew=true" return url def logout_url(self, redirect_url=None): """Return the logout URL.""" url = self.server_url + self.logout_path if redirect_url: url += '?url=' + urllib.quote_plus(redirect_url) return url def validate_url(self, service, ticket): """Return the validation URL for the given service. (For CAS 1.0)""" url = self.server_url + self.validate_path + '?service=' + urllib.quote_plus(service) + '&ticket=' + urllib.quote_plus(ticket) if self.renew: url += "&renew=true" return url def validate_ticket(self, service, ticket): """Validate the given ticket against the given service.""" f = urllib2.urlopen(self.validate_url(service, ticket)) valid = f.readline() valid = valid.strip() == 'yes' user = f.readline().strip() user = user.decode(self.coding) return valid, user class CASAuth(BaseAuth): """ handle login from CAS """ name = 'CAS' login_inputs = ['username', 'password'] logout_possible = True def __init__(self, auth_server, login_path="/login", logout_path="/logout", validate_path="/validate", action="login_cas", create_user=False, fallback_url=None): BaseAuth.__init__(self) self.cas = PyCAS(auth_server, login_path=login_path, validate_path=validate_path, logout_path=logout_path) self.action = action self.create_user = create_user self.fallback_url = fallback_url def request(self, request, user_obj, **kw): ticket = request.args.get("ticket", "") action = request.args.get("action", "") logoutRequest = request.args.get("logoutRequest", []) p = urlparse.urlparse(request.url) url = urlparse.urlunparse(('https', p.netloc, p.path, "", "", "")) # authenticated user if user_obj and user_obj.valid: return user_obj, True # anonymous if not ticket and not self.action == action: return user_obj, True # valid ticket on CAS if ticket: valid, username = self.cas.validate_ticket(url, ticket) if valid: u = user.User(request, auth_username=username, auth_method=self.name) # auto create user ? if self.create_user: u.valid = valid u.create_or_update(True) else: u.valid = u.exists() if self.fallback_url and not u.valid: request.http_redirect(self.fallback_url) return u, True # login request.http_redirect(self.cas.login_url(url)) return user_obj, True def logout(self, request, user_obj, **kw): if self.name and user_obj and user_obj.auth_method == self.name: user_obj.valid = False request.cfg.session_service.destroy_session(request, request.session) p = urlparse.urlparse(request.url) url = urlparse.urlunparse((p.scheme, p.netloc, p.path, "", "", "")) request.http_redirect(self.cas.logout_url(url)) return user_obj, False