lti_skydrive 1.1.0 → 1.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 (120) hide show
  1. checksums.yaml +8 -8
  2. data/Rakefile +20 -1
  3. data/app/assets/javascripts/skydrive/bundle.js +1792 -1794
  4. data/app/controllers/skydrive/application_controller.rb +1 -1
  5. data/app/controllers/skydrive/files_controller.rb +0 -5
  6. data/app/controllers/skydrive/launch_controller.rb +69 -19
  7. data/app/controllers/skydrive/session_controller.rb +1 -1
  8. data/app/models/skydrive/token.rb +1 -3
  9. data/app/models/skydrive/user.rb +18 -0
  10. data/app/views/layouts/skydrive/application.html.erb +0 -1
  11. data/app/views/skydrive/launch/app_redirect.html.erb +1 -1
  12. data/app/views/skydrive/launch/launch_error.html.erb +4 -0
  13. data/config/initializers/sharepoint.rb +2 -1
  14. data/config/routes.rb +1 -0
  15. data/lib/skydrive/client.rb +62 -29
  16. data/lib/skydrive/file.rb +1 -1
  17. data/lib/skydrive/raven_logger.rb +16 -0
  18. data/lib/skydrive/version.rb +1 -1
  19. data/spec/controllers/launch_controller_spec.rb +209 -0
  20. data/spec/lib/skydrive/client_spec.rb +185 -0
  21. data/spec/lib/skydrive/file_spec.rb +63 -0
  22. data/spec/lib/skydrive/folder_spec.rb +19 -0
  23. data/spec/models/api_key_spec.rb +21 -0
  24. data/spec/spec_helper.rb +49 -0
  25. data/{test/dummy → spec/test_app}/README.rdoc +0 -0
  26. data/{test/dummy → spec/test_app}/Rakefile +1 -1
  27. data/{test/dummy → spec/test_app}/app/assets/javascripts/application.js +0 -0
  28. data/{test/dummy → spec/test_app}/app/assets/stylesheets/application.css +0 -0
  29. data/{test/dummy → spec/test_app}/app/controllers/application_controller.rb +0 -0
  30. data/{test/dummy → spec/test_app}/app/helpers/application_helper.rb +0 -0
  31. data/{test/dummy → spec/test_app}/app/views/layouts/application.html.erb +1 -1
  32. data/{test/dummy → spec/test_app}/bin/bundle +0 -0
  33. data/{test/dummy → spec/test_app}/bin/rails +0 -0
  34. data/{test/dummy → spec/test_app}/bin/rake +0 -0
  35. data/{test/dummy → spec/test_app}/config/application.rb +7 -15
  36. data/{test/dummy → spec/test_app}/config/boot.rb +1 -1
  37. data/{test/dummy → spec/test_app}/config/database.yml +8 -8
  38. data/{test/dummy → spec/test_app}/config/environment.rb +1 -1
  39. data/{test/dummy → spec/test_app}/config/environments/development.rb +1 -1
  40. data/{test/dummy → spec/test_app}/config/environments/production.rb +2 -2
  41. data/{test/dummy → spec/test_app}/config/environments/test.rb +1 -1
  42. data/{test/dummy → spec/test_app}/config/initializers/backtrace_silencers.rb +0 -0
  43. data/{test/dummy → spec/test_app}/config/initializers/filter_parameter_logging.rb +0 -0
  44. data/{test/dummy → spec/test_app}/config/initializers/inflections.rb +0 -0
  45. data/{test/dummy → spec/test_app}/config/initializers/mime_types.rb +0 -0
  46. data/{test/dummy → spec/test_app}/config/initializers/secret_token.rb +1 -1
  47. data/spec/test_app/config/initializers/session_store.rb +3 -0
  48. data/{test/dummy → spec/test_app}/config/initializers/wrap_parameters.rb +0 -0
  49. data/{test/dummy → spec/test_app}/config/routes.rb +0 -0
  50. data/spec/test_app/config/sharepoint.yml +17 -0
  51. data/{test/dummy → spec/test_app}/config.ru +0 -0
  52. data/{test/dummy → spec/test_app}/db/schema.rb +0 -1
  53. data/{test/dummy → spec/test_app}/public/404.html +0 -0
  54. data/{test/dummy → spec/test_app}/public/422.html +0 -0
  55. data/{test/dummy → spec/test_app}/public/500.html +0 -0
  56. data/spec/test_app/public/assets/application-710416f2f225dfabdc1644964d73bb6e.js +14 -0
  57. data/spec/test_app/public/assets/application-f83fdeca1df44ae142d477b21c557b7b.css +14 -0
  58. data/spec/test_app/public/assets/bootstrap/glyphicons-halflings-regular-0658df48a6185a0d17acc3f86ff2073b.svg +229 -0
  59. data/spec/test_app/public/assets/bootstrap/glyphicons-halflings-regular-0c86ed38ad91a73661c05f22fed185b3.woff +0 -0
  60. data/spec/test_app/public/assets/bootstrap/glyphicons-halflings-regular-b4268224f45a1b3e5575b458b0f68636.ttf +0 -0
  61. data/spec/test_app/public/assets/bootstrap/glyphicons-halflings-regular-bb146793ac4efa4609bb0512921fc8f2.eot +0 -0
  62. data/spec/test_app/public/assets/lti_box_engine/application-e4115e9d7a64091d8e938ebe100de7b8.js +20 -0
  63. data/spec/test_app/public/assets/lti_box_engine/banner-cc541d8b706841c4c101e20c54f57cd7.png +0 -0
  64. data/spec/test_app/public/assets/lti_box_engine/icon-f4a2da1d67a72ae4962d0152d0b25d0d.png +0 -0
  65. data/{test/dummy → spec/test_app}/public/favicon.ico +0 -0
  66. metadata +98 -170
  67. data/test/controllers/skydrive/ember_controller_test.rb +0 -11
  68. data/test/dummy/config/initializers/session_store.rb +0 -3
  69. data/test/dummy/config/locales/en.yml +0 -23
  70. data/test/dummy/config/sharepoint.yml +0 -31
  71. data/test/dummy/db/development.sqlite3 +0 -0
  72. data/test/dummy/log/development.log +0 -47831
  73. data/test/dummy/tmp/cache/assets/development/sprockets/007f0b35cdfe0e199e160ca7d8c6549f +0 -0
  74. data/test/dummy/tmp/cache/assets/development/sprockets/10c0e5e7b23b9f05226f908616b8f740 +0 -0
  75. data/test/dummy/tmp/cache/assets/development/sprockets/17ae7f285d886e07f4db626a659963f8 +0 -0
  76. data/test/dummy/tmp/cache/assets/development/sprockets/18e08230de276dd3238e77cf0826189c +0 -0
  77. data/test/dummy/tmp/cache/assets/development/sprockets/1a88e6194e0de973583e2adefdc5ac18 +0 -0
  78. data/test/dummy/tmp/cache/assets/development/sprockets/1fc004bfb36f4c31a590a840e552a5c9 +0 -0
  79. data/test/dummy/tmp/cache/assets/development/sprockets/2e4c5fbbf5bf02ef9bd200cce7a7ebc1 +0 -0
  80. data/test/dummy/tmp/cache/assets/development/sprockets/358b2efd0a896115b444b609bc46fca1 +0 -0
  81. data/test/dummy/tmp/cache/assets/development/sprockets/3d9987487f81715791eeeed72ad127da +0 -0
  82. data/test/dummy/tmp/cache/assets/development/sprockets/430f864d22195be98702ac04bc5343b8 +0 -0
  83. data/test/dummy/tmp/cache/assets/development/sprockets/432817256836b657a99d2c2b8afb82bf +0 -0
  84. data/test/dummy/tmp/cache/assets/development/sprockets/45f9e6cdd4d120d901099ccb2018fb4f +0 -0
  85. data/test/dummy/tmp/cache/assets/development/sprockets/5186e21495c07e70e882425df200732c +0 -0
  86. data/test/dummy/tmp/cache/assets/development/sprockets/53de95b978e9c09ae4d2300888e937e5 +0 -0
  87. data/test/dummy/tmp/cache/assets/development/sprockets/5dc21d3b55fc9ca8fbf6582c7fac6c44 +0 -0
  88. data/test/dummy/tmp/cache/assets/development/sprockets/5dd770bdd24bc838e0604eaf2e1d62e0 +0 -0
  89. data/test/dummy/tmp/cache/assets/development/sprockets/5ed30435bc197334c5c942a45ad637d8 +0 -0
  90. data/test/dummy/tmp/cache/assets/development/sprockets/62ef3761af670019ed9f926b6893f7b7 +0 -0
  91. data/test/dummy/tmp/cache/assets/development/sprockets/74283c36e06317a57bf88c444c66eabe +0 -0
  92. data/test/dummy/tmp/cache/assets/development/sprockets/7edc3f1ebc1331bb170f75ee2ce227b1 +0 -0
  93. data/test/dummy/tmp/cache/assets/development/sprockets/88b8a5c8da15ecfa6e7154a661b242a6 +0 -0
  94. data/test/dummy/tmp/cache/assets/development/sprockets/8b73f393f902dcdfe3dd05afb0f5e2d1 +0 -0
  95. data/test/dummy/tmp/cache/assets/development/sprockets/9531ae5356e65c245a5f006c8dd1c577 +0 -0
  96. data/test/dummy/tmp/cache/assets/development/sprockets/98060297d706705e3fc0f7857857dbb9 +0 -0
  97. data/test/dummy/tmp/cache/assets/development/sprockets/98871292949834ebaa9e1eebdd3dca4f +0 -0
  98. data/test/dummy/tmp/cache/assets/development/sprockets/9e208aeff46e060cb20a25456d6be611 +0 -0
  99. data/test/dummy/tmp/cache/assets/development/sprockets/9ebc8e4abeebfae73c59c391e49d0172 +0 -0
  100. data/test/dummy/tmp/cache/assets/development/sprockets/a082682acbe6ed53f5b69ec4d6b336a4 +0 -0
  101. data/test/dummy/tmp/cache/assets/development/sprockets/a7cec1f4ecd136cb80122e121cb63b01 +0 -0
  102. data/test/dummy/tmp/cache/assets/development/sprockets/aa4851494096d3890157afaad27cc407 +0 -0
  103. data/test/dummy/tmp/cache/assets/development/sprockets/ab90d34dfc192bf5fc19836906a3dc23 +0 -0
  104. data/test/dummy/tmp/cache/assets/development/sprockets/afcc3340d4944bf68dd319f3b59c3237 +0 -0
  105. data/test/dummy/tmp/cache/assets/development/sprockets/b0728e2ff6ca4e7f91dbeff31978a9c9 +0 -0
  106. data/test/dummy/tmp/cache/assets/development/sprockets/b54c3c599c3ca4b4f26aa9df12514a56 +0 -0
  107. data/test/dummy/tmp/cache/assets/development/sprockets/b7a1dd5c7d3c2aca0f451fcbc5737cb8 +0 -0
  108. data/test/dummy/tmp/cache/assets/development/sprockets/b83d0d640ae485682db0dff00a1ef7d8 +0 -0
  109. data/test/dummy/tmp/cache/assets/development/sprockets/bcaf1a8e8073121541dcae50e896db44 +0 -0
  110. data/test/dummy/tmp/cache/assets/development/sprockets/be723463abbc85ed29cf5a5840fbfed0 +0 -0
  111. data/test/dummy/tmp/cache/assets/development/sprockets/c16ddf1297328a121de46d0ce47df069 +0 -0
  112. data/test/dummy/tmp/cache/assets/development/sprockets/ccc62e82e5e329c6af0ccf81402ac838 +0 -0
  113. data/test/dummy/tmp/cache/assets/development/sprockets/d47464809e089dba588360f9f0a59d50 +0 -0
  114. data/test/dummy/tmp/cache/assets/development/sprockets/d9044923a28026f1c6540cebecebb7e0 +0 -0
  115. data/test/dummy/tmp/cache/assets/development/sprockets/deacdc0b128cd87d3275fed230cbb7d5 +0 -0
  116. data/test/dummy/tmp/cache/assets/development/sprockets/fbbc449abae90cbd209354c203662388 +0 -0
  117. data/test/helpers/skydrive/ember_helper_test.rb +0 -6
  118. data/test/integration/navigation_test.rb +0 -10
  119. data/test/skydrive_test.rb +0 -7
  120. data/test/test_helper.rb +0 -15
