atomic_lti 1.9.0 → 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
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