mirror of
https://github.com/ClaperCo/Claper.git
synced 2026-02-24 04:01:04 +01:00
commit 705ea00064e552f482bff52c3c5b11d23fbd5b4c Author: Alex Lion <dev@alexandrelion.com> Date: Sat Dec 21 10:08:42 2024 -0500 Change version commit 330173bd64bb18c5ea7e68a2122f66497981c3c3 Author: Alex Lion <dev@alexandrelion.com> Date: Sat Dec 21 10:01:52 2024 -0500 Fix layout commit 3cc075962e961f8a78b0c30eca9b79db6b9a5731 Author: Alex Lion <dev@alexandrelion.com> Date: Thu Dec 19 14:20:59 2024 +0100 Update changelog commit 63b1fa7ee591d40e44005b7939f51c99cf3e119e Author: Alex Lion <dev@alexandrelion.com> Date: Sun Dec 15 18:56:01 2024 +0100 Fix upper commit 8e7bb1990c58e343de5aa18036eb0916573fb4c6 Author: Alex Lion <dev@alexandrelion.com> Date: Sun Dec 15 18:49:20 2024 +0100 Add pagination for events commit 28beacd120f0a1081e670e4a06bbc185cc699beb Author: Alex Lion <dev@alexandrelion.com> Date: Mon Dec 9 21:03:13 2024 +0100 Add pagination commit c79d6cce947869b98795b9baf541a32952624969 Author: Alex Lion <dev@alexandrelion.com> Date: Sun Dec 8 21:24:23 2024 +0100 Fix tests commit caad25ad75b5937ca0906dca89dedaa4d58ae072 Author: Alex Lion <dev@alexandrelion.com> Date: Sun Dec 8 17:43:55 2024 +0100 Fix user registration bug commit 38c3eecc49d1397a8bb7a4a11203775396d00272 Author: Alex Lion <dev@alexandrelion.com> Date: Sat Dec 7 22:23:24 2024 +0100 Update changelog commit e648ef08a0f61cf4b554fcbf0a83e02a2249de0d Author: Alex Lion <dev@alexandrelion.com> Date: Sat Dec 7 22:06:27 2024 +0100 Add obin commit 6925117818e117dbd60efea5ae6c81a26a57f76f Author: Alex Lion <dev@alexandrelion.com> Date: Sat Dec 7 19:39:03 2024 +0100 WIP commit be9b2886d3b879452f5bae08b3cdd181cac254f8 Author: Alex Lion <dev@alexandrelion.com> Date: Sat Dec 7 16:19:09 2024 +0100 Add LTI AGS for quizzes commit 29a7c96de6d4e38b26dfaa61bfa5e689a16d4935 Author: Alex Lion <dev@alexandrelion.com> Date: Thu Dec 5 13:30:49 2024 +0100 Add translations commit 249fdc9188c7613a6adafb0b983303c1ae7601bd Author: Alex Lion <dev@alexandrelion.com> Date: Tue Dec 3 21:37:27 2024 +0100 Add qti export commit c2d56e30cdb6c629e957c64e4393dfd9d5af7159 Author: Alex Lion <dev@alexandrelion.com> Date: Tue Dec 3 20:44:58 2024 +0100 Fix report embed commit a34c239f9014e53b079106f1935bc2b079d01eed Author: Alex Lion <dev@alexandrelion.com> Date: Sat Nov 30 11:32:32 2024 +0100 Add export quiz commit 8d1f34b90635776ae40849bd75fd135693b116fb Author: Alex Lion <dev@alexandrelion.com> Date: Sat Nov 30 00:56:50 2024 +0100 Improve design commit d9a7370419ed9e288eccf263c2715330831e45e4 Author: Alex Lion <dev@alexandrelion.com> Date: Sat Nov 30 00:43:27 2024 +0100 Add exports commit b374b7bbccfa655dfad7695d7a24c5ddd4a07b66 Author: Alex Lion <dev@alexandrelion.com> Date: Thu Nov 28 15:22:41 2024 +0100 Remove presence on manager commit 404e759ae3d2f5e555ae20437204553bffdc5065 Author: Alex Lion <dev@alexandrelion.com> Date: Thu Nov 28 15:22:33 2024 +0100 Improve engagement report commit 39dbec6692c2d3f74a97647a703993d6152bfa06 Author: Alex Lion <dev@alexandrelion.com> Date: Sat Nov 23 15:39:59 2024 +0100 Add translation commit 354c2e30aece5bc7d800893ce8dee3868a1c1f71 Author: Alex Lion <dev@alexandrelion.com> Date: Sat Nov 23 15:18:53 2024 +0100 Change product tour behavior commit 5f253812282fb11011694b8828580d886f1f5899 Author: Alex <dev@alexandrelion.com> Date: Tue Nov 19 19:59:32 2024 +0100 WIP commit f411180433a05b89fc9d029e2b313968985e5c3f Author: Alex <dev@alexandrelion.com> Date: Tue Nov 19 19:32:30 2024 +0100 WIP commit 2b5989774eeb839f7b7b2a49377aca9fe4d68c09 Author: Alex <dev@alexandrelion.com> Date: Sun Nov 17 19:31:27 2024 +0100 WIP commit c8750a667f131b68818859796670c3022c6d53fe Author: Alex <dev@alexandrelion.com> Date: Sun Nov 17 18:23:01 2024 +0100 WIP commit fdb9efecb5688423ed2c82cf445868040653d380 Author: Alex <dev@alexandrelion.com> Date: Sun Nov 17 17:55:57 2024 +0100 WIP commit 5d12b12ce33eb5c1ba2a3307ef4ac679b279f511 Author: Alex <dev@alexandrelion.com> Date: Sat Nov 16 21:22:12 2024 +0100 WIP commit 548b714fda61464517247910af7e3e1c2bdae8cf Author: Alex <dev@alexandrelion.com> Date: Fri Nov 15 15:34:00 2024 +0100 WIP commit f0c87f34ea2ac837b4b3b3d6fd51c32bd625371e Author: Alex <dev@alexandrelion.com> Date: Wed Nov 13 22:09:24 2024 +0100 WIP commit c0c8bf99a538653208e28300566cced3d444a764 Author: Alex <dev@alexandrelion.com> Date: Mon Nov 11 13:02:36 2024 +0100 WIP commit 245ea9b836c2e69c7269fc7d8c7fd2edd0032eed Author: Alex <dev@alexandrelion.com> Date: Sun Nov 10 19:07:36 2024 +0100 Add presenter commit 0cf50918d62a9ab5ea127698219e05f781c659bb Author: Alex <dev@alexandrelion.com> Date: Sat Nov 9 23:20:35 2024 +0100 Refactor reactions commit ef8ffefe56d5b19dd895be181437c461134176ab Author: Alex <dev@alexandrelion.com> Date: Sat Nov 9 22:21:13 2024 +0100 Add tests commit c4055142ed63d8ea1be921f527bcaf595a2b9268 Author: Alex <dev@alexandrelion.com> Date: Sat Nov 9 11:28:00 2024 +0100 WIP commit 779e6970f7ee7ca89aab2bdfcff6197895b9ce5e Author: Alex <dev@alexandrelion.com> Date: Fri Nov 8 17:21:11 2024 +0100 WIP commit 9d25c440b830ded7e6fc2e0bcc9353520ec4a951 Author: Alex <dev@alexandrelion.com> Date: Fri Nov 8 11:54:31 2024 +0100 WIP commit c0157487a9e20b6773e517553681915c12367851 Author: Alex <dev@alexandrelion.com> Date: Fri Nov 1 17:13:04 2024 +0100 Fix condition commit a64439fbf2d852e127deb00a11906fb86b0c9ece Author: Alex <dev@alexandrelion.com> Date: Fri Nov 1 12:16:21 2024 +0100 WIP commit a994d959afe20ee380d42feb5ca6da2ab832d569 Author: Alex <dev@alexandrelion.com> Date: Wed Oct 30 23:06:24 2024 +0100 Fix changeset commit 5b2935fc33577af21ccc2558b49d9a813f4835f3 Merge: cec1a977476269Author: Alex <dev@alexandrelion.com> Date: Sun Oct 20 11:26:51 2024 +0200 Merge branch 'dev' into feature/quizz commit cec1a97650867da3a09d8e23d0756a3a573e1bc8 Author: Alex <dev@alexandrelion.com> Date: Sat Oct 19 22:52:00 2024 +0200 WIP commit f65854f638393ce80fd9d17642e8a90ee5c1a06e Author: Alex <dev@alexandrelion.com> Date: Sat Oct 19 16:55:42 2024 +0200 WIP commit 1e6429a386c56be6a8fdd2f083e273b50a6bc4c9 Merge:19779596f8a2fdAuthor: Alex <dev@alexandrelion.com> Date: Sat Oct 19 13:49:22 2024 +0200 Merge branch 'dev' into feature/quizz # Conflicts: # lib/claper_web/live/event_live/manage.html.heex commit1977959efbAuthor: Alex <dev@alexandrelion.com> Date: Sat Oct 5 12:57:09 2024 +0200 WIP
209 lines
6.7 KiB
Elixir
209 lines
6.7 KiB
Elixir
defmodule Lti13.Tool.LaunchValidation do
|
|
import Lti13.Jwks.Validator
|
|
|
|
alias Lti13.Deployments
|
|
alias Lti13.Registrations
|
|
|
|
@message_validators [
|
|
Lti13.Tool.MessageValidators.ResourceMessageValidator
|
|
]
|
|
|
|
@authorized_to_create_event_roles [
|
|
"http://purl.imsglobal.org/vocab/lis/v2/membership#Administrator",
|
|
"http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor"
|
|
]
|
|
|
|
@doc """
|
|
Validates an incoming LTI 1.3 launch and returns the claims if successful.
|
|
"""
|
|
def validate(params, session_state, _opts \\ []) do
|
|
with {:ok} <- validate_oidc_state(params, session_state),
|
|
{:ok, registration} <- validate_registration(params),
|
|
{:ok, key_set_url} <- registration_key_set_url(registration),
|
|
{:ok, id_token} <- extract_param(params, "id_token"),
|
|
{:ok, jwt_body} <- validate_jwt_signature(id_token, key_set_url),
|
|
{:ok} <- validate_timestamps(jwt_body),
|
|
{:ok} <- validate_deployment(registration, jwt_body),
|
|
{:ok} <- validate_message(jwt_body),
|
|
{:ok, lti_user} <- validate_user(jwt_body, registration),
|
|
{:ok} <- validate_nonce(lti_user, jwt_body, "validate_launch"),
|
|
{:ok, is_instructor} <- validate_role(jwt_body),
|
|
{:ok, resource} <- validate_resource(jwt_body, lti_user, registration, is_instructor),
|
|
claims <- jwt_body do
|
|
{:ok, %{claims: claims, lti_user: lti_user, resource: resource}}
|
|
end
|
|
end
|
|
|
|
# Validate that the state sent with an OIDC launch matches the state that was sent in the OIDC response
|
|
# returns a boolean on whether it is valid or not
|
|
defp validate_oidc_state(params, session_state) do
|
|
case session_state do
|
|
nil ->
|
|
{:error,
|
|
%{
|
|
reason: :invalid_oidc_state,
|
|
msg:
|
|
"State from session is missing. Make sure cookies are enabled and configured correctly"
|
|
}}
|
|
|
|
_ ->
|
|
compare_oidc_states(params["state"], session_state)
|
|
end
|
|
end
|
|
|
|
defp compare_oidc_states(nil, _),
|
|
do: {:error, %{reason: :invalid_oidc_state, msg: "State from OIDC request is missing"}}
|
|
|
|
defp compare_oidc_states(request_state, session_state) when request_state == session_state,
|
|
do: {:ok}
|
|
|
|
defp compare_oidc_states(_, _),
|
|
do:
|
|
{:error,
|
|
%{reason: :invalid_oidc_state, msg: "State from OIDC request does not match session"}}
|
|
|
|
defp validate_registration(params) do
|
|
with {:ok, issuer, client_id} <- peek_issuer_client_id(params) do
|
|
case Registrations.get_registration_by_issuer_client_id(issuer, client_id) do
|
|
nil ->
|
|
{:error,
|
|
%{
|
|
reason: :invalid_registration,
|
|
msg:
|
|
"Registration with issuer \"#{issuer}\" and client id \"#{client_id}\" not found",
|
|
issuer: issuer,
|
|
client_id: client_id
|
|
}}
|
|
|
|
registration ->
|
|
{:ok, registration}
|
|
end
|
|
end
|
|
end
|
|
|
|
defp validate_resource(
|
|
%{
|
|
"https://purl.imsglobal.org/spec/lti/claim/custom" => %{
|
|
"resource_title" => title,
|
|
"resource_id" => resource_id
|
|
},
|
|
"https://purl.imsglobal.org/spec/lti-ags/claim/endpoint" => %{
|
|
"lineitems" => line_items_url
|
|
}
|
|
},
|
|
lti_user,
|
|
registration,
|
|
is_instructor
|
|
) do
|
|
case Lti13.Resources.get_resource_by_id_and_registration(resource_id, registration.id) do
|
|
nil -> handle_missing_resource(title, resource_id, lti_user, line_items_url, is_instructor)
|
|
resource -> handle_existing_resource(resource, lti_user, is_instructor)
|
|
end
|
|
end
|
|
|
|
defp handle_missing_resource(title, resource_id, lti_user, line_items_url, true) do
|
|
case Lti13.Resources.create_resource_with_event(%{
|
|
title: title,
|
|
resource_id: resource_id,
|
|
line_items_url: line_items_url,
|
|
lti_user: lti_user
|
|
}) do
|
|
{:ok, resource} -> {:ok, resource}
|
|
{:error, _} -> {:error, %{reason: :invalid_resource, msg: "Failed to create resource"}}
|
|
end
|
|
end
|
|
|
|
defp handle_existing_resource(resource, lti_user, true) do
|
|
maybe_create_activity_leader(resource, lti_user)
|
|
{:ok, resource}
|
|
end
|
|
|
|
defp handle_existing_resource(resource, _, false), do: {:ok, resource}
|
|
|
|
defp maybe_create_activity_leader(resource, lti_user) do
|
|
activity_leaders = Claper.Events.get_activity_leaders_for_event(resource.event_id)
|
|
activity_leaders_emails = Enum.map(activity_leaders, fn al -> al.email end)
|
|
|
|
if lti_user.email not in activity_leaders_emails && resource.event.user_id != lti_user.user_id do
|
|
Claper.Events.create_activity_leader(%{
|
|
email: lti_user.email,
|
|
user_id: lti_user.id,
|
|
event_id: resource.event_id
|
|
})
|
|
end
|
|
end
|
|
|
|
defp validate_role(jwt) do
|
|
roles = jwt["https://purl.imsglobal.org/spec/lti/claim/roles"]
|
|
is_instructor = Enum.any?(roles, fn role -> role in @authorized_to_create_event_roles end)
|
|
{:ok, is_instructor}
|
|
end
|
|
|
|
defp peek_issuer_client_id(params) do
|
|
with {:ok, jwt_string} <- extract_param(params, "id_token"),
|
|
{:ok, jwt_claims} <- peek_claims(jwt_string) do
|
|
{:ok, jwt_claims["iss"], peek_client_id(jwt_claims["aud"])}
|
|
end
|
|
end
|
|
|
|
defp peek_client_id([client_id | _]), do: client_id
|
|
defp peek_client_id(client_id), do: client_id
|
|
|
|
defp validate_deployment(registration, jwt_body) do
|
|
deployment_id = jwt_body["https://purl.imsglobal.org/spec/lti/claim/deployment_id"]
|
|
deployment = Deployments.get_deployment(registration.id, deployment_id)
|
|
|
|
case deployment do
|
|
nil ->
|
|
{:error,
|
|
%{
|
|
reason: :invalid_deployment,
|
|
msg: "Deployment with id \"#{deployment_id}\" not found",
|
|
registration_id: registration.id,
|
|
deployment_id: deployment_id
|
|
}}
|
|
|
|
_deployment ->
|
|
{:ok}
|
|
end
|
|
end
|
|
|
|
defp validate_message(jwt_body) do
|
|
case jwt_body["https://purl.imsglobal.org/spec/lti/claim/message_type"] do
|
|
nil ->
|
|
{:error, %{reason: :invalid_message_type, msg: "Missing message type"}}
|
|
|
|
message_type ->
|
|
validate_message_type(jwt_body, message_type)
|
|
end
|
|
end
|
|
|
|
defp validate_message_type(jwt_body, message_type) do
|
|
case apply_message_validator(jwt_body) do
|
|
nil ->
|
|
{:error,
|
|
%{
|
|
reason: :invalid_message_type,
|
|
msg: "Invalid or unsupported message type \"#{message_type}\""
|
|
}}
|
|
|
|
{:error, error} ->
|
|
{:error,
|
|
%{
|
|
reason: :invalid_message,
|
|
msg: "Message validation failed: (\"#{message_type}\") #{error}"
|
|
}}
|
|
|
|
_ ->
|
|
{:ok}
|
|
end
|
|
end
|
|
|
|
defp apply_message_validator(jwt_body) do
|
|
case Enum.find(@message_validators, fn mv -> mv.can_validate(jwt_body) end) do
|
|
nil -> nil
|
|
validator -> validator.validate(jwt_body)
|
|
end
|
|
end
|
|
end
|