k12-ims-lti 1.2.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c39ae99a074ae3a67bde1d7a4729e13219a3ecba19a07b251f40a53ffe7930dd
4
+ data.tar.gz: 7500ff51d6c00c0fd29ae9cffc96524353d86cb82064d7778962f824b75878f8
5
+ SHA512:
6
+ metadata.gz: 1cf5416ab0833797ad597bb54f68e444f262cad7d9b7d2b3c333c2a812cb6158d7bb03ababb0f2a295519d44ef892cfc2eb6f0d2533e2551333b2a047f02a28e
7
+ data.tar.gz: 5aee850d8217e7b7528d4d9850fc7556bc2414b5fa00d23dee0544b19d94d518ca06c2648d2737754a772106a68602c0e4f65b7dc566d857d01be944a350d12c
data/Changelog ADDED
@@ -0,0 +1,54 @@
1
+ 2020-02-03 Version 1.2.4
2
+ * Add support for submittedAt date
3
+
4
+ 2017-03-08 Version 1.2.0
5
+ * Don't downcase roles
6
+
7
+ 2016-10-05 Version 1.1.13
8
+ * Fix Oauth::Unauthorized initialization bug
9
+ * Relax oauth dependency
10
+
11
+ 2016-06-16 Version 1.1.12
12
+ * Add support for detecting observer role
13
+
14
+ 2015-02-20 Version 1.1.8
15
+ * Replace usage of the 'uuid' gem with SecureRandom
16
+ * Update the outcome extension doc example
17
+
18
+ 2015-01-09 Version 1.1.7
19
+ * Add support for new outcome extension resultTotalScore
20
+
21
+ 2013-04-24 Version 1.1.3
22
+
23
+ * Corrected lti_version launch parameter
24
+
25
+ 2012-09-05 Version 1.1.2
26
+
27
+ * Added better role checking and convenience methods
28
+
29
+ 2012-09-04 Version 1.1.1
30
+
31
+ * Added cdata value for outcome data extension
32
+
33
+ 2012-08-14 Version 1.1.0
34
+
35
+ * Added framework for LTI extensions
36
+ * Added LTI outcome data extension
37
+ * Fix tests reliant on random ordering
38
+ * Add rails 3 note to readme install docs
39
+ * Create Changelog
40
+
41
+
42
+ 2012-03-14 Version 1.0.2
43
+
44
+ * Refactor OAuth validation into its own module
45
+
46
+
47
+ 2012-03-13 Version 1.0.1
48
+
49
+ * Add gem dependencies to gemspec
50
+
51
+
52
+ 2012-03-11 Version 1.0.0
53
+
54
+ * Publish fully functional and operational IMS LTI library
data/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2012 Instructure
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to use,
6
+ copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
7
+ Software, and to permit persons to whom the Software is furnished to do so,
8
+ subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
17
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,116 @@
1
+ # IMS LTI
2
+
3
+ This ruby library is to help create Tool Providers and Tool Consumers for the
4
+ [IMS LTI standard](http://www.imsglobal.org/lti/index.html).
5
+
6
+ ## Installation
7
+ This is packaged as the `ims-lti` rubygem, so you can just add the dependency to
8
+ your Gemfile or install the gem on your system:
9
+
10
+ gem install ims-lti
11
+
12
+ To require the library in your project:
13
+
14
+ require 'ims/lti'
15
+
16
+ To validate the OAuth signatures you need to require the appropriate request
17
+ proxy for your application. For example:
18
+
19
+ # For a Rails 5 (and 2.3) app:
20
+ require 'oauth/request_proxy/action_controller_request'
21
+
22
+ # For a Sinatra or a Rails 3 or 4 app:
23
+ require 'oauth/request_proxy/rack_request'
24
+ # You also need to explicitly enable OAuth 1 support in the environment.rb or an initializer:
25
+ OAUTH_10_SUPPORT = true
26
+
27
+ For further information see the [oauth-ruby](https://github.com/oauth-xx/oauth-ruby) project.
28
+
29
+ As a quick debugging note, if you forget that step, you'll get an error like:
30
+
31
+ OAuth::RequestProxy::UnknownRequestType
32
+
33
+ ## Usage
34
+ This readme won't cover the LTI standard, just how to use the library. It will be
35
+ very helpful to read the [LTI documentation](http://www.imsglobal.org/lti/index.html)
36
+
37
+ In LTI there are Tool Providers (TP) and Tool Consumers (TC), this library is
38
+ useful for implementing both. Here is an overview of the communication process:
39
+ [LTI 1.1 Introduction](http://www.imsglobal.org/LTI/v1p1/ltiIMGv1p1.html#_Toc319560461)
40
+
41
+ This library doesn't help you manage the consumer keys and secrets. The POST
42
+ headers/parameters will contain the `oauth_consumer_key` and your app can use
43
+ that to look up the appropriate `oauth_consumer_secret`.
44
+
45
+ Your app will also need to manage the OAuth nonce to make sure the same nonce
46
+ isn't used twice with the same timestamp. [Read the LTI documentation on OAuth](http://www.imsglobal.org/LTI/v1p1/ltiIMGv1p1.html#_Toc319560468).
47
+
48
+ ### Tool Provider
49
+ As a TP your app will receive a POST request with a bunch of
50
+ [LTI launch data](http://www.imsglobal.org/LTI/v1p1/ltiIMGv1p1.html#_Toc319560465)
51
+ and it will be signed with OAuth using a key/secret that both the TP and TC share.
52
+ This is covered in the [LTI security model](http://www.imsglobal.org/LTI/v1p1/ltiIMGv1p1.html#_Toc319560466)
53
+
54
+ Here is an example of a simple TP Sinatra app using this gem:
55
+ [LTI Tool Provider](https://github.com/instructure/lti1_tool_provider_example)
56
+
57
+ Once you find the `oauth_consumer_secret` based on the `oauth_consumer_key` in
58
+ the request, you can initialize a `ToolProvider` object with them and the post parameters:
59
+
60
+ ```ruby
61
+ # Initialize TP object with OAuth creds and post parameters
62
+ provider = IMS::LTI::ToolProvider.new(consumer_key, consumer_secret, params)
63
+
64
+ # Verify OAuth signature by passing the request object
65
+ if provider.valid_request?(request)
66
+ # success
67
+ else
68
+ # handle invalid OAuth
69
+ end
70
+ ```
71
+
72
+ Once your TP object is initialized and verified you can load your tool. All of the
73
+ [launch data](http://www.imsglobal.org/LTI/v1p1/ltiIMGv1p1.html#_Toc319560465)
74
+ is available in the TP object along with some convenience methods like `provider.username`
75
+ which will try to find the name from the 3 potential name launch data attributes.
76
+
77
+ #### Returning Results of a Quiz/Assignment
78
+ If your TP provides some kind of assessment service you can write grades back to
79
+ the TC. This is documented in the LTI docs [here](http://www.imsglobal.org/LTI/v1p1/ltiIMGv1p1.html#_Toc319560471).
80
+
81
+ You can check whether the TC is expecting a grade write-back:
82
+
83
+ ```ruby
84
+ if provider.outcome_service?
85
+ # ready for grade write-back
86
+ else
87
+ # normal tool launch without grade write-back
88
+ end
89
+ ```
90
+
91
+ To write the grade back to the TC your tool will do a POST directly back to the
92
+ URL the TC passed in the launch data. You can use the TP object to do that for you:
93
+
94
+ ```ruby
95
+ # post the score to the TC, score should be a float >= 0.0 and <= 1.0
96
+ # this returns an OutcomeResponse object
97
+ response = provider.post_replace_result!(score)
98
+ if response.success?
99
+ # grade write worked
100
+ elsif response.processing?
101
+ elsif response.unsupported?
102
+ else
103
+ # failed
104
+ end
105
+ ```
106
+
107
+ You can see the error code documentation
108
+ [here](http://www.imsglobal.org/gws/gwsv1p0/imsgws_baseProfv1p0.html#1639667).
109
+
110
+ ### Tool Consumer
111
+ As a Tool Consumer your app will POST an OAuth-signed launch requests to TPs with the necessary
112
+ [LTI launch data](http://www.imsglobal.org/LTI/v1p1/ltiIMGv1p1.html#_Toc319560465).
113
+ This is covered in the [LTI security model](http://www.imsglobal.org/LTI/v1p1/ltiIMGv1p1.html#_Toc319560466)
114
+
115
+ Here is an example of a simple TC Sinatra app using this gem:
116
+ [LTI Tool Consumer](https://github.com/instructure/lti_tool_consumer_example)
@@ -0,0 +1,52 @@
1
+ # These are here for backwards-compatibility
2
+ # But they are deprecated and the new ones in
3
+ # role_checks.rb should be used
4
+ module IMS::LTI
5
+ module DeprecatedRoleChecks
6
+ # Check whether the Launch Parameters have a role
7
+ def has_role?(role)
8
+ role = role.downcase
9
+ @roles && @roles.any?{|r| r.downcase.index(role)}
10
+ end
11
+
12
+ # Convenience method for checking if the user has 'learner' or 'student' role
13
+ def student?
14
+ has_role?('learner') || has_role?('student')
15
+ end
16
+
17
+ # Convenience method for checking if the user has 'instructor' or 'faculty' or 'staff' role
18
+ def instructor?
19
+ has_role?('instructor') || has_role?('faculty') || has_role?('staff')
20
+ end
21
+
22
+ # Convenience method for checking if the user has 'contentdeveloper' role
23
+ def content_developer?
24
+ has_role?('ContentDeveloper')
25
+ end
26
+
27
+ # Convenience method for checking if the user has 'Member' role
28
+ def member?
29
+ has_role?('Member')
30
+ end
31
+
32
+ # Convenience method for checking if the user has 'Manager' role
33
+ def manager?
34
+ has_role?('Manager')
35
+ end
36
+
37
+ # Convenience method for checking if the user has 'Mentor' role
38
+ def mentor?
39
+ has_role?('Mentor')
40
+ end
41
+
42
+ # Convenience method for checking if the user has 'administrator' role
43
+ def admin?
44
+ has_role?('administrator')
45
+ end
46
+
47
+ # Convenience method for checking if the user has 'TeachingAssistant' role
48
+ def ta?
49
+ has_role?('TeachingAssistant')
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,122 @@
1
+ module IMS::LTI
2
+ module Extensions
3
+ # Module that adds Canvas specific LTI extensions
4
+ #
5
+ # It adds convenience methods for generating common canvas use case LTI configurations
6
+ #
7
+ # == Usage
8
+ # To generate an XML configuration:
9
+ #
10
+ # # Create a config object and set some options
11
+ # tc = IMS::LTI::ToolConfig.new(:title => "Example Sinatra Tool Provider", :launch_url => url)
12
+ # tc.description = "This example LTI Tool Provider supports LIS Outcome pass-back."
13
+ #
14
+ # # Extend the Canvas Tool config and add canvas related extensions
15
+ # tc.extend IMS::LTI::Extensions::Canvas::ToolConfig
16
+ # tc.homework_submission! 'http://someplace.com/homework', 'Find Homework'
17
+ #
18
+ # # generate the XML
19
+ # tc.to_xml
20
+ #
21
+ # Or to create a config object from an XML String:
22
+ #
23
+ # tc = IMS::LTI::ToolConfig.create_from_xml(xml)
24
+
25
+ module Canvas
26
+ module ToolConfig
27
+ PLATFORM = 'canvas.instructure.com'
28
+
29
+ # Canvas extension defaults
30
+ # These properties will cascade down to any options that are configured
31
+ def set_canvas_ext_param(key, value)
32
+ set_ext_param(PLATFORM, key, value)
33
+ end
34
+
35
+ def get_canvas_param(param_key)
36
+ get_ext_param PLATFORM, param_key
37
+ end
38
+
39
+ def canvas_privacy_public!()
40
+ set_canvas_ext_param(:privacy_level, 'public')
41
+ end
42
+
43
+ def canvas_privacy_name_only!()
44
+ set_canvas_ext_param(:privacy_level, 'name_only')
45
+ end
46
+
47
+ def canvas_privacy_anonymous!()
48
+ set_canvas_ext_param(:privacy_level, 'anonymous')
49
+ end
50
+
51
+ def canvas_domain!(domain)
52
+ set_canvas_ext_param(:domain, domain)
53
+ end
54
+
55
+ def canvas_text!(text)
56
+ set_canvas_ext_param(:text, text)
57
+ end
58
+
59
+ def canvas_icon_url!(icon_url)
60
+ set_canvas_ext_param(:icon_url, icon_url)
61
+ end
62
+
63
+ def canvas_tool_id!(tool_id)
64
+ set_canvas_ext_param(:tool_id, tool_id)
65
+ end
66
+
67
+ def canvas_selector_dimensions!(width, height)
68
+ set_canvas_ext_param(:selection_width, width)
69
+ set_canvas_ext_param(:selection_height, height)
70
+ end
71
+
72
+ # Canvas options
73
+ # These configure canvas to expose the tool in various locations. Any properties that are set
74
+ # at this level will override the defaults for this launch of the tool
75
+
76
+ # Enables homework submissions via the tool
77
+ # Valid properties are url, text, selection_width, selection_height, enabled
78
+ def canvas_homework_submission!(params = {})
79
+ set_canvas_ext_param(:homework_submission, params)
80
+ end
81
+
82
+ # Adds the tool to canvas' rich text editor
83
+ # Valid properties are url, icon_url, text, selection_width, selection_height, enabled
84
+ def canvas_editor_button!(params = {})
85
+ set_canvas_ext_param(:editor_button, params)
86
+ end
87
+
88
+ # Adds the tool to canvas' resource selector
89
+ # Valid properties are url, text, selection_width, selection_height, enabled
90
+ def canvas_resource_selection!(params = {})
91
+ set_canvas_ext_param(:resource_selection, params)
92
+ end
93
+
94
+ # Adds the tool to account level navigation in canvas
95
+ # Valid properties are url, text, enabled
96
+ def canvas_account_navigation!(params = {})
97
+ set_canvas_ext_param(:account_navigation, params)
98
+ end
99
+
100
+ # Adds the tool to course level navigation in canvas
101
+ # Valid properties are url, text, visibility, default, enabled
102
+ # Visibility describes who will see the navigation element. Possible values are "admins", "members", and nil
103
+ # Default determines if it is on or off by default. Possible values are "admins", "members", and nil
104
+ def canvas_course_navigation!(params = {})
105
+ set_canvas_ext_param(:course_navigation, params)
106
+ end
107
+
108
+ # Adds the tool to user level navigation in canvas
109
+ # Valid properties are url, text, enabled
110
+ def canvas_user_navigation!(params = {})
111
+ set_canvas_ext_param(:user_navigation, params)
112
+ end
113
+
114
+ # Adds canvas environment configurations options
115
+ # Valid properties are launch_url, domain, test_launch_url, test_domain, beta_launch_url, beta_domain
116
+ def canvas_environments!(params = {})
117
+ set_canvas_ext_param(:environments, params)
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,209 @@
1
+ module IMS::LTI
2
+ module Extensions
3
+
4
+ # An LTI extension that adds support for content back to the consumer
5
+ #
6
+ # # Initialize TP object with OAuth creds and post parameters
7
+ # provider = IMS::LTI::ToolProvider.new(consumer_key, consumer_secret, params)
8
+ # # add extension
9
+ # provider.extend IMS::LTI::Extensions::Content::ToolProvider
10
+ #
11
+ # If the tool was launched as an content request and it supports the content extension
12
+ # you can redirect the user to the tool consumer using the return url helper methods.
13
+ # The tool consumer is then responsible for consuming the content.
14
+ #
15
+ # #Check if a certain response type is available
16
+ # if provider.accepts_url? do
17
+ # #Generate the URL for the user
18
+ # redirect provider.url_content_return_url(url)
19
+ # end
20
+ #
21
+ module Content
22
+ module ToolProvider
23
+ include IMS::LTI::Extensions::ExtensionBase
24
+ include Base
25
+
26
+ # a list of the supported outcome data types
27
+ def accepted_content_types
28
+ return @content_types if @content_types
29
+ @content_types = []
30
+ if val = @ext_params["content_return_types"]
31
+ @content_types = val.split(',').map {|i| i.to_sym}
32
+ end
33
+
34
+ @content_types
35
+ end
36
+
37
+ def accepted_file_extensions
38
+ return @file_extensions if @file_extensions
39
+ @file_extensions = []
40
+ if val = @ext_params["content_file_extensions"]
41
+ @file_extensions = val.split(',').map {|i| i.downcase.strip}
42
+ end
43
+
44
+ @file_extensions
45
+ end
46
+
47
+ def accepts_file?(file_name = nil)
48
+ accepted_content_types.include?(:file) &&
49
+ ( file_name.nil? ||
50
+ accepted_file_extensions.empty? ||
51
+ accepted_file_extensions.any?{|ext| file_name.downcase[/#{ext}$/]} )
52
+ end
53
+
54
+ def accepts_url?
55
+ accepted_content_types.include?(:url)
56
+ end
57
+
58
+ def accepts_lti_launch_url?
59
+ accepted_content_types.include?(:lti_launch_url)
60
+ end
61
+
62
+ def accepts_image_url?
63
+ accepted_content_types.include?(:image_url)
64
+ end
65
+
66
+ def accepts_iframe?
67
+ accepted_content_types.include?(:iframe)
68
+ end
69
+
70
+ def accepts_oembed?
71
+ accepted_content_types.include?(:oembed)
72
+ end
73
+
74
+ def content_intended_use
75
+ @ext_params["content_intended_use"].to_sym if @ext_params["content_intended_use"]
76
+ end
77
+
78
+ # check if the content extension is supported
79
+ def accepts_content?
80
+ !!@ext_params["content_return_types"]
81
+ end
82
+
83
+ # check if the consumer accepts a given type of content
84
+ def accepts_content_type?(content_type)
85
+ accepted_content_types.include? content_type.to_sym
86
+ end
87
+
88
+ #check the use of the content
89
+ def is_content_for? (intended_use)
90
+ content_intended_use == intended_use
91
+ end
92
+
93
+ def content_return_url
94
+ @ext_params["content_return_url"]
95
+ end
96
+
97
+ #generates the return url for file submissions
98
+ def file_content_return_url(url, text, content_type = nil)
99
+ url = CGI::escape(url)
100
+ text = CGI::escape(text)
101
+ content_type = CGI::escape(content_type) if content_type
102
+
103
+ return_url = "#{content_return_url}?return_type=file&url=#{url}&text=#{text}"
104
+ return_url = "#{return_url}&content_type=#{content_type}" if content_type
105
+
106
+ return return_url
107
+ end
108
+
109
+ #generates the return url for url submissions
110
+ def url_content_return_url(url, title = nil, text = 'link', target = '_blank')
111
+ url = CGI::escape(url)
112
+ text = CGI::escape(text)
113
+ target = CGI::escape(target)
114
+
115
+ return_url = "#{content_return_url}?return_type=url&url=#{url}&text=#{text}&target=#{target}"
116
+ return_url = "#{return_url}&title=#{CGI::escape(title)}" if title
117
+
118
+ return return_url
119
+ end
120
+
121
+ #generates the return url for lti launch submissions
122
+ def lti_launch_content_return_url(url, text='link', title=nil)
123
+ url = CGI::escape(url)
124
+ text = CGI::escape(text)
125
+
126
+ return_url = "#{content_return_url}?return_type=lti_launch_url&url=#{url}&text=#{text}"
127
+ return_url = "#{return_url}&title=#{CGI::escape(title)}" if title
128
+
129
+ return return_url
130
+ end
131
+
132
+ #generates the return url for image submissions
133
+ def image_content_return_url(url, width, height, alt = '')
134
+ url = CGI::escape(url)
135
+ width = CGI::escape(width.to_s)
136
+ height = CGI::escape(height.to_s)
137
+ alt = CGI::escape(alt)
138
+
139
+ "#{content_return_url}?return_type=image_url&url=#{url}&width=#{width}&height=#{height}&alt=#{alt}"
140
+ end
141
+
142
+ #generates the return url for iframe submissions
143
+ def iframe_content_return_url(url, width, height, title = nil)
144
+ url = CGI::escape(url)
145
+ width = CGI::escape(width.to_s)
146
+ height = CGI::escape(height.to_s)
147
+
148
+ return_url = "#{content_return_url}?return_type=iframe&url=#{url}&width=#{width}&height=#{height}"
149
+ return_url = "#{return_url}&title=#{CGI::escape(title)}" if title
150
+
151
+ return return_url
152
+ end
153
+
154
+ #generates the return url for oembed submissions
155
+ def oembed_content_return_url(url, endpoint)
156
+ url = CGI::escape(url)
157
+ endpoint = CGI::escape(endpoint)
158
+
159
+ "#{content_return_url}?return_type=oembed&url=#{url}&endpoint=#{endpoint}"
160
+ end
161
+ end
162
+
163
+ module ToolConsumer
164
+ include IMS::LTI::Extensions::ExtensionBase
165
+ include Base
166
+
167
+ # a list of the content types accepted
168
+ #
169
+ # tc.add_content_return_types=(['url', 'text'])
170
+ # tc.add_content_return_types=("url,text")
171
+ def content_return_types=(val)
172
+ val = val.join(',') if val.is_a? Array
173
+ set_ext_param('content_return_types', val)
174
+ end
175
+
176
+ # a comma-separated string of the supported outcome data types
177
+ def content_return_types
178
+ get_ext_param('content_return_types')
179
+ end
180
+
181
+ def content_intended_use=(val)
182
+ set_ext_param('content_intended_use', val)
183
+ end
184
+
185
+ def content_intended_use
186
+ get_ext_param('content_intended_use')
187
+ end
188
+
189
+ # convenience method for setting support for homework content
190
+ def support_homework_content!
191
+ self.content_intended_use = 'homework'
192
+ self.content_return_types = 'file,url'
193
+ end
194
+
195
+ # convenience method for setting support for embed content
196
+ def support_embed_content!
197
+ self.content_intended_use = 'embed'
198
+ self.content_return_types = 'oembed,lti_launch_url,url,image_url,iframe'
199
+ end
200
+
201
+ # convenience method for setting support for navigation content
202
+ def support_navigation_content!
203
+ self.content_intended_use = 'navigation'
204
+ self.content_return_types = 'lti_launch_url'
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end