socialcast 1.3.7 → 1.3.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 788cbe26a9ad449b6b0a366c6d264069586e4653
4
- data.tar.gz: 1f21d4c2e36ed4ab2e8809ae8d0aea2d7e38b943
3
+ metadata.gz: 43487693be70d4bd0315db025f57e8a2c492fcc7
4
+ data.tar.gz: a527ada1136ef1d6e3f2fd80ae8fb4e69cd493e7
5
5
  SHA512:
6
- metadata.gz: 90c71da04a539297455463f1b7f81f9b3737c957dd5882acc9539ed52a19f2f43315fcede8e96e38a6b8ce818c91d655d26134e87995715b1444b647e326c777
7
- data.tar.gz: 7a254b2b4132c6144a58a2d026e8aede0cae18c7a39c4f63c1f680e324578ddd2789c7f099f5cd5308297a56efa60ddfbe50e526d5189449b71afcc8b25a3f98
6
+ metadata.gz: 3f23a5a4c44d39c83c2a8b1b41868ecf7c25938141129b55993cd619e832ae44d835bfa022fb2700a5d68b595108a204c26ae3b4745855fc169453ed73684975
7
+ data.tar.gz: f9703ae386594ee84cac91e11625d2d0589a5697c8ab103b433b3ef1af37826ee7f5b00a915378de274eb77c06afc5b52b8d6bbc6835875cca6d0369f0d470a9
data/lib/socialcast.rb CHANGED
@@ -2,8 +2,10 @@ require 'yaml'
2
2
  require 'fileutils'
3
3
 
4
4
  require_relative 'socialcast/command_line/ldap_connector'
5
- require_relative 'socialcast/command_line/provision'
5
+ require_relative 'socialcast/command_line/provisioner'
6
6
  require_relative 'socialcast/command_line/authenticate'
7
+ require_relative 'socialcast/command_line/provision_user'
8
+ require_relative 'socialcast/command_line/provision_photo'
7
9
  require_relative 'socialcast/command_line/message'
8
10
  require_relative 'socialcast/command_line/cli'
9
11
  require_relative 'socialcast/command_line/version'
@@ -118,7 +118,7 @@ module Socialcast
118
118
 
119
119
  desc 'provision', 'provision users from ldap compatible user repository'
120
120
  method_option :config, :default => 'ldap.yml', :aliases => '-c', :desc => 'Path to ldap config file'
121
- method_option :output, :default => Socialcast::CommandLine::Provision::DEFAULT_OUTPUT_FILE, :aliases => '-o', :desc => 'Name of the output file'
121
+ method_option :output, :default => Socialcast::CommandLine::ProvisionUser::DEFAULT_OUTPUT_FILE, :aliases => '-o', :desc => 'Name of the output file'
122
122
  method_option :setup, :type => :boolean, :desc => 'Create an example ldap config file and exit'
123
123
  method_option :delete_users_file, :type => :boolean, :desc => 'Delete the output file'
124
124
  method_option :test, :type => :boolean, :desc => 'Do not persist changes'
@@ -130,18 +130,19 @@ module Socialcast
130
130
  config = ldap_config options
131
131
  load_plugins options
132
132
 
133
- Socialcast::CommandLine::Provision.new(config, options).provision
133
+ Socialcast::CommandLine::ProvisionUser.new(config, options).provision
134
134
 
135
- rescue Socialcast::CommandLine::Provision::ProvisionError => e
135
+ rescue Socialcast::CommandLine::ProvisionUser::ProvisionError => e
136
136
  Kernel.abort e.message
137
137
  end
138
138
 
139
139
  desc 'sync_photos', 'Upload default avatar photos from LDAP repository'
140
140
  method_option :config, :default => 'ldap.yml', :aliases => '-c'
141
+ method_option :force_sync, :default => false, :aliases => '-f', :desc => 'Pushes all photos from LDAP to socialcast'
141
142
  def sync_photos
142
143
  config = ldap_config options
143
144
 
144
- Socialcast::CommandLine::Provision.new(config).sync_photos
145
+ Socialcast::CommandLine::ProvisionPhoto.new(config, options).sync
145
146
  end
146
147
 
147
148
  no_tasks do