@@ -45,4 +45,4 @@ module Skydrive
45
45
  ))
46
46
  end
47
47
  end
48
- end
48
+ end
@@ -10,10 +10,6 @@ module Skydrive
10
10
  head :unauthorized
11
11
  return
12
12
  end
13
-
14
- if current_user.token && current_user.token.not_before > Time.now
15
- current_user.token.refresh!(skydrive_client)
16
- end
17
13
  end
18
14
 
19
15
  def index
@@ -56,4 +52,3 @@ module Skydrive
56
52
  end
57
53
  end
58
54
  end
59
-
@@ -1,6 +1,14 @@
1
+ require 'raven'
1
2
  require 'ims/lti'
2
3
 
3
4
  module Skydrive
5
+
6
+ ERROR_NO_API_KEY = "Unable to get an access token, your state is invalid"
7
+ ERROR_JSON_PARSE = "JSON::ParserError"
8
+ ERROR_SKY_DRIVE_API = "Skydrive::APIErrorException"
9
+
10
+
11
+
4
12
  class LaunchController < ApplicationController
5
13
  include ActionController::Cookies
6
14
  before_filter :ensure_authenticated_user, only: :skydrive_authorized
@@ -46,16 +54,26 @@ module Skydrive
46
54
  return
47
55
  end
