simple_crowd 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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