@@ -0,0 +1,79 @@
1
+ require 'digest/md5'
2
+
3
+ module Socialcast
4
+ module CommandLine
5
+ class ProvisionPhoto
6
+ include Socialcast::CommandLine::Provisioner
7
+ def sync
8
+ each_ldap_connector do |connector|
9
+
10
+ connector.attribute_mappings.fetch('profile_photo')
11
+
12
+ each_ldap_entry do |entry|
13
+ attr_mappings = connector.attribute_mappings
14
+ email = connector.grab(entry, attr_mappings['email'])
15
+
16
+ ## GET USER INFO
17
+ search_users_resource = Socialcast::CommandLine.resource_for_path '/api/users/search', http_config
18
+ user_search_response = search_users_resource.get(:params => { :q => email, :per_page => 1 }, :accept => :json)
19
+ user_info = JSON.parse(user_search_response)['users'].first
20
+
21
+ is_community_default = user_info && user_info['avatars'] && user_info['avatars']['is_community_default']
22
+ return unless is_community_default || @options[:force_sync]
23
+
24
+ ## PHOTO URL TO BINARY
25
+ if profile_photo_data = connector.grab(entry, attr_mappings['profile_photo'])
26
+ if profile_photo_data.start_with?('http')
27
+ begin
28
+ profile_photo_data = RestClient.get(profile_photo_data)
29
+ rescue => e
30
+ puts "Unable to download photo #{profile_photo_data} for #{email}"
31
+ puts e.response
32
+ next
33
+ end
34
+ end
35
+
36
+ ## FORCE ENCODING
37
+ profile_photo_data = profile_photo_data.force_encoding('binary')
38
+
39
+ ## CONTENT TYPE
40
+ unless content_type = binary_to_content_type(profile_photo_data)
41
+ puts "Skipping photo for #{email}: unknown image format (supports .gif, .png, .jpg)"
42
+ next
43
+ end
44
+
45
+ ## WRITE TEMP FILE
46
+ tempfile = Tempfile.new(["photo_upload", ".#{content_type}"])
47
+ tempfile.write(profile_photo_data)
48
+ tempfile.rewind
49
+
50
+ puts "Uploading photo for #{email}"
51
+
52
+ ## SUBMIT PHOTO
53
+ begin
54
+ user_resource = Socialcast::CommandLine.resource_for_path "/api/users/#{user_info['id']}", http_config
55
+ user_resource.put({ :user => { :profile_photo => { :data => tempfile } } })
56
+ ensure
57
+ tempfile.unlink
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def binary_to_content_type(binary_photo_data)
67
+ case binary_photo_data
68
+ when Regexp.new("^GIF8", nil, 'n')
69
+ 'gif'
70
+ when Regexp.new('^\x89PNG', nil, 'n')
71
+ 'png'
72
+ when Regexp.new("^\xff\xd8\xff\xe0\x00\x10JFIF", nil, 'n'), Regexp.new("^\xff\xd8\xff\xe1(.*){2}Exif", nil, 'n')
73
+ 'jpg'
74
+ end
75
+ end
76
+
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,66 @@
1
+ require 'zlib'
2
+ require 'builder'
3
+ require 'set'
4
+ require 'fileutils'
5
+
6
+ module Socialcast
7
+ module CommandLine
8
+ class ProvisionUser
9
+ include Socialcast::CommandLine::Provisioner
10
+
11
+ def each_user_hash
12
+ each_ldap_connector do |connector|
13
+ connector.each_user_hash do |user_hash|
14
+ yield user_hash
15
+ end
16
+ end
17
+ end
18
+
19
+ def fetch_user_hash(identifier, options = {})
20
+ each_ldap_connector do |connector|
21
+ user_hash = connector.fetch_user_hash(identifier, options)
22
+ return user_hash if user_hash
23
+ end
24
+ nil
25
+ end
26
+
27
+ def provision
28
+ user_whitelist = Set.new
29
+ output_file = File.join Dir.pwd, @options[:output]
30
+ params = http_config.merge(:external_system => !!@options[:external_system])
31
+
32
+ Zlib::GzipWriter.open(output_file) do |gz|
33
+ xml = Builder::XmlMarkup.new(:target => gz, :indent => 1)
34
+ xml.instruct!
35
+ xml.export do |export|
36
+ export.users(:type => "array") do |users|
37
+ each_user_hash do |user_hash|
38
+ users << user_hash.to_xml(:skip_instruct => true, :root => 'user')
39
+ user_whitelist << [user_hash['contact_info']['email'], user_hash['unique_identifier'], user_hash['employee_number']]
40
+ end
41
+ end # users
42
+ end # export
43
+ end # gzip
44
+
45
+ if user_whitelist.empty? && !@options[:force]
46
+ raise ProvisionError.new "Skipping upload to Socialcast since no users were found"
47
+ else
48
+ puts "Uploading dataset to Socialcast..."
49
+ resource = Socialcast::CommandLine.resource_for_path '/api/users/provision', params
50
+ begin
51
+ File.open(output_file, 'r') do |file|
52
+ request_params = {:file => file}
53
+ request_params[:skip_emails] = 'true' if (@ldap_config['options']["skip_emails"] || @options[:skip_emails])
54
+ request_params[:test] = 'true' if (@ldap_config['options']["test"] || @options[:test])
55
+ resource.post request_params, :accept => :json
56
+ end
57
+ rescue RestClient::Unauthorized => e
58
+ raise ProvisionError.new "Authenticated user either does not have administration privileges or the community is not configured to allow provisioning. Please contact Socialcast support to if you need help." if e.http_code == 401
59
+ end
60
+ puts "Finished"
61
+ end
62
+ File.delete(output_file) if (@ldap_config['options']['delete_users_file'] || @options[:delete_users_file])
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,51 @@
1
+ module Socialcast
2
+ module CommandLine
3
+ module Provisioner
4
+ class ProvisionError < StandardError; end
5
+
6
+ DEFAULT_OUTPUT_FILE = 'users.xml.gz'
7
+
8
+ def initialize(ldap_config, options = {})
9
+ @ldap_config = ldap_config.dup
10
+ @options = options.dup
11
+
12
+ @options[:output] ||= DEFAULT_OUTPUT_FILE
13
+ end
14
+
15
+ private
16
+
17
+ def http_config
18
+ @http_config ||= @ldap_config.fetch 'http', {}
19
+ end
20
+
21
+ def ldap_connector(connection_name)
22
+ @connectors ||= {}
23
+
24
+ unless @connectors[connection_name]
25
+ @connectors[connection_name] = Socialcast::CommandLine::LDAPConnector.new(connection_name, @ldap_config)
26
+ end
27
+
28
+ @connectors[connection_name]
29
+ end
30
+
31
+ def each_ldap_connector
32
+ @ldap_config['connections'].keys.each do |connection_name|
33
+ yield ldap_connector(connection_name)
34
+ end
35
+ end
36
+
37
+ def each_ldap_entry(&block)
38
+ count = 0
39
+
40
+ each_ldap_connector do |connector|
41
+ connector.each_ldap_entry do |entry|
42
+ yield entry, connector.connection_name
43
+ count += 1
44
+ puts "Scanned #{count} users" if ((count % 100) == 0)
45
+ end
46
+ end
47
+ puts "Finished scanning #{count} users"
48
+ end
49
+ end
50
+ end
51
+ end
@@ -1,5 +1,5 @@
1
1
  module Socialcast
2
2
  module CommandLine
3
- VERSION = "1.3.7"
3
+ VERSION = "1.3.8"
4
4
  end
5
5
  end
@@ -3,13 +3,14 @@ require 'spec_helper'
3
3
  describe Socialcast::CommandLine::Authenticate do
