cas.py 4.41 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
# -*- 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)
46
            url += '&service=' + urllib.quote_plus(redirect_url)
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
        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", "")
82
        force = request.args.get("force", None) is not None
83 84 85 86 87
        logoutRequest = request.args.get("logoutRequest", [])
        p = urlparse.urlparse(request.url)
        url = urlparse.urlunparse(('https', p.netloc, p.path, "", "", ""))

        # authenticated user
88
        if not force and user_obj and user_obj.valid:
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
            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:
107
                    request.http_redirect("%s?action=%s&wiki_url=%s" % (self.fallback_url, self.action, url))
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
                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