canvas_connect 0.0.1

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.
@@ -0,0 +1,105 @@
1
+ #
2
+ # Copyright (C) 2012 Instructure, Inc.
3
+ #
4
+ # This file is part of Canvas.
5
+ #
6
+ # Canvas is free software: you can redistribute it and/or modify it under
7
+ # the terms of the GNU Affero General Public License as published by the Free
8
+ # Software Foundation, version 3 of the License.
9
+ #
10
+ # Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
11
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
+ # A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
+ # details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License along
16
+ # with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #
18
+
19
+ module CanvasConnect
20
+ class ConnectUser
21
+ attr_accessor :id, :canvas_user
22
+ attr_reader :client
23
+
24
+ # Public: Create a new ConnectUser instance.
25
+ #
26
+ # canvas_user - A Canvas User object.
27
+ # client - A CanvasConnect::Service instance. (default: CanvasConnect.client)
28
+ def initialize(canvas_user, client = CanvasConnect.client)
29
+ @canvas_user, @client = [canvas_user, client]
30
+ end
31
+
32
+ # Public: Save this user to the Adobe Connect instance.
33
+ #
34
+ # Returns true.
35
+ def save
36
+ response = @client.principal_update(
37
+ :first_name => @canvas_user.first_name.present? ? @canvas_user.first_name : 'Unknown',
38
+ :last_name => @canvas_user.last_name.present? ? @canvas_user.last_name : 'Unknown',
39
+ :login => username,
40
+ :password => password,
41
+ :type => 'user',
42
+ :has_children => 0,
43
+ :email => @canvas_user.email)
44
+ @id = response.at_xpath('//principal')['principal-id']
45
+ true
46
+ end
47
+
48
+ # Public: Generate a unique Adobe Connect username for this user.
49
+ #
50
+ # Examples
51
+ #
52
+ # connect_user.username #=> canvas_user_15
53
+ #
54
+ # Returns a username string.
55
+ def username
56
+ "canvas_user_#{@canvas_user.id}"
57
+ end
58
+
59
+ # Internal: Generate a 10 character password for Adobe Connect.
60
+ #
61
+ # Returns a password string.
62
+ def password
63
+ @password ||= Digest::SHA1.hexdigest(@canvas_user.uuid)[0..9]
64
+ end
65
+
66
+ class << self
67
+ # Public: Find a Canvas user on an Adobe Connect instance.
68
+ #
69
+ # user - A Canvas user object.
70
+ #
71
+ # Returns a CanvasConnect::ConnectUser or nil.
72
+ def find(user)
73
+ connect_user = ConnectUser.new(user)
74
+ response = connect_user.client.principal_list(:filter_login => connect_user.username)
75
+ if found_user = response.at_xpath('//principal')
76
+ connect_user.id = found_user['principal-id']
77
+ connect_user
78
+ else
79
+ nil
80
+ end
81
+ end
82
+
83
+ # Public: Create an Adobe Connect user for the given Canvas user.
84
+ #
85
+ # user - The Canvas user to create in Connect.
86
+ #
87
+ # Returns a new CanvasConnect::ConnectUser.
88
+ def create(user)
89
+ new_user = ConnectUser.new(user)
90
+ new_user.save
91
+
92
+ new_user
93
+ end
94
+
95
+ # Public: Find the given user in Connect or, if they don't exist, create them.
96
+ #
97
+ # user - A Canvas user.
98
+ #
99
+ # Returns a CanvasConnect::ConnectUser.
100
+ def find_or_create(user)
101
+ find(user) || create(user)
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,60 @@
1
+ #
2
+ # Copyright (C) 2012 Instructure, Inc.
3
+ #
4
+ # This file is part of Canvas.
5
+ #
6
+ # Canvas is free software: you can redistribute it and/or modify it under
7
+ # the terms of the GNU Affero General Public License as published by the Free
8
+ # Software Foundation, version 3 of the License.
9
+ #
10
+ # Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
11
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
+ # A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
+ # details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License along
16
+ # with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #
18
+
19
+ module CanvasConnect
20
+ class MeetingFolder
21
+ attr_accessor :name
22
+
23
+ extend ActiveSupport::Memoizable
24
+
25
+ # Public: Create a new MeetingFolder.
26
+ #
27
+ # name - The name of the folder on Adobe Connect (must already exist).
28
+ # client - A CanvasConnect::Service to make requests with. (default: CanvasConnect.client)
29
+ def initialize(name, client = CanvasConnect.client)
30
+ @name = name
31
+ @client = client
32
+ end
33
+
34
+ # Public: Get the SCO ID for this folder.
35
+ #
36
+ # Returns an SCO ID string or nil if it doesn't exist.
37
+ def id
38
+ container = @client.sco_shortcuts.at_xpath('//sco[@type="user-meetings"]')
39
+ remote_folder = @client.sco_expanded_contents(:sco_id => container['sco-id'],
40
+ :filter_name => @name)
41
+
42
+ remote_folder.at_xpath('//sco')['sco-id']
43
+ rescue NoMethodError
44
+ # Return nil if the container or remote_folder can't be found.
45
+ nil
46
+ end
47
+ memoize :id
48
+
49
+ # Public: Get the URL path for this folder.
50
+ #
51
+ # Returns a URL fragment string or nil if it can't be queried.
52
+ def url_path
53
+ response = @client.sco_info(:sco_id => @id)
54
+ response.at_xpath('//url-path').text
55
+ rescue NoMethodError
56
+ nil
57
+ end
58
+ memoize :url_path
59
+ end
60
+ end
@@ -0,0 +1,29 @@
1
+ #
2
+ # Copyright (C) 2012 Instructure, Inc.
3
+ #
4
+ # This file is part of Canvas.
5
+ #
6
+ # Canvas is free software: you can redistribute it and/or modify it under
7
+ # the terms of the GNU Affero General Public License as published by the Free
8
+ # Software Foundation, version 3 of the License.
9
+ #
10
+ # Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
11
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
+ # A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
+ # details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License along
16
+ # with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #
18
+
19
+ module CanvasConnect
20
+ class Response < SimpleDelegator
21
+ attr_reader :status, :headers, :body
22
+
23
+ def initialize(status, headers, body)
24
+ @status, @headers, @body = [status.to_i, headers, Nokogiri::XML(body)]
25
+ super(@body)
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,138 @@
1
+ #
2
+ # Copyright (C) 2012 Instructure, Inc.
3
+ #
4
+ # This file is part of Canvas.
5
+ #
6
+ # Canvas is free software: you can redistribute it and/or modify it under
7
+ # the terms of the GNU Affero General Public License as published by the Free
8
+ # Software Foundation, version 3 of the License.
9
+ #
10
+ # Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
11
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
+ # A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
+ # details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License along
16
+ # with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #
18
+
19
+ module CanvasConnect
20
+ class Service
21
+ attr_reader :username, :domain, :is_authenticated
22
+
23
+ def initialize(username, password, domain)
24
+ @username, @password, @domain = [username, password, domain]
25
+ end
26
+
27
+ # Public: Authenticate against the Adobe Connect server.
28
+ #
29
+ # Returns true.
30
+ def log_in
31
+ unless logged_in?
32
+ response = login(:login => @username, :password => @password)
33
+ if response.xpath('//status[@code="ok"]').empty?
34
+ raise ConnectionError.new("Could not log in to #{@domain}.")
35
+ end
36
+
37
+ @is_authenticated = true
38
+ end
39
+
40
+ true
41
+ end
42
+
43
+ # Public: Determine if the current session is authenticated.
44
+ #
45
+ # Returns a boolean.
46
+ def logged_in?
47
+ @is_authenticated
48
+ end
49
+
50
+ # Public: Proxy any unknown methods to the Adobe Connect API.
51
+ #
52
+ # method - The snake-cased name of an Adobe Connect method, e.g. `common_info`.
53
+ # args - Two optional arguments: a hash of GET params, and a skip_session boolean.
54
+ #
55
+ # Returns a CanvasConnect::Response.
56
+ def method_missing(method, *args)
57
+ action = "#{method}".dasherize
58
+ params, skip_session = args
59
+ params ||= {}
60
+
61
+ request(action, params, !skip_session)
62
+ end
63
+
64
+ # Public: Create a new Connect session for the given user.
65
+ #
66
+ # user - A CanvasConnect::ConnectUser.
67
+ # domain - The domain to authenticate against.
68
+ def self.user_session(user, domain)
69
+ service = CanvasConnect::Service.new(user.username, user.password, domain)
70
+ service.log_in
71
+
72
+ service.session_key
73
+ end
74
+
75
+ # Public: Get a session token for future requests.
76
+ #
77
+ # Returns a session token.
78
+ def session_key
79
+ unless @session_key
80
+ response = request('common-info', {}, false)
81
+ @session_key = response.xpath('//cookie').text
82
+ end
83
+
84
+ @session_key
85
+ end
86
+
87
+ protected
88
+ # Internal: Create and/or return a Net::HTTP instance.
89
+ #
90
+ # Returns a Net::HTTP instance.
91
+ def client
92
+ unless @client
93
+ uri = URI.parse(@domain)
94
+ @client = Net::HTTP.new(uri.host, uri.port)
95
+ @client.use_ssl = (uri.scheme == 'https')
96
+ end
97
+
98
+ @client
99
+ end
100
+
101
+ # Internal: Make a request to the Adobe Connect API.
102
+ #
103
+ # action - The name of the Connect API action to call.
104
+ # params - A hash of parameters to pass with the request. (default: {})
105
+ # with_session - If true, make the request inside a new or existing session. (default: true)
106
+ #
107
+ # Returns a CanvasConnect::Response object.
108
+ def request(action, params = {}, with_session = true)
109
+ params[:session] = session_key if with_session
110
+ response = client.get("/api/xml?action=#{action}#{format_params(params)}")
111
+
112
+ CanvasConnect::Response.new(response.code, response.each_header { |h| }, response.body)
113
+ rescue SocketError, TimeoutError => e
114
+ # Return an empty, timed-out request.
115
+ Rails.logger.error "Adobe Connect Request Error on #{action}: #{e.message}"
116
+ CanvasConnect::Response.new(408, {}, '')
117
+ end
118
+
119
+ # Internal: Convert snake-cased hash keys to dashed.
120
+ #
121
+ # params - A hash of parameters with snake-cased string or symbol keys.
122
+ #
123
+ # Examples
124
+ #
125
+ # format_params({param_name: 'value', other_param: 'value 2'})
126
+ #
127
+ # # Returns "&param-name=value&other-param=value%202"
128
+ #
129
+ # Returns a query string prefixed with a '&' (because it assumes action will be included).
130
+ def format_params(params)
131
+ params.inject(['']) do |arr, p|
132
+ key, value = p
133
+ arr << "#{key.to_s.dasherize}=#{URI.escape(value.to_s)}"
134
+ end.join('&')
135
+ end
136
+ end
137
+ end
138
+
@@ -0,0 +1,22 @@
1
+ #
2
+ # Copyright (C) 2012 Instructure, Inc.
3
+ #
4
+ # This file is part of Canvas.
5
+ #
6
+ # Canvas is free software: you can redistribute it and/or modify it under
7
+ # the terms of the GNU Affero General Public License as published by the Free
8
+ # Software Foundation, version 3 of the License.
9
+ #
10
+ # Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
11
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
+ # A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
+ # details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License along
16
+ # with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #
18
+
19
+ module CanvasConnect
20
+ VERSION = "0.0.1"
21
+ end
22
+
@@ -0,0 +1,45 @@
1
+ #
2
+ # Copyright (C) 2012 Instructure, Inc.
3
+ #
4
+ # This file is part of Canvas.
5
+ #
6
+ # Canvas is free software: you can redistribute it and/or modify it under
7
+ # the terms of the GNU Affero General Public License as published by the Free
8
+ # Software Foundation, version 3 of the License.
9
+ #
10
+ # Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
11
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
+ # A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
+ # details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License along
16
+ # with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #
18
+
19
+ require File.expand_path(File.dirname(__FILE__) + '/../../../../../../../../spec/spec_helper')
20
+
21
+ describe Canvas::Plugins::Validators::AdobeConnectValidator do
22
+ let(:plugin_setting) { mock }
23
+
24
+ subject { Canvas::Plugins::Validators::AdobeConnectValidator }
25
+
26
+ it 'should allow an empty hash' do
27
+ subject.validate({}, plugin_setting).should eql Hash.new
28
+ end
29
+
30
+ it 'should error on missing keys' do
31
+ plugin_setting.expects(:errors).returns(stub(:add_to_base => true))
32
+ subject.validate({:domain => 'example.com'}, plugin_setting).should be_false
33
+ end
34
+
35
+ it 'should pass if all keys exist' do
36
+ valid_keys = {
37
+ :domain => 'example.com',
38
+ :login => 'username',
39
+ :password => 'password',
40
+ :meeting_container => 'folder_name'
41
+ }
42
+
43
+ subject.validate(valid_keys, plugin_setting).should eql valid_keys
44
+ end
45
+ end
@@ -0,0 +1,52 @@
1
+ #
2
+ # Copyright (C) 2012 Instructure, Inc.
3
+ #
4
+ # This file is part of Canvas.
5
+ #
6
+ # Canvas is free software: you can redistribute it and/or modify it under
7
+ # the terms of the GNU Affero General Public License as published by the Free
8
+ # Software Foundation, version 3 of the License.
9
+ #
10
+ # Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
11
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
+ # A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
+ # details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License along
16
+ # with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #
18
+
19
+ require File.expand_path(File.dirname(__FILE__) + '/../../../../../../spec/spec_helper')
20
+
21
+ describe CanvasConnect::Response do
22
+ let(:body) { '<?xml version="1.0"?><items><item>Item Name</item></items>' }
23
+ subject { CanvasConnect::Response.new(200, {}, body) }
24
+
25
+ it { should respond_to(:status) }
26
+ it { should respond_to(:headers) }
27
+ it { should respond_to(:body) }
28
+
29
+ its(:body) { should be_an_instance_of(Nokogiri::XML::Document) }
30
+
31
+ describe 'initialize' do
32
+ it 'should require a status' do
33
+ lambda { CanvasConnect::Response.new }.should raise_error(ArgumentError)
34
+ end
35
+
36
+ it 'should require headers' do
37
+ lambda { CanvasConnect::Response.new(200) }.should raise_error(ArgumentError)
38
+ end
39
+
40
+ it 'should require a body' do
41
+ lambda { CanvasConnect::Response.new(200, {}) }.should raise_error(ArgumentError)
42
+ end
43
+ end
44
+
45
+ describe 'simple delegator' do
46
+ let(:body) { '<?xml version="1.0"?><items><item>Item Name</item></items>' }
47
+
48
+ it 'should delegate to body' do
49
+ subject.xpath('//item').text.should eql 'Item Name'
50
+ end
51
+ end
52
+ end