4
4
  let(:options) { { :domain => "test.socialcast.local" } }
5
5
  let(:params) { { } }
6
- subject { Socialcast::CommandLine::Authenticate.new(authenticate_type, options, params) }
6
+ subject(:authenticate) { Socialcast::CommandLine::Authenticate.new(authenticate_type, options, params) }
7
7
 
8
8
  describe '#request' do
9
9
  before do
10
10
  RestClient::Resource.should_receive(:new).with(url, {}).and_call_original
11
11
  RestClient::Resource.any_instance.should_receive(:post).with(subject.params, :accept => :json)
12
- subject.request
12
+ authenticate.should_receive(:set_default_credentials).and_return(true)
13
+ authenticate.request
13
14
  end
14
15
  context 'for a regular user' do
15
16
  let(:authenticate_type) { :user }
@@ -162,7 +162,7 @@ describe Socialcast::CommandLine::CLI do
162
162
  {
163
163
  'id' => 7,
164
164
  'avatars' => {
165
- 'is_system_default' => system_default_photo
165
+ 'is_community_default' => system_default_photo
166
166
  }
167
167
  }
168
168
  ]
@@ -199,7 +199,7 @@ describe Socialcast::CommandLine::CLI do
199
199
  {
200
200
  'id' => 7,
201
201
  'avatars' => {
202
- 'is_system_default' => system_default_photo
202
+ 'is_community_default' => system_default_photo
203
203
  }
204
204
  }
205
205
  ]
@@ -232,7 +232,7 @@ describe Socialcast::CommandLine::CLI do
232
232
  {
233
233
  'id' => 7,
234
234
  'avatars' => {
235
- 'is_system_default' => system_default_photo
235
+ 'is_community_default' => system_default_photo
236
236
  }
237
237
  }
238
238
  ]
@@ -299,7 +299,7 @@ describe Socialcast::CommandLine::CLI do
299
299
  rest_client_resource = double(:rest_client_resource)
300
300
  rest_client_resource.stub(:post).and_raise(RestClient::Unauthorized.new(double('Unauthorized HTTP Response', :code => '401', :body => 'Unauthorized HTTP Response')))
301
301
  Socialcast::CommandLine.stub(:resource_for_path).and_return(rest_client_resource)
302
- Kernel.should_receive(:abort).with("Authenticated user either does not have administration privileges or the community is not configured to allow provisioning. Please contact Socialcast support to if you need help.\nResponse from server: Unauthorized HTTP Response").once
302
+ Kernel.should_receive(:abort).with("Authenticated user either does not have administration privileges or the community is not configured to allow provisioning. Please contact Socialcast support to if you need help.").once
303
303
 
304
304
  Socialcast::CommandLine::CLI.start ['provision', '-f']
305
305
  end
@@ -0,0 +1,117 @@
1
+ require 'spec_helper'
2
+
3
+ describe Socialcast::CommandLine::ProvisionPhoto do
4
+ let(:ldap_with_profile_photo) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'ldap_with_profile_photo.yml')) }
5
+
6
+ context '#sync' do
7
+ let(:options) { {} }
8
+ subject(:sync_photos) { Socialcast::CommandLine::ProvisionPhoto.new(ldap_with_profile_photo, options).sync }
9
+ let(:user_search_resource) { double(:user_search_resource) }
10
+ let(:user_submit_resource) { double(:user_submit_resource) }
11
+ let(:is_community_default) { true }
12
+ let(:data_fingerprint) { '5d41402abc4b2a76b9719d911017c592' }
13
+ let(:search_api_response) do
14
+ {
15
+ 'users' => [
16
+ {
17
+ 'id' => 7,
18
+ 'avatars' => {
19
+ 'is_community_default' => is_community_default,
20
+ 'data_fingerprint' => data_fingerprint
21
+ }
22
+ }
23
+ ]
24
+ }
25
+ end
26
+ before do
27
+ entry = create_entry :mail => 'user@example.com', :givenName => 'first name', :sn => 'last name', :jpegPhoto => photo_data
28
+ Net::LDAP.any_instance.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'mail', 'jpegPhoto', 'memberof'])).and_yield(entry)
29
+ Socialcast::CommandLine.stub(:resource_for_path).with('/api/users/search', anything).and_return(user_search_resource)
30
+ end
31
+
32
+ context 'for when it does successfully post the photo' do
33
+ before do
34
+ user_search_resource.should_receive(:get).and_return(search_api_response.to_json)
35
+ user_resource = double(:user_resource)
36
+ user_resource.should_receive(:put) do |data|
37
+ uploaded_data = data[:user][:profile_photo][:data]
38
+ uploaded_data.path.should =~ /\.png\Z/
39
+ end
40
+ Socialcast::CommandLine.stub(:resource_for_path).with('/api/users/7', anything).and_return(user_resource)
41
+ end
42
+ context 'for a binary file' do
43
+ let(:photo_data) { "\x89PNGabc" }
44
+ before do
45
+ RestClient.should_not_receive(:get)
46
+ sync_photos
47
+ end
48
+ it 'uses the original binary to upload the photo' do end
49
+ end
50
+ context 'for an image file' do
51
+ let(:photo_data) { "http://socialcast.com/someimage.png" }
52
+ before do
53
+ RestClient.should_receive(:get).with(photo_data).and_return("\x89PNGabc")
54
+ sync_photos
55
+ end
56
+ it 'downloads the image form the web to upload the photo' do end
57
+ end
58
+ end
59
+
60
+ context 'for when it does not successfully post the photo' do
61
+ context 'for an image file' do
62
+ let(:photo_data) { "http://socialcast.com/someimage.png" }
63
+ before do
64
+ user_search_resource.should_receive(:get).and_return(search_api_response.to_json)
65
+ RestClient.should_receive(:get).with(photo_data).and_raise(RestClient::ResourceNotFound)
66
+ sync_photos
67
+ end
68
+ it 'tries to download the image from the web and rescues 404' do end
69
+ end
70
+ end
71
+
72
+ context 'when their is already a photo set' do
73
+ let(:is_community_default) { false }
74
+ let(:photo_data) { "\x89PNGabc" }
75
+ before { user_search_resource.should_receive(:get).and_return(search_api_response.to_json) }
76
+ context 'for a regular sync' do
77
+ before do
78
+ sync_photos
79
+ Socialcast::CommandLine.should_not_receive(:resource_for_path)
80
+ end
81
+ it 'does not post the new photo' do end
82
+ end
83
+ context 'when they do a force sync' do
84
+ let(:options) { { :force_sync => true } }
85
+ before do
86
+ Socialcast::CommandLine.should_receive(:resource_for_path).with('/api/users/7', {}).and_return(user_submit_resource)
87
+ user_submit_resource.should_receive(:put).and_return(true)
88
+ sync_photos
89
+ end
90
+ it 'submits the photo anyways' do end
91
+ end
92
+ end
93
+ end
94
+
95
+
96
+ context '.binary_to_content_type' do
97
+ subject { Socialcast::CommandLine::ProvisionPhoto.new(ldap_with_profile_photo, {}).send(:binary_to_content_type, binary_photo_data) }
98
+ let(:file_dir) { File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'test_images') }
99
+ let(:binary_photo_data) { File.open(File.join(file_dir, image_name), 'rb') { |file| file.read } }
100
+ context 'with a jpg' do
101
+ let(:image_name) { 'test-jpg-image.jpg' }
102
+ it { should == 'jpg' }
103
+ end
104
+ context 'with a gif' do
105
+ let(:image_name) { 'test-gif-image.gif' }
106
+ it { should == 'gif' }
107
+ end
108
+ context 'with a png' do
109
+ let(:image_name) { 'test-png-image.png' }
110
+ it { should == 'png' }
111
+ end
112
+ context 'with a tiff' do
113
+ let(:image_name) { 'test-tiff-image.tiff' }
114
+ it { should be_nil }
115
+ end
116
+ end
117
+ end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Socialcast::CommandLine::Provision do
3
+ describe Socialcast::CommandLine::ProvisionUser do
4
4
  let!(:credentials) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'credentials.yml')) }
