ims-lti 1.1.12 → 2.3.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +5 -5
  2. data/Changelog.txt +0 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +35 -86
  5. data/lib/ims/lis/context_type/handles.rb +10 -0
  6. data/lib/ims/lis/context_type/urns.rb +10 -0
  7. data/lib/ims/lis/roles/context/handles.rb +60 -0
  8. data/lib/ims/lis/roles/context/urns.rb +60 -0
  9. data/lib/ims/lis/roles/institution/handles.rb +22 -0
  10. data/lib/ims/lis/roles/institution/urns.rb +22 -0
  11. data/lib/ims/lis/roles/system/handles.rb +15 -0
  12. data/lib/ims/lis/roles/system/urns.rb +15 -0
  13. data/lib/ims/lis/statuses/simple_names.rb +9 -0
  14. data/lib/ims/lis/statuses/uris.rb +9 -0
  15. data/lib/ims/lis.rb +14 -0
  16. data/lib/ims/lti/converters/time_json_converter.rb +13 -0
  17. data/lib/ims/lti/converters.rb +5 -0
  18. data/lib/ims/lti/errors/authentication_failed_error.rb +11 -0
  19. data/lib/ims/lti/errors/invalid_lti_config_error.rb +4 -0
  20. data/lib/ims/lti/errors/invalid_tool_consumer_profile.rb +4 -0
  21. data/lib/ims/lti/errors/tool_proxy_registration_error.rb +15 -0
  22. data/lib/ims/lti/errors.rb +8 -0
  23. data/lib/ims/lti/models/base_url_choice.rb +15 -0
  24. data/lib/ims/lti/models/base_url_selector.rb +5 -0
  25. data/lib/ims/lti/models/contact.rb +5 -0
  26. data/lib/ims/lti/models/content_item_container.rb +14 -0
  27. data/lib/ims/lti/models/content_item_placement.rb +20 -0
  28. data/lib/ims/lti/models/content_items/content_item.rb +32 -0
  29. data/lib/ims/lti/models/content_items/file_item.rb +15 -0
  30. data/lib/ims/lti/models/content_items/lti_link_item.rb +14 -0
  31. data/lib/ims/lti/models/content_items.rb +7 -0
  32. data/lib/ims/lti/models/icon_endpoint.rb +5 -0
  33. data/lib/ims/lti/models/icon_info.rb +6 -0
  34. data/lib/ims/lti/models/image.rb +7 -0
  35. data/lib/ims/lti/models/localized_name.rb +12 -0
  36. data/lib/ims/lti/models/localized_text.rb +12 -0
  37. data/lib/ims/lti/models/lti_model.rb +227 -0
  38. data/lib/ims/lti/models/membership_service/agent.rb +11 -0
  39. data/lib/ims/lti/models/membership_service/container.rb +11 -0
  40. data/lib/ims/lti/models/membership_service/context.rb +11 -0
  41. data/lib/ims/lti/models/membership_service/lis_membership_container.rb +13 -0
  42. data/lib/ims/lti/models/membership_service/lis_person.rb +14 -0
  43. data/lib/ims/lti/models/membership_service/membership.rb +14 -0
  44. data/lib/ims/lti/models/membership_service/organization.rb +14 -0
  45. data/lib/ims/lti/models/membership_service/page.rb +16 -0
  46. data/lib/ims/lti/models/membership_service/person.rb +13 -0
  47. data/lib/ims/lti/models/membership_service.rb +16 -0
  48. data/lib/ims/lti/models/message_handler.rb +14 -0
  49. data/lib/ims/lti/models/messages/basic_lti_launch_request.rb +24 -0
  50. data/lib/ims/lti/models/messages/content_item_selection.rb +32 -0
  51. data/lib/ims/lti/models/messages/content_item_selection_request.rb +26 -0
  52. data/lib/ims/lti/models/messages/message.rb +239 -0
  53. data/lib/ims/lti/models/messages/registration_request.rb +20 -0
  54. data/lib/ims/lti/models/messages/request_message.rb +12 -0
  55. data/lib/ims/lti/models/messages/tool_proxy_update_request.rb +15 -0
  56. data/lib/ims/lti/models/messages.rb +11 -0
  57. data/lib/ims/lti/models/parameter.rb +28 -0
  58. data/lib/ims/lti/models/product_family.rb +8 -0
  59. data/lib/ims/lti/models/product_info.rb +26 -0
  60. data/lib/ims/lti/models/product_instance.rb +10 -0
  61. data/lib/ims/lti/models/resource_handler.rb +22 -0
  62. data/lib/ims/lti/models/resource_type.rb +6 -0
  63. data/lib/ims/lti/models/rest_service.rb +30 -0
  64. data/lib/ims/lti/models/rest_service_profile.rb +15 -0
  65. data/lib/ims/lti/models/security_contract.rb +21 -0
  66. data/lib/ims/lti/models/security_profile.rb +10 -0
  67. data/lib/ims/lti/models/serializable.rb +12 -0
  68. data/lib/ims/lti/models/service_owner.rb +26 -0
  69. data/lib/ims/lti/models/service_provider.rb +11 -0
  70. data/lib/ims/lti/models/tool_consumer_profile.rb +45 -0
  71. data/lib/ims/lti/models/tool_profile.rb +35 -0
  72. data/lib/ims/lti/models/tool_proxy.rb +21 -0
  73. data/lib/ims/lti/models/tool_setting.rb +12 -0
  74. data/lib/ims/lti/models/tool_setting_container.rb +14 -0
  75. data/lib/ims/lti/models/vendor.rb +28 -0
  76. data/lib/ims/lti/models.rb +38 -0
  77. data/lib/ims/lti/serializers/base.rb +125 -0
  78. data/lib/ims/lti/serializers/membership_service/agent_serializer.rb +5 -0
  79. data/lib/ims/lti/serializers/membership_service/container_serializer.rb +6 -0
  80. data/lib/ims/lti/serializers/membership_service/context_serializer.rb +9 -0
  81. data/lib/ims/lti/serializers/membership_service/lis_membership_container_serializer.rb +9 -0
  82. data/lib/ims/lti/serializers/membership_service/lis_person_serializer.rb +12 -0
  83. data/lib/ims/lti/serializers/membership_service/membership_serializer.rb +7 -0
  84. data/lib/ims/lti/serializers/membership_service/organization_serializer.rb +8 -0
  85. data/lib/ims/lti/serializers/membership_service/page_serializer.rb +10 -0
  86. data/lib/ims/lti/serializers/membership_service/person_serializer.rb +8 -0
  87. data/lib/ims/lti/serializers/membership_service.rb +13 -0
  88. data/lib/ims/lti/serializers.rb +6 -0
  89. data/lib/ims/lti/services/authentication_service.rb +67 -0
  90. data/lib/ims/lti/services/message_authenticator.rb +80 -0
  91. data/lib/ims/lti/services/oauth2_client.rb +22 -0
  92. data/lib/ims/lti/{tool_config.rb → services/tool_config.rb} +26 -34
  93. data/lib/ims/lti/services/tool_consumer_profile_service.rb +16 -0
  94. data/lib/ims/lti/services/tool_proxy_registration_service.rb +84 -0
  95. data/lib/ims/lti/services/tool_proxy_validator.rb +182 -0
  96. data/lib/ims/lti/services.rb +11 -0
  97. data/lib/ims/lti/version.rb +5 -0
  98. data/lib/ims/lti.rb +13 -60
  99. data/lib/ims.rb +4 -1
  100. metadata +245 -37
  101. data/Changelog +0 -44
  102. data/LICENSE +0 -18
  103. data/lib/ims/lti/deprecated_role_checks.rb +0 -52
  104. data/lib/ims/lti/extensions/canvas.rb +0 -122
  105. data/lib/ims/lti/extensions/content.rb +0 -209
  106. data/lib/ims/lti/extensions/outcome_data.rb +0 -199
  107. data/lib/ims/lti/extensions.rb +0 -45
  108. data/lib/ims/lti/launch_params.rb +0 -166
  109. data/lib/ims/lti/outcome_request.rb +0 -206
  110. data/lib/ims/lti/outcome_response.rb +0 -162
  111. data/lib/ims/lti/request_validator.rb +0 -50
  112. data/lib/ims/lti/role_checks.rb +0 -101
  113. data/lib/ims/lti/tool_base.rb +0 -29
  114. data/lib/ims/lti/tool_consumer.rb +0 -86
  115. data/lib/ims/lti/tool_provider.rb +0 -143
