Further decoupling of google_docs from models and controllers.

fixes: CNVS-12328

test plan:
  - as admin enable the google_doc plugin
  - as student visit `/profile/settings`
  - click 'Google Docs' under the 'Other Services' section
  - click 'Authorize Google Doc Access' in modal
  - when redirect to google, click 'Allow access'
  - verify success banner displays

Change-Id: I2b45ffa38ac0d1d810fb988335f4cd39154ae2a6
Reviewed-on: https://gerrit.instructure.com/32949
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
QA-Review: Trevor deHaan <tdehaan@instructure.com>
Product-Review: Simon Williams <simon@instructure.com>
This commit is contained in:
Raphael Weiner 2014-04-07 10:11:01 -06:00 committed by Simon Williams
parent 4e90438d25
commit c8d7b9f915
17 changed files with 365 additions and 175 deletions

View File

@ -1737,4 +1737,22 @@ class ApplicationController < ActionController::Base
js_env hash
end
def google_docs_connection
## @real_current_user first ensures that a masquerading user never sees the
## masqueradee's files, but in general you may want to block access to google
## docs for masqueraders earlier in the request
user = @real_current_user || @current_user
if user
service_token, service_secret = Rails.cache.fetch(['google_docs_tokens', user].cache_key) do
service = user.user_services.find_by_service("google_docs")
service && [service.token, service.secret]
end
raise GoogleDocs::NoTokenError unless service_token && service_secret
google_docs = GoogleDocs.new(service_token, service_secret)
else
google_docs = GoogleDocs.new(session[:oauth_gdocs_access_token_token], session[:oauth_gdocs_access_token_secret])
end
google_docs
end
end

View File

@ -133,7 +133,7 @@ class AssignmentsController < ApplicationController
end
begin
google_docs = GoogleDocs.new(logged_in_user, session)
google_docs = google_docs_connection
@google_docs_token = google_docs.retrieve_access_token
rescue GoogleDocs::NoTokenError
#do nothing
@ -165,9 +165,9 @@ class AssignmentsController < ApplicationController
if assignment.allow_google_docs_submission? && @real_current_user.blank?
docs = {}
begin
google_docs = GoogleDocs.new(logged_in_user, session)
google_docs = google_docs_connection
docs = google_docs.list_with_extension_filter(assignment.allowed_extensions)
rescue NoTokenError
rescue GoogleDocs::NoTokenError
#do nothing
rescue => e
ErrorReport.log_exception(:oauth, e)

View File

@ -64,7 +64,7 @@ class CollaborationsController < ApplicationController
@collaborations = @context.collaborations.active
log_asset_access("collaborations:#{@context.asset_string}", "collaborations", "other")
google_docs = GoogleDocs.new(logged_in_user, session)
google_docs = google_docs_connection
@google_docs_authorized = google_docs.verify_access_token rescue false
js_env :TITLE_MAX_LEN => Collaboration::TITLE_MAX_LENGTH,
:collaboration_types => Collaboration.collaboration_types

View File

@ -498,7 +498,7 @@ class SubmissionsController < ApplicationController
# Internal: Submit a Google Doc.
def submit_google_doc(document_id)
# fetch document from google
google_docs = GoogleDocs.new(logged_in_user, session)
google_docs = google_docs_connection
document_response, display_name, file_extension = google_docs.download(document_id)
# error handling

View File

@ -143,7 +143,17 @@ class UsersController < ApplicationController
end
return_to_url = params[:return_to] || user_profile_url(@current_user)
if params[:service] == "google_docs"
redirect_to GoogleDocs.request_token_url(return_to_url, session, logged_in_user, request.host_with_port, oauth_success_url(:service => 'google_docs'))
request_token = GoogleDocs.request_token(oauth_success_url(:service => 'google_docs'))
OauthRequest.create(
:service => 'google_docs',
:token => request_token.token,
:secret => request_token.secret,
:user_secret => CanvasUuid::Uuid.generate(nil, 16),
:return_url => return_to_url,
:user => @real_current_user || @current_user,
:original_host_with_port => request.host_with_port
)
redirect_to request_token.authorize_url
elsif params[:service] == "twitter"
redirect_to twitter_request_token_url(return_to_url)
elsif params[:service] == "linked_in"
@ -199,7 +209,24 @@ class UsersController < ApplicationController
end
elsif params[:service] == "google_docs"
begin
GoogleDocs.get_access_token(oauth_request, params[:oauth_verifier], session, logged_in_user)
access_token = GoogleDocs.get_access_token(oauth_request.token, oauth_request.secret, params[:oauth_verifier])
google_docs = GoogleDocs.new(oauth_request.token, oauth_request.secret)
service_user_id, service_user_name = google_docs.get_service_user_info access_token
if oauth_request.user
UserService.register(
:service => "google_docs",
:access_token => access_token,
:user => oauth_request.user,
:service_domain => "google.com",
:service_user_id => service_user_id,
:service_user_name => service_user_name
)
oauth_request.destroy
else
session[:oauth_gdocs_access_token_token] = access_token.token
session[:oauth_gdocs_access_token_secret] = access_token.secret
end
flash[:notice] = t('google_docs_added', "Google Docs access authorized!")
rescue => e
ErrorReport.log_exception(:oauth, e)

