simple_crowd 1.0.0

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,15 @@
1
+ module SimpleCrowd
2
+ class CrowdError < StandardError
3
+ attr_accessor :response
4
+ attr_reader :type
5
+ def initialize string, data = nil
6
+ super string
7
+ self.response = data unless data.nil?
8
+ @type = data[:detail].keys.first unless data.nil?
9
+ end
10
+
11
+ def type? type
12
+ @type == type.to_sym
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ module SimpleCrowd
2
+ class Group < CrowdEntity
3
+ property :id
4
+ property :name
5
+ property :active, :default => true
6
+ property :description
7
+ property :directory_id
8
+
9
+ property :members
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ module SimpleCrowd
2
+ module Mappers
3
+ class SoapAttributes
4
+ def self.produce hash
5
+ {"int:SOAPAttribute" => hash.inject([]) {|attrs, (key, val)| attrs << {"int:name" => key, "int:values" => {"wsdl:string" => val}}}}
6
+ end
7
+ def self.parse attributes
8
+ soap = attributes[:soap_attribute]
9
+ (soap && soap.inject({}) {|hash, attr| hash[attr[:name].to_sym] = attr[:values][:string]; hash }) || {}
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,102 @@
1
+ # Client with basic functions mocked out to stop calls from going to the server during tests
2
+ module SimpleCrowd
3
+ class MockClient
4
+ class << self
5
+ attr_accessor :users, :tokens
6
+
7
+ def reset
8
+ @users, @tokens = [], []
9
+ end
10
+ end
11
+
12
+ @users = []
13
+ @tokens = []
14
+
15
+ def get_cookie_info
16
+ {:secure=>false, :domain=>".twcrowdtest.com"}
17
+ end
18
+ def authenticate_application(name = nil, password = nil)
19
+ "MOCKAPPTOKEN"
20
+ end
21
+ alias_method :app_token, :authenticate_application
22
+
23
+ def authenticate_user name, password, factors = nil
24
+ (user = find_user_by_name(name)) && user.password && user.password == password ? new_user_token(name) : nil
25
+ end
26
+ def create_user_token name
27
+ new_user_token(name) if find_user_by_name(name)
28
+ end
29
+ def invalidate_user_token token
30
+ tokens.delete token
31
+ end
32
+ def is_valid_user_token? token, factors = nil
33
+ tokens.include? token
34
+ end
35
+ def find_all_user_names
36
+ users.map{|u| u.username}
37
+ end
38
+ def find_user_by_name name
39
+ users.detect{|u| u.username == name}
40
+ end
41
+ alias_method :find_user_with_attributes_by_name, :find_user_by_name
42
+
43
+ def find_user_by_token token
44
+ token && tokens.include?(token) && (name = /.+-TOKENFOR-(.+)$/.match(token)) && name[1] && find_user_by_name(name[1])
45
+ end
46
+ def find_username_by_token token
47
+ (user = find_user_by_token(token)) && user.username
48
+ end
49
+ def find_user_by_email email
50
+ users.detect{|u| u.email == email}
51
+ end
52
+ def search_users_by_email email
53
+ users.select{|u| u.email =~ /#{email}/}
54
+ end
55
+ def add_user user, credential
56
+ if user && user.username && !find_user_by_name(user.username)
57
+ user.password = credential
58
+ self.class.users << user
59
+ user
60
+ end
61
+ end
62
+ def remove_user name
63
+ user = users.delete(find_user_by_name(name))
64
+ tokens.reject!{|t| t =~ /.+-TOKENFOR-#{user.username}/} if user && user.username
65
+ end
66
+ def update_user_credential name, credential, encrypted = false
67
+ if user = find_user_by_name(name)
68
+ user.password = credential
69
+ end
70
+ end
71
+ def update_user_attribute user, name, value
72
+ if user = find_user_by_name(user)
73
+ user[name] = value
74
+ end
75
+ end
76
+ def update_user user
77
+ return unless user.dirty?
78
+
79
+ attrs_to_update = user.dirty_attributes
80
+ return if attrs_to_update.empty?
81
+
82
+ stored_user = find_user_by_name(user.username)
83
+ return if stored_user.blank?
84
+
85
+ attrs_to_update.each do |a|
86
+ stored_user[a] = user.send(a)
87
+ end
88
+ end
89
+
90
+ private
91
+ def new_user_token username
92
+ random_token(username).tap{|t| tokens << t}
93
+ end
94
+ def random_token username
95
+ "#{rand(36**10).to_s(36)}-TOKENFOR-#{username}"
96
+ end
97
+
98
+ def tokens; self.class.tokens; end
99
+ def users; self.class.users; end
100
+
101
+ end
102
+ end
@@ -0,0 +1,17 @@
1
+ module SimpleCrowd
2
+ class User < CrowdEntity
3
+
4
+ # Immutable properties
5
+ property :id, :immutable => true
6
+ property :username, :immutable => true, :map_soap => :name
7
+ property :description, :immutable => true
8
+ property :active, :immutable => true, :default => true
9
+ property :directory_id, :immutable => true
10
+
11
+ # Assumed available attributes (with soap aliases)
12
+ property :first_name, :attribute => true, :map_soap => :givenName
13
+ property :last_name, :attribute => true, :map_soap => :sn
14
+ property :display_name, :attribute => true, :map_soap => :displayName
15
+ property :email, :attribute => true, :map_soap => :mail
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module SimpleCrowd
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,57 @@
1
+ $:.push File.expand_path("../lib",__FILE__)
2
+ require 'simple_crowd/version'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = %q{simple_crowd}
6
+ s.version = SimpleCrowd::VERSION.dup
7
+ s.platform = Gem::Platform::RUBY
8
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
9
+ s.authors = ["Paul Strong"]
10
+ s.description = %q{Simple Atlassian Crowd client using REST and SOAP APIs where needed.
11
+ Doesn't do any fancy object mapping, etc.}
12
+ s.email = %q{paul@thestrongfamily.org}
13
+ s.files = %w(
14
+ .gitignore
15
+ Gemfile
16
+ README.md
17
+ Rakefile
18
+ lib/simple_crowd.rb
19
+ lib/simple_crowd/client.rb
20
+ lib/simple_crowd/crowd_entity.rb
21
+ lib/simple_crowd/crowd_error.rb
22
+ lib/simple_crowd/group.rb
23
+ lib/simple_crowd/mappers/soap_attributes.rb
24
+ lib/simple_crowd/mock_client.rb
25
+ lib/simple_crowd/user.rb
26
+ lib/simple_crowd/version.rb
27
+ simple_crowd.gemspec
28
+ test/crowd_config.yml.example
29
+ test/factories.rb
30
+ test/helper.rb
31
+ test/test_client.rb
32
+ test/test_simple_crowd.rb
33
+ test/test_user.rb
34
+ )
35
+ s.test_files = %w(
36
+ test/crowd_config.yml.example
37
+ test/factories.rb
38
+ test/helper.rb
39
+ test/test_client.rb
40
+ test/test_simple_crowd.rb
41
+ test/test_user.rb
42
+ )
43
+ s.homepage = %q{http://github.com/lapluviosilla/simple_crowd}
44
+ s.require_paths = ["lib"]
45
+ s.rubygems_version = %q{1.5.0}
46
+ s.summary = %q{Simple Atlassian Crowd client using REST and SOAP APIs where needed.}
47
+
48
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
49
+ s.add_development_dependency(%q<fcoury-matchy>, [">= 0"])
50
+ s.add_development_dependency(%q<factory_girl>, [">= 0"])
51
+ s.add_development_dependency(%q<forgery>, [">= 0"])
52
+ s.add_development_dependency(%q<webmock>, [">= 0"])
53
+ s.add_development_dependency(%q<rr>, [">= 0"])
54
+ s.add_runtime_dependency(%q<savon>, ["= 0.7.9"])
55
+ s.add_runtime_dependency(%q<hashie>, ["= 1.0.0"])
56
+ end
57
+
@@ -0,0 +1,6 @@
1
+ # Configure Crowd Test server for unit tests
2
+ # Replace default values with crowd server params
3
+ crowd:
4
+ service_url: http://www.crowdserver.com:8095/crowd/
5
+ app_name: crowdapp
6
+ app_password: testpass
data/test/factories.rb ADDED
@@ -0,0 +1,9 @@
1
+ Factory.define :user, :class => SimpleCrowd::User do |u|
2
+ u.first_name 'Test'
3
+ u.last_name 'User'
4
+ u.display_name {|a| "#{a.first_name} #{a.last_name}"}
5
+ u.email {|a| "#{a.first_name}.#{a.last_name}@example.com".downcase }
6
+ u.sequence(:username) {|n| "test#{n}" }
7
+ # Clear dirty properties
8
+ u.after_build { |user| user.dirty_properties.clear }
9
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+
6
+ $CROWD_CONFIG_PATH = File.join(File.dirname(__FILE__), 'crowd_config.yml')
7
+
8
+ require 'simple_crowd'
9
+ require 'test/unit'
10
+ require 'shoulda'
11
+ require 'matchy'
12
+ require 'webmock/test_unit'
13
+ require 'rr'
14
+ require 'factory_girl'
15
+ require 'forgery'
16
+
17
+ # Load factories
18
+ require File.dirname(__FILE__) + "/factories"
19
+
20
+ WebMock.allow_net_connect!
21
+
22
+ class Test::Unit::TestCase
23
+ include WebMock
24
+ include RR::Adapters::TestUnit
25
+ def setup
26
+ WebMock.allow_net_connect!
27
+ end
28
+ end
@@ -0,0 +1,331 @@
1
+ require 'helper'
2
+
3
+ class TestClient < Test::Unit::TestCase
4
+ CROWD_CONFIG = YAML.load_file($CROWD_CONFIG_PATH)['crowd']
5
+ context "A Client" do
6
+ setup do
7
+ @client = SimpleCrowd::Client.new({:service_url => CROWD_CONFIG['service_url'],
8
+ :app_name => CROWD_CONFIG['app_name'],
9
+ :app_password => CROWD_CONFIG['app_password']})
10
+ @service_url = SimpleCrowd.soap_options({:service_url => CROWD_CONFIG['service_url']})[:service_url]
11
+ reset_webmock
12
+ end
13
+ should "initialize" do
14
+ @client.should_not be nil
15
+ assert_instance_of SimpleCrowd::Client, @client
16
+ end
17
+ should "get app token" do
18
+ token = @client.app_token
19
+ token.should_not be nil
20
+ token.length.should == 24
21
+
22
+ assert_requested :post, @service_url
23
+ end
24
+ should "refresh app token if invalid" do
25
+ # Get initial valid token
26
+ token = @client.app_token
27
+ info = @client.get_cookie_info
28
+ info.should_not be nil
29
+ @client.app_token = token + "invalid"
30
+ @client.app_token.should == token + "invalid"
31
+ # making the token invalid should cause the client to refresh it
32
+ # and get the cookie info successfully
33
+ @client.get_cookie_info.should == info
34
+ # Validate refreshed token is same as original token
35
+ @client.app_token.should == token
36
+ assert_requested :post, @service_url, :times => 5
37
+ end
38
+ should "get cookie info" do
39
+ info = @client.get_cookie_info
40
+ info.should_not be nil
41
+ info[:domain].should_not be nil
42
+ info[:domain].length.should > 0
43
+ info[:secure].should_not be nil
44
+ assert_requested :post, @service_url, :times => 2
45
+ end
46
+ should "authenticate user" do
47
+ token = @client.authenticate_user "test", "test"
48
+ token.should_not be nil
49
+ token.length.should == 24
50
+
51
+ assert_requested :post, @service_url, :times => 2
52
+ end
53
+ should "authenticate user with validation factors" do
54
+ token = @client.authenticate_user "test", "test", {:test_factor => "test1234"}
55
+ token.should_not be nil
56
+ token.length.should == 24
57
+
58
+ assert_requested :post, @service_url, :times => 2
59
+ end
60
+ should "create user token without password" do
61
+ token = @client.create_user_token "test"
62
+ token.should_not be nil
63
+ token.length.should == 24
64
+
65
+ assert_requested :post, @service_url, :times => 2
66
+ end
67
+ should "return same user token with or without password" do
68
+ token_with_pass = @client.authenticate_user "test", "test"
69
+ token_with_pass.should_not be nil
70
+ token_with_pass.length.should == 24
71
+ token_without_pass = @client.create_user_token "test"
72
+ token_without_pass.should_not be nil
73
+ token_with_pass.length.should == 24
74
+
75
+ token_with_pass.should == token_without_pass
76
+
77
+ assert_requested :post, @service_url, :times => 3
78
+ end
79
+ should "validate user token" do
80
+ token = @client.authenticate_user "test", "test"
81
+ valid = @client.is_valid_user_token? token
82
+ valid.should be true
83
+ invalid = @client.is_valid_user_token?(token + "void")
84
+ invalid.should be false
85
+ assert_requested :post, @service_url, :times => 4
86
+ end
87
+ should "validate user token with factors" do
88
+ token = @client.authenticate_user "test", "test", {"Random-Number" => 6375}
89
+ @client.is_valid_user_token?(token).should be false
90
+ @client.is_valid_user_token?(token, {"Random-Number" => 6375}).should be true
91
+ token2 = @client.authenticate_user "test", "test"
92
+ @client.is_valid_user_token?(token2, {"Random-Number" => 48289}).should be false
93
+ assert_requested :post, @service_url, :times => 6
94
+ end
95
+ should "invalidate user token (logout)" do
96
+ token = @client.authenticate_user "test", "test"
97
+ @client.is_valid_user_token?(token).should be true
98
+
99
+ # Invalidate nonexistant token
100
+ @client.invalidate_user_token(token + "void").should be true
101
+ @client.is_valid_user_token?(token).should be true
102
+
103
+ # Invalidate token
104
+ @client.invalidate_user_token(token).should be true
105
+ @client.is_valid_user_token?(token).should be false
106
+
107
+ assert_requested :post, @service_url, :times => 7
108
+ end
109
+ should "reset user password" do
110
+ # Get real app token before mocking reset call
111
+ @client.app_token
112
+ WebMock.disable_net_connect!
113
+
114
+ response = %Q{<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
115
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
116
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
117
+ <soap:Body><ns1:resetPrincipalCredentialResponse xmlns:ns1="urn:SecurityServer" />
118
+ </soap:Body></soap:Envelope>
119
+ }
120
+ stub_request(:post, @service_url).to_return(:body => response, :status => 200)
121
+
122
+ @client.reset_user_password("test").should be true
123
+ end
124
+ should "find all user names" do
125
+ names = @client.find_all_user_names
126
+ names.should_not be nil
127
+ names.is_a?(Array).should be true
128
+ names.empty?.should be false
129
+ names.include?("test").should be true
130
+ end
131
+ should "find user by name" do
132
+ user = @client.find_user_by_name "test"
133
+ user.should_not be nil
134
+ [:id, :username, :description, :active, :directory_id, :first_name, :last_name, :email].each {|v| user.key?(v).should be true}
135
+ [:id, :username, :active, :directory_id].each {|v| user[v].should_not be nil}
136
+ assert_requested :post, @service_url, :times => 2
137
+ end
138
+ should "find user by token" do
139
+ token = @client.authenticate_user "test", "test"
140
+ user = @client.find_user_by_token token
141
+ user.should_not be nil
142
+ user.first_name.should == "Test"
143
+
144
+ assert_requested :post, @service_url, :times => 3
145
+ end
146
+ should "find username by token" do
147
+ token = @client.authenticate_user "test", "test"
148
+ user = @client.find_username_by_token token
149
+ user.should_not be nil
150
+ user.length.should > 0
151
+ user.should == "test"
152
+
153
+ assert_requested :post, @service_url, :times => 3
154
+ end
155
+ should "find user by email" do
156
+ user = @client.find_user_by_email "test@testing.com"
157
+ user.should_not be nil
158
+ user.first_name.should == "Test"
159
+ user.last_name.should == "User"
160
+
161
+ # partial searches should return nothing
162
+ user = @client.find_user_by_email "test"
163
+ user.should be nil
164
+
165
+ assert_requested :post, @service_url, :times => 3
166
+ end
167
+ should "search for users by email" do
168
+ users = @client.search_users_by_email "test"
169
+ users.empty?.should_not be true
170
+ users.all?{|u| u.email =~ /test/ }.should be true
171
+
172
+ assert_requested :post, @service_url, :times => 2
173
+ end
174
+ should "search users" do
175
+ users = @client.search_users({'principal.email' => "test@testing.com"})
176
+ users.should_not be nil
177
+ users.empty?.should_not be true
178
+ users.all?{|u| u.email == "test@testing.com" }.should be true
179
+
180
+ users = @client.search_users({'principal.fullname' => "Test"})
181
+ users.should_not be nil
182
+ users.empty?.should_not be true
183
+ users.each{|u| u.display_name.downcase.should =~ /test/ }
184
+
185
+ assert_requested :post, @service_url, :times => 3
186
+ end
187
+ should "return nil for nonexistant user" do
188
+ user = @client.find_user_by_name "nonexistant"
189
+ user.should be nil
190
+ assert_requested :post, @service_url, :times => 2
191
+ end
192
+ should "update user credential" do
193
+ @client.authenticate_user("test", "test").should_not be nil
194
+ @client.update_user_credential("test", "testupdate").should be true
195
+ lambda {@client.authenticate_user("test", "test")}.should raise_error
196
+ @client.authenticate_user("test", "testupdate").should_not be nil
197
+ @client.update_user_credential("test", "test").should be true
198
+ end
199
+ should "add/remove user" do
200
+ localuser = Factory.build(:user)
201
+ user = @client.add_user(localuser, "newuserpass")
202
+ user.should_not be nil
203
+ user.username.should == localuser.username
204
+ user.first_name.should == localuser.first_name
205
+ user.last_name.should == localuser.last_name
206
+ @client.authenticate_user(localuser.username, "newuserpass").should_not be nil
207
+ @client.remove_user(localuser.username).should be true
208
+ lambda {@client.authenticate_user(localuser.username, "newuserpass")}.should raise_error
209
+
210
+ assert_requested :post, @service_url, :times => 5
211
+ end
212
+
213
+ should "update user attribute" do
214
+ username = "test_update"
215
+ localuser = Factory.build(:user, :username => username)
216
+ remoteuser = @client.add_user(localuser, "updatepass")
217
+ @client.update_user_attribute(username, 'givenName', 'UpdatedFirst').should be true
218
+ updateduser = @client.find_user_by_name(username)
219
+ updateduser.last_name.should == localuser.last_name
220
+ updateduser.first_name.should == 'UpdatedFirst'
221
+ @client.remove_user "test_update"
222
+ end
223
+ should "update user custom attribute" do
224
+ username = "test_update"
225
+ localuser = Factory.build(:user, :username => username)
226
+ remoteuser = @client.add_user(localuser, "updatepass")
227
+ @client.update_user_attribute(username, 'customAttr', 'customVal').should be true
228
+ remoteuser = @client.find_user_with_attributes_by_name username
229
+ remoteuser.last_name.should == localuser.last_name
230
+
231
+ remoteuser[:customAttr].should == 'customVal'
232
+ @client.remove_user "test_update"
233
+ end
234
+ should "update user attribute array" do
235
+ username = "test_update"
236
+ localuser = Factory.build(:user, :username => username)
237
+ remoteuser = @client.add_user(localuser, "updatepass")
238
+ test_array = ["one", "two", "4"]
239
+ @client.update_user_attribute(username, 'arrayTest', test_array).should be true
240
+ remoteuser = @client.find_user_with_attributes_by_name username
241
+ remoteuser.last_name.should == localuser.last_name
242
+ remoteuser[:arrayTest].sort.should == test_array.sort
243
+ test_array.delete "two"
244
+ @client.update_user_attribute(username, 'arrayTest', test_array).should be true
245
+ remoteuser = @client.find_user_with_attributes_by_name username
246
+ remoteuser[:arrayTest].sort.should == test_array.sort
247
+ remoteuser[:arrayTest].include?("two").should be false
248
+ @client.remove_user "test_update"
249
+ end
250
+ should "update user" do
251
+ username = "test_update"
252
+ localuser = Factory.build(:user, :username => username)
253
+ remoteuser = @client.add_user(localuser, "updatepass")
254
+ remoteuser.should_not be nil
255
+ remoteuser.username.should == localuser.username
256
+ remoteuser.first_name.should == localuser.first_name
257
+ remoteuser.last_name.should == localuser.last_name
258
+
259
+ remoteuser.dirty?.should be false
260
+ # Should be ignored
261
+ remoteuser.active = false
262
+ remoteuser.first_name = "UpdatedFirst"
263
+ remoteuser.last_name = "UpdatedLast"
264
+ remoteuser.dirty?.should be true
265
+
266
+ remoteuser.dirty_attributes.should == [:first_name, :last_name]
267
+
268
+ @client.update_user(remoteuser)
269
+
270
+ remoteuser = @client.find_user_with_attributes_by_name username
271
+ remoteuser.first_name.should == "UpdatedFirst"
272
+ remoteuser.last_name.should == "UpdatedLast"
273
+ remoteuser.email.should == localuser.email
274
+ @client.remove_user "test_update"
275
+ end
276
+ should "check if cache enabled" do
277
+ enabled = @client.is_cache_enabled?
278
+ is_true = enabled.class == TrueClass
279
+ is_false = enabled.class == FalseClass
280
+ (is_true || is_false).should be true
281
+ end
282
+ should "get granted authorities" do
283
+ granted = @client.get_granted_authorities
284
+ (granted.nil? || (granted.is_a?(Array) && !granted.empty? && granted[0].is_a?(String))).should be true
285
+
286
+ assert_requested :post, @service_url, :times => 2
287
+ end
288
+ should "find group by name" do
289
+ group = @client.find_group_by_name("Testing")
290
+ group.should_not be nil
291
+ assert_requested :post, @service_url, :times => 2
292
+ end
293
+ should "find all group names" do
294
+ names = @client.find_all_group_names
295
+ names.should_not be nil
296
+ names.is_a?(Array).should be true
297
+ names.empty?.should be false
298
+ names.include?("Testing").should be true
299
+ end
300
+ should "add/remove user from group" do
301
+ @client.add_user_to_group("test", "Testing").should be true
302
+ @client.is_group_member?("Testing", "test").should be true
303
+ @client.remove_user_from_group("test", "Testing").should be true
304
+ @client.is_group_member?("Testing", "test").should be false
305
+ assert_requested :post, @service_url, :times => 5
306
+ end
307
+ # should "add/remove attribute from group" do
308
+ # @client.add_attribute_to_group("test", "tmpattribute", "Hello World").should be true
309
+ # @client.remove_attribute_from_group("test", "tmpattribute").should be true
310
+ # end
311
+ should "update group" do
312
+ @client.find_group_by_name("Testing").active.should be true
313
+ @client.update_group("Testing", "Test Description", false).should be true
314
+ updated_group = @client.find_group_by_name("Testing")
315
+ updated_group.active.should be false
316
+ updated_group.description.should == "Test Description"
317
+ @client.update_group("Testing", "", true).should be true
318
+ end
319
+ should "check if user is group member" do
320
+ @client.add_user_to_group("test", "Testing").should be true
321
+ @client.is_group_member?("Testing", "test").should be true
322
+ @client.is_group_member?("nonexistantgroup", "test").should be false
323
+ assert_requested :post, @service_url, :times => 4
324
+ end
325
+ should "accept cached app token" do
326
+ @client.app_token = "cachedtoken"
327
+ @client.app_token.should == "cachedtoken"
328
+ assert_not_requested :post, @service_url
329
+ end
330
+ end
331
+ end