atomic_lti 1.8.5 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ba4c43d0a67a951d858b5610d4b7a26a4b0898c863113c515d554f961d172131
4
- data.tar.gz: cdf108a92d6e62a73b0294a38badb1ebd519de71520bf4f2ef5bb027de4fab21
3
+ metadata.gz: 51f6a8b44052a79ca2848b1b32f9789ce2033c79cc168b6b43467429f463efe7
4
+ data.tar.gz: 9fc1c514d21fcec53c9b74d7b7a673d774052539f5313480ca12d0b3ce886643
5
5
  SHA512:
6
- metadata.gz: 33fe99f4b5131ed6df49d4d36ee87058416c100f7bfc3a0fc06b163a6de8c3d30336a8260347d4d4c9db4b028e4e83b000582f94797254b932ff6433ea998ea4
7
- data.tar.gz: 9b0812047d37e222c6b49e7abffe288afe7c0836c2eb15c970d97ae50d3446c348f5b64cdbbc6eb70848d378a40893be518f084b9d97e85cb8d455080b04a63f
6
+ metadata.gz: 2d87e7f1666d050da989f2d10b017fbc90bc4f8e213f6082e1b45c96b907636ebfd4be09085bcc73bc251610e1b0e727b0204250906f1ef7a351cab50db502d8
7
+ data.tar.gz: d0cba43fba4d45d186dd45a804531409cf4d0b9a2453bb68e8ea9207048727c1e943147845034aa952991ce360a6380f44b90e479fd1317466fe9ac1d6eb7a44
@@ -11,6 +11,7 @@ module AtomicLti
11
11
  TOOL_PLATFORM_CLAIM = "https://purl.imsglobal.org/spec/lti/claim/tool_platform".freeze
12
12
  AGS_CLAIM = "https://purl.imsglobal.org/spec/lti-ags/claim/endpoint".freeze
13
13
  BASIC_OUTCOME_CLAIM = "https://purl.imsglobal.org/spec/lti-bo/claim/basicoutcome".freeze
14
+ FOR_USER_CLAIM = "https://purl.imsglobal.org/spec/lti/claim/for_user".freeze
14
15
 
15
16
  MENTOR_CLAIM = "https://purl.imsglobal.org/spec/lti/claim/role_scope_mentor".freeze
16
17
  ROLES_CLAIM = "https://purl.imsglobal.org/spec/lti/claim/roles".freeze
@@ -30,6 +31,12 @@ module AtomicLti
30
31
 
31
32
  NAMES_AND_ROLES_SERVICE_VERSIONS = ["2.0"].freeze
32
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
+
33
40
  CALIPER_CLAIM = "https://purl.imsglobal.org/spec/lti-ces/claim/caliper-endpoint-service".freeze
34
41
 
35
42
  TOOL_LAUNCH_CALIPER_CONTEXT = "http://purl.imsglobal.org/ctx/caliper/v1p1/ToolLaunchProfile-extension".freeze
@@ -42,6 +49,7 @@ module AtomicLti
42
49
  AGS_SCOPE_SCORE = "https://purl.imsglobal.org/spec/lti-ags/scope/score".freeze
43
50
  NAMES_AND_ROLES_SCOPE = "https://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly".freeze
44
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
45
53
 
46
54
  STUDENT_SCOPE = "http://purl.imsglobal.org/vocab/lis/v2/institution/person#Student".freeze
47
55
  INSTRUCTOR_SCOPE = "http://purl.imsglobal.org/vocab/lis/v2/institution/person#Instructor".freeze
@@ -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
@@ -23,7 +23,8 @@ module AtomicLti
23
23
  resource_id: nil,
24
24
  tag: nil,
25
25
  resource_link_id: nil,
26
- external_tool_url: nil
26
+ external_tool_url: nil,
27
+ submission_review: nil
27
28
  )
28
29
  attrs = {
29
30
  scoreMaximum: max_score,
@@ -40,6 +41,9 @@ module AtomicLti
40
41
  external_tool_url: external_tool_url,
41
42
  }
42
43
  end
44
+ if submission_review
45
+ attrs[:submissionReview] = submission_review
46
+ end
43
47
  attrs
44
48
  end
45
49
 
@@ -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,3 +1,3 @@
1
1
  module AtomicLti
2
- VERSION = "1.8.5".freeze
2
+ VERSION = "1.10.0".freeze
3
3
  end
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: 1.8.5
4
+ version: 1.10.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-06-18 00:00:00.000000000 Z
13
+ date: 2024-07-05 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: pg
@@ -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.19
138
+ rubygems_version: 3.4.10
138
139
  signing_key:
139
140
  specification_version: 4
140
141
  summary: AtomicLti implements the LTI Advantage specification.