1# Copyright The Cloud Custodian Authors.
2# SPDX-License-Identifier: Apache-2.0
3import re
4
5from c7n.utils import type_schema
6from c7n_gcp.filters.iampolicy import IamPolicyFilter
7from c7n_gcp.provider import resources
8from c7n_gcp.query import QueryResourceManager, TypeInfo, ChildResourceManager, ChildTypeInfo
9from c7n_gcp.actions import MethodAction
10from c7n_gcp.filters.timerange import TimeRangeFilter
11
12
13@resources.register('project-role')
14class ProjectRole(QueryResourceManager):
15 """GCP Project Role
16 https://cloud.google.com/iam/docs/reference/rest/v1/organizations.roles#Role
17 """
18 class resource_type(TypeInfo):
19 service = 'iam'
20 version = 'v1'
21 component = 'projects.roles'
22 enum_spec = ('list', 'roles[]', None)
23 scope = 'project'
24 scope_key = 'parent'
25 scope_template = 'projects/{}'
26 name = id = "name"
27 default_report_fields = ['name', 'title', 'description', 'stage', 'deleted']
28 asset_type = "iam.googleapis.com/Role"
29 urn_component = "project-role"
30 urn_id_segments = (-1,) # Just use the last segment of the id in the URN
31
32 @staticmethod
33 def get(client, resource_info):
34 return client.execute_query(
35 'get', verb_arguments={
36 'name': 'projects/{}/roles/{}'.format(
37 resource_info['project_id'],
38 resource_info['role_name'].rsplit('/', 1)[-1])})
39
40
41@resources.register('service-account')
42class ServiceAccount(QueryResourceManager):
43
44 class resource_type(TypeInfo):
45 service = 'iam'
46 version = 'v1'
47 component = 'projects.serviceAccounts'
48 enum_spec = ('list', 'accounts[]', [])
49 scope = 'project'
50 scope_key = 'name'
51 scope_template = 'projects/{}'
52 id = "name"
53 name = 'email'
54 default_report_fields = ['name', 'displayName', 'email', 'description', 'disabled']
55 asset_type = "iam.googleapis.com/ServiceAccount"
56 metric_key = 'resource.labels.unique_id'
57 urn_component = 'service-account'
58 urn_id_path = 'email'
59
60 @staticmethod
61 def get(client, resource_info):
62 return client.execute_query(
63 'get', verb_arguments={
64 'name': 'projects/{}/serviceAccounts/{}'.format(
65 resource_info['project_id'],
66 resource_info['email_id'])})
67
68 @staticmethod
69 def get_metric_resource_name(resource):
70 return resource["uniqueId"]
71
72
73@ServiceAccount.action_registry.register('delete')
74class DeleteServiceAccount(MethodAction):
75 schema = type_schema('delete')
76 method_spec = {'op': 'delete'}
77 permissions = ("iam.serviceAccounts.delete",)
78
79 def get_resource_params(self, m, r):
80 return {'name': r['name']}
81
82
83@ServiceAccount.action_registry.register('enable')
84class EnableServiceAccount(MethodAction):
85 schema = type_schema('enable')
86 method_spec = {'op': 'enable'}
87 permissions = ("iam.serviceAccounts.enable",)
88
89 def get_resource_params(self, m, r):
90 return {'name': r['name']}
91
92
93@ServiceAccount.action_registry.register('disable')
94class DisableServiceAccount(MethodAction):
95 schema = type_schema('disable')
96 method_spec = {'op': 'disable'}
97 permissions = ("iam.serviceAccounts.disable",)
98
99 def get_resource_params(self, m, r):
100 return {'name': r['name']}
101
102
103@ServiceAccount.filter_registry.register('iam-policy')
104class ServiceAccountIamPolicyFilter(IamPolicyFilter):
105 """
106 Overrides the base implementation to process service account resources correctly.
107 """
108 permissions = ('resourcemanager.projects.getIamPolicy',)
109
110
111@resources.register('service-account-key')
112class ServiceAccountKey(ChildResourceManager):
113 """GCP Resource
114 https://cloud.google.com/iam/docs/reference/rest/v1/projects.serviceAccounts.keys
115 """
116 def _get_parent_resource_info(self, child_instance):
117 project_id, sa = re.match(
118 'projects/(.*?)/serviceAccounts/(.*?)/keys/.*',
119 child_instance['name']).groups()
120 return {'project_id': project_id,
121 'email_id': sa}
122
123 def get_resource_query(self):
124 """Does nothing as self does not need query values unlike its parent
125 which receives them with the use_child_query flag."""
126 pass
127
128 class resource_type(ChildTypeInfo):
129 service = 'iam'
130 version = 'v1'
131 component = 'projects.serviceAccounts.keys'
132 enum_spec = ('list', 'keys[]', [])
133 scope = None
134 scope_key = 'name'
135 name = id = 'name'
136 default_report_fields = ['name', 'privateKeyType', 'keyAlgorithm',
137 'validAfterTime', 'validBeforeTime', 'keyOrigin', 'keyType']
138 parent_spec = {
139 'resource': 'service-account',
140 'child_enum_params': [
141 ('name', 'name')
142 ],
143 'use_child_query': True
144 }
145 asset_type = "iam.googleapis.com/ServiceAccountKey"
146 scc_type = "google.iam.ServiceAccountKey"
147 permissions = ("iam.serviceAccounts.list",)
148 metric_key = 'metric.labels.key_id'
149 urn_component = "service-account-key"
150 urn_id_segments = (3, 5)
151
152 @staticmethod
153 def get(client, resource_info):
154 project, sa, key = re.match(
155 '.*?/projects/(.*?)/serviceAccounts/(.*?)/keys/(.*)',
156 resource_info['resourceName']).groups()
157 return client.execute_query(
158 'get', {
159 'name': 'projects/{}/serviceAccounts/{}/keys/{}'.format(
160 project, sa, key)})
161
162 @staticmethod
163 def get_metric_resource_name(resource):
164 return resource["name"].split('/')[-1]
165
166
167@ServiceAccountKey.action_registry.register('delete')
168class DeleteServiceAccountKey(MethodAction):
169
170 schema = type_schema('delete')
171 method_spec = {'op': 'delete'}
172 permissions = ("iam.serviceAccountKeys.delete",)
173
174 def get_resource_params(self, m, r):
175 return {'name': r['name']}
176
177
178@resources.register('iam-role')
179class Role(QueryResourceManager):
180 """GCP Organization Role
181 https://cloud.google.com/iam/docs/reference/rest/v1/organizations.roles#Role
182 """
183 class resource_type(TypeInfo):
184 service = 'iam'
185 version = 'v1'
186 component = 'roles'
187 enum_spec = ('list', 'roles[]', None)
188 scope = "global"
189 name = id = "name"
190 default_report_fields = ['name', 'title', 'description', 'stage', 'deleted']
191 asset_type = "iam.googleapis.com/Role"
192 urn_component = "role"
193 # Don't show the project ID in the URN.
194 urn_has_project = False
195 urn_id_segments = (-1,) # Just use the last segment of the id in the URN
196
197 @staticmethod
198 def get(client, resource_info):
199 return client.execute_command(
200 'get', {
201 'name': 'roles/{}'.format(
202 resource_info['name'])})
203
204
205@resources.register('api-key')
206class ApiKey(QueryResourceManager):
207 """GCP API Key
208 https://cloud.google.com/api-keys/docs/reference/rest/v2/projects.locations.keys#Key
209 """
210 class resource_type(TypeInfo):
211 service = 'apikeys'
212 version = 'v2'
213 component = 'projects.locations.keys'
214 enum_spec = ('list', 'keys[]', None)
215 scope = 'project'
216 scope_key = 'parent'
217 scope_template = 'projects/{}/locations/global'
218 name = id = "name"
219 default_report_fields = ['name', 'displayName', 'createTime', 'updateTime']
220 asset_type = "apikeys.googleapis.com/projects.locations.keys"
221
222
223@ApiKey.filter_registry.register('time-range')
224class ApiKeyTimeRangeFilter(TimeRangeFilter):
225 """Filters api keys that have been changed during a specific time range.
226
227 .. code-block:: yaml
228
229 policies:
230 - name: api_keys_not_rotated_more_than_90_days
231 resource: gcp.api-key
232 filters:
233 - not:
234 - type: time-range
235 value: 90
236 """
237 create_time_field_name = 'createTime'
238 expire_time_field_name = 'updateTime'
239 permissions = ('apikeys.keys.list', )