keystone  18.0.0
About: OpenStack Keystone (Core Service: Identity) provides an authentication and authorization service for other OpenStack services. Provides a catalog of endpoints for all OpenStack services.
The "Victoria" series (maintained release).
  Fossies Dox: keystone-18.0.0.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

projects.py
Go to the documentation of this file.
1 # Licensed under the Apache License, Version 2.0 (the "License"); you may
2 # not use this file except in compliance with the License. You may obtain
3 # a copy of the License at
4 #
5 # http://www.apache.org/licenses/LICENSE-2.0
6 #
7 # Unless required by applicable law or agreed to in writing, software
8 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10 # License for the specific language governing permissions and limitations
11 # under the License.
12 
13 # This file handles all flask-restful resources for /v3/projects
14 
15 import functools
16 
17 import flask
18 import http.client
19 
20 from keystone.common import json_home
21 from keystone.common import provider_api
22 from keystone.common import rbac_enforcer
23 from keystone.common import validation
24 import keystone.conf
25 from keystone import exception
26 from keystone.i18n import _
27 from keystone.resource import schema
28 from keystone.server import flask as ks_flask
29 
30 CONF = keystone.conf.CONF
31 ENFORCER = rbac_enforcer.RBACEnforcer
32 PROVIDERS = provider_api.ProviderAPIs
33 
34 
36  target = {}
37  try:
38  target['project'] = PROVIDERS.resource_api.get_project(
39  flask.request.view_args.get('project_id')
40  )
41  except exception.NotFound: # nosec
42  # Defer existence in the event the project doesn't exist, we'll
43  # check this later anyway.
44  pass
45 
46  return target
47 
48 
49 class ProjectResource(ks_flask.ResourceBase):
50  collection_key = 'projects'
51  member_key = 'project'
52  get_member_from_driver = PROVIDERS.deferred_provider_lookup(
53  api='resource_api', method='get_project')
54 
55  def _expand_project_ref(self, ref):
56  parents_as_list = self.query_filter_is_true('parents_as_list')
57  parents_as_ids = self.query_filter_is_true('parents_as_ids')
58 
59  subtree_as_list = self.query_filter_is_true('subtree_as_list')
60 
61  subtree_as_ids = self.query_filter_is_true('subtree_as_ids')
62  include_limits = self.query_filter_is_true('include_limits')
63 
64  # parents_as_list and parents_as_ids are mutually exclusive
65  if parents_as_list and parents_as_ids:
66  msg = _('Cannot use parents_as_list and parents_as_ids query '
67  'params at the same time.')
68  raise exception.ValidationError(msg)
69 
70  # subtree_as_list and subtree_as_ids are mutually exclusive
71  if subtree_as_list and subtree_as_ids:
72  msg = _('Cannot use subtree_as_list and subtree_as_ids query '
73  'params at the same time.')
74  raise exception.ValidationError(msg)
75 
76  if parents_as_list:
77  parents = PROVIDERS.resource_api.list_project_parents(
78  ref['id'], self.oslo_context.user_id, include_limits)
79  ref['parents'] = [self.wrap_member(p)
80  for p in parents]
81  elif parents_as_ids:
82  ref['parents'] = PROVIDERS.resource_api.get_project_parents_as_ids(
83  ref
84  )
85 
86  if subtree_as_list:
87  subtree = PROVIDERS.resource_api.list_projects_in_subtree(
88  ref['id'], self.oslo_context.user_id, include_limits)
89  ref['subtree'] = [self.wrap_member(p)
90  for p in subtree]
91  elif subtree_as_ids:
92  ref['subtree'] = (
93  PROVIDERS.resource_api.get_projects_in_subtree_as_ids(
94  ref['id']
95  )
96  )
97 
98  def _get_project(self, project_id):
99  """Get project.
100 
101  GET/HEAD /v3/projects/{project_id}
102  """
103  ENFORCER.enforce_call(
104  action='identity:get_project',
105  build_target=_build_project_target_enforcement
106  )
107  project = PROVIDERS.resource_api.get_project(project_id)
108  self._expand_project_ref(project)
109  return self.wrap_member(project)
110 
111  def _list_projects(self):
112  """List projects.
113 
114  GET/HEAD /v3/projects
115  """
116  filters = ('domain_id', 'enabled', 'name', 'parent_id', 'is_domain')
117  target = None
118  if self.oslo_context.domain_id:
119  target = {'domain_id': self.oslo_context.domain_id}
120  ENFORCER.enforce_call(action='identity:list_projects',
121  filters=filters,
122  target_attr=target)
123  hints = self.build_driver_hints(filters)
124 
125  # If 'is_domain' has not been included as a query, we default it to
126  # False (which in query terms means '0')
127  if 'is_domain' not in flask.request.args:
128  hints.add_filter('is_domain', '0')
129 
130  tag_params = ['tags', 'tags-any', 'not-tags', 'not-tags-any']
131  for t in tag_params:
132  if t in flask.request.args:
133  hints.add_filter(t, flask.request.args[t])
134  refs = PROVIDERS.resource_api.list_projects(hints=hints)
135  if self.oslo_context.domain_id:
136  domain_id = self.oslo_context.domain_id
137  filtered_refs = [
138  ref for ref in refs if ref['domain_id'] == domain_id
139  ]
140  else:
141  filtered_refs = refs
142  return self.wrap_collection(filtered_refs, hints=hints)
143 
144  def get(self, project_id=None):
145  """Get project or list projects.
146 
147  GET/HEAD /v3/projects
148  GET/HEAD /v3/projects/{project_id}
149  """
150  if project_id is not None:
151  return self._get_project(project_id)
152  else:
153  return self._list_projects()
154 
155  def post(self):
156  """Create project.
157 
158  POST /v3/projects
159  """
160  project = self.request_body_json.get('project', {})
161  target = {'project': project}
162  ENFORCER.enforce_call(
163  action='identity:create_project', target_attr=target
164  )
165  validation.lazy_validate(schema.project_create, project)
166  project = self._assign_unique_id(project)
167  if not project.get('is_domain'):
168  project = self._normalize_domain_id(project)
169  # Our API requires that you specify the location in the hierarchy
170  # unambiguously. This could be by parent_id or, if it is a top
171  # level project, just by providing a domain_id.
172  if not project.get('parent_id'):
173  project['parent_id'] = project.get('domain_id')
174  project = self._normalize_dict(project)
175  try:
176  ref = PROVIDERS.resource_api.create_project(
177  project['id'],
178  project,
179  initiator=self.audit_initiator)
182  return self.wrap_member(ref), http.client.CREATED
183 
184  def patch(self, project_id):
185  """Update project.
186 
187  PATCH /v3/projects/{project_id}
188  """
189  ENFORCER.enforce_call(
190  action='identity:update_project',
191  build_target=_build_project_target_enforcement
192  )
193  project = self.request_body_json.get('project', {})
194  validation.lazy_validate(schema.project_update, project)
195  self._require_matching_id(project)
196  ref = PROVIDERS.resource_api.update_project(
197  project_id,
198  project,
199  initiator=self.audit_initiator)
200  return self.wrap_member(ref)
201 
202  def delete(self, project_id):
203  """Delete project.
204 
205  DELETE /v3/projects/{project_id}
206  """
207  ENFORCER.enforce_call(
208  action='identity:delete_project',
209  build_target=_build_project_target_enforcement
210  )
211  PROVIDERS.resource_api.delete_project(
212  project_id,
213  initiator=self.audit_initiator)
214  return None, http.client.NO_CONTENT
215 
216 
217 class _ProjectTagResourceBase(ks_flask.ResourceBase):
218  collection_key = 'projects'
219  member_key = 'tags'
220  get_member_from_driver = PROVIDERS.deferred_provider_lookup(
221  api='resource_api', method='get_project_tag')
222 
223  @classmethod
224  def wrap_member(cls, ref, collection_name=None, member_name=None):
225  member_name = member_name or cls.member_key
226  # NOTE(gagehugo): Overriding this due to how the common controller
227  # expects the ref to have an id, which for tags it does not.
228  new_ref = {'links': {'self': ks_flask.full_url()}}
229  new_ref[member_name] = (ref or [])
230  return new_ref
231 
232 
234  def get(self, project_id):
235  """List tags associated with a given project.
236 
237  GET /v3/projects/{project_id}/tags
238  """
239  ENFORCER.enforce_call(
240  action='identity:list_project_tags',
241  build_target=_build_project_target_enforcement
242  )
243  ref = PROVIDERS.resource_api.list_project_tags(project_id)
244  return self.wrap_member(ref)
245 
246  def put(self, project_id):
247  """Update all tags associated with a given project.
248 
249  PUT /v3/projects/{project_id}/tags
250  """
251  ENFORCER.enforce_call(
252  action='identity:update_project_tags',
253  build_target=_build_project_target_enforcement
254  )
255  tags = self.request_body_json.get('tags', {})
256  validation.lazy_validate(schema.project_tags_update, tags)
257  ref = PROVIDERS.resource_api.update_project_tags(
258  project_id, tags, initiator=self.audit_initiator)
259  return self.wrap_member(ref)
260 
261  def delete(self, project_id):
262  """Delete all tags associated with a given project.
263 
264  DELETE /v3/projects/{project_id}/tags
265  """
266  ENFORCER.enforce_call(
267  action='identity:delete_project_tags',
268  build_target=_build_project_target_enforcement
269  )
270  PROVIDERS.resource_api.update_project_tags(project_id, [])
271  return None, http.client.NO_CONTENT
272 
273 
275  def get(self, project_id, value):
276  """Get information for a single tag associated with a given project.
277 
278  GET /v3/projects/{project_id}/tags/{value}
279  """
280  ENFORCER.enforce_call(
281  action='identity:get_project_tag',
282  build_target=_build_project_target_enforcement,
283  )
284  PROVIDERS.resource_api.get_project_tag(project_id, value)
285  return None, http.client.NO_CONTENT
286 
287  def put(self, project_id, value):
288  """Add a single tag to a project.
289 
290  PUT /v3/projects/{project_id}/tags/{value}
291  """
292  ENFORCER.enforce_call(
293  action='identity:create_project_tag',
294  build_target=_build_project_target_enforcement
295  )
296  validation.lazy_validate(schema.project_tag_create, value)
297  # Check if we will exceed the max number of tags on this project
298  tags = PROVIDERS.resource_api.list_project_tags(project_id)
299  tags.append(value)
300  validation.lazy_validate(schema.project_tags_update, tags)
301  PROVIDERS.resource_api.create_project_tag(
302  project_id,
303  value,
304  initiator=self.audit_initiator
305  )
306  url = '/'.join((ks_flask.base_url(), project_id, 'tags', value))
307  response = flask.make_response('', http.client.CREATED)
308  response.headers['Location'] = url
309  return response
310 
311  def delete(self, project_id, value):
312  """Delete a single tag from a project.
313 
314  /v3/projects/{project_id}/tags/{value}
315  """
316  ENFORCER.enforce_call(
317  action='identity:delete_project_tag',
318  build_target=_build_project_target_enforcement
319  )
320  PROVIDERS.resource_api.delete_project_tag(project_id, value)
321  return None, http.client.NO_CONTENT
322 
323 
324 class _ProjectGrantResourceBase(ks_flask.ResourceBase):
325  collection_key = 'roles'
326  member_key = 'role'
327  get_member_from_driver = PROVIDERS.deferred_provider_lookup(
328  api='role_api', method='get_role')
329 
330  @staticmethod
332  return flask.request.path.endswith('/inherited_to_projects')
333 
334  @staticmethod
335  def _build_enforcement_target_attr(role_id=None, user_id=None,
336  group_id=None, domain_id=None,
337  project_id=None,
338  allow_non_existing=False):
339  ref = {}
340  if role_id:
341  ref['role'] = PROVIDERS.role_api.get_role(role_id)
342 
343  try:
344  if user_id:
345  ref['user'] = PROVIDERS.identity_api.get_user(user_id)
346  else:
347  ref['group'] = PROVIDERS.identity_api.get_group(group_id)
349  if not allow_non_existing:
350  raise
351 
352  # NOTE(lbragstad): This if/else check will need to be expanded in the
353  # future to handle system hierarchies if that is implemented.
354  if domain_id:
355  ref['domain'] = PROVIDERS.resource_api.get_domain(domain_id)
356  elif project_id:
357  ref['project'] = PROVIDERS.resource_api.get_project(project_id)
358 
359  return ref
360 
361 
363  def get(self, project_id, user_id, role_id):
364  """Check grant for project, user, role.
365 
366  GET/HEAD /v3/projects/{project_id/users/{user_id}/roles/{role_id}
367  """
368  ENFORCER.enforce_call(
369  action='identity:check_grant',
370  build_target=functools.partial(
371  self._build_enforcement_target_attr, role_id=role_id,
372  project_id=project_id, user_id=user_id)
373  )
374  inherited = self._check_if_inherited()
375  PROVIDERS.assignment_api.get_grant(
376  role_id=role_id, user_id=user_id, project_id=project_id,
377  inherited_to_projects=inherited)
378  return None, http.client.NO_CONTENT
379 
380  def put(self, project_id, user_id, role_id):
381  """Grant role for user on project.
382 
383  PUT /v3/projects/{project_id}/users/{user_id}/roles/{role_id}
384  """
385  ENFORCER.enforce_call(
386  action='identity:create_grant',
387  build_target=functools.partial(
389  role_id=role_id, project_id=project_id, user_id=user_id)
390  )
391  inherited = self._check_if_inherited()
392  PROVIDERS.assignment_api.create_grant(
393  role_id=role_id, user_id=user_id, project_id=project_id,
394  inherited_to_projects=inherited, initiator=self.audit_initiator)
395  return None, http.client.NO_CONTENT
396 
397  def delete(self, project_id, user_id, role_id):
398  """Delete grant of role for user on project.
399 
400  DELETE /v3/projects/{project_id}/users/{user_id}/roles/{role_id}
401  """
402  ENFORCER.enforce_call(
403  action='identity:revoke_grant',
404  build_target=functools.partial(
406  role_id=role_id, user_id=user_id, project_id=project_id,
407  allow_non_existing=True)
408  )
409  inherited = self._check_if_inherited()
410  PROVIDERS.assignment_api.delete_grant(
411  role_id=role_id, user_id=user_id, project_id=project_id,
412  inherited_to_projects=inherited, initiator=self.audit_initiator)
413  return None, http.client.NO_CONTENT
414 
415 
417  def get(self, project_id, user_id):
418  """List grants for user on project.
419 
420  GET/HEAD /v3/projects/{project_id}/users/{user_id}
421  """
422  ENFORCER.enforce_call(
423  action='identity:list_grants',
424  build_target=functools.partial(
426  project_id=project_id, user_id=user_id)
427  )
428  inherited = self._check_if_inherited()
429  refs = PROVIDERS.assignment_api.list_grants(
430  user_id=user_id, project_id=project_id,
431  inherited_to_projects=inherited)
432  return self.wrap_collection(refs)
433 
434 
436  def get(self, project_id, group_id, role_id):
437  """Check grant for project, group, role.
438 
439  GET/HEAD /v3/projects/{project_id/groups/{group_id}/roles/{role_id}
440  """
441  ENFORCER.enforce_call(
442  action='identity:check_grant',
443  build_target=functools.partial(
444  self._build_enforcement_target_attr, role_id=role_id,
445  project_id=project_id, group_id=group_id)
446  )
447  inherited = self._check_if_inherited()
448  PROVIDERS.assignment_api.get_grant(
449  role_id=role_id, group_id=group_id, project_id=project_id,
450  inherited_to_projects=inherited)
451  return None, http.client.NO_CONTENT
452 
453  def put(self, project_id, group_id, role_id):
454  """Grant role for group on project.
455 
456  PUT /v3/projects/{project_id}/groups/{group_id}/roles/{role_id}
457  """
458  ENFORCER.enforce_call(
459  action='identity:create_grant',
460  build_target=functools.partial(
462  role_id=role_id, project_id=project_id, group_id=group_id)
463  )
464  inherited = self._check_if_inherited()
465  PROVIDERS.assignment_api.create_grant(
466  role_id=role_id, group_id=group_id, project_id=project_id,
467  inherited_to_projects=inherited, initiator=self.audit_initiator)
468  return None, http.client.NO_CONTENT
469 
470  def delete(self, project_id, group_id, role_id):
471  """Delete grant of role for group on project.
472 
473  DELETE /v3/projects/{project_id}/groups/{group_id}/roles/{role_id}
474  """
475  ENFORCER.enforce_call(
476  action='identity:revoke_grant',
477  build_target=functools.partial(
479  role_id=role_id, group_id=group_id, project_id=project_id,
480  allow_non_existing=True)
481  )
482  inherited = self._check_if_inherited()
483  PROVIDERS.assignment_api.delete_grant(
484  role_id=role_id, group_id=group_id, project_id=project_id,
485  inherited_to_projects=inherited, initiator=self.audit_initiator)
486  return None, http.client.NO_CONTENT
487 
488 
490  def get(self, project_id, group_id):
491  """List grants for group on project.
492 
493  GET/HEAD /v3/projects/{project_id}/groups/{group_id}
494  """
495  ENFORCER.enforce_call(
496  action='identity:list_grants',
497  build_target=functools.partial(
499  project_id=project_id, group_id=group_id)
500  )
501  inherited = self._check_if_inherited()
502  refs = PROVIDERS.assignment_api.list_grants(
503  group_id=group_id, project_id=project_id,
504  inherited_to_projects=inherited)
505  return self.wrap_collection(refs)
506 
507 
508 class ProjectAPI(ks_flask.APIBase):
509  _name = 'projects'
510  _import_name = __name__
511  resources = [ProjectResource]
512  resource_mapping = [
513  ks_flask.construct_resource_map(
514  resource=ProjectTagsResource,
515  url='/projects/<string:project_id>/tags',
516  resource_kwargs={},
517  rel='project_tags',
518  path_vars={
519  'project_id': json_home.Parameters.PROJECT_ID}
520  ),
521  ks_flask.construct_resource_map(
522  resource=ProjectTagResource,
523  url='/projects/<string:project_id>/tags/<string:value>',
524  resource_kwargs={},
525  rel='project_tags',
526  path_vars={
527  'project_id': json_home.Parameters.PROJECT_ID,
528  'value': json_home.Parameters.TAG_VALUE}
529  ),
530  ks_flask.construct_resource_map(
531  resource=ProjectUserGrantResource,
532  url=('/projects/<string:project_id>/users/<string:user_id>/'
533  'roles/<string:role_id>'),
534  resource_kwargs={},
535  rel='project_user_role',
536  path_vars={
537  'project_id': json_home.Parameters.PROJECT_ID,
538  'user_id': json_home.Parameters.USER_ID,
539  'role_id': json_home.Parameters.ROLE_ID
540  },
541  ),
542  ks_flask.construct_resource_map(
543  resource=ProjectUserListGrantResource,
544  url='/projects/<string:project_id>/users/<string:user_id>/roles',
545  resource_kwargs={},
546  rel='project_user_roles',
547  path_vars={
548  'project_id': json_home.Parameters.PROJECT_ID,
549  'user_id': json_home.Parameters.USER_ID
550  }
551  ),
552  ks_flask.construct_resource_map(
553  resource=ProjectGroupGrantResource,
554  url=('/projects/<string:project_id>/groups/<string:group_id>/'
555  'roles/<string:role_id>'),
556  resource_kwargs={},
557  rel='project_group_role',
558  path_vars={
559  'project_id': json_home.Parameters.PROJECT_ID,
560  'group_id': json_home.Parameters.GROUP_ID,
561  'role_id': json_home.Parameters.ROLE_ID
562  },
563  ),
564  ks_flask.construct_resource_map(
565  resource=ProjectGroupListGrantResource,
566  url='/projects/<string:project_id>/groups/<string:group_id>/roles',
567  resource_kwargs={},
568  rel='project_group_roles',
569  path_vars={
570  'project_id': json_home.Parameters.PROJECT_ID,
571  'group_id': json_home.Parameters.GROUP_ID
572  },
573  ),
574  ]
575 
576 
577 APIs = (ProjectAPI,)
keystone.api.projects.ProjectUserListGrantResource
Definition: projects.py:416
keystone.api.projects._ProjectTagResourceBase.member_key
string member_key
Definition: projects.py:219
keystone.exception.UserNotFound
Definition: exception.py:469
keystone.api.projects.ProjectTagsResource.put
def put(self, project_id)
Definition: projects.py:246
keystone.api.projects.ProjectGroupListGrantResource
Definition: projects.py:489
keystone.api.projects.ProjectResource.patch
def patch(self, project_id)
Definition: projects.py:184
keystone.api.projects._ProjectTagResourceBase.wrap_member
def wrap_member(cls, ref, collection_name=None, member_name=None)
Definition: projects.py:224
keystone.api.projects._ProjectGrantResourceBase
Definition: projects.py:324
keystone.exception.NotFound
Definition: exception.py:395
keystone.api.projects.ProjectResource._expand_project_ref
def _expand_project_ref(self, ref)
Definition: projects.py:55
keystone.api.projects.ProjectGroupListGrantResource.get
def get(self, project_id, group_id)
Definition: projects.py:490
keystone.api.projects._build_project_target_enforcement
def _build_project_target_enforcement()
Definition: projects.py:35
keystone.exception.ValidationError
Definition: exception.py:98
keystone.api.projects.ProjectUserListGrantResource.get
def get(self, project_id, user_id)
Definition: projects.py:417
keystone.api.projects.ProjectResource._get_project
def _get_project(self, project_id)
Definition: projects.py:98
keystone.api.projects.ProjectGroupGrantResource.delete
def delete(self, project_id, group_id, role_id)
Definition: projects.py:470
keystone.api.projects.ProjectTagsResource.delete
def delete(self, project_id)
Definition: projects.py:261
keystone.api.projects.ProjectGroupGrantResource.get
def get(self, project_id, group_id, role_id)
Definition: projects.py:436
keystone.api.projects.ProjectUserGrantResource
Definition: projects.py:362
keystone.api.projects.ProjectResource.delete
def delete(self, project_id)
Definition: projects.py:202
keystone.api.projects._ProjectGrantResourceBase._build_enforcement_target_attr
def _build_enforcement_target_attr(role_id=None, user_id=None, group_id=None, domain_id=None, project_id=None, allow_non_existing=False)
Definition: projects.py:338
keystone.api.projects.ProjectTagResource.put
def put(self, project_id, value)
Definition: projects.py:287
keystone.api.projects.ProjectResource
Definition: projects.py:49
keystone.api.projects.ProjectTagResource.delete
def delete(self, project_id, value)
Definition: projects.py:311
keystone.api.projects.ProjectAPI
Definition: projects.py:508
keystone.api.projects.ProjectResource._list_projects
def _list_projects(self)
Definition: projects.py:111
keystone.api.projects._ProjectTagResourceBase
Definition: projects.py:217
keystone.exception.DomainNotFound
Definition: exception.py:453
keystone.api.projects.ProjectUserGrantResource.put
def put(self, project_id, user_id, role_id)
Definition: projects.py:380
keystone.api.projects._ProjectGrantResourceBase._check_if_inherited
def _check_if_inherited()
Definition: projects.py:331
keystone.exception.GroupNotFound
Definition: exception.py:473
keystone.api.projects.ProjectResource.get
def get(self, project_id=None)
Definition: projects.py:144
keystone.api.projects.ProjectTagResource
Definition: projects.py:274
keystone.server
Definition: __init__.py:1
keystone.conf
Definition: __init__.py:1
keystone.api.projects.ProjectGroupGrantResource
Definition: projects.py:435
keystone.i18n._
_
Definition: i18n.py:29
keystone.api.projects.ProjectTagResource.get
def get(self, project_id, value)
Definition: projects.py:275
keystone.api.projects.ProjectGroupGrantResource.put
def put(self, project_id, group_id, role_id)
Definition: projects.py:453
keystone.common
Definition: __init__.py:1
keystone.api.projects.ProjectUserGrantResource.delete
def delete(self, project_id, user_id, role_id)
Definition: projects.py:397
keystone.exception.ProjectNotFound
Definition: exception.py:457
keystone.i18n
Definition: i18n.py:1
keystone.api.projects.ProjectResource.post
def post(self)
Definition: projects.py:155
keystone.resource
Definition: __init__.py:1
keystone.api.projects.ProjectTagsResource.get
def get(self, project_id)
Definition: projects.py:234
keystone.api.projects.ProjectTagsResource
Definition: projects.py:233
keystone.api.projects.ProjectUserGrantResource.get
def get(self, project_id, user_id, role_id)
Definition: projects.py:363