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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +28 -0
- data/README.md +110 -0
- data/Rakefile +28 -0
- data/app/controllers/canvas_oauth/canvas_controller.rb +26 -0
- data/app/controllers/canvas_oauth/oauth_application_controller.rb +5 -0
- data/app/models/canvas_oauth/authorization.rb +46 -0
- data/config/canvas.yml.example +12 -0
- data/config/routes.rb +3 -0
- data/db/migrate/20121121005358_create_canvas_oauth_authorizations.rb +16 -0
- data/lib/canvas_oauth.rb +26 -0
- data/lib/canvas_oauth/canvas_api.rb +331 -0
- data/lib/canvas_oauth/canvas_api_extensions.rb +9 -0
- data/lib/canvas_oauth/canvas_application.rb +73 -0
- data/lib/canvas_oauth/canvas_config.rb +34 -0
- data/lib/canvas_oauth/config.rb +3 -0
- data/lib/canvas_oauth/default_utf8_parser.rb +13 -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 +90 -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 +57 -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 +7 -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/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20160711200737_create_canvas_oauth_authorizations.canvas_oauth.rb +16 -0
- data/spec/dummy/db/schema.rb +27 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +1 -0
- data/spec/dummy/log/test.log +1630 -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 +364 -0
- data/spec/lib/canvas_oauth/default_utf8_parser_spec.rb +21 -0
- data/spec/models/canvas_oauth/authorization_spec.rb +47 -0
- data/spec/spec_helper.rb +64 -0
- 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,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
|
data/config/routes.rb
ADDED
@@ -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
|
data/lib/canvas_oauth.rb
ADDED
@@ -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
|