Skip to content
Snippets Groups Projects
Commit 1c0cd137 authored by Martin Juhás's avatar Martin Juhás
Browse files

Merge branch '202-regenerate-credentials' into 'main'

Resolve "Regenerate credentials"

Closes #202

See merge request inject/backend!209
parents a32902e9 9b97237a
No related branches found
No related tags found
No related merge requests found
......@@ -41,3 +41,4 @@ fix: update performance testing tools to the newest API
fix: fix SendEmailInput authorization checks
feat: addition of INJECT_SECRET_KEY env variable #141
change: set csrf cookie for `/version` endpoint
feat: endpoint for re-generation of user login credentials #202
from typing import List
import graphene
from django.contrib.auth.models import Group
from rest_framework.exceptions import PermissionDenied
from django.utils.crypto import get_random_string
from django.conf import settings
from aai.models import Perms
from aai.utils import protected, extra_protected, Check
......@@ -19,7 +19,10 @@ from user.schema.validators import (
validate_instructor_removing,
validate_definition_access_assinging,
validate_definition_access_removing,
validate_credentials_regeneration,
execute_change_userdata,
)
from user.email.email_sender import send_credentials
class AssignUsersToTeamMutation(graphene.Mutation):
......@@ -225,38 +228,57 @@ class ChangeUserDataMutation(graphene.Mutation):
change_user_input = graphene.Argument(ChangeUserInput, required=True)
@classmethod
@protected(Perms.update_user)
@protected(Perms.update_user.full_name)
def mutate(
cls, root, info, change_user_input: ChangeUserInput
) -> graphene.Mutation:
user = get_model(User, id=change_user_input.user_id)
if change_user_input.group is not None:
if (
change_user_input.group == "admin"
and not info.context.user.is_superuser
):
raise PermissionDenied(
"Permission denied - Only admin can change user to admin group"
)
user.group = Group.objects.get(name=change_user_input.group)
if change_user_input.group == "admin":
user.is_superuser = True
user.is_staff = True
elif change_user_input.group == "instructor":
user.is_staff = True
user.is_superuser = False
elif change_user_input.group == "trainee":
user.is_staff = False
user.is_superuser = False
if (
change_user_input.active is not None and not user.is_imported
): # can not change is_active of imported user (should be always False)
user.is_active = change_user_input.active
user.save()
changes = execute_change_userdata(
change_user_input, user, info.context.user
)
logger.info(
log_user_msg(info.context, info.context.user)
+ f"updated user: {user} changed data: {changes}"
)
return ChangeUserDataMutation(user=user)
class RegenerateCredentialsMutation(graphene.Mutation):
class Arguments:
user_ids = graphene.List(
graphene.ID,
required=True,
description="IDs of the users to have re-generated credentials",
)
operation_done = graphene.Boolean()
@classmethod
@protected(Perms.update_user.full_name)
def mutate(cls, root, info, user_ids: List[str]) -> graphene.Mutation:
users = User.objects.filter(id__in=user_ids, is_active=True)
regenerated_users = []
validate_credentials_regeneration(users, info.context.user)
for user in users:
password = get_random_string(length=15)
user.set_password(password)
regenerated_users.append((user, password))
User.objects.bulk_update(users, ["password"])
if (
not settings.DEBUG
and not settings.TESTING_MODE
and regenerated_users
):
send_credentials(regenerated_users)
logger.info(
log_user_msg(info.context, info.context.user)
+ f"re-generated credentials for users: {users}"
)
return RegenerateCredentialsMutation(operation_done=True)
class Mutation(graphene.ObjectType):
assign_users_to_team = AssignUsersToTeamMutation.Field(
description="Mutation for assigning users to the specific team of the exercise"
......@@ -279,3 +301,6 @@ class Mutation(graphene.ObjectType):
change_user_data = ChangeUserDataMutation.Field(
description="Mutation for changing user data"
)
regenerate_credentials = RegenerateCredentialsMutation.Field(
description="Mutation for re-generating credentials for users"
)
......@@ -64,7 +64,7 @@ class Query(graphene.ObjectType):
def resolve_user(self, info, user_id: str) -> User:
return get_model(User, id=user_id)
@protected(Perms.view_user)
@protected(Perms.view_user.full_name)
def resolve_tags(self, info) -> List[Tag]:
return Tag.objects.all()
......
from typing import List, Type, TypeVar, Union
from django.db.models import Model
from django.conf import settings
from rest_framework.exceptions import PermissionDenied
from django.db.models import QuerySet
from django.contrib.auth.models import Group
from user.models import UserInTeam, User, InstructorOfExercise, DefinitionAccess
from exercise.models import Team, Exercise, Definition
from aai.models import UserGroup
from user.graphql_inputs import ChangeUserInput
ModelType = TypeVar("ModelType", bound=Model)
......@@ -62,6 +67,18 @@ def __create_users_msg_format(users: List[User]) -> str:
return ", ".join(invalid_group_users)
def _validate_group_change(requester: User, changed_user: User):
if settings.NOAUTH:
return
if (
changed_user.group == UserGroup.ADMIN
and requester.group != UserGroup.ADMIN
):
raise PermissionDenied(
"Permission denied - Only admin can change user to admin group"
)
def validate_team_assigning(user_ids: List[str], team: Team) -> List[User]:
users = _check_nonexistent_users(user_ids)
......@@ -147,3 +164,48 @@ def validate_definition_access_removing(
definition_id=definition.id,
)
return users
def validate_credentials_regeneration(users: QuerySet[User], requester: User):
if settings.NOAUTH:
return
if (
requester.group != UserGroup.ADMIN
and users.filter(group__name=UserGroup.ADMIN).exists()
):
raise PermissionDenied(
"Permission denied - Only admin can re-generate credentials for admin users"
)
def execute_change_userdata(
change_user_input: ChangeUserInput, changing_user: User, requester: User
) -> List[str]:
changes = []
old_group = changing_user.group
old_status = changing_user.is_active
if change_user_input.group is not None:
_validate_group_change(requester, changing_user)
changing_user.group = Group.objects.get(name=change_user_input.group)
if change_user_input.group == "admin":
changing_user.is_superuser = True
changing_user.is_staff = True
changes.append(f"group=(old: {old_group}, new: admin)")
elif change_user_input.group == "instructor":
changing_user.is_staff = True
changing_user.is_superuser = False
changes.append(f"group=(old: {old_group}, new: instructor)")
elif change_user_input.group == "trainee":
changing_user.is_staff = False
changing_user.is_superuser = False
changes.append(f"group=(old: {old_group}, new: trainee)")
if (
change_user_input.active is not None and not changing_user.is_imported
): # can not change is_active of imported user (should be always False)
changing_user.is_active = change_user_input.active
changes.append(
f"active=(old: {old_status}, new: {change_user_input.active})"
)
changing_user.save()
return changes
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment