Ticket #210: MapFishAuthentication_v2.patch
| File MapFishAuthentication_v2.patch, 13.2 kB (added by sypasche, 4 months ago) |
|---|
-
server/python/mapfish/controllers/auth_proxy.py
old new 1 from pylons import request, response 2 from pylons.controllers import WSGIController 3 4 from authkit.authorize.pylons_adaptors import authorized 5 from authkit.authorize import NotAuthorizedError 6 import re 7 import cgi 8 import urlparse 9 import urllib2 10 from urllib import urlencode 11 12 def add_routes(map, url, controller): 13 """Add the pylons routes for a proxy 14 """ 15 map.connect(url + "*more", controller=controller, action='get', 16 conditions=dict(method=['GET'])) 17 18 class Layer(object): 19 def __init__(self, alias, url, layers, **kwargs): 20 self.alias = alias 21 self.url = url 22 self.layers = layers 23 self.options = kwargs 24 25 def get_permissions(self): 26 return { 27 "url": self.url, 28 "layers": dict(((name, authorized(perm)) for (name, perm) in 29 self.layers.iteritems())) 30 } 31 32 def check_permissions(self): 33 # Check layers 34 layers = self.get_requested_layers() 35 for layer in layers: 36 if self.layers.has_key(layer): 37 perm = self.layers[layer] 38 elif self.layers.has_key("DEFAULT"): 39 perm = self.layers["DEFAULT"] 40 else: 41 continue 42 if not authorized(perm): 43 return False, "Not allowed to access layer: %s" % cgi.escape(layer) 44 45 # TODO Check bbox 46 47 return True, "Access allowed" 48 49 def get_requested_layers(self): 50 raise NotImplementedError() 51 52 def get_requested_bbox(self): 53 raise NotImplementedError() 54 55 class WMSLayer(Layer): 56 def get_param(self, name): 57 """Returns the request parameter given in argument, ignoring case. 58 If not found, None is returned. 59 """ 60 for k in request.params.keys(): 61 if k.lower() == name: 62 return request.params[k] 63 return None 64 65 def get_requested_layers(self): 66 layers = self.get_param("layers") 67 if not layers: 68 return [] 69 return layers.split(",") 70 71 class TileCacheLayer(Layer): 72 def get_requested_layers(self): 73 raise NotImplementedError("TODO TileCacheLayer::get_requested_layers") 74 75 76 class AuthProxyController(WSGIController): 77 def __init__(self): 78 self.layers = [] 79 self.alias_to_layer = {} 80 81 def set_layers(self, layers): 82 self.alias_to_layers = dict(((layer.alias, layer) for layer in layers)) 83 self.layers = layers 84 85 def _get_permissions(self): 86 return { 87 "layer": [l.get_permissions() for l in self.layers] 88 } 89 90 def get(self, more): 91 """Proxy action 92 """ 93 # extract the first segment of the url 94 m = re.match("/?([^/]+)(/?.*)$", more) 95 if not m: 96 response.status_code = 500 97 return "Invalid URL, missing alias in the path" 98 alias, more = m.groups() 99 100 if not self.alias_to_layers.has_key(alias): 101 response.status_code = 500 102 return "Wrong alias %s" % cgi.escape(alias) 103 layer = self.alias_to_layers[alias] 104 105 allowed, msg = layer.check_permissions() 106 if not allowed: 107 # We don't use 403 to avoid triggering AuthKit login dialog 108 response.status_code = 406 109 return msg 110 111 url = layer.url + more 112 return self._proxy(url) 113 114 def _proxy(self, url): 115 """Do the actual action of proxying the call. 116 """ 117 query = urlencode(request.params) 118 full_url = url 119 if query: 120 if not full_url.endswith("?"): 121 full_url += "?" 122 full_url += query 123 124 # build the request with its headers 125 print "full_url", full_url 126 req = urllib2.Request(url=full_url) 127 for header in request.headers: 128 if header.lower() == "host": 129 req.add_header(header, urlparse.urlparse(url)[1]) 130 else: 131 req.add_header(header, request.headers[header]) 132 res = urllib2.urlopen(req) 133 134 # add response headers 135 i = res.info() 136 response.status_code = res.code 137 got_content_length = False 138 for header in i: 139 response.headers[header] =i[header] 140 if header.lower() == "content-length": 141 got_content_length = True 142 143 # return the result 144 result = res.read() 145 res.close() 146 147 if not got_content_length: 148 response.headers['content-length'] = str(len(result)) 149 return result 150 -
server/python/mapfish/controllers/security.py
old new 1 import logging 2 3 from pylons.controllers import WSGIController 4 from pylons.decorators import jsonify 5 from pylons import config 6 from authkit.authorize.pylons_adaptors import authorize, authorized 7 from authkit.permissions import ValidAuthKitUser 8 from authkit.authorize import NotAuthorizedError 9 import simplejson 10 11 import sys 12 import os 13 import os.path 14 import inspect 15 16 17 log = logging.getLogger(__name__) 18 19 class SecurityController(WSGIController): 20 def _get_master_permissions(self): 21 """Read the permissions in the config/permissions.json file and return 22 an object 23 """ 24 perm_json = os.path.join(config.here, config['pylons.package'], 25 "config", "permissions.json") 26 27 permissions = {} 28 modulename = "%s.config.permissions" % config['pylons.package'] 29 try: 30 mod = __import__(modulename) 31 for comp in modulename.split(".")[1:]: 32 mod = getattr(mod, comp) 33 permissions = mod.permissions 34 except ImportError: 35 log.debug("Couldn't find permission configuration module %s" % modulename) 36 37 # compute permissions 38 # TODO: calling authorized should be propagated inside nested dicts. 39 permissions = dict(((key, authorized(perm)) for (key, perm) in 40 permissions.iteritems())) 41 42 return permissions 43 44 @jsonify 45 def permissions(self): 46 controllers_module_name = config['pylons.package'] + '.controllers' 47 __import__(controllers_module_name) 48 module = sys.modules[controllers_module_name] 49 50 permissions = self._get_master_permissions() 51 module_names = [] 52 53 # Iterate through all the controllers to call the method "get_permissions" 54 # at the module level if available. 55 # The value returned is merged into the permission object. 56 57 if hasattr(module, '__path__'): 58 for file in os.listdir(module.__path__[0]): 59 if not file.endswith(".py"): 60 continue 61 path = os.path.join(module.__path__[0], file) 62 module_name = inspect.getmodulename(file) 63 if not module_name or module_name == '__init__': 64 continue 65 66 method_name = "get_permissions" 67 full_module_name = controllers_module_name + '.' + module_name 68 try: 69 __import__(full_module_name) 70 except Exception, e: 71 log.warn("Failure while fetching controller in module %s (%s: %s)", 72 full_module_name, e.__class__, e) 73 continue 74 mod = sys.modules[full_module_name] 75 if not hasattr(mod, method_name): 76 continue 77 get_permissions_method = getattr(mod, method_name) 78 79 perms = get_permissions_method() 80 if not isinstance(perms, dict): 81 raise Exception("Invalid permission type, should be dict, was %s" % type(perms)) 82 83 # TODO: should use a recursive udpate 84 permissions.update(perms) 85 86 return {"permissions": permissions} 87 88 # XXX for debug 89 @authorize(ValidAuthKitUser()) 90 def log_me_in(self): 91 return "This page requires login <br/><a href='/logout'>logout</a>" 92 93 # XXX for debug 94 # XXX could be used by client to retrieve user/roles. 95 @jsonify 96 def user_roles(self): 97 user = request.environ.get("REMOTE_USER", "anonymous") 98 users = request.environ['authkit.users'] 99 roles = [] 100 for role in users.list_roles(): 101 if users.user_has_role(user, role): 102 roles.append(role) 103 return {"user": user, "roles": roles} 104 105 class Deny: 106 """A special AuthKit permission that always deny access 107 """ 108 def check(self, app, environ, start_response): 109 raise NotAuthorizedError("Not authorized at all") -
server/python/mapfish/lib/user_auth.py
old new 1 from authkit.users.sqlalchemy_driver import UsersFromDatabase 2 from time import time 3 4 class CachedUsersFromDatabase(UsersFromDatabase): 5 """ 6 Caching proxy for AuthKit's class. 7 8 It's not 100% thread safe, but who cares if the request is done twice for 9 nothing? 10 """ 11 12 #number of seconds between two purge of the caches 13 REFRESH_PERIOD = 60 14 15 def __init__(self, model, encrypt=None): 16 UsersFromDatabase.__init__(self, model, encrypt) 17 self.nextRefresh = 0 18 self.checkCache() 19 20 def user_delete(self, username): 21 self.users.pop(username) 22 self.fullUsers.pop(username) 23 return UsersFromDatabase.user_delete(self, username) 24 25 def user_exists(self, username): 26 self.checkCache() 27 if self.users.has_key(username): 28 return self.users.get(username) 29 else: 30 result = UsersFromDatabase.user_exists(self, username) 31 self.users[username] = result 32 return result 33 34 def role_delete(self, rolename): 35 self.roles.pop(rolename) 36 return UsersFromDatabase.role_delete(self, rolename) 37 38 def role_exists(self, rolename): 39 self.checkCache() 40 if self.roles.has_key(rolename): 41 return self.roles.get(rolename) 42 else: 43 result = UsersFromDatabase.role_exists(self, rolename) 44 self.roles[rolename] = result 45 return result 46 47 def group_delete(self, groupname): 48 self.groups.pop(groupname) 49 return UsersFromDatabase.group_delete(self, groupname) 50 51 def group_exists(self, groupname): 52 self.checkCache() 53 if self.groups.has_key(groupname): 54 return self.groups.get(groupname) 55 else: 56 result = UsersFromDatabase.group_exists(self, groupname) 57 self.groups[groupname] = result 58 return result 59 60 def user(self, username): 61 self.checkCache() 62 if self.fullUsers.has_key(username): 63 return self.fullUsers.get(username) 64 else: 65 result = UsersFromDatabase.user(self, username) 66 self.fullUsers[username] = result 67 return result 68 69 def user_roles(self, username): 70 return self.user(username)['roles'] 71 72 def user_group(self, username): 73 return self.user(username)['group'] 74 75 def user_password(self, username): 76 return self.user(username)['password'] 77 78 def user_has_role(self, username, role): 79 return self.user_roles(username).count(role)>0 80 81 def user_has_group(self, username, group): 82 return self.user_group(username)==group 83 84 def user_has_password(self, username, password): 85 return self.user(username)['password']==self.encrypt(password) 86 87 def user_set_username(self, username, new_username): 88 self.fullUsers.pop(username) 89 UsersFromDatabase.user_set_username(self, username, new_username) 90 91 def user_set_group(self, username, group, auto_add_group=False): 92 self.fullUsers.pop(username) 93 self.groups.pop(group) 94 UsersFromDatabase.user_set_group(self, username, group, auto_add_group) 95 96 def user_add_role(self, username, role, auto_add_role=False): 97 self.fullUsers.pop(username) 98 self.roles.pop(role) 99 UsersFromDatabase.user_add_role(self, username, role, auto_add_role) 100 101 def user_remove_group(self, username): 102 self.fullUsers.pop(username) 103 UsersFromDatabase.user_remove_group(self, username, group) 104 105 def user_remove_role(self, username, role): 106 self.fullUsers.pop(username) 107 UsersFromDatabase.user_remove_role(self, username, role) 108 109 def checkCache(self): 110 if self.nextRefresh <= time(): 111 self.nextRefresh = time() + self.REFRESH_PERIOD 112 self.users = {} 113 self.roles = {} 114 self.groups = {} 115 self.fullUsers = {}