socialcast 1.1.5 → 1.1.6
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.
- data/lib/socialcast/cli.rb +63 -13
- data/lib/socialcast/net_ldap_ext.rb +1 -1
- data/lib/socialcast/version.rb +1 -1
- data/socialcast.gemspec +1 -1
- data/spec/cli_spec.rb +40 -9
- metadata +21 -21
data/lib/socialcast/cli.rb
CHANGED
|
@@ -11,6 +11,7 @@ require File.join(File.dirname(__FILE__), 'net_ldap_ext')
|
|
|
11
11
|
require 'zlib'
|
|
12
12
|
require 'logger'
|
|
13
13
|
require 'builder'
|
|
14
|
+
require 'set'
|
|
14
15
|
require 'net/ldap'
|
|
15
16
|
|
|
16
17
|
# uncomment to debug HTTP traffic
|
|
@@ -97,6 +98,7 @@ module Socialcast
|
|
|
97
98
|
method_option :test, :type => :boolean
|
|
98
99
|
method_option :skip_emails, :type => :boolean
|
|
99
100
|
method_option :force, :type => :boolean, :aliases => '-f', :default => false
|
|
101
|
+
method_option :sanity_check, :type => :boolean, :default => false
|
|
100
102
|
def provision
|
|
101
103
|
config_file = File.expand_path options[:config]
|
|
102
104
|
|
|
@@ -106,11 +108,12 @@ module Socialcast
|
|
|
106
108
|
end
|
|
107
109
|
return
|
|
108
110
|
end
|
|
109
|
-
|
|
111
|
+
|
|
110
112
|
fail "Unable to load configuration file: #{config_file}" unless File.exists?(config_file)
|
|
111
113
|
say "Using configuration file: #{config_file}"
|
|
112
|
-
|
|
114
|
+
|
|
113
115
|
config = load_configuration config_file
|
|
116
|
+
http_config = config.fetch('http', {})
|
|
114
117
|
|
|
115
118
|
required_mappings = %w{email first_name last_name}
|
|
116
119
|
mappings = config.fetch 'mappings', {}
|
|
@@ -125,6 +128,8 @@ module Socialcast
|
|
|
125
128
|
attributes = mappings.values
|
|
126
129
|
attributes << membership_attribute
|
|
127
130
|
|
|
131
|
+
user_identifier_list = %w{email unique_identifier employee_number}
|
|
132
|
+
user_whitelist = Set.new
|
|
128
133
|
count = 0
|
|
129
134
|
output_file = File.join Dir.pwd, options[:output]
|
|
130
135
|
Zlib::GzipWriter.open(output_file) do |gz|
|
|
@@ -132,20 +137,14 @@ module Socialcast
|
|
|
132
137
|
xml.instruct!
|
|
133
138
|
xml.export do |export|
|
|
134
139
|
export.users(:type => "array") do |users|
|
|
135
|
-
config
|
|
136
|
-
say "Connecting to #{key} at #{[connection["host"], connection["port"]].join(':')}"
|
|
137
|
-
|
|
138
|
-
ldap = Net::LDAP.new :host => connection["host"], :port => connection["port"], :base => connection["basedn"]
|
|
139
|
-
ldap.encryption connection['encryption'].to_sym if connection['encryption']
|
|
140
|
-
ldap.auth connection["username"], connection["password"]
|
|
141
|
-
say "Searching base DN: #{connection["basedn"]} with filter: #{connection["filter"]}"
|
|
142
|
-
|
|
140
|
+
ldap_connections(config) do |key, connection, ldap|
|
|
143
141
|
ldap.search(:return_result => false, :filter => connection["filter"], :base => connection["basedn"], :attributes => attributes) do |entry|
|
|
144
142
|
next if entry.grab(mappings["email"]).blank? || (mappings.has_key?("unique_identifier") && entry.grab(mappings["unique_identifier"]).blank?)
|
|
145
143
|
|
|
146
144
|
users.user do |user|
|
|
147
145
|
entry.build_xml_from_mappings user, ldap, mappings, permission_mappings
|
|
148
146
|
end
|
|
147
|
+
user_whitelist << user_identifier_list.map { |identifier| entry.grab(mappings[identifier]) }
|
|
149
148
|
count += 1
|
|
150
149
|
say "Scanned #{count} users" if ((count % 100) == 0)
|
|
151
150
|
end # search
|
|
@@ -155,11 +154,27 @@ module Socialcast
|
|
|
155
154
|
end # gzip
|
|
156
155
|
say "Finished scanning #{count} users"
|
|
157
156
|
|
|
157
|
+
if options[:sanity_check]
|
|
158
|
+
say "Sanity checking users currently marked as needing to be terminated"
|
|
159
|
+
ldap_connections(config) do |key, connection, ldap|
|
|
160
|
+
(current_socialcast_users(http_config) - user_whitelist).each do |user_identifiers|
|
|
161
|
+
combined_filters = []
|
|
162
|
+
user_identifier_list.each_with_index do |identifier, index|
|
|
163
|
+
combined_filters << ((mappings[identifier].blank? || user_identifiers[index].nil?) ? nil : Net::LDAP::Filter.eq(mappings[identifier], user_identifiers[index]))
|
|
164
|
+
end
|
|
165
|
+
combined_filters.compact!
|
|
166
|
+
filter = ((combined_filters.size > 1) ? '(|%s)' : '%s') % combined_filters.join(' ')
|
|
167
|
+
filter = Net::LDAP::Filter.construct(filter) & Net::LDAP::Filter.construct(connection["filter"])
|
|
168
|
+
ldap_result = ldap.search(:return_result => true, :base => connection["basedn"], :filter => filter, :attributes => attributes)
|
|
169
|
+
abort("Found user marked for termination that should not be terminated: #{user_identifiers}") unless ldap_result.blank?
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
158
174
|
if count == 0 && !options[:force]
|
|
159
175
|
Kernel.abort("Skipping upload to Socialcast since no users were found")
|
|
160
176
|
else
|
|
161
177
|
say "Uploading dataset to Socialcast..."
|
|
162
|
-
http_config = config.fetch('http', {})
|
|
163
178
|
resource = Socialcast.resource_for_path '/api/users/provision', http_config
|
|
164
179
|
File.open(output_file, 'r') do |file|
|
|
165
180
|
request_params = {:file => file}
|
|
@@ -171,12 +186,47 @@ module Socialcast
|
|
|
171
186
|
end
|
|
172
187
|
File.delete(output_file) if (config['options']['delete_users_file'] || options[:delete_users_file])
|
|
173
188
|
end
|
|
174
|
-
|
|
189
|
+
|
|
175
190
|
no_tasks do
|
|
176
191
|
def load_configuration(path)
|
|
177
192
|
YAML.load_file path
|
|
178
193
|
end
|
|
194
|
+
def ldap_connections(config)
|
|
195
|
+
config["connections"].each_pair do |key, connection|
|
|
196
|
+
say "Connecting to #{key} at #{[connection["host"], connection["port"]].join(':')}"
|
|
197
|
+
ldap = create_ldap_instance(connection)
|
|
198
|
+
say "Searching base DN: #{connection["basedn"]} with filter: #{connection["filter"]}"
|
|
199
|
+
yield key, connection, ldap
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
def create_ldap_instance(connection)
|
|
203
|
+
ldap = Net::LDAP.new :host => connection["host"], :port => connection["port"], :base => connection["basedn"]
|
|
204
|
+
ldap.encryption connection['encryption'].to_sym if connection['encryption']
|
|
205
|
+
ldap.auth connection["username"], connection["password"]
|
|
206
|
+
ldap
|
|
207
|
+
end
|
|
208
|
+
def current_socialcast_users(http_config)
|
|
209
|
+
current_socialcast_list = Set.new
|
|
210
|
+
request_params = {:per_page => 500, :format => 'json'}
|
|
211
|
+
request_params[:page] = 1
|
|
212
|
+
resource = create_socialcast_user_index_request(http_config, request_params)
|
|
213
|
+
while true
|
|
214
|
+
response = resource.get
|
|
215
|
+
result = JSON.parse(response)
|
|
216
|
+
users = result["users"]
|
|
217
|
+
break if users.blank?
|
|
218
|
+
request_params[:page] += 1
|
|
219
|
+
resource = create_socialcast_user_index_request(http_config, request_params)
|
|
220
|
+
users.each do |user|
|
|
221
|
+
current_socialcast_list << [user['contact_info']['email'], user['company_login'], user['employee_number']]
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
current_socialcast_list
|
|
225
|
+
end
|
|
226
|
+
def create_socialcast_user_index_request(http_config, request_params)
|
|
227
|
+
path_template = "/api/users.json?format=%{format}&per_page=%{per_page}&page=%{page}"
|
|
228
|
+
Socialcast.resource_for_path((path_template % request_params), http_config)
|
|
229
|
+
end
|
|
179
230
|
end
|
|
180
|
-
|
|
181
231
|
end
|
|
182
232
|
end
|
data/lib/socialcast/version.rb
CHANGED
data/socialcast.gemspec
CHANGED
|
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
|
|
|
20
20
|
s.add_runtime_dependency 'highline', '>= 1.6.2'
|
|
21
21
|
s.add_runtime_dependency 'socialcast-net-ldap', '>= 0.1.6'
|
|
22
22
|
s.add_runtime_dependency 'activeresource', '>= 2.3.11'
|
|
23
|
-
s.add_development_dependency "rspec", '>= 2.
|
|
23
|
+
s.add_development_dependency "rspec", '>= 2.11.0'
|
|
24
24
|
s.add_development_dependency "webmock", '>= 1.7.7'
|
|
25
25
|
s.add_development_dependency 'rake', '0.9.2.2'
|
|
26
26
|
|
data/spec/cli_spec.rb
CHANGED
|
@@ -10,14 +10,14 @@ describe Socialcast::CLI do
|
|
|
10
10
|
with(:body => /message\_type\"\:null/).
|
|
11
11
|
with(:body => /testing/).
|
|
12
12
|
to_return(:status => 200, :body => "", :headers => {})
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
Socialcast::CLI.start ['share', 'testing']
|
|
15
15
|
end
|
|
16
16
|
it 'should send a POST with a message body of "testing" and nil message-type' do
|
|
17
17
|
# See expectations
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
context 'with a message_type message' do
|
|
22
22
|
before do
|
|
23
23
|
Socialcast.stub(:credentials).and_return(YAML.load_file(File.join(File.dirname(__FILE__), 'fixtures', 'credentials.yml')))
|
|
@@ -25,7 +25,7 @@ describe Socialcast::CLI do
|
|
|
25
25
|
with(:body => /message\_type\"\:review\_request/).
|
|
26
26
|
with(:body => /please\sreview/).
|
|
27
27
|
to_return(:status => 200, :body => "", :headers => {})
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
Socialcast::CLI.start ['share', 'please review', '--message_type=review_request']
|
|
30
30
|
end
|
|
31
31
|
it 'should send a POST with a message body of "please review" and message_type of "review_request"' do
|
|
@@ -38,7 +38,7 @@ describe Socialcast::CLI do
|
|
|
38
38
|
stub_request(:post, "https://ryan%40socialcast.com:foo@test.staging.socialcast.com/api/messages.json").
|
|
39
39
|
with(:body => /group\_id\"\:123/).
|
|
40
40
|
to_return(:status => 200, :body => "", :headers => {})
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
Socialcast::CLI.start ['share', 'hi', '--group_id=123']
|
|
43
43
|
end
|
|
44
44
|
it 'should send a POST with group_id param == 123' do
|
|
@@ -52,16 +52,16 @@ describe Socialcast::CLI do
|
|
|
52
52
|
with(:body => /message\_type\"\:null/).
|
|
53
53
|
with(:body => /testing/).
|
|
54
54
|
to_return(:status => 200, :body => "", :headers => {})
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
Socialcast::CLI.start ['share', 'testing']
|
|
57
57
|
end
|
|
58
58
|
it 'should send a POST with a message body of "testing" and nil message-type' do
|
|
59
59
|
# See expectations
|
|
60
60
|
end
|
|
61
61
|
end
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
end
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
describe '#provision' do
|
|
66
66
|
before do
|
|
67
67
|
Socialcast::CLI.instance_eval do # to supress warning from stubbing load_configuration
|
|
@@ -144,6 +144,37 @@ describe Socialcast::CLI do
|
|
|
144
144
|
@result.should_not =~ %r{roles}
|
|
145
145
|
end
|
|
146
146
|
end
|
|
147
|
+
context "with a user marked for termination that shouldn't be and sanity_check option passed" do
|
|
148
|
+
before do
|
|
149
|
+
@entry = Net::LDAP::Entry.new("cn=Ryan,dc=example,dc=com")
|
|
150
|
+
@entry[:mail] = 'ryan@example.com'
|
|
151
|
+
@valid_entry = Net::LDAP::Entry.new("cn=Sean,dc=example,dc=com")
|
|
152
|
+
@valid_entry[:mail] = 'sean@example.com'
|
|
153
|
+
ldap_search_block = double("ldapsearchblock")
|
|
154
|
+
ldap_search_block.should_receive(:search).and_yield(@entry)
|
|
155
|
+
ldap_return = double("ldapreturn")
|
|
156
|
+
ldap_return.should_receive(:search).with(include(:filter=>Net::LDAP::Filter.construct("(&(mail=sean@example.com)(mail=*))"))).and_return(@valid_entry)
|
|
157
|
+
|
|
158
|
+
Socialcast::CLI.any_instance.should_receive(:create_ldap_instance).and_return(ldap_search_block, ldap_return)
|
|
159
|
+
|
|
160
|
+
@result = ''
|
|
161
|
+
Zlib::GzipWriter.stub(:open).and_yield(@result)
|
|
162
|
+
Socialcast.stub(:credentials).and_return(YAML.load_file(File.join(File.dirname(__FILE__), 'fixtures', 'credentials.yml')))
|
|
163
|
+
Socialcast::CLI.any_instance.should_receive(:load_configuration).with(/ldap.yml/).and_return(YAML.load_file(File.join(File.dirname(__FILE__), 'fixtures', 'ldap.yml')))
|
|
164
|
+
File.stub(:open).with(/users.xml.gz/, anything).and_yield(@result)
|
|
165
|
+
|
|
166
|
+
Socialcast::CLI.any_instance.should_receive(:create_socialcast_user_index_request).and_return(
|
|
167
|
+
double("request1", :get => {"users" => [{"contact_info" => {"email" => @entry[:mail][0]}}]}.to_json),
|
|
168
|
+
double("request2", :get => {"users" => [{"contact_info" => {"email" => @valid_entry[:mail][0]}}]}.to_json),
|
|
169
|
+
double("empty_request", :get => {"users" => []}.to_json)
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
RestClient::Resource.any_instance.should_receive(:post).never
|
|
173
|
+
end
|
|
174
|
+
it 'does not post to Socialcast and throws Kernel.abort' do
|
|
175
|
+
lambda { Socialcast::CLI.start ['provision', '-c', 'spec/fixtures/ldap.yml', '--sanity_check', true] }.should raise_error SystemExit
|
|
176
|
+
end
|
|
177
|
+
end
|
|
147
178
|
context 'with external group member' do
|
|
148
179
|
before do
|
|
149
180
|
@entry = Net::LDAP::Entry.new("dc=example,dc=com")
|
|
@@ -294,10 +325,10 @@ describe Socialcast::CLI do
|
|
|
294
325
|
@entry[:mail] = 'ryan@example.com'
|
|
295
326
|
@entry[:manager] = 'cn=bossman,dc=example,dc=com'
|
|
296
327
|
@manager_email = 'bossman@example.com'
|
|
297
|
-
|
|
328
|
+
|
|
298
329
|
@entry.stub(:dereference_mail).with(kind_of(Net::LDAP), "manager", "mail").and_return(@manager_email)
|
|
299
330
|
Net::LDAP.any_instance.stub(:search).and_yield(@entry)
|
|
300
|
-
|
|
331
|
+
|
|
301
332
|
@result = ''
|
|
302
333
|
Zlib::GzipWriter.stub(:open).and_yield(@result)
|
|
303
334
|
Socialcast.stub(:credentials).and_return(YAML.load_file(File.join(File.dirname(__FILE__), 'fixtures', 'credentials.yml')))
|
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.1.
|
|
4
|
+
version: 1.1.6
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -10,11 +10,11 @@ authors:
|
|
|
10
10
|
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date: 2012-
|
|
13
|
+
date: 2012-08-15 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: rest-client
|
|
17
|
-
requirement: &
|
|
17
|
+
requirement: &2159242000 !ruby/object:Gem::Requirement
|
|
18
18
|
none: false
|
|
19
19
|
requirements:
|
|
20
20
|
- - ! '>='
|
|
@@ -22,10 +22,10 @@ dependencies:
|
|
|
22
22
|
version: 1.4.0
|
|
23
23
|
type: :runtime
|
|
24
24
|
prerelease: false
|
|
25
|
-
version_requirements: *
|
|
25
|
+
version_requirements: *2159242000
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: json
|
|
28
|
-
requirement: &
|
|
28
|
+
requirement: &2159241000 !ruby/object:Gem::Requirement
|
|
29
29
|
none: false
|
|
30
30
|
requirements:
|
|
31
31
|
- - ! '>='
|
|
@@ -33,10 +33,10 @@ dependencies:
|
|
|
33
33
|
version: 1.4.6
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
|
-
version_requirements: *
|
|
36
|
+
version_requirements: *2159241000
|
|
37
37
|
- !ruby/object:Gem::Dependency
|
|
38
38
|
name: thor
|
|
39
|
-
requirement: &
|
|
39
|
+
requirement: &2159240520 !ruby/object:Gem::Requirement
|
|
40
40
|
none: false
|
|
41
41
|
requirements:
|
|
42
42
|
- - ! '>='
|
|
@@ -44,10 +44,10 @@ dependencies:
|
|
|
44
44
|
version: 0.14.6
|
|
45
45
|
type: :runtime
|
|
46
46
|
prerelease: false
|
|
47
|
-
version_requirements: *
|
|
47
|
+
version_requirements: *2159240520
|
|
48
48
|
- !ruby/object:Gem::Dependency
|
|
49
49
|
name: highline
|
|
50
|
-
requirement: &
|
|
50
|
+
requirement: &2159240060 !ruby/object:Gem::Requirement
|
|
51
51
|
none: false
|
|
52
52
|
requirements:
|
|
53
53
|
- - ! '>='
|
|
@@ -55,10 +55,10 @@ dependencies:
|
|
|
55
55
|
version: 1.6.2
|
|
56
56
|
type: :runtime
|
|
57
57
|
prerelease: false
|
|
58
|
-
version_requirements: *
|
|
58
|
+
version_requirements: *2159240060
|
|
59
59
|
- !ruby/object:Gem::Dependency
|
|
60
60
|
name: socialcast-net-ldap
|
|
61
|
-
requirement: &
|
|
61
|
+
requirement: &2159239420 !ruby/object:Gem::Requirement
|
|
62
62
|
none: false
|
|
63
63
|
requirements:
|
|
64
64
|
- - ! '>='
|
|
@@ -66,10 +66,10 @@ dependencies:
|
|
|
66
66
|
version: 0.1.6
|
|
67
67
|
type: :runtime
|
|
68
68
|
prerelease: false
|
|
69
|
-
version_requirements: *
|
|
69
|
+
version_requirements: *2159239420
|
|
70
70
|
- !ruby/object:Gem::Dependency
|
|
71
71
|
name: activeresource
|
|
72
|
-
requirement: &
|
|
72
|
+
requirement: &2159238740 !ruby/object:Gem::Requirement
|
|
73
73
|
none: false
|
|
74
74
|
requirements:
|
|
75
75
|
- - ! '>='
|
|
@@ -77,21 +77,21 @@ dependencies:
|
|
|
77
77
|
version: 2.3.11
|
|
78
78
|
type: :runtime
|
|
79
79
|
prerelease: false
|
|
80
|
-
version_requirements: *
|
|
80
|
+
version_requirements: *2159238740
|
|
81
81
|
- !ruby/object:Gem::Dependency
|
|
82
82
|
name: rspec
|
|
83
|
-
requirement: &
|
|
83
|
+
requirement: &2159238080 !ruby/object:Gem::Requirement
|
|
84
84
|
none: false
|
|
85
85
|
requirements:
|
|
86
86
|
- - ! '>='
|
|
87
87
|
- !ruby/object:Gem::Version
|
|
88
|
-
version: 2.
|
|
88
|
+
version: 2.11.0
|
|
89
89
|
type: :development
|
|
90
90
|
prerelease: false
|
|
91
|
-
version_requirements: *
|
|
91
|
+
version_requirements: *2159238080
|
|
92
92
|
- !ruby/object:Gem::Dependency
|
|
93
93
|
name: webmock
|
|
94
|
-
requirement: &
|
|
94
|
+
requirement: &2159237620 !ruby/object:Gem::Requirement
|
|
95
95
|
none: false
|
|
96
96
|
requirements:
|
|
97
97
|
- - ! '>='
|
|
@@ -99,10 +99,10 @@ dependencies:
|
|
|
99
99
|
version: 1.7.7
|
|
100
100
|
type: :development
|
|
101
101
|
prerelease: false
|
|
102
|
-
version_requirements: *
|
|
102
|
+
version_requirements: *2159237620
|
|
103
103
|
- !ruby/object:Gem::Dependency
|
|
104
104
|
name: rake
|
|
105
|
-
requirement: &
|
|
105
|
+
requirement: &2159237160 !ruby/object:Gem::Requirement
|
|
106
106
|
none: false
|
|
107
107
|
requirements:
|
|
108
108
|
- - =
|
|
@@ -110,7 +110,7 @@ dependencies:
|
|
|
110
110
|
version: 0.9.2.2
|
|
111
111
|
type: :development
|
|
112
112
|
prerelease: false
|
|
113
|
-
version_requirements: *
|
|
113
|
+
version_requirements: *2159237160
|
|
114
114
|
description: publish messages to your stream from a command line interface
|
|
115
115
|
email:
|
|
116
116
|
- ryan@socialcast.com
|