atomic_lti 1.9.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/lib/atomic_lti/definitions.rb +8 -0
- data/app/lib/atomic_lti/exceptions.rb +4 -0
- data/app/lib/atomic_lti/services/platform_notifications.rb +100 -0
- data/app/models/atomic_lti/context.rb +2 -1
- data/app/models/atomic_lti/deployment.rb +3 -1
- data/app/models/atomic_lti/install.rb +2 -3
- data/app/models/atomic_lti/platform.rb +4 -0
- data/lib/atomic_lti/version.rb +1 -1
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aac6096f2a93856e8186ad6dfa5cd43856a0ac2ffd16d2e9a1353d404742c69a
|
4
|
+
data.tar.gz: 64b8ffeeb7d74e020ae63fa8c42d2401e4632a4fb4a311fdf06cb177069e801a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1ad65c60634d64306683166dffbf5cb2fb72a79b5db177c76c720cfa98859e3b5db6e30951f92a198be6c232f0a2ffc84be8449e38bb55279cd90d62de54ee3
|
7
|
+
data.tar.gz: 8b1df93f772d15b9e20482928bdcd8f766fe00769818c1217bf98e3a3ff111a80793ad2ee2d005992175bc147ccad024fc2fe3f453f49ceb0b85589c3009d336
|
@@ -31,6 +31,12 @@ module AtomicLti
|
|
31
31
|
|
32
32
|
NAMES_AND_ROLES_SERVICE_VERSIONS = ["2.0"].freeze
|
33
33
|
|
34
|
+
PLATFORM_NOTIFICATION_SERVICE_CLAIM = "https://purl.imsglobal.org/spec/lti/claim/platformnotificationservice".freeze
|
35
|
+
PLATFORM_NOTIFICATION_SERVICE_VERSIONS = ["1.0".freeze].freeze
|
36
|
+
NOTICE_TYPE_CLAIM = "https://purl.imsglobal.org/spec/lti/claim/notice_type".freeze
|
37
|
+
PLATFORM_NOTIFICATION_CONTEXT_COPY_NOTICE = "LtiContextCopyNotice".freeze
|
38
|
+
PLATFORM_NOTIFICATION_CONTEXT_COPY_ORIGINS_CLAIM = "https://purl.imsglobal.org/spec/lti/claim/origin_contexts".freeze
|
39
|
+
|
34
40
|
CALIPER_CLAIM = "https://purl.imsglobal.org/spec/lti-ces/claim/caliper-endpoint-service".freeze
|
35
41
|
|
36
42
|
TOOL_LAUNCH_CALIPER_CONTEXT = "http://purl.imsglobal.org/ctx/caliper/v1p1/ToolLaunchProfile-extension".freeze
|
@@ -43,6 +49,7 @@ module AtomicLti
|
|
43
49
|
AGS_SCOPE_SCORE = "https://purl.imsglobal.org/spec/lti-ags/scope/score".freeze
|
44
50
|
NAMES_AND_ROLES_SCOPE = "https://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly".freeze
|
45
51
|
CALIPER_SCOPE = "https://purl.imsglobal.org/spec/lti-ces/v1p0/scope/send".freeze
|
52
|
+
PNS_SCOPE_NOTICEHANDLERS = "https://purl.imsglobal.org/spec/lti/scope/noticehandlers".freeze
|
46
53
|
|
47
54
|
STUDENT_SCOPE = "http://purl.imsglobal.org/vocab/lis/v2/institution/person#Student".freeze
|
48
55
|
INSTRUCTOR_SCOPE = "http://purl.imsglobal.org/vocab/lis/v2/institution/person#Instructor".freeze
|
@@ -167,6 +174,7 @@ module AtomicLti
|
|
167
174
|
|
168
175
|
OBSERVER_ROLES = [
|
169
176
|
MENTOR_INSTITUTION_ROLE,
|
177
|
+
MENTOR_CONTEXT_ROLE,
|
170
178
|
# NON_CREDIT_LEARNER,
|
171
179
|
].freeze
|
172
180
|
|
@@ -14,6 +14,8 @@ module AtomicLti
|
|
14
14
|
class NamesAndRolesError < AtomicLtiException
|
15
15
|
end
|
16
16
|
|
17
|
+
class PlatformNotificationsError < AtomicLtiException; end
|
18
|
+
|
17
19
|
class ScoreError < AtomicLtiException
|
18
20
|
end
|
19
21
|
|
@@ -98,5 +100,7 @@ module AtomicLti
|
|
98
100
|
|
99
101
|
class PaginationLimitExceeded < AtomicLtiException
|
100
102
|
end
|
103
|
+
|
104
|
+
class InvalidPlatformNotification < AtomicLtiException; end
|
101
105
|
end
|
102
106
|
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module AtomicLti
|
2
|
+
module Services
|
3
|
+
class PlatformNotifications < AtomicLti::Services::Base
|
4
|
+
def scopes
|
5
|
+
[AtomicLti::Definitions::PNS_SCOPE_NOTICEHANDLERS]
|
6
|
+
end
|
7
|
+
|
8
|
+
def endpoint
|
9
|
+
url = @id_token_decoded.dig(AtomicLti::Definitions::PLATFORM_NOTIFICATION_SERVICE_CLAIM, "platform_notification_service_url")
|
10
|
+
raise AtomicLti::Exceptions::PlatformNotificationsError, "Unable to access platform notifications" if url.blank?
|
11
|
+
|
12
|
+
url
|
13
|
+
end
|
14
|
+
|
15
|
+
def url_for(query = nil)
|
16
|
+
url = endpoint.dup
|
17
|
+
url << "?#{query}" if query.present?
|
18
|
+
url
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.enabled?(id_token_decoded)
|
22
|
+
return false unless id_token_decoded&.dig(AtomicLti::Definitions::PLATFORM_NOTIFICATION_SERVICE_CLAIM)
|
23
|
+
|
24
|
+
(AtomicLti::Definitions::PLATFORM_NOTIFICATION_SERVICE_VERSIONS &
|
25
|
+
(id_token_decoded.dig(AtomicLti::Definitions::PLATFORM_NOTIFICATION_SERVICE_CLAIM, "service_versions") || [])).present?
|
26
|
+
end
|
27
|
+
|
28
|
+
def valid?
|
29
|
+
self.class.enabled?(@id_token_decoded)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Get platform notifications
|
33
|
+
def get(query: {})
|
34
|
+
uri = Addressable::URI.parse(endpoint)
|
35
|
+
uri.query_values = (uri.query_values || {}).merge(query)
|
36
|
+
url = uri.to_str
|
37
|
+
|
38
|
+
response, = service_get(url, headers:)
|
39
|
+
response.parsed_response
|
40
|
+
end
|
41
|
+
|
42
|
+
def update(notice_type, handler)
|
43
|
+
content_type = { "Content-Type" => "application/json" }
|
44
|
+
payload = { notice_type:, handler: }
|
45
|
+
response, = service_put(endpoint, body: JSON.dump(payload), headers: headers(content_type))
|
46
|
+
response
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.validate_notification(notification)
|
50
|
+
decoded_token = AtomicLti::Authorization.validate_token(notification)
|
51
|
+
if decoded_token.blank?
|
52
|
+
raise AtomicLti::Exceptions::InvalidPlatformNotification
|
53
|
+
end
|
54
|
+
|
55
|
+
errors = []
|
56
|
+
|
57
|
+
if decoded_token["iss"].blank?
|
58
|
+
errors.push("LTI token is missing required field iss")
|
59
|
+
end
|
60
|
+
|
61
|
+
if decoded_token["aud"].blank?
|
62
|
+
errors.push("LTI token is missing required field aud")
|
63
|
+
end
|
64
|
+
|
65
|
+
if decoded_token["aud"].is_a?(Array) && decoded_token["aud"].length > 1
|
66
|
+
# OpenID Connect spec specifies the AZP should exist and be an AUD
|
67
|
+
if decoded_token["azp"].blank?
|
68
|
+
errors.push("LTI token has multiple aud and is missing required field azp")
|
69
|
+
elsif decoded_token["aud"].exclude?(decoded_token["azp"])
|
70
|
+
errors.push("LTI token azp is not one of the aud's")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
if decoded_token[AtomicLti::Definitions::DEPLOYMENT_ID].blank?
|
75
|
+
errors.push(
|
76
|
+
"LTI token is missing required field #{AtomicLti::Definitions::DEPLOYMENT_ID}",
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
if decoded_token[AtomicLti::Definitions::NOTICE_TYPE_CLAIM].blank?
|
81
|
+
errors.push(
|
82
|
+
"LTI token is missing required claim #{AtomicLti::Definitions::NOTICE_TYPE_CLAIM}",
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
86
|
+
if errors.present?
|
87
|
+
raise Exceptions::InvalidPlatformNotification.new(errors.join(" "))
|
88
|
+
end
|
89
|
+
|
90
|
+
if decoded_token[AtomicLti::Definitions::LTI_VERSION].blank?
|
91
|
+
raise AtomicLti::Exceptions::NoLTIVersion
|
92
|
+
end
|
93
|
+
|
94
|
+
raise AtomicLti::Exceptions::InvalidLTIVersion unless AtomicLti::Lti.valid_version?(decoded_token)
|
95
|
+
|
96
|
+
decoded_token
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
module AtomicLti
|
2
2
|
class Context < ApplicationRecord
|
3
|
+
query_constraints :context_id, :deployment_id, :iss
|
3
4
|
belongs_to :platform, primary_key: :iss, foreign_key: :iss
|
4
|
-
|
5
|
+
belongs_to :deployment, query_constraints: [:iss, :deployment_id], optional: true
|
5
6
|
|
6
7
|
validates :context_id, presence: true
|
7
8
|
validates :deployment_id, presence: true
|
@@ -1,7 +1,9 @@
|
|
1
1
|
module AtomicLti
|
2
2
|
class Deployment < ApplicationRecord
|
3
|
+
query_constraints :deployment_id, :iss
|
3
4
|
belongs_to :platform, primary_key: :iss, foreign_key: :iss
|
4
|
-
belongs_to :install,
|
5
|
+
belongs_to :install, query_constraints: [:client_id, :iss], optional: true
|
6
|
+
has_many :contexts, query_constraints: [:deployment_id, :iss]
|
5
7
|
|
6
8
|
# we won't have platform_guid during dynamic registration
|
7
9
|
# belongs_to :platform_instance, primary_key: :guid, foreign_key: :platform_guid, optional: true
|
@@ -1,11 +1,10 @@
|
|
1
1
|
module AtomicLti
|
2
2
|
class Install < ApplicationRecord
|
3
|
+
query_constraints :client_id, :iss
|
3
4
|
belongs_to :platform, primary_key: :iss, foreign_key: :iss
|
5
|
+
has_many :deployments, query_constraints: [:client_id, :iss]
|
4
6
|
|
5
7
|
validates :client_id, presence: true
|
6
8
|
validates :iss, presence: true
|
7
|
-
def deployments
|
8
|
-
AtomicLti::Deployment.where("iss = ? AND client_id = ?", iss, client_id)
|
9
|
-
end
|
10
9
|
end
|
11
10
|
end
|
@@ -1,5 +1,9 @@
|
|
1
1
|
module AtomicLti
|
2
2
|
class Platform < ApplicationRecord
|
3
|
+
query_constraints :iss
|
3
4
|
validates :iss, presence: true
|
5
|
+
has_many :platform_instances, foreign_key: :iss, primary_key: :iss
|
6
|
+
has_many :deployments, foreign_key: :iss, primary_key: :iss
|
7
|
+
has_many :contexts, foreign_key: :iss, primary_key: :iss
|
4
8
|
end
|
5
9
|
end
|
data/lib/atomic_lti/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: atomic_lti
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Petro
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2024-
|
13
|
+
date: 2024-10-03 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: pg
|
@@ -32,14 +32,14 @@ dependencies:
|
|
32
32
|
requirements:
|
33
33
|
- - "~>"
|
34
34
|
- !ruby/object:Gem::Version
|
35
|
-
version: '7.
|
35
|
+
version: '7.1'
|
36
36
|
type: :runtime
|
37
37
|
prerelease: false
|
38
38
|
version_requirements: !ruby/object:Gem::Requirement
|
39
39
|
requirements:
|
40
40
|
- - "~>"
|
41
41
|
- !ruby/object:Gem::Version
|
42
|
-
version: '7.
|
42
|
+
version: '7.1'
|
43
43
|
description: AtomicLti implements the LTI Advantage specification. This gem does contain
|
44
44
|
source code specific to other Atomic Jolt products
|
45
45
|
email:
|
@@ -77,6 +77,7 @@ files:
|
|
77
77
|
- app/lib/atomic_lti/services/base.rb
|
78
78
|
- app/lib/atomic_lti/services/line_items.rb
|
79
79
|
- app/lib/atomic_lti/services/names_and_roles.rb
|
80
|
+
- app/lib/atomic_lti/services/platform_notifications.rb
|
80
81
|
- app/lib/atomic_lti/services/results.rb
|
81
82
|
- app/lib/atomic_lti/services/score.rb
|
82
83
|
- app/lib/atomic_lti/services/score_canvas.rb
|
@@ -134,7 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
134
135
|
- !ruby/object:Gem::Version
|
135
136
|
version: '0'
|
136
137
|
requirements: []
|
137
|
-
rubygems_version: 3.4.
|
138
|
+
rubygems_version: 3.4.19
|
138
139
|
signing_key:
|
139
140
|
specification_version: 4
|
140
141
|
summary: AtomicLti implements the LTI Advantage specification.
|