• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

source-academy / backend / 18dc689a4df4836fc6967bf0f74dc252964bd175-PR-1180

08 Sep 2024 06:14PM UTC coverage: 79.088% (-15.3%) from 94.372%
18dc689a4df4836fc6967bf0f74dc252964bd175-PR-1180

Pull #1180

github

josh1248
Change appropriate routes into admin scope
Pull Request #1180: Transfer groundControl (and admin panel) from staff to admin route

7 of 12 new or added lines in 1 file covered. (58.33%)

499 existing lines in 25 files now uncovered.

2602 of 3290 relevant lines covered (79.09%)

1023.2 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

35.44
/lib/cadet_web/admin_controllers/admin_user_controller.ex
1
defmodule CadetWeb.AdminUserController do
2
  use CadetWeb, :controller
3
  use PhoenixSwagger
4

5
  import Ecto.Query
6

7
  alias Cadet.Repo
8
  alias Cadet.{Accounts, Assessments, Courses}
9
  alias Cadet.Accounts.{CourseRegistrations, CourseRegistration, Role}
10

11
  # This controller is used to find all users of a course
12

13
  def index(conn, filter) do
UNCOV
14
    users =
×
UNCOV
15
      filter |> try_keywordise_string_keys() |> Accounts.get_users_by(conn.assigns.course_reg)
×
16

UNCOV
17
    render(conn, "users.json", users: users)
×
18
  end
19

20
  def combined_total_xp(conn, %{"course_reg_id" => course_reg_id}) do
UNCOV
21
    course_reg = Repo.get(CourseRegistration, course_reg_id)
×
22

UNCOV
23
    course_id = course_reg.course_id
×
UNCOV
24
    user_id = course_reg.user_id
×
UNCOV
25
    course_reg_id = course_reg.id
×
26

UNCOV
27
    total_xp = Assessments.user_total_xp(course_id, user_id, course_reg_id)
×
UNCOV
28
    json(conn, %{totalXp: total_xp})
×
29
  end
30

31
  @add_users_role ~w(admin)a
32
  def get_students(conn, filter) do
33
    users =
×
34
      filter |> try_keywordise_string_keys() |> Accounts.get_users_by(conn.assigns.course_reg)
×
35

36
    render(conn, "get_students.json", users: users)
×
37
  end
38

39
  @add_users_role ~w(admin)a
40
  def upsert_users_and_groups(conn, %{
41
        "course_id" => course_id,
42
        "users" => usernames_roles_groups,
43
        "provider" => provider
44
      }) do
UNCOV
45
    %{role: admin_role} = conn.assigns.course_reg
×
UNCOV
46
    usernames_roles_groups = usernames_roles_groups |> Enum.map(&to_snake_case_atom_keys/1)
×
47

UNCOV
48
    with {:validate_cap, true} <-
×
49
           {:validate_cap,
50
            Enum.count(CourseRegistrations.get_users(course_id) ++ usernames_roles_groups) <= 1500},
UNCOV
51
         {:validate_role, true} <- {:validate_role, admin_role in @add_users_role},
×
UNCOV
52
         {:validate_provider, true} <-
×
53
           {:validate_provider,
54
            Map.has_key?(Application.get_env(:cadet, :identity_providers, %{}), provider)},
UNCOV
55
         {:validate_usernames, true} <-
×
56
           {:validate_usernames,
57
            Enum.all?(usernames_roles_groups, fn x ->
UNCOV
58
              Map.has_key?(x, :username) and is_binary(x.username) and x.username != ""
×
59
            end)},
UNCOV
60
         {:validate_roles, true} <-
×
61
           {:validate_roles,
62
            Enum.all?(usernames_roles_groups, fn x ->
UNCOV
63
              Map.has_key?(x, :role) and String.to_atom(x.role) in Role.__enums__()
×
64
            end)} do
UNCOV
65
      {:ok, conn} =
×
66
        Repo.transaction(
67
          fn ->
UNCOV
68
            with {:upsert_users, :ok} <-
×
69
                   {:upsert_users,
70
                    CourseRegistrations.upsert_users_in_course(
71
                      provider,
72
                      usernames_roles_groups,
73
                      course_id
74
                    )},
UNCOV
75
                 {:upsert_groups, :ok} <-
×
76
                   {:upsert_groups,
77
                    Courses.upsert_groups_in_course(usernames_roles_groups, course_id, provider)} do
UNCOV
78
              text(conn, "OK")
×
79
            else
80
              {:upsert_users, {:error, {status, message}}} ->
81
                conn |> put_status(status) |> text(message)
×
82

83
              {:upsert_groups, {:error, {status, message}}} ->
84
                conn |> put_status(status) |> text(message)
×
85
            end
86
          end,
87
          timeout: 20_000
88
        )