View File

@ -27,7 +27,7 @@ class GoogleDocsCollaboration < Collaboration
def delete_document
if !self.document_id && self.user
google_docs = GoogleDocs.new(user, {})
google_docs = google_docs_for_user
google_docs.delete_doc(GoogleDocEntry.new(self.data))
end
end
@ -39,8 +39,8 @@ class GoogleDocsCollaboration < Collaboration
name = nil if name && name.empty?
name ||= I18n.t('lib.google_docs.default_document_name', "Instructure Doc")
google_docs = GoogleDocs.new(user, {})
file = google_docs.create_doc(name)
google_docs = google_docs_for_user
file = google_docs.create_doc(name, google_docs.retrieve_access_token)
self.document_id = file.document_id
self.data = file.entry.to_xml
self.url = file.alternate_url.to_s
@ -62,7 +62,7 @@ class GoogleDocsCollaboration < Collaboration
service_user_id = google_services.find{|s| s.service_user_id}.service_user_id rescue nil
collaborator = self.collaborators.find_by_user_id(user.id)
if collaborator && collaborator.authorized_service_user_id != service_user_id
google_docs = GoogleDocs.new(user, {})
google_docs = google_docs_for_user
google_docs.acl_remove(self.document_id, [collaborator.authorized_service_user_id]) if collaborator.authorized_service_user_id
google_docs.acl_add(self.document_id, [user])
collaborator.update_attributes(:authorized_service_user_id => service_user_id)
@ -70,7 +70,7 @@ class GoogleDocsCollaboration < Collaboration
end
def remove_users_from_document(users_to_remove)
google_docs = GoogleDocs.new(user, {})
google_docs = google_docs_for_user
google_docs.acl_remove(self.document_id, users_to_remove) if self.document_id
end
@ -81,7 +81,7 @@ class GoogleDocsCollaboration < Collaboration
nil
end
if document_id
google_docs = GoogleDocs.new(user, {})
google_docs = google_docs_for_user
google_docs.acl_add(self.document_id, new_users, domain)
end
end
@ -93,4 +93,14 @@ class GoogleDocsCollaboration < Collaboration
def self.config
GoogleDocs.config
end
private
def google_docs_for_user
service_token, service_secret = Rails.cache.fetch(['google_docs_tokens', self.user].cache_key) do
service = self.user.user_services.find_by_service("google_docs")
service && [service.token, service.secret]
end
raise GoogleDocs::NoTokenError unless service_token && service_secret
GoogleDocs.new(service_token, service_secret)
end
end

View File