48
56
 
49
- user = @account.users.where(username: tp.user_id).first ||
57
+ user_id = tp.get_custom_param('masquerading_user_id') || tp.user_id
58
+ is_masquerading = user_id == tp.get_custom_param('masquerading_user_id')
59
+ name = is_masquerading ? 'masqueraded session' : tp.lis_person_name_full
60
+ email = is_masquerading ? 'masqueraded session' : tp.lis_person_contact_email_primary
61
+
62
+ user = @account.users.where(username: user_id).first ||
50
63
  @account.users.create(
51
- lti_user_id: tp.user_id,
52
- name: tp.lis_person_name_full,
53
- username: tp.user_id,
54
- email: tp.lis_person_contact_email_primary,
64
+ lti_user_id: user_id,
65
+ name: name,
66
+ username: user_id,
67
+ email: email
55
68
  )
56
69
 
57
- user.token = Token.create unless user.token
70
+ if !is_masquerading && (user.email != email || user.name != name)
71
+ user.email = email
72
+ user.name = name
73
+ user.save!
74
+ end
58
75
 
76
+ user.ensure_token
59
77
  user.cleanup_api_keys
60
78
 
61
79
  code = user.session_api_key(params).oauth_code
@@ -65,30 +83,40 @@ module Skydrive
65
83
  def skydrive_authorized
