Skip to content

Commit 62da75d

Browse files
authored
Added update_role and delete_user endpoints (#781)
* Update views to match frontend requirements * Added update user role route * Added delete user from course
1 parent 80aa571 commit 62da75d

File tree

7 files changed

+553
-5
lines changed

7 files changed

+553
-5
lines changed

lib/cadet/accounts/accounts.ex

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ defmodule Cadet.Accounts do
88

99
alias Cadet.Accounts.{Query, User, CourseRegistration}
1010
alias Cadet.Auth.Provider
11+
alias Cadet.Assessments.{Answer, Submission}
1112

1213
@doc """
1314
Register new User entity using Cadet.Accounts.Form.Registration
@@ -109,4 +110,91 @@ defmodule Cadet.Accounts do
109110
{:error, changeset} -> {:error, {:internal_server_error, full_error_messages(changeset)}}
110111
end
111112
end
113+
114+
@update_role_roles ~w(admin)a
115+
def update_role(
116+
_admin_course_reg = %CourseRegistration{
117+
id: admin_course_reg_id,
118+
course_id: admin_course_id,
119+
role: admin_role
120+
},
121+
role,
122+
coursereg_id
123+
) do
124+
with {:validate_role, true} <- {:validate_role, admin_role in @update_role_roles},
125+
{:validate_not_self, true} <- {:validate_not_self, admin_course_reg_id != coursereg_id},
126+
{:get_cr, user_course_reg} when not is_nil(user_course_reg) <-
127+
{:get_cr, CourseRegistration |> where(id: ^coursereg_id) |> Repo.one()},
128+
{:validate_same_course, true} <-
129+
{:validate_same_course, user_course_reg.course_id == admin_course_id},
130+
{:update_db, {:ok, _} = result} <-
131+
{:update_db,
132+
user_course_reg |> CourseRegistration.changeset(%{role: role}) |> Repo.update()} do
133+
result
134+
else
135+
{:validate_role, false} ->
136+
{:error, {:forbidden, "User is not permitted to change others' roles"}}
137+
138+
{:validate_not_self, false} ->
139+
{:error, {:bad_request, "Admin not allowed to downgrade own role"}}
140+
141+
{:get_cr, _} ->
142+
{:error, {:bad_request, "User course registration does not exist"}}
143+
144+
{:validate_same_course, false} ->
145+
{:error, {:forbidden, "Wrong course"}}
146+
147+
{:update_db, {:error, changeset}} ->
148+
{:error, {:bad_request, full_error_messages(changeset)}}
149+
end
150+
end
151+
152+
@delete_user_roles ~w(admin)a
153+
def delete_user(
154+
_admin_course_reg = %CourseRegistration{
155+
id: admin_course_reg_id,
156+
course_id: admin_course_id,
157+
role: admin_role
158+
},
159+
coursereg_id
160+
) do
161+
with {:validate_role, true} <- {:validate_role, admin_role in @delete_user_roles},
162+
{:validate_not_self, true} <- {:validate_not_self, admin_course_reg_id != coursereg_id},
163+
{:get_cr, user_course_reg} when not is_nil(user_course_reg) <-
164+
{:get_cr, CourseRegistration |> where(id: ^coursereg_id) |> Repo.one()},
165+
{:prevent_delete_admin, true} <- {:prevent_delete_admin, user_course_reg.role != :admin},
166+
{:validate_same_course, true} <-
167+
{:validate_same_course, user_course_reg.course_id == admin_course_id} do
168+
# TODO: Handle deletions of achievement entries, etc. too
169+
170+
# Delete submissions and answers before deleting user
171+
Submission
172+
|> where(student_id: ^user_course_reg.id)
173+
|> Repo.all()
174+
|> Enum.each(fn x ->
175+
Answer
176+
|> where(submission_id: ^x.id)
177+
|> Repo.delete_all()
178+
179+
Repo.delete(x)
180+
end)
181+
182+
Repo.delete(user_course_reg)
183+
else
184+
{:validate_role, false} ->
185+
{:error, {:forbidden, "User is not permitted to delete other users"}}
186+
187+
{:validate_not_self, false} ->
188+
{:error, {:bad_request, "Admin not allowed to delete ownself from course"}}
189+
190+
{:get_cr, _} ->
191+
{:error, {:bad_request, "User course registration does not exist"}}
192+
193+
{:prevent_delete_admin, false} ->
194+
{:error, {:bad_request, "Admins cannot be deleted"}}
195+
196+
{:validate_same_course, false} ->
197+
{:error, {:forbidden, "Wrong course"}}
198+
end
199+
end
112200
end

lib/cadet_web/admin_controllers/admin_user_controller.ex

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,30 @@ defmodule CadetWeb.AdminUserController do
1313
render(conn, "users.json", users: users)
1414
end
1515

16+
def update_role(conn, %{"role" => role, "crId" => coursereg_id}) do
17+
case Accounts.update_role(conn.assigns.course_reg, role, coursereg_id) do
18+
{:ok, %{}} ->
19+
text(conn, "OK")
20+
21+
{:error, {status, message}} ->
22+
conn
23+
|> put_status(status)
24+
|> text(message)
25+
end
26+
end
27+
28+
def delete_user(conn, %{"crId" => coursereg_id}) do
29+
case Accounts.delete_user(conn.assigns.course_reg, coursereg_id) do
30+
{:ok, %{}} ->
31+
text(conn, "OK")
32+
33+
{:error, {status, message}} ->
34+
conn
35+
|> put_status(status)
36+
|> text(message)
37+
end
38+
end
39+
1640
swagger_path :index do
1741
get("/v2/courses/{course_id}/admin/users")
1842

@@ -24,6 +48,56 @@ defmodule CadetWeb.AdminUserController do
2448
response(401, "Unauthorised")
2549
end
2650

51+
swagger_path :update_role do
52+
put("/v2/courses/{course_id}/admin/users/role")
53+
54+
summary("Updates the role of the given user in the the course")
55+
security([%{JWT: []}])
56+
consumes("application/json")
57+
58+
parameters do
59+
course_id(:path, :integer, "Course ID", required: true)
60+
role(:body, :role, "The new role", required: true)
61+
62+
crId(:body, :integer, "The course registration of the user whose role is to be updated",
63+
required: true
64+
)
65+
end
66+
67+
response(200, "OK")
68+
69+
response(
70+
400,
71+
"Bad Request. User course registration does not exist or admin not allowed to downgrade own role"
72+
)
73+
74+
response(403, "Forbidden. User is in different course, or you are not an admin")
75+
end
76+
77+
swagger_path :delete_user do
78+
delete("/v2/courses/{course_id}/admin/users")
79+
80+
summary("Deletes a user from a course")
81+
consumes("application/json")
82+
83+
parameters do
84+
course_id(:path, :integer, "Course ID", required: true)
85+
86+
crId(:body, :integer, "The course registration of the user whose role is to be updated",
87+
required: true
88+
)
89+
end
90+
91+
response(200, "OK")
92+
93+
response(
94+
400,
95+
"Bad Request. User course registration does not exist or admin not allowed to delete ownself from course or admins cannot be deleted"
96+
)
97+
98+
response(403, "Forbidden. User is in different course, or you are not an admin")
99+
end
100+
27101
def swagger_definitions do
28102
%{
29103
AdminUserInfo:

lib/cadet_web/admin_views/admin_user_view.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ defmodule CadetWeb.AdminUserView do
1010
crId: cr.id,
1111
course_id: cr.course_id,
1212
name: cr.user.name,
13+
username: cr.user.username,
1314
role: cr.role,
1415
group:
1516
case cr.group do

lib/cadet_web/router.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ defmodule CadetWeb.Router do
127127
)
128128

129129
get("/users", AdminUserController, :index)
130+
put("/users/role", AdminUserController, :update_role)
131+
delete("/users", AdminUserController, :delete_user)
130132
post("/users/:userid/goals/:uuid/progress", AdminGoalsController, :update_progress)
131133

132134
put("/achievements", AdminAchievementsController, :bulk_update)

lib/cadet_web/views/user_view.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ defmodule CadetWeb.UserView do
6565

6666
_ ->
6767
%{
68+
crId: latest.id,
6869
courseId: latest.course_id,
6970
role: latest.role,
7071
group:

test/cadet/accounts/accounts_test.exs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,127 @@ defmodule Cadet.AccountsTest do
155155
assert length(all_stu_in_c1g2) == 0
156156
end
157157
end
158+
159+
describe "update_role" do
160+
setup do
161+
c1 = insert(:course, %{course_name: "c1"})
162+
c2 = insert(:course, %{course_name: "c2"})
163+
admin1 = insert(:course_registration, %{course: c1, role: :admin})
164+
staff1 = insert(:course_registration, %{course: c1, role: :staff})
165+
student1 = insert(:course_registration, %{course: c1, role: :student})
166+
student2 = insert(:course_registration, %{course: c2, role: :student})
167+
168+
{:ok, %{a1: admin1, s1: student1, s2: student2, st1: staff1}}
169+
end
170+
171+
test "successful when admin is admin of the course the user is in (student)", %{
172+
a1: admin1,
173+
s1: %{id: coursereg_id}
174+
} do
175+
{:ok, updated_coursereg} = Accounts.update_role(admin1, "student", coursereg_id)
176+
assert updated_coursereg.role == :student
177+
end
178+
179+
test "successful when admin is admin of the course the user is in (staff)", %{
180+
a1: admin1,
181+
s1: %{id: coursereg_id}
182+
} do
183+
{:ok, updated_coursereg} = Accounts.update_role(admin1, "staff", coursereg_id)
184+
assert updated_coursereg.role == :staff
185+
end
186+
187+
test "successful when admin is admin of the course the user is in (admin)", %{
188+
a1: admin1,
189+
s1: %{id: coursereg_id}
190+
} do
191+
{:ok, updated_coursereg} = Accounts.update_role(admin1, "admin", coursereg_id)
192+
assert updated_coursereg.role == :admin
193+
end
194+
195+
test "fails when admin tries to downgrade own role", %{a1: %{id: coursereg_id} = admin1} do
196+
assert {:error, {:bad_request, "Admin not allowed to downgrade own role"}} ==
197+
Accounts.update_role(admin1, "staff", coursereg_id)
198+
end
199+
200+
test "fails when user course registration does not exist", %{
201+
a1: admin1,
202+
s2: %{id: coursereg_id}
203+
} do
204+
assert {:error, {:bad_request, "User course registration does not exist"}} ==
205+
Accounts.update_role(admin1, "staff", coursereg_id + 1)
206+
end
207+
208+
test "admin is not admin of the course the user is in", %{a1: admin1, s2: %{id: coursereg_id}} do
209+
assert {:error, {:forbidden, "Wrong course"}} ==
210+
Accounts.update_role(admin1, "staff", coursereg_id)
211+
end
212+
213+
test "invalid role provided", %{a1: admin1, s1: %{id: coursereg_id}} do
214+
assert {:error, {:bad_request, "role is invalid"}} ==
215+
Accounts.update_role(admin1, "invalidrole", coursereg_id)
216+
end
217+
218+
test "fails when staff makes changes", %{st1: staff1, s1: %{id: coursereg_id}} do
219+
assert {:error, {:forbidden, "User is not permitted to change others' roles"}} ==
220+
Accounts.update_role(staff1, "staff", coursereg_id)
221+
end
222+
end
223+
224+
describe "delete_user" do
225+
setup do
226+
c1 = insert(:course, %{course_name: "c1"})
227+
c2 = insert(:course, %{course_name: "c2"})
228+
admin1 = insert(:course_registration, %{course: c1, role: :admin})
229+
admin2 = insert(:course_registration, %{course: c1, role: :admin})
230+
staff1 = insert(:course_registration, %{course: c1, role: :staff})
231+
student1 = insert(:course_registration, %{course: c1, role: :student})
232+
student2 = insert(:course_registration, %{course: c2, role: :student})
233+
234+
{:ok, %{a1: admin1, a2: admin2, s1: student1, s2: student2, st1: staff1}}
235+
end
236+
237+
test "successful when admin is admin of the course the user is in (student)", %{
238+
a1: admin1,
239+
s1: %{id: coursereg_id}
240+
} do
241+
{:ok, deleted_entry} = Accounts.delete_user(admin1, coursereg_id)
242+
assert deleted_entry.id == coursereg_id
243+
end
244+
245+
test "successful when admin is admin of the course the user is in (staff)", %{
246+
a1: admin1,
247+
st1: %{id: coursereg_id}
248+
} do
249+
{:ok, deleted_entry} = Accounts.delete_user(admin1, coursereg_id)
250+
assert deleted_entry.id == coursereg_id
251+
end
252+
253+
test "fails when staff tries to delete user", %{st1: staff1, s1: %{id: coursereg_id}} do
254+
assert {:error, {:forbidden, "User is not permitted to delete other users"}} ==
255+
Accounts.delete_user(staff1, coursereg_id)
256+
end
257+
258+
test "fails when deleting own self", %{a1: %{id: coursereg_id} = admin1} do
259+
assert {:error, {:bad_request, "Admin not allowed to delete ownself from course"}} ==
260+
Accounts.delete_user(admin1, coursereg_id)
261+
end
262+
263+
test "fails when user course registration does not exist", %{
264+
a1: admin1,
265+
s2: %{id: coursereg_id}
266+
} do
267+
assert {:error, {:bad_request, "User course registration does not exist"}} ==
268+
Accounts.delete_user(admin1, coursereg_id + 1)
269+
end
270+
271+
test "fails when deleting an admin", %{a1: admin1, a2: %{id: coursereg_id}} do
272+
assert {:error, {:bad_request, "Admins cannot be deleted"}} ==
273+
Accounts.delete_user(admin1, coursereg_id)
274+
end
275+
276+
test "fails when deleting a user from another course", %{a1: admin1, s2: %{id: coursereg_id}} do
277+
assert {:error, {:forbidden, "Wrong course"}} ==
278+
Accounts.delete_user(admin1, coursereg_id)
279+
end
280+
end
158281
end

0 commit comments

Comments
 (0)