qalam_oauth_engine 2.2.9

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 (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