66
84
  skydrive_token = current_user.token
67
85
  if skydrive_token && skydrive_token.requires_refresh?
68
- skydrive_token.refresh!(skydrive_client)
86
+ begin
87
+ skydrive_client.refresh_token = skydrive_token.refresh_token
88
+ skydrive_token.refresh!(skydrive_client)
89
+ rescue Skydrive::APIResponseErrorException => error
90
+ current_user.reset_token!
91
+ end
69
92
  end
70
93
 
71
94
  if skydrive_token && skydrive_token.is_valid?
72
95
  render json: {}, status: 201
73
96
  else
74
97
  code = current_user.api_keys.active.skydrive_oauth.create.oauth_code
75
-
76
98
  render text: skydrive_client.oauth_authorize_redirect_uri(microsoft_oauth_url, state: code), status: 401
77
99
  end
78
100
  end
79
101
 
80
102
  def microsoft_oauth
81
- @current_user = ApiKey.trade_oauth_code_for_access_token(params['state']).user
82
-
83
- skydrive_client.request_oauth_token(params['code'], microsoft_oauth_url)
84
- service = skydrive_client.get_my_files_service()
85
- current_user.token.resource = service["serviceResourceId"]
86
- current_user.token.refresh!(skydrive_client)
87
-
88
- personal_url = skydrive_client.get_personal_url(service["serviceEndpointUri"]).gsub(/\/Documents$/,'/')
89
- current_user.token.update_attribute(:personal_url, personal_url)
90
-
91
- redirect_to "#{root_path}oauth/callback"
103
+ begin
104
+ api_key = ApiKey.trade_oauth_code_for_access_token(params['state'])
105
+ raise RuntimeError, ERROR_NO_API_KEY unless api_key
106
+
107
+ @current_user = api_key.user
108
+ skydrive_client.request_oauth_token(params['code'], microsoft_oauth_url)
109
+ service = skydrive_client.get_my_files_service()
110
+ current_user.token.resource = service["serviceResourceId"]
111
+ current_user.token.refresh!(skydrive_client)
112
+
113
+ personal_url = skydrive_client.get_personal_url(service["serviceEndpointUri"]).gsub(/\/Documents$/,'/')
114
+ current_user.token.update_attribute(:personal_url, personal_url)
115
+
116
+ redirect_to "#{root_path}oauth/callback"
117
+ rescue Exception => api_error
118
+ launch_exception_handler api_error
119
+ end
92
120
  end
93
121
 
94
122
  def xml_config
@@ -107,6 +135,7 @@ module Skydrive
107
135
  tc.canvas_homework_submission!
108
136
  tc.canvas_account_navigation!
109
137
  tc.canvas_course_navigation!(visibility: 'admin')
138
+ tc.set_custom_param('masquerading_user_id', '$Canvas.masqueradingUser.userId')
110
139
  render xml: tc.to_xml
111
140
  end
112
141
 
@@ -114,5 +143,26 @@ module Skydrive
114
143
  render json: {}, status: 200
115
144
  current_user.token.destroy
116
145
  end
146
+
147
+ def launch_error
148
+ @title = %s{Ooops! Something went terribly wrong!}
149
+ @message = %s{Not sure what happened, or how you got here?!}
150
+ render status: 400, layout: "skydrive/error"
151
+ end
152
+
153
+ private
154
+ def launch_exception_handler(api_error)
155
+ RavenLogger.capture_exception(api_error)
156
+ @api_error = api_error
157
+ @title = %s{Ooops! Something went terribly wrong!}
158
+ @message = "An error or invalid response was received from the OneDrive API. Close any popups, refresh the page, and relaunch OneDrive."
159
+
160
+ if api_error.message == ERROR_NO_API_KEY
161
+ @title = %s{Unable to retrieve an access token}
162
+ @message = %s{It looks like your using an old page. Close any popups, and relaunch OneDrive.}
163
+ end
164
+
165
+ render action: :launch_error, status: 400, layout: "skydrive/error"
166
+ end
117
167
  end
