dce_lti 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +154 -0
- data/Rakefile +19 -0
- data/app/assets/javascripts/dce_lti/application.js +13 -0
- data/app/assets/stylesheets/dce_lti/application.css +15 -0
- data/app/concerns/dce_lti/session_helpers.rb +55 -0
- data/app/controllers/dce_lti/application_controller.rb +5 -0
- data/app/controllers/dce_lti/configs_controller.rb +26 -0
- data/app/controllers/dce_lti/sessions_controller.rb +20 -0
- data/app/helpers/dce_lti/application_helper.rb +4 -0
- data/app/models/dce_lti/nonce.rb +17 -0
- data/app/models/dce_lti/user.rb +16 -0
- data/app/services/dce_lti/timestamp_validator.rb +7 -0
- data/app/services/dce_lti/user_initializer.rb +22 -0
- data/app/views/dce_lti/sessions/invalid.html.erb +4 -0
- data/app/views/layouts/dce_lti/application.html.erb +14 -0
- data/config/routes.rb +9 -0
- data/db/migrate/20141003180140_create_dce_lti_users.rb +16 -0
- data/db/migrate/20141008172001_create_dce_lti_nonces.rb +10 -0
- data/lib/dce_lti/controller_methods.rb +18 -0
- data/lib/dce_lti/engine.rb +30 -0
- data/lib/dce_lti/version.rb +3 -0
- data/lib/dce_lti.rb +5 -0
- data/lib/tasks/dce_lti_tasks.rake +34 -0
- data/spec/controllers/dce_lti/configs_controller_spec.rb +71 -0
- data/spec/controllers/dce_lti/sessions_controller_spec.rb +201 -0
- data/spec/controllers/embedding_headers_are_correct_spec.rb +8 -0
- data/spec/controllers/posts_controller_spec.rb +22 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/controllers/posts_controller.rb +6 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config/application.rb +29 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +19 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +82 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/assets.rb +8 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/dce_lti_config.rb +40 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/initializers/x_frame_options.rb +9 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +7 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/schema.rb +40 -0
- data/spec/dummy/log/development.log +2875 -0
- data/spec/dummy/log/test.log +108620 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/factories.rb +7 -0
- data/spec/models/dce_lti/nonce_spec.rb +42 -0
- data/spec/models/dce_lti/user_spec.rb +71 -0
- data/spec/services/dce_lti/timestamp_validator_spec.rb +15 -0
- data/spec/services/dce_lti/user_initializer_spec.rb +58 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/support/database_cleaner.rb +21 -0
- data/spec/support/dce_lti/configuration_helpers.rb +36 -0
- data/spec/support/factory_girl.rb +3 -0
- metadata +327 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 537eafe1ddf43015784d36ac3d0b75b29636d274
|
4
|
+
data.tar.gz: b1b6c42e830086952c486bb6ef33030b2e1bd9b4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3507c09883950f002947a628e0fec151c6b046633575d739d3318c9dd67931ac32fd74a143027bce9d4c56f30d97bb1b9cf56268d0d78fddefe9a60bad03a1c3
|
7
|
+
data.tar.gz: 30f1ae5dbd4e394ba1f9eb3f31104a161666585fbb4998ff59193b17dc56c5e96348689650701bf1484975ec4dfcd3f8296b50a00923d0818892f159d45e33a9
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2014 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
# DceLti - LTI Authentication for Rails apps
|
2
|
+
|
3
|
+
The DceLti engine simplifies integrating LTI authentication for Rails apps via
|
4
|
+
the [IMS::LTI gem](https://github.com/instructure/ims-lti).
|
5
|
+
|
6
|
+
## Prerequisites
|
7
|
+
|
8
|
+
* A postgres database
|
9
|
+
* Rails > 4.1.x
|
10
|
+
|
11
|
+
## Getting started
|
12
|
+
|
13
|
+
Add this engine to your gemfile:
|
14
|
+
|
15
|
+
gem 'dce_lti'
|
16
|
+
|
17
|
+
Install it and run migrations:
|
18
|
+
|
19
|
+
bundle
|
20
|
+
rake dce_lti:install
|
21
|
+
rake db:migrate
|
22
|
+
|
23
|
+
Mount the engine in 'config/routes.rb'
|
24
|
+
|
25
|
+
mount DceLti::Engine => "/lti"
|
26
|
+
|
27
|
+
Once mounted, you can use the engine-provided methods `authenticate_via_lti`
|
28
|
+
and `current_user`. Use `authenticate_via_lti` as a `before_filter` to ensure
|
29
|
+
you have a valid LTI-provided user in `current_user`, thusly:
|
30
|
+
|
31
|
+
class VideosController < ApplicationController
|
32
|
+
before_filter :authenticate_via_lti
|
33
|
+
|
34
|
+
def show
|
35
|
+
@post = current_user.posts.where(id: params[:id])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
## Configuration
|
40
|
+
|
41
|
+
The generated config looks something like (commented defaults omitted):
|
42
|
+
|
43
|
+
DceLti::Engine.setup do |lti|
|
44
|
+
lti.consumer_secret = (ENV['LTI_CONSUMER_SECRET'] || 'consumer_secret')
|
45
|
+
lti.consumer_key = (ENV['LTI_CONSUMER_KEY'] || 'consumer_key')
|
46
|
+
lti.tool_config_extensions = ->(controller, tool_config) do
|
47
|
+
tool_config.extend ::IMS::LTI::Extensions::Canvas::ToolConfig
|
48
|
+
tool_config.canvas_domain!(controller.request.host)
|
49
|
+
tool_config.canvas_privacy_public!
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
### Basic attributes
|
54
|
+
|
55
|
+
Most basic attributes are configured via ENV. See the generated
|
56
|
+
`config/initializers/dce_lti_config.rb` file.
|
57
|
+
|
58
|
+
### X-Frame Options
|
59
|
+
|
60
|
+
We install a config file that removes `X-Frame-Options` by default to allow
|
61
|
+
your application to be embedded in an `iframe`. Feel free to edit this file if
|
62
|
+
you'd like to lock down `iframe` policies.
|
63
|
+
|
64
|
+
### Consumer key and secret configuration
|
65
|
+
|
66
|
+
If you're building an LTI app that will only ever provide a tool to one
|
67
|
+
consumer, then getting the key and secret from ENV is OK. However, in all
|
68
|
+
likelihood you'll want your tool to work for any approved consumer and will
|
69
|
+
need something more flexible.
|
70
|
+
|
71
|
+
With that in mind, `consumer_key` and `consumer_secret` can be lambdas and
|
72
|
+
receive the `launch_params` as sent by the consumer. These launch parameters
|
73
|
+
include the `consumer_key` and other attributes to help you identify a consumer
|
74
|
+
uniquely - most likely `context_id` or `tool_consumer_instance_guid`. Example:
|
75
|
+
|
76
|
+
DceLti::Engine.setup do |lti|
|
77
|
+
lti.consumer_secret = ->(launch_params) {
|
78
|
+
Consumer.find_by(context_id: launch_params[:context_id]).consumer_secret
|
79
|
+
}
|
80
|
+
lti.consumer_key = ->(launch_params) {
|
81
|
+
Consumer.find_by(context_id: launch_params[:context_id]).consumer_key
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
85
|
+
### Customizing the Tool Provider XML configuration
|
86
|
+
|
87
|
+
The tool config instance (provided by
|
88
|
+
[IMS::LTI::ToolConfig](https://github.com/instructure/ims-lti/blob/master/lib/ims/lti/tool_config.rb))
|
89
|
+
can be configured directly via the `tool_config_extensions` lambda. This allows
|
90
|
+
you to set LMS-specific config extensions. A common example for the Canvas LMS
|
91
|
+
is created by default in the generated configs.
|
92
|
+
|
93
|
+
The `tool_config_extensions` lambda runs before the xml is generated and gets
|
94
|
+
two parameters:
|
95
|
+
|
96
|
+
* controller - An instance of DceLti::ConfigsController
|
97
|
+
* tool_config - An instance of IMS::LTI::ToolConfig
|
98
|
+
|
99
|
+
See
|
100
|
+
[IMS::LTI::Extensions::Canvas::ToolConfig](https://github.com/instructure/ims-lti/blob/master/lib/ims/lti/extensions/canvas.rb)
|
101
|
+
and other classes/modules under the IMS::LTI::Extensions hierarchy for further
|
102
|
+
options.
|
103
|
+
|
104
|
+
## Notes
|
105
|
+
|
106
|
+
The `DceLti` provided controllers inherit from `ApplicationController` as
|
107
|
+
defined in your application.
|
108
|
+
|
109
|
+
### Consumer context info
|
110
|
+
|
111
|
+
For successful launches, the controller `session` hash will contain:
|
112
|
+
|
113
|
+
* `:context_id`
|
114
|
+
* `:context_label`
|
115
|
+
* `:context_title`
|
116
|
+
* `:resource_link_id`
|
117
|
+
* `:resource_link_title`
|
118
|
+
* `:tool_consumer_instance_guid`
|
119
|
+
|
120
|
+
These values come from the LTI values posted by the consumer.
|
121
|
+
|
122
|
+
### After a successful launch
|
123
|
+
|
124
|
+
By default, a successful launch will redirect to your application's
|
125
|
+
`root_path`. This is configured via `redirect_after_successful_auth` which is
|
126
|
+
evaluated in engine controller context. This method should have access to
|
127
|
+
`current_user`, rails route helpers and other controller-specific context.
|
128
|
+
|
129
|
+
### Invalid LTI Sessions
|
130
|
+
|
131
|
+
If an LTI session cannot be validated, `dce_lti/sessions/invalid` will be
|
132
|
+
rendered. You can customize this output by creating a file named
|
133
|
+
`app/views/dce_lti/sessions/invalid.html.erb`, per the default engine view
|
134
|
+
resolution behavior.
|
135
|
+
|
136
|
+
### Nonce cleanup
|
137
|
+
|
138
|
+
You can clean up lti-related
|
139
|
+
[nonces](http://en.wikipedia.org/wiki/Cryptographic_nonce) via the
|
140
|
+
engine-provided `dce_lti:clean_nonces` rake task, which'll remove nonces older
|
141
|
+
than 6 hours. You should probably run this in a cron job every hour or so. You
|
142
|
+
can also just invoke `DceLti::Nonce.clean` on your own.
|
143
|
+
|
144
|
+
## Contributors
|
145
|
+
|
146
|
+
* Dan Collis-Puro - @djcp
|
147
|
+
|
148
|
+
## License
|
149
|
+
|
150
|
+
This project is licensed under the same terms as Rails itself.
|
151
|
+
|
152
|
+
## Copyright
|
153
|
+
|
154
|
+
2014 President and Fellows of Harvard College
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
8
|
+
load 'rails/tasks/engine.rake'
|
9
|
+
|
10
|
+
Bundler::GemHelper.install_tasks
|
11
|
+
|
12
|
+
require 'rspec/core'
|
13
|
+
require 'rspec/core/rake_task'
|
14
|
+
desc "Run all specs in spec directory (excluding plugin specs)"
|
15
|
+
|
16
|
+
RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
|
17
|
+
|
18
|
+
task(:default).clear
|
19
|
+
task default: [:spec]
|
@@ -0,0 +1,13 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree .
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any styles
|
10
|
+
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
|
11
|
+
* file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module DceLti
|
2
|
+
module SessionHelpers
|
3
|
+
ATTRIBUTES_TO_EXTRACT_FOR_SESSION = %i|
|
4
|
+
context_id
|
5
|
+
context_label
|
6
|
+
context_title
|
7
|
+
resource_link_id
|
8
|
+
resource_link_title
|
9
|
+
tool_consumer_instance_guid
|
10
|
+
|
|
11
|
+
|
12
|
+
def valid_lti_request?(request)
|
13
|
+
tool_provider.valid_request?(request) &&
|
14
|
+
Nonce.valid?(tool_provider.oauth_nonce) &&
|
15
|
+
TimestampValidator.valid?(tool_provider.oauth_timestamp)
|
16
|
+
end
|
17
|
+
|
18
|
+
def launch_params
|
19
|
+
params.reject{ |k,v| ['controller','action'].include? k }
|
20
|
+
end
|
21
|
+
|
22
|
+
def consumer_key
|
23
|
+
find_from_config(:consumer_key)
|
24
|
+
end
|
25
|
+
|
26
|
+
def consumer_secret
|
27
|
+
find_from_config(:consumer_secret)
|
28
|
+
end
|
29
|
+
|
30
|
+
def find_from_config(attribute)
|
31
|
+
value = Engine.config.send(attribute)
|
32
|
+
if value.respond_to?(:call)
|
33
|
+
value.call(launch_params)
|
34
|
+
else
|
35
|
+
value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def redirect_after_successful_auth
|
40
|
+
Engine.config.redirect_after_successful_auth.call
|
41
|
+
end
|
42
|
+
|
43
|
+
def tool_provider
|
44
|
+
@tool_provider ||= IMS::LTI::ToolProvider.new(
|
45
|
+
consumer_key, consumer_secret, launch_params
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
def captured_attributes_from(tool_provider)
|
50
|
+
ATTRIBUTES_TO_EXTRACT_FOR_SESSION.inject({}) do |attributes, att|
|
51
|
+
attributes.merge(att => tool_provider.send(att))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module DceLti
|
2
|
+
class ConfigsController < ApplicationController
|
3
|
+
skip_before_filter :authenticate_via_lti
|
4
|
+
respond_to :xml
|
5
|
+
|
6
|
+
def index
|
7
|
+
tool_config = ::IMS::LTI::ToolConfig.new(
|
8
|
+
launch_url: sessions_url,
|
9
|
+
title: engine_config.provider_title,
|
10
|
+
description: engine_config.provider_description,
|
11
|
+
)
|
12
|
+
|
13
|
+
if engine_config.respond_to?(:tool_config_extensions)
|
14
|
+
engine_config.tool_config_extensions.call(self, tool_config)
|
15
|
+
end
|
16
|
+
|
17
|
+
respond_with tool_config
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def engine_config
|
23
|
+
DceLti::Engine.config
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'oauth/request_proxy/rack_request'
|
2
|
+
|
3
|
+
module DceLti
|
4
|
+
class SessionsController < ApplicationController
|
5
|
+
include SessionHelpers
|
6
|
+
|
7
|
+
skip_before_filter :verify_authenticity_token, :authenticate_via_lti
|
8
|
+
|
9
|
+
def create
|
10
|
+
if valid_lti_request?(request)
|
11
|
+
user = UserInitializer.find_from(tool_provider)
|
12
|
+
session[:current_user_id] = user.id
|
13
|
+
session.merge!(captured_attributes_from(tool_provider))
|
14
|
+
redirect_to redirect_after_successful_auth
|
15
|
+
else
|
16
|
+
render :invalid
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module DceLti
|
2
|
+
class Nonce < ActiveRecord::Base
|
3
|
+
def self.clean
|
4
|
+
delete_all(['created_at < ?', Time.now - 6.hours])
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.valid?(nonce)
|
8
|
+
begin
|
9
|
+
self.create!(nonce: nonce)
|
10
|
+
true
|
11
|
+
rescue => e
|
12
|
+
Rails.logger.warn(%Q|Creating nonce failed: "#{nonce}"|)
|
13
|
+
false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module DceLti
|
2
|
+
class User < ActiveRecord::Base
|
3
|
+
validates :lti_user_id,
|
4
|
+
uniqueness: true,
|
5
|
+
length: { maximum: 255 },
|
6
|
+
presence: true
|
7
|
+
|
8
|
+
def roles=(roles)
|
9
|
+
super roles.map{|role| role.downcase}
|
10
|
+
end
|
11
|
+
|
12
|
+
def has_role?(role)
|
13
|
+
roles.include?(role.to_s.downcase)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module DceLti
|
2
|
+
class UserInitializer
|
3
|
+
TOOL_PROVIDER_ATTRIBUTES = %i|
|
4
|
+
roles
|
5
|
+
lis_person_contact_email_primary
|
6
|
+
lis_person_name_family
|
7
|
+
lis_person_name_full
|
8
|
+
lis_person_name_given
|
9
|
+
lis_person_sourcedid
|
10
|
+
user_image
|
11
|
+
|
|
12
|
+
|
13
|
+
def self.find_from(tool_provider)
|
14
|
+
User.find_or_create_by(lti_user_id: tool_provider.user_id).tap do |user|
|
15
|
+
TOOL_PROVIDER_ATTRIBUTES.each do |attribute|
|
16
|
+
user.send("#{attribute}=", tool_provider.send(attribute))
|
17
|
+
end
|
18
|
+
user.save
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>DceLti</title>
|
5
|
+
<%= stylesheet_link_tag "dce_lti/application", media: "all" %>
|
6
|
+
<%= javascript_include_tag "dce_lti/application" %>
|
7
|
+
<%= csrf_meta_tags %>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
|
11
|
+
<%= yield %>
|
12
|
+
|
13
|
+
</body>
|
14
|
+
</html>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
class CreateDceLtiUsers < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :dce_lti_users do |t|
|
4
|
+
t.string :lti_user_id, nil: false
|
5
|
+
t.string :lis_person_contact_email_primary, size: 1.kilobyte
|
6
|
+
t.string :lis_person_name_family, size: 1.kilobyte
|
7
|
+
t.string :lis_person_name_full, size: 1.kilobyte
|
8
|
+
t.string :lis_person_name_given, size: 1.kilobyte
|
9
|
+
t.string :lis_person_sourcedid, size: 1.kilobyte
|
10
|
+
t.string :user_image, size: 1.kilobyte
|
11
|
+
t.string :roles, array: true, default: []
|
12
|
+
|
13
|
+
t.timestamps
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module DceLti
|
2
|
+
module ControllerMethods
|
3
|
+
def authenticate_via_lti
|
4
|
+
if ! current_user
|
5
|
+
redirect_to Engine.routes.url_helpers.invalid_sessions_path
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def current_user
|
10
|
+
@current_user ||=
|
11
|
+
if ENV['FAKE_USER_ID']
|
12
|
+
User.find_by(id: ENV['FAKE_USER_ID'])
|
13
|
+
else
|
14
|
+
User.find_by(id: session[:current_user_id])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'ims/lti'
|
2
|
+
require 'pg'
|
3
|
+
|
4
|
+
module DceLti
|
5
|
+
class Engine < ::Rails::Engine
|
6
|
+
def self.setup
|
7
|
+
config.provider_title = (ENV['LTI_PROVIDER_TITLE'] || 'DCE LTI Provider')
|
8
|
+
config.provider_description = (ENV['LTI_PROVIDER_DESCRIPTION'] || 'A description of this')
|
9
|
+
|
10
|
+
config.redirect_after_successful_auth = -> do
|
11
|
+
Rails.application.routes.url_helpers.root_path
|
12
|
+
end
|
13
|
+
config.tool_config_extensions = ->(*) {}
|
14
|
+
yield config
|
15
|
+
end
|
16
|
+
|
17
|
+
initializer 'dce_lti.load_helpers' do
|
18
|
+
ActionController::Base.send :include, ControllerMethods
|
19
|
+
end
|
20
|
+
|
21
|
+
isolate_namespace DceLti
|
22
|
+
|
23
|
+
config.generators do |g|
|
24
|
+
g.test_framework :rspec, :fixture => false
|
25
|
+
g.fixture_replacement :factory_girl, :dir => 'spec/factories'
|
26
|
+
g.assets false
|
27
|
+
g.helper false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/dce_lti.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
namespace :dce_lti do
|
2
|
+
|
3
|
+
def install_file(config_file)
|
4
|
+
if ! File.exists?(config_file)
|
5
|
+
puts %Q|Copied configuration #{config_file} from dce_lti|
|
6
|
+
FileUtils.copy("#{DceLti::Engine.root}/spec/dummy/#{config_file}", config_file)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
desc "Install the DceLti engine into your application"
|
11
|
+
task :install do
|
12
|
+
install_file 'config/initializers/dce_lti_config.rb'
|
13
|
+
install_file 'config/initializers/x_frame_options.rb'
|
14
|
+
|
15
|
+
Rake::Task["dce_lti:install:migrations"].invoke
|
16
|
+
|
17
|
+
puts %Q|
|
18
|
+
Base migrations and config files have been installed, please see above.
|
19
|
+
|
20
|
+
Be sure to mount this engine in your config/routes.rb file, thusly:
|
21
|
+
|
22
|
+
mount DceLti::Engine => "/lti"
|
23
|
+
|
24
|
+
Please see the README for more information about configuration and what this
|
25
|
+
engine provides.
|
26
|
+
|
27
|
+
|
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'Clean up old nonces'
|
31
|
+
task clean_nonces: :environment do
|
32
|
+
DceLti::Nonce.clean
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module DceLti
|
2
|
+
describe ConfigsController do
|
3
|
+
include ConfigurationHelpers
|
4
|
+
|
5
|
+
context '#index' do
|
6
|
+
it 'uses IMS::LTI::ToolConfig to construct the tool config' do
|
7
|
+
configurer_double = create_configurer_double
|
8
|
+
|
9
|
+
get :index, { format: :xml, use_route: :dce_lti }
|
10
|
+
|
11
|
+
expect(configurer_double).to have_received(:to_xml)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'renders XML' do
|
15
|
+
create_configurer_double
|
16
|
+
|
17
|
+
get :index, {format: :xml, use_route: :dce_lti }
|
18
|
+
|
19
|
+
expect(response.content_type).to eq 'application/xml'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'defaults launch_url to sessions_url' do
|
23
|
+
sessions_url = 'foobar'
|
24
|
+
allow(controller).to receive(:sessions_url).and_return(sessions_url)
|
25
|
+
create_configurer_double
|
26
|
+
|
27
|
+
get :index, {format: :xml, use_route: :dce_lti }
|
28
|
+
|
29
|
+
expect(IMS::LTI::ToolConfig).to have_received(:new).with(
|
30
|
+
hash_including(launch_url: sessions_url)
|
31
|
+
)
|
32
|
+
expect(controller).to have_received(:sessions_url)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'evaluates custom lambdas with controller context correctly' do
|
36
|
+
tool_config_extensions = ->(controller, tool_config) {
|
37
|
+
tool_config.extend ::IMS::LTI::Extensions::Canvas::ToolConfig
|
38
|
+
tool_config.canvas_domain!(controller.request.host)
|
39
|
+
}
|
40
|
+
with_overridden_lti_config_of({tool_config_extensions: tool_config_extensions}) do
|
41
|
+
get :index, { format: :xml, use_route: :dce_lti }
|
42
|
+
expect(response.body).to include 'test.host'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'passes in the correct variables' do
|
47
|
+
with_overridden_lti_config_of({}) do |lti_config|
|
48
|
+
create_configurer_double
|
49
|
+
get :index, { format: :xml, use_route: :dce_lti }
|
50
|
+
|
51
|
+
expect(IMS::LTI::ToolConfig).to have_received(:new).with(
|
52
|
+
hash_including(
|
53
|
+
title: lti_config.provider_title,
|
54
|
+
description: lti_config.provider_description,
|
55
|
+
)
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def create_configurer_double
|
62
|
+
double(
|
63
|
+
'IMS::LTI::ToolConfig',
|
64
|
+
to_xml: '<xml></xml>',
|
65
|
+
set_ext_param: '',
|
66
|
+
).tap do |configurer_double|
|
67
|
+
allow(IMS::LTI::ToolConfig).to receive(:new).and_return(configurer_double)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|