qalam_oauth_engine 2.2.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +28 -0
  3. data/README.md +110 -0
  4. data/Rakefile +28 -0
  5. data/app/controllers/canvas_oauth/canvas_controller.rb +26 -0
  6. data/app/controllers/canvas_oauth/oauth_application_controller.rb +5 -0
  7. data/app/models/canvas_oauth/authorization.rb +46 -0
  8. data/config/canvas.yml.example +12 -0
  9. data/config/routes.rb +3 -0
  10. data/db/migrate/20121121005358_create_canvas_oauth_authorizations.rb +16 -0
  11. data/lib/canvas_oauth.rb +26 -0
  12. data/lib/canvas_oauth/canvas_api.rb +331 -0
  13. data/lib/canvas_oauth/canvas_api_extensions.rb +9 -0
  14. data/lib/canvas_oauth/canvas_application.rb +73 -0
  15. data/lib/canvas_oauth/canvas_config.rb +34 -0
  16. data/lib/canvas_oauth/config.rb +3 -0
  17. data/lib/canvas_oauth/default_utf8_parser.rb +13 -0
  18. data/lib/canvas_oauth/engine.rb +15 -0
  19. data/lib/canvas_oauth/version.rb +3 -0
  20. data/lib/tasks/canvas_oauth_tasks.rake +1 -0
  21. data/spec/controllers/canvas_oauth/canvas_controller_spec.rb +90 -0
  22. data/spec/dummy/README.rdoc +261 -0
  23. data/spec/dummy/Rakefile +7 -0
  24. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  25. data/spec/dummy/app/controllers/welcome_controller.rb +5 -0
  26. data/spec/dummy/config.ru +4 -0
  27. data/spec/dummy/config/application.rb +57 -0
  28. data/spec/dummy/config/boot.rb +10 -0
  29. data/spec/dummy/config/canvas.yml +12 -0
  30. data/spec/dummy/config/database.yml +25 -0
  31. data/spec/dummy/config/environment.rb +5 -0
  32. data/spec/dummy/config/environments/development.rb +26 -0
  33. data/spec/dummy/config/environments/production.rb +69 -0
  34. data/spec/dummy/config/environments/test.rb +33 -0
  35. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  36. data/spec/dummy/config/initializers/inflections.rb +15 -0
  37. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  38. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  39. data/spec/dummy/config/initializers/session_store.rb +8 -0
  40. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  41. data/spec/dummy/config/locales/en.yml +5 -0
  42. data/spec/dummy/config/routes.rb +4 -0
  43. data/spec/dummy/db/development.sqlite3 +0 -0
  44. data/spec/dummy/db/migrate/20160711200737_create_canvas_oauth_authorizations.canvas_oauth.rb +16 -0
  45. data/spec/dummy/db/schema.rb +27 -0
  46. data/spec/dummy/db/test.sqlite3 +0 -0
  47. data/spec/dummy/log/development.log +1 -0
  48. data/spec/dummy/log/test.log +1630 -0
  49. data/spec/dummy/public/404.html +26 -0
  50. data/spec/dummy/public/422.html +26 -0
  51. data/spec/dummy/public/500.html +25 -0
  52. data/spec/dummy/public/favicon.ico +0 -0
  53. data/spec/dummy/public/robots.txt +5 -0
  54. data/spec/dummy/script/rails +6 -0
  55. data/spec/lib/canvas_oauth/canvas_api_extensions_spec.rb +13 -0
  56. data/spec/lib/canvas_oauth/canvas_api_spec.rb +364 -0
  57. data/spec/lib/canvas_oauth/default_utf8_parser_spec.rb +21 -0
  58. data/spec/models/canvas_oauth/authorization_spec.rb +47 -0
  59. data/spec/spec_helper.rb +64 -0
  60. metadata +377 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ca4df23009a1ac767c658d767afb25ce07614896e9a64c4a29902034377be29c
