Skip to content

Commit 7e0c984

Browse files
author
gitstart_bot
committed
chore> implement tools segmentation
1 parent 0c74869 commit 7e0c984

File tree

6 files changed

+525
-1
lines changed

6 files changed

+525
-1
lines changed

server/covmanager/views.py

+20-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from rest_framework import filters, mixins, viewsets
1313
from rest_framework.authentication import SessionAuthentication, TokenAuthentication
1414

15-
from crashmanager.models import Tool
15+
from crashmanager.models import Tool, User
1616
from server.views import JsonQueryFilterBackend, SimpleQueryFilterBackend
1717

1818
from .models import Collection, Report, ReportConfiguration, ReportSummary, Repository
@@ -715,6 +715,25 @@ class CollectionViewSet(
715715
CollectionFilterBackend,
716716
]
717717

718+
def create(self, request, *args, **kwargs):
719+
"""Check user has access to tools before creation"""
720+
tools_str = request.data.get('tools')
721+
requested_tools = set(tools_str.split(','))
722+
723+
user = User.get_or_create_restricted(request.user)[0]
724+
if user.restricted:
725+
allowed_tools = set(user.defaultToolsFilter.values_list('name', flat=True))
726+
if not allowed_tools:
727+
raise PermissionDenied({"message": "No tools assigned to user"})
728+
729+
unauthorized_tools = requested_tools - allowed_tools
730+
if unauthorized_tools:
731+
raise PermissionDenied(
732+
{"message": f"You don't have permission to use the following tools: {', '.join(unauthorized_tools)}"}
733+
)
734+
735+
return super().create(request, *args, **kwargs)
736+
718737