@ -26,28 +26,18 @@ class GoogleDocs
end
end
def initialize(user, session)
@user = user
@session = session
def initialize(oauth_gdocs_access_token, oauth_gdocs_access_token_secret)
@oauth_gdocs_access_token = oauth_gdocs_access_token
@oauth_gdocs_access_token_secret = oauth_gdocs_access_token_secret
end
def retrieve_access_token
consumer = GoogleDocs.consumer
return nil unless consumer
if @user
service_token, service_secret = Rails.cache.fetch(['google_docs_tokens', @user].cache_key) do
service = @user.user_services.find_by_service("google_docs")
service && [service.token, service.secret]
end
raise NoTokenError unless service_token && service_secret
access_token = OAuth::AccessToken.new(consumer, service_token, service_secret)
else
access_token = OAuth::AccessToken.new(consumer, @session[:oauth_gdocs_access_token_token], @session[:oauth_gdocs_access_token_secret])
end
access_token
@access_token ||= OAuth::AccessToken.new(consumer, @oauth_gdocs_access_token, @oauth_gdocs_access_token_secret)
end
def get_service_user(access_token)
def get_service_user_info(access_token)
doc = create_doc("Temp Doc: #{Time.now.strftime("%d %b %Y, %I:%M %p")}", access_token)
delete_doc(doc, access_token)
service_user_id = doc.entry.authors[0].email rescue nil
@ -55,47 +45,17 @@ class GoogleDocs
return service_user_id, service_user_name
end
def self.get_access_token(oauth_request, oauth_verifier, session, user)
def self.get_access_token(token, secret, oauth_verifier)
consumer = GoogleDocs.consumer
request_token = OAuth::RequestToken.new(consumer,
session.delete(:oauth_google_docs_request_token_token),
session.delete(:oauth_google_docs_request_token_secret))
access_token = request_token.get_access_token(:oauth_verifier => oauth_verifier)
google_docs = GoogleDocs.new(user, session)
service_user_id, service_user_name = google_docs.get_service_user(access_token)
session[:oauth_gdocs_access_token_token] = access_token.token
session[:oauth_gdocs_access_token_secret] = access_token.secret
if oauth_request.user
UserService.register(
:service => "google_docs",
:access_token => access_token,
:user => oauth_request.user,
:service_domain => "google.com",
:service_user_id => service_user_id,
:service_user_name => service_user_name
)
oauth_request.destroy
session.delete(:oauth_gdocs_access_token_token)
session.delete(:oauth_gdocs_access_token_secret)
end
access_token
token,
secret)
request_token.get_access_token(:oauth_verifier => oauth_verifier)
end
def self.request_token_url(return_to, session, user, host_with_port, oauth_callback)
def self.request_token(oauth_callback)
consumer = GoogleDocs.consumer
request_token = consumer.get_request_token({ :oauth_callback => oauth_callback}, {:scope => "https://docs.google.com/feeds/ https://spreadsheets.google.com/feeds/"})
session[:oauth_google_docs_request_token_token] = request_token.token
session[:oauth_google_docs_request_token_secret] = request_token.secret
OauthRequest.create(
:service => 'google_docs',
:token => request_token.token,
:secret => request_token.secret,
:user_secret => CanvasUuid::Uuid.generate(nil, 16),
:return_url => return_to,
:user => user,
:original_host_with_port => host_with_port
)
request_token.authorize_url
consumer.get_request_token({ :oauth_callback => oauth_callback}, {:scope => "https://docs.google.com/feeds/ https://spreadsheets.google.com/feeds/"})
end
@ -276,8 +236,7 @@ class GoogleDocs
add_extension_namespace :gAcl, 'http://schemas.google.com/acl/2007'
end
def create_doc(name, access_token=nil)
access_token ||= retrieve_access_token
def create_doc(name, access_token)
url = "https://docs.google.com/feeds/documents/private/full"
entry = Atom::Entry.new do |entry|
entry.title = name

View File

