lti2 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,230 @@
1
+ require "test/unit"
2
+ require "rubygems"
3
+ require 'json'
4
+ require 'jsonpath'
5
+
6
+ require File.expand_path('../../lib/lti2_commons/json_wrapper', __FILE__)
7
+ require File.expand_path('../../lib/lti2_commons/utils', __FILE__)
8
+
9
+ include Lti2Commons
10
+ include Utils
11
+
12
+ class TestJsonWrapper < Test::Unit::TestCase
13
+ def setup
14
+ @json_str =
15
+ <<PROXY
16
+ {
17
+ "@context" : [
18
+ "http://www.imsglobal.org/imspurl/lti/v2/ctx/ToolProxy",
19
+ "http://purl.org/blackboard/ctx/v1/iconStyle"
20
+ ],
21
+ "@type" : "ToolProxy",
22
+ "@id" : "http://fabericious..com/ToolProxy/869e5ce5-214c-4e85-86c6-b99e8458a592",
23
+ "lti_version" : "LTI-2p0",
24
+ "tool_proxy_guid" : "869e5ce5-214c-4e85-86c6-b99e8458a592",
25
+ "tool_consumer_profile" : "http://lms.example.com/profile/b6ffa601-ce1d-4549-9ccf-145670a964d4",
26
+ "tool_profile" : {
27
+ "lti_version" : "LTI-2p0",
28
+ "product_instance" : {
29
+ "guid" : "fd75124a-140e-470f-944c-114d2d92bb40",
30
+ "product_info" : {
31
+ "product_name" : {
32
+ "default_value" : "Acme Assessments",
33
+ "key" : "tool.name"
34
+ },
35
+ "description" : {
36
+ "default_value" : "Acme Assessments provide an interactive test format.",
37
+ "key" : "tool.description"
38
+ },
39
+ "product_version" : "10.3",
40
+ "technical_description" : {
41
+ "default_value" : "Support provided for all LTI 1 extensions as well as LTI 2",
42
+ "key" : "tool.technical"
43
+ },
44
+ "product_family" : {
45
+ "code" : "assessment-tool",
46
+ "vendor" : {
47
+ "code" : "acme.com",
48
+ "name" : {
49
+ "default_value" : "Acme",
50
+ "key" : "tool.vendor.name"
51
+ },
52
+ "description" : {
53
+ "default_value" : "Acme is a leading provider of interactive tools for education",
54
+ "key" : "tool.vendor.description"
55
+ },
56
+ "website" : "http://acme.example.com",
57
+ "timestamp" : "2012-04-05T09:08:16-04:00",
58
+ "contact" : {
59
+ "email" : "info@example.com"
60
+ }
61
+ }
62
+ }
63
+ },
64
+ "support" : {
65
+ "email" : "helpdesk@example.com"
66
+ },
67
+ "service_provider" : {
68
+ "guid" : "18e7ea50-3d6d-4f6b-aff2-ed3ab577716c",
69
+ "provider_name" : {
70
+ "default_value" : "Acme Hosting",
71
+ "key" : "service_provider.name"
72
+ },
73
+ "description" : {
74
+ "default_value" : "Provider of high performance managed hosting environments",
75
+ "key" : "service_provider.description"
76
+ },
77
+ "support" : {
78
+ "email" : "support@acme-hosting.example.com"
79
+ },
80
+ "timestamp" : "2012-04-05T09:08:16-04:00"
81
+ }
82
+ },
83
+ "base_url_choice" : [
84
+ { "default_base_url" : "http://acme.example.com",
85
+ "secure_base_url" : "https://acme.example.com",
86
+ "selector" : {
87
+ "applies_to" : [
88
+ "IconEndpoint",
89
+ "MessageHandler"
90
+ ]
91
+ }
92
+ }
93
+ ],
94
+ "resource_handler" : [
95
+ {
96
+ "resource_type": "urn:lti:ResourceType:{fabericious.lti.org}/handler/launchRequest",
97
+ "name" : {
98
+ "default_value" : "Acme Assessment",
99
+ "key" : "assessment.resource.name"
100
+ },
101
+ "description" : {
102
+ "default_value" : "An interactive assessment using the Acme scale.",
103
+ "key" : "assessment.resource.description"
104
+ },
105
+ "message" : [{
106
+ "message_type" : "basic-lti-launch-request",
107
+ "path" : "/handler/launchRequest",
108
+ "capability" : [
109
+ "Result.autocreate",
110
+ "Result.sourcedGUID"
111
+ ],
112
+ "parameter" : [
113
+ { "name" : "result_id",
114
+ "variable" : "$Result.sourcedGUID"
115
+ },
116
+ { "name" : "discipline",
117
+ "fixed" : "chemistry"
118
+ }
119
+ ]
120
+ }],
121
+ "icon_info" : [
122
+ {
123
+ "default_location" : {
124
+ "path" : "/images/bb/en/icon.png"
125
+ },
126
+ "key" : "iconStyle.default.path"
127
+ },
128
+ { "icon_style" : "BbListElementIcon",
129
+ "default_location" : {
130
+ "path" : "/images/bb/en/listElement.png"
131
+ },
132
+ "key" : "iconStyle.bb.listElement.path"
133
+ },
134
+ { "icon_style" : "BbPushButtonIcon",
135
+ "default_location" : {
136
+ "path" : "images/bb/en/pushButton.png"
137
+ },
138
+ "key" : "iconStyle.bb.pushButton.path"
139
+ }
140
+ ]
141
+ }
142
+ ]
143
+ },
144
+ "security_contract" : {
145
+ "shared_secret" : "ThisIsASecret!",
146
+ "tool_service" : [
147
+ { "@type" : "RestServiceProfile",
148
+ "service" : "http://lms.example.com/profile/b6ffa601-ce1d-4549-9ccf-145670a964d4#ToolProxy.collection",
149
+ "action" : "POST"
150
+ },
151
+ { "@type" : "RestServiceProfile",
152
+ "service" : "http://lms.example.com/profile/b6ffa601-ce1d-4549-9ccf-145670a964d4#ToolProxy.item",
153
+ "action" : [
154
+ "GET",
155
+ "PUT"
156
+ ]
157
+ },
158
+ { "@type" : "RestService",
159
+ "service" : "http://lms.example.com/profile/b6ffa601-ce1d-4549-9ccf-145670a964d4#Result.item",
160
+ "action" : [
161
+ "GET",
162
+ "PUT"
163
+ ]
164
+ }
165
+ ]
166
+ }
167
+ }
168
+ PROXY
169
+
170
+ @json_wrapper = JsonWrapper.new @json_str
171
+ end
172
+
173
+ def test_constuctor
174
+ assert_not_nil @json_wrapper.root
175
+ end
176
+
177
+ def test_at
178
+ assert_equal ["869e5ce5-214c-4e85-86c6-b99e8458a592"], @json_wrapper.at('tool_proxy_guid')
179
+ end
180
+
181
+ def test_first_at
182
+ assert_equal "869e5ce5-214c-4e85-86c6-b99e8458a592", @json_wrapper.first_at('tool_proxy_guid')
183
+ assert_nil @json_wrapper.first_at('asdf')
184
+ end
185
+
186
+ def test_each_leaf
187
+ counter = 0
188
+ @json_wrapper.each_leaf { |node| counter += 1 }
189
+ assert_equal 70, counter
190
+ end
191
+
192
+ def test_deep_copy
193
+ new_json_wrapper = @json_wrapper.deep_copy
194
+ assert_not_equal new_json_wrapper.object_id, @json_wrapper.object_id
195
+ assert_equal "869e5ce5-214c-4e85-86c6-b99e8458a592", new_json_wrapper.first_at('tool_proxy_guid')
196
+ end
197
+
198
+ def test_search
199
+ assert_equal "/handler/launchRequest",
200
+ ( @json_wrapper.search 'tool_profile.resource_handler..message',
201
+ { 'message_type' => "basic-lti-launch-request" },
202
+ "path")
203
+ assert_nil @json_wrapper.search 'tool_profile.resource_handler..message',
204
+ { 'message_type' => "basic-lti-launch-request" },
205
+ "xxxx"
206
+ assert_nil @json_wrapper.search 'tool_profile.resource_handler..message',
207
+ { 'message_type' => "xxx" },
208
+ "path"
209
+ end
210
+
211
+ def test_select
212
+ assert_equal "http://acme.example.com",
213
+ @json_wrapper.select('tool_profile.base_url_choice',
214
+ "selector.applies_to", "MessageHandler", 'default_base_url')
215
+ assert_equal nil, @json_wrapper.select('tool_profile.xxxxx',
216
+ "selector.applies_to", "MessageHandler", 'default_base_url')
217
+ end
218
+
219
+ def test_each_deep_with_substitution
220
+ hash = {"root" => {"child1" => "{value}1", "child2" => {"grandchild1" => "{grand}{value}1"}, "child3" => "{value}3"}}
221
+ json_hash = JsonWrapper.new hash
222
+ subst_hash = {'value' => 'VLU', 'great' => 'GRT', 'grand' => 'GRND'}
223
+ json_hash.substitute_text_in_all_nodes '{', '}', subst_hash
224
+ assert_equal "VLU1", json_hash.first_at('$..child1')
225
+ assert_equal "GRNDVLU1", json_hash.first_at('$..grandchild1')
226
+ end
227
+
228
+ ARGV = ['', "--name", "test_select"]
229
+ # Test::Unit::AutoRunner.run(false, nil, ARGV)
230
+ end
@@ -0,0 +1,143 @@
1
+ require 'test/unit'
2
+ require File.expand_path('../../lib/lti2_commons/message_support', __FILE__)
3
+ require File.expand_path('../../lib/lti2_commons/cache', __FILE__)
4
+ require File.expand_path('../../lib/lti2_commons/utils', __FILE__)
5
+ require File.expand_path('../../lib/lti2_commons/signer', __FILE__)
6
+ require File.expand_path('../../lib/lti2_commons/oauth_request', __FILE__)
7
+
8
+ require 'rack'
9
+ require 'oauth'
10
+
11
+ include Lti2Commons
12
+ include Signer
13
+ include Utils
14
+ include MessageSupport
15
+ include OAuth::RequestProxy
16
+
17
+ class TestOAuthRequest < Test::Unit::TestCase
18
+ def setup
19
+ @launch_url = 'http://localhost:3000/tenants/3'
20
+ # @launch_url = 'http://vst-bc.com/tenants/3/books'
21
+ @http_method = 'post'
22
+ @consumer_key = '12345'
23
+ @consumer_secret = 'secret'
24
+ @params = {'oauth_consumer_key'=>'12345', 'oauth_signature_method'=> "HMAC-SHA1", 'user_id'=>'jt'}
25
+ @oauth_request = OAuth::OAuthProxy::OAuthRequest.new \
26
+ "method" => @http_method,
27
+ "uri" => @launch_url,
28
+ "parameters" => @params
29
+ @nonce_cache = Lti2Commons::Cache.new :ttl => 300
30
+ end
31
+
32
+ def create_stale_request(minutes_stale)
33
+ now = Time::now
34
+ stale_time = now + (minutes_stale*60.0)
35
+ # puts "Now: #{now} Stale_time: #{stale_time}]"
36
+ params = @params.dup
37
+ params['oauth_timestamp'] = stale_time.to_i
38
+ request = Lti2Commons::Signer.create_signed_request @launch_url, @http_method, @consumer_key, @consumer_secret, params
39
+ # dump_oauth_parameters request, "stale create"
40
+ request
41
+ end
42
+
43
+ def dump_oauth_parameters(request, label=nil)
44
+ puts ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
45
+ if label
46
+ puts "*** #{label}"
47
+ end
48
+ puts "non_oauth_parameters: #{request.non_oauth_parameters.inspect}"
49
+ puts "oauth_header: #{request.oauth_header}"
50
+ puts "oauth_parameters: #{request.oauth_parameters.inspect}"
51
+ puts "signature_base_string: #{request.signature_base_string}"
52
+ puts "signed? #{request.signed?}"
53
+ puts "signed_uri: #{request.signed_uri}"
54
+ puts "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
55
+ end
56
+
57
+ def test_oauth_request
58
+ request = Signer.create_signed_request @launch_url, @http_method, @consumer_key, @consumer_secret, @params
59
+ dump_oauth_parameters(request, 'postsigning')
60
+ end
61
+
62
+ def test_valid_request
63
+ request = Signer.create_signed_request @launch_url, @http_method, @consumer_key, @consumer_secret, @params
64
+ assert_not_nil request
65
+ assert_equal true, request.verify_signature?(@consumer_secret, @nonce_cache)
66
+ end
67
+
68
+ def test_create_signed_request_simple_post
69
+ request = Lti2Commons::Signer.create_signed_request @launch_url, @http_method, @consumer_key, @consumer_secret, @params
70
+ assert_not_nil request
71
+ signature_base_string = request.signature_base_string
72
+ assert_equal "POST&http%3A%2F%2Flocalhost%3A3000%2Ftenants%2F3&basiclti_submit%3DPress%2520t", signature_base_string[0..77]
73
+ end
74
+
75
+ def test_create_signed_request_get
76
+ request = Lti2Commons::Signer.create_signed_request @launch_url, 'get', @consumer_key, @consumer_secret, @params
77
+ assert_not_nil request
78
+ signature_base_string = request.signature_base_string
79
+ assert_equal "GET&http%3A%2F%2Flocalhost%3A3000%2Ftenants%2F3&basiclti_submit%3DPress%2520t", signature_base_string[0..76]
80
+ end
81
+
82
+ def test_create_signed_request_put
83
+ request = Lti2Commons::Signer.create_signed_request @launch_url, 'put', @consumer_key, @consumer_secret, @params
84
+ assert_not_nil request
85
+ signature_base_string = request.signature_base_string
86
+ assert_equal "PUT&http%3A%2F%2Flocalhost%3A3000%2Ftenants%2F3&basiclti_submit%3DPress%2520t", signature_base_string[0..76]
87
+ end
88
+
89
+ def test_lti_message_body_from_signed_request
90
+ request = Signer.create_signed_request @launch_url, 'put', @consumer_key, @consumer_secret, @params
91
+ assert_not_nil request
92
+ assert_not_nil MessageSupport.create_lti_message_body_from_signed_request request
93
+ puts MessageSupport.create_lti_message_body_from_signed_request request
94
+ end
95
+
96
+ def test_lti_message_body
97
+ assert_not_nil MessageSupport.create_lti_message_body @launch_url, @params
98
+ puts MessageSupport.create_lti_message_body @launch_url, @params
99
+ end
100
+
101
+ def test_break_signature
102
+ request = Lti2Commons::Signer.create_signed_request @launch_url, @http_method, @consumer_key, @consumer_secret, @params
103
+ request.parameters['oauth_signature'] = "asdf"
104
+ assert_equal false, (request.verify_signature? @consumer_secret, @nonce_cache)
105
+ end
106
+
107
+ def test_exception_on_signature_error
108
+ request = Lti2Commons::Signer.create_signed_request @launch_url, @http_method, @consumer_key, @consumer_secret, @params
109
+ request.parameters['oauth_signature'] = "asdf"
110
+ begin
111
+ request.verify_signature? @consumer_secret, @nonce_cache, false
112
+ fail "Exception expected here"
113
+ rescue
114
+ # expected
115
+ end
116
+ end
117
+
118
+ def test_timeout_within_margin
119
+ request = create_stale_request 2
120
+ assert_equal true, (request.verify_signature? @consumer_secret, @nonce_cache)
121
+ request = create_stale_request(-2)
122
+ assert_equal true, (request.verify_signature? @consumer_secret, @nonce_cache)
123
+ end
124
+
125
+ def test_timeout_error
126
+ request = create_stale_request 10
127
+ assert_equal false, (request.verify_signature? @consumer_secret, @nonce_cache)
128
+ request = create_stale_request(-10)
129
+ assert_equal false, (request.verify_signature? @consumer_secret, @nonce_cache)
130
+ end
131
+
132
+ def test_duplicate_nonce
133
+ params = @params.dup
134
+ params['oauth_nonce'] = "1234"
135
+ request = Lti2Commons::Signer.create_signed_request @launch_url, @http_method, @consumer_key, @consumer_secret, params
136
+ assert_equal true, (request.verify_signature? @consumer_secret, @nonce_cache)
137
+ request = Lti2Commons::Signer.create_signed_request @launch_url, @http_method, @consumer_key, @consumer_secret, params
138
+ assert_equal false, (request.verify_signature? @consumer_secret, @nonce_cache)
139
+ end
140
+
141
+ ARGV = ['', "--name", "test_duplicate_nonce"]
142
+ Test::Unit::AutoRunner.run(false, nil, ARGV)
143
+ end
@@ -0,0 +1,71 @@
1
+ require 'test/unit'
2
+ require File.expand_path('../../lib/lti2_commons/substitution_support', __FILE__)
3
+
4
+ include Lti2Commons
5
+ include SubstitutionSupport
6
+
7
+
8
+ class TestObject
9
+ attr_accessor :user_id, :name
10
+
11
+ def initialize
12
+ @user_id = 'jt'
13
+ @name = "john.thomas"
14
+ @code = @name.reverse
15
+ end
16
+
17
+ def to_s
18
+ @user_id
19
+ end
20
+ end
21
+
22
+ class TestSubstitutionSupport < Test::Unit::TestCase
23
+ def setup
24
+ @resolver = Resolver.new
25
+ @resolver.add_resolver "user", {'name' => "jt", 'id' => "1234"}
26
+ @resolver.add_resolver "user", {'name' => "jt", 'id' => "9876", 'sport' => 'judo'}
27
+ @resolver.add_resolver "course", lambda { |x| "COURSE-#{x}"}
28
+ @resolver.add_resolver "resource", self.method(:resource_transform_sample)
29
+ @resolver.add_resolver "testobject", TestObject.new
30
+ @resolver.add_resolver "testnest", {'nestlevel' => 'inner'}
31
+ @resolver.add_resolver "*", {'ip'=> '192.168.2.177'}
32
+ end
33
+
34
+ def resource_transform_sample(name)
35
+ name.reverse
36
+ end
37
+
38
+ def test_add_hash
39
+ assert_equal "jt", @resolver.resolve('user.name')
40
+ assert_equal "judo", @resolver.resolve('user.sport')
41
+ assert_equal "192.168.2.177", @resolver.resolve('user.ip')
42
+ assert_equal "192.168.2.177", @resolver.resolve('foo.ip')
43
+ assert_equal "user.notfound", @resolver.resolve('user.notfound')
44
+ end
45
+
46
+ def test_add_block
47
+ assert_equal "COURSE-anything", @resolver.resolve('course.anything')
48
+ end
49
+
50
+ def test_internal_method
51
+ assert_equal "fdsa", @resolver.resolve('resource.asdf')
52
+ end
53
+
54
+ def test_testobject
55
+ assert_equal "john.thomas", @resolver.resolve('testobject.name')
56
+ end
57
+
58
+ def test_nested_resolver
59
+ outer_resolver = Resolver.new
60
+ outer_resolver.add_resolver "testnest", {'nestlevel' => 'outer'}
61
+ outer_resolver.add_resolver "inner", @resolver
62
+
63
+ assert_equal "inner", @resolver.resolve('testnest.nestlevel')
64
+ assert_equal "outer", outer_resolver.resolve('testnest.nestlevel')
65
+ assert_equal "jt", outer_resolver.resolve('user.name')
66
+ assert_equal "COURSE-anything", outer_resolver.resolve('course.anything')
67
+ end
68
+
69
+ ARGV = ['', "--name", "test_nested_resolver"]
70
+ # Test::Unit::AutoRunner.run(false, nil, ARGV)
71
+ end
@@ -0,0 +1,15 @@
1
+ require File.expand_path('../../lib/lti2_commons/wire_log', __FILE__)
2
+ require File.expand_path('../../lib/lti2_commons/mock_request', __FILE__)
3
+
4
+ wl = Lti2Commons::WireLogSupport::WireLog.new 'test', 'test.txt'
5
+
6
+ wl.clear_log
7
+ wl.log 'test'
8
+ wl.log 'tube'
9
+
10
+ wl.log <<aa
11
+ hello,
12
+ how are you?
13
+ aa
14
+
15
+ puts 'done'