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.
- data/CHANGELOG.md +210 -0
- data/README.md +282 -0
- data/assets/aker/form/login.css +73 -0
- data/assets/aker/form/login.html.erb +44 -0
- data/lib/aker/authorities/automatic_access.rb +36 -0
- data/lib/aker/authorities/composite.rb +301 -0
- data/lib/aker/authorities/static.rb +283 -0
- data/lib/aker/authorities/support/find_sole_user.rb +24 -0
- data/lib/aker/authorities/support.rb +9 -0
- data/lib/aker/authorities.rb +46 -0
- data/lib/aker/cas/authority.rb +79 -0
- data/lib/aker/cas/configuration_helper.rb +85 -0
- data/lib/aker/cas/middleware/logout_responder.rb +49 -0
- data/lib/aker/cas/middleware/ticket_remover.rb +35 -0
- data/lib/aker/cas/middleware.rb +6 -0
- data/lib/aker/cas/proxy_mode.rb +108 -0
- data/lib/aker/cas/rack_proxy_callback.rb +188 -0
- data/lib/aker/cas/service_mode.rb +88 -0
- data/lib/aker/cas/service_url.rb +62 -0
- data/lib/aker/cas/user_ext.rb +64 -0
- data/lib/aker/cas.rb +31 -0
- data/lib/aker/central_parameters.rb +101 -0
- data/lib/aker/configuration.rb +534 -0
- data/lib/aker/deprecation.rb +105 -0
- data/lib/aker/form/custom_views_mode.rb +80 -0
- data/lib/aker/form/login_form_asset_provider.rb +56 -0
- data/lib/aker/form/middleware/custom_view_login_responder.rb +19 -0
- data/lib/aker/form/middleware/login_renderer.rb +72 -0
- data/lib/aker/form/middleware/login_responder.rb +71 -0
- data/lib/aker/form/middleware/logout_responder.rb +26 -0
- data/lib/aker/form/middleware.rb +10 -0
- data/lib/aker/form/mode.rb +118 -0
- data/lib/aker/form.rb +26 -0
- data/lib/aker/group.rb +67 -0
- data/lib/aker/group_membership.rb +162 -0
- data/lib/aker/ldap/authority.rb +392 -0
- data/lib/aker/ldap/user_ext.rb +19 -0
- data/lib/aker/ldap.rb +22 -0
- data/lib/aker/modes/base.rb +85 -0
- data/lib/aker/modes/http_basic.rb +100 -0
- data/lib/aker/modes/support/attempted_path.rb +22 -0
- data/lib/aker/modes/support/rfc_2617.rb +32 -0
- data/lib/aker/modes/support.rb +12 -0
- data/lib/aker/modes.rb +48 -0
- data/lib/aker/rack/authenticate.rb +37 -0
- data/lib/aker/rack/configuration_helper.rb +18 -0
- data/lib/aker/rack/default_logout_responder.rb +36 -0
- data/lib/aker/rack/environment_helper.rb +34 -0
- data/lib/aker/rack/facade.rb +102 -0
- data/lib/aker/rack/failure.rb +69 -0
- data/lib/aker/rack/logout.rb +63 -0
- data/lib/aker/rack/request_ext.rb +19 -0
- data/lib/aker/rack/session_timer.rb +95 -0
- data/lib/aker/rack/setup.rb +77 -0
- data/lib/aker/rack.rb +107 -0
- data/lib/aker/test/helpers.rb +22 -0
- data/lib/aker/test.rb +8 -0
- data/lib/aker/user.rb +231 -0
- data/lib/aker/version.rb +3 -0
- data/lib/aker.rb +51 -0
- data/spec/aker/aker-sample.yml +11 -0
- data/spec/aker/authorities/automatic_access_spec.rb +52 -0
- data/spec/aker/authorities/composite_spec.rb +488 -0
- data/spec/aker/authorities/nu-schema.jar +0 -0
- data/spec/aker/authorities/static_spec.rb +455 -0
- data/spec/aker/authorities/support/find_sole_user_spec.rb +33 -0
- data/spec/aker/authorities_spec.rb +16 -0
- data/spec/aker/cas/authority_spec.rb +106 -0
- data/spec/aker/cas/configuration_helper_spec.rb +92 -0
- data/spec/aker/cas/middleware/logout_responder_spec.rb +47 -0
- data/spec/aker/cas/middleware/ticket_remover_spec.rb +49 -0
- data/spec/aker/cas/proxy_mode_spec.rb +185 -0
- data/spec/aker/cas/rack_proxy_callback_spec.rb +190 -0
- data/spec/aker/cas/service_mode_spec.rb +122 -0
- data/spec/aker/cas/service_url_spec.rb +114 -0
- data/spec/aker/cas/user_ext_spec.rb +27 -0
- data/spec/aker/cas_spec.rb +19 -0
- data/spec/aker/central_parameters_spec.rb +44 -0
- data/spec/aker/configuration_spec.rb +465 -0
- data/spec/aker/deprecation_spec.rb +115 -0
- data/spec/aker/form/a_form_mode.rb +129 -0
- data/spec/aker/form/custom_views_mode_spec.rb +34 -0
- data/spec/aker/form/login_form_asset_provider_spec.rb +80 -0
- data/spec/aker/form/middleware/a_form_login_responder.rb +89 -0
- data/spec/aker/form/middleware/custom_view_login_responder_spec.rb +47 -0
- data/spec/aker/form/middleware/login_renderer_spec.rb +56 -0
- data/spec/aker/form/middleware/login_responder_spec.rb +34 -0
- data/spec/aker/form/middleware/logout_responder_spec.rb +55 -0
- data/spec/aker/form/mode_spec.rb +15 -0
- data/spec/aker/form_spec.rb +11 -0
- data/spec/aker/group_membership_spec.rb +208 -0
- data/spec/aker/group_spec.rb +66 -0
- data/spec/aker/ldap/authority_spec.rb +414 -0
- data/spec/aker/ldap/ldap-users.ldif +197 -0
- data/spec/aker/ldap_spec.rb +11 -0
- data/spec/aker/modes/a_aker_mode.rb +41 -0
- data/spec/aker/modes/http_basic_spec.rb +127 -0
- data/spec/aker/modes/support/attempted_path_spec.rb +32 -0
- data/spec/aker/modes_spec.rb +11 -0
- data/spec/aker/rack/authenticate_spec.rb +78 -0
- data/spec/aker/rack/default_logout_responder_spec.rb +67 -0
- data/spec/aker/rack/facade_spec.rb +154 -0
- data/spec/aker/rack/failure_spec.rb +151 -0
- data/spec/aker/rack/logout_spec.rb +63 -0
- data/spec/aker/rack/request_ext_spec.rb +29 -0
- data/spec/aker/rack/session_timer_spec.rb +134 -0
- data/spec/aker/rack/setup_spec.rb +87 -0
- data/spec/aker/rack_spec.rb +216 -0
- data/spec/aker/test/helpers_spec.rb +44 -0
- data/spec/aker/user_spec.rb +362 -0
- data/spec/aker_spec.rb +80 -0
- data/spec/deprecation_helper.rb +58 -0
- data/spec/java_helper.rb +5 -0
- data/spec/logger_helper.rb +17 -0
- data/spec/matchers.rb +31 -0
- data/spec/mock_builder.rb +25 -0
- data/spec/spec_helper.rb +52 -0
- 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
|