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