Changeset 1213 for trunk/rp/trac
- Timestamp:
- 01/09/08 10:40:43 (12 months ago)
- Location:
- trunk/rp/trac/infocard_acct/0.11
- Files:
-
- 5 modified
-
infocard_acct/groups.py (modified) (2 diffs)
-
infocard_acct/session.py (modified) (3 diffs)
-
infocard_acct/web_ui.py (modified) (3 diffs)
-
setup.cfg (modified) (1 diff)
-
setup.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/rp/trac/infocard_acct/0.11/infocard_acct/groups.py
r1123 r1213 1 1 # Copyright (c) 2007 Novell, Inc. 2 2 # All Rights Reserved. 3 3 4 4 # This library is free software; you can redistribute it and/or 5 5 # modify it under the terms of the GNU Lesser General Public License as 6 6 # published by the Free Software Foundation; version 2.1 of the license. 7 7 8 8 # This library is distributed in the hope that it will be useful, 9 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 11 # GNU Lesser General Public License for more details. 12 12 13 13 # You should have received a copy of the GNU Lesser General Public License 14 14 # along with this library; if not, contact Novell, Inc. 15 15 16 16 # To contact Novell about this file by physical or electronic mail, 17 17 # you may find current contact information at www.novell.com 18 18 # 19 20 import os, sys 19 21 20 22 from trac.core import * … … 30 32 31 33 class SecTokenGroups(Component): 32 """Using Policy to move items from the security token to the session as groups 33 evaluated once, then cleared from the cache on logout, 34 Also provides permission groups by evaluating what is stored on the session 35 """ 36 37 implements(SecTokenChangeListener, IPermissionGroupProvider) 34 """Using Policy to move items from the security token to the session as groups 35 evaluated once, then cleared from the cache on logout, 36 Also provides permission groups by evaluating what is stored on the session 37 """ 38 38 39 def __init__(self): 40 self.gDefs = dict() 41 self.enabled = self.config.getbool('infocard_acct', 'groups', True) 42 if not self.enabled: 43 self.env.log.debug('SecTokenGroups disabled') 44 return 39 implements(SecTokenChangeListener, IPermissionGroupProvider) 45 40 46 # self.env.log.debug('SecTokenGroups processing group definition : ' + gDef) 47 groupRules = {} 48 groupRules['name'] = gDef 49 groupRules['exclusive'] = self.config.getbool(gDef, 'exclusive', False) 50 groupRules['matchExpression'] = self.config.get(gDef, 'match_expresion') 51 groupRules['matchStatement'] = self.config.get(gDef, 'match_statement') 52 groupRules['ruleExpresion'] = self.config.get(gDef, 'rule_expresion') 53 groupRules['ruleStatement'] = self.config.get(gDef, 'rule_statement') 54 55 if gDef and (groupRules.has_key('ruleExpresion') 56 or groupRules.has_key('ruleStatment')): 57 self.gDefs[gDef] = groupRules 41 def __init__(self): 42 self.gDefs = dict() 43 self.enabled = self.config.getbool('infocard_acct', 'groups', True) 44 if not self.enabled: 45 self.env.log.debug('SecTokenGroups(__init__) :: disabled') 46 return 58 47 59 def login(self, req, secToken): 60 """ on login set the parse the token and setup the sec_groups on the session""" 61 req.session[session_var_name] = '' 48 gDefSets = self.config.getlist('infocard_acct', 'group_definitions') 49 for gDef in gDefSets: 50 gDef = gDef.strip() #remove any unsightly spaces 51 groupRules = {} 52 groupRules['name'] = gDef 53 groupRules['exclusive'] = self.config.getbool(gDef, 'exclusive', False) 62 54 63 groups = [] 64 if not self.enabled or not self.gDefs.items() or not secToken: 65 return 66 67 for ruleName in self.gDefs.keys(): 68 rule = self.gDefs[ruleName] 69 try: 70 if self._does_rule_match(rule, secToken): 71 values = self._evalute_rule(rule, secToken) 72 if values: 73 self.env.log.debug('SecTokenGroups rule adds : '\ 74 + ','.join(values)) 75 groups.extend(values) 76 if rule['exclusive']: 77 self.env.log.debug('SecTokenGroups exclusive rule fired: '+ ruleName) 78 break 79 except Exception: 80 self.env.log.debug('SecTokenGroups error evaluating rule: %s' % (ruleName)) 81 82 if groups and groups.count(): 83 self.env.log.debug('SecTokenGroups groups (%d): ' %(groups.count() ) + ','.join(groups)) 55 # self.log.debug('SecTokenGroups(__init__) processing: ' + gDef) 56 groupRules['module'] = self._manageimport( 57 self.config.get(gDef, 'import'), 58 self.config.get(gDef, 'from')) 84 59 85 return groups 86 87 def logout(self, req): 88 """On logout we must cleanup the sec_groups variable""" 89 if req.session.has_key(session_var_name): 90 del req.session[session_var_name] 60 if gDef and (groupRules.has_key('module')): 61 self.gDefs[gDef] = groupRules 62 else: 63 self.log.debug('SecTokenGroups(__init__) Rule %s was not loaded' % gDef) 64 # self.log.debug('SecTokenGroups(__init__) done') 91 65 92 def _does_rule_match(self, rule, secToken): 93 # try: 94 locals = {'secToken': sectoken} 95 if rule.has_key('matchExpresion'): 96 eval(rule['matchExpresion'], None, locals) 97 else: 98 return True 99 # except Exception: 100 # return False 66 def _import(self, moduleName, elementName, paths): 67 module = None 68 try: 69 module = __import__( moduleName, globals(), locals(), []) 70 except ImportError: 71 for path in paths: 72 try: 73 module = __import__(os.path.join(path, moduleName), 74 globals(), locals(), []) 75 except ImportError: 76 #whoa, way scary stuff here 77 if path not in sys.path: 78 sys.path.append(path) 79 module = __import__( moduleName, globals(), locals(), []) 80 sys.path.remove(path) 81 if elementName and len(elementName): 82 return vars(module)[elementName]() 83 else: 84 return module 101 85 102 def _evalute_rule(self, rule, secToken): 103 if rule and secToken: 104 # try: 105 locals = {'secToken': sectoken} 106 if rule.has_key('ruleExpresion'): 107 data = eval(rule['ruleExpresion'], None, locals) 108 return (data,) 109 elif rule.has_key('ruleStatement'): 110 pass 111 # else: 112 # data = eval('tok_claim_privatepersonalidentifier', None, session) 113 # return (data,) 114 # except Exception: 115 # pass 116 return None 86 def _importfuncs(self, groupRules, moduleName, elementName, functions, paths): 87 module = self._import(moduleName, elementName, paths) 88 if module: 89 funcs = [] 90 for func in functions: 91 try: 92 groupRules[func] = module.func 93 except: 94 self.log.debug('SecTokenGroups unable to find : ' + func) 95 pass 117 96 118 # IPermissionGroupProvider interface 119 def get_permission_groups(self, username): 120 """Return a list of names of the groups that the user with the specified 121 name is a member of.""" 97 def _manageimport(self, importName, fromName): 98 """ get the match and evaluate functions setup """ 99 # funcs = ('evaluate', 'match') 100 paths = (os.path.join(self.env.path, 'conf'), ) 101 # evaluate = None 102 # match= None 103 module = None 104 if importName and fromName: 105 # self._importfuncs(groupRules, fromName, importName, funcs, paths) 106 module = self._import(fromName, importName, paths) 107 elif importName: 108 # self._importfuncs(groupRules, importName, None, funcs, paths) 109 module = self._import(importName, None, paths) 110 else: 111 # self._importfuncs(groupRules, fromName, None, funcs, paths) 112 module = self._import(fromName, None, paths) 122 113 123 #since we don't have the session object we have to fake it 124 try: 125 db = self.env.get_db_cnx() 126 cursor = db.cursor() 127 cursor.execute("SELECT name,value FROM session_attribute " 128 "WHERE sid=%s and authenticated=%s and name=%s", (username, int(True), session_var_name)) 129 for name, value in cursor: 130 if value and len(value): 131 self.env.log.debug('SecTokenGroups groups: ' + ','.join(value)) 132 return value.split(' ') 133 except Exception: 134 pass 114 return module 135 115 136 return [] 116 def login(self, req, secToken): 117 """ on login set the parse the token and setup the sec_groups on the session""" 118 req.session[session_var_name] = '' 137 119 120 groups = [] 121 if not self.enabled or not self.gDefs.items() or not secToken: 122 self.env.log.debug('SecTokenGroups(login) disabled') 123 return 138 124 125 for ruleName in self.gDefs.keys(): 126 rule = self.gDefs[ruleName] 127 values = self._evalute_rule(rule, secToken) 128 if values: 129 # self.env.log.debug('SecTokenGroups rule %s adds : '\ 130 # % (ruleName) + ','.join(values)) 131 groups.extend(values) 132 # if rule['exclusive']: 133 # self.env.log.debug('SecTokenGroups exclusive rule fired: '+ ruleName) 134 break 135 136 if groups and len(groups): 137 self.env.log.debug('SecTokenGroups groups : ' + ','.join(groups)) 138 139 return groups 140 141 def logout(self, req): 142 """On logout we must cleanup the sec_groups variable""" 143 if req.session.has_key(session_var_name): 144 del req.session[session_var_name] 145 146 def _evalute_rule(self, rule, secToken): 147 self.env.log.debug('SecTokenGroups evaluate rule :' + rule['name']) 148 values = rule['module'].evaluate(secToken) 149 if values and isinstance(values, str): 150 values = (values, ) 151 return values 152 # except: 153 # return None 154 155 # IPermissionGroupProvider interface 156 def get_permission_groups(self, username): 157 """Return a list of names of the groups that the user with the specified 158 name is a member of.""" 159 160 #since we don't have the session object we have to fake it 161 try: 162 db = self.env.get_db_cnx() 163 cursor = db.cursor() 164 cursor.execute("SELECT name,value FROM session_attribute " 165 "WHERE sid=%s and authenticated=%s and name=%s", (username, int(True), session_var_name)) 166 for name, value in cursor: 167 if value and len(value): 168 self.env.log.debug('SecTokenGroups groups: ' + ','.join(value)) 169 return value.split(' ') 170 except Exception: 171 pass 172 173 return [] 174 -
trunk/rp/trac/infocard_acct/0.11/infocard_acct/session.py
r1123 r1213 40 40 listeners = ExtensionPoint(SecTokenChangeListener) 41 41 42 def login(self, req, secToken ):42 def login(self, req, secToken, options = None): 43 43 """called on succesful login""" 44 44 for listener in self.listeners: 45 # self.log.debug('SecTokenSessionModule login : ' + listener.__class__.__name__) 45 46 listener.login(req, secToken) 46 47 47 def logout(self, req ):48 def logout(self, req, options=None): 48 49 """called during logout, cleanup any transitory settings""" 49 50 for listener in self.listeners: … … 63 64 def login(self, req, secToken): 64 65 """ on login set the email address and name on the session""" 65 66 66 67 # self.log.debug('SecTokenPreferences setup %s %s' % (req, req.session)) 67 68 if secToken and req.session: … … 88 89 subsequent authentications which don't use a security token""" 89 90 pass 90 91 91 92 92 93 class SecTokenInfo(Component): 93 94 """An example of lazy setting of session vars for all claims and metadata -
trunk/rp/trac/infocard_acct/0.11/infocard_acct/web_ui.py
r1123 r1213 8 8 # This library is distributed in the hope that it will be useful, 9 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 11 # GNU Lesser General Public License for more details. 12 12 … … 18 18 # 19 19 # This file incorporates work covered by the following copyright and 20 # permission notice:21 # 22 # -*- coding: utf8 -*-23 # 24 # Copyright (C) 2007 Matthew Good <trac@matt-good.net>25 # 26 # "THE BEER-WARE LICENSE" (Revision 42):27 # <trac@matt-good.net> wrote this file.As long as you retain this notice you28 # can do whatever you want with this stuff. If we meet some day, and you think29 # this stuff is worth it, you can buy me a beer in return.Matthew Good30 # 31 # Author: Matthew Good <trac@matt-good.net>20 # permission notice: 21 # 22 # -*- coding: utf8 -*- 23 # 24 # Copyright (C) 2007 Matthew Good <trac@matt-good.net> 25 # 26 # "THE BEER-WARE LICENSE" (Revision 42): 27 # <trac@matt-good.net> wrote this file. As long as you retain this notice you 28 # can do whatever you want with this stuff. If we meet some day, and you think 29 # this stuff is worth it, you can buy me a beer in return. Matthew Good 30 # 31 # Author: Matthew Good <trac@matt-good.net> 32 32 33 33 import random … … 60 60 61 61 def if_enabled(func): 62 def wrap(self, *args, **kwds):63 if not self.enabled:64 return None65 return func(self, *args, **kwds)66 return wrap62 def wrap(self, *args, **kwds): 63 if not self.enabled: 64 return None 65 return func(self, *args, **kwds) 66 return wrap 67 67 68 68 class LoginModule(auth.LoginModule): 69 69 70 implements(ITemplateProvider) 71 72 privateKey = '/' 73 privateKeyPassPhrase = '' 74 mandatoryClaims = ('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier',) 75 76 def __init__(self): 77 self.processors = {} 78 79 self.privateKey = self.config.get('infocard_acct', 'private_key_path') 80 self.privateKeyPassPhrase = self.config.get('infocard_acct', 'private_key_pass_phrase') 81 self.processors['infocard_acct'] = self._process_options('infocard_acct') 82 83 #read any sets of infocard options 84 Sets = self.config.getlist('infocard_acct', 'infocard_definitions') 85 if Sets: 86 for element in Sets: 87 name = element.strip() #remove any unsightly spaces 88 self.processors[name] = self._process_options(name) 89 90 scheme, host = urlparse.urlparse(self.env.abs_href())[:2] 91 if not scheme or not host: 92 self.log.error('Trac configuration for \'base_url\' option '\ 93 'doesn\'t contain host and scheme information.') 94 95 def _process_options(self, tag): 96 """read a block of options from the configuration and store them in a 97 local structure for later use. 98 """ 99 options = {} 100 #uniquely name this block of options using it's tag from trac.ini 101 options['blockID'] = tag 102 103 #process basic options with no defaults 104 config_fields = ['required_claims', 'optional_claims', 105 'privacy_url', 'privacy_version', 'issuer', 'associated_user', 106 'Audience'] 107 for field in config_fields: 108 data = self.config.get(tag, field) 109 if data: 110 options[field] = data 111 112 #process warning levels and debug options, these may result in upgrades 113 #or downgrades in error reporting 114 for field in event.config_fields: 115 data = self.config.get(tag, field) 116 if data: 117 options[field] = data 118 119 #process options which have defaults and need to be 120 options['debug_page'] = self.config.getbool(tag, 'debug_page', False) 121 options['header_text'] = self.config.get(tag, 'header_text', 'InfoCard Login') 122 options['help_text'] = self.config.get(tag, 'help_text', 'Use any InfoCard to login to your existing account') 123 options['token_type'] = self.config.get(tag, 'token_type', 'urn:oasis:names:tc:SAML:1.0:assertion') 124 125 #append all hard coded mandatory claims to the required claims 126 if options.has_key('required_claims'): 127 options['required_claims'] = options['required_claims'] + " " + " ".join(self.mandatoryClaims) 128 else: 129 options['required_claims'] = " " + ' '.join(self.mandatoryClaims) 130 131 #setup basic infocard processor for duty 132 processor = InfoCardProcessor() 133 if processor: 134 processor.set_decode(self.privateKey, self.privateKeyPassPhrase, True, False) 135 processor.set_claims( 136 self._get_option_from_options(options, 'required_claims'), 137 self._get_option_from_options(options, 'optional_claims')) 138 processor.set_options(options) 139 options['processorTag'] = processor 140 # self.log.debug("For %s required: %s, optional: %s" % 141 # (tag, self._get_option_from_options(options, 'required_claims'), 142 # self._get_option_from_options(options, 'optional_claims'))) 143 return options 144 145 def _was_sent_secure(self, req): 146 # self.log.error('was_sent_secure: '+req.abs_href.base) 147 # host = req.get_header('Host') 148 # if not host: 149 # self.log.error('Request missing Host header, assuming secure') 150 # return True 151 # scheme, host = urlparse.urlparse(host)[:2] 152 # if scheme == 'https': 153 return True 154 # return False 155 156 def _redirect_secure(self, req): 157 """ redirect to absolute url over https for /login """ 158 url = urlparse.urlparse(self.env.abs_href()) 159 if not url.hostname: 160 host = req.get_header('Host') 161 else: 162 host = url.hostname 163 req.redirect(urlparse.urlunparse(('https', host, 164 url.path, req.path_info, None, None))) 165 166 def authenticate(self, req): 167 """called when req.authname is accessed, since this is actually called 168 by many processes in trac, it will often be the time when the security 169 token is read and processed. """ 170 #self.log.debug('web_ui:LoginModule:authenticate' ) 171 authnanme = None 172 if req.method == 'POST' and req.path_info.startswith('/login'): 173 #try: 174 # self.log.debug('web_ui:LoginModule:authenticate:: posted request to login for \"%s\"', req.environ['REMOTE_USER']) 175 #except (NameError, KeyError): 176 req.environ['REMOTE_USER'] = self._remote_user(req) 177 #self.log.debug('web_ui:LoginModule:authenticate:: remote: \"%s\", req.environ[\'REMOTE_USER\']: \"%s\"', req.remote_user, req.environ['REMOTE_USER']) 178 authname = auth.LoginModule.authenticate(self, req) 179 #self.log.debug('web_ui:LoginModule:authenticate:: authname = \"%s\"', authname) 180 return authname 181 182 authenticate = if_enabled(authenticate) 183 184 def match_request(self, req): 185 """See if we handle the request, we watch for login/logout""" 186 if if_enabled(auth.LoginModule.match_request) \ 187 and ( (re.match(r'/login/?$', req.path_info) is not None) \ 188 or (re.match(r'/logout/?$', req.path_info) is not None)): 189 return True 190 191 return False 192 193 def process_request(self, req): 194 """ handle display of the login page, display of login results, 195 and logout requests""" 196 197 # self.log.debug('web_ui:LoginModule:process_request : %s' % 198 # (req.path_info)) 199 if req.path_info.startswith('/logout'): 200 self._cleanup_session(req) 201 if req.path_info.startswith('/login'): 202 if req.authname == 'anonymous': 203 204 if not self._was_sent_secure(req): 205 self._redirect_secure(req) 206 207 data = { 208 'title': 'Login', 209 'login_header': 'Login', 210 'error_header': 'Error', 211 'referer': self._referer(req), 212 'reset_password_enabled': AccountModule(self.env).reset_password_enabled, 213 'submit_text': 'Login', 214 'display_infocard': True, 215 'infocards': self.processors 216 } 217 218 if not req.args.get('xmlToken') and not req.args.get('cardkeyhash'): 219 data['display_infocard'] = True 220 if req.method == 'POST': 221 data['login_error'] = 'Invalid username or password' 222 else: 223 secToken = self._get_token(req) 224 cardkeyhash = None 225 if req.args.get('cardkeyhash'): 226 cardkeyhash = req.args.get('cardkeyhash') 227 elif secToken: 228 if secToken and secToken.isValid: 229 cardkeyhash = secToken.getMetaData(infocard.infocardlib.META_CardKeyHash) 230 self._setup_session(req) 231 elif secToken: 232 #invalid security token, handle error 233 data['infocard'] = secToken 234 data['title'] = 'Invalid Infocard Detail' 235 if secToken.eventLog: 236 data['events'] = secToken.eventLog.events 237 return 'infocard-detail.html', data, None 238 239 data['cardkeyhash'] = cardkeyhash 240 241 data['title'] = 'Associate' 242 data['login_header'] = 'Login to associate an InfoCard with an Account' 243 data['error_header'] = 'Warning', 244 data['login_error'] = 'Credentials have not been associated with your account' 245 data['submit_text'] = 'Associate' 246 data['blockID'] = 'infocard_acct' 247 return 'authenticate.html', data, None 248 elif req.args.get('xmlToken'): 249 secToken = self._get_token(req) 250 data = {'infocard': secToken} 251 252 if self._get_option(req, 'debug_page'): 253 self._do_debug_login(req) 254 self._setup_session(req) 255 data['title'] = 'Infocard Debug' 256 if not secToken.isValid: 257 data['title'] = 'Invalid Infocard Detail' 258 if secToken.eventLog: 259 data['events'] = secToken.eventLog.events 260 if (not secToken.isValid) or self._get_option(req, 'debug_page'): 261 return 'infocard-detail.html', data, None 262 263 self._setup_session(req) 264 return auth.LoginModule.process_request(self, req) 265 266 def _cleanup_session(self, req): 267 """We need to delete session attributes here, allow all people who 268 potentially cached information from the login to act on the logout 269 """ 270 SecTokenSessionModule(self.env).logout(req) 271 272 def _setup_session(self, req): 273 """setup session with infocard variables so we can get back to them 274 the infocard must be parsed and availible on this request 275 """ 276 SecToken