5
5
  let!(:ldap_default_config) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'ldap.yml')) }
6
6
  let!(:ldap_blank_basedn_config) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'ldap_with_blank_basedn.yml')) }
@@ -13,17 +13,8 @@ describe Socialcast::CommandLine::Provision do
13
13
  let!(:ldap_with_manager_attribute_config) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'ldap_with_manager_attribute.yml')) }
14
14
  let!(:ldap_with_roles_without_account_type_config) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'ldap_with_roles_without_account_type.yml')) }
15
15
  let!(:ldap_with_unique_identifier_config) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'ldap_with_unique_identifier.yml')) }
16
- let!(:ldap_with_profile_photo) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'ldap_with_profile_photo.yml')) }
17
16
  let!(:ldap_without_account_type_or_roles_config) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'ldap_without_account_type_or_roles.yml')) }
18
17
 
19
- def create_entry(entry_attributes)
20
- Net::LDAP::Entry.new("dc=example,dc=com").tap do |e|
21
- entry_attributes.each_pair do |attr, value|
22
- e[attr] = value
23
- end
24
- end
25
- end
26
-
27
18
  describe "#provision" do
28
19
  let(:result) { '' }
29
20
 
@@ -39,7 +30,7 @@ describe Socialcast::CommandLine::Provision do
39
30
  Net::LDAP.any_instance.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'mail', 'isMemberOf'])).and_yield(entry)
40
31
  RestClient::Resource.any_instance.should_receive(:post).once.with(hash_including(:file => result), { :accept => :json })
41
32
 
42
- Socialcast::CommandLine::Provision.new(ldap_default_config, {}).provision
33
+ Socialcast::CommandLine::ProvisionUser.new(ldap_default_config, {}).provision
43
34
  end
44
35
  it "puts the user in the output file" do
45
36
  result.should =~ /user@example.com/
@@ -51,7 +42,7 @@ describe Socialcast::CommandLine::Provision do
51
42
  Net::LDAP.any_instance.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'uid', 'isMemberOf'])).and_yield(entry)
52
43
  RestClient::Resource.any_instance.should_receive(:post).once.with(hash_including(:file => result), { :accept => :json })
53
44
 
54
- Socialcast::CommandLine::Provision.new(ldap_with_unique_identifier_config, {}).provision
45
+ Socialcast::CommandLine::ProvisionUser.new(ldap_with_unique_identifier_config, {}).provision
55
46
  end
56
47
  it "puts the user in the output file" do
57
48
  result.should =~ /userID/
@@ -65,8 +56,8 @@ describe Socialcast::CommandLine::Provision do
65
56
  end
66
57
  it "does not put the user in the output file" do
67
58
  expect do
68
- Socialcast::CommandLine::Provision.new(ldap_default_config, {}).provision
69
- end.to raise_error(Socialcast::CommandLine::Provision::ProvisionError, "Skipping upload to Socialcast since no users were found")
59
+ Socialcast::CommandLine::ProvisionUser.new(ldap_default_config, {}).provision
60
+ end.to raise_error(Socialcast::CommandLine::ProvisionUser::ProvisionError, "Skipping upload to Socialcast since no users were found")
70
61
  end
71
62
  end
72
63
  context "attribute mappings" do
@@ -99,7 +90,7 @@ describe Socialcast::CommandLine::Provision do
99
90
  entry = create_entry :mail => 'user@example.com', :givenName => 'first name', :sn => 'last name'
100
91
  Net::LDAP.any_instance.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'mail', 'isMemberOf'])).and_yield(entry)
101
92
 
102
- Socialcast::CommandLine::Provision.new(ldap_default_config, {}).provision
93
+ Socialcast::CommandLine::ProvisionUser.new(ldap_default_config, {}).provision
103
94
  end
