dce_lti 0.2.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.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +154 -0
  4. data/Rakefile +19 -0
  5. data/app/assets/javascripts/dce_lti/application.js +13 -0
  6. data/app/assets/stylesheets/dce_lti/application.css +15 -0
  7. data/app/concerns/dce_lti/session_helpers.rb +55 -0
  8. data/app/controllers/dce_lti/application_controller.rb +5 -0
  9. data/app/controllers/dce_lti/configs_controller.rb +26 -0
  10. data/app/controllers/dce_lti/sessions_controller.rb +20 -0
  11. data/app/helpers/dce_lti/application_helper.rb +4 -0
  12. data/app/models/dce_lti/nonce.rb +17 -0
  13. data/app/models/dce_lti/user.rb +16 -0
  14. data/app/services/dce_lti/timestamp_validator.rb +7 -0
  15. data/app/services/dce_lti/user_initializer.rb +22 -0
  16. data/app/views/dce_lti/sessions/invalid.html.erb +4 -0
  17. data/app/views/layouts/dce_lti/application.html.erb +14 -0
  18. data/config/routes.rb +9 -0
  19. data/db/migrate/20141003180140_create_dce_lti_users.rb +16 -0
  20. data/db/migrate/20141008172001_create_dce_lti_nonces.rb +10 -0
  21. data/lib/dce_lti/controller_methods.rb +18 -0
  22. data/lib/dce_lti/engine.rb +30 -0
  23. data/lib/dce_lti/version.rb +3 -0
  24. data/lib/dce_lti.rb +5 -0
  25. data/lib/tasks/dce_lti_tasks.rake +34 -0
  26. data/spec/controllers/dce_lti/configs_controller_spec.rb +71 -0
  27. data/spec/controllers/dce_lti/sessions_controller_spec.rb +201 -0
  28. data/spec/controllers/embedding_headers_are_correct_spec.rb +8 -0
  29. data/spec/controllers/posts_controller_spec.rb +22 -0
  30. data/spec/dummy/README.rdoc +28 -0
  31. data/spec/dummy/Rakefile +6 -0
  32. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  33. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  34. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  35. data/spec/dummy/app/controllers/posts_controller.rb +6 -0
  36. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  37. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  38. data/spec/dummy/bin/bundle +3 -0
  39. data/spec/dummy/bin/rails +4 -0
  40. data/spec/dummy/bin/rake +4 -0
  41. data/spec/dummy/config/application.rb +29 -0
  42. data/spec/dummy/config/boot.rb +5 -0
  43. data/spec/dummy/config/database.yml +19 -0
  44. data/spec/dummy/config/environment.rb +5 -0
  45. data/spec/dummy/config/environments/development.rb +37 -0
  46. data/spec/dummy/config/environments/production.rb +82 -0
  47. data/spec/dummy/config/environments/test.rb +39 -0
  48. data/spec/dummy/config/initializers/assets.rb +8 -0
  49. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  50. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  51. data/spec/dummy/config/initializers/dce_lti_config.rb +40 -0
  52. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  53. data/spec/dummy/config/initializers/inflections.rb +16 -0
  54. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  55. data/spec/dummy/config/initializers/session_store.rb +3 -0
  56. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  57. data/spec/dummy/config/initializers/x_frame_options.rb +9 -0
  58. data/spec/dummy/config/locales/en.yml +23 -0
  59. data/spec/dummy/config/routes.rb +7 -0
  60. data/spec/dummy/config/secrets.yml +22 -0
  61. data/spec/dummy/config.ru +4 -0
  62. data/spec/dummy/db/schema.rb +40 -0
  63. data/spec/dummy/log/development.log +2875 -0
  64. data/spec/dummy/log/test.log +108620 -0
  65. data/spec/dummy/public/404.html +67 -0
  66. data/spec/dummy/public/422.html +67 -0
  67. data/spec/dummy/public/500.html +66 -0
  68. data/spec/dummy/public/favicon.ico +0 -0
  69. data/spec/factories.rb +7 -0
  70. data/spec/models/dce_lti/nonce_spec.rb +42 -0
  71. data/spec/models/dce_lti/user_spec.rb +71 -0
  72. data/spec/services/dce_lti/timestamp_validator_spec.rb +15 -0
  73. data/spec/services/dce_lti/user_initializer_spec.rb +58 -0
  74. data/spec/spec_helper.rb +25 -0
  75. data/spec/support/database_cleaner.rb +21 -0
  76. data/spec/support/dce_lti/configuration_helpers.rb +36 -0
  77. data/spec/support/factory_girl.rb +3 -0
  78. metadata +327 -0
