aker 3.0.0.pre

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.
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