@ -25,6 +25,80 @@ describe ApplicationController do
controller.stubs(:request).returns(stub(:host_with_port => "www.example.com"))
end
describe "#google_docs_connection" do
it "uses @real_current_user first" do
mock_real_current_user = mock()
mock_current_user = mock()
controller.instance_variable_set(:@real_current_user, mock_real_current_user)
controller.instance_variable_set(:@current_user, mock_current_user)
session[:oauth_gdocs_access_token_token] = "session_token"
session[:oauth_gdocs_access_token_secret] = "sesion_secret"
Rails.cache.expects(:fetch).with(['google_docs_tokens', mock_real_current_user].cache_key).returns(["real_current_user_token", "real_current_user_secret"])
GoogleDocs.expects(:new).with("real_current_user_token", "real_current_user_secret")
controller.send(:google_docs_connection)
end
it "uses @current_user second" do
mock_current_user = mock()
controller.instance_variable_set(:@real_current_user, nil)
controller.instance_variable_set(:@current_user, mock_current_user)
session[:oauth_gdocs_access_token_token] = "session_token"
session[:oauth_gdocs_access_token_secret] = "sesion_secret"
Rails.cache.expects(:fetch).with(['google_docs_tokens', mock_current_user].cache_key).returns(["current_user_token", "current_user_secret"])
GoogleDocs.expects(:new).with("current_user_token", "current_user_secret")
controller.send(:google_docs_connection)
end
it "queries user services if token isn't in the cache" do
mock_current_user = mock()
controller.instance_variable_set(:@real_current_user, nil)
controller.instance_variable_set(:@current_user, mock_current_user)
session[:oauth_gdocs_access_token_token] = "session_token"
session[:oauth_gdocs_access_token_secret] = "sesion_secret"
mock_user_services = mock("mock_user_services")
mock_current_user.expects(:user_services).returns(mock_user_services)
mock_user_services.expects(:find_by_service).with("google_docs").returns(mock(token: "user_service_token", secret: "user_service_secret"))
GoogleDocs.expects(:new).with("user_service_token", "user_service_secret")
controller.send(:google_docs_connection)
end
it "uses the session values if no users are set" do
controller.instance_variable_set(:@real_current_user, nil)
controller.instance_variable_set(:@current_user, nil)
session[:oauth_gdocs_access_token_token] = "session_token"
session[:oauth_gdocs_access_token_secret] = "sesion_secret"
GoogleDocs.expects(:new).with("session_token", "sesion_secret")
controller.send(:google_docs_connection)
end
it "raises a NoTokenError when the user exists but does not have a user service" do
mock_current_user = mock()
controller.instance_variable_set(:@real_current_user, nil)
controller.instance_variable_set(:@current_user, mock_current_user)
session[:oauth_gdocs_access_token_token] = "session_token"
session[:oauth_gdocs_access_token_secret] = "sesion_secret"
mock_user_services = mock("mock_user_services")
mock_current_user.expects(:user_services).returns(mock_user_services)
mock_user_services.expects(:find_by_service).with("google_docs").returns(nil)
expect {
controller.send(:google_docs_connection)
}.to raise_error(GoogleDocs::NoTokenError)
end
end
describe "js_env" do
it "should set items" do
HostUrl.expects(:file_host).with(Account.default, "www.example.com").returns("files.example.com")
@ -165,7 +239,7 @@ describe ApplicationController do
@pseudonym.update_attribute(:sis_user_id, 'test1')
controller.instance_variable_set(:@domain_root_account, Account.default)
controller.stubs(:named_context_url).with(@user, :context_url).returns('')
controller.stubs(:params).returns({ :user_id => 'sis_user_id:test1' })
controller.stubs(:params).returns({:user_id => 'sis_user_id:test1'})
controller.stubs(:api_request?).returns(true)
controller.send(:get_context)
controller.instance_variable_get(:@context).should == @user
@ -177,7 +251,7 @@ describe ApplicationController do
@section.update_attribute(:sis_source_id, 'test1')
controller.instance_variable_set(:@domain_root_account, Account.default)
controller.stubs(:named_context_url).with(@section, :context_url).returns('')
controller.stubs(:params).returns({ :course_section_id => 'sis_section_id:test1' })
controller.stubs(:params).returns({:course_section_id => 'sis_section_id:test1'})
controller.stubs(:api_request?).returns(true)
controller.send(:get_context)
controller.instance_variable_get(:@context).should == @section
@ -191,7 +265,7 @@ describe ApplicationController do
acct = Account.default
acct.default_locale = "es"
acct.save!
controller.instance_variable_set(:@domain_root_account, acct)
controller.instance_variable_set(:@domain_root_account, acct)
req = mock()
req.stubs(:headers).returns({})
controller.stubs(:request).returns(req)
@ -200,7 +274,7 @@ describe ApplicationController do
I18n.locale.to_s.should == "es"
course_model(:locale => "ru")
controller.stubs(:named_context_url).with(@course, :context_url).returns('')
controller.stubs(:params).returns({ :course_id => @course.id })
controller.stubs(:params).returns({:course_id => @course.id})
controller.stubs(:api_request?).returns(false)
controller.stubs(:session).returns({})
controller.stubs(:js_env).returns({})
@ -219,7 +293,6 @@ describe ApplicationController do
end
end
end
end
describe WikiPagesController do
@ -233,7 +306,7 @@ describe WikiPagesController do
get 'pages_index', :course_id => @course.id
controller.js_env.should include(:WIKI_RIGHTS)
controller.js_env[:WIKI_RIGHTS].should == Hash[@course.wiki.check_policy(@teacher).map {|right| [right, true]}]
controller.js_env[:WIKI_RIGHTS].should == Hash[@course.wiki.check_policy(@teacher).map { |right| [right, true] }]
end
end
end

View File

@ -41,8 +41,28 @@ describe CollaborationsController do
it "should assign variables" do
course_with_student_logged_in(:active_all => true)
mock_user_service = mock()
@user.expects(:user_services).returns(mock_user_service)
mock_user_service.expects(:find_by_service).with("google_docs").returns(mock(token: "token", secret: "secret"))
GoogleDocs.any_instance.expects(:verify_access_token).returns(true)
get 'index', :course_id => @course.id
response.should be_success
assigns(:google_docs_authorized).should == true
end
it "should assign variables when verify raises" do
course_with_student_logged_in(:active_all => true)
mock_user_service = mock()
@user.expects(:user_services).returns(mock_user_service)
mock_user_service.expects(:find_by_service).with("google_docs").returns(mock(token: "token", secret: "secret"))
GoogleDocs.any_instance.expects(:verify_access_token).raises("Error")
get 'index', :course_id => @course.id
response.should be_success
assigns(:google_docs_authorized).should == false
end
it "should not allow the student view student to access collaborations" do
@ -61,6 +81,10 @@ describe CollaborationsController do
group = gc.groups.create!(:context => @course)
group.add_user(@student, 'accepted')
mock_user_service = mock()
@user.expects(:user_services).returns(mock_user_service)
mock_user_service.expects(:find_by_service).with("google_docs").returns(mock(token: "token", secret: "secret"))
get 'index', :group_id => group.id
response.should be_success
end