@@ -0,0 +1,67 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The page you were looking for doesn't exist (404)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/404.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>The page you were looking for doesn't exist.</h1>
62
+ <p>You may have mistyped the address or the page may have moved.</p>
63
+ </div>
64
+ <p>If you are the application owner check the logs for more information.</p>
65
+ </div>
66
+ </body>
67
+ </html>
@@ -0,0 +1,67 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/422.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>The change you wanted was rejected.</h1>
62
+ <p>Maybe you tried to change something you didn't have access to.</p>
63
+ </div>
64
+ <p>If you are the application owner check the logs for more information.</p>
65
+ </div>
66
+ </body>
67
+ </html>
@@ -0,0 +1,66 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/500.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>We're sorry, but something went wrong.</h1>
62
+ </div>
63
+ <p>If you are the application owner check the logs for more information.</p>
64
+ </div>
65
+ </body>
66
+ </html>
File without changes
data/spec/factories.rb ADDED
@@ -0,0 +1,7 @@
1
+ module DceLti
2
+ FactoryGirl.define do
3
+ factory :user, class: User do
4
+ sequence(:lti_user_id) { |n| "user_id-#{n}" }
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,42 @@
1
+ module DceLti
2
+ describe Nonce do
3
+ it { should have_db_index(:nonce).unique(:true) }
4
+
5
+ context '.valid?' do
6
+ it 'returns true when a value is unique' do
7
+ expect(described_class.valid?('100')).to be true
8
+ end
9
+
10
+ it 'returns false when a value already exists' do
11
+ described_class.create!(nonce: '100')
12
+
13
+ expect(described_class.valid?('100')).to be false
14
+ end
15
+ end
16
+
17
+ context 'logging' do
18
+ it 'logs about it when a nonce is invalid' do
19
+ allow(Rails.logger).to receive(:warn)
20
+ allow(described_class).to receive(:create!).and_raise
21
+
22
+ described_class.valid?('100')
23
+
24
+ expect(Rails.logger).to have_received(:warn).with(/Creating nonce failed: "100"/)
25
+ end
26
+ end
27
+
28
+ context '.clean' do
29
+ it 'deletes based on the time limit in seconds set in config' do
30
+ Time.freeze do
31
+ allow(described_class).to receive(:delete_all)
32
+
33
+ described_class.clean
34
+
35
+ expect(described_class).to have_received(:delete_all).with(
36
+ ['created_at < ?', Time.now - 6.hours]
37
+ )
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,71 @@
1
+ module DceLti
2
+ describe User do
3
+ context '#lti_user_id' do
4
+ it { should validate_presence_of(:lti_user_id) }
5
+ it { should validate_uniqueness_of(:lti_user_id) }
6
+ it { should ensure_length_of(:lti_user_id).is_at_most(255) }
7
+ end
8
+
9
+ context '#roles' do
10
+ it 'keeps an array of roles' do
11
+ roles = %w|foo bar|
12
+
13
+ user = build(:user, roles: roles)
14
+ user.save && user.reload
15
+
16
+ expect(user.roles).to match_array(roles)
17
+ end
18
+
19
+ it 'are case downcased before storing' do
20
+ roles = %w|foo bar BAZ|
21
+
22
+ user = build(:user, roles: roles)
23
+ user.save && user.reload
24
+
25
+ expect(user.roles).to match_array(['foo','bar', 'baz'])
26
+ end
27
+
28
+ end
29
+
30
+ context 'has_role?' do
31
+ it 'handles nil elegantly' do
32
+ user = described_class.new
33
+
34
+ expect(user).not_to have_role(nil)
35
+ end
36
+
37
+ it 'can be queried via predicate methods' do
38
+ user = create(:user, roles: ['foo', 'blee'])
39
+
40
+ expect(user).to have_role(:foo)
41
+ end
42
+
43
+ it 'downcases a role before querying' do
44
+ user = create(:user, roles: ['foo', 'BLEE'])
45
+
46
+ expect(user).to have_role('blee')
47
+ end
48
+
49
+ it 'does not care if a role is a string or a symbol' do
50
+ user = create(:user, roles: ['foo', 'BLEE'])
51
+
52
+ expect(user).to have_role('blee')
53
+ expect(user).to have_role(:blee)
54
+ end
55
+ end
56
+
57
+ it 'stores additional attributes from the consumer' do
58
+ user = described_class.new
59
+ %i|
60
+ lis_person_contact_email_primary
61
+ lis_person_name_family
62
+ lis_person_name_full
63
+ lis_person_name_given
64
+ lis_person_sourcedid
65
+ user_image
66
+ |.each do |attribute|
67
+ expect(user).to respond_to(attribute)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,15 @@
1
+ module DceLti
2
+ describe TimestampValidator do
3
+ it 'returns true for a timestamp that is in range' do
4
+ timestamp = (Time.now - 1.hour).to_i
5
+
6
+ expect(described_class.valid?(timestamp)).to be true
7
+ end
8
+
9
+ it 'returns false for a timestamp that is not in range' do
10
+ timestamp = (Time.now - 6.hours).to_i
11
+
12
+ expect(described_class.valid?(timestamp)).to be false
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,58 @@
1
+ module DceLti
2
+ describe UserInitializer do
3
+ it 'finds or creates a user based on the tool provider user_id' do
4
+ user_double = double('user').as_null_object
5
+ fake_oauth_id = 'an_oauth_id'
6
+ allow(tool_provider_double).to receive(:user_id).
7
+ and_return(fake_oauth_id)
8
+ allow(User).to receive(:find_or_create_by).and_return(user_double)
9
+
10
+ described_class.find_from(tool_provider_double)
11
+
12
+ expect(User).to have_received(:find_or_create_by).
13
+ with(lti_user_id: fake_oauth_id)
14
+ expect(tool_provider_double).to have_received(:user_id)
15
+ end
16
+
17
+ it 'adds additional attributes to a user from the tool_provider' do
18
+ user = User.new
19
+ allow(user).to receive_messages(tool_provider_methods)
20
+ allow(User).to receive(:find_or_create_by).and_return(user)
21
+
22
+ described_class.find_from(tool_provider_double)
23
+
24
+ tool_provider_methods.each do |tool_provider_attribute|
25
+ send_to_tool_provider = tool_provider_attribute.to_s.gsub(/=\Z/,'').to_sym
26
+ expect(user).to have_received(tool_provider_attribute).with(
27
+ tool_provider_attributes[send_to_tool_provider]
28
+ )
29
+ end
30
+ end
31
+
32
+ def tool_provider_methods
33
+ tool_provider_attributes.keys.reject do |att|
34
+ att == :user_id
35
+ end.map{|att| "#{att}=".to_sym}
36
+ end
37
+
38
+ def tool_provider_attributes
39
+ {
40
+ user_id: 'an_oauth_id',
41
+ roles: [ 'instructor' ],
42
+ lis_person_contact_email_primary: 'instructor@example.com',
43
+ lis_person_name_family: 'Last Name',
44
+ lis_person_name_full: 'Last Name, First Name',
45
+ lis_person_name_given: 'First Name',
46
+ lis_person_sourcedid: 'sourcedid',
47
+ user_image: 'http://example.com/image.png'
48
+ }
49
+ end
50
+
51
+ def tool_provider_double
52
+ @tool_provider_double ||= double(
53
+ 'Tool Provider',
54
+ tool_provider_attributes
55
+ )
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,25 @@
1
+ ENV['RAILS_ENV'] = 'test'
2
+
3
+ require File.expand_path('../dummy/config/environment.rb', __FILE__)
4
+
5
+ require 'rspec/rails'
6
+ require 'shoulda/matchers'
7
+ require 'factory_girl_rails'
8
+ require 'pry-byebug'
9
+ require 'database_cleaner'
10
+
11
+ Rails.backtrace_cleaner.remove_silencers!
12
+
13
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
14
+
15
+ RSpec.configure do |config|
16
+ config.expect_with :rspec do |c|
17
+ c.syntax = :expect
18
+ end
19
+
20
+ # config.include Features, type: :feature
21
+ config.infer_base_class_for_anonymous_controllers = false
22
+ config.infer_spec_type_from_file_location!
23
+ config.order = 'random'
24
+ config.use_transactional_fixtures = false
25
+ end
@@ -0,0 +1,21 @@
1
+ RSpec.configure do |config|
2
+ config.before(:suite) do
3
+ DatabaseCleaner.clean_with(:deletion)
4
+ end
5
+
6
+ config.before(:each) do
7
+ DatabaseCleaner.strategy = :transaction
8
+ end
9
+
10
+ config.before(:each, :js => true) do
11
+ DatabaseCleaner.strategy = :deletion
12
+ end
13
+
14
+ config.before(:each) do
15
+ DatabaseCleaner.start
16
+ end
17
+
18
+ config.after(:each) do
19
+ DatabaseCleaner.clean
20
+ end
21
+ end
@@ -0,0 +1,36 @@
1
+ module DceLti
2
+ module ConfigurationHelpers
3
+ def with_overridden_lti_config_of(new_config)
4
+ original_config = get_original_config(new_config)
5
+
6
+ begin
7
+ new_config.keys.each do |config_att|
8
+ DceLti::Engine.config.send("#{config_att}=", new_config[config_att])
9
+ end
10
+ yield DceLti::Engine.config
11
+ ensure
12
+ new_config.keys.each do |config_att|
13
+ DceLti::Engine.config.send("#{config_att}=", original_config[config_att])
14
+ end
15
+ end
16
+ end
17
+
18
+ def get_original_config(new_config)
19
+ original_config = {}
20
+ new_config.keys.each do |config_att|
21
+ if ! DceLti::Engine.config.respond_to?(config_att)
22
+ DceLti::Engine.config.send("#{config_att}=", nil)
23
+ end
24
+ original_config[config_att] = DceLti::Engine.config.send(config_att.to_sym)
25
+ end
26
+ original_config
27
+ end
28
+
29
+ def lti_config
30
+ {
31
+ provider_title: 'An awesome title',
32
+ provider_description: 'A cool description',
33
+ }
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ RSpec.configure do |config|
2
+ config.include FactoryGirl::Syntax::Methods
3
+ end