104
95
  let(:expected_attribute_xml) do
105
96
  %Q[<first-name>first name</first-name>
@@ -114,7 +105,7 @@ describe Socialcast::CommandLine::Provision do
114
105
 
115
106
  context "with mappings at the connection level" do
116
107
  before do
117
- provision_instance = Socialcast::CommandLine::Provision.new(ldap_multiple_connection_mapping_config, {})
108
+ provision_instance = Socialcast::CommandLine::ProvisionUser.new(ldap_multiple_connection_mapping_config, {})
118
109
 
119
110
  ldap_instance1 = double(Net::LDAP, :encryption => nil, :auth => nil)
120
111
  Net::LDAP.should_receive(:new).once.ordered.and_return(ldap_instance1)
@@ -148,7 +139,7 @@ describe Socialcast::CommandLine::Provision do
148
139
  entry = create_entry :mail => 'user@example.com', :custom_ldap1 => 'custom value 1', :custom_ldap2 => 'custom value 2'
149
140
  Net::LDAP.any_instance.should_receive(:search).once.with(hash_including(:attributes => ['custom_ldap1', 'custom_ldap2', 'mail', 'isMemberOf'])).and_yield(entry)
150
141
 
151
- Socialcast::CommandLine::Provision.new(ldap_with_custom_attributes_config, {}).provision
142
+ Socialcast::CommandLine::ProvisionUser.new(ldap_with_custom_attributes_config, {}).provision
152
143
  end
153
144
  let(:expected_attribute_xml) do
154
145
  %Q[<contact-info>
@@ -172,7 +163,7 @@ describe Socialcast::CommandLine::Provision do
172
163
 
173
164
  context "with manager" do
174
165
  before do
175
- provision_instance = Socialcast::CommandLine::Provision.new(ldap_with_manager_attribute_config, {})
166
+ provision_instance = Socialcast::CommandLine::ProvisionUser.new(ldap_with_manager_attribute_config, {})
176
167
 
177
168
  ldap_instance = double(Net::LDAP, :encryption => nil, :auth => nil)
178
169
  Net::LDAP.should_receive(:new).once.and_return(ldap_instance)
@@ -205,7 +196,7 @@ describe Socialcast::CommandLine::Provision do
205
196
  entry = create_entry :test_ldap_attribute_mapping => 'user@example.com'
206
197
  Net::LDAP.any_instance.should_receive(:search).once.with(hash_including(:attributes => ['test_ldap_attribute_mapping', 'isMemberOf'])).and_yield(entry)
207
198
 
208
- Socialcast::CommandLine::Provision.new(ldap_with_class_ldap_attribute_config, {}).provision
199
+ Socialcast::CommandLine::ProvisionUser.new(ldap_with_class_ldap_attribute_config, {}).provision
209
200
  end
210
201
  after do
211
202
  Object.send(:remove_const, :TestLdapAttributeMapping)
@@ -255,7 +246,7 @@ describe Socialcast::CommandLine::Provision do
255
246
  let(:ldap_groups) { ["cn=External,dc=example,dc=com", "cn=SbiAdmins,dc=example,dc=com", "cn=TownHallAdmins,dc=example,dc=com"] }
256
247
  before do
257
248
  Net::LDAP.any_instance.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'mail', ldap_group_attribute])).and_yield(entry)
258
- Socialcast::CommandLine::Provision.new(ldap_default_config, {}).provision
249
+ Socialcast::CommandLine::ProvisionUser.new(ldap_default_config, {}).provision
259
250
  end
260
251
  let(:expected_permission_xml) do
261
252
  %Q[<account-type>external</account-type>]
@@ -267,7 +258,7 @@ describe Socialcast::CommandLine::Provision do
267
258
  let(:ldap_groups) { ["cn=SbiAdmins,dc=example,dc=com", "cn=TownHallAdmins,dc=example,dc=com"] }
268
259
  before do
269
260
  Net::LDAP.any_instance.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'mail', ldap_group_attribute])).and_yield(entry)
270
- Socialcast::CommandLine::Provision.new(ldap_default_config, {}).provision
261
+ Socialcast::CommandLine::ProvisionUser.new(ldap_default_config, {}).provision
271
262
  end
272
263
  let(:expected_permission_xml) do
273
264
  %Q[<account-type>member</account-type>
@@ -283,7 +274,7 @@ describe Socialcast::CommandLine::Provision do
283
274
  let(:ldap_groups) { ["cn=SbiAdmins,dc=example,dc=com", "cn=TownHallAdmins,dc=example,dc=com"] }
284
275
  before do
285
276
  Net::LDAP.any_instance.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'mail', ldap_group_attribute])).and_yield(entry)
286
- Socialcast::CommandLine::Provision.new(ldap_with_account_type_without_roles_config, {}).provision
277
+ Socialcast::CommandLine::ProvisionUser.new(ldap_with_account_type_without_roles_config, {}).provision
287
278
  end
288
279
  let(:expected_permission_xml) do
289
280
  %Q[<account-type>member</account-type>]
@@ -295,7 +286,7 @@ describe Socialcast::CommandLine::Provision do
295
286
  let(:ldap_groups) { ["cn=SbiAdmins,dc=example,dc=com", "cn=TownHallAdmins,dc=example,dc=com"] }
296
287
  before do
297
288
  Net::LDAP.any_instance.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'mail', ldap_group_attribute])).and_yield(entry)
298
- Socialcast::CommandLine::Provision.new(ldap_with_roles_without_account_type_config, {}).provision
289
+ Socialcast::CommandLine::ProvisionUser.new(ldap_with_roles_without_account_type_config, {}).provision
299
290
  end
300
291
  let(:expected_permission_xml) do
301
292
  %Q[<account-type>member</account-type>
@@ -311,7 +302,7 @@ describe Socialcast::CommandLine::Provision do
311
302
  let(:ldap_groups) { ["cn=SbiAdmins,dc=example,dc=com", "cn=TownHallAdmins,dc=example,dc=com"] }
312
303
  before do
313
304
  Net::LDAP.any_instance.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'mail', ldap_group_attribute])).and_yield(entry)
