ims-lti 2.1.2 → 2.3.3
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 +5 -5
- data/README.md +1 -1
- data/lib/ims/lti/errors/authentication_failed_error.rb +11 -0
- data/lib/ims/lti/errors.rb +1 -0
- data/lib/ims/lti/models/lti_model.rb +9 -2
- data/lib/ims/lti/models/membership_service/lis_person.rb +2 -1
- data/lib/ims/lti/models/messages/basic_lti_launch_request.rb +2 -1
- data/lib/ims/lti/models/messages/message.rb +73 -13
- data/lib/ims/lti/models/messages/registration_request.rb +3 -2
- data/lib/ims/lti/models/messages/{tool_proxy_reregistration_request.rb → tool_proxy_update_request.rb} +2 -2
- data/lib/ims/lti/models/messages.rb +1 -1
- data/lib/ims/lti/models/tool_consumer_profile.rb +7 -1
- data/lib/ims/lti/serializers/membership_service/lis_person_serializer.rb +1 -0
- data/lib/ims/lti/services/authentication_service.rb +67 -0
- data/lib/ims/lti/services/message_authenticator.rb +17 -5
- data/lib/ims/lti/services/oauth2_client.rb +18 -0
- data/lib/ims/lti/services/tool_consumer_profile_service.rb +16 -0
- data/lib/ims/lti/services/tool_proxy_registration_service.rb +1 -1
- data/lib/ims/lti/services/tool_proxy_validator.rb +23 -7
- data/lib/ims/lti/services.rb +4 -1
- data/lib/ims/lti/version.rb +1 -1
- data/lib/ims/lti.rb +8 -6
- metadata +97 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2471bb32ba4e415a4134bca8b7fd89e395e6d2fcea9cf694ae3a094599bed683
|
4
|
+
data.tar.gz: 9978a2ca0c9f89cd4fdd03217cb0f13bde37098f45c319e2d813c3fad6102b31
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b96119ce642dde977d998fb405576b21ca755311b841b49d9a985f556bf0fdfbbab60798027060f641463edd1329505a4a53ad916f75a9ce344d9c4723d1ab20
|
7
|
+
data.tar.gz: 5e4fd658b0658adcdbcca33feacf429f04e8a0f354eb6db1d031cfa4b0655f9a8478131919f215d833d872478462677bfcd04ed6ee298a00ed85bcb921f3313c
|
data/README.md
CHANGED
@@ -37,7 +37,7 @@ return false unless authenticator.valid_signature?
|
|
37
37
|
# check if `params['oauth_nonce']` has already been used
|
38
38
|
|
39
39
|
#check if the message is too old
|
40
|
-
return false if DateTime.strptime(request.request_parameters['oauth_timestamp'],'%s')
|
40
|
+
return false if DateTime.strptime(request.request_parameters['oauth_timestamp'],'%s') < 5.minutes.ago
|
41
41
|
|
42
42
|
```
|
43
43
|
|
data/lib/ims/lti/errors.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
1
3
|
module IMS::LTI::Models
|
2
4
|
class LTIModel
|
3
5
|
LTI_VERSION_2P0 = 'LTI-2p0'.freeze
|
@@ -90,8 +92,13 @@ module IMS::LTI::Models
|
|
90
92
|
end
|
91
93
|
|
92
94
|
def from_json(json)
|
93
|
-
|
94
|
-
|
95
|
+
json = json.to_json unless json.is_a?(String)
|
96
|
+
begin
|
97
|
+
data = JSON.parse(json)
|
98
|
+
rescue
|
99
|
+
data = JSON.parse(URI::DEFAULT_PARSER.unescape(json))
|
100
|
+
end
|
101
|
+
|
95
102
|
if data.is_a? Array
|
96
103
|
data.map { |hash| self.class.from_json(hash.to_json) }
|
97
104
|
else
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module IMS::LTI::Models::MembershipService
|
2
2
|
class LISPerson < Person
|
3
|
-
attr_reader :email, :result_sourced_id, :sourced_id, :user_id
|
3
|
+
attr_reader :email, :result_sourced_id, :sourced_id, :user_id, :sis_id
|
4
4
|
|
5
5
|
def initialize(opts={})
|
6
6
|
super(opts)
|
@@ -8,6 +8,7 @@ module IMS::LTI::Models::MembershipService
|
|
8
8
|
@result_sourced_id = opts[:result_sourced_id]
|
9
9
|
@sourced_id = opts[:sourced_id]
|
10
10
|
@user_id = opts[:user_id]
|
11
|
+
@sis_id = opts[:sis_id]
|
11
12
|
end
|
12
13
|
end
|
13
14
|
end
|
@@ -3,7 +3,8 @@ module IMS::LTI::Models::Messages
|
|
3
3
|
|
4
4
|
add_required_params :resource_link_id
|
5
5
|
add_recommended_params :context_id, :launch_presentation_return_url, :tool_consumer_instance_guid
|
6
|
-
add_optional_params :context_type, :role_scope_mentor, :user_image
|
6
|
+
add_optional_params :context_type, :role_scope_mentor, :user_image,
|
7
|
+
:lis_outcome_service_url, :lis_person_sourced_id, :lis_result_sourcedid
|
7
8
|
add_deprecated_params :context_title, :context_label, :resource_link_title, :resource_link_description,
|
8
9
|
:lis_person_name_given, :lis_person_name_family, :lis_person_name_full,
|
9
10
|
:lis_person_contact_email_primary, :user_image, :lis_person_sourcedid,
|
@@ -40,13 +40,26 @@ module IMS::LTI::Models::Messages
|
|
40
40
|
def inherited(klass)
|
41
41
|
@descendants ||= Set.new
|
42
42
|
@descendants << klass
|
43
|
-
superclass.inherited(klass) unless(self == Message)
|
43
|
+
superclass.inherited(klass) unless (self == Message)
|
44
44
|
end
|
45
45
|
|
46
46
|
def descendants
|
47
47
|
@descendants || Set.new
|
48
48
|
end
|
49
49
|
|
50
|
+
# For signature generation -- see usage in signed_post_params
|
51
|
+
def convert_param_values_to_crlf_endings(hash)
|
52
|
+
hash.transform_values do |val|
|
53
|
+
if val.is_a?(String)
|
54
|
+
# Convert to all newlines first, for consistency, just in case there
|
55
|
+
# is some weird mix of newlines & carriage returns in input
|
56
|
+
val.encode(universal_newline: true).encode(crlf_newline: true)
|
57
|
+
else
|
58
|
+
val
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
50
63
|
private
|
51
64
|
|
52
65
|
def add_params(instance_variable, param, *params)
|
@@ -81,20 +94,34 @@ module IMS::LTI::Models::Messages
|
|
81
94
|
OAUTH_KEYS = :oauth_callback, :oauth_consumer_key, :oauth_nonce, :oauth_signature, :oauth_signature_method,
|
82
95
|
:oauth_timestamp, :oauth_token, :oauth_verifier, :oauth_version
|
83
96
|
|
84
|
-
attr_accessor :launch_url, *OAUTH_KEYS
|
97
|
+
attr_accessor :launch_url, :jwt, *OAUTH_KEYS
|
85
98
|
attr_reader :unknown_params, :custom_params, :ext_params
|
86
99
|
|
87
100
|
add_required_params :lti_message_type, :lti_version
|
88
101
|
|
89
|
-
def self.generate(
|
90
|
-
|
91
|
-
klass
|
102
|
+
def self.generate(launch_params)
|
103
|
+
params = launch_params.key?('jwt') ? parse_jwt(jwt: launch_params['jwt']) : launch_params
|
104
|
+
klass = self.descendants.select {|d| d::MESSAGE_TYPE == params['lti_message_type']}.first
|
105
|
+
message = klass ? klass.new(params) : Message.new(params)
|
106
|
+
message.jwt = launch_params['jwt'] if launch_params.key?('jwt')
|
107
|
+
message
|
92
108
|
end
|
93
109
|
|
94
|
-
def
|
110
|
+
def self.parse_jwt(jwt:)
|
111
|
+
decoded_jwt = JSON::JWT.decode(jwt, :skip_verification)
|
112
|
+
params = decoded_jwt['org.imsglobal.lti.message'] || {}
|
113
|
+
custom = params.delete(:custom)
|
114
|
+
custom.each {|k,v| params["custom_#{k}"] = v }
|
115
|
+
params['consumer_key'] = decoded_jwt[:sub]
|
116
|
+
ext = params.delete(:ext)
|
117
|
+
ext.each {|k,v| params["ext_#{k}"] = v }
|
118
|
+
params
|
119
|
+
end
|
120
|
+
|
121
|
+
def initialize(attrs = {}, custom_params = {}, ext_params = {})
|
95
122
|
|
96
|
-
@custom_params =
|
97
|
-
@ext_params =
|
123
|
+
@custom_params = custom_params
|
124
|
+
@ext_params = ext_params
|
98
125
|
@unknown_params = {}
|
99
126
|
|
100
127
|
attrs.each do |k, v|
|
@@ -114,20 +141,32 @@ module IMS::LTI::Models::Messages
|
|
114
141
|
end
|
115
142
|
|
116
143
|
def add_custom_params(params)
|
117
|
-
params.each {
|
144
|
+
params.each {|k, v| k.to_s.start_with?('custom_') ? @custom_params[k.to_s] = v : @custom_params["custom_#{k.to_s}"] = v}
|
118
145
|
end
|
119
146
|
|
120
147
|
def get_custom_params
|
121
|
-
@custom_params.inject({}) {
|
148
|
+
@custom_params.inject({}) {|hash, (k, v)| hash[k.gsub(/\Acustom_/, '')] = v; hash}
|
149
|
+
end
|
150
|
+
|
151
|
+
def get_ext_params
|
152
|
+
@ext_params.inject({}) {|hash, (k, v)| hash[k.gsub(/\Aext_/, '')] = v; hash}
|
122
153
|
end
|
123
154
|
|
124
155
|
def post_params
|
125
156
|
unknown_params.merge(@custom_params).merge(@ext_params).merge(parameters)
|
126
157
|
end
|
127
158
|
|
159
|
+
def jwt_params(private_key:, originating_domain:, algorithm: :HS256)
|
160
|
+
{ 'jwt' => to_jwt(private_key: private_key, originating_domain: originating_domain, algorithm: algorithm) }
|
161
|
+
end
|
162
|
+
|
128
163
|
def signed_post_params(secret)
|
129
|
-
|
130
|
-
|
164
|
+
# The params will be used in an HTML form, and browsers will always use
|
165
|
+
# newlines+carriage return line endings for submitted form data. The
|
166
|
+
# signature needs to match, and signature generation does not add carriage
|
167
|
+
# returns, so we ensure CRLF endings here.
|
168
|
+
message_params = self.class.convert_param_values_to_crlf_endings(oauth_params.merge(post_params))
|
169
|
+
@message_authenticator = IMS::LTI::Services::MessageAuthenticator.new(launch_url, message_params, secret)
|
131
170
|
@message_authenticator.signed_params
|
132
171
|
end
|
133
172
|
|
@@ -165,6 +204,27 @@ module IMS::LTI::Models::Messages
|
|
165
204
|
end
|
166
205
|
end
|
167
206
|
|
207
|
+
def to_jwt(private_key:, originating_domain:, algorithm: :HS256)
|
208
|
+
now = Time.now
|
209
|
+
exp = now + 60 * 5
|
210
|
+
ims = unknown_params.merge(parameters)
|
211
|
+
ims[:custom] = get_custom_params
|
212
|
+
ims[:ext] = get_ext_params
|
213
|
+
claim = {
|
214
|
+
iss: originating_domain,
|
215
|
+
sub: consumer_key,
|
216
|
+
aud: launch_url,
|
217
|
+
iat: now,
|
218
|
+
exp: exp,
|
219
|
+
jti: SecureRandom.uuid,
|
220
|
+
"org.imsglobal.lti.message" => ims
|
221
|
+
}
|
222
|
+
jwt = JSON::JWT.new(claim).sign(private_key, algorithm)
|
223
|
+
jwt.to_s
|
224
|
+
end
|
225
|
+
|
226
|
+
alias_attribute :consumer_key, :oauth_consumer_key
|
227
|
+
|
168
228
|
private
|
169
229
|
|
170
230
|
def collect_attributes(attributes)
|
@@ -176,4 +236,4 @@ module IMS::LTI::Models::Messages
|
|
176
236
|
end
|
177
237
|
|
178
238
|
end
|
179
|
-
end
|
239
|
+
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
module IMS::LTI::Models::Messages
|
2
2
|
class RegistrationRequest < RequestMessage
|
3
3
|
|
4
|
-
add_required_params :reg_key, :reg_password, :tc_profile_url, :launch_presentation_return_url
|
4
|
+
add_required_params :reg_key, :reg_password, :tc_profile_url, :launch_presentation_return_url, :tool_proxy_guid,
|
5
|
+
:tool_proxy_url
|
5
6
|
|
6
7
|
|
7
8
|
MESSAGE_TYPE = 'ToolProxyRegistrationRequest'
|
@@ -16,4 +17,4 @@ module IMS::LTI::Models::Messages
|
|
16
17
|
end
|
17
18
|
|
18
19
|
end
|
19
|
-
end
|
20
|
+
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
module IMS::LTI::Models::Messages
|
2
|
-
class
|
2
|
+
class ToolProxyUpdateRequest < RequestMessage
|
3
3
|
|
4
4
|
add_required_params :tc_profile_url, :launch_presentation_return_url
|
5
5
|
|
6
6
|
|
7
|
-
MESSAGE_TYPE = '
|
7
|
+
MESSAGE_TYPE = 'ToolProxyUpdateRequest'
|
8
8
|
|
9
9
|
def initialize(attrs = {})
|
10
10
|
super(attrs)
|
@@ -6,6 +6,6 @@ module IMS::LTI::Models
|
|
6
6
|
require_relative 'messages/basic_lti_launch_request'
|
7
7
|
require_relative 'messages/content_item_selection_request'
|
8
8
|
require_relative 'messages/content_item_selection'
|
9
|
-
require_relative 'messages/
|
9
|
+
require_relative 'messages/tool_proxy_update_request'
|
10
10
|
end
|
11
11
|
end
|
@@ -11,6 +11,7 @@ module IMS::LTI::Models
|
|
11
11
|
add_attribute :id, json_key:'@id'
|
12
12
|
add_attribute :type, json_key:'@type'
|
13
13
|
add_attribute :context, json_key:'@context'
|
14
|
+
add_attribute :security_profile, relation: 'IMS::LTI::Models::SecurityProfile'
|
14
15
|
add_attribute :product_instance, relation:'IMS::LTI::Models::ProductInstance'
|
15
16
|
add_attribute :service_offered, relation: 'IMS::LTI::Models::RestService'
|
16
17
|
|
@@ -33,7 +34,12 @@ module IMS::LTI::Models
|
|
33
34
|
end
|
34
35
|
|
35
36
|
def reregistration_capable?
|
36
|
-
@capability_offered.include?(Messages::
|
37
|
+
@capability_offered.include?(Messages::ToolProxyUpdateRequest::MESSAGE_TYPE)
|
37
38
|
end
|
39
|
+
|
40
|
+
def security_profile_by_name(security_profile_name:)
|
41
|
+
security_profiles.find { |sp| sp.security_profile_name == security_profile_name}
|
42
|
+
end
|
43
|
+
|
38
44
|
end
|
39
45
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module IMS::LTI::Services
|
2
|
+
class AuthenticationService
|
3
|
+
|
4
|
+
attr_accessor :connection, :iss, :aud, :sub, :secret, :grant_type,
|
5
|
+
:additional_claims, :additional_params
|
6
|
+
attr_writer :secret
|
7
|
+
|
8
|
+
def initialize(iss:, aud:, sub:, secret:)
|
9
|
+
@iss = iss
|
10
|
+
@aud = aud
|
11
|
+
@sub = sub
|
12
|
+
@secret = secret
|
13
|
+
@additional_claims = {}
|
14
|
+
@additional_params = {}
|
15
|
+
@grant_type = 'urn:ietf:params:oauth:grant-type:jwt-bearer'
|
16
|
+
end
|
17
|
+
|
18
|
+
def connection
|
19
|
+
@connection ||= Faraday.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def access_token
|
23
|
+
access_token_request['access_token']
|
24
|
+
end
|
25
|
+
|
26
|
+
def expiration
|
27
|
+
expires_in = access_token_request['expires_in'].to_i
|
28
|
+
@_response_time + expires_in
|
29
|
+
end
|
30
|
+
|
31
|
+
def expired?
|
32
|
+
expiration < Time.now
|
33
|
+
end
|
34
|
+
|
35
|
+
def invalidate!
|
36
|
+
@_access_token_request = nil
|
37
|
+
@_response_time = nil
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def access_token_request
|
43
|
+
@_access_token_request ||= begin
|
44
|
+
assertion = JSON::JWT.new(
|
45
|
+
iss: iss,
|
46
|
+
sub: sub,
|
47
|
+
aud: aud.to_s,
|
48
|
+
iat: Time.now.to_i,
|
49
|
+
exp: 1.minute.from_now,
|
50
|
+
jti: SecureRandom.uuid
|
51
|
+
)
|
52
|
+
assertion.merge!(@additional_claims)
|
53
|
+
assertion = assertion.sign(@secret, :HS256).to_s
|
54
|
+
body = {
|
55
|
+
grant_type: grant_type,
|
56
|
+
assertion: assertion
|
57
|
+
}
|
58
|
+
body.merge!(@additional_params)
|
59
|
+
response = connection.post(aud, body)
|
60
|
+
raise IMS::LTI::Errors::AuthenticationFailedError.new(response: response) unless response.success?
|
61
|
+
@_response_time = Time.now
|
62
|
+
response.body
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -14,7 +14,7 @@ module IMS::LTI::Services
|
|
14
14
|
|
15
15
|
|
16
16
|
def valid_signature?
|
17
|
-
|
17
|
+
message.jwt ? valid_jwt? : simple_oauth_header.valid?(signature: signature)
|
18
18
|
end
|
19
19
|
|
20
20
|
def message
|
@@ -33,8 +33,7 @@ module IMS::LTI::Services
|
|
33
33
|
@options.merge(
|
34
34
|
{
|
35
35
|
consumer_key: consumer_key,
|
36
|
-
consumer_secret: @secret
|
37
|
-
callback: 'about:blank'
|
36
|
+
consumer_secret: @secret
|
38
37
|
}
|
39
38
|
)
|
40
39
|
)
|
@@ -47,11 +46,24 @@ module IMS::LTI::Services
|
|
47
46
|
end
|
48
47
|
|
49
48
|
def signed_params
|
50
|
-
simple_oauth_header.signed_attributes.merge(
|
49
|
+
simple_oauth_header.signed_attributes.merge(@parsed_params)
|
51
50
|
end
|
52
51
|
|
53
52
|
|
54
53
|
private
|
54
|
+
|
55
|
+
def valid_jwt?
|
56
|
+
begin
|
57
|
+
jwt = JSON::JWT.decode(message.jwt, @secret)
|
58
|
+
aud1 = Addressable::URI.parse(jwt['aud'])
|
59
|
+
aud2 = Addressable::URI.parse(launch_url)
|
60
|
+
[aud1, aud2].each{ |aud| aud.fragment = '' }
|
61
|
+
aud1.normalize == aud2.normalize
|
62
|
+
rescue JSON::JWS::VerificationFailed
|
63
|
+
false
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
55
67
|
def parse_params(params)
|
56
68
|
params.inject([{}, {}]) do |array, (k, v)|
|
57
69
|
attr = k.to_s.sub('oauth_', '').to_sym
|
@@ -65,4 +77,4 @@ module IMS::LTI::Services
|
|
65
77
|
end
|
66
78
|
|
67
79
|
end
|
68
|
-
end
|
80
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module IMS::LTI::Services
|
2
|
+
|
3
|
+
class OAuth2Client
|
4
|
+
attr_accessor :token, :base_url
|
5
|
+
attr_writer :connection
|
6
|
+
|
7
|
+
def initialize(token:, base_url: nil)
|
8
|
+
@base_url = base_url
|
9
|
+
@token = token
|
10
|
+
end
|
11
|
+
|
12
|
+
def connection
|
13
|
+
@connection ||= Faraday.new base_url do |conn|
|
14
|
+
conn.authorization :Bearer, token
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module IMS::LTI::Services
|
2
|
+
class ToolConsumerProfileService
|
3
|
+
|
4
|
+
attr_accessor :tcp
|
5
|
+
|
6
|
+
def initialize(tool_consumer_profile)
|
7
|
+
@tcp = tool_consumer_profile
|
8
|
+
end
|
9
|
+
|
10
|
+
def supports_capabilities?(capability, *capabilities)
|
11
|
+
capabilities.unshift(capability)
|
12
|
+
(capabilities - tcp.capabilities_offered).empty?
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -85,25 +85,42 @@ module IMS::LTI::Services
|
|
85
85
|
ret_val
|
86
86
|
end
|
87
87
|
|
88
|
+
def invalid_security_profiles
|
89
|
+
security_profiles = tool_proxy.tool_profile.security_profiles
|
90
|
+
array = security_profiles.each_with_object([]) do |sp, array|
|
91
|
+
tcp_sp = tool_consumer_profile.security_profile_by_name(security_profile_name: sp.security_profile_name)
|
92
|
+
if tcp_sp
|
93
|
+
supported_algorithms = sp.digest_algorithms & tcp_sp.digest_algorithms
|
94
|
+
unsupported_algorithms = sp.digest_algorithms - supported_algorithms
|
95
|
+
unless unsupported_algorithms.empty?
|
96
|
+
array << { name: sp.security_profile_name, algorithms: unsupported_algorithms }
|
97
|
+
end
|
98
|
+
else
|
99
|
+
array << { name: sp.security_profile_name }
|
100
|
+
end
|
101
|
+
end
|
102
|
+
array
|
103
|
+
end
|
104
|
+
|
88
105
|
def errors
|
89
106
|
ret_val = {}
|
90
107
|
ret_val[:invalid_security_contract] = invalid_security_contract unless invalid_security_contract.empty?
|
91
108
|
ret_val[:invalid_capabilities] = invalid_capabilities unless invalid_capabilities.empty?
|
92
109
|
ret_val[:invalid_message_handlers] = invalid_message_handlers unless invalid_message_handlers.empty?
|
93
110
|
ret_val[:invalid_services] = invalid_services unless invalid_services.empty?
|
94
|
-
|
111
|
+
ret_val[:invalid_security_profiles] = invalid_security_profiles unless invalid_security_profiles.empty?
|
95
112
|
ret_val
|
96
113
|
end
|
97
114
|
|
98
115
|
def valid?
|
99
|
-
|
116
|
+
errors.keys.empty?
|
100
117
|
end
|
101
118
|
|
102
119
|
private
|
103
120
|
|
104
121
|
def normalize_strings(string, *strings)
|
105
122
|
strings.push(string)
|
106
|
-
normalized = strings.map {
|
123
|
+
normalized = strings.map {|s| s.upcase.strip}
|
107
124
|
normalized
|
108
125
|
end
|
109
126
|
|
@@ -112,7 +129,7 @@ module IMS::LTI::Services
|
|
112
129
|
invalid_capabilities = mh.enabled_capabilities - capabilities_offered
|
113
130
|
invalid_parameters = validate_parameters(mh.parameters)
|
114
131
|
if !invalid_parameters.empty? || !invalid_capabilities.empty?
|
115
|
-
hash = {message_type: mh.message_type, }
|
132
|
+
hash = { message_type: mh.message_type, }
|
116
133
|
hash[:invalid_capabilities] = invalid_capabilities unless invalid_capabilities.empty?
|
117
134
|
hash[:invalid_parameters] = invalid_parameters unless invalid_parameters.empty?
|
118
135
|
array << hash
|
@@ -124,7 +141,7 @@ module IMS::LTI::Services
|
|
124
141
|
def validate_parameters(parameters)
|
125
142
|
parameters.each_with_object([]) do |p, array|
|
126
143
|
if !p.fixed? && !capabilities_offered.include?(p.variable)
|
127
|
-
array << {name: p.name, variable: p.variable}
|
144
|
+
array << { name: p.name, variable: p.variable }
|
128
145
|
end
|
129
146
|
end
|
130
147
|
end
|
@@ -142,7 +159,7 @@ module IMS::LTI::Services
|
|
142
159
|
invalid_mhs = validate_message_handlers(rh.messages)
|
143
160
|
if !invalid_mhs.empty? || !invalid_message_types.empty?
|
144
161
|
hash = {
|
145
|
-
|
162
|
+
code: rh.resource_type.code,
|
146
163
|
}
|
147
164
|
hash[:messages] = invalid_mhs unless invalid_mhs.empty?
|
148
165
|
hash[:invalid_message_types] = invalid_message_types unless invalid_message_types.empty?
|
@@ -161,6 +178,5 @@ module IMS::LTI::Services
|
|
161
178
|
hash
|
162
179
|
end
|
163
180
|
|
164
|
-
|
165
181
|
end
|
166
182
|
end
|
data/lib/ims/lti/services.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
module IMS::LTI
|
2
2
|
module Services
|
3
|
+
require_relative 'services/oauth2_client'
|
3
4
|
require_relative 'services/tool_proxy_registration_service'
|
4
5
|
require_relative 'services/tool_config'
|
5
6
|
require_relative 'services/tool_proxy_validator'
|
6
7
|
require_relative 'services/message_authenticator'
|
8
|
+
require_relative 'services/tool_consumer_profile_service'
|
9
|
+
require_relative 'services/authentication_service'
|
7
10
|
end
|
8
|
-
end
|
11
|
+
end
|
data/lib/ims/lti/version.rb
CHANGED
data/lib/ims/lti.rb
CHANGED
@@ -1,17 +1,19 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require 'simple_oauth'
|
1
|
+
require 'addressable/uri'
|
2
|
+
require 'builder'
|
4
3
|
require 'faraday'
|
5
4
|
require 'faraday_middleware'
|
6
|
-
require '
|
5
|
+
require 'json'
|
6
|
+
require 'json/jwt'
|
7
7
|
require 'rexml/document'
|
8
|
+
require 'securerandom'
|
9
|
+
require 'simple_oauth'
|
8
10
|
|
9
11
|
module IMS
|
10
12
|
module LTI
|
11
|
-
require_relative 'lti/models'
|
12
13
|
require_relative 'lti/converters'
|
13
|
-
require_relative 'lti/services'
|
14
14
|
require_relative 'lti/errors'
|
15
|
+
require_relative 'lti/models'
|
15
16
|
require_relative 'lti/serializers'
|
17
|
+
require_relative 'lti/services'
|
16
18
|
end
|
17
19
|
end
|
metadata
CHANGED
@@ -1,99 +1,133 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ims-lti
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Instructure
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-01-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: addressable
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.5'
|
20
|
+
- - ">="
|
18
21
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
22
|
+
version: 2.5.1
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
|
-
- -
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '2.5'
|
30
|
+
- - ">="
|
25
31
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
32
|
+
version: 2.5.1
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
34
|
+
name: builder
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
30
36
|
requirements:
|
31
37
|
- - "~>"
|
32
38
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
39
|
+
version: '3.2'
|
34
40
|
type: :runtime
|
35
41
|
prerelease: false
|
36
42
|
version_requirements: !ruby/object:Gem::Requirement
|
37
43
|
requirements:
|
38
44
|
- - "~>"
|
39
45
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
46
|
+
version: '3.2'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: faraday
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "<"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '2.0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "<"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '2.0'
|
41
61
|
- !ruby/object:Gem::Dependency
|
42
62
|
name: faraday_middleware
|
43
63
|
requirement: !ruby/object:Gem::Requirement
|
44
64
|
requirements:
|
45
|
-
- - "
|
65
|
+
- - "<"
|
46
66
|
- !ruby/object:Gem::Version
|
47
|
-
version: '0
|
67
|
+
version: '2.0'
|
48
68
|
type: :runtime
|
49
69
|
prerelease: false
|
50
70
|
version_requirements: !ruby/object:Gem::Requirement
|
51
71
|
requirements:
|
52
|
-
- - "
|
72
|
+
- - "<"
|
53
73
|
- !ruby/object:Gem::Version
|
54
|
-
version: '0
|
74
|
+
version: '2.0'
|
55
75
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
76
|
+
name: json-jwt
|
57
77
|
requirement: !ruby/object:Gem::Requirement
|
58
78
|
requirements:
|
59
79
|
- - "~>"
|
60
80
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
81
|
+
version: '1.7'
|
62
82
|
type: :runtime
|
63
83
|
prerelease: false
|
64
84
|
version_requirements: !ruby/object:Gem::Requirement
|
65
85
|
requirements:
|
66
86
|
- - "~>"
|
67
87
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
88
|
+
version: '1.7'
|
69
89
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
90
|
+
name: simple_oauth
|
71
91
|
requirement: !ruby/object:Gem::Requirement
|
72
92
|
requirements:
|
73
93
|
- - "~>"
|
74
94
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
76
|
-
type: :
|
95
|
+
version: 0.3.1
|
96
|
+
type: :runtime
|
77
97
|
prerelease: false
|
78
98
|
version_requirements: !ruby/object:Gem::Requirement
|
79
99
|
requirements:
|
80
100
|
- - "~>"
|
81
101
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
102
|
+
version: 0.3.1
|
83
103
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
104
|
+
name: rexml
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
type: :runtime
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
name: byebug
|
85
119
|
requirement: !ruby/object:Gem::Requirement
|
86
120
|
requirements:
|
87
121
|
- - "~>"
|
88
122
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
123
|
+
version: '9.0'
|
90
124
|
type: :development
|
91
125
|
prerelease: false
|
92
126
|
version_requirements: !ruby/object:Gem::Requirement
|
93
127
|
requirements:
|
94
128
|
- - "~>"
|
95
129
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
130
|
+
version: '9.0'
|
97
131
|
- !ruby/object:Gem::Dependency
|
98
132
|
name: guard
|
99
133
|
requirement: !ruby/object:Gem::Requirement
|
@@ -128,14 +162,14 @@ dependencies:
|
|
128
162
|
requirements:
|
129
163
|
- - "~>"
|
130
164
|
- !ruby/object:Gem::Version
|
131
|
-
version: '
|
165
|
+
version: '3.0'
|
132
166
|
type: :development
|
133
167
|
prerelease: false
|
134
168
|
version_requirements: !ruby/object:Gem::Requirement
|
135
169
|
requirements:
|
136
170
|
- - "~>"
|
137
171
|
- !ruby/object:Gem::Version
|
138
|
-
version: '
|
172
|
+
version: '3.0'
|
139
173
|
- !ruby/object:Gem::Dependency
|
140
174
|
name: pry
|
141
175
|
requirement: !ruby/object:Gem::Requirement
|
@@ -151,19 +185,47 @@ dependencies:
|
|
151
185
|
- !ruby/object:Gem::Version
|
152
186
|
version: '0.10'
|
153
187
|
- !ruby/object:Gem::Dependency
|
154
|
-
name:
|
188
|
+
name: rake
|
155
189
|
requirement: !ruby/object:Gem::Requirement
|
156
190
|
requirements:
|
157
191
|
- - "~>"
|
158
192
|
- !ruby/object:Gem::Version
|
159
|
-
version: '
|
193
|
+
version: '12.0'
|
160
194
|
type: :development
|
161
195
|
prerelease: false
|
162
196
|
version_requirements: !ruby/object:Gem::Requirement
|
163
197
|
requirements:
|
164
198
|
- - "~>"
|
165
199
|
- !ruby/object:Gem::Version
|
166
|
-
version: '
|
200
|
+
version: '12.0'
|
201
|
+
- !ruby/object:Gem::Dependency
|
202
|
+
name: rspec
|
203
|
+
requirement: !ruby/object:Gem::Requirement
|
204
|
+
requirements:
|
205
|
+
- - "~>"
|
206
|
+
- !ruby/object:Gem::Version
|
207
|
+
version: '3.2'
|
208
|
+
type: :development
|
209
|
+
prerelease: false
|
210
|
+
version_requirements: !ruby/object:Gem::Requirement
|
211
|
+
requirements:
|
212
|
+
- - "~>"
|
213
|
+
- !ruby/object:Gem::Version
|
214
|
+
version: '3.2'
|
215
|
+
- !ruby/object:Gem::Dependency
|
216
|
+
name: timecop
|
217
|
+
requirement: !ruby/object:Gem::Requirement
|
218
|
+
requirements:
|
219
|
+
- - "~>"
|
220
|
+
- !ruby/object:Gem::Version
|
221
|
+
version: '0.8'
|
222
|
+
type: :development
|
223
|
+
prerelease: false
|
224
|
+
version_requirements: !ruby/object:Gem::Requirement
|
225
|
+
requirements:
|
226
|
+
- - "~>"
|
227
|
+
- !ruby/object:Gem::Version
|
228
|
+
version: '0.8'
|
167
229
|
description:
|
168
230
|
email: opensource@instructure.com
|
169
231
|
executables: []
|
@@ -189,6 +251,7 @@ files:
|
|
189
251
|
- lib/ims/lti/converters.rb
|
190
252
|
- lib/ims/lti/converters/time_json_converter.rb
|
191
253
|
- lib/ims/lti/errors.rb
|
254
|
+
- lib/ims/lti/errors/authentication_failed_error.rb
|
192
255
|
- lib/ims/lti/errors/invalid_lti_config_error.rb
|
193
256
|
- lib/ims/lti/errors/invalid_tool_consumer_profile.rb
|
194
257
|
- lib/ims/lti/errors/tool_proxy_registration_error.rb
|
@@ -226,7 +289,7 @@ files:
|
|
226
289
|
- lib/ims/lti/models/messages/message.rb
|
227
290
|
- lib/ims/lti/models/messages/registration_request.rb
|
228
291
|
- lib/ims/lti/models/messages/request_message.rb
|
229
|
-
- lib/ims/lti/models/messages/
|
292
|
+
- lib/ims/lti/models/messages/tool_proxy_update_request.rb
|
230
293
|
- lib/ims/lti/models/parameter.rb
|
231
294
|
- lib/ims/lti/models/product_family.rb
|
232
295
|
- lib/ims/lti/models/product_info.rb
|
@@ -259,8 +322,11 @@ files:
|
|
259
322
|
- lib/ims/lti/serializers/membership_service/page_serializer.rb
|
260
323
|
- lib/ims/lti/serializers/membership_service/person_serializer.rb
|
261
324
|
- lib/ims/lti/services.rb
|
325
|
+
- lib/ims/lti/services/authentication_service.rb
|
262
326
|
- lib/ims/lti/services/message_authenticator.rb
|
327
|
+
- lib/ims/lti/services/oauth2_client.rb
|
263
328
|
- lib/ims/lti/services/tool_config.rb
|
329
|
+
- lib/ims/lti/services/tool_consumer_profile_service.rb
|
264
330
|
- lib/ims/lti/services/tool_proxy_registration_service.rb
|
265
331
|
- lib/ims/lti/services/tool_proxy_validator.rb
|
266
332
|
- lib/ims/lti/version.rb
|
@@ -283,8 +349,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
283
349
|
- !ruby/object:Gem::Version
|
284
350
|
version: '0'
|
285
351
|
requirements: []
|
286
|
-
|
287
|
-
rubygems_version: 2.6.8
|
352
|
+
rubygems_version: 3.2.22
|
288
353
|
signing_key:
|
289
354
|
specification_version: 4
|
290
355
|
summary: Ruby library for creating IMS LTI tool providers and consumers
|