rkerberos 0.2.1 → 0.2.3
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.
- checksums.yaml +4 -4
- data/CHANGES.md +28 -8
- data/MANIFEST.md +24 -16
- data/Rakefile +4 -1
- data/ext/rkerberos/ccache.c +98 -1
- data/ext/rkerberos/config.c +10 -7
- data/ext/rkerberos/context.c +6 -0
- data/ext/rkerberos/extconf.rb +16 -3
- data/ext/rkerberos/kadm5.c +97 -45
- data/ext/rkerberos/keytab.c +247 -106
- data/ext/rkerberos/keytab_entry.c +2 -1
- data/ext/rkerberos/principal.c +28 -0
- data/ext/rkerberos/rkerberos.c +91 -13
- data/rkerberos.gemspec +1 -1
- data/spec/config_spec.rb +29 -7
- data/spec/context_spec.rb +6 -7
- data/spec/credentials_cache_spec.rb +79 -12
- data/spec/kadm5_spec.rb +57 -16
- data/spec/krb5_keytab_spec.rb +200 -4
- data/spec/krb5_spec.rb +81 -9
- data/spec/policy_spec.rb +8 -7
- data/spec/principal_spec.rb +18 -0
- data/spec/spec_helper.rb +34 -0
- metadata +3 -2
data/spec/kadm5_spec.rb
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# spec/kadm5_spec.rb
|
|
2
2
|
# RSpec tests for Kerberos::Kadm5
|
|
3
3
|
|
|
4
|
-
require '
|
|
4
|
+
require 'spec_helper'
|
|
5
5
|
require 'socket'
|
|
6
6
|
|
|
7
|
-
RSpec.describe Kerberos::Kadm5 do
|
|
7
|
+
RSpec.describe 'Kerberos::Kadm5', :kadm5 do
|
|
8
|
+
let(:server){ Kerberos::Kadm5::Config.new.admin_server }
|
|
9
|
+
subject(:klass){ Kerberos::Kadm5 }
|
|
10
|
+
|
|
8
11
|
before(:all) do
|
|
9
|
-
@server = Kerberos::Kadm5::Config.new.admin_server
|
|
10
12
|
@host = Socket.gethostname
|
|
11
13
|
@user = ENV['KRB5_ADMIN_PRINCIPAL']
|
|
12
14
|
@pass = ENV['KRB5_ADMIN_PASSWORD']
|
|
@@ -23,40 +25,79 @@ RSpec.describe Kerberos::Kadm5 do
|
|
|
23
25
|
|
|
24
26
|
describe 'constructor' do
|
|
25
27
|
it 'responds to .new' do
|
|
26
|
-
expect(
|
|
28
|
+
expect(subject).to respond_to(:new)
|
|
27
29
|
end
|
|
28
30
|
it 'works with valid user and password' do
|
|
29
|
-
expect {
|
|
31
|
+
expect { subject.new(principal: user, password: pass) }.not_to raise_error
|
|
30
32
|
end
|
|
31
33
|
it 'works with valid service' do
|
|
32
34
|
expect {
|
|
33
|
-
|
|
35
|
+
subject.new(principal: user, password: pass, service: 'kadmin/admin')
|
|
34
36
|
}.not_to raise_error
|
|
35
37
|
end
|
|
36
38
|
it 'only accepts a hash argument' do
|
|
37
|
-
expect {
|
|
38
|
-
expect {
|
|
39
|
+
expect { subject.new(user) }.to raise_error(TypeError)
|
|
40
|
+
expect { subject.new(1) }.to raise_error(TypeError)
|
|
39
41
|
end
|
|
40
42
|
it 'accepts a block and yields itself' do
|
|
41
|
-
expect {
|
|
42
|
-
|
|
43
|
+
expect { subject.new(principal: user, password: pass) {} }.not_to raise_error
|
|
44
|
+
subject.new(principal: user, password: pass) { |kadm5| expect(kadm5).to be_a(subject) }
|
|
43
45
|
end
|
|
44
46
|
it 'requires principal to be specified' do
|
|
45
|
-
expect {
|
|
47
|
+
expect { subject.new({}) }.to raise_error(ArgumentError)
|
|
46
48
|
end
|
|
47
49
|
it 'requires principal to be a string' do
|
|
48
|
-
expect {
|
|
50
|
+
expect { subject.new(principal: 1) }.to raise_error(TypeError)
|
|
49
51
|
end
|
|
50
52
|
it 'requires password to be a string' do
|
|
51
|
-
expect {
|
|
53
|
+
expect { subject.new(principal: user, password: 1) }.to raise_error(TypeError)
|
|
52
54
|
end
|
|
53
55
|
it 'requires keytab to be a string or boolean' do
|
|
54
|
-
expect {
|
|
56
|
+
expect { subject.new(principal: user, keytab: 1) }.to raise_error(TypeError)
|
|
55
57
|
end
|
|
56
58
|
it 'requires service to be a string' do
|
|
57
|
-
expect {
|
|
59
|
+
expect { subject.new(principal: user, password: pass, service: 1) }.to raise_error(TypeError)
|
|
58
60
|
end
|
|
59
61
|
end
|
|
60
62
|
|
|
61
|
-
#
|
|
63
|
+
describe '#get_privileges' do
|
|
64
|
+
before(:each) do
|
|
65
|
+
@kadm5 = subject.new(principal: user, password: pass)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
after(:each) do
|
|
69
|
+
@kadm5.close
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it 'returns an integer bitmask by default' do
|
|
73
|
+
result = @kadm5.get_privileges
|
|
74
|
+
expect(result).to be_a(Integer)
|
|
75
|
+
expect(result).not_to eq(0)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'returns an array of strings when passed a truthy argument' do
|
|
79
|
+
result = @kadm5.get_privileges(true)
|
|
80
|
+
expect(result).to be_a(Array)
|
|
81
|
+
expect(result).not_to be_empty
|
|
82
|
+
expect(result).to all(be_a(String))
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it 'only contains valid privilege names' do
|
|
86
|
+
result = @kadm5.get_privileges(true)
|
|
87
|
+
valid = %w[GET ADD MODIFY DELETE]
|
|
88
|
+
result.each do |priv|
|
|
89
|
+
expect(valid).to include(priv)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it 'does not contain UNKNOWN entries' do
|
|
94
|
+
result = @kadm5.get_privileges(true)
|
|
95
|
+
expect(result).not_to include('UNKNOWN')
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it 'includes GET for an admin principal' do
|
|
99
|
+
result = @kadm5.get_privileges(true)
|
|
100
|
+
expect(result).to include('GET')
|
|
101
|
+
end
|
|
102
|
+
end
|
|
62
103
|
end
|
data/spec/krb5_keytab_spec.rb
CHANGED
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
require 'rkerberos'
|
|
5
5
|
require 'tmpdir'
|
|
6
6
|
require 'fileutils'
|
|
7
|
-
require 'pty'
|
|
8
|
-
require 'expect'
|
|
9
|
-
|
|
10
7
|
|
|
8
|
+
unless File::ALT_SEPARATOR
|
|
9
|
+
require 'pty'
|
|
10
|
+
require 'expect'
|
|
11
|
+
end
|
|
11
12
|
|
|
12
|
-
RSpec.describe Kerberos::Krb5::Keytab do
|
|
13
|
+
RSpec.describe Kerberos::Krb5::Keytab, :kadm5 do
|
|
13
14
|
before(:all) do
|
|
14
15
|
@realm = Kerberos::Kadm5::Config.new.realm
|
|
15
16
|
@keytab_file = File.join(Dir.tmpdir, 'test.keytab')
|
|
@@ -29,6 +30,10 @@ RSpec.describe Kerberos::Krb5::Keytab do
|
|
|
29
30
|
end
|
|
30
31
|
end
|
|
31
32
|
|
|
33
|
+
after(:all) do
|
|
34
|
+
FileUtils.rm_f(@keytab_file)
|
|
35
|
+
end
|
|
36
|
+
|
|
32
37
|
subject(:keytab) { described_class.new }
|
|
33
38
|
|
|
34
39
|
describe 'constructor' do
|
|
@@ -43,4 +48,195 @@ RSpec.describe Kerberos::Krb5::Keytab do
|
|
|
43
48
|
}.to raise_error(Kerberos::Krb5::Keytab::Exception)
|
|
44
49
|
end
|
|
45
50
|
end
|
|
51
|
+
|
|
52
|
+
describe '#keytab_name and #keytab_type' do
|
|
53
|
+
it 'returns the underlying name and type strings' do
|
|
54
|
+
kt = described_class.new(@keytab_name)
|
|
55
|
+
expect(kt).to respond_to(:keytab_name)
|
|
56
|
+
expect(kt).to respond_to(:keytab_type)
|
|
57
|
+
|
|
58
|
+
expect(kt.keytab_name).to be_a(String)
|
|
59
|
+
expect(kt.keytab_type).to be_a(String)
|
|
60
|
+
|
|
61
|
+
# name should include the residual portion we supplied
|
|
62
|
+
expect(kt.keytab_name).to include(File.basename(@keytab_file))
|
|
63
|
+
# type should match the scheme
|
|
64
|
+
expect(kt.keytab_type.downcase).to eq("file")
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
describe '#close' do
|
|
69
|
+
it 'returns true' do
|
|
70
|
+
kt = described_class.new(@keytab_name)
|
|
71
|
+
expect(kt.close).to eq(true)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'can be called multiple times without error' do
|
|
75
|
+
kt = described_class.new(@keytab_name)
|
|
76
|
+
kt.close
|
|
77
|
+
expect { kt.close }.not_to raise_error
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it 'raises an error when calling keytab_name after close' do
|
|
81
|
+
kt = described_class.new(@keytab_name)
|
|
82
|
+
kt.close
|
|
83
|
+
expect { kt.keytab_name }.to raise_error(Kerberos::Krb5::Exception)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it 'raises an error when calling keytab_type after close' do
|
|
87
|
+
kt = described_class.new(@keytab_name)
|
|
88
|
+
kt.close
|
|
89
|
+
expect { kt.keytab_type }.to raise_error(Kerberos::Krb5::Exception)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it 'does not segfault when garbage collected after close' do
|
|
93
|
+
kt = described_class.new(@keytab_name)
|
|
94
|
+
kt.close
|
|
95
|
+
kt = nil
|
|
96
|
+
GC.start
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
describe '.foreach' do
|
|
101
|
+
it 'yields keytab entries for a valid keytab' do
|
|
102
|
+
entries = []
|
|
103
|
+
described_class.foreach(@keytab_name) { |entry| entries << entry }
|
|
104
|
+
expect(entries.length).to eq(2)
|
|
105
|
+
entries.each do |entry|
|
|
106
|
+
expect(entry).to be_a(Kerberos::Krb5::Keytab::Entry)
|
|
107
|
+
expect(entry.principal).to be_a(String)
|
|
108
|
+
expect(entry.principal).to match(/@#{Regexp.quote(@realm)}$/)
|
|
109
|
+
expect(entry.vno).to be_a(Integer)
|
|
110
|
+
expect(entry.timestamp).to be_a(Time)
|
|
111
|
+
expect(entry.key).to be_a(Integer)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it 'uses the default keytab when no name is provided' do
|
|
116
|
+
# The default keytab may not exist in the test container, so we
|
|
117
|
+
# just verify it attempts to use it (raises keytab-related error
|
|
118
|
+
# rather than ArgumentError or similar).
|
|
119
|
+
begin
|
|
120
|
+
described_class.foreach { |_| }
|
|
121
|
+
rescue Kerberos::Krb5::Exception
|
|
122
|
+
# Expected when default keytab is absent
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it 'raises an error for a non-existent keytab file' do
|
|
127
|
+
expect {
|
|
128
|
+
described_class.foreach("FILE:/no/such/keytab") { |_| }
|
|
129
|
+
}.to raise_error(Kerberos::Krb5::Exception)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
it 'raises an error for an invalid keytab type' do
|
|
133
|
+
expect {
|
|
134
|
+
described_class.foreach("BOGUS:/tmp/keytab") { |_| }
|
|
135
|
+
}.to raise_error(Kerberos::Krb5::Exception)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it 'does not leak resources when the block raises' do
|
|
139
|
+
expect {
|
|
140
|
+
described_class.foreach(@keytab_name) { |_| raise "boom" }
|
|
141
|
+
}.to raise_error(RuntimeError, "boom")
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
it 'does not leak resources when the block breaks' do
|
|
145
|
+
result = catch(:done) do
|
|
146
|
+
described_class.foreach(@keytab_name) { |_| throw :done, :escaped }
|
|
147
|
+
:completed
|
|
148
|
+
end
|
|
149
|
+
expect(result).to eq(:escaped)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
describe '#each' do
|
|
154
|
+
it 'yields keytab entries for a valid keytab' do
|
|
155
|
+
kt = described_class.new(@keytab_name)
|
|
156
|
+
entries = []
|
|
157
|
+
kt.each { |entry| entries << entry }
|
|
158
|
+
expect(entries.length).to eq(2)
|
|
159
|
+
entries.each do |entry|
|
|
160
|
+
expect(entry).to be_a(Kerberos::Krb5::Keytab::Entry)
|
|
161
|
+
expect(entry.principal).to be_a(String)
|
|
162
|
+
expect(entry.vno).to be_a(Integer)
|
|
163
|
+
expect(entry.timestamp).to be_a(Time)
|
|
164
|
+
expect(entry.key).to be_a(Integer)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
it 'does not leak resources when the block raises' do
|
|
169
|
+
kt = described_class.new(@keytab_name)
|
|
170
|
+
expect {
|
|
171
|
+
kt.each { |_| raise "boom" }
|
|
172
|
+
}.to raise_error(RuntimeError, "boom")
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
it 'does not leak resources when the block breaks' do
|
|
176
|
+
kt = described_class.new(@keytab_name)
|
|
177
|
+
result = catch(:done) do
|
|
178
|
+
kt.each { |_| throw :done, :escaped }
|
|
179
|
+
:completed
|
|
180
|
+
end
|
|
181
|
+
expect(result).to eq(:escaped)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
describe '#get_entry' do
|
|
186
|
+
it 'finds an entry by principal name' do
|
|
187
|
+
kt = described_class.new(@keytab_name)
|
|
188
|
+
entry = kt.get_entry("testuser1@#{@realm}")
|
|
189
|
+
expect(entry).to be_a(Kerberos::Krb5::Keytab::Entry)
|
|
190
|
+
expect(entry.principal).to eq("testuser1@#{@realm}")
|
|
191
|
+
expect(entry.vno).to eq(1)
|
|
192
|
+
expect(entry.timestamp).to be_a(Time)
|
|
193
|
+
expect(entry.key).to be_a(Integer)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
it 'finds an entry filtering by vno' do
|
|
197
|
+
kt = described_class.new(@keytab_name)
|
|
198
|
+
entry = kt.get_entry("testuser1@#{@realm}", 1)
|
|
199
|
+
expect(entry.principal).to eq("testuser1@#{@realm}")
|
|
200
|
+
expect(entry.vno).to eq(1)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
it 'finds an entry filtering by vno and enctype' do
|
|
204
|
+
kt = described_class.new(@keytab_name)
|
|
205
|
+
# aes128-cts-hmac-sha1-96 is enctype 17
|
|
206
|
+
entry = kt.get_entry("testuser1@#{@realm}", 1, 17)
|
|
207
|
+
expect(entry.principal).to eq("testuser1@#{@realm}")
|
|
208
|
+
expect(entry.vno).to eq(1)
|
|
209
|
+
expect(entry.key).to eq(17)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
it 'raises an error for a non-existent principal' do
|
|
213
|
+
kt = described_class.new(@keytab_name)
|
|
214
|
+
expect {
|
|
215
|
+
kt.get_entry("bogus@#{@realm}")
|
|
216
|
+
}.to raise_error(Kerberos::Krb5::Exception)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
it 'is aliased as find' do
|
|
220
|
+
kt = described_class.new(@keytab_name)
|
|
221
|
+
expect(kt.method(:find)).to eq(kt.method(:get_entry))
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
describe '#dup' do
|
|
226
|
+
it 'creates an independent handle referring to same keytab' do
|
|
227
|
+
kt1 = described_class.new(@keytab_name)
|
|
228
|
+
kt2 = kt1.dup
|
|
229
|
+
expect(kt2).to be_a(described_class)
|
|
230
|
+
expect(kt2.keytab_name).to eq(kt1.keytab_name)
|
|
231
|
+
|
|
232
|
+
# closing one should not invalidate the other
|
|
233
|
+
kt1.close
|
|
234
|
+
expect { kt2.keytab_name }.not_to raise_error
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
it 'clone is an alias for dup' do
|
|
238
|
+
kt = described_class.new(@keytab_name)
|
|
239
|
+
expect(kt.method(:clone)).to eq(kt.method(:dup))
|
|
240
|
+
end
|
|
241
|
+
end
|
|
46
242
|
end
|
data/spec/krb5_spec.rb
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
# spec/krb5_spec.rb
|
|
2
2
|
# RSpec tests for Kerberos::Krb5
|
|
3
3
|
|
|
4
|
-
require '
|
|
4
|
+
require 'spec_helper'
|
|
5
5
|
require 'open3'
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
|
|
7
|
+
unless File::ALT_SEPARATOR
|
|
8
|
+
require 'pty'
|
|
9
|
+
require 'expect'
|
|
10
|
+
end
|
|
8
11
|
|
|
9
12
|
RSpec.describe Kerberos::Krb5 do
|
|
10
13
|
before(:all) do
|
|
14
|
+
krb5_conf = RSpec.configuration.krb5_conf
|
|
11
15
|
@cache_found = true
|
|
12
16
|
Open3.popen3('klist') { |_, _, stderr| @cache_found = false unless stderr.gets.nil? }
|
|
13
|
-
@
|
|
14
|
-
@realm = IO.read(@krb5_conf).split("\n").grep(/default_realm/).first.split('=').last.lstrip.chomp
|
|
15
|
-
|
|
17
|
+
@realm = IO.read(krb5_conf).split("\n").grep(/default_realm/).first.split('=').last.lstrip.chomp
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
subject(:krb5) { described_class.new }
|
|
@@ -21,7 +23,7 @@ RSpec.describe Kerberos::Krb5 do
|
|
|
21
23
|
let(:service) { 'kadmin/admin' }
|
|
22
24
|
|
|
23
25
|
it 'has the correct version constant' do
|
|
24
|
-
expect(Kerberos::Krb5::VERSION).to eq('0.2.
|
|
26
|
+
expect(Kerberos::Krb5::VERSION).to eq('0.2.3')
|
|
25
27
|
end
|
|
26
28
|
|
|
27
29
|
it 'accepts a block and yields itself' do
|
|
@@ -48,7 +50,7 @@ RSpec.describe Kerberos::Krb5 do
|
|
|
48
50
|
end
|
|
49
51
|
end
|
|
50
52
|
|
|
51
|
-
describe '#verify_init_creds' do
|
|
53
|
+
describe '#verify_init_creds', :kadm5 do
|
|
52
54
|
# Some KDC setups may not correctly set the initial password during
|
|
53
55
|
# entrypoint startup; enforce it here via the admin API so the test is
|
|
54
56
|
# deterministic.
|
|
@@ -116,7 +118,77 @@ RSpec.describe Kerberos::Krb5 do
|
|
|
116
118
|
end
|
|
117
119
|
end
|
|
118
120
|
|
|
119
|
-
describe '#
|
|
121
|
+
describe '#change_password', :kadm5 do
|
|
122
|
+
before do
|
|
123
|
+
# Ensure testuser1 has a known password before each test.
|
|
124
|
+
Kerberos::Kadm5.new(
|
|
125
|
+
principal: ENV.fetch('KRB5_ADMIN_PRINCIPAL', 'admin/admin@EXAMPLE.COM'),
|
|
126
|
+
password: ENV.fetch('KRB5_ADMIN_PASSWORD', 'adminpassword')
|
|
127
|
+
) do |kadmin|
|
|
128
|
+
kadmin.set_password(user, 'changeme')
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
after do
|
|
133
|
+
# Reset to known password so later tests are not affected.
|
|
134
|
+
Kerberos::Kadm5.new(
|
|
135
|
+
principal: ENV.fetch('KRB5_ADMIN_PRINCIPAL', 'admin/admin@EXAMPLE.COM'),
|
|
136
|
+
password: ENV.fetch('KRB5_ADMIN_PASSWORD', 'adminpassword')
|
|
137
|
+
) do |kadmin|
|
|
138
|
+
kadmin.set_password(user, 'changeme')
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it 'responds to change_password' do
|
|
143
|
+
expect(krb5).to respond_to(:change_password)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it 'requires exactly two arguments' do
|
|
147
|
+
expect { krb5.change_password }.to raise_error(ArgumentError)
|
|
148
|
+
expect { krb5.change_password('old') }.to raise_error(ArgumentError)
|
|
149
|
+
expect { krb5.change_password('old', 'new', 'extra') }.to raise_error(ArgumentError)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
it 'raises if no principal has been established' do
|
|
153
|
+
expect { krb5.change_password('changeme', 'newpass1A!') }.to raise_error(Kerberos::Krb5::Exception, /no principal/)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it 'changes the password successfully' do
|
|
157
|
+
krb5.get_init_creds_password(user, 'changeme')
|
|
158
|
+
expect(krb5.change_password('changeme', 'Newpass99!')).to be true
|
|
159
|
+
# Verify we can authenticate with the new password
|
|
160
|
+
krb5_check = described_class.new
|
|
161
|
+
expect(krb5_check.get_init_creds_password(user, 'Newpass99!')).to be true
|
|
162
|
+
krb5_check.close
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it 'raises with a meaningful message when the old password is wrong' do
|
|
166
|
+
krb5.get_init_creds_password(user, 'changeme')
|
|
167
|
+
expect {
|
|
168
|
+
krb5.change_password('wrongpass', 'Newpass99!')
|
|
169
|
+
}.to raise_error(Kerberos::Krb5::Exception, /krb5_(get_init_creds_password|change_password)/)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
it 'requires string arguments' do
|
|
173
|
+
expect { krb5.change_password(1, 'new') }.to raise_error(TypeError)
|
|
174
|
+
expect { krb5.change_password('old', 1) }.to raise_error(TypeError)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
context 'when the KDC rejects the new password due to policy' do
|
|
178
|
+
let(:policy_user) { "policyuser@#{@realm}" }
|
|
179
|
+
# Password must satisfy strict_policy (minlength=8, minclasses=3).
|
|
180
|
+
let(:compliant_pw) { 'Changeme1!' }
|
|
181
|
+
|
|
182
|
+
it 'raises an exception with the KDC rejection reason' do
|
|
183
|
+
krb5.get_init_creds_password(policy_user, compliant_pw)
|
|
184
|
+
expect {
|
|
185
|
+
krb5.change_password(compliant_pw, 'a')
|
|
186
|
+
}.to raise_error(Kerberos::Krb5::Exception, /krb5_change_password/)
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
describe '#get_init_creds_keytab', :unix do
|
|
120
192
|
before(:each) do
|
|
121
193
|
@kt_file = File.join(Dir.tmpdir, "test_get_init_creds_#{Process.pid}_#{rand(10000)}.keytab")
|
|
122
194
|
|
data/spec/policy_spec.rb
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# spec/policy_spec.rb
|
|
2
2
|
# RSpec tests for Kerberos::Kadm5::Policy
|
|
3
3
|
|
|
4
|
-
require '
|
|
4
|
+
require 'spec_helper'
|
|
5
5
|
|
|
6
|
-
RSpec.describe Kerberos::Kadm5::Policy do
|
|
7
|
-
subject(:
|
|
6
|
+
RSpec.describe 'Kerberos::Kadm5::Policy', :kadm5 do
|
|
7
|
+
subject(:klass){ Kerberos::Kadm5::Policy }
|
|
8
|
+
let(:policy) { klass.new(name: 'test', max_life: 10000) }
|
|
8
9
|
|
|
9
10
|
describe 'name' do
|
|
10
11
|
it 'responds to policy' do
|
|
@@ -15,10 +16,10 @@ RSpec.describe Kerberos::Kadm5::Policy do
|
|
|
15
16
|
expect(policy.method(:name)).to eq(policy.method(:policy))
|
|
16
17
|
end
|
|
17
18
|
it 'must be a string' do
|
|
18
|
-
expect {
|
|
19
|
+
expect { klass.new(name: 1) }.to raise_error(TypeError)
|
|
19
20
|
end
|
|
20
21
|
it 'must be present' do
|
|
21
|
-
expect {
|
|
22
|
+
expect { klass.new(max_life: 10000) }.to raise_error(ArgumentError)
|
|
22
23
|
end
|
|
23
24
|
end
|
|
24
25
|
|
|
@@ -28,7 +29,7 @@ RSpec.describe Kerberos::Kadm5::Policy do
|
|
|
28
29
|
expect { policy.min_life }.not_to raise_error
|
|
29
30
|
end
|
|
30
31
|
it 'must be a number if not nil' do
|
|
31
|
-
expect {
|
|
32
|
+
expect { klass.new(name: 'test', min_life: 'test') }.to raise_error(TypeError)
|
|
32
33
|
end
|
|
33
34
|
end
|
|
34
35
|
|
|
@@ -38,7 +39,7 @@ RSpec.describe Kerberos::Kadm5::Policy do
|
|
|
38
39
|
expect { policy.max_life }.not_to raise_error
|
|
39
40
|
end
|
|
40
41
|
it 'must be a number if not nil' do
|
|
41
|
-
expect {
|
|
42
|
+
expect { klass.new(name: 'test', max_life: 'test') }.to raise_error(TypeError)
|
|
42
43
|
end
|
|
43
44
|
end
|
|
44
45
|
|
data/spec/principal_spec.rb
CHANGED
|
@@ -12,6 +12,24 @@ RSpec.describe Kerberos::Krb5::Principal do
|
|
|
12
12
|
expect { described_class.new(1) }.to raise_error(TypeError)
|
|
13
13
|
expect { described_class.new(true) }.to raise_error(TypeError)
|
|
14
14
|
end
|
|
15
|
+
|
|
16
|
+
it 'accepts an explicit nil argument' do
|
|
17
|
+
expect{ described_class.new(nil) }.not_to raise_error
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'works as expected with a nil argument to the constructor' do
|
|
21
|
+
expect(described_class.new(nil).principal).to be_nil
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe '#realm' do
|
|
26
|
+
it 'returns the expected value' do
|
|
27
|
+
expect(subject.realm).to eq('EXAMPLE.COM')
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'raises an error if the constructor argument was nil' do
|
|
31
|
+
expect{ described_class.new(nil).realm }.to raise_error(Kerberos::Krb5::Exception, /no principal/)
|
|
32
|
+
end
|
|
15
33
|
end
|
|
16
34
|
|
|
17
35
|
describe '#name' do
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'rkerberos'
|
|
2
|
+
require 'rspec'
|
|
3
|
+
require 'etc'
|
|
4
|
+
require 'tmpdir'
|
|
5
|
+
|
|
6
|
+
RSpec.configure do |config|
|
|
7
|
+
config.filter_run_excluding :kadm5 => true unless defined?(Kerberos::Kadm5::Config)
|
|
8
|
+
config.filter_run_excluding :unix => true if File::ALT_SEPARATOR
|
|
9
|
+
|
|
10
|
+
krb5_conf = ENV['KRB5_CONFIG']
|
|
11
|
+
krb5_cc_name = ENV['KRB5CCNAME']
|
|
12
|
+
login = Etc.getlogin || ENV['USER'] || (Etc.getpwuid(Process.uid).name rescue nil)
|
|
13
|
+
|
|
14
|
+
if File::ALT_SEPARATOR
|
|
15
|
+
krb5_conf ||= 'C:\\ProgramData\\MIT\\Kerberos5\\krb5.ini'
|
|
16
|
+
krb5_cc_name ||= File.join(ENV['USERPROFILE'], 'krb5cache')
|
|
17
|
+
else
|
|
18
|
+
krb5_conf ||= '/etc/krb5.conf'
|
|
19
|
+
krb5_cc_name ||= File.join(Dir.tmpdir, "krb5cc_#{Etc.getpwnam(login).uid}")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
config.add_setting :krb5_conf
|
|
23
|
+
config.krb5_conf = krb5_conf
|
|
24
|
+
|
|
25
|
+
config.add_setting :krb5_cc_name
|
|
26
|
+
config.krb5_cc_name = krb5_cc_name
|
|
27
|
+
|
|
28
|
+
config.add_setting :login
|
|
29
|
+
config.login = login
|
|
30
|
+
|
|
31
|
+
unless File.exist?(krb5_conf)
|
|
32
|
+
config.filter_run_excluding :krb5_config => true
|
|
33
|
+
end
|
|
34
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rkerberos
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel Berger
|
|
@@ -92,6 +92,7 @@ files:
|
|
|
92
92
|
- spec/krb5_spec.rb
|
|
93
93
|
- spec/policy_spec.rb
|
|
94
94
|
- spec/principal_spec.rb
|
|
95
|
+
- spec/spec_helper.rb
|
|
95
96
|
homepage: http://github.com/rkerberos/rkerberos
|
|
96
97
|
licenses:
|
|
97
98
|
- Artistic-2.0
|
|
@@ -119,7 +120,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
119
120
|
- !ruby/object:Gem::Version
|
|
120
121
|
version: '0'
|
|
121
122
|
requirements: []
|
|
122
|
-
rubygems_version: 4.0.
|
|
123
|
+
rubygems_version: 4.0.6
|
|
123
124
|
specification_version: 4
|
|
124
125
|
summary: A Ruby interface for the the Kerberos library
|
|
125
126
|
test_files:
|