View File

@ -184,6 +184,9 @@ describe SubmissionsController do
flag.feature = 'google_docs_domain_restriction'
flag.state = 'on'
flag.save!
mock_user_service = mock()
@user.expects(:user_services).returns(mock_user_service)
mock_user_service.expects(:find_by_service).with("google_docs").returns(mock(token: "token", secret: "secret"))
end
it "should not save if domain restriction prevents it" do

View File

@ -794,4 +794,25 @@ describe UsersController do
end
end
end
describe "oauth_success" do
it "should use the access token to get user info" do
user_with_pseudonym
admin = @user
user_session(admin)
mock_oauth_request = stub(token: 'token', secret: 'secret', original_host_with_port: 'test.host', user: @user, return_url: '/')
OauthRequest.expects(:find_by_token_and_service).with('token', 'google_docs').returns(mock_oauth_request)
mock_access_token = stub(token: '123', secret: 'abc')
GoogleDocs.expects(:get_access_token).with('token', 'secret', 'oauth_verifier').returns(mock_access_token)
mock_google_docs = stub()
GoogleDocs.expects(:new).with('token', 'secret').returns(mock_google_docs)
mock_google_docs.expects(:get_service_user_info).with(mock_access_token)
get 'oauth_success', {oauth_token: 'token', service: 'google_docs', oauth_verifier: 'oauth_verifier'}, {host_with_port: 'test.host'}
end
end
end

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom">
<title>Temp Doc: 07 Apr 2014, 08:32 AM</title>
<category label="document" scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/docs/2007#document"/>
</entry>

View File

@ -23,6 +23,17 @@ describe CollaborationsController, type: :request do
it 'should properly link to the user who posted the collaboration' do
PluginSetting.create!(:name => 'etherpad', :settings => {})
course_with_teacher_logged_in :active_all => true, :name => "teacher 1"
UserService.register(
:service => "google_docs",
:token => "token",
:secret => "secret",
:user => @user,
:service_domain => "google.com",
:service_user_id => "service_user_id",
:service_user_name => "service_user_name"
)
get "/courses/#{@course.id}/collaborations/"
response.should be_success

View File

@ -94,7 +94,7 @@ describe integration do
# mock up the response from the 3rd party service, so we don't actually contact it
OAuth::Consumer.any_instance.expects(:token_request).with(anything, anything, anything, has_entry(:oauth_verifier, "test_verifier"), anything).returns({:oauth_token => "test_token", :oauth_token_secret => "test_secret"})
if integration == "GoogleDocs"
GoogleDocs.any_instance.expects(:get_service_user).with(instance_of(OAuth::AccessToken)).returns(["test_user_id", "test_user_name"])
GoogleDocs.any_instance.expects(:get_service_user_info).returns(["test_user_id", "test_user_name"])
elsif integration == "LinkedIn"
LinkedIn::Connection.any_instance.expects(:get_service_user_info).with(instance_of(OAuth::AccessToken)).returns(["test_user_id", "test_user_name"])
else
@ -121,7 +121,7 @@ describe integration do
# pretend that somehow we think we got a valid auth token, but we actually didn't
if integration == "GoogleDocs"
GoogleDocs.any_instance.expects(:get_service_user).with(instance_of(OAuth::AccessToken)).raises(RuntimeError, "Third-party service totally like, failed")
GoogleDocs.any_instance.expects(:get_service_user_info).raises(RuntimeError, "Third-party service totally like, failed")
elsif integration == "LinkedIn"
LinkedIn::Connection.any_instance.expects(:get_service_user_info).with(instance_of(OAuth::AccessToken)).raises(RuntimeError, "Third-party service totally like, failed")
else

View File