314
- Socialcast::CommandLine::Provision.new(ldap_without_account_type_or_roles_config, {}).provision
305
+ Socialcast::CommandLine::ProvisionUser.new(ldap_without_account_type_or_roles_config, {}).provision
315
306
  end
316
307
  let(:expected_permission_xml) do
317
308
  %Q[<account-type>member</account-type>]
@@ -323,7 +314,7 @@ describe Socialcast::CommandLine::Provision do
323
314
  let(:ldap_group_attribute) { 'memberOf' }
324
315
  let(:ldap_groups) { }
325
316
  before do
326
- provision_instance = Socialcast::CommandLine::Provision.new(ldap_multiple_connection_permission_mapping_config, {})
317
+ provision_instance = Socialcast::CommandLine::ProvisionUser.new(ldap_multiple_connection_permission_mapping_config, {})
327
318
 
328
319
  ldap_instance1 = double(Net::LDAP, :encryption => nil, :auth => nil)
329
320
  Net::LDAP.should_receive(:new).once.ordered.and_return(ldap_instance1)
@@ -355,7 +346,7 @@ describe Socialcast::CommandLine::Provision do
355
346
  before do
356
347
  RestClient::Resource.any_instance.should_receive(:post).once.with(hash_including(:file => result), { :accept => :json })
357
348
 
358
- provision_instance = Socialcast::CommandLine::Provision.new(ldap_blank_basedn_config, {})
349
+ provision_instance = Socialcast::CommandLine::ProvisionUser.new(ldap_blank_basedn_config, {})
359
350
 
360
351
  root_entry = create_entry(:namingcontexts => ['dc=foo,dc=com', 'dc=bar,dc=com'])
361
352
  ldap_instance = double(Net::LDAP, :encryption => nil, :auth => nil)
@@ -375,7 +366,7 @@ describe Socialcast::CommandLine::Provision do
375
366
  end
376
367
 
377
368
  describe "#each_user_hash" do
378
- let(:provision_instance) { Socialcast::CommandLine::Provision.new(ldap_default_config) }
369
+ let(:provision_instance) { Socialcast::CommandLine::ProvisionUser.new(ldap_default_config) }
379
370
  before do
380
371
  entry = create_entry :mail => 'user@example.com', :givenName => 'first name', :sn => 'last name'
381
372
  Net::LDAP.any_instance.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'mail', 'isMemberOf'])).and_yield(entry)
@@ -398,7 +389,7 @@ describe Socialcast::CommandLine::Provision do
398
389
 
399
390
  describe "#fetch_user_hash" do
400
391
  context "when the first connector returns the entry" do
401
- let(:provision_instance) { Socialcast::CommandLine::Provision.new(ldap_multiple_connection_mapping_config, {}) }
392
+ let(:provision_instance) { Socialcast::CommandLine::ProvisionUser.new(ldap_multiple_connection_mapping_config, {}) }
402
393
  let(:entry) { create_entry :mailCon => 'user@example.com' }
403
394
  before do
404
395
  filter = Net::LDAP::Filter.construct('(&(mail=*)(mailCon=user@example.com))')
@@ -418,7 +409,7 @@ describe Socialcast::CommandLine::Provision do
418
409
  end
419
410
  end
420
411
  context "when another connector returns the entry" do
421
- let(:provision_instance) { Socialcast::CommandLine::Provision.new(ldap_multiple_connection_mapping_config, {}) }
412
+ let(:provision_instance) { Socialcast::CommandLine::ProvisionUser.new(ldap_multiple_connection_mapping_config, {}) }
422
413
  let(:entry) { create_entry :mailCon2 => 'user@example.com', :firstName => 'first name' }
423
414
  before do
424
415
  ldap_instance1 = double(Net::LDAP, :auth => nil)
@@ -448,7 +439,7 @@ describe Socialcast::CommandLine::Provision do
448
439
  end
449
440
  end
450
441
  context "when no connectors return the entry" do
451
- let(:provision_instance) { Socialcast::CommandLine::Provision.new(ldap_multiple_connection_mapping_config, {}) }
442
+ let(:provision_instance) { Socialcast::CommandLine::ProvisionUser.new(ldap_multiple_connection_mapping_config, {}) }
452
443
  before do
453
444
  ldap_instance1 = double(Net::LDAP, :auth => nil)
454
445
  Net::LDAP.should_receive(:new).once.ordered.and_return(ldap_instance1)
@@ -467,71 +458,4 @@ describe Socialcast::CommandLine::Provision do
467
458
  end
468
459
  end
469
460
  end
