diff --git a/tildes/tests/test_user.py b/tildes/tests/test_user.py index ce278ad..8c77eef 100644 --- a/tildes/tests/test_user.py +++ b/tildes/tests/test_user.py @@ -134,3 +134,11 @@ def test_banned_user_no_message_permission(): principals = principals_allowed_by_permission(banned_user, "message") assert not principals + + +def test_only_admin_has_ban_permission(): + """Ensure only admins have ban permissions.""" + user = User("Test_User", "password") + + principals = principals_allowed_by_permission(user, "ban") + assert principals == {"admin"} diff --git a/tildes/tildes/models/user/user.py b/tildes/tildes/models/user/user.py index 6ef2233..7308e61 100644 --- a/tildes/tildes/models/user/user.py +++ b/tildes/tildes/models/user/user.py @@ -198,6 +198,14 @@ class User(DatabaseModel): acl.append((Deny, self.user_id, "message")) acl.append((Allow, Authenticated, "message")) + # ban: + # - admins can ban non-deleted users except themselves + if self.is_deleted: + acl.append((Deny, Everyone, "ban")) + + acl.append((Deny, self.user_id, "ban")) # required so users can't self-ban + acl.append((Allow, "admin", "ban")) + # grant the user all other permissions on themself acl.append((Allow, self.user_id, ALL_PERMISSIONS)) diff --git a/tildes/tildes/routes.py b/tildes/tildes/routes.py index 64b12c9..a805433 100644 --- a/tildes/tildes/routes.py +++ b/tildes/tildes/routes.py @@ -152,6 +152,7 @@ def add_intercooler_routes(config: Configurator) -> None: "/default_listing_options", factory=user_by_username, ) + add_ic_route("user_ban", "/ban", factory=user_by_username) class LoggedInFactory: diff --git a/tildes/tildes/templates/user.jinja2 b/tildes/tildes/templates/user.jinja2 index 4e05c24..68e971b 100644 --- a/tildes/tildes/templates/user.jinja2 +++ b/tildes/tildes/templates/user.jinja2 @@ -134,4 +134,20 @@ {% if request.has_permission('message', user) %} Send a private message {% endif %} + +{% if request.has_permission("ban", user) %} +
+ {% if user.is_banned %} + + {% else %} + + {% endif %} +{% endif %} + {% endblock %} diff --git a/tildes/tildes/views/api/web/user.py b/tildes/tildes/views/api/web/user.py index e071e0d..4686c96 100644 --- a/tildes/tildes/views/api/web/user.py +++ b/tildes/tildes/views/api/web/user.py @@ -329,3 +329,27 @@ def put_filtered_topic_tags(request: Request, tags: str) -> dict: request.user.filtered_topic_tags = result.data["tags"] return IC_NOOP + + +@ic_view_config(route_name="user_ban", request_method="PUT", permission="ban") +def put_user_ban(request: Request) -> Response: + """Ban a user.""" + user = request.context + + user.is_banned = True + + # delete all of the user's outstanding invite codes + request.query(UserInviteCode).filter( + UserInviteCode.user_id == user.user_id, + UserInviteCode.invitee_id == None, # noqa + ).delete(synchronize_session=False) + + return Response("Banned") + + +@ic_view_config(route_name="user_ban", request_method="DELETE", permission="ban") +def delete_user_ban(request: Request) -> Response: + """Unban a user.""" + request.context.is_banned = False + + return Response("Unbanned")