@ -31,69 +31,78 @@ describe GoogleDocs do
let(:xml_doc_list_one) { load_fixture("doc_list_one.xml") }
let(:xml_doc_list_many) { load_fixture("doc_list_many.xml") }
let(:xml_create_doc_request) { load_fixture("create_doc_request.xml") }
let(:xml_create_temp_doc_request) { load_fixture("create_temp_doc_request.xml") }
let(:xml_create_doc_response) { load_fixture("create_doc_response.xml") }
let(:xml_remove_doc_request) { load_fixture("remove_doc_request.xml") }
let(:xml_remove_doc_response) { load_fixture("remove_doc_response.xml") }
let(:xml_add_user_acl) { load_fixture("add_user_acl.xml") }
let(:token) { "token" }
let(:secret) { "secret" }
before do
@user = User.create!
GoogleDocs.stubs(:config).returns(google_doc_settings)
UserService.register(
:service => "google_docs",
:token => GoogleDocs.config["test_user_token"],
:secret => GoogleDocs.config["test_user_secret"],
:user => @user,
:service_domain => "google.com",
:service_user_id => GoogleDocs.config["test_user_id"],
:service_user_name => GoogleDocs.config["test_user_name"]
)
config = {
"api_key" => "key",
"secret_key" => "secret",
}
Setting.stubs(:from_config).returns(config)
Canvas::Plugin.stubs(:find).returns(nil)
end
describe "#retrieve_access_token" do
it "should not error out if the google plugin is not configured" do
GoogleDocs.stubs(:config).returns nil
google_docs = GoogleDocs.new(@user, {})
Canvas::Plugin.stubs(:find).returns(nil)
Setting.stubs(:from_config).returns(nil)
google_docs = GoogleDocs.new(token, secret)
google_docs.retrieve_access_token.should be_nil
end
it "should use the current_user" do
user_service = mock
google_docs_service = mock(token: 'u_token_from_service', secret: 'u_secret_from_service')
user_service.expects(:find_by_service).with("google_docs").returns(google_docs_service)
@user.expects(:user_services).returns(user_service)
it "news up an OAuth::AccessToken" do
access_token = mock_access_token
consumer = new_mock_consumer('key', 'secret')
access_token = new_mock_access_token(consumer, 'u_token_from_service', 'u_secret_from_service')
google_docs = GoogleDocs.new(@user, {})
google_docs.retrieve_access_token.should eql access_token
google_docs = GoogleDocs.new(token, secret)
google_docs.retrieve_access_token.should == access_token
end
end
it "should use the session data if no user" do
consumer = new_mock_consumer('key', 'secret')
access_token = new_mock_access_token(consumer, 'u_token_from_session', 'u_secret_from_session')
session = {
:oauth_gdocs_access_token_token => 'u_token_from_session',
:oauth_gdocs_access_token_secret => 'u_secret_from_session'
}
@google_docs = GoogleDocs.new(nil, session)
@google_docs.retrieve_access_token.should == access_token
describe "#get_service_user_info" do
it "returns service user id and name" do
known_time = Time.new(2014, 4, 7, 8, 32)
Timecop.freeze(known_time) do
prepare_mock_post(xml_schema_id, xml_create_temp_doc_request, xml_create_doc_response)
google_docs = GoogleDocs.new(token, secret)
prepare_mock_delete("#{xml_schema_id}/document:1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA")
user_service_id, user_service_name = google_docs.get_service_user_info google_docs.retrieve_access_token
user_service_id.should == "instructure.test.2011@gmail.com"
user_service_name.should == "instructure.test.2011@gmail.com"
end
end
end
it "raises NoTokenError if user does not have google_docs service" do
Rails.cache.delete(['google_docs_tokens', @user].cache_key)
describe ".get_access_token" do
it "returns the access token" do
mock_consumer = mock_consumer()
mock_request_token = mock()
OAuth::RequestToken.expects(:new).with(mock_consumer, token, secret).returns(mock_request_token)
mock_access_token = mock()
mock_request_token.expects(:get_access_token).with(oauth_verifier: "oauth-verifier").returns(mock_access_token)
user_service = mock
user_service.expects(:find_by_service).with("google_docs").returns(nil)
@user.expects(:user_services).returns(user_service)
access_token = GoogleDocs.get_access_token(token, secret, "oauth-verifier")
new_mock_consumer('key', 'secret')
access_token.should == mock_access_token
end
end
google_docs = GoogleDocs.new(@user, {})
expect do
google_docs.retrieve_access_token
end.to raise_error(GoogleDocs::NoTokenError)
describe ".request_token" do
it "returns a request token" do
consumer = mock_consumer
mock_request_token = mock()
consumer.expects(:get_request_token).with({:oauth_callback => "http://callback.example.com"}, {:scope => "https://docs.google.com/feeds/ https://spreadsheets.google.com/feeds/"}).returns(mock_request_token)
request_token = GoogleDocs.request_token("http://callback.example.com")
request_token.should == mock_request_token
end
end
@ -101,7 +110,7 @@ describe GoogleDocs do
context "with an empty list" do
before do
prepare_mock_get xml_doc_list_empty
@google_docs = GoogleDocs.new(@user, {})
@google_docs = GoogleDocs.new(token, secret)
end
it "handles an empty list" do
document_id_list = @google_docs.list_with_extension_filter(nil).files.map(&:document_id)
@ -115,7 +124,7 @@ describe GoogleDocs do
context "with a single document" do
before do
@google_docs = GoogleDocs.new(@user, {})
@google_docs = GoogleDocs.new(token, secret)
end
it "and nil filter" do
prepare_mock_get xml_doc_list_one
@ -148,7 +157,7 @@ describe GoogleDocs do
context "with multiple documents" do
before do
@google_docs = GoogleDocs.new(@user, {})
@google_docs = GoogleDocs.new(token, secret)
end
it "returns filesystem view of results" do
prepare_mock_get xml_doc_list_many
@ -179,18 +188,17 @@ describe GoogleDocs do
end
end
describe "#download" do
it "pulls the document out that matches the provided id" do
doc_id = 'spreadsheet:0AiN8C_VHrPxkdEF6YmQyc3p2Qm02ODhJWGJnUmJYY2c'
token = mock_access_token
access_token = mock_access_token
document_response = mock()
token.expects(:get).with('https://docs.google.com/feeds/download/spreadsheets/Export?key=0AiN8C_VHrPxkdEF6YmQyc3p2Qm02ODhJWGJnUmJYY2c').returns(document_response)
access_token.expects(:get).with('https://docs.google.com/feeds/download/spreadsheets/Export?key=0AiN8C_VHrPxkdEF6YmQyc3p2Qm02ODhJWGJnUmJYY2c').returns(document_response)
response = mock()
response.expects(:body).returns(xml_doc_list_many)
token.expects(:get).with(xml_schema_id).returns(response)
access_token.expects(:get).with(xml_schema_id).returns(response)
google_docs = GoogleDocs.new(@user, {})
google_docs = GoogleDocs.new(token, secret)
doc_array = google_docs.download(doc_id)
doc_array[0].should == document_response
doc_array[1].should == 'Sprint Teams'
@ -199,19 +207,19 @@ describe GoogleDocs do
it "follows redirects" do
doc_id = 'spreadsheet:0AiN8C_VHrPxkdEF6YmQyc3p2Qm02ODhJWGJnUmJYY2c'
token = mock_access_token
access_token = mock_access_token
document_response = mock()
redirect = Net::HTTPFound.new(1.0, 302, "FOUND")
redirect['Location'] = 'http://example.com/1234'
token.expects(:get).with('https://docs.google.com/feeds/download/spreadsheets/Export?key=0AiN8C_VHrPxkdEF6YmQyc3p2Qm02ODhJWGJnUmJYY2c').returns(redirect)
token.expects(:get).with('http://example.com/1234').returns(document_response)
access_token.expects(:get).with('https://docs.google.com/feeds/download/spreadsheets/Export?key=0AiN8C_VHrPxkdEF6YmQyc3p2Qm02ODhJWGJnUmJYY2c').returns(redirect)
access_token.expects(:get).with('http://example.com/1234').returns(document_response)
response = mock()
response.expects(:body).returns(xml_doc_list_many)
token.expects(:get).with(xml_schema_id).returns(response)
access_token.expects(:get).with(xml_schema_id).returns(response)
google_docs = GoogleDocs.new(@user, {})
google_docs = GoogleDocs.new(token, secret)
doc_array = google_docs.download(doc_id)
doc_array[0].should == document_response
doc_array[1].should == 'Sprint Teams'
@ -220,25 +228,24 @@ describe GoogleDocs do
it "handles nonexistant entry" do
doc_id = 'spreadsheet:WRONG'
token = mock_access_token
#token.expects(:get).with('https://docs.google.com/feeds/download/spreadsheets/Export?key=0AiN8C_VHrPxkdEF6YmQyc3p2Qm02ODhJWGJnUmJYY2c').returns(mock())
access_token = mock_access_token
response = mock()
response.expects(:body).returns(xml_doc_list_many)
token.expects(:get).with(xml_schema_id).returns(response)
access_token.expects(:get).with(xml_schema_id).returns(response)
google_docs = GoogleDocs.new(@user, {})
google_docs = GoogleDocs.new(token, secret)
doc_array = google_docs.download(doc_id)
doc_array.should == [nil, nil, nil]
end
end
describe "documents" do
describe "#create_doc" do
it "can be created" do
prepare_mock_post(xml_schema_id, xml_create_doc_request, xml_create_doc_response)
google_docs = GoogleDocs.new(@user, {})
google_docs = GoogleDocs.new(token, secret)
new_document = google_docs.create_doc "test document"
new_document = google_docs.create_doc "test document", google_docs.retrieve_access_token
new_document.document_id.should == 'document:1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA'
new_document.extension.should == 'doc'
new_document.display_name.should == 'test document'
@ -246,26 +253,30 @@ describe GoogleDocs do
new_document.alternate_url.should be_a(Atom::Link)
new_document.alternate_url.href.should == 'https://docs.google.com/document/d/1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA/edit?hl=en_US'
end
end
it "can be removed" do
prepare_mock_post('https://docs.google.com/feeds/acl/private/full/document:1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA/batch', xml_remove_doc_request, xml_remove_doc_response)
google_docs = GoogleDocs.new(@user, {})
result = google_docs.acl_remove 'document:1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA', ['user@example.com']
result.should == []
end
describe "#delete_doc" do
it "can be deleted" do
prepare_mock_post(xml_schema_id, xml_create_doc_request, xml_create_doc_response)
google_docs = GoogleDocs.new(@user, {})
new_document = google_docs.create_doc "test document"
google_docs = GoogleDocs.new(token, secret)
new_document = google_docs.create_doc("test document", google_docs.retrieve_access_token)
prepare_mock_delete "#{xml_schema_id}/document:1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA"
google_docs.delete_doc new_document
end
end
describe "#acl_remove" do
it "can be removed" do
prepare_mock_post('https://docs.google.com/feeds/acl/private/full/document:1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA/batch', xml_remove_doc_request, xml_remove_doc_response)
google_docs = GoogleDocs.new(token, secret)
result = google_docs.acl_remove 'document:1HJoN38KHlnu32B5z_THgchnTMUbj7dgs8P-Twrm38cA', ['user@example.com']
result.should == []
end
end
describe "#acl_add" do
let(:doc_response) { mock }
let(:doc_id) { "12345" }
@ -274,7 +285,7 @@ describe GoogleDocs do
it "should add users to a document" do
prepare_mock_post(url, xml_add_user_acl, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<feed xmlns=\"http://www.w3.org/2005/Atom\"/>\n")
google_docs = GoogleDocs.new(@user, {})
google_docs = GoogleDocs.new(token, secret)
mock_user = mock()
mock_user.stubs(:id).returns(192)
mock_user.stubs(:google_docs_address).returns('u_id')
@ -286,7 +297,7 @@ describe GoogleDocs do
access_token = mock_access_token
access_token.expects(:post).never
google_docs = GoogleDocs.new(@user, {})
google_docs = GoogleDocs.new(token, secret)
mock_user = mock()
mock_user.stubs(:id).returns(192)
mock_user.stubs(:google_docs_address).returns('u_id')
@ -314,7 +325,7 @@ describe GoogleDocs do
def mock_consumer
consumer = mock()
OAuth::Consumer.expects(:new).with(
OAuth::Consumer.expects(:new).at_least_once.with(
GoogleDocs.config["api_key"],
GoogleDocs.config["secret_key"], {
:signature_method => 'HMAC-SHA1',
@ -325,33 +336,13 @@ describe GoogleDocs do
return consumer
end
def new_mock_consumer(api_key, secret_key)
consumer = mock()
OAuth::Consumer.expects(:new).with(
api_key,
secret_key, {
:signature_method => 'HMAC-SHA1',
:request_token_path => '/accounts/OAuthGetRequestToken',
:site => 'https://www.google.com',
:authorize_path => '/accounts/OAuthAuthorizeToken',
:access_token_path => '/accounts/OAuthGetAccessToken'}).returns(consumer)
return consumer
end
def mock_access_token
access_token = mock()
OAuth::AccessToken.expects(:new).with(mock_consumer(),
GoogleDocs.config["test_user_token"],
GoogleDocs.config["test_user_secret"]).returns(access_token)
return access_token
end
def new_mock_access_token(consumer, user_token, user_secret)
access_token = mock()
OAuth::AccessToken.expects(:new).with(consumer,
user_token,
user_secret).returns(access_token)
return access_token
return @access_token if @access_token
@access_token = mock()
OAuth::AccessToken.expects(:new).at_least_once.with(mock_consumer(),
"token",
"secret").returns(@access_token)
return @access_token
end
def prepare_mock_get(response_xml)

View File

@ -0,0 +1,37 @@
#
# Copyright (C) 2014 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
require 'spec_helper'
describe GoogleDocsCollaboration do
describe "#initialize_document" do
let(:user) { User.new }
it "creates a google doc" do
google_docs_collaboration = GoogleDocsCollaboration.new
google_docs_collaboration.title = "title"
google_docs_collaboration.user = user
google_doc_connection = stub(retrieve_access_token: "asdf123")
GoogleDocs.expects(:new).returns(google_doc_connection)
file = stub(document_id: 1, entry: stub(to_xml: "<xml></xml>"), alternate_url: "http://google.com")
google_doc_connection.expects(:create_doc).with("title", "asdf123").returns(file)
Rails.cache.expects(:fetch).returns(["token", "secret"])
google_docs_collaboration.initialize_document
end
end
end

View File

@ -84,6 +84,17 @@ describe "collaborations" do
before(:each) do
course_with_teacher_logged_in
UserService.register(
:service => "google_docs",
:token => "token",
:secret => "secret",
:user => @user,
:service_domain => "google.com",
:service_user_id => "service_user_id",
:service_user_name => "service_user_name"
)
if type == 'google_docs'
GoogleDocs.any_instance.
stubs(:verify_access_token).