Attachment 'RemoteHttpAuth.py'

Download

   1 # -*- coding: iso-8859-1 -*-
   2 """
   3     MoinMoin - auth plugin using remote http(s) server with
   4     authentication.
   5     Based on a code found here:
   6     http://www.voidspace.org.uk/python/articles/authentication.shtml
   7     
   8     @copyright: 2009 Jakub Schmidtke <sjakub-at-gmail.com>
   9     @license: GNU GPL, see COPYING for details.
  10 """
  11 
  12 import urllib2
  13 import sys
  14 import re
  15 import base64
  16 from urlparse import urlparse
  17 
  18 from MoinMoin import config, user
  19 
  20 from MoinMoin.auth import BaseAuth, CancelLogin, ContinueLogin
  21 
  22 class RemoteHttpAuth(BaseAuth):
  23     """ 
  24         Auth plugin using remote http(s) authentication
  25     """
  26     name = 'remote_http'
  27     login_inputs = ['username', 'password']
  28     logout_possible = True
  29         
  30     def __init__(self,
  31         auth_url,                    # url used for authentication:
  32                                      # http(s)://server_with_authentication/some_page
  33         auth_hint = None,            # login hint        
  34         required_web_content = None, # Content required from URL if properly authenticated.
  35                                      # Content fetched from URL should be equal to this value\
  36                                      # (if specified, if not this test is not performed)
  37 	auto_email_domain = None,    # if set, it will be used for registering automatic
  38 	                             # email for new users, in form: username@auto_email_domain
  39 	autocreate = False,          # should new users be created?
  40         ):
  41         BaseAuth.__init__(self)
  42         self.auth_url = auth_url
  43         self.auth_hint = auth_hint
  44         self.required_web_content = required_web_content
  45 	self.auto_email_domain = auto_email_domain
  46         self.autocreate = autocreate;
  47 
  48     def login_hint(self, request):
  49         if self.auth_hint:
  50             return request.getText(self.auth_hint)
  51         
  52         return None
  53         
  54     def login(self, request, user_obj, **kw):
  55         username = kw.get('username')
  56         password = kw.get('password')
  57         _ = request.getText
  58 
  59         if not username or not password:
  60             return ContinueLogin(user_obj)
  61     
  62         req = urllib2.Request(self.auth_url)
  63         
  64         try:
  65             handle = urllib2.urlopen(req)
  66         except IOError, e:
  67             # here we *want* to fail
  68             pass
  69         else:
  70             # If we don't fail then the page isn't protected
  71             return ContinueLogin(user_obj, _("Error Authenticating: URL used for RemoteHttpAuth "
  72                     " does not require authentication. Please contact administrator."))
  73 
  74         if not hasattr(e, 'code') or e.code != 401:
  75             # we got an error - but not a 401 error
  76             return ContinueLogin(user_obj, _("Error Authenticating: URL used for RemoteHttpAuth "
  77                     "does not require authentication or failed for antoher reason. "
  78                     "Please contact administrator."))
  79 
  80         authline = e.headers['www-authenticate']
  81         # this gets the www-authenticate line from the headers
  82         # which has the authentication scheme and realm in it
  83 
  84         authobj = re.compile(r'''(?:\s*www-authenticate\s*:)?\s*(\w*)\s+realm=['"]([^'"]+)['"]''', re.IGNORECASE)
  85         # this regular expression is used to extract scheme and realm
  86         matchobj = authobj.match(authline)
  87 
  88         if not matchobj:
  89             # if the authline isn't matched by the regular expression
  90             # then something is wrong
  91             return ContinueLogin(user_obj, _("Error Authenticating: Authentication header received from URL "
  92                     "used for RemoteHttpAuth is badly formed. Please contact administrator."))
  93 
  94         scheme = matchobj.group(1)
  95         realm = matchobj.group(2)
  96         # here we've extracted the scheme
  97         # and the realm from the header
  98         if scheme.lower() != 'basic':
  99             return ContinueLogin(user_obj, _("Error Authenticating: RemoteHttpAuth requires BASIC authentication "
 100                     "instead of '" + scheme + "'. Please contact administrator."))
 101 
 102         base64string = base64.encodestring('%s:%s' % (username, password))[:-1]
 103         authheader =  "Basic %s" % base64string
 104         req.add_header("Authorization", authheader)
 105         
 106         try:
 107             handle = urllib2.urlopen(req)
 108         except IOError, e:
 109             # here we shouldn't fail if the username/password is right
 110             return ContinueLogin(user_obj, _("Incorrect username or password."))
 111 
 112         thepage = handle.read()
 113 
 114 	if self.required_web_content:
 115 	    if thepage != self.required_web_content:
 116 		return ContinueLogin(user_obj, _("Authorization failed: '%s'" %(str(thepage))))
 117     
 118         u = user.User(request, name=username, auth_username=username, auth_method=self.name,
 119 		auth_attribs=('name', 'auth_username', 'password'))
 120 
 121 	if self.autocreate and self.auto_email_domain and not u.email:
 122 	    u.email = username + "@" + self.auto_email_domain
 123 
 124 	u.valid = True
 125 
 126         if u and self.autocreate:
 127             u.create_or_update(True)
 128 
 129         return ContinueLogin(u)

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2009-02-28 07:32:30, 4.9 KB) [[attachment:RemoteHttpAuth.py]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.