rkerberos 0.2.2 → 0.3.0
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 +22 -0
- data/EXAMPLES.md +1015 -0
- data/MANIFEST.md +17 -12
- data/README.md +5 -5
- data/ext/rkerberos/ccache.c +105 -26
- data/ext/rkerberos/config.c +59 -10
- data/ext/rkerberos/context.c +76 -7
- data/ext/rkerberos/extconf.rb +16 -3
- data/ext/rkerberos/kadm5.c +301 -96
- data/ext/rkerberos/keytab.c +222 -66
- data/ext/rkerberos/keytab_entry.c +2 -1
- data/ext/rkerberos/policy.c +25 -23
- data/ext/rkerberos/principal.c +136 -10
- data/ext/rkerberos/rkerberos.c +244 -34
- data/ext/rkerberos/rkerberos.h +8 -0
- data/rkerberos.gemspec +10 -7
- data/spec/config_spec.rb +58 -3
- data/spec/context_spec.rb +57 -7
- data/spec/credentials_cache_spec.rb +106 -39
- data/spec/kadm5_spec.rb +292 -17
- data/spec/krb5_keytab_spec.rb +229 -24
- data/spec/krb5_spec.rb +142 -29
- data/spec/policy_spec.rb +12 -7
- data/spec/principal_spec.rb +102 -4
- data/spec/spec_helper.rb +34 -0
- metadata +10 -8
data/spec/kadm5_spec.rb
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
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
|
+
require 'fileutils'
|
|
7
|
+
|
|
8
|
+
RSpec.describe 'Kerberos::Kadm5', :kadm5 do
|
|
9
|
+
let(:server){ Kerberos::Kadm5::Config.new.admin_server }
|
|
10
|
+
subject(:klass){ Kerberos::Kadm5 }
|
|
6
11
|
|
|
7
|
-
RSpec.describe Kerberos::Kadm5 do
|
|
8
12
|
before(:all) do
|
|
9
|
-
@server = Kerberos::Kadm5::Config.new.admin_server
|
|
10
13
|
@host = Socket.gethostname
|
|
11
14
|
@user = ENV['KRB5_ADMIN_PRINCIPAL']
|
|
12
15
|
@pass = ENV['KRB5_ADMIN_PASSWORD']
|
|
@@ -23,44 +26,135 @@ RSpec.describe Kerberos::Kadm5 do
|
|
|
23
26
|
|
|
24
27
|
describe 'constructor' do
|
|
25
28
|
it 'responds to .new' do
|
|
26
|
-
expect(
|
|
29
|
+
expect(subject).to respond_to(:new)
|
|
27
30
|
end
|
|
31
|
+
|
|
28
32
|
it 'works with valid user and password' do
|
|
29
|
-
expect {
|
|
33
|
+
expect { subject.new(principal: user, password: pass) }.not_to raise_error
|
|
30
34
|
end
|
|
35
|
+
|
|
31
36
|
it 'works with valid service' do
|
|
32
37
|
expect {
|
|
33
|
-
|
|
38
|
+
subject.new(principal: user, password: pass, service: 'kadmin/admin')
|
|
34
39
|
}.not_to raise_error
|
|
35
40
|
end
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
expect {
|
|
41
|
+
|
|
42
|
+
it 'accepts only keyword arguments' do
|
|
43
|
+
expect { subject.new({principal: user, password: pass}) }.to raise_error(ArgumentError)
|
|
44
|
+
expect { subject.new(user) }.to raise_error(ArgumentError)
|
|
45
|
+
expect { subject.new(unknown: true) }.to raise_error(ArgumentError, /unknown keyword/)
|
|
39
46
|
end
|
|
47
|
+
|
|
40
48
|
it 'accepts a block and yields itself' do
|
|
41
|
-
expect {
|
|
42
|
-
|
|
49
|
+
expect { subject.new(principal: user, password: pass) {} }.not_to raise_error
|
|
50
|
+
subject.new(principal: user, password: pass) { |kadm5| expect(kadm5).to be_a(subject) }
|
|
43
51
|
end
|
|
52
|
+
|
|
44
53
|
it 'requires principal to be specified' do
|
|
45
|
-
expect {
|
|
54
|
+
expect { subject.new }.to raise_error(ArgumentError)
|
|
46
55
|
end
|
|
56
|
+
|
|
47
57
|
it 'requires principal to be a string' do
|
|
48
|
-
expect {
|
|
58
|
+
expect { subject.new(principal: 1, password: pass) }.to raise_error(TypeError)
|
|
49
59
|
end
|
|
60
|
+
|
|
50
61
|
it 'requires password to be a string' do
|
|
51
|
-
expect {
|
|
62
|
+
expect { subject.new(principal: user, password: 1) }.to raise_error(TypeError)
|
|
52
63
|
end
|
|
64
|
+
|
|
53
65
|
it 'requires keytab to be a string or boolean' do
|
|
54
|
-
expect {
|
|
66
|
+
expect { subject.new(principal: user, keytab: 1) }.to raise_error(TypeError)
|
|
55
67
|
end
|
|
68
|
+
|
|
56
69
|
it 'requires service to be a string' do
|
|
57
|
-
expect {
|
|
70
|
+
expect { subject.new(principal: user, password: pass, service: 1) }.to raise_error(TypeError)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'accepts a context keyword argument' do
|
|
74
|
+
ctx = Kerberos::Krb5::Context.new
|
|
75
|
+
expect { subject.new(principal: user, password: pass, context: ctx) }.not_to raise_error
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'raises TypeError for non-Context context argument' do
|
|
79
|
+
expect { subject.new(principal: user, password: pass, context: "bad") }.to raise_error(TypeError)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it 'raises error for a closed context' do
|
|
83
|
+
ctx = Kerberos::Krb5::Context.new
|
|
84
|
+
ctx.close
|
|
85
|
+
expect { subject.new(principal: user, password: pass, context: ctx) }.to raise_error(Kerberos::Krb5::Exception)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
context 'with a credentials cache', :cache do
|
|
89
|
+
let(:krb5) { Kerberos::Krb5.new }
|
|
90
|
+
let(:cache_path) { "/tmp/test_kadm5_ccache_#{Process.pid}" }
|
|
91
|
+
let(:cache_name) { "FILE:#{cache_path}" }
|
|
92
|
+
let(:cache) { Kerberos::Krb5::CredentialsCache.new(cache_name: cache_name) }
|
|
93
|
+
|
|
94
|
+
before(:each) do
|
|
95
|
+
krb5.get_init_creds_password(
|
|
96
|
+
principal: user,
|
|
97
|
+
password: pass,
|
|
98
|
+
service: 'kadmin/admin',
|
|
99
|
+
ccache: cache
|
|
100
|
+
)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
after(:each) do
|
|
104
|
+
cache.close rescue nil
|
|
105
|
+
krb5.close rescue nil
|
|
106
|
+
FileUtils.rm_f([cache_path, "#{cache_path}.lock"])
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it 'works with a populated credentials cache' do
|
|
110
|
+
expect { subject.new(principal: user, ccache: cache) }.not_to raise_error
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it 'returns a Kadm5 object' do
|
|
114
|
+
kadm5 = subject.new(principal: user, ccache: cache)
|
|
115
|
+
expect(kadm5).to be_a(subject)
|
|
116
|
+
kadm5.close
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it 'can perform operations after ccache authentication' do
|
|
120
|
+
kadm5 = subject.new(principal: user, ccache: cache)
|
|
121
|
+
privs = kadm5.get_privileges
|
|
122
|
+
expect(privs).to be_a(Integer)
|
|
123
|
+
expect(privs).not_to eq(0)
|
|
124
|
+
kadm5.close
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it 'raises TypeError if ccache is not a CredentialsCache' do
|
|
128
|
+
expect {
|
|
129
|
+
subject.new(principal: user, ccache: "not_a_ccache")
|
|
130
|
+
}.to raise_error(TypeError)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it 'raises an error for a destroyed credentials cache' do
|
|
134
|
+
dead_ccache = Kerberos::Krb5::CredentialsCache.new
|
|
135
|
+
dead_ccache.destroy
|
|
136
|
+
expect {
|
|
137
|
+
subject.new(principal: user, ccache: dead_ccache)
|
|
138
|
+
}.to raise_error(Kerberos::Krb5::Exception)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it 'raises ArgumentError when both password and ccache are given' do
|
|
142
|
+
expect {
|
|
143
|
+
subject.new(principal: user, password: pass, ccache: cache)
|
|
144
|
+
}.to raise_error(ArgumentError)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
it 'raises ArgumentError when both keytab and ccache are given' do
|
|
148
|
+
expect {
|
|
149
|
+
subject.new(principal: user, keytab: true, ccache: cache)
|
|
150
|
+
}.to raise_error(ArgumentError)
|
|
151
|
+
end
|
|
58
152
|
end
|
|
59
153
|
end
|
|
60
154
|
|
|
61
155
|
describe '#get_privileges' do
|
|
62
156
|
before(:each) do
|
|
63
|
-
@kadm5 =
|
|
157
|
+
@kadm5 = subject.new(principal: user, password: pass)
|
|
64
158
|
end
|
|
65
159
|
|
|
66
160
|
after(:each) do
|
|
@@ -98,4 +192,185 @@ RSpec.describe Kerberos::Kadm5 do
|
|
|
98
192
|
expect(result).to include('GET')
|
|
99
193
|
end
|
|
100
194
|
end
|
|
195
|
+
|
|
196
|
+
describe '#create_policy' do
|
|
197
|
+
before(:each) do
|
|
198
|
+
@kadm5 = subject.new(principal: user, password: pass)
|
|
199
|
+
@policy_name = "keyword_policy_#{Process.pid}"
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
after(:each) do
|
|
203
|
+
@kadm5.delete_policy(@policy_name) rescue nil
|
|
204
|
+
@kadm5.close
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
it 'accepts policy attributes as keywords' do
|
|
208
|
+
expect {
|
|
209
|
+
@kadm5.create_policy(name: @policy_name, min_length: 8)
|
|
210
|
+
}.not_to raise_error
|
|
211
|
+
expect(@kadm5.get_policy(@policy_name).min_length).to eq(8)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
it 'accepts a Policy object positionally' do
|
|
215
|
+
policy = Kerberos::Kadm5::Policy.new(name: @policy_name, min_length: 9)
|
|
216
|
+
expect { @kadm5.create_policy(policy) }.not_to raise_error
|
|
217
|
+
expect(@kadm5.get_policy(@policy_name).min_length).to eq(9)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
it 'rejects positional option hashes and unknown keywords' do
|
|
221
|
+
expect { @kadm5.create_policy({name: @policy_name}) }.to raise_error(TypeError)
|
|
222
|
+
expect {
|
|
223
|
+
@kadm5.create_policy(name: @policy_name, unknown: true)
|
|
224
|
+
}.to raise_error(ArgumentError, /unknown keyword/)
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
describe '#create_principal' do
|
|
229
|
+
before(:each) do
|
|
230
|
+
@kadm5 = subject.new(principal: user, password: pass)
|
|
231
|
+
@realm = Kerberos::Kadm5::Config.new.realm
|
|
232
|
+
@created = []
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
after(:each) do
|
|
236
|
+
@created.each do |name|
|
|
237
|
+
@kadm5.delete_principal(name) rescue nil
|
|
238
|
+
end
|
|
239
|
+
@kadm5.close
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
it 'creates a principal with name: and password:' do
|
|
243
|
+
pname = "create_test1@#{@realm}"
|
|
244
|
+
@kadm5.create_principal(name: pname, password: 'Test1234!')
|
|
245
|
+
@created << pname
|
|
246
|
+
p = @kadm5.find_principal(pname)
|
|
247
|
+
expect(p).to be_a(Kerberos::Krb5::Principal)
|
|
248
|
+
expect(p.principal).to eq(pname)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
it 'returns self for chaining' do
|
|
252
|
+
pname = "create_test2@#{@realm}"
|
|
253
|
+
result = @kadm5.create_principal(name: pname, password: 'Test1234!')
|
|
254
|
+
@created << pname
|
|
255
|
+
expect(result).to equal(@kadm5)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
it 'accepts a nil db_args option' do
|
|
259
|
+
pname = "create_test3@#{@realm}"
|
|
260
|
+
expect {
|
|
261
|
+
@kadm5.create_principal(name: pname, password: 'Test1234!', db_args: nil)
|
|
262
|
+
}.not_to raise_error
|
|
263
|
+
@created << pname
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
it 'raises ArgumentError when password: is missing' do
|
|
267
|
+
expect {
|
|
268
|
+
@kadm5.create_principal(name: "create_nopass@#{@realm}")
|
|
269
|
+
}.to raise_error(ArgumentError)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
it 'raises ArgumentError when neither name: nor principal: is given' do
|
|
273
|
+
expect {
|
|
274
|
+
@kadm5.create_principal(password: 'Test1234!')
|
|
275
|
+
}.to raise_error(ArgumentError)
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
it 'raises ArgumentError with no arguments' do
|
|
279
|
+
expect {
|
|
280
|
+
@kadm5.create_principal
|
|
281
|
+
}.to raise_error(ArgumentError)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
it 'rejects positional arguments and unknown keywords' do
|
|
285
|
+
expect {
|
|
286
|
+
@kadm5.create_principal("positional@#{@realm}", 'Test1234!')
|
|
287
|
+
}.to raise_error(ArgumentError)
|
|
288
|
+
expect {
|
|
289
|
+
@kadm5.create_principal(name: "unknown_kw@#{@realm}", password: 'Test1234!', unknown: true)
|
|
290
|
+
}.to raise_error(ArgumentError, /unknown keyword/)
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
it 'raises ArgumentError when both name: and principal: are given' do
|
|
294
|
+
p = Kerberos::Krb5::Principal.new(name: "create_both@#{@realm}")
|
|
295
|
+
expect {
|
|
296
|
+
@kadm5.create_principal(name: "create_both@#{@realm}", principal: p, password: 'Test1234!')
|
|
297
|
+
}.to raise_error(ArgumentError)
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
it 'raises an error for a duplicate principal' do
|
|
301
|
+
pname = "create_dup@#{@realm}"
|
|
302
|
+
@kadm5.create_principal(name: pname, password: 'Test1234!')
|
|
303
|
+
@created << pname
|
|
304
|
+
expect {
|
|
305
|
+
@kadm5.create_principal(name: pname, password: 'Test1234!')
|
|
306
|
+
}.to raise_error(Kerberos::Kadm5::Exception)
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
context 'with a Principal object' do
|
|
310
|
+
it 'creates a principal from a Principal object' do
|
|
311
|
+
pname = "create_obj1@#{@realm}"
|
|
312
|
+
p = Kerberos::Krb5::Principal.new(name: pname)
|
|
313
|
+
@kadm5.create_principal(principal: p, password: 'Test1234!')
|
|
314
|
+
@created << pname
|
|
315
|
+
found = @kadm5.find_principal(pname)
|
|
316
|
+
expect(found).to be_a(Kerberos::Krb5::Principal)
|
|
317
|
+
expect(found.principal).to eq(pname)
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
it 'raises TypeError if principal: is not a Principal object' do
|
|
321
|
+
expect {
|
|
322
|
+
@kadm5.create_principal(principal: "notaobj", password: 'Test1234!')
|
|
323
|
+
}.to raise_error(TypeError)
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
it 'raises ArgumentError if the principal has no name' do
|
|
327
|
+
p = Kerberos::Krb5::Principal.new
|
|
328
|
+
expect {
|
|
329
|
+
@kadm5.create_principal(principal: p, password: 'Test1234!')
|
|
330
|
+
}.to raise_error(ArgumentError)
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
it 'forwards the policy attribute' do
|
|
334
|
+
pname = "create_pol@#{@realm}"
|
|
335
|
+
p = Kerberos::Krb5::Principal.new(name: pname)
|
|
336
|
+
p.policy = 'strict_policy'
|
|
337
|
+
@kadm5.create_principal(principal: p, password: 'Changeme1!')
|
|
338
|
+
@created << pname
|
|
339
|
+
found = @kadm5.find_principal(pname)
|
|
340
|
+
expect(found.policy).to eq('strict_policy')
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
it 'forwards expire_time' do
|
|
344
|
+
pname = "create_exp@#{@realm}"
|
|
345
|
+
p = Kerberos::Krb5::Principal.new(name: pname)
|
|
346
|
+
future = Time.now + 86400 * 365
|
|
347
|
+
p.expire_time = future
|
|
348
|
+
@kadm5.create_principal(principal: p, password: 'Test1234!')
|
|
349
|
+
@created << pname
|
|
350
|
+
found = @kadm5.find_principal(pname)
|
|
351
|
+
# Allow a small delta for timestamp rounding
|
|
352
|
+
expect(found.expire_time.to_i).to be_within(1).of(future.to_i)
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
it 'forwards max_life' do
|
|
356
|
+
pname = "create_maxlife@#{@realm}"
|
|
357
|
+
p = Kerberos::Krb5::Principal.new(name: pname)
|
|
358
|
+
p.max_life = 7200
|
|
359
|
+
@kadm5.create_principal(principal: p, password: 'Test1234!')
|
|
360
|
+
@created << pname
|
|
361
|
+
found = @kadm5.find_principal(pname)
|
|
362
|
+
expect(found.max_life).to eq(7200)
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
it 'forwards max_renewable_life' do
|
|
366
|
+
pname = "create_maxrenew@#{@realm}"
|
|
367
|
+
p = Kerberos::Krb5::Principal.new(name: pname)
|
|
368
|
+
p.max_renewable_life = 14400
|
|
369
|
+
@kadm5.create_principal(principal: p, password: 'Test1234!')
|
|
370
|
+
@created << pname
|
|
371
|
+
found = @kadm5.find_principal(pname)
|
|
372
|
+
expect(found.max_renewable_life).to eq(14400)
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
end
|
|
101
376
|
end
|