mil-ims-lti 1.1.3

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
+ SHA1:
3
+ metadata.gz: dd669611923a7aeccccb86825e265d149e05eccf
4
+ data.tar.gz: 20ebf881f4bf4079b9afa5cd51bdea5a5a930a2f
5
+ SHA512:
6
+ metadata.gz: 6e15d7a0c9868b900e929c02b504728d5f6b12daa095661bc18231ad092a280535c706a862090bd56c62a8dd1c57e38717089b24a26e9c7ff94985cc16d8bc41
7
+ data.tar.gz: aae5e5120cc560f6ff5c28373f46ebbc30705202ee1eb8b719ec6d5eb80169905c31a1d77afa8ce39827be05ac9365c73a1a21a0caa9a1af3ec1d9f5cae8e482
data/Changelog ADDED
@@ -0,0 +1,30 @@
1
+ 2012-09-05 Version 1.1.2
2
+
3
+ * Added better role checking and convenience methods
4
+
5
+ 2012-09-04 Version 1.1.1
6
+
7
+ * Added cdata value for outcome data extension
8
+
9
+ 2012-08-14 Version 1.1.0
10
+
11
+ * Added framework for LTI extensions
12
+ * Added LTI outcome data extension
13
+ * Fix tests reliant on random ordering
14
+ * Add rails 3 note to readme install docs
15
+ * Create Changelog
16
+
17
+
18
+ 2012-03-14 Version 1.0.2
19
+
20
+ * Refactor OAuth validation into its own module
21
+
22
+
23
+ 2012-03-13 Version 1.0.1
24
+
25
+ * Add gem dependencies to gemspec
26
+
27
+
28
+ 2012-03-11 Version 1.0.0
29
+
30
+ * 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,112 @@
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 Sinatra or a Rails 3 app:
20
+ require 'oauth/request_proxy/rack_request'
21
+ # You also need to explicitly enable OAuth 1 support in the environment.rb or an initializer:
22
+ OAUTH_10_SUPPORT = true
23
+
24
+ # For a Rails 2.3 app:
25
+ require 'oauth/request_proxy/action_controller_request'
26
+
27
+ For further information see the [oauth-ruby](https://github.com/oauth/oauth-ruby) project.
28
+
29
+ ## Usage
30
+ This readme won't cover the LTI standard, just how to use the library. It will be
31
+ very helpful to read the [LTI documentation](http://www.imsglobal.org/lti/index.html)
32
+
33
+ In LTI there are Tool Providers (TP) and Tool Consumers (TC), this library is
34
+ useful for implementing both. Here is an overview of the communication process:
35
+ [LTI 1.1 Introduction](http://www.imsglobal.org/lti/v1p1pd/ltiIMGv1p1pd.html#_Toc309649680)
36
+
37
+ This library doesn't help you manage the consumer keys and secrets. The POST
38
+ headers/parameters will contain the `oauth_consumer_key` and your app can use
39
+ that to look up the appropriate `oauth_consumer_secret`.
40
+
41
+ Your app will also need to manage the OAuth nonce to make sure the same nonce
42
+ isn't used twice with the same timestamp. [Read the LTI documentation on OAuth](http://www.imsglobal.org/LTI/v1p1pd/ltiIMGv1p1pd.html#_Toc309649687).
43
+
44
+ ### Tool Provider
45
+ As a TP your app will receive a POST request with a bunch of
46
+ [LTI launch data](http://www.imsglobal.org/lti/v1p1pd/ltiIMGv1p1pd.html#_Toc309649684)
47
+ and it will be signed with OAuth using a key/secret that both the TP and TC share.
48
+ This is covered in the [LTI security model](http://www.imsglobal.org/lti/v1p1pd/ltiIMGv1p1pd.html#_Toc309649685)
49
+
50
+ Here is an example of a simple TP Sinatra app using this gem:
51
+ [LTI Tool Provider](https://github.com/instructure/lti_tool_provider_example)
52
+
53
+ Once you find the `oauth_consumer_secret` based on the `oauth_consumer_key` in
54
+ the request, you can initialize a `ToolProvider` object with them and the post parameters:
55
+
56
+ ```ruby
57
+ # Initialize TP object with OAuth creds and post parameters
58
+ provider = IMS::LTI::ToolProvider.new(consumer_key, consumer_secret, params)
59
+
60
+ # Verify OAuth signature by passing the request object
61
+ if provider.valid_request?(request)
62
+ # success
63
+ else
64
+ # handle invalid OAuth
65
+ end
66
+ ```
67
+
68
+ Once your TP object is initialized and verified you can load your tool. All of the
69
+ [launch data](http://www.imsglobal.org/lti/v1p1pd/ltiIMGv1p1pd.html#_Toc309649684)
70
+ is available in the TP object along with some convenience methods like `provider.username`
71
+ which will try to find the name from the 3 potential name launch data attributes.
72
+
73
+ #### Returning Results of a Quiz/Assignment
74
+ If your TP provides some kind of assessment service you can write grades back to
75
+ the TC. This is documented in the LTI docs [here](http://www.imsglobal.org/lti/v1p1pd/ltiIMGv1p1pd.html#_Toc309649690).
76
+
77
+ You can check whether the TC is expecting a grade write-back:
78
+
79
+ ```ruby
80
+ if provider.outcome_service?
81
+ # ready for grade write-back
82
+ else
83
+ # normal tool launch without grade write-back
84
+ end
85
+ ```
86
+
87
+ To write the grade back to the TC your tool will do a POST directly back to the
88
+ URL the TC passed in the launch data. You can use the TP object to do that for you:
89
+
90
+ ```ruby
91
+ # post the score to the TC, score should be a float >= 0.0 and <= 1.0
92
+ # this returns an OutcomeResponse object
93
+ response = provider.post_replace_result!(score)
94
+ if response.success?
95
+ # grade write worked
96
+ elsif response.processing?
97
+ elsif response.unsupported?
98
+ else
99
+ # failed
100
+ end
101
+ ```
102
+
103
+ You can see the error code documentation
104
+ [here](http://www.imsglobal.org/gws/gwsv1p0/imsgws_baseProfv1p0.html#1639667).
105
+
106
+ ### Tool Consumer
107
+ As a Tool Consumer your app will POST an OAuth-signed launch requests to TPs with the necessary
108
+ [LTI launch data](http://www.imsglobal.org/lti/v1p1pd/ltiIMGv1p1pd.html#_Toc309649684).
109
+ This is covered in the [LTI security model](http://www.imsglobal.org/lti/v1p1pd/ltiIMGv1p1pd.html#_Toc309649685)
110
+
111
+ Here is an example of a simple TC Sinatra app using this gem:
112
+ [LTI Tool Consumer](https://github.com/instructure/lti_tool_consumer_example)
data/ims-lti.gemspec ADDED
@@ -0,0 +1,35 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = %q{ims-lti}
3
+ s.version = "1.1.3"
4
+
5
+ s.add_dependency 'builder'
6
+ s.add_dependency 'oauth', '~> 0.4.5'
7
+ s.add_dependency 'uuid'
8
+
9
+ s.add_development_dependency 'rspec'
10
+ s.add_development_dependency 'ruby-deug'
11
+
12
+ s.authors = ["Instructure"]
13
+ s.date = %q{2012-09-05}
14
+ s.extra_rdoc_files = %W(LICENSE)
15
+ s.files = %W(
16
+ Changelog
17
+ LICENSE
18
+ README.md
19
+ lib/ims.rb
20
+ lib/ims/lti.rb
21
+ lib/ims/lti/extensions.rb
22
+ lib/ims/lti/extensions/outcome_data.rb
23
+ lib/ims/lti/launch_params.rb
24
+ lib/ims/lti/outcome_request.rb
25
+ lib/ims/lti/outcome_response.rb
26
+ lib/ims/lti/request_validator.rb
27
+ lib/ims/lti/tool_config.rb
28
+ lib/ims/lti/tool_consumer.rb
29
+ lib/ims/lti/tool_provider.rb
30
+ ims-lti.gemspec
31
+ )
32
+ s.homepage = %q{http://github.com/instructure/ims-lti}
33
+ s.require_paths = %W(lib)
34
+ s.summary = %q{Ruby library for creating IMS LTI tool providers and consumers}
35
+ end
data/lib/ims.rb ADDED
@@ -0,0 +1 @@
1
+ require 'ims/lti'
data/lib/ims/lti.rb ADDED
@@ -0,0 +1,50 @@
1
+ require 'oauth'
2
+ require 'builder'
3
+ require "rexml/document"
4
+ require 'uuid'
5
+ require 'cgi'
6
+
7
+ module IMS # :nodoc:
8
+
9
+ # :main:IMS::LTI
10
+ # LTI is a standard defined by IMS for creating eduction Tool Consumers/Providers.
11
+ # LTI documentation: http://www.imsglobal.org/lti/index.html
12
+ #
13
+ # When creating these tools you will work primarily with the ToolProvider and
14
+ # ToolConsumer classes.
15
+ #
16
+ # For validating OAuth request be sure to require the necessary proxy request
17
+ # object. See IMS::LTI::RequestValidator#valid_request? for more documentation.
18
+ #
19
+ # == Installation
20
+ # This is packaged as the `ims-lti` rubygem, so you can just add the dependency to
21
+ # your Gemfile or install the gem on your system:
22
+ #
23
+ # gem install ims-lti
24
+ #
25
+ # To require the library in your project:
26
+ #
27
+ # require 'ims/lti'
28
+ module LTI
29
+
30
+ # The versions of LTI this library supports
31
+ VERSIONS = %w{1.0 1.1}
32
+
33
+ class InvalidLTIConfigError < StandardError
34
+ end
35
+
36
+ # Generates a unique identifier
37
+ def self.generate_identifier
38
+ UUID.new
39
+ end
40
+ end
41
+ end
42
+
43
+ require 'ims/lti/extensions'
44
+ require 'ims/lti/launch_params'
45
+ require 'ims/lti/request_validator'
46
+ require 'ims/lti/tool_provider'
47
+ require 'ims/lti/tool_consumer'
48
+ require 'ims/lti/outcome_request'
49
+ require 'ims/lti/outcome_response'
50
+ require 'ims/lti/tool_config'
@@ -0,0 +1,43 @@
1
+
2
+ module IMS::LTI
3
+ module Extensions
4
+
5
+ # Base functionality for creating LTI extension modules
6
+ # See the test for this class for a simple example of how to create an extension module
7
+ module Base
8
+ def outcome_request_extensions
9
+ []
10
+ end
11
+
12
+ def outcome_response_extensions
13
+ []
14
+ end
15
+
16
+ def extend_outcome_request(request)
17
+ outcome_request_extensions.each do |ext|
18
+ request.extend(ext)
19
+ end
20
+ request
21
+ end
22
+
23
+ def extend_outcome_response(response)
24
+ outcome_response_extensions.each do |ext|
25
+ response.extend(ext)
26
+ end
27
+ response
28
+ end
29
+ end
30
+
31
+ module ExtensionBase
32
+ def outcome_request_extensions
33
+ super
34
+ end
35
+
36
+ def outcome_response_extensions
37
+ super
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ require 'ims/lti/extensions/outcome_data'
@@ -0,0 +1,156 @@
1
+ module IMS::LTI
2
+ module Extensions
3
+
4
+ # An LTI extension that adds support for sending data back to the consumer
5
+ # in addition to the score.
6
+ #
7
+ # # Initialize TP object with OAuth creds and post parameters
8
+ # provider = IMS::LTI::ToolProvider.new(consumer_key, consumer_secret, params)
9
+ # # add extension
10
+ # provider.extend IMS::LTI::Extensions::OutcomeData::ToolProvider
11
+ #
12
+ # If the tool was launch as an outcome service and it supports the data extension
13
+ # you can POST a score to the TC.
14
+ # The POST calls all return an OutcomeResponse object which can be used to
15
+ # handle the response appropriately.
16
+ #
17
+ # # post the score to the TC, score should be a float >= 0.0 and <= 1.0
18
+ # # this returns an OutcomeResponse object
19
+ # if provider.accepts_outcome_text?
20
+ # response = provider.post_replace_result_with_data!(score, "text" => "submission text")
21
+ # else
22
+ # response = provider.post_replace_result!(score)
23
+ # end
24
+ # if response.success?
25
+ # # grade write worked
26
+ # elsif response.processing?
27
+ # elsif response.unsupported?
28
+ # else
29
+ # # failed
30
+ # end
31
+ module OutcomeData
32
+
33
+ #IMS::LTI::Extensions::OutcomeData::ToolProvider
34
+ module Base
35
+ def outcome_request_extensions
36
+ super + [IMS::LTI::Extensions::OutcomeData::OutcomeRequest]
37
+ end
38
+ end
39
+
40
+ module ToolProvider
41
+ include IMS::LTI::Extensions::ExtensionBase
42
+ include Base
43
+
44
+ # a list of the supported outcome data types
45
+ def accepted_outcome_types
46
+ return @outcome_types if @outcome_types
47
+ @outcome_types = []
48
+ if val = @ext_params["outcome_data_values_accepted"]
49
+ @outcome_types = val.split(',')
50
+ end
51
+
52
+ @outcome_types
53
+ end
54
+
55
+ # check if the outcome data extension is supported
56
+ def accepts_outcome_data?
57
+ !!@ext_params["outcome_data_values_accepted"]
58
+ end
59
+
60
+ # check if the consumer accepts text as outcome data
61
+ def accepts_outcome_text?
62
+ accepted_outcome_types.member?("text")
63
+ end
64
+
65
+ # check if the consumer accepts a url as outcome data
66
+ def accepts_outcome_url?
67
+ accepted_outcome_types.member?("url")
68
+ end
69
+
70
+ # POSTs the given score to the Tool Consumer with a replaceResult and
71
+ # adds the specified data. The data hash can have the keys "text", "cdata_text", or "url"
72
+ #
73
+ # If both cdata_text and text are sent, cdata_text will be used
74
+ #
75
+ # Creates a new OutcomeRequest object and stores it in @outcome_requests
76
+ #
77
+ # @return [OutcomeResponse] the response from the Tool Consumer
78
+ def post_replace_result_with_data!(score, data={})
79
+ req = new_request
80
+ if data["cdata_text"]
81
+ req.outcome_cdata_text = data["cdata_text"]
82
+ elsif data["text"]
83
+ req.outcome_text = data["text"]
84
+ end
85
+ req.outcome_url = data["url"] if data["url"]
86
+ req.post_replace_result!(score)
87
+ end
88
+
89
+ end
90
+
91
+ module ToolConsumer
92
+ include IMS::LTI::Extensions::ExtensionBase
93
+ include Base
94
+
95
+ OUTCOME_DATA_TYPES = %w{text url}
96
+
97
+ # a list of the outcome data types accepted, currently only 'url' and
98
+ # 'text' are valid
99
+ #
100
+ # tc.outcome_data_values_accepted(['url', 'text'])
101
+ # tc.outcome_data_valued_accepted("url,text")
102
+ def outcome_data_values_accepted=(val)
103
+ if val.is_a? Array
104
+ val = val.join(',')
105
+ end
106
+
107
+ set_ext_param('outcome_data_values_accepted', val)
108
+ end
109
+
110
+ # a comma-separated string of the supported outcome data types
111
+ def outcome_data_values_accepted
112
+ get_ext_param('outcome_data_values_accepted')
113
+ end
114
+
115
+ # convenience method for setting support for all current outcome data types
116
+ def support_outcome_data!
117
+ self.outcome_data_values_accepted = OUTCOME_DATA_TYPES
118
+ end
119
+ end
120
+
121
+ module OutcomeRequest
122
+ include IMS::LTI::Extensions::ExtensionBase
123
+ include Base
124
+
125
+ attr_accessor :outcome_text, :outcome_url, :outcome_cdata_text
126
+
127
+ def result_values(node)
128
+ super
129
+ if @outcome_text || @outcome_url || @outcome_cdata_text
130
+ node.resultData do |res_data|
131
+ if @outcome_cdata_text
132
+ res_data.text {
133
+ res_data.cdata! @outcome_cdata_text
134
+ }
135
+ elsif @outcome_text
136
+ res_data.text @outcome_text
137
+ end
138
+ res_data.url @outcome_url if @outcome_url
139
+ end
140
+ end
141
+ end
142
+
143
+ def has_result_data?
144
+ !!@outcome_text || !!@outcome_url || super
145
+ end
146
+
147
+ def extention_process_xml(doc)
148
+ super
149
+ @outcome_text = doc.get_text("//resultRecord/result/resultData/text")
150
+ @outcome_url = doc.get_text("//resultRecord/result/resultData/url")
151
+ end
152
+ end
153
+
154
+ end
155
+ end
156
+ end