118
168
  end
@@ -9,4 +9,4 @@ module Skydrive
9
9
  end
10
10
  end
11
11
  end
12
- end
12
+ end
@@ -13,13 +13,11 @@ module Skydrive
13
13
 
14
14
  def refresh!(client)
15
15
  results = {}
16
-
17
16
  results = client.refresh_token(resource: resource)
18
-
19
17
  if results.key? 'access_token'
20
18
  attrs = ['token_type', 'expires_in', 'expires_on', 'not_before', 'resource', 'access_token', 'refresh_token']
21
19
  update_attributes(results.reject{|a| !attrs.include?(a)})
22
20
  end
23
21
  end
24
22
  end
25
- end
23
+ end
@@ -24,5 +24,23 @@ module Skydrive
24
24
  def valid_skydrive_token?
25
25
  !!self.token && self.token.is_valid?
26
26
  end
27
+
28
+ def ensure_token
29
+ self.token = self.create_token unless self.token
30
+ end
31
+
32
+ def reset_token!
33
+ self.token.delete
34
+ ensure_token
35
+ end
36
+
37
+ # Convenience method
38
+ def onedrive_client
39
+ @onedrive_client ||=
40
+ Client.new(SHAREPOINT.merge(
41
+ personal_url: self.token.personal_url,
42
+ token: self.token.access_token
43
+ ))
44
+ end
27
45
  end
28
46
  end
@@ -21,4 +21,3 @@
21
21
  <% end %>
22
22
  </body>
23
23
  </html>
24
-
@@ -1 +1 @@
1
- <p>result : <%= @result %></p>
1
+ <p>result : <%= @result %></p>
@@ -0,0 +1,4 @@
1
+ <h2><%= @title %></h2>
2
+ <p>
3
+ <%= @message %>
4
+ </p>
@@ -1 +1,2 @@
1
- SHAREPOINT = YAML.load(File.read("#{Rails.root}/config/sharepoint.yml"))[Rails.env].symbolize_keys
1
+ require 'yaml'
2
+ SHAREPOINT = YAML.load(File.read("#{Rails.root}/config/sharepoint.yml"))[Rails.env].symbolize_keys
data/config/routes.rb CHANGED
@@ -24,6 +24,7 @@ Skydrive::Engine.routes.draw do
24
24
  get 'config' => 'launch#xml_config'
25
25
 
26
26
  post 'oauth2/token' => 'api_keys#oauth2_token'
27
+ match 'error' => 'launch#launch_error', :via => [:get, :post]
27
28
 
28
29
  # forward all other requests to react application
29
30
  get '*path', to: 'ember#index'
@@ -3,16 +3,32 @@ require 'curb'
3
3
  require 'json'
4
4
  require 'mimemagic'
5
5
  require 'jwt'
6
+ require 'skydrive/raven_logger'
6
7
 
7
8
 
8
9
  module Skydrive
10
+ class APIErrorException < RuntimeError
11
+ end
12
+
13
+ class APIResponseErrorException < RuntimeError
14
+ attr_reader :response, :code, :description
15
+ def initialize(response)
16
+ @response = response
17
+ @code = response['error']
18
+ @description = response['error_description']
19
+ super("#{@code}: #{@description}\n#{response}")
20
+ end
21
+ end
22
+
9
23
  class Client
10
24
  include ActionView::Helpers::NumberHelper
11
25
 
12
26
  attr_accessor :client_id, :client_secret, :guid, :personal_url, :token, :refresh_token
13
27
 
14
28
  def initialize(options = {})
15
- options.each { |key, val| self.send("#{key}=", val) if self.respond_to?("#{key}=") }
29
+ options.each do |key, val|
30
+ self.send("#{key}=", val) if self.respond_to?("#{key}=")
31
+ end
16
32
  end
17
33
 
18
34
  # URL used to authorize this app for a sharepoint tenant
@@ -42,7 +58,7 @@ module Skydrive
42
58
 
43
59
  RestClient.post endpoint, options do |response, request, result|
44
60
  log_restclient_response(response, request, result)
45
- results = format_results(JSON.parse(response))
61
+ results = format_results(parse_api_response(response))
46
62
  self.token = results['access_token']
47
63
  self.refresh_token = results['refresh_token']
48
64
  results
@@ -69,7 +85,7 @@ module Skydrive
69
85
 
70
86
  RestClient.post endpoint, options do |response, request, result|
71
87
  log_restclient_response(response, request, result)
72
- results = format_results(JSON.parse(response))
88
+ results = format_results(parse_api_response(response))
73
89
  self.token = results['access_token']
74
90
  self.refresh_token = results['refresh_token']
75
91
  results
