-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathtool_provider.rb
166 lines (134 loc) · 4.42 KB
/
tool_provider.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
require 'sinatra'
require 'ims/lti'
# must include the oauth proxy object
require 'oauth/request_proxy/rack_request'
enable :sessions
set :protection, :except => :frame_options
get '/' do
erb :index
end
# the consumer keys/secrets
$oauth_creds = {"test" => "secret", "testing" => "supersecret"}
def show_error(message)
@message = message
end
def authorize!
if key = params['oauth_consumer_key']
if secret = $oauth_creds[key]
@tp = IMS::LTI::ToolProvider.new(key, secret, params)
else
@tp = IMS::LTI::ToolProvider.new(nil, nil, params)
@tp.lti_msg = "Your consumer didn't use a recognized key."
@tp.lti_errorlog = "You did it wrong!"
show_error "Consumer key wasn't recognized"
return false
end
else
show_error "No consumer key"
return false
end
if !@tp.valid_request?(request)
show_error "The OAuth signature was invalid"
return false
end
if Time.now.utc.to_i - @tp.request_oauth_timestamp.to_i > 60*60
show_error "Your request is too old."
return false
end
# this isn't actually checking anything like it should, just want people
# implementing real tools to be aware they need to check the nonce
if was_nonce_used_in_last_x_minutes?(@tp.request_oauth_nonce, 60)
show_error "Why are you reusing the nonce?"
return false
end
@username = @tp.username("Dude")
return true
end
# The url for launching the tool
# It will verify the OAuth signature
post '/lti_tool' do
return erb :error unless authorize!
if @tp.outcome_service?
# It's a launch for grading
erb :assessment
else
# normal tool launch without grade write-back
signature = OAuth::Signature.build(request, :consumer_secret => @tp.consumer_secret)
@signature_base_string = signature.signature_base_string
@secret = signature.send(:secret)
@tp.lti_msg = "Sorry that tool was so boring"
erb :boring_tool
end
end
post '/signature_test' do
erb :proxy_setup
end
post '/proxy_launch' do
uri = URI.parse(params['launch_url'])
if uri.port == uri.default_port
host = uri.host
else
host = "#{uri.host}:#{uri.port}"
end
consumer = OAuth::Consumer.new(params['lti']['oauth_consumer_key'], params['oauth_consumer_secret'], {
:site => "#{uri.scheme}://#{host}",
:signature_method => "HMAC-SHA1"
})
path = uri.path
path = '/' if path.empty?
@lti_params = params['lti'].clone
if uri.query != nil
CGI.parse(uri.query).each do |query_key, query_values|
unless @lti_params[query_key]
@lti_params[query_key] = query_values.first
end
end
end
path = uri.path
path = '/' if path.empty?
proxied_request = consumer.send(:create_http_request, :post, path, @lti_params)
signature = OAuth::Signature.build(proxied_request, :uri => params['launch_url'], :consumer_secret => params['oauth_consumer_secret'])
@signature_base_string = signature.signature_base_string
@secret = signature.send(:secret)
@oauth_signature = signature.signature
erb :proxy_launch
end
# post the assessment results
post '/assessment' do
launch_params = request['launch_params']
if launch_params
key = launch_params['oauth_consumer_key']
else
show_error "The tool never launched"
return erb :error
end
@tp = IMS::LTI::ToolProvider.new(key, $oauth_creds[key], launch_params)
if !@tp.outcome_service?
show_error "This tool wasn't lunched as an outcome service"
return erb :error
end
# post the given score to the TC
score = (params['score'] != '' ? params['score'] : nil)
res = @tp.post_replace_result!(params['score'])
if res.success?
@score = params['score']
@tp.lti_msg = "Message shown when arriving back at Tool Consumer."
erb :assessment_finished
else
@tp.lti_errormsg = "The Tool Consumer failed to add the score."
show_error "Your score was not recorded: #{res.description}"
return erb :error
end
end
get '/tool_config.xml' do
host = request.scheme + "://" + request.host_with_port
url = (params['signature_proxy_test'] ? host + "/signature_test" : host + "/lti_tool")
tc = IMS::LTI::ToolConfig.new(:title => "Example Sinatra Tool Provider", :launch_url => url)
tc.description = "This example LTI Tool Provider supports LIS Outcome pass-back."
headers 'Content-Type' => 'text/xml'
tc.to_xml(:indent => 2)
end
def was_nonce_used_in_last_x_minutes?(nonce, minutes=60)
# some kind of caching solution or something to keep a short-term memory of used nonces
false
end