OLD | NEW |
1 # Copyright 2015 The Swarming Authors. All rights reserved. | 1 # Copyright 2015 The Swarming Authors. All rights reserved. |
2 # Use of this source code is governed by the Apache v2.0 license that can be | 2 # Use of this source code is governed by the Apache v2.0 license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import re | 5 import re |
6 | 6 |
7 from components import auth | 7 from components import auth |
8 from components import config | 8 from components import config |
9 from components import utils | 9 from components import utils |
10 | 10 |
11 from proto import service_config_pb2 | 11 from proto import service_config_pb2 |
12 import common | 12 import common |
| 13 import projects |
| 14 import services |
13 import storage | 15 import storage |
14 | 16 |
15 | 17 |
16 @utils.memcache('acl.cfg', time=60) | 18 @utils.memcache('acl.cfg', time=60) |
17 def read_acl_cfg(): | 19 def read_acl_cfg(): |
18 return storage.get_self_config_async( | 20 return storage.get_self_config_async( |
19 common.ACL_FILENAME, service_config_pb2.AclCfg).get_result() | 21 common.ACL_FILENAME, service_config_pb2.AclCfg).get_result() |
20 | 22 |
| 23 def _has_access(access_list): |
| 24 cur_ident = auth.get_current_identity().to_bytes() |
| 25 for ac in access_list: |
| 26 if ac.startswith('group:'): |
| 27 if auth.is_group_member(ac.split(':', 2)[1]): |
| 28 return True |
| 29 else: |
| 30 identity_str = ac |
| 31 if ':' not in identity_str: |
| 32 identity_str = 'user:%s' % identity_str |
| 33 if cur_ident == identity_str: |
| 34 return True |
| 35 return False |
21 | 36 |
22 def can_read_config_set(config_set, headers=None): | 37 |
| 38 def can_read_config_set(config_set): |
23 """Returns True if current requester has access to the |config_set|. | 39 """Returns True if current requester has access to the |config_set|. |
24 | 40 |
25 Raise: | 41 Raise: |
26 ValueError if config_set is malformed. | 42 ValueError if config_set is malformed. |
27 """ | 43 """ |
28 try: | 44 try: |
29 service_match = config.SERVICE_CONFIG_SET_RGX.match(config_set) | 45 service_match = config.SERVICE_CONFIG_SET_RGX.match(config_set) |
30 if service_match: | 46 if service_match: |
31 service_name = service_match.group(1) | 47 service_name = service_match.group(1) |
32 return can_read_service_config(service_name, headers=headers) | 48 return has_service_access(service_name) |
33 | 49 |
34 project_match = config.PROJECT_CONFIG_SET_RGX.match(config_set) | 50 project_match = config.PROJECT_CONFIG_SET_RGX.match(config_set) |
35 if project_match: | 51 if project_match: |
36 project_id = project_match.group(1) | 52 project_id = project_match.group(1) |
37 return can_read_project_config(project_id) | 53 return has_project_access(project_id) |
38 | 54 |
39 ref_match = config.REF_CONFIG_SET_RGX.match(config_set) | 55 ref_match = config.REF_CONFIG_SET_RGX.match(config_set) |
40 if ref_match: | 56 if ref_match: |
41 project_id = ref_match.group(1) | 57 project_id = ref_match.group(1) |
42 return can_read_project_config(project_id) | 58 return has_project_access(project_id) |
43 | 59 |
44 except ValueError: # pragma: no cover | 60 except ValueError: # pragma: no cover |
45 # Make sure we don't let ValueError raise for a reason different than | 61 # Make sure we don't let ValueError raise for a reason different than |
46 # malformed config_set. | 62 # malformed config_set. |
47 logging.exception('Unexpected ValueError in can_read_config_set()') | 63 logging.exception('Unexpected ValueError in can_read_config_set()') |
48 return False | 64 return False |
49 raise ValueError() | 65 raise ValueError() |
50 | 66 |
51 | 67 |
52 def can_read_service_config(service_id, headers=None): | 68 def has_service_access(service_id): |
53 """Returns True if current requester can read service configs. | 69 """Returns True if current requester can read service configs. |
54 | 70 |
55 If X-Appengine-Inbound-Appid header matches service_id, the permission is | 71 An app <app-id> has access to configs of service with id <app-id>. |
56 granted. | |
57 """ | 72 """ |
58 assert isinstance(service_id, basestring) | 73 assert isinstance(service_id, basestring) |
59 assert service_id | 74 assert service_id |
60 | 75 |
61 group = read_acl_cfg().service_access_group | 76 if auth.is_admin(): |
| 77 return True |
| 78 |
| 79 service_cfg = services.get_service_async(service_id).get_result() |
| 80 return service_cfg and _has_access(service_cfg.access) |
| 81 |
| 82 |
| 83 def has_project_access(project_id): |
| 84 metadata = projects.get_metadata(project_id) |
| 85 super_group = read_acl_cfg().project_access_group |
62 return ( | 86 return ( |
63 auth.is_admin() or | 87 auth.is_admin() or |
64 group and auth.is_group_member(group) or | 88 super_group and auth.is_group_member(super_group) or |
65 (headers or {}).get('X-Appengine-Inbound-Appid') == service_id | 89 metadata and _has_access(metadata.access) |
66 ) | 90 ) |
67 | |
68 | |
69 # pylint: disable=W0613 | |
70 def can_read_project_config(project_id): # pragma: no cover | |
71 return has_project_access() | |
72 | |
73 | |
74 def can_read_project_list(): # pragma: no cover | |
75 return has_project_access() | |
76 | |
77 | |
78 def has_project_access(): | |
79 group = read_acl_cfg().project_access_group | |
80 return auth.is_admin() or (group and auth.is_group_member(group)) | |
OLD | NEW |