aker 3.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. data/CHANGELOG.md +210 -0
  2. data/README.md +282 -0
  3. data/assets/aker/form/login.css +73 -0
  4. data/assets/aker/form/login.html.erb +44 -0
  5. data/lib/aker/authorities/automatic_access.rb +36 -0
  6. data/lib/aker/authorities/composite.rb +301 -0
  7. data/lib/aker/authorities/static.rb +283 -0
  8. data/lib/aker/authorities/support/find_sole_user.rb +24 -0
  9. data/lib/aker/authorities/support.rb +9 -0
  10. data/lib/aker/authorities.rb +46 -0
  11. data/lib/aker/cas/authority.rb +79 -0
  12. data/lib/aker/cas/configuration_helper.rb +85 -0
  13. data/lib/aker/cas/middleware/logout_responder.rb +49 -0
  14. data/lib/aker/cas/middleware/ticket_remover.rb +35 -0
  15. data/lib/aker/cas/middleware.rb +6 -0
  16. data/lib/aker/cas/proxy_mode.rb +108 -0
  17. data/lib/aker/cas/rack_proxy_callback.rb +188 -0
  18. data/lib/aker/cas/service_mode.rb +88 -0
  19. data/lib/aker/cas/service_url.rb +62 -0
  20. data/lib/aker/cas/user_ext.rb +64 -0
  21. data/lib/aker/cas.rb +31 -0
  22. data/lib/aker/central_parameters.rb +101 -0
  23. data/lib/aker/configuration.rb +534 -0
  24. data/lib/aker/deprecation.rb +105 -0
  25. data/lib/aker/form/custom_views_mode.rb +80 -0
  26. data/lib/aker/form/login_form_asset_provider.rb +56 -0
  27. data/lib/aker/form/middleware/custom_view_login_responder.rb +19 -0
  28. data/lib/aker/form/middleware/login_renderer.rb +72 -0
  29. data/lib/aker/form/middleware/login_responder.rb +71 -0
  30. data/lib/aker/form/middleware/logout_responder.rb +26 -0
  31. data/lib/aker/form/middleware.rb +10 -0
  32. data/lib/aker/form/mode.rb +118 -0
  33. data/lib/aker/form.rb +26 -0
  34. data/lib/aker/group.rb +67 -0
  35. data/lib/aker/group_membership.rb +162 -0
  36. data/lib/aker/ldap/authority.rb +392 -0
  37. data/lib/aker/ldap/user_ext.rb +19 -0
  38. data/lib/aker/ldap.rb +22 -0
  39. data/lib/aker/modes/base.rb +85 -0
  40. data/lib/aker/modes/http_basic.rb +100 -0
  41. data/lib/aker/modes/support/attempted_path.rb +22 -0
  42. data/lib/aker/modes/support/rfc_2617.rb +32 -0
  43. data/lib/aker/modes/support.rb +12 -0
  44. data/lib/aker/modes.rb +48 -0
  45. data/lib/aker/rack/authenticate.rb +37 -0
  46. data/lib/aker/rack/configuration_helper.rb +18 -0
  47. data/lib/aker/rack/default_logout_responder.rb +36 -0
  48. data/lib/aker/rack/environment_helper.rb +34 -0
  49. data/lib/aker/rack/facade.rb +102 -0
  50. data/lib/aker/rack/failure.rb +69 -0
  51. data/lib/aker/rack/logout.rb +63 -0
  52. data/lib/aker/rack/request_ext.rb +19 -0
  53. data/lib/aker/rack/session_timer.rb +95 -0
  54. data/lib/aker/rack/setup.rb +77 -0
  55. data/lib/aker/rack.rb +107 -0
  56. data/lib/aker/test/helpers.rb +22 -0
  57. data/lib/aker/test.rb +8 -0
  58. data/lib/aker/user.rb +231 -0
  59. data/lib/aker/version.rb +3 -0
  60. data/lib/aker.rb +51 -0
  61. data/spec/aker/aker-sample.yml +11 -0
  62. data/spec/aker/authorities/automatic_access_spec.rb +52 -0
  63. data/spec/aker/authorities/composite_spec.rb +488 -0
  64. data/spec/aker/authorities/nu-schema.jar +0 -0
  65. data/spec/aker/authorities/static_spec.rb +455 -0
  66. data/spec/aker/authorities/support/find_sole_user_spec.rb +33 -0
  67. data/spec/aker/authorities_spec.rb +16 -0
  68. data/spec/aker/cas/authority_spec.rb +106 -0
  69. data/spec/aker/cas/configuration_helper_spec.rb +92 -0
  70. data/spec/aker/cas/middleware/logout_responder_spec.rb +47 -0
  71. data/spec/aker/cas/middleware/ticket_remover_spec.rb +49 -0
  72. data/spec/aker/cas/proxy_mode_spec.rb +185 -0
  73. data/spec/aker/cas/rack_proxy_callback_spec.rb +190 -0
  74. data/spec/aker/cas/service_mode_spec.rb +122 -0
  75. data/spec/aker/cas/service_url_spec.rb +114 -0
  76. data/spec/aker/cas/user_ext_spec.rb +27 -0
  77. data/spec/aker/cas_spec.rb +19 -0
  78. data/spec/aker/central_parameters_spec.rb +44 -0
  79. data/spec/aker/configuration_spec.rb +465 -0
  80. data/spec/aker/deprecation_spec.rb +115 -0
  81. data/spec/aker/form/a_form_mode.rb +129 -0
  82. data/spec/aker/form/custom_views_mode_spec.rb +34 -0
  83. data/spec/aker/form/login_form_asset_provider_spec.rb +80 -0
  84. data/spec/aker/form/middleware/a_form_login_responder.rb +89 -0
  85. data/spec/aker/form/middleware/custom_view_login_responder_spec.rb +47 -0
  86. data/spec/aker/form/middleware/login_renderer_spec.rb +56 -0
  87. data/spec/aker/form/middleware/login_responder_spec.rb +34 -0
  88. data/spec/aker/form/middleware/logout_responder_spec.rb +55 -0
  89. data/spec/aker/form/mode_spec.rb +15 -0
  90. data/spec/aker/form_spec.rb +11 -0
  91. data/spec/aker/group_membership_spec.rb +208 -0
  92. data/spec/aker/group_spec.rb +66 -0
  93. data/spec/aker/ldap/authority_spec.rb +414 -0
  94. data/spec/aker/ldap/ldap-users.ldif +197 -0
  95. data/spec/aker/ldap_spec.rb +11 -0
  96. data/spec/aker/modes/a_aker_mode.rb +41 -0
  97. data/spec/aker/modes/http_basic_spec.rb +127 -0
  98. data/spec/aker/modes/support/attempted_path_spec.rb +32 -0
  99. data/spec/aker/modes_spec.rb +11 -0
  100. data/spec/aker/rack/authenticate_spec.rb +78 -0
  101. data/spec/aker/rack/default_logout_responder_spec.rb +67 -0
  102. data/spec/aker/rack/facade_spec.rb +154 -0
  103. data/spec/aker/rack/failure_spec.rb +151 -0
  104. data/spec/aker/rack/logout_spec.rb +63 -0
  105. data/spec/aker/rack/request_ext_spec.rb +29 -0
  106. data/spec/aker/rack/session_timer_spec.rb +134 -0
  107. data/spec/aker/rack/setup_spec.rb +87 -0
  108. data/spec/aker/rack_spec.rb +216 -0
  109. data/spec/aker/test/helpers_spec.rb +44 -0
  110. data/spec/aker/user_spec.rb +362 -0
  111. data/spec/aker_spec.rb +80 -0
  112. data/spec/deprecation_helper.rb +58 -0
  113. data/spec/java_helper.rb +5 -0
  114. data/spec/logger_helper.rb +17 -0
  115. data/spec/matchers.rb +31 -0
  116. data/spec/mock_builder.rb +25 -0
  117. data/spec/spec_helper.rb +52 -0
  118. metadata +265 -0
