dce_lti 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +103 -3
- data/db/migrate/20150206152909_add_sessions_table.rb +12 -0
- data/lib/dce_lti/controller_methods.rb +5 -0
- data/lib/dce_lti/engine.rb +16 -2
- data/lib/dce_lti/middleware/cookie_shim.rb +36 -0
- data/lib/dce_lti/middleware/cookieless_sessions.rb +79 -0
- data/lib/dce_lti/redirect_to_helper.rb +25 -0
- data/lib/dce_lti/version.rb +1 -1
- data/lib/dce_lti.rb +3 -0
- data/lib/tasks/dce_lti_tasks.rake +7 -0
- data/spec/dummy/config/initializers/dce_lti_config.rb +15 -1
- data/spec/dummy/log/development.log +66 -0
- data/spec/dummy/log/test.log +10190 -0
- data/spec/middleware/dce_lti/cookie_shim_spec.rb +58 -0
- data/spec/middleware/dce_lti/cookieless_sessions_spec.rb +71 -0
- data/spec/support/dce_lti/middleware_helpers.rb +7 -0
- metadata +41 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a4dcee8b8e745797874479ffb1d75eb6e468bd16
|
4
|
+
data.tar.gz: 844b534a13367e71716209aaf8818a74ff19c9aa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: edebe996ebdddee116b497723a5958fd494027adfc4292e36eca9880737d84d6bb8ab12da1615be1605be6445fbc66a4b5737d6b2c4521d232b012c197ac5ddd
|
7
|
+
data.tar.gz: 60aec8cf3c206bf68891a781a125f8c5d66348dc734989a88a5d1e9a4d69b27152321e57ba9f1b92957ae87c3c0a92532faf62ed971ebdfd7fb05f4b21351046
|
data/README.md
CHANGED
@@ -10,11 +10,18 @@ the [IMS::LTI gem](https://github.com/instructure/ims-lti).
|
|
10
10
|
|
11
11
|
## Getting started
|
12
12
|
|
13
|
-
Add
|
13
|
+
Add these gems to your gemfile:
|
14
14
|
|
15
15
|
gem 'dce_lti'
|
16
|
+
gem 'activerecord-session_store', '~> 0.1.1''
|
16
17
|
|
17
|
-
|
18
|
+
Update (or create) `config/initializers/session_store.rb` and ensure it contains:
|
19
|
+
|
20
|
+
Rails.application.config.session_store :active_record_store, key: '_your_app_session', expire_after: 60.minutes
|
21
|
+
|
22
|
+
Where `_your_app_session` is your application's session key.
|
23
|
+
|
24
|
+
Bundle, install and then run migrations:
|
18
25
|
|
19
26
|
bundle
|
20
27
|
rake dce_lti:install
|
@@ -36,6 +43,10 @@ you have a valid LTI-provided user in `current_user`, thusly:
|
|
36
43
|
end
|
37
44
|
end
|
38
45
|
|
46
|
+
That's it! You'll need to configure to fit your use case, but you've got the
|
47
|
+
basics of LTI authentication (including experimental cookieless sessioning, see
|
48
|
+
below) working already.
|
49
|
+
|
39
50
|
## Configuration
|
40
51
|
|
41
52
|
The generated config looks something like (commented defaults omitted):
|
@@ -105,7 +116,7 @@ See
|
|
105
116
|
and other classes/modules under the IMS::LTI::Extensions hierarchy for further
|
106
117
|
options.
|
107
118
|
|
108
|
-
##
|
119
|
+
## Other
|
109
120
|
|
110
121
|
The `DceLti` provided controllers inherit from `ApplicationController` as
|
111
122
|
defined in your application.
|
@@ -138,6 +149,95 @@ rendered. You can customize this output by creating a file named
|
|
138
149
|
`app/views/dce_lti/sessions/invalid.html.erb`, per the default engine view
|
139
150
|
resolution behavior.
|
140
151
|
|
152
|
+
### Cookieless Sessions - Experimental
|
153
|
+
|
154
|
+
If you're running your LTI app on a domain different than your LMS, it will not
|
155
|
+
work in recent Safari browers. This is because [Safari blocks third party
|
156
|
+
cookies set in an iframe by
|
157
|
+
default](https://support.apple.com/kb/PH19214?locale=en_US). Mozilla has hinted
|
158
|
+
at implementing this default as well, so the days of setting a cookie in an
|
159
|
+
iframe and expecting it to work are probably numbered. Thanks, pervasive ad
|
160
|
+
networks!
|
161
|
+
|
162
|
+
There are a few options:
|
163
|
+
|
164
|
+
1. Run your LMS and LTI provider on the same domain. This isn't really doable
|
165
|
+
if you want to provide a tool useful to multiple consumers on multiple domains,
|
166
|
+
1. Only provide completely anonymous LTI tools,
|
167
|
+
1. Build single page javascript-driven apps,
|
168
|
+
1. Ask your users to enable third-party cookies,
|
169
|
+
1. Block users when you detect they don't support third-party cookies,
|
170
|
+
1. Persist the users session by including it in every link and form.
|
171
|
+
|
172
|
+
This engine implements the last option by detecting when a browser doesn't
|
173
|
+
accept cookies and then rewriting outgoing URLs and forms to include a session.
|
174
|
+
|
175
|
+
This behavior is disabled by default and requires minimal app-level changes:
|
176
|
+
|
177
|
+
1. Edit `config/initializers/dce_lti_config.rb`. Uncomment
|
178
|
+
`lti.enable_cookieless_sessions = false` and set it to `true`.
|
179
|
+
1. You must use database sessions as provided by `activerecord-session_store`,
|
180
|
+
which we install by default and you should've already configured.
|
181
|
+
1. The `redirect_after_successful_auth` path must include the session key and
|
182
|
+
id so we can pick it up if cookies aren't available (this is the default as
|
183
|
+
well).
|
184
|
+
|
185
|
+
Please report bugs to github issues, there are bound to be a few.
|
186
|
+
|
187
|
+
If a user supports cookies, we do basically nothing. We don't rewrite forms or
|
188
|
+
URLs and we use a cookied (and database-backed) session per the usual.
|
189
|
+
|
190
|
+
#### How cookieless sessions work
|
191
|
+
|
192
|
+
When a request comes in without a cookie but with the session key and ID, then
|
193
|
+
the `DceLti::Middleware::CookieShim` middleware "shims" it into the Rack
|
194
|
+
environment and the session information is restored by subsequent middleware.
|
195
|
+
|
196
|
+
When we detect that a user doesn't accept third-party cookies, we use
|
197
|
+
`Rack::Plastic` to rewrite forms and URLs to include the session key and id
|
198
|
+
from the `redirect_after_successful_auth` redirect. This happens in the
|
199
|
+
`DceLti::Middleware::CookielessSessions` middleware.
|
200
|
+
|
201
|
+
#### Known issues with cookieless sessions
|
202
|
+
|
203
|
+
* Even if your app works with cookieless sessions, other cookie sessioned
|
204
|
+
iframe'd apps won't: for instance the youtube javascript iframe API and many
|
205
|
+
other third-party javascript apps.
|
206
|
+
* We only rewrite URLs without a protocol and domain ('/posts/1') to match the
|
207
|
+
URLs emitted by rails by default. If you're manually inserting links to your
|
208
|
+
application that include the protocol and domain name
|
209
|
+
('http://example.com/posts/1'), the middleware doesn't catch it. This could
|
210
|
+
be fixed to be a bit smarter in the future.
|
211
|
+
* You will need to ensure that the session key and id tags along for ajax
|
212
|
+
requests to your LTI application.
|
213
|
+
|
214
|
+
#### Database session cleanup
|
215
|
+
|
216
|
+
Run the included `dce_lti:clean_sessions` rake task periodically to remove old
|
217
|
+
sessions - the default is 7 days, you can modify this with the
|
218
|
+
`OLDER_THAN_X_DAYS` environment variable, thusly:
|
219
|
+
|
220
|
+
OLDER_THAN_X_DAYS=14 rake dce_lti:clean_sessions
|
221
|
+
|
222
|
+
#### Database session hijacking for cookieless sessions
|
223
|
+
|
224
|
+
This is an issue, unfortunately. If a malicious user were able to get ahold of
|
225
|
+
a link in another user's LTI session (when that other user is under a
|
226
|
+
cookieless session) it'd contain a working session ID and could be exploited.
|
227
|
+
|
228
|
+
This can be mitigated several ways:
|
229
|
+
|
230
|
+
1. Deliver your LTI application over SSL to protect the transport layer. You
|
231
|
+
pretty much need to do this anyway, so this shouldn't be a big deal.
|
232
|
+
1. Expire your sessions by setting the `expire_after` option in
|
233
|
+
`config/initializers/session_store.rb` to a value short enough to not annoy
|
234
|
+
your users.
|
235
|
+
|
236
|
+
If you set `expire_after` too short, your users will get annoyed. If you set it
|
237
|
+
too long, the sessions will linger and increase the time the session is
|
238
|
+
vulnerable. We're looking into other ways of mitigating this as well - PRs
|
239
|
+
accepted!
|
240
|
+
|
141
241
|
### Nonce cleanup
|
142
242
|
|
143
243
|
You can clean up lti-related
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class AddSessionsTable < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :sessions do |t|
|
4
|
+
t.string :session_id, :null => false
|
5
|
+
t.text :data
|
6
|
+
t.timestamps
|
7
|
+
end
|
8
|
+
|
9
|
+
add_index :sessions, :session_id, :unique => true
|
10
|
+
add_index :sessions, :updated_at
|
11
|
+
end
|
12
|
+
end
|
data/lib/dce_lti/engine.rb
CHANGED
@@ -12,20 +12,34 @@ resource_link_id
|
|
12
12
|
resource_link_title
|
13
13
|
tool_consumer_instance_guid
|
14
14
|
launch_presentation_return_url
|
15
|
-
|
15
|
+
|
|
16
|
+
|
17
|
+
config.enable_cookieless_sessions = false
|
16
18
|
|
17
19
|
config.provider_title = (ENV['LTI_PROVIDER_TITLE'] || 'DCE LTI Provider')
|
18
20
|
config.provider_description = (ENV['LTI_PROVIDER_DESCRIPTION'] || 'A description of this')
|
19
21
|
|
20
22
|
config.redirect_after_successful_auth = -> (controller) do
|
21
|
-
Rails.application.
|
23
|
+
session_key_name = Rails.application.config.session_options[:key]
|
24
|
+
Rails.application.routes.url_helpers.root_path(session_key_name => controller.session.id)
|
22
25
|
end
|
26
|
+
|
23
27
|
config.tool_config_extensions = ->(*) {}
|
24
28
|
yield config
|
25
29
|
end
|
26
30
|
|
27
31
|
initializer 'dce_lti.load_helpers' do
|
28
32
|
ActionController::Base.send :include, ControllerMethods
|
33
|
+
ActionController::Base.send :include, RedirectToHelper
|
34
|
+
ActionController::Base.send :helper, RedirectToHelper
|
35
|
+
ApplicationController.skip_before_filter :verify_authenticity_token, if: :cookieless_session?
|
36
|
+
end
|
37
|
+
|
38
|
+
initializer 'dce_lti.add_middleware' do |app|
|
39
|
+
if config.enable_cookieless_sessions
|
40
|
+
app.middleware.insert_before ActionDispatch::Cookies, 'DceLti::Middleware::CookieShim'
|
41
|
+
app.middleware.use 'DceLti::Middleware::CookielessSessions'
|
42
|
+
end
|
29
43
|
end
|
30
44
|
|
31
45
|
isolate_namespace DceLti
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module DceLti
|
2
|
+
module Middleware
|
3
|
+
class CookieShim
|
4
|
+
def initialize(app)
|
5
|
+
@app = app
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
if env['HTTP_COOKIE'].to_s.strip.empty?
|
10
|
+
params = parse_query_string(env)
|
11
|
+
if params[session_key_name.to_s]
|
12
|
+
env['HTTP_COOKIE'] = "#{session_key_name}=#{params[session_key_name.to_s]};shimmed_cookie=1"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
@app.call(env)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def parse_query_string(env)
|
22
|
+
query_string = env['QUERY_STRING']
|
23
|
+
params = {}
|
24
|
+
query_string.split('&').each do |parameter|
|
25
|
+
(key, value) = parameter.split('=')
|
26
|
+
params[key] = value
|
27
|
+
end
|
28
|
+
params
|
29
|
+
end
|
30
|
+
|
31
|
+
def session_key_name
|
32
|
+
@session_key_name ||= Rails.application.config.session_options[:key]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'rack-plastic'
|
2
|
+
|
3
|
+
module DceLti
|
4
|
+
module Middleware
|
5
|
+
class CookielessSessions < Rack::Plastic
|
6
|
+
def change_nokogiri_doc(doc)
|
7
|
+
if no_cookies? || shimmed_cookie?
|
8
|
+
doc.css('a').each do |a|
|
9
|
+
href = a[:href]
|
10
|
+
|
11
|
+
next unless local_url?(href)
|
12
|
+
next if url_has_key_already?(href)
|
13
|
+
|
14
|
+
if href.match(/\?/)
|
15
|
+
a[:href] += "&#{session_key_name}=#{session_id}"
|
16
|
+
else
|
17
|
+
a[:href] += "?#{session_key_name}=#{session_id}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
doc.css('form').each do |form|
|
22
|
+
action = form[:action]
|
23
|
+
next unless local_url?(action)
|
24
|
+
next if url_has_key_already?(action)
|
25
|
+
|
26
|
+
# For PATCH, PUT, DELETE and POST, which allow
|
27
|
+
# params mixed in the action and the form.
|
28
|
+
if action.match(/\?/)
|
29
|
+
form[:action] += "&#{session_key_name}=#{session_id}"
|
30
|
+
else
|
31
|
+
form[:action] += "?#{session_key_name}=#{session_id}"
|
32
|
+
end
|
33
|
+
|
34
|
+
# For GET, oddly. GET method forms stomp all params encoded
|
35
|
+
# in the action
|
36
|
+
input_node = Nokogiri::XML::Node.new('input', doc)
|
37
|
+
input_node[:type] = 'hidden'
|
38
|
+
input_node[:name] = session_key_name
|
39
|
+
input_node[:value] = session_id
|
40
|
+
form.children.first.add_previous_sibling(
|
41
|
+
input_node
|
42
|
+
)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
doc
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def shimmed_cookie?
|
51
|
+
@p.request.env['HTTP_COOKIE'].to_s.strip.match(/shimmed_cookie/)
|
52
|
+
end
|
53
|
+
|
54
|
+
def no_cookies?
|
55
|
+
@p.request.env['HTTP_COOKIE'].to_s.strip.empty?
|
56
|
+
end
|
57
|
+
|
58
|
+
def local_url?(url)
|
59
|
+
! url.match(/\Ahttps?:\/\/|\/\//i)
|
60
|
+
end
|
61
|
+
|
62
|
+
def url_has_key_already?(url)
|
63
|
+
url.match(/#{session_key_name}/i)
|
64
|
+
end
|
65
|
+
|
66
|
+
def session_key_name
|
67
|
+
@session_key_name ||= Rails.application.config.session_options[:key]
|
68
|
+
end
|
69
|
+
|
70
|
+
def session
|
71
|
+
@p.request.env['rack.session']
|
72
|
+
end
|
73
|
+
|
74
|
+
def session_id
|
75
|
+
session.id
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module DceLti
|
2
|
+
module RedirectToHelper
|
3
|
+
def redirect_to(options)
|
4
|
+
session_key_name = Rails.application.config.session_options[:key]
|
5
|
+
if request.env.fetch('HTTP_COOKIE', '').match(/shimmed_cookie/) &&
|
6
|
+
(::DceLti::Engine.config.enable_cookieless_sessions)
|
7
|
+
case options
|
8
|
+
when Hash
|
9
|
+
options.merge!(session_key_name => session.id)
|
10
|
+
when String
|
11
|
+
if options.match(/\?/)
|
12
|
+
unless options.match(/#{session_key_name}/)
|
13
|
+
options += %Q|&#{session_key_name}=#{session.id}|
|
14
|
+
end
|
15
|
+
else
|
16
|
+
unless options.match(/#{session_key_name}/)
|
17
|
+
options += %Q|?#{session_key_name}=#{session.id}|
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
super(options)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/dce_lti/version.rb
CHANGED
data/lib/dce_lti.rb
CHANGED
@@ -31,4 +31,11 @@ engine provides.
|
|
31
31
|
task clean_nonces: :environment do
|
32
32
|
DceLti::Nonce.clean
|
33
33
|
end
|
34
|
+
|
35
|
+
desc 'Clean up old sessions'
|
36
|
+
task clean_sessions: :environment do
|
37
|
+
older_than = (ENV.fetch('OLDER_THAN_X_DAYS', 7)).to_i
|
38
|
+
session_klass = ActionDispatch::Session::ActiveRecordStore.session_class
|
39
|
+
session_klass.where('updated_at < ?', (Time.now - older_than.days)).delete_all
|
40
|
+
end
|
34
41
|
end
|
@@ -5,7 +5,21 @@ DceLti::Engine.setup do |lti|
|
|
5
5
|
#
|
6
6
|
# lti.provider_title = (ENV['LTI_PROVIDER_TITLE'] || 'DCE LTI Provider')
|
7
7
|
# lti.provider_description = (ENV['LTI_PROVIDER_DESCRIPTION'] || 'A description of this')
|
8
|
-
#
|
8
|
+
#
|
9
|
+
# Set this to `true` to enable the form and URL-rewriting behavior that
|
10
|
+
# allows for the creation of cookieless sessions. The default is `false`,
|
11
|
+
# meaning we don't attempt to use cookieless sessions when a cookie cannot be
|
12
|
+
# set - the session just fails.
|
13
|
+
#
|
14
|
+
# lti.enable_cookieless_sessions = false
|
15
|
+
#
|
16
|
+
# The default post-auth redirect includes the session key and session id so
|
17
|
+
# that we can instantiate a successful cookieless session if needed.
|
18
|
+
#
|
19
|
+
# lti.redirect_after_successful_auth = ->(controller) {
|
20
|
+
# session_key_name = Rails.application.config.session_options[:key]
|
21
|
+
# Rails.application.routes.url_helpers.root_path(session_key_name => controller.session.id)
|
22
|
+
# }
|
9
23
|
|
10
24
|
lti.consumer_secret = (ENV['LTI_CONSUMER_SECRET'] || 'consumer_secret')
|
11
25
|
lti.consumer_key = (ENV['LTI_CONSUMER_KEY'] || 'consumer_key')
|
@@ -2983,3 +2983,69 @@ Completed 200 OK in 3ms (Views: 0.4ms | ActiveRecord: 0.6ms)
|
|
2983
2983
|
[1m[36m (0.2ms)[0m [1mSELECT version FROM "schema_migrations"[0m
|
2984
2984
|
[1m[35m (1.2ms)[0m INSERT INTO "schema_migrations" (version) VALUES ('20141008172001')
|
2985
2985
|
[1m[36m (1.1ms)[0m [1mINSERT INTO "schema_migrations" (version) VALUES ('20141003180140')[0m
|
2986
|
+
[1m[36m (105.5ms)[0m [1mDROP DATABASE IF EXISTS "dce_lti_dummy_test"[0m
|
2987
|
+
[1m[35m (241.9ms)[0m CREATE DATABASE "dce_lti_dummy_test" ENCODING = 'utf8'
|
2988
|
+
[1m[36mSQL (0.1ms)[0m [1mCREATE EXTENSION IF NOT EXISTS "plpgsql"[0m
|
2989
|
+
[1m[35m (13.1ms)[0m CREATE TABLE "dce_lti_nonces" ("id" serial primary key, "nonce" character varying(255), "created_at" timestamp, "updated_at" timestamp)
|
2990
|
+
[1m[36m (4.4ms)[0m [1mCREATE UNIQUE INDEX "index_dce_lti_nonces_on_nonce" ON "dce_lti_nonces" USING btree ("nonce")[0m
|
2991
|
+
[1m[35m (6.6ms)[0m CREATE TABLE "dce_lti_users" ("id" serial primary key, "lti_user_id" character varying(255), "lis_person_contact_email_primary" character varying(255), "lis_person_name_family" character varying(255), "lis_person_name_full" character varying(255), "lis_person_name_given" character varying(255), "lis_person_sourcedid" character varying(255), "user_image" character varying(255), "roles" character varying(255)[] DEFAULT '{}', "created_at" timestamp, "updated_at" timestamp)
|
2992
|
+
[1m[36m (1.4ms)[0m [1mCREATE TABLE "schema_migrations" ("version" character varying(255) NOT NULL) [0m
|
2993
|
+
[1m[35m (3.4ms)[0m CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version")
|
2994
|
+
[1m[36m (0.3ms)[0m [1mSELECT version FROM "schema_migrations"[0m
|
2995
|
+
[1m[35m (1.2ms)[0m INSERT INTO "schema_migrations" (version) VALUES ('20141008172001')
|
2996
|
+
[1m[36m (1.0ms)[0m [1mINSERT INTO "schema_migrations" (version) VALUES ('20141003180140')[0m
|
2997
|
+
[1m[36m (205.8ms)[0m [1mDROP DATABASE IF EXISTS "dce_lti_dummy_test"[0m
|
2998
|
+
[1m[35m (227.7ms)[0m CREATE DATABASE "dce_lti_dummy_test" ENCODING = 'utf8'
|
2999
|
+
[1m[36mSQL (0.1ms)[0m [1mCREATE EXTENSION IF NOT EXISTS "plpgsql"[0m
|
3000
|
+
[1m[35m (7.5ms)[0m CREATE TABLE "dce_lti_nonces" ("id" serial primary key, "nonce" character varying(255), "created_at" timestamp, "updated_at" timestamp)
|
3001
|
+
[1m[36m (3.5ms)[0m [1mCREATE UNIQUE INDEX "index_dce_lti_nonces_on_nonce" ON "dce_lti_nonces" USING btree ("nonce")[0m
|
3002
|
+
[1m[35m (13.6ms)[0m CREATE TABLE "dce_lti_users" ("id" serial primary key, "lti_user_id" character varying(255), "lis_person_contact_email_primary" character varying(255), "lis_person_name_family" character varying(255), "lis_person_name_full" character varying(255), "lis_person_name_given" character varying(255), "lis_person_sourcedid" character varying(255), "user_image" character varying(255), "roles" character varying(255)[] DEFAULT '{}', "created_at" timestamp, "updated_at" timestamp)
|
3003
|
+
[1m[36m (2.7ms)[0m [1mCREATE TABLE "schema_migrations" ("version" character varying(255) NOT NULL) [0m
|
3004
|
+
[1m[35m (7.0ms)[0m CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version")
|
3005
|
+
[1m[36m (0.2ms)[0m [1mSELECT version FROM "schema_migrations"[0m
|
3006
|
+
[1m[35m (2.3ms)[0m INSERT INTO "schema_migrations" (version) VALUES ('20141008172001')
|
3007
|
+
[1m[36m (2.2ms)[0m [1mINSERT INTO "schema_migrations" (version) VALUES ('20141003180140')[0m
|
3008
|
+
[1m[36m (104.9ms)[0m [1mDROP DATABASE IF EXISTS "dce_lti_dummy_test"[0m
|
3009
|
+
[1m[35m (233.8ms)[0m CREATE DATABASE "dce_lti_dummy_test" ENCODING = 'utf8'
|
3010
|
+
[1m[36mSQL (0.2ms)[0m [1mCREATE EXTENSION IF NOT EXISTS "plpgsql"[0m
|
3011
|
+
[1m[35m (8.3ms)[0m CREATE TABLE "dce_lti_nonces" ("id" serial primary key, "nonce" character varying(255), "created_at" timestamp, "updated_at" timestamp)
|
3012
|
+
[1m[36m (3.3ms)[0m [1mCREATE UNIQUE INDEX "index_dce_lti_nonces_on_nonce" ON "dce_lti_nonces" USING btree ("nonce")[0m
|
3013
|
+
[1m[35m (7.5ms)[0m CREATE TABLE "dce_lti_users" ("id" serial primary key, "lti_user_id" character varying(255), "lis_person_contact_email_primary" character varying(255), "lis_person_name_family" character varying(255), "lis_person_name_full" character varying(255), "lis_person_name_given" character varying(255), "lis_person_sourcedid" character varying(255), "user_image" character varying(255), "roles" character varying(255)[] DEFAULT '{}', "created_at" timestamp, "updated_at" timestamp)
|
3014
|
+
[1m[36m (1.5ms)[0m [1mCREATE TABLE "schema_migrations" ("version" character varying(255) NOT NULL) [0m
|
3015
|
+
[1m[35m (7.3ms)[0m CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version")
|
3016
|
+
[1m[36m (0.5ms)[0m [1mSELECT version FROM "schema_migrations"[0m
|
3017
|
+
[1m[35m (2.4ms)[0m INSERT INTO "schema_migrations" (version) VALUES ('20141008172001')
|
3018
|
+
[1m[36m (2.6ms)[0m [1mINSERT INTO "schema_migrations" (version) VALUES ('20141003180140')[0m
|
3019
|
+
[1m[36m (205.3ms)[0m [1mDROP DATABASE IF EXISTS "dce_lti_dummy_test"[0m
|
3020
|
+
[1m[35m (229.4ms)[0m CREATE DATABASE "dce_lti_dummy_test" ENCODING = 'utf8'
|
3021
|
+
[1m[36mSQL (0.2ms)[0m [1mCREATE EXTENSION IF NOT EXISTS "plpgsql"[0m
|
3022
|
+
[1m[35m (16.2ms)[0m CREATE TABLE "dce_lti_nonces" ("id" serial primary key, "nonce" character varying(255), "created_at" timestamp, "updated_at" timestamp)
|
3023
|
+
[1m[36m (4.8ms)[0m [1mCREATE UNIQUE INDEX "index_dce_lti_nonces_on_nonce" ON "dce_lti_nonces" USING btree ("nonce")[0m
|
3024
|
+
[1m[35m (7.7ms)[0m CREATE TABLE "dce_lti_users" ("id" serial primary key, "lti_user_id" character varying(255), "lis_person_contact_email_primary" character varying(255), "lis_person_name_family" character varying(255), "lis_person_name_full" character varying(255), "lis_person_name_given" character varying(255), "lis_person_sourcedid" character varying(255), "user_image" character varying(255), "roles" character varying(255)[] DEFAULT '{}', "created_at" timestamp, "updated_at" timestamp)
|
3025
|
+
[1m[36m (1.3ms)[0m [1mCREATE TABLE "schema_migrations" ("version" character varying(255) NOT NULL) [0m
|
3026
|
+
[1m[35m (3.3ms)[0m CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version")
|
3027
|
+
[1m[36m (0.3ms)[0m [1mSELECT version FROM "schema_migrations"[0m
|
3028
|
+
[1m[35m (1.0ms)[0m INSERT INTO "schema_migrations" (version) VALUES ('20141008172001')
|
3029
|
+
[1m[36m (1.0ms)[0m [1mINSERT INTO "schema_migrations" (version) VALUES ('20141003180140')[0m
|
3030
|
+
[1m[36m (107.1ms)[0m [1mDROP DATABASE IF EXISTS "dce_lti_dummy_test"[0m
|
3031
|
+
[1m[35m (329.4ms)[0m CREATE DATABASE "dce_lti_dummy_test" ENCODING = 'utf8'
|
3032
|
+
[1m[36mSQL (0.2ms)[0m [1mCREATE EXTENSION IF NOT EXISTS "plpgsql"[0m
|
3033
|
+
[1m[35m (15.0ms)[0m CREATE TABLE "dce_lti_nonces" ("id" serial primary key, "nonce" character varying(255), "created_at" timestamp, "updated_at" timestamp)
|
3034
|
+
[1m[36m (7.1ms)[0m [1mCREATE UNIQUE INDEX "index_dce_lti_nonces_on_nonce" ON "dce_lti_nonces" USING btree ("nonce")[0m
|
3035
|
+
[1m[35m (15.3ms)[0m CREATE TABLE "dce_lti_users" ("id" serial primary key, "lti_user_id" character varying(255), "lis_person_contact_email_primary" character varying(255), "lis_person_name_family" character varying(255), "lis_person_name_full" character varying(255), "lis_person_name_given" character varying(255), "lis_person_sourcedid" character varying(255), "user_image" character varying(255), "roles" character varying(255)[] DEFAULT '{}', "created_at" timestamp, "updated_at" timestamp)
|
3036
|
+
[1m[36m (2.6ms)[0m [1mCREATE TABLE "schema_migrations" ("version" character varying(255) NOT NULL) [0m
|
3037
|
+
[1m[35m (7.6ms)[0m CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version")
|
3038
|
+
[1m[36m (0.3ms)[0m [1mSELECT version FROM "schema_migrations"[0m
|
3039
|
+
[1m[35m (2.6ms)[0m INSERT INTO "schema_migrations" (version) VALUES ('20141008172001')
|
3040
|
+
[1m[36m (2.3ms)[0m [1mINSERT INTO "schema_migrations" (version) VALUES ('20141003180140')[0m
|
3041
|
+
[1m[36m (106.1ms)[0m [1mDROP DATABASE IF EXISTS "dce_lti_dummy_test"[0m
|
3042
|
+
[1m[35m (233.4ms)[0m CREATE DATABASE "dce_lti_dummy_test" ENCODING = 'utf8'
|
3043
|
+
[1m[36mSQL (0.4ms)[0m [1mCREATE EXTENSION IF NOT EXISTS "plpgsql"[0m
|
3044
|
+
[1m[35m (10.2ms)[0m CREATE TABLE "dce_lti_nonces" ("id" serial primary key, "nonce" character varying(255), "created_at" timestamp, "updated_at" timestamp)
|
3045
|
+
[1m[36m (7.4ms)[0m [1mCREATE UNIQUE INDEX "index_dce_lti_nonces_on_nonce" ON "dce_lti_nonces" USING btree ("nonce")[0m
|
3046
|
+
[1m[35m (15.2ms)[0m CREATE TABLE "dce_lti_users" ("id" serial primary key, "lti_user_id" character varying(255), "lis_person_contact_email_primary" character varying(255), "lis_person_name_family" character varying(255), "lis_person_name_full" character varying(255), "lis_person_name_given" character varying(255), "lis_person_sourcedid" character varying(255), "user_image" character varying(255), "roles" character varying(255)[] DEFAULT '{}', "created_at" timestamp, "updated_at" timestamp)
|
3047
|
+
[1m[36m (2.6ms)[0m [1mCREATE TABLE "schema_migrations" ("version" character varying(255) NOT NULL) [0m
|
3048
|
+
[1m[35m (6.8ms)[0m CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version")
|
3049
|
+
[1m[36m (0.2ms)[0m [1mSELECT version FROM "schema_migrations"[0m
|
3050
|
+
[1m[35m (2.1ms)[0m INSERT INTO "schema_migrations" (version) VALUES ('20141008172001')
|
3051
|
+
[1m[36m (2.1ms)[0m [1mINSERT INTO "schema_migrations" (version) VALUES ('20141003180140')[0m
|