diff options
Diffstat (limited to 'paste/auth/grantip.py')
-rw-r--r-- | paste/auth/grantip.py | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/paste/auth/grantip.py b/paste/auth/grantip.py new file mode 100644 index 0000000..3fe6e1c --- /dev/null +++ b/paste/auth/grantip.py @@ -0,0 +1,114 @@ +# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) +# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php +""" +Grant roles and logins based on IP address. +""" +import six +from paste.util import ip4 + +class GrantIPMiddleware(object): + + """ + On each request, ``ip_map`` is checked against ``REMOTE_ADDR`` + and logins and roles are assigned based on that. + + ``ip_map`` is a map of {ip_mask: (username, roles)}. Either + ``username`` or ``roles`` may be None. Roles may also be prefixed + with ``-``, like ``'-system'`` meaning that role should be + revoked. ``'__remove__'`` for a username will remove the username. + + If ``clobber_username`` is true (default) then any user + specification will override the current value of ``REMOTE_USER``. + ``'__remove__'`` will always clobber the username. + + ``ip_mask`` is something that `paste.util.ip4:IP4Range + <class-paste.util.ip4.IP4Range.html>`_ can parse. Simple IP + addresses, IP/mask, ip<->ip ranges, and hostnames are allowed. + """ + + def __init__(self, app, ip_map, clobber_username=True): + self.app = app + self.ip_map = [] + for key, value in ip_map.items(): + self.ip_map.append((ip4.IP4Range(key), + self._convert_user_role(value[0], value[1]))) + self.clobber_username = clobber_username + + def _convert_user_role(self, username, roles): + if roles and isinstance(roles, six.string_types): + roles = roles.split(',') + return (username, roles) + + def __call__(self, environ, start_response): + addr = ip4.ip2int(environ['REMOTE_ADDR'], False) + remove_user = False + add_roles = [] + for range, (username, roles) in self.ip_map: + if addr in range: + if roles: + add_roles.extend(roles) + if username == '__remove__': + remove_user = True + elif username: + if (not environ.get('REMOTE_USER') + or self.clobber_username): + environ['REMOTE_USER'] = username + if (remove_user and 'REMOTE_USER' in environ): + del environ['REMOTE_USER'] + if roles: + self._set_roles(environ, add_roles) + return self.app(environ, start_response) + + def _set_roles(self, environ, roles): + cur_roles = environ.get('REMOTE_USER_TOKENS', '').split(',') + # Get rid of empty roles: + cur_roles = list(filter(None, cur_roles)) + remove_roles = [] + for role in roles: + if role.startswith('-'): + remove_roles.append(role[1:]) + else: + if role not in cur_roles: + cur_roles.append(role) + for role in remove_roles: + if role in cur_roles: + cur_roles.remove(role) + environ['REMOTE_USER_TOKENS'] = ','.join(cur_roles) + + +def make_grantip(app, global_conf, clobber_username=False, **kw): + """ + Grant roles or usernames based on IP addresses. + + Config looks like this:: + + [filter:grant] + use = egg:Paste#grantip + clobber_username = true + # Give localhost system role (no username): + 127.0.0.1 = -:system + # Give everyone in 192.168.0.* editor role: + 192.168.0.0/24 = -:editor + # Give one IP the username joe: + 192.168.0.7 = joe + # And one IP is should not be logged in: + 192.168.0.10 = __remove__:-editor + + """ + from paste.deploy.converters import asbool + clobber_username = asbool(clobber_username) + ip_map = {} + for key, value in kw.items(): + if ':' in value: + username, role = value.split(':', 1) + else: + username = value + role = '' + if username == '-': + username = '' + if role == '-': + role = '' + ip_map[key] = value + return GrantIPMiddleware(app, ip_map, clobber_username) + + |