470
-
471
- describe '#sync_photos' do
472
- let(:user_search_resource) { double(:user_search_resource) }
473
- let(:search_api_response) do
474
- {
475
- 'users' => [
476
- {
477
- 'id' => 7,
478
- 'avatars' => {
479
- 'is_system_default' => true
480
- }
481
- }
482
- ]
483
- }
484
- end
485
- before do
486
- entry = create_entry :mail => 'user@example.com', :givenName => 'first name', :sn => 'last name', :jpegPhoto => photo_data
487
- Net::LDAP.any_instance.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'mail', 'jpegPhoto', 'memberof'])).and_yield(entry)
488
-
489
- Socialcast::CommandLine.stub(:resource_for_path).with('/api/users/search', anything).and_return(user_search_resource)
490
- end
491
- let(:sync_photos) { Socialcast::CommandLine::Provision.new(ldap_with_profile_photo, {}).sync_photos }
492
-
493
- context 'for when it does successfully post the photo' do
494
- before do
495
- user_search_resource.should_receive(:get).and_return(search_api_response.to_json)
496
- user_resource = double(:user_resource)
497
- user_resource.should_receive(:put) do |data|
498
- uploaded_data = data[:user][:profile_photo][:data]
499
- uploaded_data.path.should =~ /\.png\Z/
500
- end
501
- Socialcast::CommandLine.stub(:resource_for_path).with('/api/users/7', anything).and_return(user_resource)
502
- end
503
- context 'for a binary file' do
504
- let(:photo_data) { "\x89PNGabc" }
505
- before do
506
- RestClient.should_not_receive(:get)
507
- sync_photos
508
- end
509
- it 'uses the original binary to upload the photo' do end
510
- end
511
- context 'for an image file' do
512
- let(:photo_data) { "http://socialcast.com/someimage.png" }
513
- context 'when it successfully downloads' do
514
- before do
515
- RestClient.should_receive(:get).with(photo_data).and_return("\x89PNGabc")
516
- sync_photos
517
- end
518
- it 'downloads the image form the web to upload the photo' do end
519
- end
520
- end
521
- end
522
-
523
- context 'for when it does not successfully post the photo' do
524
- context 'for an image file' do
525
- let(:photo_data) { "http://socialcast.com/someimage.png" }
526
- before do
527
- user_search_resource.should_not_receive(:get)
528
- RestClient.should_receive(:get).with(photo_data).and_raise(RestClient::ResourceNotFound)
529
- sync_photos
530
- end
531
- it 'tries to download the image from the web and rescues 404' do end
532
- end
533
- end
534
-
535
- end
536
-
537
461
  end
data/spec/spec_helper.rb CHANGED
@@ -10,6 +10,11 @@ require_relative '../lib/socialcast'
10
10
  RSpec.configure do |config|
11
11
  config.mock_with :rspec
12
12
 
13
+ config.before do
14
+ stubbed_credentials = File.join(File.dirname(__FILE__), '..', 'fixtures')
15
+ Socialcast::CommandLine.stub(:config_dir).and_return(stubbed_credentials)
16
+ end
17
+
13
18
  def capture_with_status(stream)
14
19
  exit_status = 0
15
20
  begin
@@ -27,6 +32,14 @@ RSpec.configure do |config|
27
32
  return result, exit_status
28
33
  end
29
34
 
35
+ def create_entry(entry_attributes)
36
+ Net::LDAP::Entry.new("dc=example,dc=com").tap do |e|
37
+ entry_attributes.each_pair do |attr, value|
38
+ e[attr] = value
39
+ end
40
+ end
41
+ end
42
+
30
43
  def remove_directories(*names)
31
44
  project_dir = Pathname.new(Dir.pwd)
32
45
  names.each do |name|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: socialcast
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.7
4
+ version: 1.3.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Sonnek
@@ -221,7 +221,9 @@ files:
221
221
  - lib/socialcast/command_line/cli.rb
222
222
  - lib/socialcast/command_line/ldap_connector.rb
223
223
  - lib/socialcast/command_line/message.rb
224
- - lib/socialcast/command_line/provision.rb
224
+ - lib/socialcast/command_line/provision_photo.rb
225
+ - lib/socialcast/command_line/provision_user.rb
226
+ - lib/socialcast/command_line/provisioner.rb
225
227
  - lib/socialcast/command_line/version.rb
226
228
  - socialcast.gemspec
227
229
  - spec/fixtures/credentials.yml
@@ -245,10 +247,15 @@ files:
245
247
  - spec/fixtures/ldap_with_unique_identifier.yml
246
248
  - spec/fixtures/ldap_without_account_type_or_roles.yml
247
249
  - spec/fixtures/ldap_without_permission_mappings.yml
250
+ - spec/fixtures/test_images/test-gif-image.gif
251
+ - spec/fixtures/test_images/test-jpg-image.jpg
252
+ - spec/fixtures/test_images/test-png-image.png
253
+ - spec/fixtures/test_images/test-tiff-image.tiff
248
254
  - spec/socialcast/command_line/authenticate_spec.rb
249
255
  - spec/socialcast/command_line/cli_spec.rb
250
256
  - spec/socialcast/command_line/ldap_connector_spec.rb
251
- - spec/socialcast/command_line/provision_spec.rb
257
+ - spec/socialcast/command_line/provision_photo_spec.rb
258
+ - spec/socialcast/command_line/provision_user_spec.rb
252
259
  - spec/socialcast/socialcast_spec.rb
253
260
  - spec/spec_helper.rb
254
261
  homepage: http://github.com/socialcast/socialcast-command-line
@@ -297,9 +304,14 @@ test_files:
297
304
  - spec/fixtures/ldap_with_unique_identifier.yml
298
305
  - spec/fixtures/ldap_without_account_type_or_roles.yml
299
306
  - spec/fixtures/ldap_without_permission_mappings.yml
307
+ - spec/fixtures/test_images/test-gif-image.gif
308
+ - spec/fixtures/test_images/test-jpg-image.jpg
309
+ - spec/fixtures/test_images/test-png-image.png
310
+ - spec/fixtures/test_images/test-tiff-image.tiff
300
311
  - spec/socialcast/command_line/authenticate_spec.rb
301
312
  - spec/socialcast/command_line/cli_spec.rb
302
313
  - spec/socialcast/command_line/ldap_connector_spec.rb
303
- - spec/socialcast/command_line/provision_spec.rb
314
+ - spec/socialcast/command_line/provision_photo_spec.rb
315
+ - spec/socialcast/command_line/provision_user_spec.rb
304
316
  - spec/socialcast/socialcast_spec.rb
305
317
  - spec/spec_helper.rb