4
+ data.tar.gz: f64d346bf4ef5619e98b491de827602c8b18bec4c38d2e8ee3a761be4b85e858
5
+ SHA512:
6
+ metadata.gz: b3f808048822cc4442059f75203df5662bc0657cedca326842c2a909ed05e16688ffd3bc295addb02081363cc6d37a68e72e898d49ae4b19fa1cfa4c077c91f7
7
+ data.tar.gz: 476c685d5a946c4a89494fed0500e439fae6bc1adbec7e51f7f04497ecdfc9e48d5a05be906bd4700fc0ea478360bbcec69a68552812685398472a7d5f4ff897
data/MIT-LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'CanvasOauth'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+ Bundler::GemHelper.install_tasks
27
+
28
+ task :default => :spec
data/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # CanvasOauth
2
+
3
+ CanvasOauth is a mountable engine for handling the oauth workflow with canvas
4
+ and making api calls from your rails app. This is tested with Rails 3.2, we'll
5
+ be looking at verifying with Rails 4 soon.
6
+
7
+ ## Warning
8
+
9
+ The current version of this gem relies heavily on being able to access the user
10
+ session. Since LTI apps generally run in an iframe inside the LMS, this means
11
+ that tools using this engine MUST run on the same root domain as the LMS they
12
+ are plugging into. This is obviously not ideal, and we will be working to
13
+ remove this limitation in the future.
14
+
15
+ ## Installation
16
+
17
+ Add the gem to your `Gemfile` with the following line, and then `bundle install`
18
+
19
+ ```
20
+ gem 'qalam_oauth_engine', :require => 'canvas_oauth'
21
+ ```
22
+
23
+ Then, mount the engine to your app by adding this line to your `routes.rb` file
24
+
25
+ ```
26
+ mount CanvasOauth::Engine => "/canvas_oauth"
27
+ ```
28
+
29
+ Next, include the engine in your `ApplicationController`
30
+
31
+ ```
32
+ class ApplicationController < ActionController::Base
33
+ include CanvasOauth::CanvasApplication
34
+
35
+ ...
36
+ end
37
+ ```
38
+
39
+ After that, create an `canvas.yml` file in your `config/` folder that looks something
40
+ like this (or see `config/canvas.yml.example` for a template):
41
+
42
+ ```
43
+ default: &default
44
+ key: your_key
45
+ secret: your_secret
46
+
47
+ development:
48
+ <<: *default
49
+
50
+ test:
51
+ <<: *default
52
+
53
+ production:
54
+ <<: *default
55
+ ```
56
+
57
+ The values of key and secret should be set from the developer key that you
58
+ generate in the canvas application.
59
+
60
+ Finally, run migrations:
61
+
62
+ ```
63
+ bundle install
64
+ bundle exec rake railties:install:migrations
65
+ bundle exec rake db:migrate
66
+ ```
67
+
68
+ This will create the `canvas_oauth_authorizations` table which stores
69
+ successful oauth tokens for later use, so the user does not have to accept the
70
+ oauth login each time they use the app.
71
+
72
+ ## Usage
73
+
74
+ The engine only uses one url, whatever it is mounted to, to serve as the
75
+ `redirect_url` in the oauth workflow.
76
+
77
+ The engine sets up a global `before_filter`, which checks for a valid oauth
78
+ token, and if one does not exist, starts the oauth login flow. It handles
79
+ posting the oauth request, verifying the result and redirecting to the
80
+ application root. It exposes the following methods to your controllers:
81
+
82
+ * `canvas`
83
+ * `canvas_token`
84
+
85
+ The first is an instance of HTTParty ready to make api requests to your canvas
86
+ application. The second is if you need access to the oauth token directly.
87
+
88
+ ## Configuring the Tool Consumer
89
+
90
+ You will a developer key and secret from canvas, which should be entered into
91
+ you `canvas.yml` file.
92
+
93
+ ## Example
94
+
95
+ You can see and interact with an example of an app using this engine by looking
96
+ at `spec/dummy`. This is a full rails app which integrates the gem and has
97
+ a simple index page that says 'Hello Oauth' if the app is launched and the
98
+ oauth flow is successful.
99
+
100
+ ## About Oauth
101
+
102
+ TODO...
103
+
104
+ ## Contributing
105
+
106
+ 1. Fork it
107
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
108
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
109
+ 4. Push to the branch (`git push origin my-new-feature`)
110
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'CanvasOauth'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+ Bundler::GemHelper.install_tasks
27
+
28
+ task :default => :spec
@@ -0,0 +1,26 @@
1
+ module CanvasOauth
2
+ class CanvasController < CanvasOauth::OauthApplicationController
3
+ skip_before_action :request_canvas_authentication
4
+
5
+ def oauth
6
+ if verify_oauth2_state(params[:state]) && params[:code]
7
+ if token = canvas.get_access_token(params[:code])
8
+ refresh_token = canvas.refresh_token
9
+ if CanvasOauth::Authorization.cache_token(token, refresh_token, user_id, tool_consumer_instance_guid)
10
+ redirect_to main_app.root_path
11
+ else
12
+ render plain: "Error: unable to save token"
13
+ end
14
+ else
15
+ render plain: "Error: invalid code - #{params[:code]}"
16
+ end
17
+ else
18
+ render plain: "#{CanvasOauth::Config.tool_name} needs access to your account in order to function properly. Please try again and click log in to approve the integration."
19
+ end
20
+ end
21
+
22
+ def verify_oauth2_state(callback_state)
23
+ callback_state.present? && callback_state == session.delete(:oauth2_state)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,5 @@
1
+ module CanvasOauth
2
+ class OauthApplicationController < ActionController::Base
3
+ include CanvasOauth::CanvasApplication
4
+ end
5
+ end
@@ -0,0 +1,46 @@
1
+ module CanvasOauth
2
+ class Authorization < ActiveRecord::Base
3
+ validates :canvas_user_id, :token, :refresh_token, :last_used_at, presence: true
4
+
5
+ def self.cache_token(token, refresh_token, user_id, tool_consumer_instance_guid)
6
+ create do |t|
7
+ t.token = token
8
+ t.refresh_token = refresh_token
9
+ t.canvas_user_id = user_id
10
+ t.tool_consumer_instance_guid = tool_consumer_instance_guid
11
+ t.last_used_at = Time.now
12
+ end
13
+ end
14
+
15
+ def self.fetch_token(user_id, tool_consumer_instance_guid)
16
+ user_tokens = where(canvas_user_id: user_id, tool_consumer_instance_guid: tool_consumer_instance_guid).order("created_at DESC")
17
+ if canvas_auth = user_tokens.first
18
+ canvas_auth.update_attribute(:last_used_at, Time.now)
19
+ return canvas_auth.token
20
+ end
21
+ end
22
+
23
+ def self.fetch_refresh_token(user_id, tool_consumer_instance_guid)
24
+ user_tokens = where(canvas_user_id: user_id, tool_consumer_instance_guid: tool_consumer_instance_guid).order("created_at DESC")
25
+ if canvas_auth = user_tokens.first
26
+ canvas_auth.update_attribute(:last_used_at, Time.now)
27
+ return canvas_auth.refresh_token
28
+ end
29
+ end
30
+
31
+ def self.update_token(refresh_token, token)
32
+ if token
33
+ user_tokens = where(refresh_token: refresh_token).order("created_at DESC")
34
+ if canvas_auth = user_tokens.first
35
+ canvas_auth.update_attribute(:token, token)
36
+ canvas_auth.update_attribute(:last_used_at, Time.now)
37
+ return canvas_auth.token
38
+ end
39
+ end
40
+ end
41
+
42
+ def self.clear_tokens(user_id, tool_consumer_instance_guid)
43
+ where(canvas_user_id: user_id, tool_consumer_instance_guid: tool_consumer_instance_guid).destroy_all
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,12 @@
1
+ default: &default
2
+ key: your_key
3
+ secret: your_secret
4
+
5
+ development:
6
+ <<: *default
7
+
8
+ test:
9
+ <<: *default
10
+
11
+ cucumber:
12
+ <<: *default
data/config/routes.rb ADDED
@@ -0,0 +1,3 @@
1
+ CanvasOauth::Engine.routes.draw do
2
+ match "/", to: "canvas#oauth", via: :get, as: :canvas_oauth
3
+ end
@@ -0,0 +1,16 @@
1
+ class CreateCanvasOauthAuthorizations < ActiveRecord::Migration[4.2]
2
+ def change
3
+ create_table "canvas_oauth_authorizations", :force => true do |t|
4
+ t.integer "canvas_user_id", :limit => 8
5
+ t.string "tool_consumer_instance_guid", :null => false
6
+ t.string "token"
7
+ t.string "refresh_token"
8
+ t.datetime "last_used_at"
9
+ t.datetime "created_at", :null => false
10
+ t.datetime "updated_at", :null => false
11
+ end
12
+
13
+ add_index :canvas_oauth_authorizations, [:canvas_user_id, :tool_consumer_instance_guid],
14
+ name: 'index_canvas_oauth_auths_on_user_id_and_tciguid'
15
+ end
16
+ end
@@ -0,0 +1,26 @@
1
+ require 'ostruct'
2
+
3
+ require 'httparty'
4
+ require 'link_header'
5
+
6
+ require "canvas_oauth/config"
7
+ require "canvas_oauth/canvas_application"
8
+ require 'canvas_oauth/canvas_api'
9
+ require 'canvas_oauth/canvas_api_extensions'
10
+ require 'canvas_oauth/canvas_config'
11
+ require 'canvas_oauth/default_utf8_parser'
12
+
13
+ module CanvasOauth
14
+ mattr_accessor :app_root
15
+
16
+ def self.setup
17
+ yield self
18
+ end
19
+
20
+ def self.config
21
+ yield(CanvasOauth::Config)
22
+ end
23
+ end
24
+
25
+ require "canvas_oauth/engine"
26
+
@@ -0,0 +1,331 @@
1
+ module CanvasOauth
2
+ class CanvasApi
3
+ include HTTParty
4
+ PER_PAGE = 50
5
+
6
+ attr_accessor :token, :refresh_token, :key, :secret
7
+ attr_reader :canvas_url, :canvas_user_id
8
+
9
+ def initialize(canvas_url, canvas_user_id, token, refresh_token, key, secret)
10
+ unless [key, secret].all?(&:present?)
11
+ raise "Invalid Canvas oAuth configuration"
12
+ end
13
+
14
+ self.refresh_token = refresh_token
15
+ self.canvas_url = canvas_url
16
+ self.canvas_user_id = canvas_user_id
17
+ self.token = token
18
+ self.key = key
19
+ self.secret = secret
20
+ end
21
+
22
+ def authenticated_request(method, *params)
23
+ params << {} if params.size == 1
24
+
25
+ params.last[:headers] ||= {}
26
+ params.last[:headers]['Authorization'] = "Bearer #{token}"
27
+
28
+ start = Time.now
29
+
30
+ response = self.class.send(method, *params)
31
+
32
+ Rails.logger.info {
33
+ stop = Time.now
34
+ elapsed = ((stop - start) * 1000).round(2)
35
+
36
+ params.last[:headers].reject! { |k| k == 'Authorization' }
37
+ "API call (#{elapsed}ms): #{method} #{params.inspect}"
38
+ }
39
+
40
+ if response && response.unauthorized?
41
+ if refresh_token
42
+ get_access_token_by_refresh_token
43
+ authenticated_request(method, *params)
44
+ else
45
+ if response.headers['WWW-Authenticate'].present?
46
+ raise CanvasApi::Authenticate
47
+ else
48
+ raise CanvasApi::Unauthorized
49
+ end
50
+ end
51
+ else
52
+ return response
53
+ end
54
+ end
55
+
56
+ def paginated_get(url, params = {})
57
+ params[:query] ||= {}
58
+ params[:query][:per_page] = PER_PAGE
59
+
60
+ all_pages = []
61
+
62
+ while url && current_page = authenticated_get(url, params) do
63
+ all_pages.concat(current_page) if valid_page?(current_page)
64
+
65
+ links = LinkHeader.parse(current_page.headers['link'])
66
+ url = links.find_link(["rel", "next"]).try(:href)
67
+ params[:query] = nil if params[:query]
68
+ end
69
+
70
+ all_pages
71
+ end
72
+
73
+ def get_report(account_id, report_type, params)
74
+ report = authenticated_post("/api/v1/accounts/#{account_id}/reports/#{report_type}", { body: params })
75
+ report = authenticated_get "/api/v1/accounts/#{account_id}/reports/#{report_type}/#{report['id']}"
76
+ while (report['status'] == 'created' || report['status'] == 'running')
77
+ sleep(4)
78
+ report = authenticated_get "/api/v1/accounts/#{account_id}/reports/#{report_type}/#{report['id']}"
79
+ end
80
+
81
+ if report['status'] == 'complete'
82
+ file_id = report['file_url'].match(/files\/([0-9]+)\/download/)[1]
83
+ file = get_file(file_id)
84
+ return hash_csv(self.class.get(file['url'], limit: 15, parser: DefaultUTF8Parser).parsed_response)
85
+ else
86
+ return report
87
+ end
88
+ end
89
+
90
+ def valid_page?(page)
91
+ page && page.size > 0
92
+ end
93
+
94
+ def get_file(file_id)
95
+ authenticated_get "/api/v1/files/#{file_id}"
96
+ end
97
+
98
+ def get_accounts_provisioning_report(account_id)
99
+ get_report(account_id, :provisioning_csv, 'parameters[accounts]' => true)
100
+ end
101
+
102
+ #Needs to be refactored to somewhere more generic
103
+ def hash_csv(csv_string)
104
+ require 'csv'
105
+
106
+ csv = csv_string.is_a?(String) ? CSV.parse(csv_string) : csv_string
107
+ headers = csv.shift
108
+ output = []
109
+
110
+ csv.each do |row|
111
+ hash = {}
112
+ headers.each do |header|
113
+ hash[header] = row.shift.to_s
114
+ end
115
+ output << hash
116
+ end
117
+
118
+ return output
119
+ end
120
+
121
+ def authenticated_get(*params)
122
+ authenticated_request(:get, *params)
123
+ end
124
+
125
+ def authenticated_post(*params)
126
+ authenticated_request(:post, *params)
127
+ end
128
+
129
+ def authenticated_put(*params)
130
+ authenticated_request(:put, *params)
131
+ end
132
+
133
+ def get_courses
134
+ paginated_get "/api/v1/courses"
135
+ end
136
+
137
+ def get_account(account_id)
138
+ authenticated_get "/api/v1/accounts/#{account_id}"
139
+ end
140
+
141
+ def get_account_sub_accounts(account_id)
142
+ paginated_get "/api/v1/accounts/#{account_id}/sub_accounts", { query: { :recursive => true } }
143
+ end
144
+
145
+ def get_account_courses(account_id)
146
+ paginated_get "/api/v1/accounts/#{account_id}/courses"
147
+ end
148
+
149
+ def get_account_courses(account_id)
150
+ paginated_get "/api/v1/accounts/#{account_id}/users"
151
+ end
152
+
153
+ def get_course(course_id)
154
+ authenticated_get "/api/v1/courses/#{course_id}"
155
+ end
156
+
157
+ def get_section_enrollments(section_id)
158
+ paginated_get "/api/v1/sections/#{section_id}/enrollments"
159
+ end
160
+
161
+ def get_user_enrollments(user_id)
162
+ paginated_get "/api/v1/users/#{user_id}/enrollments"
163
+ end
164
+
165
+ def get_course_users(course_id)
166
+ paginated_get "/api/v1/courses/#{course_id}/users"
167
+ end
168
+
169
+ def get_all_course_users(course_id)
170
+ paginated_get "/api/v1/courses/#{course_id}/users", { query: {enrollment_state: ["active","invited","rejected","completed","inactive"] } }
171
+ end
172
+
173
+ def get_course_teachers_and_tas(course_id)
174
+ paginated_get "/api/v1/courses/#{course_id}/users", { query: { enrollment_type: ['teacher', 'ta'] } }
175
+ end
176
+
177
+ def get_course_students(course_id)
178
+ paginated_get "/api/v1/courses/#{course_id}/students"
179
+ end
180
+
181
+ def get_course_active_students(course_id)
182
+ paginated_get "/api/v1/courses/#{course_id}/active_users"
183
+ end
184
+
185
+ def get_section(section_id)
186
+ authenticated_get "/api/v1/sections/#{section_id}"
187
+ end
188
+
189
+ def get_sections(course_id)
190
+ paginated_get "/api/v1/courses/#{course_id}/sections", { query: { :include => ['students', 'avatar_url', 'enrollments'] } }
191
+ end
192
+
193
+ def get_assignments(course_id)
194
+ paginated_get "/api/v1/courses/#{course_id}/assignments"
195
+ end
196
+
197
+ def get_assignment(course_id, assignment_id)
198
+ authenticated_get "/api/v1/courses/#{course_id}/assignments/#{assignment_id}"
199
+ end
200
+
201
+ def get_user_profile(user_id)
202
+ authenticated_get "/api/v1/users/#{user_id}/profile"
203
+ end
204
+
205
+ def create_assignment(course_id, params)
206
+ authenticated_post "/api/v1/courses/#{course_id}/assignments", { body: { assignment: params } }
207
+ end
208
+
209
+ def update_assignment(course_id, assignment_id, params)
210
+ authenticated_put "/api/v1/courses/#{course_id}/assignments/#{assignment_id}", { body: { assignment: params } }
211
+ end
212
+
213
+ def grade_assignment(course_id, assignment_id, user_id, params)
214
+ authenticated_put "/api/v1/courses/#{course_id}/assignments/#{assignment_id}/submissions/#{user_id}", { body: params }
215
+ end
216
+
217
+ def get_submission(course_id, assignment_id, user_id)
218
+ authenticated_get "/api/v1/courses/#{course_id}/assignments/#{assignment_id}/submissions/#{user_id}"
219
+ end
220
+
221
+ def course_account_id(course_id)
222
+ course = get_course(course_id)
223
+ course['account_id'] if course
224
+ end
225
+
226
+ def root_account_id(account_id)
227
+ if account_id && account = get_account(account_id)
228
+ root_id = account['root_account_id']
229
+ end
230
+
231
+ root_id || account_id
232
+ end
233
+
234
+ def course_root_account_id(course_id)
235
+ root_account_id(course_account_id(course_id))
236
+ end
237
+
238
+ def auth_url(redirect_uri, oauth2_state)
239
+ "#{canvas_url}/login/oauth2/auth?client_id=#{key}&response_type=code&state=#{oauth2_state}&redirect_uri=#{redirect_uri}"
240
+ end
241
+
242
+ def qalam_auth_url(qalam_url, redirect_uri, oauth2_state)
243
+ "#{qalam_url}/login/oauth2/auth?client_id=#{key}&response_type=code&state=#{oauth2_state}&redirect_uri=#{redirect_uri}"
244
+ end
245
+
246
+ def get_access_token(code)
247
+ params = {
248
+ body: {
249
+ client_id: key,
250
+ client_secret: secret,
251
+ code: code
252
+ }
253
+ }
254
+
255
+ response = self.class.post '/login/oauth2/token', params
256
+ self.refresh_token = response['refresh_token']
257
+ self.token = response['access_token']
258
+ end
259
+
260
+ def get_access_token_by_refresh_token
261
+ params = {
262
+ body: {
263
+ grant_type: 'refresh_token',
264
+ client_id: key,
265
+ client_secret: secret,
266
+ refresh_token: refresh_token
267
+ }
268
+ }
269
+
270
+ response = self.class.post '/login/oauth2/token', params
271
+ self.token = response['access_token']
272
+ CanvasOauth::Authorization.update_token(refresh_token, token)
273
+ end
274
+
275
+ def hex_sis_id(name, value)
276
+ hex = value.unpack("H*")[0]
277
+ return "hex:#{name}:#{hex}"
278
+ end
279
+
280
+ def canvas_url=(value)
281
+ @canvas_url = value
282
+ self.class.base_uri(value)
283
+ end
284
+
285
+ def canvas_user_id=(value)
286
+ @canvas_user_id = value
287
+ end
288
+
289
+ ### QALAM ###
290
+ def get_canvas_user_profile
291
+ authenticated_get "/api/v1/users/#{canvas_user_id}/profile"
292
+ end
293
+
294
+ def get_course_active_pages(course_id, publish=nil)
295
+ unless publish.nil?
296
+ paginated_get "/api/v1/courses/#{course_id}/pages?published=#{publish}&sort=created_at&order=desc"
297
+ else
298
+ paginated_get "/api/v1/courses/#{course_id}/pages?sort=created_at&order=desc"
299
+ end
300
+ end
301
+
302
+ def course_external_tool_update(external_tool_url, tool_id, account_id, publish)
303
+ authenticated_put "/api/v1/accounts/#{account_id}/external_tools_url", { body: {"course_navigation"=>{"enabled"=>"#{publish}"},
304
+ "tool_id"=>tool_id, "external_tool_url"=>external_tool_url} }
305
+ end
306
+
307
+ def account_external_tool_update(external_tool_url, tool_id, account_id, publish)
308
+ authenticated_put "/api/v1/accounts/#{account_id}/external_tools_url", { body: {"account_navigation"=>{"enabled"=>"#{publish}"},
309
+ "tool_id"=>tool_id, "external_tool_url"=>external_tool_url} }
310
+ end
311
+
312
+ def get_school_details(account_id)
313
+ authenticated_get "/api/v1/accounts/#{account_id}/school_details"
314
+ end
315
+
316
+ def get_school_account(account_id)
317
+ authenticated_get "/api/v1/accounts/#{account_id}/school_account"
318
+ end
319
+
320
+ def get_students_by_grade(account_id, grade_id)
321
+ paginated_get "/api/v1/accounts/#{account_id}/students_by_grade/#{grade_id}"
322
+ end
323
+
324
+ def get_students_by_class(account_id, class_id)
325
+ paginated_get "/api/v1/accounts/#{account_id}/students_by_class_room/#{class_id}"
326
+ end
327
+ end
328
+
329
+ class CanvasApi::Unauthorized < StandardError ; end
330
+ class CanvasApi::Authenticate < StandardError ; end
331
+ end