adjutant/adjutant/actions/v1/projects.py

461 lines
16 KiB
Python

# Copyright (C) 2015 Catalyst IT Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from uuid import uuid4
from django.utils import timezone
from adjutant.common import user_store
from adjutant.common.utils import str_datetime
from adjutant.actions.v1.base import (
BaseAction, UserNameAction, UserMixin, ProjectMixin)
class NewProjectAction(BaseAction, ProjectMixin, UserMixin):
"""
Creates a new project for the current keystone_user.
This action can only be used for an autheticated taskview.
"""
required = [
'domain_id',
'parent_id',
'project_name',
'description',
]
def __init__(self, *args, **kwargs):
super(NewProjectAction, self).__init__(*args, **kwargs)
def _validate(self):
self.action.valid = (
self._validate_domain_id() and
self._validate_parent_project() and
self._validate_project_absent())
self.action.save()
def _validate_domain_id(self):
keystone_user = self.action.task.keystone_user
if keystone_user['project_domain_id'] != self.domain_id:
self.add_note('Domain id does not match keystone user domain.')
return False
return super(NewProjectAction, self)._validate_domain_id()
def _validate_parent_project(self):
if self.parent_id:
keystone_user = self.action.task.keystone_user
if self.parent_id != keystone_user['project_id']:
self.add_note(
'Parent id does not match keystone user project.')
return False
return super(NewProjectAction, self)._validate_parent_project()
return True
def _pre_approve(self):
self._validate()
def _post_approve(self):
project_id = self.get_cache('project_id')
if project_id:
self.action.task.cache['project_id'] = project_id
self.add_note("Project already created.")
else:
self._validate()
if not self.valid:
return
self._create_project()
user_id = self.get_cache('user_id')
if user_id:
self.action.task.cache['user_id'] = user_id
self.add_note("User already given roles.")
else:
default_roles = self.settings.get("default_roles", {})
project_id = self.get_cache('project_id')
keystone_user = self.action.task.keystone_user
try:
id_manager = user_store.IdentityManager()
user = id_manager.get_user(keystone_user['user_id'])
self.grant_roles(user, default_roles, project_id)
except Exception as e:
self.add_note(
("Error: '%s' while adding roles %s "
"to user '%s' on project '%s'") %
(e, default_roles, user.name, project_id))
raise
# put user_id into action cache:
self.action.task.cache['user_id'] = user.id
self.set_cache('user_id', user.id)
self.add_note(("Existing user '%s' attached to project %s" +
" with roles: %s")
% (user.name, project_id,
default_roles))
def _submit(self, token_data):
"""
Nothing to do here. Everything is done at post_approve.
"""
pass
class NewProjectWithUserAction(UserNameAction, ProjectMixin, UserMixin):
"""
Makes a new project for the given username. Will create the user if it
doesn't exists.
"""
required = [
'domain_id',
'parent_id',
'project_name',
'username',
'email'
]
def __init__(self, *args, **kwargs):
super(NewProjectWithUserAction, self).__init__(*args, **kwargs)
def _validate(self):
self.action.valid = (
self._validate_domain_id() and
self._validate_parent_project() and
self._validate_project_absent() and
self._validate_user())
self.action.save()
def _validate_user(self):
id_manager = user_store.IdentityManager()
user = id_manager.find_user(self.username, self.domain_id)
if not user:
# add to cache to use in template
self.action.task.cache['user_state'] = "default"
self.action.need_token = True
self.set_token_fields(["password"])
self.add_note("No user present with username '%s'." %
self.username)
return True
if user.email != self.email:
self.add_note("Existing user '%s' with non-matching email." %
self.username)
return False
if not user.enabled:
self.action.state = "disabled"
# add to cache to use in template
self.action.task.cache['user_state'] = "disabled"
self.action.need_token = True
# as they are disabled we'll reset their password
self.set_token_fields(["password"])
self.add_note(
"Existing disabled user '%s' with matching email." %
self.email)
return True
else:
self.action.state = "existing"
# add to cache to use in template
self.action.task.cache['user_state'] = "existing"
self.action.need_token = False
self.add_note("Existing user '%s' with matching email." %
self.email)
return True
def _validate_user_submit(self):
user_id = self.get_cache('user_id')
project_id = self.get_cache('project_id')
id_manager = user_store.IdentityManager()
user = id_manager.get_user(user_id)
project = id_manager.get_project(project_id)
if user and project:
self.action.valid = True
else:
self.action.valid = False
self.action.task.cache['user_state'] = self.action.state
self.action.save()
def _pre_approve(self):
self._validate()
def _post_approve(self):
"""
Approving a new project means we set up the project itself,
and if the user doesn't exist, create it right away. An existing
user automatically gets added to the new project.
"""
if not self.valid:
return
project_id = self.get_cache('project_id')
if project_id:
self.action.task.cache['project_id'] = project_id
self.add_note("Project already created.")
else:
self.action.valid = (
self._validate_domain_id() and
self._validate_parent_project() and
self._validate_project_absent())
self.action.save()
if not self.valid:
return
self._create_project()
# User validation and checks
user_id = self.get_cache('user_id')
roles_granted = self.get_cache('roles_granted')
if user_id and roles_granted:
self.action.task.cache['user_id'] = user_id
self.action.task.cache['user_state'] = self.action.state
self.add_note("User already setup.")
elif not user_id:
self.action.valid = self._validate_user()
self.action.save()
if not self.valid:
return
self._create_user_for_project()
elif not roles_granted:
self._create_user_for_project()
def _create_user_for_project(self):
id_manager = user_store.IdentityManager()
default_roles = self.settings.get("default_roles", {})
project_id = self.get_cache('project_id')
if self.action.state == "default":
try:
# Generate a temporary password:
password = uuid4().hex + uuid4().hex
user_id = self.get_cache('user_id')
if not user_id:
user = id_manager.create_user(
name=self.username, password=password,
email=self.email, domain=self.domain_id,
created_on=str_datetime(timezone.now()))
self.set_cache('user_id', user.id)
else:
user = id_manager.get_user(user_id)
# put user_id into action cache:
self.action.task.cache['user_id'] = user.id
self.grant_roles(user, default_roles, project_id)
except Exception as e:
self.add_note(
"Error: '%s' while creating user: %s with roles: %s" %
(e, self.username, default_roles))
raise
self.set_cache('roles_granted', True)
self.add_note(
"New user '%s' created for project %s with roles: %s" %
(self.username, project_id, default_roles))
elif self.action.state == "existing":
try:
user_id = self.get_cache('user_id')
if not user_id:
user = id_manager.find_user(
self.username, self.domain_id)
self.set_cache('user_id', user.id)
else:
user = id_manager.get_user(user_id)
self.action.task.cache['user_id'] = user.id
self.grant_roles(user, default_roles, project_id)
except Exception as e:
self.add_note(
"Error: '%s' while granting roles: %s to user: %s" %
(e, default_roles, self.username))
raise
self.set_cache('roles_granted', True)
self.add_note(("Existing user '%s' setup on project %s" +
" with roles: %s")
% (self.username, project_id,
default_roles))
elif self.action.state == "disabled":
user_id = self.get_cache('user_id')
if not user_id:
# first re-enable user
try:
user = id_manager.find_user(self.username, self.domain_id)
id_manager.enable_user(user)
except Exception as e:
self.add_note(
"Error: '%s' while re-enabling user: %s" %
(e, self.username))
raise
# and now update their password
# Generate a temporary password:
password = uuid4().hex + uuid4().hex
try:
id_manager.update_user_password(user, password)
except Exception as e:
self.add_note(
"Error: '%s' while changing password for user: %s" %
(e, self.username))
raise
self.add_note(
'User %s password has been changed.' % self.username)
self.set_cache('user_id', user.id)
else:
user = id_manager.get_user(user_id)
self.action.task.cache['user_id'] = user.id
# now add their roles
roles_granted = self.get_cache('roles_granted')
if not roles_granted:
try:
self.grant_roles(user, default_roles, project_id)
except Exception as e:
self.add_note(
"Error: '%s' while granting user: %s roles: %s" %
(e, self.username, default_roles))
raise
self.set_cache('roles_granted', True)
self.add_note(("Existing user '%s' setup on project %s" +
" with roles: %s")
% (self.username, project_id,
default_roles))
def _submit(self, token_data):
"""
The submit action is performed when a token is submitted.
This is done to set a user password only, and so should now only
change the user password. The project and user themselves are created
on post_approve.
"""
self._validate_user_submit()
if not self.valid:
return
project_id = self.get_cache('project_id')
self.action.task.cache['project_id'] = project_id
user_id = self.get_cache('user_id')
self.action.task.cache['user_id'] = user_id
id_manager = user_store.IdentityManager()
if self.action.state in ["default", "disabled"]:
user = id_manager.get_user(user_id)
try:
id_manager.update_user_password(
user, token_data['password'])
except Exception as e:
self.add_note(
"Error: '%s' while changing password for user: %s" %
(e, self.username))
raise
self.add_note('User %s password has been changed.' % self.username)
elif self.action.state == "existing":
# do nothing, everything is already done.
self.add_note(
"Existing user '%s' already attached to project %s" % (
user_id, project_id))
class AddDefaultUsersToProjectAction(BaseAction, ProjectMixin, UserMixin):
"""
The purpose of this action is to add a given set of users after
the creation of a new Project. This is mainly for administrative
purposes, and for users involved with migrations, monitoring, and
general admin tasks that should be present by default.
"""
required = [
'domain_id',
]
def __init__(self, *args, **kwargs):
super(AddDefaultUsersToProjectAction, self).__init__(*args, **kwargs)
self.users = self.settings.get('default_users', [])
self.roles = self.settings.get('default_roles', [])
def _validate_users(self):
id_manager = user_store.IdentityManager()
all_found = True
for user in self.users:
ks_user = id_manager.find_user(user, self.domain_id)
if ks_user:
self.add_note('User: %s exists.' % user)
else:
self.add_note('ERROR: User: %s does not exist.' % user)
all_found = False
return all_found
def _pre_validate(self):
self.action.valid = self._validate_users()
self.action.save()
def _validate(self):
self.action.valid = (
self._validate_users() and
self._validate_project_id()
)
self.action.save()
def _pre_approve(self):
self._pre_validate()
def _post_approve(self):
id_manager = user_store.IdentityManager()
self.project_id = self.action.task.cache.get('project_id', None)
self._validate()
if self.valid and not self.action.state == "completed":
try:
for user in self.users:
ks_user = id_manager.find_user(user, self.domain_id)
self.grant_roles(ks_user, self.roles, self.project_id)
self.add_note(
'User: "%s" given roles: %s on project: %s.' %
(ks_user.name, self.roles, self.project_id))
except Exception as e:
self.add_note(
"Error: '%s' while adding users to project: %s" %
(e, self.project_id))
raise
self.action.state = "completed"
self.action.save()
self.add_note("All users added.")
def _submit(self, token_data):
pass