89

UNCOV
90
      conn
×
91
    else
92
      {:validate_cap, false} ->
UNCOV
93
        conn |> put_status(:bad_request) |> text("A course can have maximum of 1500 users")
×
94

95
      {:validate_role, false} ->
UNCOV
96
        conn |> put_status(:forbidden) |> text("User is not permitted to add users")
×
97

98
      {:validate_provider, false} ->
UNCOV
99
        conn |> put_status(:bad_request) |> text("Invalid authentication provider")
×
100

101
      {:validate_usernames, false} ->
UNCOV
102
        conn |> put_status(:bad_request) |> text("Invalid username(s) provided")
×
103

104
      {:validate_roles, false} ->
UNCOV
105
        conn |> put_status(:bad_request) |> text("Invalid role(s) provided")
×
106
    end
107
  end
108

109
  @update_role_roles ~w(admin)a
110
  def update_role(conn, %{"role" => role, "course_reg_id" => course_reg_id}) do
UNCOV
111
    course_reg_id = course_reg_id |> String.to_integer()
×
112

UNCOV
113
    %{id: admin_course_reg_id, role: admin_role, course_id: admin_course_id} =
×
UNCOV
114
      conn.assigns.course_reg
×
115

UNCOV
116
    with {:validate_role, true} <- {:validate_role, admin_role in @update_role_roles},
×
UNCOV
117
         {:validate_not_self, true} <- {:validate_not_self, admin_course_reg_id != course_reg_id},
×
UNCOV
118
         {:get_cr, user_course_reg} when not is_nil(user_course_reg) <-
×
UNCOV
119
           {:get_cr, CourseRegistration |> where(id: ^course_reg_id) |> Repo.one()},
×
UNCOV
120
         {:validate_same_course, true} <-
×
UNCOV
121
           {:validate_same_course, user_course_reg.course_id == admin_course_id} do
×
UNCOV
122
      case CourseRegistrations.update_role(role, course_reg_id) do
×
123
        {:ok, %{}} ->
UNCOV
124
          text(conn, "OK")
×
125

126
        {:error, {status, message}} ->
127
          conn
128
          |> put_status(status)
UNCOV
129
          |> text(message)
×
130
      end
131
    else
132
      {:validate_role, false} ->
UNCOV
133
        conn |> put_status(:forbidden) |> text("User is not permitted to change others' roles")
×
134

135
      {:validate_not_self, false} ->
136
        conn |> put_status(:bad_request) |> text("Admin not allowed to downgrade own role")
×
137

138
      {:get_cr, _} ->
UNCOV
139
        conn |> put_status(:bad_request) |> text("User course registration does not exist")
×
140

141
      {:validate_same_course, false} ->
UNCOV
142
        conn |> put_status(:forbidden) |> text("User is in a different course")
×
143
    end
144
  end
145

146
  @delete_user_roles ~w(admin)a
147
  def delete_user(conn, %{"course_reg_id" => course_reg_id}) do
148
    course_reg_id = course_reg_id |> String.to_integer()
6✔
149

150
    %{id: admin_course_reg_id, role: admin_role, course_id: admin_course_id} =
6✔
151
      conn.assigns.course_reg
6✔
152

153
    with {:validate_role, true} <- {:validate_role, admin_role in @delete_user_roles},
6✔
154
         {:validate_not_self, true} <- {:validate_not_self, admin_course_reg_id != course_reg_id},
6✔
155
         {:get_cr, user_course_reg} when not is_nil(user_course_reg) <-
5✔
156
           {:get_cr, CourseRegistration |> where(id: ^course_reg_id) |> Repo.one()},
5✔
157
         {:prevent_delete_admin, true} <- {:prevent_delete_admin, user_course_reg.role != :admin},
4✔
158
         {:validate_same_course, true} <-
3✔
159
           {:validate_same_course, user_course_reg.course_id == admin_course_id} do
3✔
160
      case CourseRegistrations.delete_course_registration(course_reg_id) do
2✔
161
        {:ok, %{}} ->
162
          text(conn, "OK")
2✔
163

164
        {:error, {status, message}} ->
165
          conn
166
          |> put_status(status)
167
          |> text(message)
×
168
      end
169
    else
170
      {:validate_role, false} ->
UNCOV
171
        conn |> put_status(:forbidden) |> text("User is not permitted to delete other users")
×
172

173
      {:validate_not_self, false} ->
174
        conn
175
        |> put_status(:bad_request)
176
        |> text("Admin not allowed to delete ownself from course")
1✔
177

