atomic_lti 1.9.0 → 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: e8b38d2b638688e1308e60601a9586a6d4f44c973bfe82aa3d94c7941f1f3f79
4
- data.tar.gz: b280f79bab178696f88dfb2bb34af9cf1a4cf8b0c2835a2c8040890d7c994b2b
3
+ metadata.gz: 51f6a8b44052a79ca2848b1b32f9789ce2033c79cc168b6b43467429f463efe7
4
+ data.tar.gz: 9fc1c514d21fcec53c9b74d7b7a673d774052539f5313480ca12d0b3ce886643
5
5
  SHA512:
6
- metadata.gz: 8a117f214fe877902d629474a237edcae89f7635b5028534010e64925a0c845b20066cca8694fa8350e3018f1e1713ec1f25667801216230a1a2f755d6639a8c
7
- data.tar.gz: a20d30b4094c1344fb45fbcb440319182626b08e0ef968dcf80e0a610184c35a71f6f5690322b7f168b39925929ce8f7da47652e5b588fee697fdf7c5c43e2b4
6
+ metadata.gz: 2d87e7f1666d050da989f2d10b017fbc90bc4f8e213f6082e1b45c96b907636ebfd4be09085bcc73bc251610e1b0e727b0204250906f1ef7a351cab50db502d8
7
+ data.tar.gz: d0cba43fba4d45d186dd45a804531409cf4d0b9a2453bb68e8ea9207048727c1e943147845034aa952991ce360a6380f44b90e479fd1317466fe9ac1d6eb7a44
@@ -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
@@ -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,3 +1,3 @@
1
1
  module AtomicLti
2
- VERSION = "1.9.0".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.9.0
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-07-02 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