@@ -123,7 +139,7 @@ module Skydrive
123
139
  new_file.content_tag = f['ContentTag']
124
140
  folder.files << new_file
125
141
  end
126
-
142
+
127
143
  sub_folders = api_call(CGI::unescape(data['Folders']['__deferred']['uri']))['results']
128
144
  sub_folders.each do |sf|
129
145
 
@@ -152,37 +168,49 @@ module Skydrive
152
168
  end
153
169
 
154
170
  def api_call(url, headers = {})
155
- url.gsub!("https:/i", "https://i")
156
- uri = URI.escape(url)
171
+ begin
157
172
 
158
- pid = generate_pid
173
+ url.gsub!("https:/i", "https://i")
174
+ uri = URI.escape(url)
159
175
 
160
- headers['Authorization'] = "Bearer #{token}" unless headers.has_key? 'Authorization'
161
- headers['Accept'] = "application/json; odata=verbose" unless headers.has_key? 'Accept'
162
176
 
163
- c = Curl::Easy.new(uri) do |http|
164
- headers.each {|k,v| http.headers[k] = v if v }
165
- end
177
+ headers['Authorization'] = "Bearer #{token}" unless headers.has_key? 'Authorization'
178
+ headers['Accept'] = "application/json; odata=verbose" unless headers.has_key? 'Accept'
166
179
 
167
- headers = []
168
- buffer = ""
169
- c.on_body { |data|
170
- buffer << data
171
- data.size
172
- }
173
- c.on_header { |data|
174
- headers << data
175
- data.size
176
- }
177
- c.perform
180
+ c = Curl::Easy.new(uri) do |http|
181
+ headers.each {|k,v| http.headers[k] = v if v }
182
+ end
178
183
 
179
- Skydrive.logger.info("[#{pid}] SKYDRIVE REQUEST: #{uri.to_s}")
180
- headerOutput = c.headers.map {|k,v| "#{k}: #{v}"}.join("\n - ")
181
- Skydrive.logger.info("[#{pid}] SKYDRIVE REQUEST HEADERS:\n - #{headerOutput}")
182
- Skydrive.logger.info("[#{pid}] SKYDRIVE RESPONSE HEADERS:\n - #{headers.join(' - ')}")
183
- Skydrive.logger.info("[#{pid}] SKYDRIVE RESPONSE BODY:\n#{buffer}");
184
+ headers = []
185
+ buffer = ""
186
+ c.on_body { |data|
187
+ buffer << data
188
+ data.size
189
+ }
190
+ c.on_header { |data|
191
+ headers << data
192
+ data.size
193
+ }
194
+ c.perform
195
+
196
+ result = parse_api_response(buffer)
197
+ rescue Exception => error
198
+ pid = generate_pid
199
+ headerOutput = c.headers.map {|k,v| "#{k}: #{v}"}.join("\n[#{pid}] - ")
200
+ backtrace_output = error.backtrace.join("\n[#{pid}] - ")
201
+ buffer_output = buffer.split("\n").join("\n[#{pid}] - ")
202
+
203
+ Skydrive.logger.error("[#{pid}] SKYDRIVE ERROR: #{error.class.to_s} ◊ #{error}")
204
+ Skydrive.logger.info("[#{pid}] SKYDRIVE BACKTRACE: \n[#{pid}] - #{backtrace_output}")
205
+ Skydrive.logger.info("[#{pid}] SKYDRIVE REQUEST: #{uri.to_s}")
206
+ Skydrive.logger.info("[#{pid}] SKYDRIVE REQUEST HEADERS:\n[#{pid}] - #{headerOutput}")
207
+ Skydrive.logger.info("[#{pid}] SKYDRIVE RESPONSE HEADERS:\n[#{pid}] - #{headers.join(' - ')}")
208
+ Skydrive.logger.info("[#{pid}] SKYDRIVE RESPONSE BODY:\n[#{pid}] - #{buffer_output}");
209
+ Skydrive.logger.info("[#{pid}] END --\n");
210
+ RavenLogger.capture_exception(error)
211
+ raise error
212
+ end
184
213
 
185
- result = JSON.parse(buffer)
186
214
  result["d"] || result
187
215
  end
188
216
 
@@ -206,5 +234,10 @@ module Skydrive
206
234
  (0...8).map { (65 + rand(26)).chr }.join
207
235
  end
208
236
 
237
+ def parse_api_response(body)
238
+ result = JSON.parse(body)
239
+ raise APIResponseErrorException, result if result["error"]
240
+ result
241
+ end
209
242
  end
210
243
  end
data/lib/skydrive/file.rb CHANGED
@@ -33,7 +33,7 @@ module Skydrive
33
33
  self.kind = mm.comment
34
34
  self.suffix = mm.extensions.last
35
35
 
