dce_lti 0.4.0 → 0.5.0
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.
- 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
|