adjutant/stacktask/actions/models.py

1099 lines
37 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 logging import getLogger
from uuid import uuid4
from django.conf import settings
from django.db import models
from django.utils import timezone
from jsonfield import JSONField
from stacktask.actions import serializers
from stacktask.actions import user_store
class Action(models.Model):
"""
Database model representation of an action.
"""
action_name = models.CharField(max_length=200)
action_data = JSONField(default={})
cache = JSONField(default={})
state = models.CharField(max_length=200, default="default")
valid = models.BooleanField(default=False)
need_token = models.BooleanField(default=False)
task = models.ForeignKey('api.Task')
order = models.IntegerField()
created = models.DateTimeField(default=timezone.now)
def get_action(self):
"""Returns self as the appropriate action wrapper type."""
data = self.action_data
return settings.ACTION_CLASSES[self.action_name][0](
data=data, action_model=self)
class BaseAction(object):
"""
Base class for the object wrapping around the database model.
Setup to allow multiple action types and different internal logic
per type but built from a single database type.
- 'required' defines what fields to setup from the data.
If need_token MAY be true, you must implement '_token_email',
which should return the email the action wants the token sent to.
While there are checks to prevent duplicates or different emails,
try and only have one action in your chain provide the email.
The Action can do anything it needs at one of the three functions
called by the views:
- 'pre_approve'
- 'post_approve'
- 'submit'
All logic and validation should be handled within the action itself,
and any other actions it is linked to. The way in which pre_approve,
post_approve, and submit are called should rarely change. Actions
should be built with those steps in mind, thinking about what they mean,
and when they execute.
By using 'get_cache' and 'set_cache' they can pass data along which
may be needed by the action later. This cache is backed to the database.
Passing data along to other actions is done via the task and
it's cache, but this is in memory only, so it is only useful during the
same action stage ('post_approve', etc.).
Other than the task cache, actions should not be altering database
models other than themselves. This is not enforced, just a guideline.
"""
required = []
def __init__(self, data, action_model=None, task=None,
order=None):
"""
Build itself around an existing database model,
or build itself and creates a new database model.
Sets up required data as fields.
"""
self.logger = getLogger('stacktask')
for field in self.required:
field_data = data[field]
setattr(self, field, field_data)
if action_model:
self.action = action_model
else:
# make new model and save in db
action = Action.objects.create(
action_name=self.__class__.__name__,
action_data=data,
task=task,
order=order
)
action.save()
self.action = action
@property
def valid(self):
return self.action.valid
@property
def need_token(self):
return self.action.need_token
def get_email(self):
return self._get_email()
def _get_email(self):
return None
def get_cache(self, key):
return self.action.cache.get(key, None)
def set_cache(self, key, value):
self.action.cache[key] = value
self.action.save()
@property
def token_fields(self):
return self.action.cache.get("token_fields", [])
def set_token_fields(self, token_fields):
self.action.cache["token_fields"] = token_fields
self.action.save()
def add_note(self, note):
"""
Logs the note, and also adds it to the task action notes.
"""
self.logger.info("(%s) - %s" % (timezone.now(), note))
note = "%s - (%s)" % (note, timezone.now())
self.action.task.add_action_note(
unicode(self), note)
def pre_approve(self):
return self._pre_approve()
def post_approve(self):
return self._post_approve()
def submit(self, token_data):
return self._submit(token_data)
def _pre_approve(self):
raise NotImplementedError
def _post_approve(self):
raise NotImplementedError
def _submit(self, token_data):
raise NotImplementedError
def __unicode__(self):
return self.__class__.__name__
class ResourceMixin(object):
"""Base Mixin class for dealing with Openstack resources."""
def _validate_keystone_user(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
if keystone_user['project_id'] != self.project_id:
self.add_note('Project id does not match keystone user project.')
return False
return True
def _validate_domain_id(self):
id_manager = user_store.IdentityManager()
domain = id_manager.get_domain(self.domain_id)
if not domain:
self.add_note('Domain does not exist.')
return False
return True
def _validate_project_id(self):
# Handle an edge_case where some actions set their
# own project_id value.
if not self.project_id:
self.add_note('No project_id given.')
return False
# Now actually check the project exists.
id_manager = user_store.IdentityManager()
project = id_manager.get_project(self.project_id)
if not project:
self.add_note('Project with id %s does not exist.' %
self.project_id)
return False
self.add_note('Project with id %s exists.' % self.project_id)
return True
def _validate_domain_name(self):
id_manager = user_store.IdentityManager()
self.domain = id_manager.find_domain(self.domain_name)
if not self.domain:
self.add_note('Domain does not exist.')
return False
return True
class UserMixin(ResourceMixin):
"""Mixin with functions for users."""
def _validate_username_exists(self):
id_manager = user_store.IdentityManager()
self.user = id_manager.find_user(self.username, self.domain.id)
if not self.user:
self.add_note('No user present with username')
return False
return True
def _grant_roles(self, user, roles, project_id):
id_manager = user_store.IdentityManager()
ks_roles = []
for role in roles:
ks_role = id_manager.find_role(role)
if ks_role:
ks_roles.append(ks_role)
else:
raise TypeError("Keystone missing role: %s" % role)
for role in ks_roles:
id_manager.add_user_role(user, role, project_id)
def _validate_role_permissions(self):
keystone_user = self.action.task.keystone_user
# Role permissions check
if not self.are_roles_managable(user_roles=keystone_user['roles'],
requested_roles=self.roles):
self.add_note('User does not have permission to edit role(s).')
return False
return True
def are_roles_managable(self, user_roles=[], requested_roles=[]):
requested_roles = set(requested_roles)
# blacklist checks
blacklist_roles = set(['admin'])
if len(blacklist_roles & requested_roles) > 0:
return False
# user managable role
managable_roles = user_store.get_managable_roles(user_roles)
intersection = set(managable_roles) & requested_roles
# if all requested roles match, we can proceed
return intersection == requested_roles
class ProjectMixin(ResourceMixin):
"""Mixin with functions for projects."""
def _validate_parent_project(self):
id_manager = user_store.IdentityManager()
# NOTE(adriant): If parent id is None, Keystone defaults to the domain.
# So we only care to validate if parent_id is not None.
if self.parent_id:
parent = id_manager.get_project(self.parent_id)
if not parent:
self.add_note("Parent id: '%s' does not exist." %
self.project_name)
return False
return True
def _validate_project_absent(self):
id_manager = user_store.IdentityManager()
project = id_manager.find_project(
self.project_name, self.domain_id)
if project:
self.add_note("Existing project with name '%s'." %
self.project_name)
return False
self.add_note("No existing project with name '%s'." %
self.project_name)
return True
def _create_project(self):
id_manager = user_store.IdentityManager()
try:
project = id_manager.create_project(
self.project_name, created_on=str(timezone.now()),
parent=self.parent_id, domain=self.domain_id)
except Exception as e:
self.add_note(
"Error: '%s' while creating project: %s" %
(e, self.project_name))
raise
# put project_id into action cache:
self.action.task.cache['project_id'] = project.id
self.set_cache('project_id', project.id)
self.add_note("New project '%s' created." % project.name)
class UserIdAction(BaseAction):
def _get_target_user(self):
"""
Gets the target user by id
"""
id_manager = user_store.IdentityManager()
user = id_manager.get_user(self.user_id)
return user
class UserNameAction(BaseAction):
"""
Base action for dealing with users. Removes username if
USERNAME_IS_EMAIL and sets email to be username.
"""
def __init__(self, *args, **kwargs):
if settings.USERNAME_IS_EMAIL:
try:
self.required.remove('username')
except ValueError:
pass
# nothing to remove
super(UserNameAction, self).__init__(*args, **kwargs)
self.username = self.email
else:
super(UserNameAction, self).__init__(*args, **kwargs)
def _get_email(self):
return self.email
def _get_target_user(self):
"""
Gets the target user by their username
"""
id_manager = user_store.IdentityManager()
user = id_manager.find_user(self.username, self.domain_id)
return user
class NewUserAction(UserNameAction, ProjectMixin, UserMixin):
"""
Setup a new user with a role on the given project.
Creates the user if they don't exist, otherwise
if the username and email for the request match the
existing one, will simply add the project role.
"""
required = [
'username',
'email',
'project_id',
'roles',
'domain_id',
]
def _validate_targer_user(self):
id_manager = user_store.IdentityManager()
# check if user exists and is valid
# this may mean we need a token.
user = self._get_target_user()
if not user:
self.action.need_token = True
# add to cache to use in template
self.action.task.cache['user_state'] = "default"
self.set_token_fields(["password"])
self.add_note(
'No user present with username. Need to create new user.')
return True
if user.email != self.email:
self.add_note(
'Found matching username, but email did not match.' +
'Reporting as invalid.')
return False
if not user.enabled:
self.action.need_token = True
self.action.state = "disabled"
# add to cache to use in template
self.action.task.cache['user_state'] = "disabled"
# as they are disabled we'll reset their password
self.set_token_fields(["password"])
self.add_note(
'Existing disabled user with matching email.')
return True
# role_validation
roles = id_manager.get_roles(user, self.project_id)
role_names = {role.name for role in roles}
missing = set(self.roles) - role_names
if not missing:
self.action.need_token = False
self.action.state = "complete"
self.add_note(
'Existing user already has roles.'
)
else:
self.roles = list(missing)
self.action.need_token = True
self.set_token_fields(["confirm"])
self.action.state = "existing"
# add to cache to use in template
self.action.task.cache['user_state'] = "existing"
self.add_note(
'Existing user with matching email missing roles.')
return True
def _validate(self):
self.action.valid = (
self._validate_role_permissions() and
self._validate_keystone_user() and
self._validate_domain_id() and
self._validate_project_id() and
self._validate_targer_user()
)
self.action.save()
def _pre_approve(self):
self._validate()
def _post_approve(self):
self._validate()
def _submit(self, token_data):
self._validate()
if not self.valid:
return
id_manager = user_store.IdentityManager()
if self.action.state == "default":
# default action: Create a new user in the tenant and add roles
try:
user = id_manager.create_user(
name=self.username, password=token_data['password'],
email=self.email, domain=self.domain_id,
created_on=str(timezone.now()))
self._grant_roles(user, self.roles, self.project_id)
except Exception as e:
self.add_note(
"Error: '%s' while creating user: %s with roles: %s" %
(e, self.username, self.roles))
raise
self.add_note(
'User %s has been created, with roles %s in project %s.'
% (self.username, self.roles, self.project_id))
elif self.action.state == "disabled":
# 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 with roles: %s" %
(e, self.username, self.roles))
raise
# now add their roles
try:
self._grant_roles(user, self.roles, self.project_id)
except Exception as e:
self.add_note(
"Error: '%s' while attaching user: %s with roles: %s" %
(e, self.username, self.roles))
raise
# and now update their password
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)
self.add_note(
'Existing user %s has been given roles %s in project %s.'
% (self.username, self.roles, self.project_id))
elif self.action.state == "existing":
# Existing action: only add roles.
try:
user = id_manager.find_user(self.username, self.domain_id)
self._grant_roles(user, self.roles, self.project_id)
except Exception as e:
self.add_note(
"Error: '%s' while attaching user: %s with roles: %s" %
(e, self.username, self.roles))
raise
self.add_note(
'Existing user %s has been given roles %s in project %s.'
% (self.username, self.roles, self.project_id))
elif self.action.state == "complete":
# complete action: nothing to do.
self.add_note(
'Existing user %s already had roles %s in project %s.'
% (self.username, self.roles, self.project_id))
# TODO(adriant): Write tests for this action.
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',
]
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 = settings.ACTION_SETTINGS.get(
'NewProjectAction', {}).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, self.username, default_roles, 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")
% (self.username, 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
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.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.
"""
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.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 = settings.ACTION_SETTINGS.get(
'NewProjectAction', {}).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(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 ResetUserPasswordAction(UserNameAction, UserMixin):
"""
Simple action to reset a password for a given user.
"""
username = models.CharField(max_length=200)
email = models.EmailField()
required = [
'domain_name',
'username',
'email'
]
blacklist = settings.ACTION_SETTINGS.get(
'ResetUserPasswordAction', {}).get("blacklisted_roles", {})
def _validate_user_roles(self):
id_manager = user_store.IdentityManager()
self.user = id_manager.find_user(self.username, self.domain.id)
roles = id_manager.get_all_roles(self.user)
user_roles = []
for project, roles in roles.iteritems():
user_roles.extend(role.name for role in roles)
if set(self.blacklist) & set(user_roles):
self.add_note('Cannot reset users with blacklisted roles.')
return False
if self.user.email == self.email:
self.action.need_token = True
self.set_token_fields(["password"])
self.add_note('Existing user with matching email.')
return True
else:
self.add_note('Existing user with non-matching email.')
return False
def _validate(self):
# Here, the order of validation matters
# as each one adds new class variables
self.action.valid = (
self._validate_domain_name() and
self._validate_username_exists() and
self._validate_user_roles()
)
self.action.save()
def _pre_approve(self):
self._validate()
def _post_approve(self):
self._validate()
def _submit(self, token_data):
self._validate()
if not self.valid:
return
id_manager = user_store.IdentityManager()
try:
id_manager.update_user_password(self.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)
class EditUserRolesAction(UserIdAction, ProjectMixin, UserMixin):
"""
A class for adding or removing roles
on a user for the given project.
"""
required = [
'domain_id',
'project_id',
'user_id',
'roles',
'remove'
]
def _validate_target_user(self):
# Get target user
user = self._get_target_user()
if not user:
self.add_note('No user present with user_id')
return False
return True
def _validate_user_roles(self):
id_manager = user_store.IdentityManager()
user = self._get_target_user()
project = id_manager.get_project(self.project_id)
# user roles
current_roles = id_manager.get_roles(user, project)
current_role_names = {role.name for role in current_roles}
if self.remove:
remaining = set(current_role_names) & set(self.roles)
if not remaining:
self.action.state = "complete"
self.add_note(
"User doesn't have roles to remove.")
else:
self.roles = list(remaining)
self.add_note(
'User has roles to remove.')
else:
missing = set(self.roles) - set(current_role_names)
if not missing:
self.action.state = "complete"
self.add_note(
'User already has roles.')
else:
self.roles = list(missing)
self.add_note(
'User user missing roles.')
# All paths are valid here
# We've just set state and roles that need to be changed.
return True
def _validate(self):
self.action.valid = (
self._validate_keystone_user() and
self._validate_role_permissions() and
self._validate_domain_id() and
self._validate_project_id() and
self._validate_target_user() and
self._validate_user_roles()
)
self.action.save()
def _pre_approve(self):
self._validate()
def _post_approve(self):
self._validate()
def _submit(self, token_data):
self._validate()
if not self.valid:
return
id_manager = user_store.IdentityManager()
if self.action.state == "default":
try:
user = self._get_target_user()
roles = []
for role in self.roles:
roles.append(id_manager.find_role(role))
if self.remove:
for role in roles:
id_manager.remove_user_role(
user, role, self.project_id)
else:
for role in roles:
id_manager.add_user_role(
user, role, self.project_id)
except Exception as e:
if self.remove:
self.add_note(
"Error: '%s' removing roles: %s from user: %s" %
(e, self.roles, self.user_id))
else:
self.add_note(
"Error: '%s' adding roles: %s to user: %s" %
(e, self.roles, self.user_id))
raise
if self.remove:
self.add_note(
'User %s has had roles %s removed from project %s.'
% (self.user_id, self.roles, self.project_id))
else:
self.add_note(
'User %s has been given roles %s in project %s.'
% (self.user_id, self.roles, self.project_id))
elif self.action.state == "complete":
if self.remove:
self.add_note(
'User %s already had roles %s in project %s.'
% (self.user_id, self.roles, self.project_id))
else:
self.add_note(
"User %s didn't have roles %s in project %s."
% (self.user_id, self.roles, self.project_id))
# Update settings dict with tuples in the format:
# (<ActionClass>, <ActionSerializer>)
def register_action_class(action_class, serializer_class):
data = {}
data[action_class.__name__] = (action_class, serializer_class)
settings.ACTION_CLASSES.update(data)
# Register each action model
register_action_class(NewUserAction, serializers.NewUserSerializer)
register_action_class(
NewProjectWithUserAction, serializers.NewProjectWithUserSerializer)
register_action_class(ResetUserPasswordAction, serializers.ResetUserSerializer)
register_action_class(EditUserRolesAction, serializers.EditUserRolesSerializer)