36
- if allowed_extensions.empty? || (allowed_extensions & mm.extensions).size > 0
36
+ if allowed_extensions.blank? || (allowed_extensions & mm.extensions).size > 0
37
37
  self.is_embeddable = true
38
38
  else
39
39
  self.is_embeddable = false
@@ -0,0 +1,16 @@
1
+ require 'raven'
2
+
3
+ module Skydrive
4
+ class RavenLogger
5
+ ENV_KEY = "RAVEN_SKYDRIVE_DSN"
6
+
7
+ def self.capture_exception(error)
8
+ return if Rails.env.test?
9
+ if (ENV[ENV_KEY])
10
+ config = Raven.configuration
11
+ config.dsn = ENV.fetch(ENV_KEY)
12
+ end
13
+ Raven.capture_exception(error, {configuration: config})
14
+ end
15
+ end
16
+ end
@@ -1,3 +1,3 @@
1
1
  module Skydrive
2
- VERSION = "1.1.0"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -0,0 +1,209 @@
1
+ require 'spec_helper'
2
+
3
+ module Skydrive
4
+ describe LaunchController, :type => :controller do
5
+
6
+ account = Account.find_or_create_by!(key: "one", secret: "not_two" )
7
+ let(:email) {'user@email.com'}
8
+ let(:username) {'user'}
9
+ let(:name) {'User'}
10
+ let(:sharepoint_client_domain) {'login.windows.net'}
11
+
12
+ let(:account) {account}
13
+ let(:user) {account.users.find_or_initialize_by(email: 'user@email.com', username: 'user', name: 'User')}
14
+
15
+ describe '#basic_launch' do
16
+
17
+ before(:each) do
18
+ tp = IMS::LTI::ToolProvider.new(nil, nil, {})
19
+ tp.lis_person_contact_email_primary = email
20
+ tp.set_custom_param('sharepoint_client_domain', 'test')
21
+ tp.user_id = username
22
+ tp.lis_person_name_full = name
23
+
24
+ allow_any_instance_of(LaunchController).to receive(:tool_provider).and_return(tp)
25
+ controller.instance_variable_set("@account", account)
26
+ end
27
+
28
+ it "creates a new user" do
29
+ expect(User.where(email: email).count).to be(0)
30
+
31
+ post 'basic_launch', use_route: :skydrive
32
+ expect(response).to be_redirect, response.body
33
+
34
+ user = User.where(email: email).first!
35
+ expect(user.email).to eq(email)
36
+ expect(user.username).to eq(username)
37
+ expect(user.name).to eq(name)
38
+
39
+ expect(user.token).to be_a Token
40
+ end
41
+
42
+ it "updates existing users' name and email" do
43
+ user.save
44
+ tp = IMS::LTI::ToolProvider.new(nil, nil, {})
45
+ tp.lis_person_contact_email_primary = "updated_email@example.com"
46
+ tp.set_custom_param('sharepoint_client_domain', 'test')
47
+ tp.user_id = username
48
+ tp.lis_person_name_full = "Updated Name"
49
+ allow_any_instance_of(LaunchController).to receive(:tool_provider).and_return(tp)
50
+
51
+ post 'basic_launch', use_route: :skydrive
52
+ expect(response).to be_redirect, response.body
53
+
54
+ user = User.where(username: username).first!
55
+ expect(user.email).to eq("updated_email@example.com")
56
+ expect(user.username).to eq(username)
57
+ expect(user.name).to eq("Updated Name")
58
+ expect(user.token).to be_a Token
59
+ end
60
+
61
+ it "returns a valid oauth code" do
62
+ post 'basic_launch', use_route: :skydrive
63
+
64
+ code = response.header['Location'].split('/').last
65
+ api_key = ApiKey.where(oauth_code: code).first!
66
+ end
67
+
68
+ it "find existing users" do
69
+ user.save
70
+
71
+ post 'basic_launch', use_route: :skydrive
72
+ code = response.header['Location'].split('/').last
73
+ api_key = ApiKey.where(oauth_code: code).first!
74
+ expect(api_key.user).to eq(user)
75
+ end
76
+
77
+ it "cleans out expired tokens" do
78
+ user.save
79
+ api_key = user.session_api_key
80
+ api_key.update_attributes(expired_at: Time.now)
81
+
82
+ post 'basic_launch', use_route: :skydrive
83
+
84
+ expect(ApiKey.where(id: api_key.id).count).to be(0)
85
+ end
86
+ end
87
+
88
+
89
+ let(:masquerading_user_id) {'this_is_a_masqueraded_id'}
90
+ let(:masquerading_email) {'masquerading_user@asd.com'}
91
+ let(:masquerading_name) { 'Dr. masquerading name'}
92
+ let(:masquerading_user) {account.users.find_or_initialize_by(email: masquerading_email, username: masquerading_user_id, name: masquerading_name)}
93
+
94
+ describe '#basic_launch masqueraded' do
95
+
96
+ before(:each) do
97
+ tp = IMS::LTI::ToolProvider.new(nil, nil, {})
98
+ tp.lis_person_contact_email_primary = email
99
+ tp.set_custom_param('sharepoint_client_domain', 'test')
100
+ tp.set_custom_param('masquerading_user_id', masquerading_user_id)
101
+ tp.user_id = username
102
+ tp.lis_person_name_full = name
103
+
104
+ allow_any_instance_of(LaunchController).to receive(:tool_provider).and_return(tp)
105
+ controller.instance_variable_set("@account", account)
106
+ end
107
+
108
+ it "creates a new user with masqueraded user" do
109
+ expect(User.where(username: masquerading_user_id).count).to be(0)
110
+
111
+ post 'basic_launch', use_route: :skydrive
112
+ expect(response).to be_redirect, response.body
113
+
114
+ user = User.where(username: masquerading_user_id).first!
115
+ expect(user.email).to_not eq(email)
116
+ expect(user.username).to eq(masquerading_user_id)
117
+ expect(user.name).to_not eq(name)
118
+
119
+ expect(user.token).to be_a Token
120
+ end
121
+
122
+ it "returns a valid oauth code with masqueraded user" do
123
+ post 'basic_launch', use_route: :skydrive
124
+
125
+ code = response.header['Location'].split('/').last
126
+ api_key = ApiKey.where(oauth_code: code).first!
127
+ end
128
+
129
+ it "find existing users with masqueraded user" do
130
+ masquerading_user.save
131
+
132
+ post 'basic_launch', use_route: :skydrive
133
+ code = response.header['Location'].split('/').last
134
+ api_key = ApiKey.where(oauth_code: code).first!
135
+ expect(api_key.user).to eq(masquerading_user)
136
+ end
137
+
138
+ it "find existing users with masqueraded user" do
139
+ masquerading_user.save
140
+
141
+ post 'basic_launch', use_route: :skydrive
142
+ code = response.header['Location'].split('/').last
143
+ api_key = ApiKey.where(oauth_code: code).first!
144
+
145
+ user = api_key.user
146
+ expect(user).to eq(masquerading_user)
147
+ expect(user.email).to eq(masquerading_email)
148
+ expect(user.username).to eq(masquerading_user_id)
149
+ expect(user.name).to eq(masquerading_name)
150
+ end
151
+
152
+
153
+ it "doesn't update existing users' name and email if they are masquerading" do
154
+
155
+ masquerading_user.save
156
+
157
+ tp = IMS::LTI::ToolProvider.new(nil, nil, {})
158
+ tp.lis_person_contact_email_primary = "updated_email@example.com"
159
+ tp.set_custom_param('sharepoint_client_domain', 'test')
160
+ tp.user_id = username
161
+ tp.lis_person_name_full = "Updated Name"
162
+ allow_any_instance_of(LaunchController).to receive(:tool_provider).and_return(tp)
163
+
164
+ post 'basic_launch', use_route: :skydrive
165
+ expect(response).to be_redirect, response.body
166
+
167
+ user = User.where(username: username).first!
168
+ expect(user.email).to eq("updated_email@example.com")
169
+ expect(user.username).to eq(username)
170
+ expect(user.name).to eq("Updated Name")
171
+ expect(user.token).to be_a Token
172
+ end
173
+
174
+ it "cleans out expired tokens with masqueraded user" do
175
+ masquerading_user.save
176
+ api_key = masquerading_user.session_api_key
177
+ api_key.update_attributes(expired_at: Time.now)
178
+
179
+ post 'basic_launch', use_route: :skydrive
180
+
181
+ expect(ApiKey.where(id: api_key.id).count).to be(0)
182
+ end
183
+ end
184
+
185
+ describe '#skydrive_authorized' do
186
+
187
+ it "returns a skydrive_auth url when the skydrive token is invalid" do
188
+ user.save
189
+ user.token = Token.create()
190
+
191
+ allow_any_instance_of(LaunchController).to receive(:current_user).and_return(user)
192
+
193
+ post 'skydrive_authorized', use_route: :skydrive
194
+ expect(response.code).to eq("401")
195
+ expect(response.body).to include sharepoint_client_domain
196
+ end
197
+
198
+ it "returns a 200 when the skydrive token is valid" do
199
+ user.save
200
+ user.token = Token.create(access_token: 'token', expires_on: 1.week.from_now)
201
+
202
+ allow_any_instance_of(LaunchController).to receive(:current_user).and_return(user)
203
+
204
+ post 'skydrive_authorized', use_route: :skydrive
205
+ expect(response).to be_success
206
+ end
207
+ end
208
+ end
209
+ end