coalescing_panda 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +28 -0
- data/app/assets/javascripts/coalescing_panda/application.js +15 -0
- data/app/assets/javascripts/coalescing_panda/coalescing_panda/oauth2.js +2 -0
- data/app/assets/javascripts/coalescing_panda/oauth2.js.coffee +8 -0
- data/app/assets/stylesheets/coalescing_panda/application.css +13 -0
- data/app/assets/stylesheets/coalescing_panda/coalescing_panda/oauth2.css +4 -0
- data/app/assets/stylesheets/coalescing_panda/oauth2.css +4 -0
- data/app/controllers/coalescing_panda/application_controller.rb +4 -0
- data/app/controllers/coalescing_panda/lti_controller.rb +32 -0
- data/app/controllers/coalescing_panda/oauth2_controller.rb +34 -0
- data/app/models/coalescing_panda/canvas_api_auth.rb +6 -0
- data/app/models/coalescing_panda/lti_account.rb +23 -0
- data/app/models/coalescing_panda/lti_nonce.rb +12 -0
- data/app/views/coalescing_panda/oauth2/oauth2.html.haml +10 -0
- data/app/views/coalescing_panda/oauth2/redirect.html.haml +5 -0
- data/app/views/layouts/coalescing_panda/application.html.erb +14 -0
- data/config/routes.rb +4 -0
- data/db/migrate/20131114150001_create_coalescing_panda_canvas_api_auths.rb +10 -0
- data/db/migrate/20131118211442_create_coalescing_panda_lti_accounts.rb +14 -0
- data/db/migrate/20131119165343_create_coalescing_panda_lti_nonces.rb +9 -0
- data/lib/coalescing_panda/controller_helpers.rb +61 -0
- data/lib/coalescing_panda/engine.rb +17 -0
- data/lib/coalescing_panda/route_helpers.rb +28 -0
- data/lib/coalescing_panda/version.rb +3 -0
- data/lib/coalescing_panda.rb +31 -0
- data/lib/tasks/coalescing_panda_tasks.rake +4 -0
- data/spec/controllers/coalescing_panda/lti_controller_spec.rb +42 -0
- data/spec/controllers/coalescing_panda/oauth2_controller_spec.rb +22 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config/application.rb +23 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +29 -0
- data/spec/dummy/config/environments/production.rb +80 -0
- data/spec/dummy/config/environments/test.rb +36 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +12 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +5 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/schema.rb +24 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +9 -0
- data/spec/dummy/log/test.log +2084 -0
- data/spec/dummy/public/404.html +58 -0
- data/spec/dummy/public/422.html +58 -0
- data/spec/dummy/public/500.html +57 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/models/coalescing_panda/canvas_api_auth_spec.rb +11 -0
- data/spec/spec_helper.rb +50 -0
- metadata +312 -0
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'CoalescingPanda'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
Bundler::GemHelper.install_tasks
|
23
|
+
|
24
|
+
require 'rspec/core/rake_task'
|
25
|
+
|
26
|
+
RSpec::Core::RakeTask.new(:spec)
|
27
|
+
|
28
|
+
task :default => :spec
|
@@ -0,0 +1,15 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require jquery
|
14
|
+
//= require jquery_ujs
|
15
|
+
//= require_tree .
|
@@ -0,0 +1,13 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the top of the
|
9
|
+
* compiled file, but it's generally better to create a new file per style scope.
|
10
|
+
*
|
11
|
+
*= require_self
|
12
|
+
*= require_tree .
|
13
|
+
*/
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require_dependency "coalescing_panda/application_controller"
|
2
|
+
|
3
|
+
module CoalescingPanda
|
4
|
+
class LtiController < ApplicationController
|
5
|
+
|
6
|
+
def lti_config
|
7
|
+
lti_options = CoalescingPanda.lti_options
|
8
|
+
lti_nav = CoalescingPanda.lti_paths
|
9
|
+
platform = 'canvas.instructure.com'
|
10
|
+
host = "#{request.scheme}://#{request.host_with_port}"
|
11
|
+
tc = IMS::LTI::ToolConfig.new(:title => lti_options[:title], :launch_url => (lti_options[:launch_url])|| 'ABC')
|
12
|
+
tc.set_ext_param(platform, :domain, request.host)
|
13
|
+
tc.set_ext_param(platform, :privacy_level, 'public')
|
14
|
+
lti_nav.each do |k, v|
|
15
|
+
tc.set_ext_param(platform, "#{k.to_s}_navigation".to_sym, ext_params(v) )
|
16
|
+
end
|
17
|
+
|
18
|
+
#strip the launch url
|
19
|
+
xml = tc.to_xml.sub( /<blti:launch_url>.*<\/blti:launch_url>/, '') if lti_options[:launch_url].blank?
|
20
|
+
render :xml => xml
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def ext_params(options)
|
26
|
+
url = options.delete(:url)
|
27
|
+
options[:url] = main_app.send(url+'_url')
|
28
|
+
options
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require_dependency "coalescing_panda/application_controller"
|
2
|
+
|
3
|
+
module CoalescingPanda
|
4
|
+
class Oauth2Controller < ApplicationController
|
5
|
+
|
6
|
+
def oauth2
|
7
|
+
end
|
8
|
+
|
9
|
+
def redirect
|
10
|
+
unless params[:error]
|
11
|
+
lti_account = LtiAccount.find_by_key(params[:key])
|
12
|
+
client_id = lti_account.oauth2_client_id
|
13
|
+
client_key = lti_account.oauth2_client_key
|
14
|
+
user_id = params[:user_id]
|
15
|
+
api_domain = params[:api_domain]
|
16
|
+
client = Bearcat::Client.new(prefix: oauth2_protocol+'://'+api_domain)
|
17
|
+
token = client.retrieve_token(client_id, coalescing_panda.oauth2_redirect_url, client_key, params['code'])
|
18
|
+
CanvasApiAuth.where('user_id = ? and api_domain = ?', user_id, api_domain).first_or_create do |auth|
|
19
|
+
auth.api_token = token
|
20
|
+
auth.user_id = user_id
|
21
|
+
auth.api_domain = api_domain
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def oauth2_protocol
|
30
|
+
ENV['OAUTH_PROTOCOL'] || 'https'
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module CoalescingPanda
|
2
|
+
class LtiAccount < ActiveRecord::Base
|
3
|
+
validates :name, :key, uniqueness: true
|
4
|
+
validates :name, :key, :secret, presence: true
|
5
|
+
has_many :coalescing_panda_lti_nonces,
|
6
|
+
:foreign_key => :coalescing_panda_lti_account_id,
|
7
|
+
:class_name => 'CoalescingPanda::LtiNonce'
|
8
|
+
|
9
|
+
def validate_nonce(nonce, timestamp)
|
10
|
+
cleanup_nonce
|
11
|
+
if timestamp > 15.minutes.ago
|
12
|
+
coalescing_panda_lti_nonces.create(nonce:nonce, timestamp:timestamp).persisted?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def cleanup_nonce
|
19
|
+
coalescing_panda_lti_nonces.where('timestamp > ?', 15.minutes.ago).delete_all
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module CoalescingPanda
|
2
|
+
class LtiNonce < ActiveRecord::Base
|
3
|
+
validates :coalescing_panda_lti_account, :nonce, :timestamp, :presence => true
|
4
|
+
validates :nonce, uniqueness: {scope: :coalescing_panda_lti_account}
|
5
|
+
belongs_to :coalescing_panda_lti_account, :class_name => 'CoalescingPanda::LtiAccount'
|
6
|
+
end
|
7
|
+
|
8
|
+
def cleanup
|
9
|
+
CoalescingPanda::LtiNonce.where("coalescing_panda_lti_account_id = ? AND timestamp < ?", b.coalescing_panda_lti_account.id, 15.minutes.ago )
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
%input{type: 'hidden', value: "#{@canvas_url}", name: :canvas_oauth_url}
|
2
|
+
%form.oauth2
|
3
|
+
%h2 Canvas Authentication Required
|
4
|
+
%button.btn-canvas#oauth_btn Authenticate
|
5
|
+
|
6
|
+
%form{id: 'lti_form', action: "#{request.original_url}", method: 'post'}
|
7
|
+
- @lti_params.each do |k,v|
|
8
|
+
%input{:type =>"hidden", :value=>"#{v}", :name=>"#{k}"}
|
9
|
+
|
10
|
+
= javascript_include_tag "coalescing_panda/oauth2"
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>CoalescingPanda</title>
|
5
|
+
<%= stylesheet_link_tag "coalescing_panda/application", media: "all" %>
|
6
|
+
<%= javascript_include_tag "coalescing_panda/application" %>
|
7
|
+
<%= csrf_meta_tags %>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
|
11
|
+
<%= yield %>
|
12
|
+
|
13
|
+
</body>
|
14
|
+
</html>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
class CreateCoalescingPandaLtiAccounts < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :coalescing_panda_lti_accounts do |t|
|
4
|
+
t.string :name
|
5
|
+
t.string :key
|
6
|
+
t.string :secret
|
7
|
+
t.string :oauth2_client_id
|
8
|
+
t.string :oauth2_client_key
|
9
|
+
|
10
|
+
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module CoalescingPanda
|
2
|
+
module ControllerHelpers
|
3
|
+
|
4
|
+
def canvas_oauth2
|
5
|
+
user_id = params['user_id']
|
6
|
+
api_domain = params['custom_canvas_api_domain']
|
7
|
+
|
8
|
+
if token = CanvasApiAuth.where('user_id = ? and api_domain = ?', user_id, api_domain).pluck(:api_token).first
|
9
|
+
@client = Bearcat::Client.new(token: token, prefix: ENV['OAUTH_PROTOCOL']+'://'+api_domain)
|
10
|
+
else
|
11
|
+
client_id = ENV['OAUTH_CLIENT_ID']
|
12
|
+
client = Bearcat::Client.new(prefix: 'http://'+api_domain)
|
13
|
+
@lti_params = params.to_hash
|
14
|
+
@canvas_url = client.auth_redirect_url(client_id,
|
15
|
+
coalescing_panda.oauth2_redirect_url({key: params['oauth_consumer_key'],
|
16
|
+
user_id: user_id, api_domain: api_domain}))
|
17
|
+
#delete the added params so the original oauth sig still works
|
18
|
+
@lti_params.delete('action')
|
19
|
+
@lti_params.delete('controller')
|
20
|
+
render 'coalescing_panda/oauth2/oauth2'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def lti_authorize!(*roles)
|
25
|
+
authorized = false
|
26
|
+
if @lti_account = params['oauth_consumer_key'] && LtiAccount.find_by_key(params['oauth_consumer_key'])
|
27
|
+
@tp = IMS::LTI::ToolProvider.new(@lti_account.key, @lti_account.secret, params)
|
28
|
+
authorized = @tp.valid_request?(request)
|
29
|
+
end
|
30
|
+
authorized = authorized && (roles == 0 || (roles && lti_roles).count > 1)
|
31
|
+
authorized = authorized && @lti_account.validate_nonce(params['oauth_nonce'], DateTime.strptime(params['oauth_timestamp'],'%s'))
|
32
|
+
if !authorized
|
33
|
+
render :text => 'Invalid Credentials, please contact your Administrator.', :status => :unauthorized
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def lti_roles
|
38
|
+
@lti_roles ||= params['roles'].split(',').map { |role|
|
39
|
+
case role.downcase.strip
|
40
|
+
when 'admin'
|
41
|
+
:admin
|
42
|
+
when 'urn:lti:instrole:ims/lis/administrator'
|
43
|
+
:admin
|
44
|
+
when 'learner'
|
45
|
+
:student
|
46
|
+
when 'instructor'
|
47
|
+
:teacher
|
48
|
+
when 'urn:lti:role:ims/lis/teachingassistant'
|
49
|
+
:ta
|
50
|
+
when 'contentdeveloper'
|
51
|
+
:designer
|
52
|
+
when 'urn:lti:instrole:ims/lis/observer'
|
53
|
+
:observer
|
54
|
+
else
|
55
|
+
:none
|
56
|
+
end
|
57
|
+
}.uniq
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module CoalescingPanda
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
config.autoload_paths += Dir["#{config.root}/lib/**/"]
|
4
|
+
isolate_namespace CoalescingPanda
|
5
|
+
|
6
|
+
initializer 'coalescing_panda.app_controller' do |app|
|
7
|
+
OAUTH_10_SUPPORT = true
|
8
|
+
ActiveSupport.on_load(:action_controller) do
|
9
|
+
include CoalescingPanda::ControllerHelpers
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
initializer 'cloaescing_panda.route_helper' do |route|
|
14
|
+
ActionDispatch::Routing::Mapper.send :include, CoalescingPanda::RouteHelpers
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module CoalescingPanda
|
2
|
+
module RouteHelpers
|
3
|
+
def lti_nav(nav, *rest, &block)
|
4
|
+
base_path = Rails.application.routes.named_routes[:coalescing_panda].path.spec
|
5
|
+
raise LtiNavigationInUse.new('CoalescingPanda must be mounted before defining lti_nav routes') if base_path.blank?
|
6
|
+
valid_options = [:course, :user, :account, :editor, :external_tool]
|
7
|
+
if rest.empty? && nav.is_a?(Hash)
|
8
|
+
options = nav
|
9
|
+
nav, to = options.find {|name, _value| valid_options.include? name}
|
10
|
+
options[:to] = to
|
11
|
+
options.delete(nav)
|
12
|
+
else
|
13
|
+
options = rest.pop || {}
|
14
|
+
end
|
15
|
+
lti_options = options.delete(:lti_options) || {}
|
16
|
+
path = "#{base_path}/#{nav.to_s}"
|
17
|
+
lti_options[:url] = path.split('/').reject(&:empty?).join('_')
|
18
|
+
CoalescingPanda::lti_navigation(nav, lti_options)
|
19
|
+
post path, options, &block
|
20
|
+
end
|
21
|
+
|
22
|
+
def lti_mount(app, options = nil)
|
23
|
+
CoalescingPanda.lti_options = options && options.delete(:lti_options)
|
24
|
+
mount(app, options)
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "coalescing_panda/engine" if defined?(Rails)
|
2
|
+
require 'ims/lti'
|
3
|
+
require 'bearcat'
|
4
|
+
require 'oauth/request_proxy/rack_request'
|
5
|
+
|
6
|
+
module CoalescingPanda
|
7
|
+
class LtiNavigationInUse < StandardError;end
|
8
|
+
class NotMounted < StandardError;end
|
9
|
+
|
10
|
+
@@lti_navigation = {}
|
11
|
+
@@lti_options
|
12
|
+
|
13
|
+
def self.lti_options= lti_options
|
14
|
+
@@lti_options = lti_options
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.lti_options
|
18
|
+
@@lti_options.deep_dup
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.lti_navigation(navigation, options)
|
22
|
+
raise LtiNavigationInUse.new("#{navigation} can only be defined once") if @@lti_navigation.has_key?(navigation)
|
23
|
+
@@lti_navigation[navigation] = options
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.lti_paths
|
27
|
+
@@lti_navigation.deep_dup
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CoalescingPanda::LtiController do
|
4
|
+
routes { CoalescingPanda::Engine.routes }
|
5
|
+
|
6
|
+
describe '#lti_config' do
|
7
|
+
|
8
|
+
it 'generates lti xml config'do
|
9
|
+
controller.main_app.stub(:test_action_url) {'foo'}
|
10
|
+
get(:lti_config)
|
11
|
+
xml = Nokogiri::XML(response.body)
|
12
|
+
xml.at_xpath('//blti:title').text.should == 'LTI Tool'
|
13
|
+
xml.at_xpath('//lticm:property[@name="domain"]').text.should_not == nil
|
14
|
+
xml.at_xpath('//lticm:property[@name="privacy_level"]').text.should == 'public'
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'generates lti nav config' do
|
18
|
+
controller.main_app.stub(:test_action_url) {'foo'}
|
19
|
+
CoalescingPanda.lti_navigation(:account, {
|
20
|
+
url: 'test_action',
|
21
|
+
text: 'My Title',
|
22
|
+
enabled: false
|
23
|
+
})
|
24
|
+
get(:lti_config)
|
25
|
+
xml = Nokogiri::XML(response.body)
|
26
|
+
account_nav = xml.at_xpath('//lticm:options[@name="account_navigation"]')
|
27
|
+
account_nav.at_xpath('lticm:property[@name="enabled"]').text.should == 'false'
|
28
|
+
account_nav.at_xpath('lticm:property[@name="text"]').text.should == 'My Title'
|
29
|
+
account_nav.at_xpath('lticm:property[@name="url"]').text.should == 'foo'
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
it 'get the url, from the action string' do
|
36
|
+
controller.main_app.stub(:test_action_url) {'foo'}
|
37
|
+
options = controller.send(:ext_params, {url:'test_action'})
|
38
|
+
options[:url].should == 'foo'
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CoalescingPanda::Oauth2Controller do
|
4
|
+
routes { CoalescingPanda::Engine.routes }
|
5
|
+
|
6
|
+
describe "#redirect" do
|
7
|
+
it 'creates a token in the db' do
|
8
|
+
ENV['OAUTH_PROTOCOL'] = 'http'
|
9
|
+
Bearcat::Client.any_instance.stub(retrieve_token: 'foobar')
|
10
|
+
get :redirect, {user_id: 1, api_domain:'foo.com', code:'bar'}
|
11
|
+
auth = CoalescingPanda::CanvasApiAuth.find_by_user_id_and_api_domain(1, 'foo.com')
|
12
|
+
auth.should_not == nil
|
13
|
+
end
|
14
|
+
|
15
|
+
it "doesn't create a token in the db" do
|
16
|
+
get :redirect, {error: 'your face'}
|
17
|
+
CoalescingPanda::CanvasApiAuth.all.count.should == 0
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
== README
|
2
|
+
|
3
|
+
This README would normally document whatever steps are necessary to get the
|
4
|
+
application up and running.
|
5
|
+
|
6
|
+
Things you may want to cover:
|
7
|
+
|
8
|
+
* Ruby version
|
9
|
+
|
10
|
+
* System dependencies
|
11
|
+
|
12
|
+
* Configuration
|
13
|
+
|
14
|
+
* Database creation
|
15
|
+
|
16
|
+
* Database initialization
|
17
|
+
|
18
|
+
* How to run the test suite
|
19
|
+
|
20
|
+
* Services (job queues, cache servers, search engines, etc.)
|
21
|
+
|
22
|
+
* Deployment instructions
|
23
|
+
|
24
|
+
* ...
|
25
|
+
|
26
|
+
|
27
|
+
Please feel free to use a different markup language if you do not plan to run
|
28
|
+
<tt>rake doc:app</tt>.
|
data/spec/dummy/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree .
|
@@ -0,0 +1,13 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the top of the
|
9
|
+
* compiled file, but it's generally better to create a new file per style scope.
|
10
|
+
*
|
11
|
+
*= require_self
|
12
|
+
*= require_tree .
|
13
|
+
*/
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Dummy</title>
|
5
|
+
<%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %>
|
6
|
+
<%= javascript_include_tag "application", "data-turbolinks-track" => true %>
|
7
|
+
<%= csrf_meta_tags %>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
|
11
|
+
<%= yield %>
|
12
|
+
|
13
|
+
</body>
|
14
|
+
</html>
|
data/spec/dummy/bin/rake
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.expand_path('../boot', __FILE__)
|
2
|
+
|
3
|
+
require 'rails/all'
|
4
|
+
|
5
|
+
Bundler.require(*Rails.groups)
|
6
|
+
require "coalescing_panda"
|
7
|
+
|
8
|
+
module Dummy
|
9
|
+
class Application < Rails::Application
|
10
|
+
# Settings in config/environments/* take precedence over those specified here.
|
11
|
+
# Application configuration should go into files in config/initializers
|
12
|
+
# -- all .rb files in that directory are automatically loaded.
|
13
|
+
|
14
|
+
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
|
15
|
+
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
|
16
|
+
# config.time_zone = 'Central Time (US & Canada)'
|
17
|
+
|
18
|
+
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
|
19
|
+
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
|
20
|
+
# config.i18n.default_locale = :de
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|