@@ -0,0 +1,239 @@
1
+ module IMS::LTI::Models::Messages
2
+ class Message
3
+
4
+ attr_reader :message_authenticator
5
+
6
+ class << self
7
+
8
+ def required_params
9
+ supers_params('@required_params') | (@required_params || [])
10
+ end
11
+
12
+ def add_required_params(param, *params)
13
+ add_params('@required_params', param, *params)
14
+ end
15
+
16
+ def recommended_params
17
+ supers_params('@recommended_params') | (@recommended_params || [])
18
+ end
19
+
20
+ def add_recommended_params(param, *params)
21
+ add_params('@recommended_params', param, *params)
22
+ end
23
+
24
+ def optional_params
25
+ supers_params('@optional_params') | (@optional_params || [])
26
+ end
27
+
28
+ def add_optional_params(param, *params)
29
+ add_params('@optional_params', param, *params)
30
+ end
31
+
32
+ def deprecated_params
33
+ supers_params('@deprecated_params') | (@deprecated_params || [])
34
+ end
35
+
36
+ def add_deprecated_params(param, *params)
37
+ add_params('@deprecated_params', param, *params)
38
+ end
39
+
40
+ def inherited(klass)
41
+ @descendants ||= Set.new
42
+ @descendants << klass
43
+ superclass.inherited(klass) unless (self == Message)
44
+ end
45
+
46
+ def descendants
47
+ @descendants || Set.new
48
+ end
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
+
63
+ private
64
+
65
+ def add_params(instance_variable, param, *params)
66
+ instance_var = self.instance_variable_get(instance_variable) || []
67
+ instance_var |= params.unshift(param)
68
+ self.instance_variable_set(instance_variable, instance_var)
69
+ attr_accessor(params.shift, *params)
70
+ end
71
+
72
+ def parameters
73
+ required_params + recommended_params + optional_params + deprecated_params
74
+ end
75
+
76
+ def supers_params(instance_variable)
77
+ if name == "IMS::LTI::Models::Messages::Message"
78
+ []
79
+ else
80
+ (superclass.instance_variable_get(instance_variable) || []) | superclass.send(:supers_params, instance_variable)
81
+ end
82
+ end
83
+
84
+ end
85
+
86
+ MESSAGE_TYPE = "".freeze
87
+
88
+ LAUNCH_TARGET_IFRAME = 'iframe'
89
+ LAUNCH_TARGET_WINDOW = 'window'
90
+
91
+ EXTENSION_PREFIX = 'ext_'
92
+ CUSTOM_PREFIX = 'custom_'
93
+
94
+ OAUTH_KEYS = :oauth_callback, :oauth_consumer_key, :oauth_nonce, :oauth_signature, :oauth_signature_method,
95
+ :oauth_timestamp, :oauth_token, :oauth_verifier, :oauth_version
96
+
97
+ attr_accessor :launch_url, :jwt, *OAUTH_KEYS
98
+ attr_reader :unknown_params, :custom_params, :ext_params
99
+
100
+ add_required_params :lti_message_type, :lti_version
101
+
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
108
+ end
109
+
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 = {})
122
+
123
+ @custom_params = custom_params
124
+ @ext_params = ext_params
125
+ @unknown_params = {}
126
+
127
+ attrs.each do |k, v|
128
+ str_key = k.to_s
129
+ if str_key.start_with?(EXTENSION_PREFIX)
130
+ @ext_params[str_key] = v
131
+ elsif str_key.start_with?(CUSTOM_PREFIX)
132
+ @custom_params[str_key] = v
133
+ elsif !v.nil? && self.respond_to?(k.to_sym)
134
+ send(("#{k}=").to_sym, v)
135
+
136
+ else
137
+ warn "Unknown parameter #{k}"
138
+ @unknown_params[str_key] = v
139
+ end
140
+ end
141
+ end
142
+
143
+ def add_custom_params(params)
144
+ params.each {|k, v| k.to_s.start_with?('custom_') ? @custom_params[k.to_s] = v : @custom_params["custom_#{k.to_s}"] = v}
145
+ end
146
+
147
+ def get_custom_params
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}
153
+ end
154
+
155
+ def post_params
156
+ unknown_params.merge(@custom_params).merge(@ext_params).merge(parameters)
157
+ end
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
+
163
+ def signed_post_params(secret)
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)
170
+ @message_authenticator.signed_params
171
+ end
172
+
173
+ def parameters
174
+ collect_attributes(self.class.send("parameters"))
175
+ end
176
+
177
+ def required_params
178
+ collect_attributes(self.class.required_params)
179
+ end
180
+
181
+ def recommended_params
182
+ collect_attributes(self.class.recommended_params)
183
+ end
184
+
185
+ def optional_params
186
+ collect_attributes(self.class.optional_params)
187
+ end
188
+
189
+ def deprecated_params
190
+ collect_attributes(self.class.deprecated_params)
191
+ end
192
+
193
+ def oauth_params
194
+ collect_attributes(OAUTH_KEYS)
195
+ end
196
+
197
+ def method_missing(meth, *args, &block)
198
+ if match = /^(custom|ext)_([^=$]*)/.match(meth)
199
+ param_type, key = match.captures
200
+ param_hash = instance_variable_get("@#{param_type}_params".to_sym)
201
+ meth =~ /=$/ ? param_hash[match.to_s] = args[0] : param_hash[match.to_s]
202
+ else
203
+ super
204
+ end
205
+ end
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
+
228
+ private
229
+
230
+ def collect_attributes(attributes)
231
+ attributes.inject({}) do |h, param|
232
+ value = instance_variable_get("@#{param}")
233
+ h[param.to_s] = value if value
234
+ h
235
+ end
236
+ end
237
+
238
+ end
239
+ end
@@ -0,0 +1,20 @@
1
+ module IMS::LTI::Models::Messages
2
+ class RegistrationRequest < RequestMessage
3
+
4
+ add_required_params :reg_key, :reg_password, :tc_profile_url, :launch_presentation_return_url, :tool_proxy_guid,
5
+ :tool_proxy_url
6
+
7
+
8
+ MESSAGE_TYPE = 'ToolProxyRegistrationRequest'
9
+
10
+ def initialize(attrs = {})
11
+ super(attrs)
12
+ self.lti_message_type = MESSAGE_TYPE
13
+ end
14
+
15
+ def generate_key_and_password
16
+ self.reg_key, self.reg_password = 2.times.map { SecureRandom.uuid }
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,12 @@
1
+ module IMS::LTI::Models::Messages
2
+ class RequestMessage < Message
3
+
4
+ add_recommended_params :user_id, :roles, :launch_presentation_document_target, :launch_presentation_width, :launch_presentation_height
5
+ add_optional_params :launch_presentation_locale, :launch_presentation_css_url
6
+
7
+ def initialize(attrs = {})
8
+ super(attrs)
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ module IMS::LTI::Models::Messages
2
+ class ToolProxyUpdateRequest < RequestMessage
3
+
4
+ add_required_params :tc_profile_url, :launch_presentation_return_url
5
+
6
+
7
+ MESSAGE_TYPE = 'ToolProxyUpdateRequest'
8
+
9
+ def initialize(attrs = {})
10
+ super(attrs)
11
+ self.lti_message_type = MESSAGE_TYPE
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ module IMS::LTI::Models
2
+ module Messages
3
+ require_relative 'messages/message'
4
+ require_relative 'messages/request_message'
5
+ require_relative 'messages/registration_request'
6
+ require_relative 'messages/basic_lti_launch_request'
7
+ require_relative 'messages/content_item_selection_request'
8
+ require_relative 'messages/content_item_selection'
9
+ require_relative 'messages/tool_proxy_update_request'
10
+ end
11
+ end
@@ -0,0 +1,28 @@
1
+ module IMS::LTI::Models
2
+ class Parameter < LTIModel
3
+
4
+ add_attributes :name, :variable, :fixed
5
+
6
+ def fixed?
7
+ !fixed.nil? && fixed.to_s.strip != ''
8
+ end
9
+
10
+ def self.process_params(parameters, lookup_hash)
11
+ [*parameters].inject({}) do |hash, param|
12
+ hash[param.name] = param.fixed? ? param.fixed : expand_variable(lookup_hash[param.variable]) || '$' + param.variable
13
+ hash
14
+ end
15
+ end
16
+
17
+ def ==(obj)
18
+ self.fixed? == obj.fixed? && self.name == obj.name && self.fixed == obj.fixed
19
+ end
20
+
21
+ private
22
+
23
+ def self.expand_variable(value)
24
+ value.respond_to?(:call) ? value.call : value
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,8 @@
1
+ module IMS::LTI::Models
2
+ class ProductFamily < LTIModel
3
+ add_attributes :code
4
+ add_attribute :id, json_key: '@id'
5
+ add_attribute :vendor, relation: 'IMS::LTI::Models::Vendor'
6
+
7
+ end
8
+ end
@@ -0,0 +1,26 @@
1
+ module IMS::LTI::Models
2
+ class ProductInfo < LTIModel
3
+ add_attribute :product_version
4
+ add_attribute :product_family, relation: 'IMS::LTI::Models::ProductFamily'
5
+ add_attribute :description, relation: 'IMS::LTI::Models::LocalizedText'
6
+ add_attribute :product_name, relation: 'IMS::LTI::Models::LocalizedName'
7
+ add_attribute :technical_description, relation: 'IMS::LTI::Models::LocalizedText'
8
+
9
+ def create_product_name(name, key = 'product.name')
10
+ @product_name = LocalizedName.new(name, key)
11
+ end
12
+
13
+ def create_description(name, key = 'product.description')
14
+ @description = LocalizedText.new(name, key)
15
+ end
16
+
17
+ def default_description
18
+ description && description.default_value
19
+ end
20
+
21
+ def default_name
22
+ product_name && product_name.default_value
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,10 @@
1
+ module IMS::LTI::Models
2
+ class ProductInstance < LTIModel
3
+ add_attributes :guid
4
+ add_attribute :product_info, relation:'IMS::LTI::Models::ProductInfo'
5
+ add_attribute :service_owner, relation:'IMS::LTI::Models::ServiceOwner'
6
+ add_attribute :service_provider, relation:'IMS::LTI::Models::ServiceProvider'
7
+ add_attribute :support, relation:'IMS::LTI::Models::Contact'
8
+
9
+ end
10
+ end
@@ -0,0 +1,22 @@
1
+ module IMS::LTI::Models
2
+ class ResourceHandler < LTIModel
3
+ add_attribute :resource_type, relation: 'IMS::LTI::Models::ResourceType'
4
+ add_attribute :resource_name, relation: 'IMS::LTI::Models::LocalizedName'
5
+ add_attribute :description, relation: 'IMS::LTI::Models::LocalizedText'
6
+ add_attribute :message, relation: 'IMS::LTI::Models::MessageHandler'
7
+ add_attribute :icon_info, relation: 'IMS::LTI::Models::IconInfo'
8
+
9
+ def default_name
10
+ resource_name && resource_name.default_value
11
+ end
12
+
13
+ def default_description
14
+ description && description.default_value
15
+ end
16
+
17
+ def messages
18
+ [*message]
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,6 @@
1
+ module IMS::LTI::Models
2
+ class ResourceType < LTIModel
3
+ add_attribute :code
4
+
5
+ end
6
+ end
@@ -0,0 +1,30 @@
1
+ module IMS::LTI::Models
2
+ class RestService < LTIModel
3
+ TYPE = 'RestService'
4
+
5
+ add_attributes :endpoint, :format, :action
6
+ add_attribute :id, json_key:'@id'
7
+ add_attribute :type, json_key: '@type'
8
+
9
+ def initialize(attributes = {})
10
+ @type = TYPE
11
+ super(attributes)
12
+ end
13
+
14
+ def profile
15
+ RestServiceProfile.new(
16
+ service: endpoint,
17
+ action: action
18
+ )
19
+ end
20
+
21
+ def actions
22
+ [*action]
23
+ end
24
+
25
+ def formats
26
+ [*format]
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,15 @@
1
+ module IMS::LTI::Models
2
+ class RestServiceProfile < LTIModel
3
+ add_attributes :service, :action
4
+ add_attribute :type, json_key: '@type'
5
+
6
+ def initialize(attributes = {})
7
+ @type = "RestServiceProfile"
8
+ super(attributes)
9
+ end
10
+
11
+ def actions
12
+ [*action]
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ module IMS::LTI::Models
2
+ class SecurityContract < LTIModel
3
+
4
+ add_attributes :shared_secret, :tp_half_shared_secret
5
+ add_attribute :tool_service, relation: 'IMS::LTI::Models::RestServiceProfile'
6
+ add_attribute :end_user_service, relation: 'IMS::LTI::Models::RestServiceProfile'
7
+
8
+ def tool_services
9
+ [*tool_service]
10
+ end
11
+
12
+ def end_user_services
13
+ [*end_user_service]
14
+ end
15
+
16
+ def services
17
+ tool_services + end_user_services
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ module IMS::LTI::Models
2
+ class SecurityProfile < LTIModel
3
+ add_attributes :security_profile_name, :digest_algorithm
4
+
5
+ def digest_algorithms
6
+ [*@digest_algorithm]
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ module IMS::LTI::Models
2
+ module Serializable
3
+ def as_json(opts={})
4
+ class_name = self.class.name.gsub(/^IMS::LTI::Models/, 'IMS::LTI::Serializers') + 'Serializer'
5
+ Object.const_get(class_name).as_json(self)
6
+ end
7
+
8
+ def to_json
9
+ as_json.to_json
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,26 @@
1
+ module IMS::LTI::Models
2
+ class ServiceOwner < LTIModel
3
+ add_attribute :id, json_key:'@id'
4
+ add_attribute :description, relation: 'IMS::LTI::Models::LocalizedText'
5
+ add_attribute :timestamp, json_converter:'IMS::LTI::Converters::TimeJSONConverter'
6
+ add_attribute :service_owner_name, relation: 'IMS::LTI::Models::LocalizedName'
7
+ add_attribute :support, relation:'IMS::LTI::Models::Contact'
8
+
9
+ def create_service_owner_name(name, key = 'service_owner.name')
10
+ @service_owner_name = LocalizedName.new(name, key)
11
+ end
12
+
13
+ def create_description(name, key = 'service_owner.description')
14
+ @description = LocalizedText.new(name, key)
15
+ end
16
+
17
+ def default_name
18
+ service_owner_name && service_owner_name.default_value
19
+ end
20
+
21
+ def default_description
22
+ description && description.default_value
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,11 @@
1
+ module IMS::LTI::Models
2
+ class ServiceProvider < LTIModel
3
+ add_attribute :guid
4
+ add_attribute :id, json_key:'@id'
5
+ add_attribute :service_provider_name, relation:'IMS::LTI::Models::LocalizedName'
6
+ add_attribute :description, relation:'IMS::LTI::Models::LocalizedText'
7
+ add_attribute :support, relation:'IMS::LTI::Models::Contact'
8
+ add_attribute :timestamp, json_converter: 'IMS::LTI::Converters::TimeJSONConverter'
9
+
10
+ end
11
+ end
@@ -0,0 +1,45 @@
1
+ module IMS::LTI::Models
2
+ class ToolConsumerProfile < LTIModel
3
+
4
+ CONTEXT = "http://purl.imsglobal.org/ctx/lti/v2/ToolConsumerProfile"
5
+ TYPE = "ToolConsumerProfile"
6
+
7
+ MESSAGING_CAPABILITIES = %w(basic-lti-launch-request)
8
+ OUTCOMES_CAPABILITIES = %w(Result.autocreate)
9
+
10
+ add_attributes :lti_version, :guid, :capability_offered, :security_profile
11
+ add_attribute :id, json_key:'@id'
12
+ add_attribute :type, json_key:'@type'
13
+ add_attribute :context, json_key:'@context'
14
+ add_attribute :security_profile, relation: 'IMS::LTI::Models::SecurityProfile'
15
+ add_attribute :product_instance, relation:'IMS::LTI::Models::ProductInstance'
16
+ add_attribute :service_offered, relation: 'IMS::LTI::Models::RestService'
17
+
18
+ def initialize(attr = {})
19
+ @context = [CONTEXT]
20
+ @type = TYPE
21
+ super(attr)
22
+ end
23
+
24
+ def services_offered
25
+ [*@service_offered]
26
+ end
27
+
28
+ def capabilities_offered
29
+ [*@capability_offered]
30
+ end
31
+
32
+ def security_profiles
33
+ [*@security_profile]
34
+ end
35
+
36
+ def reregistration_capable?
37
+ @capability_offered.include?(Messages::ToolProxyUpdateRequest::MESSAGE_TYPE)
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
+
44
+ end
45
+ end
@@ -0,0 +1,35 @@
1
+ module IMS::LTI::Models
2
+ class ToolProfile < LTIModel
3
+ add_attributes :lti_version
4
+ add_attribute :id, json_key: '@id'
5
+ add_attribute :product_instance, relation:'IMS::LTI::Models::ProductInstance'
6
+ add_attribute :base_url_choice, relation: 'IMS::LTI::Models::BaseUrlChoice'
7
+ add_attribute :resource_handler, relation:'IMS::LTI::Models::ResourceHandler'
8
+ add_attribute :message, relation:'IMS::LTI::Models::MessageHandler'
9
+ add_attribute :service_offered, relation:'IMS::LTI::Models::RestService'
10
+ add_attribute :security_profile, relation:'IMS::LTI::Models::SecurityProfile'
11
+
12
+
13
+ def base_message_url
14
+ if base_url_choice
15
+ choice = base_url_choice.find { |choice| choice.default_message_url != '' }
16
+ choice ? choice.default_message_url : ''
17
+ else
18
+ ''
19
+ end
20
+ end
21
+
22
+ def resource_handlers
23
+ [*@resource_handler]
24
+ end
25
+
26
+ def messages
27
+ [*@message]
28
+ end
29
+
30
+ def security_profiles
31
+ [*@security_profile]
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ module IMS::LTI::Models
2
+ class ToolProxy < LTIModel
3
+ add_attributes :lti_version, :tool_proxy_guid, :custom, :custom_url, :enabled_capability, :tc_half_shared_secret
4
+ add_attribute :context, json_key:'@context'
5
+ add_attribute :type, json_key:'@type'
6
+ add_attribute :id, json_key:'@id'
7
+ add_attribute :tool_consumer_profile
8
+ add_attribute :tool_profile, relation:'IMS::LTI::Models::ToolProfile'
9
+ add_attribute :security_contract, relation:'IMS::LTI::Models::SecurityContract'
10
+
11
+ def initialize(attr = {})
12
+ @context = ['http://purl.imsglobal.org/ctx/lti/v2/ToolProxy']
13
+ super(attr)
14
+ @type = 'ToolProxy'
15
+ end
16
+
17
+ def enabled_capabilities
18
+ [*enabled_capability]
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,12 @@
1
+ module IMS::LTI::Models
2
+ class ToolSetting < LTIModel
3
+ add_attribute :type, json_key: '@type'
4
+ add_attribute :id, json_key: '@id'
5
+ add_attributes :custom, :custom_url
6
+
7
+ TOOL_PROXY_BINDING_TYPE = 'ToolProxyBinding'
8
+ TOOL_PROXY_TYPE = 'ToolProxy'
9
+ LTI_LINK_TYPE = 'LtiLink'
10
+
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ module IMS::LTI::Models
2
+ class ToolSettingContainer < LTIModel
3
+ add_attribute :context, json_key: '@context'
4
+ add_attribute :graph, json_key: '@graph', relation: 'IMS::LTI::Models::ToolSetting'
5
+
6
+ TOOL_SETTING_CONTAINER_CONTEXT = 'http://purl.imsglobal.org/ctx/lti/v2/ToolSettings'
7
+
8
+ def initialize(opts = {})
9
+ super(opts)
10
+ self.context = TOOL_SETTING_CONTAINER_CONTEXT
11
+ end
12
+
13
+ end
14
+ end