canvas_oauth_engine 1.0.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.
- data/MIT-LICENSE +20 -0
- data/README.md +102 -0
- data/Rakefile +28 -0
- data/app/controllers/canvas_oauth/application_controller.rb +5 -0
- data/app/controllers/canvas_oauth/canvas_controller.rb +25 -0
- data/app/models/canvas_oauth/authorization.rb +26 -0
- data/config/canvas.yml.example +12 -0
- data/config/routes.rb +3 -0
- data/db/migrate/20121121005358_create_canvas_oauth_authorizations.rb +15 -0
- data/lib/canvas_oauth.rb +25 -0
- data/lib/canvas_oauth/canvas_api.rb +249 -0
- data/lib/canvas_oauth/canvas_api_extensions.rb +8 -0
- data/lib/canvas_oauth/canvas_application.rb +66 -0
- data/lib/canvas_oauth/canvas_config.rb +28 -0
- data/lib/canvas_oauth/config.rb +1 -0
- data/lib/canvas_oauth/engine.rb +15 -0
- data/lib/canvas_oauth/version.rb +3 -0
- data/lib/tasks/canvas_oauth_tasks.rake +1 -0
- data/spec/controllers/canvas_oauth/canvas_controller_spec.rb +80 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/controllers/welcome_controller.rb +5 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +55 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/canvas.yml +12 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +26 -0
- data/spec/dummy/config/environments/production.rb +69 -0
- data/spec/dummy/config/environments/test.rb +33 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +8 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +4 -0
- data/spec/dummy/db/migrate/20130326194409_create_canvas_oauth_authorizations.canvas_oauth.rb +13 -0
- data/spec/dummy/db/schema.rb +25 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/test.log +8127 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/robots.txt +5 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/lib/canvas_oauth/canvas_api_extensions_spec.rb +13 -0
- data/spec/lib/canvas_oauth/canvas_api_spec.rb +228 -0
- data/spec/models/canvas_oauth/authorization_spec.rb +47 -0
- data/spec/spec_helper.rb +57 -0
- metadata +353 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2012 - 2015 Instructure, Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,102 @@
|
|
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
|
+
## Installation
|
8
|
+
|
9
|
+
Add the gem to your `Gemfile` with the following line, and then `bundle install`
|
10
|
+
|
11
|
+
```
|
12
|
+
gem 'canvas_oauth_engine', :require => 'canvas_oauth'
|
13
|
+
```
|
14
|
+
|
15
|
+
Then, mount the engine to your app by adding this line to your `routes.rb` file
|
16
|
+
|
17
|
+
```
|
18
|
+
mount CanvasOauth::Engine => "/canvas_oauth"
|
19
|
+
```
|
20
|
+
|
21
|
+
Next, include the engine in your `ApplicationController`
|
22
|
+
|
23
|
+
```
|
24
|
+
class ApplicationController < ActionController::Base
|
25
|
+
include CanvasOauth::CanvasApplication
|
26
|
+
|
27
|
+
...
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
After that, create an `canvas.yml` file in your `config/` folder that looks something
|
32
|
+
like this (or see `config/canvas.yml.example` for a template):
|
33
|
+
|
34
|
+
```
|
35
|
+
default: &default
|
36
|
+
key: your_key
|
37
|
+
secret: your_secret
|
38
|
+
|
39
|
+
development:
|
40
|
+
<<: *default
|
41
|
+
|
42
|
+
test:
|
43
|
+
<<: *default
|
44
|
+
|
45
|
+
production:
|
46
|
+
<<: *default
|
47
|
+
```
|
48
|
+
|
49
|
+
The values of key and secret should be set from the developer key that you
|
50
|
+
generate in the canvas application.
|
51
|
+
|
52
|
+
Finally, run migrations:
|
53
|
+
|
54
|
+
```
|
55
|
+
bundle exec install
|
56
|
+
bundle exec rake railties:install:migrations
|
57
|
+
bundle exec rake db:migrate
|
58
|
+
```
|
59
|
+
|
60
|
+
This will create the `canvas_oauth_authorizations` table which stores
|
61
|
+
successful oauth tokens for later use, so the user does not have to accept the
|
62
|
+
oauth login each time they use the app.
|
63
|
+
|
64
|
+
## Usage
|
65
|
+
|
66
|
+
The engine only uses one url, whatever it is mounted to, to serve as the
|
67
|
+
`redirect_url` in the oauth workflow.
|
68
|
+
|
69
|
+
The engine sets up a global `before_filter`, which checks for a valid oauth
|
70
|
+
token, and if one does not exist, starts the oauth login flow. It handles
|
71
|
+
posting the oauth request, verifying the result and redirecting to the
|
72
|
+
application root. It exposes the following methods to your controllers:
|
73
|
+
|
74
|
+
* `canvas`
|
75
|
+
* `canvas_token`
|
76
|
+
|
77
|
+
The first is an instance of HTTParty ready to make api requests to your canvas
|
78
|
+
application. The second is if you need access to the oauth token directly.
|
79
|
+
|
80
|
+
## Configuring the Tool Consumer
|
81
|
+
|
82
|
+
You will a developer key and secret from canvas, which should be entered into
|
83
|
+
you `canvas.yml` file.
|
84
|
+
|
85
|
+
## Example
|
86
|
+
|
87
|
+
You can see and interact with an example of an app using this engine by looking
|
88
|
+
at `spec/dummy`. This is a full rails app which integrates the gem and has
|
89
|
+
a simple index page that says 'Hello Oauth' if the app is launched and the
|
90
|
+
oauth flow is successful.
|
91
|
+
|
92
|
+
## About Oauth
|
93
|
+
|
94
|
+
TODO...
|
95
|
+
|
96
|
+
## Contributing
|
97
|
+
|
98
|
+
1. Fork it
|
99
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
100
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
101
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
102
|
+
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,25 @@
|
|
1
|
+
module CanvasOauth
|
2
|
+
class CanvasController < CanvasOauth::ApplicationController
|
3
|
+
skip_before_filter :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
|
+
if CanvasOauth::Authorization.cache_token(token, user_id, tool_consumer_instance_guid)
|
9
|
+
redirect_to main_app.root_path
|
10
|
+
else
|
11
|
+
render text: "Error: unable to save token"
|
12
|
+
end
|
13
|
+
else
|
14
|
+
render text: "Error: invalid code - #{params[:code]}"
|
15
|
+
end
|
16
|
+
else
|
17
|
+
render text: "#{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."
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def verify_oauth2_state(callback_state)
|
22
|
+
callback_state.present? && callback_state == session.delete(:oauth2_state)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module CanvasOauth
|
2
|
+
class Authorization < ActiveRecord::Base
|
3
|
+
validates :canvas_user_id, :token, :last_used_at, presence: true
|
4
|
+
|
5
|
+
def self.cache_token(token, user_id, tool_consumer_instance_guid)
|
6
|
+
create(
|
7
|
+
token: token,
|
8
|
+
canvas_user_id: user_id,
|
9
|
+
last_used_at: Time.now,
|
10
|
+
tool_consumer_instance_guid: tool_consumer_instance_guid
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.fetch_token(user_id, tool_consumer_instance_guid)
|
15
|
+
user_tokens = where(canvas_user_id: user_id, tool_consumer_instance_guid: tool_consumer_instance_guid).order("created_at DESC")
|
16
|
+
if canvas_auth = user_tokens.first
|
17
|
+
canvas_auth.update_attribute(:last_used_at, Time.now)
|
18
|
+
return canvas_auth.token
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.clear_tokens(user_id, tool_consumer_instance_guid)
|
23
|
+
where(canvas_user_id: user_id, tool_consumer_instance_guid: tool_consumer_instance_guid).destroy_all
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
class CreateCanvasOauthAuthorizations < ActiveRecord::Migration
|
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.datetime "last_used_at"
|
8
|
+
t.datetime "created_at", :null => false
|
9
|
+
t.datetime "updated_at", :null => false
|
10
|
+
end
|
11
|
+
|
12
|
+
add_index :canvas_oauth_authorizations, [:canvas_user_id, :tool_consumer_instance_guid],
|
13
|
+
name: 'index_canvas_oauth_auths_on_user_id_and_tciguid'
|
14
|
+
end
|
15
|
+
end
|
data/lib/canvas_oauth.rb
ADDED
@@ -0,0 +1,25 @@
|
|
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
|
+
|
12
|
+
module CanvasOauth
|
13
|
+
mattr_accessor :app_root
|
14
|
+
|
15
|
+
def self.setup
|
16
|
+
yield self
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.config
|
20
|
+
yield(CanvasOauth::Config)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
require "canvas_oauth/engine"
|
25
|
+
|
@@ -0,0 +1,249 @@
|
|
1
|
+
module CanvasOauth
|
2
|
+
class CanvasApi
|
3
|
+
include HTTParty
|
4
|
+
PER_PAGE = 50
|
5
|
+
|
6
|
+
attr_accessor :token, :key, :secret
|
7
|
+
attr_reader :canvas_url
|
8
|
+
|
9
|
+
def initialize(canvas_url, token, key, secret)
|
10
|
+
self.canvas_url = canvas_url
|
11
|
+
self.token = token
|
12
|
+
self.key = key
|
13
|
+
self.secret = secret
|
14
|
+
end
|
15
|
+
|
16
|
+
def authenticated_request(method, *params)
|
17
|
+
params << {} if params.size == 1
|
18
|
+
|
19
|
+
params.last[:headers] ||= {}
|
20
|
+
params.last[:headers]['Authorization'] = "Bearer #{token}"
|
21
|
+
|
22
|
+
start = Time.now
|
23
|
+
|
24
|
+
response = self.class.send(method, *params)
|
25
|
+
|
26
|
+
Rails.logger.info {
|
27
|
+
stop = Time.now
|
28
|
+
elapsed = ((stop - start) * 1000).round(2)
|
29
|
+
|
30
|
+
params.last[:headers].reject! { |k| k == 'Authorization' }
|
31
|
+
"API call (#{elapsed}ms): #{method} #{params.inspect}"
|
32
|
+
}
|
33
|
+
|
34
|
+
if response && response.unauthorized?
|
35
|
+
if response.headers['WWW-Authenticate'].present?
|
36
|
+
raise CanvasApi::Authenticate
|
37
|
+
else
|
38
|
+
raise CanvasApi::Unauthorized
|
39
|
+
end
|
40
|
+
else
|
41
|
+
return response
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def paginated_get(*params)
|
46
|
+
params[1] ||= {}
|
47
|
+
params[1][:query] ||= {}
|
48
|
+
params[1][:query][:per_page] = PER_PAGE
|
49
|
+
|
50
|
+
all_pages = []
|
51
|
+
|
52
|
+
while params[0] do
|
53
|
+
if current_page = authenticated_get(*params)
|
54
|
+
all_pages += current_page if valid_page?(current_page)
|
55
|
+
|
56
|
+
links = LinkHeader.parse(current_page.headers['link'])
|
57
|
+
params[0] = links.find_link(["rel", "next"]).try(:href)
|
58
|
+
else
|
59
|
+
params[0] = nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
all_pages
|
64
|
+
end
|
65
|
+
|
66
|
+
def get_report(account_id, report_type, params)
|
67
|
+
report = authenticated_post("/api/v1/accounts/#{account_id}/reports/#{report_type}", { body: params })
|
68
|
+
report = authenticated_get "/api/v1/accounts/#{account_id}/reports/#{report_type}/#{report['id']}"
|
69
|
+
while report['status'] == 'running'
|
70
|
+
sleep(4)
|
71
|
+
report = authenticated_get "/api/v1/accounts/#{account_id}/reports/#{report_type}/#{report['id']}"
|
72
|
+
end
|
73
|
+
|
74
|
+
if report['status'] == 'complete'
|
75
|
+
file_id = report['file_url'].match(/files\/([0-9]+)\/download/)[1]
|
76
|
+
file = get_file(file_id)
|
77
|
+
return hash_csv(self.class.get(file['url'], limit: 15).parsed_response)
|
78
|
+
else
|
79
|
+
return report
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def valid_page?(page)
|
84
|
+
page && page.size > 0
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_file(file_id)
|
88
|
+
authenticated_get "/api/v1/files/#{file_id}"
|
89
|
+
end
|
90
|
+
|
91
|
+
def get_accounts_provisioning_report(account_id)
|
92
|
+
get_report(account_id, :provisioning_csv, 'parameters[accounts]' => true)
|
93
|
+
end
|
94
|
+
|
95
|
+
#Needs to be refactored to somewhere more generic
|
96
|
+
def hash_csv(csv_string)
|
97
|
+
require 'csv'
|
98
|
+
|
99
|
+
csv = CSV.parse(csv_string)
|
100
|
+
headers = csv.shift
|
101
|
+
output = []
|
102
|
+
|
103
|
+
csv.each do |row|
|
104
|
+
hash = {}
|
105
|
+
headers.each do |header|
|
106
|
+
hash[header] = row.shift.to_s
|
107
|
+
end
|
108
|
+
output << hash
|
109
|
+
end
|
110
|
+
|
111
|
+
return output
|
112
|
+
end
|
113
|
+
|
114
|
+
def authenticated_get(*params)
|
115
|
+
authenticated_request(:get, *params)
|
116
|
+
end
|
117
|
+
|
118
|
+
def authenticated_post(*params)
|
119
|
+
authenticated_request(:post, *params)
|
120
|
+
end
|
121
|
+
|
122
|
+
def authenticated_put(*params)
|
123
|
+
authenticated_request(:put, *params)
|
124
|
+
end
|
125
|
+
|
126
|
+
def get_courses
|
127
|
+
paginated_get "/api/v1/courses"
|
128
|
+
end
|
129
|
+
|
130
|
+
def get_account(account_id)
|
131
|
+
authenticated_get "/api/v1/accounts/#{account_id}"
|
132
|
+
end
|
133
|
+
|
134
|
+
def get_account_sub_accounts(account_id)
|
135
|
+
paginated_get "/api/v1/accounts/#{account_id}/sub_accounts", { query: { :recursive => true } }
|
136
|
+
end
|
137
|
+
|
138
|
+
def get_account_courses(account_id)
|
139
|
+
paginated_get "/api/v1/accounts/#{account_id}/courses"
|
140
|
+
end
|
141
|
+
|
142
|
+
def get_account_users(account_id)
|
143
|
+
paginated_get "/api/v1/accounts/#{account_id}/users"
|
144
|
+
end
|
145
|
+
|
146
|
+
def get_course(course_id)
|
147
|
+
authenticated_get "/api/v1/courses/#{course_id}"
|
148
|
+
end
|
149
|
+
|
150
|
+
def get_section_enrollments(section_id)
|
151
|
+
paginated_get "/api/v1/sections/#{section_id}/enrollments"
|
152
|
+
end
|
153
|
+
|
154
|
+
def get_user_enrollments(user_id)
|
155
|
+
paginated_get "/api/v1/users/#{user_id}/enrollments"
|
156
|
+
end
|
157
|
+
|
158
|
+
def get_course_users(course_id)
|
159
|
+
paginated_get "/api/v1/courses/#{course_id}/users"
|
160
|
+
end
|
161
|
+
|
162
|
+
def get_all_course_users(course_id)
|
163
|
+
paginated_get "/api/v1/courses/#{course_id}/users", { query: {enrollment_state: ["active","invited","rejected","completed","inactive"] } }
|
164
|
+
end
|
165
|
+
|
166
|
+
def get_course_teachers_and_tas(course_id)
|
167
|
+
paginated_get "/api/v1/courses/#{course_id}/users", { query: { enrollment_type: ['teacher', 'ta'] } }
|
168
|
+
end
|
169
|
+
|
170
|
+
def get_course_students(course_id)
|
171
|
+
paginated_get "/api/v1/courses/#{course_id}/students"
|
172
|
+
end
|
173
|
+
|
174
|
+
def get_section(section_id)
|
175
|
+
authenticated_get "/api/v1/sections/#{section_id}"
|
176
|
+
end
|
177
|
+
|
178
|
+
def get_sections(course_id)
|
179
|
+
paginated_get "/api/v1/courses/#{course_id}/sections", { query: { :include => ['students', 'avatar_url', 'enrollments'] } }
|
180
|
+
end
|
181
|
+
|
182
|
+
def get_assignments(course_id)
|
183
|
+
paginated_get "/api/v1/courses/#{course_id}/assignments"
|
184
|
+
end
|
185
|
+
|
186
|
+
def get_assignment(course_id, assignment_id)
|
187
|
+
authenticated_get "/api/v1/courses/#{course_id}/assignments/#{assignment_id}"
|
188
|
+
end
|
189
|
+
|
190
|
+
def get_user_profile(user_id)
|
191
|
+
authenticated_get "/api/v1/users/#{user_id}/profile"
|
192
|
+
end
|
193
|
+
|
194
|
+
def create_assignment(course_id, params)
|
195
|
+
authenticated_post "/api/v1/courses/#{course_id}/assignments", { body: { assignment: params } }
|
196
|
+
end
|
197
|
+
|
198
|
+
def grade_assignment(course_id, assignment_id, user_id, params)
|
199
|
+
authenticated_put "/api/v1/courses/#{course_id}/assignments/#{assignment_id}/submissions/#{user_id}", { body: params }
|
200
|
+
end
|
201
|
+
|
202
|
+
def course_account_id(course_id)
|
203
|
+
course = get_course(course_id)
|
204
|
+
course['account_id'] if course
|
205
|
+
end
|
206
|
+
|
207
|
+
def root_account_id(account_id)
|
208
|
+
if account_id && account = get_account(account_id)
|
209
|
+
root_id = account['root_account_id']
|
210
|
+
end
|
211
|
+
|
212
|
+
root_id || account_id
|
213
|
+
end
|
214
|
+
|
215
|
+
def course_root_account_id(course_id)
|
216
|
+
root_account_id(course_account_id(course_id))
|
217
|
+
end
|
218
|
+
|
219
|
+
def auth_url(redirect_uri, oauth2_state)
|
220
|
+
"#{canvas_url}/login/oauth2/auth?client_id=#{key}&response_type=code&state=#{oauth2_state}&redirect_uri=#{redirect_uri}"
|
221
|
+
end
|
222
|
+
|
223
|
+
def get_access_token(code)
|
224
|
+
params = {
|
225
|
+
body: {
|
226
|
+
client_id: key,
|
227
|
+
client_secret: secret,
|
228
|
+
code: code
|
229
|
+
}
|
230
|
+
}
|
231
|
+
|
232
|
+
response = self.class.post '/login/oauth2/token', params
|
233
|
+
self.token = response['access_token']
|
234
|
+
end
|
235
|
+
|
236
|
+
def hex_sis_id(name, value)
|
237
|
+
hex = value.unpack("H*")[0]
|
238
|
+
return "hex:#{name}:#{hex}"
|
239
|
+
end
|
240
|
+
|
241
|
+
def canvas_url=(value)
|
242
|
+
@canvas_url = value
|
243
|
+
self.class.base_uri(value)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
class CanvasApi::Unauthorized < StandardError ; end
|
248
|
+
class CanvasApi::Authenticate < StandardError ; end
|
249
|
+
end
|