1 | # (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) |
---|
2 | # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php |
---|
3 | """ |
---|
4 | Grant roles and logins based on IP address. |
---|
5 | """ |
---|
6 | from paste.util import ip4 |
---|
7 | |
---|
8 | class GrantIPMiddleware(object): |
---|
9 | |
---|
10 | """ |
---|
11 | On each request, ``ip_map`` is checked against ``REMOTE_ADDR`` |
---|
12 | and logins and roles are assigned based on that. |
---|
13 | |
---|
14 | ``ip_map`` is a map of {ip_mask: (username, roles)}. Either |
---|
15 | ``username`` or ``roles`` may be None. Roles may also be prefixed |
---|
16 | with ``-``, like ``'-system'`` meaning that role should be |
---|
17 | revoked. ``'__remove__'`` for a username will remove the username. |
---|
18 | |
---|
19 | If ``clobber_username`` is true (default) then any user |
---|
20 | specification will override the current value of ``REMOTE_USER``. |
---|
21 | ``'__remove__'`` will always clobber the username. |
---|
22 | |
---|
23 | ``ip_mask`` is something that `paste.util.ip4:IP4Range |
---|
24 | <class-paste.util.ip4.IP4Range.html>`_ can parse. Simple IP |
---|
25 | addresses, IP/mask, ip<->ip ranges, and hostnames are allowed. |
---|
26 | """ |
---|
27 | |
---|
28 | def __init__(self, app, ip_map, clobber_username=True): |
---|
29 | self.app = app |
---|
30 | self.ip_map = [] |
---|
31 | for key, value in ip_map.items(): |
---|
32 | self.ip_map.append((ip4.IP4Range(key), |
---|
33 | self._convert_user_role(value[0], value[1]))) |
---|
34 | self.clobber_username = clobber_username |
---|
35 | |
---|
36 | def _convert_user_role(self, username, roles): |
---|
37 | if roles and isinstance(roles, basestring): |
---|
38 | roles = roles.split(',') |
---|
39 | return (username, roles) |
---|
40 | |
---|
41 | def __call__(self, environ, start_response): |
---|
42 | addr = ip4.ip2int(environ['REMOTE_ADDR'], False) |
---|
43 | remove_user = False |
---|
44 | add_roles = [] |
---|
45 | for range, (username, roles) in self.ip_map: |
---|
46 | if addr in range: |
---|
47 | if roles: |
---|
48 | add_roles.extend(roles) |
---|
49 | if username == '__remove__': |
---|
50 | remove_user = True |
---|
51 | elif username: |
---|
52 | if (not environ.get('REMOTE_USER') |
---|
53 | or self.clobber_username): |
---|
54 | environ['REMOTE_USER'] = username |
---|
55 | if (remove_user and 'REMOTE_USER' in environ): |
---|
56 | del environ['REMOTE_USER'] |
---|
57 | if roles: |
---|
58 | self._set_roles(environ, add_roles) |
---|
59 | return self.app(environ, start_response) |
---|
60 | |
---|
61 | def _set_roles(self, environ, roles): |
---|
62 | cur_roles = environ.get('REMOTE_USER_TOKENS', '').split(',') |
---|
63 | # Get rid of empty roles: |
---|
64 | cur_roles = filter(None, cur_roles) |
---|
65 | remove_roles = [] |
---|
66 | for role in roles: |
---|
67 | if role.startswith('-'): |
---|
68 | remove_roles.append(role[1:]) |
---|
69 | else: |
---|
70 | if role not in cur_roles: |
---|
71 | cur_roles.append(role) |
---|
72 | for role in remove_roles: |
---|
73 | if role in cur_roles: |
---|
74 | cur_roles.remove(role) |
---|
75 | environ['REMOTE_USER_TOKENS'] = ','.join(cur_roles) |
---|
76 | |
---|
77 | |
---|
78 | def make_grantip(app, global_conf, clobber_username=False, **kw): |
---|
79 | """ |
---|
80 | Grant roles or usernames based on IP addresses. |
---|
81 | |
---|
82 | Config looks like this:: |
---|
83 | |
---|
84 | [filter:grant] |
---|
85 | use = egg:Paste#grantip |
---|
86 | clobber_username = true |
---|
87 | # Give localhost system role (no username): |
---|
88 | 127.0.0.1 = -:system |
---|
89 | # Give everyone in 192.168.0.* editor role: |
---|
90 | 192.168.0.0/24 = -:editor |
---|
91 | # Give one IP the username joe: |
---|
92 | 192.168.0.7 = joe |
---|
93 | # And one IP is should not be logged in: |
---|
94 | 192.168.0.10 = __remove__:-editor |
---|
95 | |
---|
96 | """ |
---|
97 | from paste.deploy.converters import asbool |
---|
98 | clobber_username = asbool(clobber_username) |
---|
99 | ip_map = {} |
---|
100 | for key, value in kw.items(): |
---|
101 | if ':' in value: |
---|
102 | username, role = value.split(':', 1) |
---|
103 | else: |
---|
104 | username = value |
---|
105 | role = '' |
---|
106 | if username == '-': |
---|
107 | username = '' |
---|
108 | if role == '-': |
---|
109 | role = '' |
---|
110 | ip_map[key] = value |
---|
111 | return GrantIPMiddleware(app, ip_map, clobber_username) |
---|
112 | |
---|
113 | |
---|