crowd-stefanwille 0.5.8

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,9 @@
1
+ class Crowd #:nodoc:
2
+ module Version #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 5
5
+ TINY = 8
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
data/lib/crowd.rb ADDED
@@ -0,0 +1,669 @@
1
+ #
2
+ # Updated by Stefan Wille, post@stefanwille.com on 2010-07-13
3
+ # Updated by Evgeny Zislis, evgeny.zislis@gmail.con on 2008-05-15
4
+ # Created by Jason Rimmer, jrimmer@irth.net on 2007-10-16.
5
+ # I hereby place this work that I have authored into the public domain
6
+ # and in the process abandon all copyright protection.
7
+ #
8
+ require 'sha1'
9
+ require 'base64'
10
+ require 'rubygems'
11
+ gem 'soap4r'
12
+
13
+ require File.join(File.dirname(__FILE__), 'crowd', 'version')
14
+ require File.join(File.dirname(__FILE__), 'crowd', 'soap', 'driver.rb')
15
+
16
+ #
17
+ # Place 'server.wiredump_dev = STDERR' after any to see
18
+ # the raw SOAP calls and responses in a test console
19
+ #
20
+
21
+ # public static final String USERNAME = "username";
22
+ # public static final String FIRSTNAME = "givenName";
23
+ # public static final String LASTNAME = "sn";
24
+ # public static final String DISPLAYNAME = "displayName";
25
+ # public static final String EMAIL = "mail";
26
+ # public static final String ICON_LOCATION = "iconLocation";
27
+ # public static final String PASSWORD_LASTCHANGED = "passwordLastChanged";
28
+ # public static final String LAST_AUTHENTICATED = "lastAuthenticated";
29
+ # public static final String INVALID_PASSWORD_ATTEMPTS = "invalidPasswordAttempts";
30
+ # public static final String REQUIRES_PASSWORD_CHANGE = "requiresPasswordChange";
31
+ # public static final String ACTIVE = "active";
32
+ #
33
+
34
+ class Crowd
35
+
36
+ #
37
+ # shortcircuit Crowd::SOAP
38
+ #
39
+ include SOAP
40
+
41
+ #
42
+ # Class variables
43
+ #
44
+ private
45
+
46
+ @@application_token = nil
47
+ @@crowd_app_name = nil
48
+ @@crowd_app_pword = nil
49
+
50
+ public
51
+
52
+ @@crowd_url = nil
53
+
54
+ def self.crowd_url=(value); @@crowd_url = value; end
55
+ def self.crowd_app_name=(value); @@crowd_app_name = value; end
56
+ def self.crowd_app_pword=(value); @@crowd_app_pword = value; end
57
+
58
+
59
+ # for testing
60
+ def self.crowd_url; @@crowd_url; end
61
+ def self.crowd_app_name; @@crowd_app_name; end
62
+ def self.crowd_app_pword; @@crowd_app_pword; end
63
+ def self.application_token; @@application_token; end
64
+
65
+ public
66
+
67
+ #
68
+ # Exceptions
69
+ #
70
+ class AuthenticationException < StandardError; end
71
+ class AuthenticationConnectionException < ::Errno::ECONNREFUSED; end
72
+ class AuthenticationInvalidCredentialException < AuthenticationException; end
73
+ class AuthenticationInvalidException < AuthenticationException; end
74
+ class AuthenticationObjectNotFoundException < AuthenticationException; end
75
+
76
+ #
77
+ # Public methods
78
+ #
79
+
80
+ ##
81
+ # Authenticates an application client to the Crowd security server.
82
+ def self.authenticate_application(validation_factors = {})
83
+ pword = PasswordCredential.new(@@crowd_app_pword, false)
84
+ aovf = helper_validation_factors(validation_factors)
85
+ ctx = ApplicationAuthenticationContext.new(pword, @@crowd_app_name, aovf)
86
+ arg = AuthenticateApplication.new(ctx)
87
+ begin
88
+ response = server.authenticateApplication(arg)
89
+ rescue Errno::ECONNREFUSED => e
90
+ raise AuthenticationConnectionException, e
91
+ end
92
+ @@application_token = response.out
93
+ end
94
+
95
+ ##
96
+ # Authenticates a principal verses the calling who is in the application's assigned directory.
97
+ #
98
+ # To use SSO, set:
99
+ # validation_factors = { 'USER_AGENT' => '...', 'REMOTE_ADDRESS' => '...' }
100
+ # for proxy users { 'X_FORWARDED_FOR" => '...' } might be useful as well.
101
+ def self.authenticate_principal(username, password, validation_factors = {})
102
+ response = authenticated_connection do
103
+ pword = PasswordCredential.new(password, false)
104
+ aovf = helper_validation_factors(validation_factors)
105
+ ctx = UserAuthenticationContext.new(@@application_token.name, pword, username, aovf)
106
+ arg = AuthenticatePrincipal.new(@@application_token, ctx)
107
+
108
+ server.authenticatePrincipal(arg)
109
+ end
110
+
111
+ #evaluate the response. ideally, the authenticatePrincipal call should
112
+ #return an AuthenticationInvalidCredentialException and we can handle it more
113
+ #nicely below.
114
+ case response
115
+ when AuthenticatePrincipalResponse
116
+ return response.out
117
+ when InvalidAuthenticationException
118
+ return nil
119
+ when InactiveAccountException
120
+ return nil
121
+ when nil #no reponse
122
+ raise AuthenticationInvalidCredentialException, response
123
+ when ::AuthenticationException
124
+ raise AuthenticationInvalidCredentialException, response
125
+ else
126
+ raise AuthenticationException, response
127
+ end
128
+ end
129
+
130
+
131
+ ##
132
+ # Authenticates a principal without validating a password.
133
+ def self.create_principal_token(username, validation_factors = {})
134
+ response = authenticated_connection do
135
+ aovf = helper_validation_factors(validation_factors)
136
+ arg = CreatePrincipalToken.new(@@application_token, username, aovf)
137
+ server.createPrincipalToken(arg)
138
+ end
139
+ response.out
140
+ end
141
+
142
+ ##
143
+ # Checks if the principal's current token is still valid.
144
+ def self.is_valid_principal_token?(principal_token, validation_factors = {})
145
+ response = authenticated_connection do
146
+ aovf = ArrayOfValidationFactor.new
147
+ validation_factors.each { |name,value| aovf << ValidationFactor.new(name, value)}
148
+ arg = IsValidPrincipalToken.new(@@application_token, principal_token, aovf)
149
+ server.isValidPrincipalToken(arg)
150
+ end
151
+
152
+ case response
153
+ when IsValidPrincipalTokenResponse
154
+ return response.out
155
+ else
156
+ raise AuthenticationException, response
157
+ end
158
+ end
159
+
160
+ ##
161
+ # Add Principal
162
+ def self.add_principal(username, password, description, is_active, attributes)
163
+ response = authenticated_connection do
164
+
165
+ attrs = ArrayOfSOAPAttribute.new()
166
+ attributes.each do |key, val|
167
+ if (val.class == Array)
168
+ attrVal = ArrayOfString.new(val)
169
+ else
170
+ attrVal = ArrayOfString.new
171
+ attrVal << val
172
+ end
173
+ attrs << SOAPAttribute.new(key, attrVal)
174
+ end
175
+
176
+ principal = SOAPPrincipal.new(nil, is_active, attrs, nil, description, nil, nil, username)
177
+ pword = PasswordCredential.new(password, false)
178
+ arg = AddPrincipal.new(@@application_token, principal, pword)
179
+
180
+ server.addPrincipal(arg)
181
+ end
182
+
183
+ case response
184
+ when AddPrincipalResponse
185
+ return true
186
+ when InvalidCredentialException
187
+ raise AuthenticationInvalidCredentalException
188
+ when InvalidUserException
189
+ raise AuthenticationInvalidException, response
190
+ else
191
+ raise AuthenticationException, response
192
+ end
193
+ end
194
+
195
+ ##
196
+ # Find Principal via username
197
+ def self.find_principal_by_username(username)
198
+ response = authenticated_connection do
199
+ arg = FindPrincipalByName.new(@@application_token, username)
200
+ server.findPrincipalByName(arg)
201
+ end
202
+
203
+ case response
204
+ when FindPrincipalByNameResponse
205
+ return parse_principal(response.out)
206
+ when ObjectNotFoundException
207
+ return nil
208
+ else
209
+ raise AuthenticationException, response
210
+ end
211
+ rescue AuthenticationException => e
212
+ raise AuthenticationObjectNotFoundException, e
213
+ end
214
+
215
+ ##
216
+ # Find Principal via token
217
+ def self.find_principal_by_token(token)
218
+ response = authenticated_connection do
219
+ arg = FindPrincipalByToken.new(@@application_token, token)
220
+ server.findPrincipalByToken(arg)
221
+ end
222
+ case response
223
+ when FindPrincipalByTokenResponse
224
+ return parse_principal(response.out)
225
+ when ObjectNotFoundException
226
+ return nil
227
+ else
228
+ raise AuthenticationException, response
229
+ end
230
+ rescue AuthenticationObjectNotFoundException
231
+ return nil
232
+ rescue AuthenticationException => e
233
+ nil
234
+ rescue ::SOAP::FaultError => e
235
+ raise AuthenticationException, e.message
236
+ end
237
+
238
+ ##
239
+ # Invalidate Principal Token
240
+ def self.invalidate_principal_token(token)
241
+ response = authenticated_connection do
242
+ arg = InvalidatePrincipalToken.new(@@application_token, token)
243
+ server.invalidatePrincipalToken(arg)
244
+ end
245
+
246
+ case response
247
+ when InvalidatePrincipalTokenResponse
248
+ return true
249
+ else
250
+ raise AuthenticationException, response
251
+ end
252
+ end
253
+
254
+ ##
255
+ # Remove principal attribute
256
+ def self.remove_attribute_principal(username, attributes)
257
+ if(attributes.class != Array)
258
+ attributes = [attributes]
259
+ end
260
+
261
+ attributes.each do |attr|
262
+ response = authenticated_connection do
263
+ arg = RemoveAttributeFromPrincipal.new(@@application_token, username, attr)
264
+ server.removeAttributeFromPrincipal(arg)
265
+ end
266
+
267
+ case response
268
+ when RemoveAttributeFromPrincipalResponse
269
+ # Burying as this means it succeeded
270
+ when ObjectNotFoundException
271
+ raise AuthenticationObjectNotFoundException
272
+ else
273
+ raise AuthenticationException, response
274
+ end
275
+ end
276
+ end
277
+
278
+ ##
279
+ # Add attribute to principal
280
+ def self.add_attribute_principal(username, attributes)
281
+ attributes.each do |key, val|
282
+ response = authenticated_connection do
283
+ if(val.class == Array)
284
+ valArray = ArrayOfString.new(val)
285
+ else
286
+ valArray = ArrayOfString.new
287
+ valArray << val
288
+ end
289
+
290
+ tuple = SOAPAttribute.new(key, valArray)
291
+ arg = AddAttributeToPrincipal.new(@@application_token, username, tuple)
292
+
293
+ server.addAttributeToPrincipal(arg)
294
+ end
295
+
296
+ case response
297
+ when AddAttributeToPrincipalResponse
298
+ # Burying it because this means it was successful
299
+ when ObjectNotFoundException
300
+ raise AuthenticationObjectNotFoundException
301
+ else
302
+ raise AuthenticationException, response
303
+ end
304
+ end
305
+
306
+ true
307
+ end
308
+
309
+ ##
310
+ # Update attribute on principal
311
+ def self.update_attribute_principal(username, attributes)
312
+ attributes.each do |key, val|
313
+ response = authenticated_connection do
314
+ if val.is_a?(Array)
315
+ valArray = ArrayOfString.new(val)
316
+ else
317
+ valArray = ArrayOfString.new
318
+ valArray << val
319
+ end
320
+
321
+ tuple = SOAPAttribute.new(key, valArray)
322
+ arg = UpdatePrincipalAttribute.new(@@application_token, username, tuple)
323
+
324
+ server.updatePrincipalAttribute(arg)
325
+ end
326
+
327
+ case response
328
+ when UpdatePrincipalAttributeResponse
329
+ # Burying as it worked
330
+ when ObjectNotFoundException
331
+ raise AuthenticationObjectNotFoundException
332
+ else
333
+ raise AuthenticationException, response
334
+ end
335
+ end
336
+
337
+ true
338
+ end
339
+
340
+ ##
341
+ # Remove principal
342
+ def self.remove_principal(username)
343
+ response = authenticated_connection do
344
+ arg = RemovePrincipal.new(@@application_token, username)
345
+ server.removePrincipal(arg)
346
+ end
347
+
348
+ case response
349
+ when RemovePrincipalResponse
350
+ return true
351
+ when ObjectNotFoundException
352
+ raise AuthenticationObjectNotFoundException
353
+ else
354
+ raise AuthenticationException, response
355
+ end
356
+ end
357
+
358
+ ##
359
+ # Find all principal names
360
+ def self.find_all_principal_names
361
+ response = authenticated_connection do
362
+ arg = FindAllPrincipalNames.new(@@application_token)
363
+ server.findAllPrincipalNames(arg)
364
+ end
365
+
366
+ case response
367
+ when FindAllPrincipalNamesResponse
368
+ return response.out
369
+ when ObjectNotFoundException
370
+ return {}
371
+ else
372
+ raise AuthenticationException, response
373
+ end
374
+ end
375
+
376
+ ##
377
+ # Find all role names
378
+ def self.find_all_role_names
379
+ response = authenticated_connection do
380
+ arg = FindAllRoleNames.new(@@application_token)
381
+ server.findAllRoleNames(arg)
382
+ end
383
+
384
+ case response
385
+ when FindAllRoleNamesResponse
386
+ return response.out
387
+ when ObjectNotFoundException
388
+ return {}
389
+ else
390
+ raise AuthenticationException, response
391
+ end
392
+ end
393
+
394
+ ##
395
+ # Add Role
396
+ def self.add_role(name, description, is_active)
397
+ response = authenticated_connection do
398
+ role = SOAPRole.new(nil, is_active, nil, nil, description, nil, nil, nil, name)
399
+ arg = AddRole.new(@@application_token, role)
400
+ server.addRole(arg)
401
+ end
402
+
403
+ case response
404
+ when AddRoleResponse
405
+ return true
406
+ when ObjectNotFoundException
407
+ return AuthenticationObjectNotFoundException
408
+ else
409
+ raise AuthenticationException, response
410
+ end
411
+ end
412
+
413
+ ##
414
+ # Add Principal to Role
415
+ def self.add_principal_to_role(username, role)
416
+ response = authenticated_connection do
417
+ arg = AddPrincipalToRole.new(@@application_token, username, role)
418
+ #raise arg.to_yaml
419
+ server.addPrincipalToRole(arg)
420
+ end
421
+
422
+ case response
423
+ when AddPrincipalToRoleResponse
424
+ return true
425
+ when ObjectNotFoundException
426
+ return AuthenticationObjectNotFoundException
427
+ else
428
+ raise AuthenticationException, response
429
+ end
430
+ end
431
+
432
+ ##
433
+ # Remove Principal form Role
434
+ def self.remove_principal_from_role(username, role)
435
+ response = authenticated_connection do
436
+ arg = RemovePrincipalFromRole.new(@@application_token, username, role)
437
+ server.removePrincipalFromRole(arg)
438
+ end
439
+
440
+ case response
441
+ when RemovePrincipalFromRoleResponse
442
+ return true
443
+ when ObjectNotFoundException
444
+ return AuthenticationObjectNotFoundException
445
+ else
446
+ raise AuthenticationException, response
447
+ end
448
+ end
449
+
450
+ ##
451
+ # Is Role Member
452
+ def self.is_role_member(username, role)
453
+ response = authenticated_connection do
454
+ arg = IsRoleMember.new(@@application_token, username, role )
455
+ server.isRoleMember(arg)
456
+ end
457
+
458
+ case response
459
+ when IsRoleMemberResponse
460
+ return response.out
461
+ when ObjectNotFoundException
462
+ return AuthenticationObjectNotFoundException
463
+ else
464
+ raise AuthenticationException, response
465
+ end
466
+ end
467
+
468
+
469
+
470
+ ##
471
+ # Remove Role
472
+ def self.remove_role(role)
473
+ response = authenticated_connection do
474
+ arg = RemoveRole.new(@@application_token, role)
475
+ server.removeRole(arg)
476
+ end
477
+
478
+ case response
479
+ when RemoveRoleResponse
480
+ return true
481
+ when ObjectNotFoundException
482
+ return AuthenticationObjectNotFoundException
483
+ else
484
+ raise AuthenticationException, response
485
+ end
486
+ end
487
+
488
+ ##
489
+ # Is Group Member
490
+ def self.is_group_member(username, group)
491
+ response = authenticated_connection do
492
+ arg = IsGroupMember.new(@@application_token, group, username )
493
+ server.isGroupMember(arg)
494
+ end
495
+
496
+ case response
497
+ when IsGroupMemberResponse
498
+ return response.out
499
+ when ObjectNotFoundException
500
+ return AuthenticationObjectNotFoundException
501
+ else
502
+ raise AuthenticationException, response
503
+ end
504
+ end
505
+
506
+ def self.find_all_group_names
507
+ response = authenticated_connection do
508
+ arg = FindAllGroupNames.new(@@application_token)
509
+ server.findAllGroupNames(arg)
510
+ end
511
+
512
+ case response
513
+ when FindAllGroupNamesResponse
514
+ return response.out
515
+ when ObjectNotFoundException
516
+ return AuthenticationObjectNotFoundException
517
+ else
518
+ raise AuthenticationException, response
519
+ end
520
+ end
521
+
522
+ def self.find_all_groups_for_principal(username)
523
+ response = authenticated_connection do
524
+ arg = FindGroupMemberships.new(@@application_token, username)
525
+ server.findGroupMemberships(arg)
526
+ end
527
+
528
+ case response
529
+ when FindGroupMembershipsResponse
530
+ return response.out
531
+ when ObjectNotFoundException
532
+ return AuthenticationObjectNotFoundException
533
+ else
534
+ raise AuthenticationException, response
535
+ end
536
+ end
537
+
538
+ def self.get_group(name)
539
+ response = authenticated_connection do
540
+ arg = FindGroupByName.new(@@application_token, name)
541
+ server.findGroupByName(arg)
542
+ end
543
+
544
+ case response
545
+ when FindGroupByNameResponse
546
+ return response.out
547
+ when ObjectNotFoundException
548
+ return AuthenticationObjectNotFoundException
549
+ else
550
+ raise AuthenticationException, response
551
+ end
552
+ end
553
+
554
+ private
555
+
556
+ # Parse the user
557
+ def self.parse_principal(rp)
558
+ p = {}
559
+ p[:id] = rp.iD
560
+ p[:active] = rp.active
561
+ p[:conception] = rp.conception
562
+ p[:description] = rp.description
563
+ p[:directoryID] = rp.directoryId
564
+ p[:lastModified] = rp.lastModified
565
+ p[:name] = rp.name
566
+
567
+ p[:attributes] = {}
568
+
569
+ rp.attributes.each do |attr|
570
+ case attr.values.size
571
+ when 0
572
+ p[:attributes][attr.name.to_sym] = nil
573
+ when 1
574
+ p[:attributes][attr.name.to_sym] = attr.values[0]
575
+ else
576
+ p[:attributes][attr.name.to_sym] = attr.values.to_a
577
+ end
578
+ end
579
+
580
+ return p
581
+ end
582
+
583
+ # Create ArrayOfValidationFactor from a ruby key=>value hash
584
+ def self.helper_validation_factors(validation_factors = {})
585
+ aovf = ArrayOfValidationFactor.new
586
+ validation_factors.each { |name,value| aovf << ValidationFactor.new(name, value)}
587
+ end
588
+
589
+ # Has the application been authenticated?
590
+ def self.application_auth_check
591
+ authenticate_application if @@application_token.nil?
592
+ end
593
+
594
+ # Shorthand for getting the security server object
595
+ def self.server
596
+ @@server ||= SecurityServerPortType.new(@@crowd_url)
597
+ end
598
+
599
+ # Wrapper for catching common exceptions. Also allows the application a chance
600
+ # to re-authenticate if the token is invalid.
601
+ def self.authenticated_connection
602
+ raise ArgumentError unless block_given?
603
+
604
+ application_auth_check
605
+
606
+ response = yield
607
+ rescue AuthenticationException => e
608
+ # Push the response into the exception message
609
+ raise AuthenticationException, e
610
+ rescue Errno::ECONNREFUSED => e
611
+ raise AuthenticationConnectionException, e
612
+ rescue ::SOAP::FaultError => e
613
+ # We'll retry once more on any fault.
614
+ begin
615
+ authenticate_application
616
+ response = yield
617
+ rescue AuthenticationException => e
618
+ raise AuthenticationException, e
619
+ rescue Errno::ECONNREFUSED => e
620
+ raise AuthenticationConnectionException, e
621
+ rescue Exception => e
622
+ raise AuthenticationException, e
623
+ end
624
+ rescue Exception => e
625
+ raise AuthenticationException, e
626
+ ensure
627
+ if response.is_a?(InvalidAuthorizationTokenException)
628
+ authenticate_application
629
+ response = yield
630
+ end
631
+ response
632
+ end
633
+
634
+ ##
635
+ # Returns the domain configured in Crowd or null if no domain has been set.
636
+ #
637
+ # *Deprecated:* This method has been superceded by get_cookie_config.
638
+ def self.get_domain
639
+ response = authenticated_connection do
640
+ arg = GetDomain.new(@@application_token)
641
+ server.getDomain(arg)
642
+ end
643
+
644
+ case response
645
+ when GetDomainResponse
646
+ return response.out
647
+ else
648
+ raise AuthenticationException, response
649
+ end
650
+ end
651
+
652
+ ##
653
+ # Updates the password credential for a principal who is in the application's assigned directory.
654
+ def self.update_principal_credential(principal, password)
655
+ response = authenticated_connection do
656
+ hash = Digest::SHA512.new.update(password).digest
657
+ cred = PasswordCredential.new(Base64::encode64(hash).gsub(/\n/, ''))
658
+ arg = UpdatePrincipalCredential.new(@@application_token, principal, cred)
659
+ server.updatePrincipalCredential(arg)
660
+ end
661
+
662
+ case response
663
+ when UpdatePrincipalCredentialResponse
664
+ return nil
665
+ else
666
+ raise AuthenticationException, response
667
+ end
668
+ end
669
+ end