719738
class ReportFilterBackend(filters.BaseFilterBackend):
720739
"""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from django.core.management.base import BaseCommand
2+
from rest_framework.authtoken.models import Token
3+
from crashmanager.models import Tool, User as CrashManagerUser
4+
5+
6+
class Command(BaseCommand):
7+
help = "Assigns a tool to a user's defaultToolsFilter by looking up their token and ensures the user is restricted."
8+
9+
def add_arguments(self, parser):
10+
parser.add_argument("token", help="The token string from get_auth_token command")
11+
parser.add_argument("tool_name", help="Name of the tool to add")
12+
13+
def handle(self, *args, **options):
14+
token_str = options["token"]
15+
tool_name = options["tool_name"]
16+
17+
try:
18+
token_obj = Token.objects.get(key=token_str)
19+
except Token.DoesNotExist:
20+
print(f"No token found for {token_str}")
21+
return
22+
23+
crash_manager_user = CrashManagerUser.get_or_create_restricted(token_obj.user)[0]
24+
25+
tool, created = Tool.objects.get_or_create(name=tool_name)
26+
if created:
27+
print(f"Tool '{tool_name}' was created.")
28+
29+
# Ensure the user is restricted, so they can only access the tool being added
30+
if not crash_manager_user.restricted:
31+
crash_manager_user.restricted = True
32+
crash_manager_user.save()
33+
print(f"User '{crash_manager_user.user.username}' has been restricted for security.")
34+
35+
crash_manager_user.defaultToolsFilter.add(tool)
36+
37+
print(f"Tool '{tool_name}' added to user '{crash_manager_user.user.username}'.")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from django.core.management.base import BaseCommand
2+
from rest_framework.authtoken.models import Token
3+
from crashmanager.models import Tool, User as CrashManagerUser
4+
import sys
5+
6+
7+
class Command(BaseCommand):
8+
help = "Removes a tool from a user's defaultToolsFilter by looking up their token."
9+
10+
def add_arguments(self, parser):
11+
parser.add_argument("token", help="The token string from get_auth_token command")
12+
parser.add_argument("tool_name", help="Name of the tool to remove")
13+
14+
def handle(self, *args, **options):
15+
token_str = options["token"]
16+
tool_name = options["tool_name"]
17+
18+
try:
19+
token_obj = Token.objects.get(key=token_str)
20+
except Token.DoesNotExist:
21+
print(f"No token found for {token_str}")
22+
return
23+
24+
crash_manager_user = CrashManagerUser.get_or_create_restricted(token_obj.user)[0]
25+
26+
try:
27+
tool = Tool.objects.get(name=tool_name)
28+
except Tool.DoesNotExist:
29+
print(f"Error: Tool '{tool_name}' is not present in the database")
30+
return
31+
32+
crash_manager_user.defaultToolsFilter.remove(tool)
33+
34+
# Keep user restricted regardless of tool count
35+
if not crash_manager_user.restricted:
36+
crash_manager_user.restricted = True
37+
crash_manager_user.save()
38+
if not crash_manager_user.defaultToolsFilter.exists():
39+
print(f"User '{crash_manager_user.user.username}' has no tools assigned but remains restricted.")
40+
41+
print(f"Tool '{tool_name}' removed from user '{crash_manager_user.user.username}'.")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
"""Tests for add/remove tool management commands
2+
3+
@license:
4+
This Source Code Form is subject to the terms of the Mozilla Public
5+
License, v. 2.0. If a copy of the MPL was not distributed with this
6+
file, You can obtain one at http://mozilla.org/MPL/2.0/.
7+
"""
8+
import pytest
9+
from django.core.management import CommandError, call_command
10+
from django.contrib.auth.models import User
11+
from rest_framework.authtoken.models import Token
12+
from crashmanager.models import Tool, User as CrashManagerUser
13+
14+
pytestmark = pytest.mark.django_db()
15+
16+
def test_add_tool_to_token_new_tool(capsys):
17+
"""Test adding new tool to user's filter"""
18+
user = User.objects.create_user("testuser")
19+
token = Token.objects.create(user=user)
20+
21+
call_command("add_tool_to_token", token.key, "newtool")
22+
23+
# Check command output
24+
out, _ = capsys.readouterr()
25+
assert "Tool 'newtool' was created." in out
26+
assert "added to user" in out
27+
28+
# Verify database state
29+
cm_user = CrashManagerUser.objects.get(user=user)
30+
assert cm_user.restricted is True
31+
assert cm_user.defaultToolsFilter.filter(name="newtool").exists()
32+
33+
def test_add_tool_to_token_existing_tool(capsys):
34+
"""Test adding existing tool doesn't recreate it"""
35+
# Create tool first
36+
Tool.objects.create(name="existingtool")
37+
38+
user = User.objects.create_user("testuser")
39+
token = Token.objects.create(user=user)
40+
41+
call_command("add_tool_to_token", token.key, "existingtool")
42+
43+
# Check command output
44+
out, _ = capsys.readouterr()
45+
assert "Tool 'existingtool' was created." not in out
46+
assert "added to user" in out
47+
48+
def test_add_tool_to_token_restricts_user(capsys):
49+
"""Test unrestricted user becomes restricted when adding tool"""
50+
user = User.objects.create_user("testuser")
51+
cm_user = CrashManagerUser.get_or_create_restricted(user)[0]
52+
cm_user.restricted = False
53+
cm_user.save()
54+
55+
token = Token.objects.create(user=user)
56+
57+
call_command("add_tool_to_token", token.key, "newtool")
58+
59+
# Check warning message
60+
out, _ = capsys.readouterr()
61+
assert "has been restricted for security" in out
62+
63+
# Verify restriction
64+
cm_user.refresh_from_db()
65+
assert cm_user.restricted is True
66+
67+
def test_remove_tool_from_token_exists(capsys):
68+
"""Test removing existing tool from filter"""
69+
user = User.objects.create_user("testuser")
70+
cm_user = CrashManagerUser.get_or_create_restricted(user)[0]
71+
tool = Tool.objects.create(name="oldtool")
72+
cm_user.defaultToolsFilter.add(tool)
73+
74+
token = Token.objects.create(user=user)
75+
76+
call_command("remove_tool_from_token", token.key, "oldtool")
77+
78+
# Check output
79+
out, _ = capsys.readouterr()
80+
assert "removed from user" in out
81+
assert not cm_user.defaultToolsFilter.filter(name="oldtool").exists()
82+
83+
def test_remove_tool_from_token_last_tool(capsys):
84+
"""Test warning when removing last tool"""
85+
user = User.objects.create_user("testuser")
86+
cm_user = CrashManagerUser.get_or_create_restricted(user)[0]
87+
tool = Tool.objects.create(name="lasttool")
88+
cm_user.defaultToolsFilter.add(tool)
89+
90+
token = Token.objects.create(user=user)
91+
92+
call_command("remove_tool_from_token", token.key, "lasttool")
93+
94+
# Check warning
95+
out, _ = capsys.readouterr()
96+
assert "has no tools assigned" in out
97+
98+
# Refresh from DB to get updated restriction status
99+
cm_user.refresh_from_db()
100+
assert cm_user.restricted is True
101+
102+
def test_remove_tool_from_token_nonexistent(capsys):
103+
"""Test removing non-existent tool shows error"""
104+
user = User.objects.create_user("testuser")
105+
token = Token.objects.create(user=user)
106+
107+
# Should return normally with error message
108+
call_command("remove_tool_from_token", token.key, "notexist")
109+
110+
# Verify error message
111+
out, _ = capsys.readouterr()
112+
assert "Error: Tool 'notexist' is not present in the database" in out
113+
114+
# Verify no changes to tools
115+
cm_user = CrashManagerUser.objects.get(user=user)
116+
assert cm_user.defaultToolsFilter.count() == 0
117+
118+
def test_add_tool_to_token_invalid_token(capsys):
119+
"""Test error handling for invalid token"""
120+
call_command("add_tool_to_token", "invalid", "tool")
121+
out, _ = capsys.readouterr()
122+
assert "No token found for invalid" in out
123+
124+
def test_remove_tool_from_token_invalid_token(capsys):
125+
"""Test error handling for invalid token"""
126+
call_command("remove_tool_from_token", "invalid", "tool")
127+
out, _ = capsys.readouterr()
128+
assert "No token found for invalid" in out

0 commit comments

Comments
 (0)