class EditPermissionStateChange(BaseStateChange): descriptive_text = { "verb": "edit", "default_string": "permission", "preposition": "on" } section = "Permissions" allowable_targets = [PermissionsItem] settable_classes = ["all_models"] model_based_validation = (PermissionsItem, ["anyone", "roles", "actors"]) actors = field_utils.ActorListField( label="Actors who have this permission", null_value=list) roles = field_utils.RoleListField(label="Roles who have this permission", null_value=list) anyone = field_utils.BooleanField(label="Everyone has the permission", null_value=False) def validate(self, actor, target): if not self.actors and not self.roles and not self.anyone: raise ValidationError( "Must change at least one field to edit permission.") def implement(self, actor, target, **kwargs): field_dict = {} if self.actors: field_dict["actors"] = self.actors if self.roles: field_dict["roles"] = self.roles if self.anyone: field_dict["anyone"] = self.anyone target.set_fields(**field_dict) target.save() return target
class AddMembersStateChange(BaseStateChange): """State change to add members to Community.""" descriptive_text = { "verb": "add", "default_string": "members to community", "detail_string": "{member_pk_list} as members" } section = "Community" allowable_targets = ["all_community_models"] linked_filters = ["SelfMembershipFilter"] member_pk_list = field_utils.ActorListField(label="People to add as members", required=True) def validate(self, actor, target): if not isinstance(self.member_pk_list, list): raise ValidationError(f"member_pk_list must be list, not {type(self.member_pk_list)}") if not all([isinstance(member_pk, int) for member_pk in self.member_pk_list]): raise ValidationError(message="member_pk_list must contain only integers") def implement(self, actor, target, **kwargs): target.roles.add_members(self.member_pk_list) target.save() return target
class ChangeOwnersStateChange(BaseStateChange): """State change to add or remove owners from Community.""" descriptive_text = { "verb": "change", "default_string": "owners of community" } is_foundational = True section = "Leadership" allowable_targets = ["all_community_models"] roles_to_add = field_utils.RoleListField(label="Roles to add") roles_to_remove = field_utils.RoleListField(label="Roles to remove") actors_to_add = field_utils.ActorListField(label="People to add") actors_to_remove = field_utils.ActorListField(label="People to remove") def make_changes(self, actor, target): if self.actors_to_add: [target.roles.add_owner(pk) for pk in self.actors_to_add] if self.actors_to_remove: [target.roles.remove_owner(pk) for pk in self.actors_to_remove] if self.roles_to_add: [target.roles.add_owner_role(role) for role in self.roles_to_add] if self.roles_to_remove: [target.roles.remove_owner_role(role) for role in self.roles_to_remove] return target def validate(self, actor, target): if not self.roles_to_add and not self.roles_to_remove and not self.actors_to_add \ and not self.actors_to_remove: raise ValidationError("Must add or remove at least one individual owner or owning role.") target = self.make_changes(actor, target) target.roles.validate_role_handler() target.refresh_from_db() def implement(self, actor, target, **kwargs): target = self.make_changes(actor, target) target.save() return target
class RemovePeopleFromRoleStateChange(BaseStateChange): """State change to remove people from role in Community.""" descriptive_text = { "verb": "remove", "default_string": "people from role", "detail_string": "people {people_to_remove} from role '{role_name}'", "preposition": "in" } section = "Community" allowable_targets = ["all_community_models"] role_name = field_utils.RoleField(label="Role to remove people from", required=True) people_to_remove = field_utils.ActorListField(label="People to remove from role", required=True) def is_conditionally_foundational(self, action): """If role_name is owner or governor role, should should be treated as a conditional change.""" if self.role_name in action.target.roles.get_owners()["roles"]: return True if self.role_name in action.target.roles.get_governors()["roles"]: return True return False def validate(self, actor, target): """When removing people from a role, we must check that doing so does not leave us without any owners.""" if self.role_name not in target.roles.get_owners()["roles"]: return # this isn't an owner role if len(self.people_to_remove) < len(target.roles.get_users_given_role(self.role_name)): return # removing these users will not result in empty role if len(target.roles.get_owners()["actors"]) > 0: return # community has individual actor owners so it doesn't need roles for role in target.roles.get_owners()["roles"]: if role == self.role_name: continue actors = target.roles.get_users_given_role(role) if len(actors) > 0: return # there are other owner roles with actors specified raise ValidationError("Cannot remove everyone from this role as " + "doing so would leave the community without an owner") def implement(self, actor, target, **kwargs): target.roles.remove_people_from_role(self.role_name, self.people_to_remove) target.save() return target
class RemoveMembersStateChange(BaseStateChange): """State change to remove members from Community.""" descriptive_text = { "verb": "remove", "default_string": "members from community", "detail_string": "members {member_pk_list} from community", "preposition": "from" } section = "Community" linked_filters = ["SelfMembershipFilter"] allowable_targets = ["all_community_models"] member_pk_list = field_utils.ActorListField(label="People to remove as members", required=True) def validate(self, actor, target): """If any of the members to be removed are an owner or governor (either directly, or through being in an owner or governor role) the action is not valid.""" governor_list, owner_list = [], [] for pk in self.member_pk_list: is_governor, result = target.roles.is_governor(pk) if is_governor: governor_list.append(str(pk)) is_owner, result = target.roles.is_owner(pk) if is_owner: owner_list.append(str(pk)) if governor_list or owner_list: message = f"Cannot remove members as some are owners or governors. Owners: {', '.join(owner_list)}, " + \ f"Governors: {', '.join(governor_list)}" raise ValidationError(message) def implement(self, actor, target, **kwargs): # Remove members from custom roles for role_name in target.roles.get_custom_roles(): target.roles.remove_people_from_role(role_name, self.member_pk_list) # Now remove them from members target.roles.remove_members(self.member_pk_list) target.save() return target
class AddPeopleToRoleStateChange(BaseStateChange): """State change to add people to role in Community.""" descriptive_text = { "verb": "add", "default_string": "people to role", "detail_string": "people {people_to_add} to role '{role_name}'", "preposition": "in" } section = "Community" allowable_targets = ["all_community_models"] role_name = field_utils.RoleField(label="Role to add people to", required=True) people_to_add = field_utils.ActorListField(label="People to add to role", required=True) linked_filters = ["RoleMatchesFilter"] def is_conditionally_foundational(self, action): """If role_name is owner or governor role, should should be treated as a conditional change.""" if self.role_name in action.target.roles.get_owners()["roles"]: return True if self.role_name in action.target.roles.get_governors()["roles"]: return True return False def validate(self, actor, target): if not isinstance(self.role_name, str): raise ValidationError(f"Role must be type str, not {str(type(self.role_name))}") if not target.roles.is_role(self.role_name): raise ValidationError(f"Role {self.role_name} does not exist") people_already_in_role = [] for person in self.people_to_add: if target.roles.has_specific_role(self.role_name, person): people_already_in_role.append(str(person)) if people_already_in_role: raise ValidationError(f"Users {list_to_text(people_already_in_role)} already in role {self.role_name}") def implement(self, actor, target, **kwargs): target.roles.add_people_to_role(self.role_name, self.people_to_add) target.save() return target
class AddPermissionStateChange(BaseStateChange): """State change to add a permission to something.""" descriptive_text = { # note that description_present_tense and past tense are overridden below "verb": "add", "default_string": "permission" } section = "Permissions" model_based_validation = (PermissionsItem, ["change_type", "anyone", "inverse"]) change_type = field_utils.CharField( label="Type of action the permission covers", required=True) actors = field_utils.ActorListField( label="Actors who have this permission", null_value=list) roles = field_utils.RoleListField(label="Roles who have this permission", null_value=list) anyone = field_utils.BooleanField(label="Everyone has the permission", null_value=False) inverse = field_utils.BooleanField( label="Do the inverse of this permission", null_value=False) condition_data = field_utils.CharField( label="Condition for this permission") def description_present_tense(self): return f"add permission '{get_verb_given_permission_type(self.change_type)}'" def description_past_tense(self): return f"added permission '{get_verb_given_permission_type(self.change_type)}'" def is_conditionally_foundational(self, action): """Some state changes are only foundational in certain conditions. Those state changes override this method to apply logic and determine whether a specific instance is foundational or not.""" from concord.utils.lookups import get_state_change_object change_object = get_state_change_object(self.change_type) return action.change.is_foundational def validate(self, actor, target): permission = get_state_change_object(self.change_type) # check that target is a valid class for the permission to be set on if target.__class__ not in permission.get_settable_classes(): settable_classes_str = ", ".join( [str(option) for option in permission.get_settable_classes()]) raise ValidationError( f"This kind of permission cannot be set on target {target} of class " + f"{target.__class__}, must be {settable_classes_str}") # validate condition data if self.condition_data: for condition in self.condition_data: is_valid, message = validate_condition( condition["condition_type"], condition["condition_data"], condition["permission_data"], target) if not is_valid: raise ValidationError(message) def implement(self, actor, target, **kwargs): permission = PermissionsItem() permission.set_fields(owner=target.get_owner(), permitted_object=target, anyone=self.anyone, change_type=self.change_type, inverse=self.inverse, actors=self.actors, roles=self.roles) permission.save() # if condition, add and save if self.condition_data: # create initial manager owner = permission.get_owner() manager = ConditionManager.objects.create(owner=owner, community=owner.pk, set_on="permission") permission.condition = manager # add conditions for condition in self.condition_data: data = { "condition_type": condition["condition_type"], "condition_data": condition["condition_data"], "permission_data": condition["permission_data"] } manager.add_condition(data_for_condition=data) manager.save() return permission