@@ -1,166 +0,0 @@
1
- require 'zlib'
2
- require 'builder'
3
- require 'set'
4
- require 'fileutils'
5
-
6
- module Socialcast
7
- module CommandLine
8
- class Provision
9
- DEFAULT_OUTPUT_FILE = 'users.xml.gz'
10
-
11
- class ProvisionError < StandardError; end
12
-
13
- def initialize(ldap_config, options = {})
14
- @ldap_config = ldap_config.dup
15
- @options = options.dup
16
-
17
- @options[:output] ||= DEFAULT_OUTPUT_FILE
18
- end
19
-
20
- def each_user_hash
21
- each_ldap_connector do |connector|
22
- connector.each_user_hash do |user_hash|
23
- yield user_hash
24
- end
25
- end
26
- end
27
-
28
- def fetch_user_hash(identifier, options = {})
29
- each_ldap_connector do |connector|
30
- user_hash = connector.fetch_user_hash(identifier, options)
31
- return user_hash if user_hash
32
- end
33
- nil
34
- end
35
-
36
- def provision
37
- params = @ldap_config.fetch('http', {}).merge(:external_system => !!@options[:external_system])
38
-
39
- user_whitelist = Set.new
40
- output_file = File.join Dir.pwd, @options[:output]
41
-
42
- Zlib::GzipWriter.open(output_file) do |gz|
43
- xml = Builder::XmlMarkup.new(:target => gz, :indent => 1)
44
- xml.instruct!
45
- xml.export do |export|
46
- export.users(:type => "array") do |users|
47
- each_user_hash do |user_hash|
48
- users << user_hash.to_xml(:skip_instruct => true, :root => 'user')
49
- user_whitelist << [user_hash['contact_info']['email'], user_hash['unique_identifier'], user_hash['employee_number']]
50
- end
51
- end # users
52
- end # export
53
- end # gzip
54
-
55
- if user_whitelist.empty? && !@options[:force]
56
- raise ProvisionError.new "Skipping upload to Socialcast since no users were found"
57
- else
58
- puts "Uploading dataset to Socialcast..."
59
- resource = Socialcast::CommandLine.resource_for_path '/api/users/provision', params
60
- begin
61
- File.open(output_file, 'r') do |file|
62
- request_params = {:file => file}
63
- request_params[:skip_emails] = 'true' if (@ldap_config['options']["skip_emails"] || @options[:skip_emails])
64
- request_params[:test] = 'true' if (@ldap_config['options']["test"] || @options[:test])
65
- resource.post request_params, :accept => :json
66
- end
67
- rescue RestClient::Unauthorized => e
68
- error_message = @options[:external_system] ? "External Provisioning System was not found." : "Authenticated user either does not have administration privileges or the community is not configured to allow provisioning."
69
- contact_message = "Please contact Socialcast support to if you need help."
70
- raise ProvisionError.new "#{error_message} #{contact_message}\nResponse from server: #{e.response.body}" if e.http_code == 401
71
- end
72
- puts "Finished"
73
- end
74
- File.delete(output_file) if (@ldap_config['options']['delete_users_file'] || @options[:delete_users_file])
75
- end
76
-
77
- def sync_photos
78
- http_config = @ldap_config.fetch 'http', {}
79
-
80
- each_ldap_connector do |connector|
81
- connector.attribute_mappings.fetch('profile_photo')
82
- end
83
-
84
- search_users_resource = Socialcast::CommandLine.resource_for_path '/api/users/search', http_config
85
-
86
- each_ldap_connector do |connector|
87
- each_ldap_entry do |entry|
88
- attr_mappings = connector.attribute_mappings
89
- email = connector.grab(entry, attr_mappings['email'])
90
- if profile_photo_data = connector.grab(entry, attr_mappings['profile_photo'])
91
- if profile_photo_data.start_with?('http')
92
- begin
93
- profile_photo_data = RestClient.get(profile_photo_data)
94
- rescue => e
95
- puts "Unable to download photo #{profile_photo_data} for #{email}"
96
- puts e.response
97
- next
98
- end
99
- end
100
- profile_photo_data = profile_photo_data.force_encoding('binary')
101
-
102
- user_search_response = search_users_resource.get(:params => { :q => email, :per_page => 1 }, :accept => :json)
103
- user_info = JSON.parse(user_search_response)['users'].first
104
- if user_info && user_info['avatars'] && user_info['avatars']['is_system_default']
105
- puts "Uploading photo for #{email}"
106
-
107
- user_resource = Socialcast::CommandLine.resource_for_path "/api/users/#{user_info['id']}", http_config
108
- content_type = case profile_photo_data
109
- when Regexp.new("\AGIF8", nil, 'n')
110
- 'gif'
111
- when Regexp.new('\A\x89PNG', nil, 'n')
112
- 'png'
113
- when Regexp.new("\A\xff\xd8\xff\xe0\x00\x10JFIF", nil, 'n'), Regexp.new("\A\xff\xd8\xff\xe1(.*){2}Exif", nil, 'n')
114
- 'jpg'
115
- else
116
- puts "Skipping photo for #{email}: unknown image format (supports .gif, .png, .jpg)"
117
- next
118
- end
119
-
120
- tempfile = Tempfile.new(["photo_upload", ".#{content_type}"])
121
- tempfile.write(profile_photo_data)
122
- tempfile.rewind
123
- begin
124
- user_resource.put({ :user => { :profile_photo => { :data => tempfile } } })
125
- ensure
126
- tempfile.unlink
127
- end
128
- end
129
- end
130
- end
131
- end
132
- end
133
-
134
- private
135
-
136
- def ldap_connector(connection_name)
137
- @connectors ||= {}
138
-
139
- unless @connectors[connection_name]
140
- @connectors[connection_name] = Socialcast::CommandLine::LDAPConnector.new(connection_name, @ldap_config)
141
- end
142
-
143
- @connectors[connection_name]
144
- end
145
-
146
- def each_ldap_connector
147
- @ldap_config['connections'].keys.each do |connection_name|
148
- yield ldap_connector(connection_name)
149
- end
150
- end
151
-
152
- def each_ldap_entry(&block)
153
- count = 0
154
-
155
- each_ldap_connector do |connector|
156
- connector.each_ldap_entry do |entry|
157
- yield entry, connector.connection_name
158
- count += 1
159
- puts "Scanned #{count} users" if ((count % 100) == 0)
160
- end
161
- end
162
- puts "Finished scanning #{count} users"
163
- end
164
- end
165
- end
166
- end