@@ -0,0 +1,208 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ module Aker
4
+ module GroupMembershipHelpers
5
+ def group(root, children=[], grandchildren=[])
6
+ req = Group.new(root)
7
+ children.each { |n| req << Group.new(n) }
8
+ if req.children.first
9
+ grandchildren.each { |n| req.children.first << Group.new(n) }
10
+ end
11
+ req
12
+ end
13
+
14
+ def unaffiliated(g)
15
+ GroupMembership.new(Group === g ? g : group(g))
16
+ end
17
+
18
+ def affiliated(g, *affiliate_ids)
19
+ unaffiliated(g).tap { |gm| gm.affiliate_ids = affiliate_ids }
20
+ end
21
+ end
22
+
23
+ describe GroupMemberships do
24
+ include GroupMembershipHelpers
25
+
26
+ it "exposes the related portal" do
27
+ GroupMemberships.new(:ENU).portal.should == :ENU
28
+ end
29
+
30
+ it "is enumerable" do
31
+ GroupMemberships.new(:NOTIS).should respond_to(:each)
32
+ GroupMemberships.new(:NOTIS).should respond_to(:detect)
33
+ end
34
+
35
+ it "is appendable" do
36
+ @gm = GroupMemberships.new(:NOTIS)
37
+ @gm << GroupMembership.new(Group.new('User'))
38
+ @gm.size.should == 1
39
+ end
40
+
41
+ def a_notis_user
42
+ GroupMemberships.new(:NOTIS).tap do |gm|
43
+ admin = group('Admin', %w(Manager Auditor), %w(User Observer))
44
+ manager, auditor = admin.children
45
+ user = manager.children.first
46
+ gm << unaffiliated(auditor) << affiliated(user, 42) << affiliated(manager, 28)
47
+ end
48
+ end
49
+
50
+ describe "#include?" do
51
+ before do
52
+ @gm = a_notis_user
53
+ end
54
+
55
+ describe "with an affiliate" do
56
+ it "includes unaffiliated groups" do
57
+ @gm.include?("Auditor", 28).should be_true
58
+ end
59
+
60
+ it "includes groups with a matching affiliate" do
61
+ @gm.include?("User", 42).should be_true
62
+ end
63
+
64
+ it "excludes groups without a matching affiliate" do
65
+ @gm.include?("Manager", 42).should be_false
66
+ end
67
+
68
+ it "excludes groups without a membership" do
69
+ @gm.include?("Admin", 42).should be_false
70
+ end
71
+
72
+ it "includes transitive (child) groups" do
73
+ @gm.include?("Observer", 28).should be_true
74
+ end
75
+
76
+ it "includes indirect affiliations for groups that also have a direct membership" do
77
+ @gm.include?("User", 28).should be_true
78
+ end
79
+
80
+ it "includes any a group membership as long as one affiliate matches" do
81
+ @gm.include?("Manager", 28, 42).should be_true
82
+ end
83
+ end
84
+
85
+ describe "without an affiliate" do
86
+ it "includes unaffiliated memberships" do
87
+ @gm.include?("Auditor").should be_true
88
+ end
89
+
90
+ it "includes affiliated memberships" do
91
+ @gm.include?("User").should be_true
92
+ end
93
+
94
+ it "excludes groups without a membership" do
95
+ @gm.include?("Admin").should be_false
96
+ end
97
+
98
+ it "includes transitive (child) groups" do
99
+ @gm.include?("Observer").should be_true
100
+ end
101
+ end
102
+
103
+ it "is case-insensitive" do
104
+ @gm.include?("auditoR").should be_true
105
+ end
106
+ end
107
+
108
+ describe "find" do
109
+ it "returns all matches" do
110
+ actual = a_notis_user.find("User")
111
+ actual.size.should == 2
112
+ abstract = actual.collect { |gm| [gm.group_name, gm.affiliate_ids] }.sort_by { |p| p[0] }
113
+ abstract[0].should == ["Manager", [28]]
114
+ abstract[1].should == ["User", [42]]
115
+ end
116
+
117
+ it "returns specific affiliate matches" do
118
+ actual = a_notis_user.find("Manager", 28, 42)
119
+ actual.size.should == 1
120
+ actual[0].group_name.should == "Manager"
121
+ actual[0].affiliate_ids.should == [28]
122
+ end
123
+
124
+ it "returns nothing for an unmatched affiliate" do
125
+ a_notis_user.find("Manager", 42).should == []
126
+ end
127
+
128
+ it "returns nothing for an unmatched group" do
129
+ a_notis_user.find("Admin").should == []
130
+ end
131
+ end
132
+
133
+ describe "serialization" do
134
+ before do
135
+ @user = a_notis_user
136
+ end
137
+
138
+ def marshal_and_unmarshal
139
+ serialized = Marshal.dump(@user)
140
+ Marshal.load(serialized)
141
+ end
142
+
143
+ it "works" do
144
+ lambda { marshal_and_unmarshal }.should_not raise_error
145
+ end
146
+
147
+ it "includes all memberships" do
148
+ marshal_and_unmarshal.size.should == 3
149
+ end
150
+
151
+ it "preserves the group relationships" do
152
+ # this is a membership that is indirectly included via the
153
+ # manager, 28 membership
154
+ marshal_and_unmarshal.include?("User", 28).should be_true
155
+ end
156
+
157
+ it "does not have relationships that did not exist in the original" do
158
+ marshal_and_unmarshal.include?("Admin").should be_false
159
+ end
160
+
161
+ it "preserves the portal" do
162
+ marshal_and_unmarshal.portal.should == :NOTIS
163
+ end
164
+ end
165
+ end
166
+
167
+ describe GroupMembership do
168
+ include GroupMembershipHelpers
169
+
170
+ it "exposes the group name" do
171
+ unaffiliated("foo").group_name.should == "foo"
172
+ end
173
+
174
+ it "exposes the group" do
175
+ unaffiliated("foo").group.class.should == Aker::Group
176
+ end
177
+
178
+ it "never has a nil affiliate list" do
179
+ unaffiliated("foo").affiliate_ids.should == []
180
+ end
181
+
182
+ describe "#include_affiliate?" do
183
+ describe "when not affiliate-specific" do
184
+ it "is always true" do
185
+ unaffiliated("all").include_affiliate?(42).should be_true
186
+ end
187
+ end
188
+
189
+ describe "when affiliate-specific" do
190
+ before do
191
+ @gm = affiliated("dc", 34, 42, "abc")
192
+ end
193
+
194
+ it "is true when the affiliate is present" do
195
+ @gm.include_affiliate?(42).should be_true
196
+ end
197
+
198
+ it "is false when the affiliate is not present" do
199
+ @gm.include_affiliate?(43).should be_false
200
+ end
201
+
202
+ it "accepts string affiliate IDs" do
203
+ @gm.include_affiliate?("abc").should be_true
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,66 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ module Aker
4
+ describe Group do
5
+ describe "serialization" do
6
+ before do
7
+ g = Group.new("Foo")
8
+ g << Group.new("Bar")
9
+ serialized = Marshal.dump(g)
10
+ @deserialized = Marshal.restore(serialized)
11
+ end
12
+
13
+ it "deserializes into an object with the right type" do
14
+ @deserialized.class.should == Group
15
+ end
16
+
17
+ it "deserializes into an object with the right name" do
18
+ @deserialized.name.should == "Foo"
19
+ end
20
+
21
+ it "deserializes children" do
22
+ @deserialized.should have(1).child
23
+ end
24
+
25
+ it "deserializes children with the right type" do
26
+ @deserialized.children.first.class.should == Group
27
+ end
28
+
29
+ it "deserializes children with the right names" do
30
+ @deserialized.children.first.name.should == "Bar"
31
+ end
32
+ end
33
+
34
+ describe "#include?" do
35
+ it "matches the exact name" do
36
+ Group.new("Foo").include?("Foo").should be_true
37
+ end
38
+
39
+ it "matches case-insensitively" do
40
+ Group.new("FOo").include?("fOo").should be_true
41
+ end
42
+
43
+ it "matches a symbol" do
44
+ Group.new("Foo").include?(:foo).should be_true
45
+ end
46
+
47
+ it "matches another group" do
48
+ Group.new("Bar").include?(Group.new("Bar")).should be_true
49
+ end
50
+
51
+ it "does not match a mismatched group" do
52
+ Group.new("Bar").include?(Group.new("Foo")).should be_false
53
+ end
54
+
55
+ it "does not match a mismatched name" do
56
+ Group.new("Bar").include?("Foo").should be_false
57
+ end
58
+
59
+ it "matches a child group" do
60
+ g = Group.new("Foo").tap { |f|
61
+ f << Group.new("Bar") << Group.new("Quux") << Group.new("Baz") }
62
+ g.include?("quux").should be_true
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,414 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ require 'ladle'
4
+
5
+ module Aker::Ldap
6
+ describe Authority do
7
+ before(:all) do
8
+ # Create server once; it will be started by specs or groups that
9
+ # need it.
10
+ @server = Ladle::Server.new(
11
+ :quiet => true,
12
+ :allow_anonymous => false,
13
+ :ldif => File.expand_path("../ldap-users.ldif", __FILE__),
14
+ :domain => "dc=northwestern,dc=edu",
15
+ :port => 3897 + port_offset,
16
+ :timeout => ENV['AKER_ENV'] ? 45 : 15 # the CI server is slow sometimes
17
+ )
18
+ end
19
+
20
+ after(:all) do
21
+ @server.stop if @server
22
+ end
23
+
24
+ # Minimal valid set
25
+ let(:params) {
26
+ { :server => '127.0.0.1' }
27
+ }
28
+
29
+ def actual
30
+ # creates an instance which could connect to the ladle-provided server
31
+ Authority.new({
32
+ # TODO: why doesn't the ladle-provided instance work in
33
+ # anonymous mode?
34
+ :user => 'uid=rms,ou=People,dc=northwestern,dc=edu',
35
+ :password => 'rhett',
36
+ :use_tls => false,
37
+ :port => @server.port
38
+ }.merge(params))
39
+ end
40
+
41
+ it "can be instantiated with a Aker::Configuration" do
42
+ p = params
43
+ Authority.new(Aker::Configuration.new { ldap_parameters p }).
44
+ server.should == "127.0.0.1"
45
+ end
46
+
47
+ it 'uses a specified name to find its parameters' do
48
+ p = params
49
+ Authority.new(Aker::Configuration.new { foo_parameters p }, :foo).
50
+ server.should == "127.0.0.1"
51
+ end
52
+
53
+ it "can be instantiated with a parameters hash" do
54
+ Authority.new(params).server.should == "127.0.0.1"
55
+ end
56
+
57
+ describe "at-construction parameter validation" do
58
+ it "requires the server name" do
59
+ params[:server] = nil
60
+ lambda { actual }.should raise_error("The server parameter is required for ldap.")
61
+ end
62
+
63
+ it "does not require a user name" do
64
+ params[:user] = nil
65
+ params[:password] = nil
66
+ lambda { actual }.should_not raise_error
67
+ end
68
+
69
+ it "does not require a password usually" do
70
+ params[:user] = nil
71
+ params[:password] = nil
72
+ lambda { actual }.should_not raise_error
73
+ end
74
+
75
+ it "requires a password if there is a username" do
76
+ params[:user] = 'uid=rms'
77
+ params[:password] = nil
78
+ lambda { actual }.should raise_error(
79
+ "For ldap, both user and password are required if one is set.")
80
+ end
81
+
82
+ it "does not require a port" do
83
+ params[:port] = nil
84
+ actual.port.should == 636
85
+ end
86
+ end
87
+
88
+ describe "#server" do
89
+ it "will be set from the :server parameter" do
90
+ actual.server.should == '127.0.0.1'
91
+ end
92
+ end
93
+
94
+ describe "Net::LDAP parameter set" do
95
+ before do
96
+ params[:user] = "uid=rms,ou=People,dc=northwestern,dc=edu"
97
+ params[:password] = "rhett"
98
+ end
99
+
100
+ it "uses the specified server" do
101
+ actual.ldap_parameters[:host].should == "127.0.0.1"
102
+ end
103
+
104
+ it "uses the specified port" do
105
+ params[:port] = 23443
106
+ actual.ldap_parameters[:port].should == 23443
107
+ end
108
+
109
+ it "uses the provided username" do
110
+ actual.ldap_parameters[:auth][:username].
111
+ should == "uid=rms,ou=People,dc=northwestern,dc=edu"
112
+ end
113
+
114
+ it "uses the provided password" do
115
+ actual.ldap_parameters[:auth][:password].should == "rhett"
116
+ end
117
+
118
+ it "uses the simple auth method when authentication is required" do
119
+ actual.ldap_parameters[:auth][:method].should == :simple
120
+ end
121
+
122
+ it 'uses anonymous auth when there are no connection credentials' do
123
+ params[:user] = nil
124
+ params[:password] = nil
125
+ actual.ldap_parameters[:auth][:method].should == :anonymous
126
+ end
127
+
128
+ it "uses TLS encryption by default" do
129
+ Authority.new(params).ldap_parameters[:encryption].should == :simple_tls
130
+ end
131
+
132
+ it "uses no encryption when so configured" do
133
+ params[:use_tls] = false
134
+ actual.ldap_parameters[:encryption].should be_nil
135
+ end
136
+ end
137
+
138
+ describe "#attribute_map" do
139
+ subject { Authority.new(params).attribute_map }
140
+
141
+ it 'has defaults' do
142
+ subject[:givenname].should == :first_name
143
+ end
144
+
145
+ it 'accepts overrides in the configuration' do
146
+ params[:attribute_map] = { :givenname => nil }
147
+ subject[:givenname].should be_nil
148
+ end
149
+
150
+ it 'accepts extensions in the configuration' do
151
+ params[:attribute_map] = { :hat_size => :title }
152
+ subject[:hat_size].should == :title
153
+ end
154
+ end
155
+
156
+ describe "#attribute_processors" do
157
+ subject { Authority.new(params).attribute_processors }
158
+ let(:user) { Aker::User.new('fred') }
159
+
160
+ def process(processor, entry)
161
+ processor.call(user, entry, lambda { |k| [*entry[k]].first })
162
+ end
163
+
164
+ describe 'a mapping processor' do
165
+ it 'exists' do
166
+ process(subject[:givenname], { :givenname => ['Fred'] })
167
+ user.first_name.should == 'Fred'
168
+ end
169
+
170
+ it 'works when no value is present' do
171
+ process(subject[:givenname], {})
172
+ user.first_name.should be_nil
173
+ end
174
+ end
175
+
176
+ it 'accepts overrides in the configuration' do
177
+ params[:attribute_processors] = {
178
+ :givenname => lambda { |user, entry, s| user.first_name = s[:givenname] * 2 }
179
+ }
180
+ process(subject[:givenname], { :givenname => ['Fred'] })
181
+ user.first_name.should == 'FredFred'
182
+ end
183
+
184
+ it 'accepts extensions in the configuration' do
185
+ params[:attribute_processors] = {
186
+ :ssn => lambda { |user, entry, s| user.identifiers[:ssn] = s[:ssn] }
187
+ }
188
+ process(subject[:ssn], { :ssn => ['123-08'] })
189
+ user.identifiers[:ssn].should == '123-08'
190
+ end
191
+ end
192
+
193
+ describe "a created user object" do
194
+ before do
195
+ @server.start
196
+ @user = actual.find_user('wakibbe')
197
+ end
198
+
199
+ it "has a username" do
200
+ @user.username.should == "wakibbe"
201
+ end
202
+
203
+ it "has a first name" do
204
+ @user.first_name.should == "Warren"
205
+ end
206
+
207
+ it "has a last name" do
208
+ @user.last_name.should == "Kibbe"
209
+ end
210
+
211
+ it "has a title" do
212
+ @user.title.should == "Research Associate Professor"
213
+ end
214
+
215
+ it "has a business phone" do
216
+ @user.business_phone.should == "+1 312 555 3229"
217
+ end
218
+
219
+ it "has a fax" do
220
+ actual.find_user('cbrinson').fax.should == "+1 847 555 0540"
221
+ end
222
+
223
+ it "has an e-mail address" do
224
+ @user.email.should == "wakibbe@northwestern.edu"
225
+ end
226
+
227
+ it 'uses custom mappings' do
228
+ params[:attribute_map] = { :employeenumber => :country }
229
+ actual.find_user('blc').country.should == '107'
230
+ end
231
+
232
+ it 'uses custom processors' do
233
+ params[:attribute_processors] = {
234
+ :addressprocessero => lambda { |user, entry, s|
235
+ user.address = s[:postaladdress].split('$').first
236
+ user.city = s[:postaladdress].split('$').last
237
+ }
238
+ }
239
+ actual.find_user('blc').address.should == 'RUBLOFF 750 N Lake Shore Dr'
240
+ actual.find_user('blc').city.should == 'CH'
241
+ end
242
+
243
+ it 'uses no deprecated methods' do
244
+ deprecation_message.should be_nil
245
+ end
246
+
247
+ describe "#ldap_attributes" do
248
+ it "has all values for multivalued attributes" do
249
+ @user.ldap_attributes[:ou].should == [
250
+ "NU Clinical and Translational Sciences Institute, Feinberg School of Medicine",
251
+ "Center for Genetic Medicine, Feinberg School of Medicine",
252
+ "People"
253
+ ]
254
+ end
255
+
256
+ it "uses downcased keys" do
257
+ @user.ldap_attributes[:givenname].should == ['Warren']
258
+ end
259
+ end
260
+
261
+ # ruby-net-ldap strings are weird and can't be serialized.
262
+ it "is serializable" do
263
+ ser = Marshal.dump(@user)
264
+ Marshal.load(ser).username.should == 'wakibbe'
265
+ end
266
+ end
267
+
268
+ # Net::LDAP::Filter isn't very testable
269
+ describe "#find_users" do
270
+ def found_usernames(*criteria)
271
+ @server.start
272
+ actual.find_users(*criteria).collect { |u| u.username }.sort
273
+ end
274
+
275
+ it "returns nothing for no criteria" do
276
+ found_usernames().should == []
277
+ end
278
+
279
+ describe "with a string" do
280
+ it "filters by username" do
281
+ found_usernames('wakibbe').should == %w(wakibbe)
282
+ end
283
+ end
284
+
285
+ describe "with Aker::User attributes" do
286
+ it "filters by username" do
287
+ found_usernames(:username => 'sbw').should == %w(sbw)
288
+ end
289
+
290
+ it "filters by first name" do
291
+ found_usernames(:first_name => 'Rhett').should == %w(rms)
292
+ end
293
+
294
+ it "filters by last name" do
295
+ found_usernames(:last_name => "Garcia").should == %w(ega)
296
+ end
297
+
298
+ it "filters by title" do
299
+ found_usernames(:title => 'Research Associate Professor').
300
+ should == %w(wakibbe)
301
+ end
302
+
303
+ it "filters by e-mail address" do
304
+ found_usernames(:email => 'b-chamberlain@northwestern.edu').
305
+ should == %w(blc)
306
+ end
307
+
308
+ it "filters by phone number" do
309
+ found_usernames(:business_phone => '+1 312 555 2324').should == %w(rms)
310
+ end
311
+
312
+ it "filters by fax" do
313
+ found_usernames(:fax => '+1 847 555 0540').should == %w(cbrinson)
314
+ end
315
+
316
+ it 'filters using a custom attribute mapping' do
317
+ params[:attribute_map] = { :displayname => :country }
318
+ found_usernames(:country => 'Warren A Kibbe').should == %w(wakibbe)
319
+ end
320
+ end
321
+
322
+ describe 'with explicitly mapped criteria attributes' do
323
+ it 'works' do
324
+ params[:criteria_map] = { :emplid => :employeenumber }
325
+ found_usernames(:emplid => '105').should == %w(rms)
326
+ end
327
+
328
+ it 'prefers the mapping that is explicitly for criteria' do
329
+ params[:criteria_map] = { :title => :employeenumber }
330
+ params[:attribute_map] = { :displayname => :title }
331
+ found_usernames(:title => '107').should == %w(blc)
332
+ end
333
+ end
334
+
335
+ describe "with other attributes" do
336
+ it "ignores them when they are in combination with known attributes" do
337
+ found_usernames(:username => 'rms', :frob => 'pulse').should == %w(rms)
338
+ end
339
+
340
+ it "returns nothing when they are alone" do
341
+ found_usernames(:frob => 'pulse').should == []
342
+ end
343
+ end
344
+
345
+ describe "with nil values" do
346
+ it "ignore those keys when in combination with non-nil values" do
347
+ found_usernames(:username => nil, :first_name => 'Sean').
348
+ should == %w(sbw)
349
+ end
350
+
351
+ it "returns nothing if they are alone" do
352
+ found_usernames(:title => nil).should == []
353
+ end
354
+ end
355
+
356
+ it "requires that all criteria match" do
357
+ found_usernames(:first_name => 'Warren', :last_name => 'Kibbe').
358
+ should == %w(wakibbe)
359
+ found_usernames(:first_name => 'L Catherine', :last_name => 'Kibbe').
360
+ should == []
361
+ end
362
+
363
+ describe "with multiple disjoint criteria" do
364
+ it "understands multiple usernames" do
365
+ found_usernames('wakibbe', 'cbrinson', 'stl667').should == %w(cbrinson wakibbe)
366
+ end
367
+
368
+ it "ORs multiples sets of criteria" do
369
+ found_usernames({ :first_name => 'L Catherine' }, { :last_name => 'Kibbe'}).
370
+ should == %w(cbrinson wakibbe)
371
+ end
372
+
373
+ it "handles a mix of hashes and usernames" do
374
+ found_usernames(
375
+ { :first_name => 'Brian' }, { :first_name => 'Sean' }, 'ega', 'sbw').
376
+ should == %w(blc ega sbw)
377
+ end
378
+ end
379
+ end
380
+
381
+ describe "credential validation" do
382
+ def login(username, password)
383
+ @server.start
384
+ actual.valid_credentials?(:user, username, password)
385
+ end
386
+
387
+ describe "when authentic" do
388
+ it "is not nil" do
389
+ login('wakibbe', 'warren').should_not be_nil
390
+ end
391
+
392
+ it "is filled out" do
393
+ login('rms', 'rhett').last_name.should == 'Sutphin'
394
+ end
395
+ end
396
+
397
+ it "is not valid for an unknown user" do
398
+ login('jp', 'foo').should be_nil
399
+ end
400
+
401
+ it "is not valid when not authentic" do
402
+ login('wakibbe', 'ekib').should be_nil
403
+ end
404
+
405
+ it "is not valid with a blank password" do
406
+ login('wakibbe', '').should be_nil
407
+ end
408
+
409
+ it "only handles :user credentials" do
410
+ actual.valid_credentials?(:retina_scan, 1701).should == :unsupported
411
+ end
412
+ end
413
+ end
414
+ end