178
      {:get_cr, _} ->
179
        conn |> put_status(:bad_request) |> text("User course registration does not exist")
1✔
180

181
      {:prevent_delete_admin, false} ->
182
        conn |> put_status(:bad_request) |> text("Admins cannot be deleted")
1✔
183

184
      {:validate_same_course, false} ->
185
        conn |> put_status(:forbidden) |> text("User is in a different course")
1✔
186
    end
187
  end
188

189
  swagger_path :index do
1✔
190
    get("/courses/{course_id}/admin/users")
191

192
    summary("Returns a list of users in the course owned by the admin")
193

194
    security([%{JWT: []}])
195
    produces("application/json")
196
    response(200, "OK", Schema.ref(:AdminUserInfo))
197
    response(401, "Unauthorised")
198
  end
199

200
  swagger_path :combined_total_xp do
1✔
201
    get("/courses/{course_id}/admin/users/{course_reg_id}/total_xp")
202

203
    summary("Get the specified user's total XP from achievements and assessments")
204

205
    security([%{JWT: []}])
206
    produces("application/json")
207

208
    parameters do
209
      course_id(:path, :integer, "Course ID", required: true)
210
      course_reg_id(:path, :integer, "Course registration ID", required: true)
211
    end
212

213
    response(200, "OK", Schema.ref(:TotalXPInfo))
214
    response(401, "Unauthorised")
215
  end
216

217
  swagger_path :upsert_users_and_groups do
1✔
218
    put("/courses/{course_id}/admin/users")
219

220
    summary("Adds the list of usernames and roles to the course")
221
    security([%{JWT: []}])
222
    consumes("application/json")
223

224
    parameters do
225
      course_id(:path, :integer, "Course ID", required: true)
226
      users(:body, Schema.array(:UsernameAndRole), "Array of usernames and roles", required: true)
227

228
      provider(:body, :string, "The authentication provider linked to these usernames",
229
        required: true
230
      )
231
    end
232

233
    response(200, "OK")
234
    response(400, "Bad Request. Invalid provider, username or role")
235
    response(403, "Forbidden. You are not an admin")
236
  end
237

238
  swagger_path :update_role do
1✔
239
    put("/courses/{course_id}/admin/users/{course_reg_id}/role")
240

241
    summary("Updates the role of the given user in the the course")
242
    security([%{JWT: []}])
243
    consumes("application/json")
244

245
    parameters do
246
      course_id(:path, :integer, "Course ID", required: true)
247
      role(:body, :role, "The new role", required: true)
248

249
      courseRegId(
250
        :body,
251
        :integer,
252
        "The course registration of the user whose role is to be updated",
253
        required: true
254
      )
255
    end
256

257
    response(200, "OK")
258

259
    response(
260
      400,
261
      "Bad Request. User course registration does not exist or admin not allowed to downgrade own role"
262
    )
263

264
    response(403, "Forbidden. User is in different course, or you are not an admin")
265
  end
266

267
  swagger_path :delete_user do
1✔
268
    delete("/courses/{course_id}/admin/users/{course_reg_id}")
269

270
    summary("Deletes a user from a course")
271
    consumes("application/json")
272

273
    parameters do
274
      course_id(:path, :integer, "Course ID", required: true)
275

276
      courseRegId(
277
        :body,
278
        :integer,
279
        "The course registration of the user whose role is to be updated",
280
        required: true
281
      )
282
    end
283

284
    response(200, "OK")
285

286
    response(
287
      400,
288
      "Bad Request. User course registration does not exist or admin not allowed to delete ownself from course or admins cannot be deleted"
289
    )
290

291
    response(403, "Forbidden. User is in different course, or you are not an admin")
292
  end
293

294
  def swagger_definitions do
295
    %{
1✔
296
      AdminUserInfo:
297
        swagger_schema do
1✔
298
          title("User")
299
          description("Basic information about the user in this course")
300

301
          properties do
1✔
302
            userId(:integer, "User's ID")
303
            name(:string, "Full name of the user")
304

305
            role(
306
              :string,
307
              "Role of the user in this course. Can be 'student', 'staff', or 'admin'"
308
            )
309

310
            group(
1✔
311
              :string,
312
              "Group the user belongs to in this course. May be null if the user does not belong to any group"
313
            )
314
          end
315
        end,
316
      UsernameAndRole:
317
        swagger_schema do
1✔
318
          title("Username and role")
319
          description("Username and role of the user to add to this course")
320

321
          properties do
1✔
322
            username(:string, "The user's username")
323
            role(:role, "The user's role. Can be 'student', 'staff', or 'admin'")
1✔
324
          end
325
        end
326
    }
327
  end
328
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc