rkerberos 0.2.3 → 0.3.1

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/spec/kadm5_spec.rb CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  require 'spec_helper'
5
5
  require 'socket'
6
+ require 'fileutils'
6
7
 
7
8
  RSpec.describe 'Kerberos::Kadm5', :kadm5 do
8
9
  let(:server){ Kerberos::Kadm5::Config.new.admin_server }
@@ -27,37 +28,156 @@ RSpec.describe 'Kerberos::Kadm5', :kadm5 do
27
28
  it 'responds to .new' do
28
29
  expect(subject).to respond_to(:new)
29
30
  end
31
+
30
32
  it 'works with valid user and password' do
31
33
  expect { subject.new(principal: user, password: pass) }.not_to raise_error
32
34
  end
35
+
33
36
  it 'works with valid service' do
34
37
  expect {
35
38
  subject.new(principal: user, password: pass, service: 'kadmin/admin')
36
39
  }.not_to raise_error
37
40
  end
38
- it 'only accepts a hash argument' do
39
- expect { subject.new(user) }.to raise_error(TypeError)
40
- expect { subject.new(1) }.to raise_error(TypeError)
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/)
41
46
  end
47
+
42
48
  it 'accepts a block and yields itself' do
43
49
  expect { subject.new(principal: user, password: pass) {} }.not_to raise_error
44
50
  subject.new(principal: user, password: pass) { |kadm5| expect(kadm5).to be_a(subject) }
45
51
  end
52
+
46
53
  it 'requires principal to be specified' do
47
- expect { subject.new({}) }.to raise_error(ArgumentError)
54
+ expect { subject.new }.to raise_error(ArgumentError)
48
55
  end
56
+
49
57
  it 'requires principal to be a string' do
50
- expect { subject.new(principal: 1) }.to raise_error(TypeError)
58
+ expect { subject.new(principal: 1, password: pass) }.to raise_error(TypeError)
51
59
  end
60
+
52
61
  it 'requires password to be a string' do
53
62
  expect { subject.new(principal: user, password: 1) }.to raise_error(TypeError)
54
63
  end
64
+
55
65
  it 'requires keytab to be a string or boolean' do
56
66
  expect { subject.new(principal: user, keytab: 1) }.to raise_error(TypeError)
57
67
  end
68
+
69
+ it 'requires a principal when using a keytab' do
70
+ expect { subject.new(keytab: true) }.to raise_error(ArgumentError, /principal must be specified/)
71
+ end
72
+
58
73
  it 'requires service to be a string' do
59
74
  expect { subject.new(principal: user, password: pass, service: 1) }.to raise_error(TypeError)
60
75
  end
76
+
77
+ it 'accepts a context keyword argument' do
78
+ ctx = Kerberos::Krb5::Context.new
79
+ kadm5 = subject.new(principal: user, password: pass, context: ctx)
80
+ expect { ctx.close }.to raise_error(Kerberos::Krb5::Exception, /dependent wrapper/)
81
+ kadm5.close
82
+ expect { ctx.close }.not_to raise_error
83
+ end
84
+
85
+ it 'raises TypeError for non-Context context argument' do
86
+ expect { subject.new(principal: user, password: pass, context: "bad") }.to raise_error(TypeError)
87
+ end
88
+
89
+ it 'raises error for a closed context' do
90
+ ctx = Kerberos::Krb5::Context.new
91
+ ctx.close
92
+ expect { subject.new(principal: user, password: pass, context: ctx) }.to raise_error(Kerberos::Krb5::Exception)
93
+ end
94
+
95
+ context 'with a credentials cache', :cache do
96
+ let(:krb5) { Kerberos::Krb5.new }
97
+ let(:cache_path) { "/tmp/test_kadm5_ccache_#{Process.pid}" }
98
+ let(:cache_name) { "FILE:#{cache_path}" }
99
+ let(:cache) { Kerberos::Krb5::CredentialsCache.new(cache_name: cache_name) }
100
+
101
+ before(:each) do
102
+ krb5.get_init_creds_password(
103
+ principal: user,
104
+ password: pass,
105
+ service: 'kadmin/admin',
106
+ ccache: cache
107
+ )
108
+ end
109
+
110
+ after(:each) do
111
+ cache.close rescue nil
112
+ krb5.close rescue nil
113
+ FileUtils.rm_f([cache_path, "#{cache_path}.lock"])
114
+ end
115
+
116
+ it 'works with a populated credentials cache' do
117
+ expect { subject.new(principal: user, ccache: cache) }.not_to raise_error
118
+ end
119
+
120
+ it 'returns a Kadm5 object' do
121
+ kadm5 = subject.new(principal: user, ccache: cache)
122
+ expect(kadm5).to be_a(subject)
123
+ kadm5.close
124
+ end
125
+
126
+ it 'can perform operations after ccache authentication' do
127
+ kadm5 = subject.new(principal: user, ccache: cache)
128
+ privs = kadm5.get_privileges
129
+ expect(privs).to be_a(Integer)
130
+ expect(privs).not_to eq(0)
131
+ kadm5.close
132
+ end
133
+
134
+ it 'raises TypeError if ccache is not a CredentialsCache' do
135
+ expect {
136
+ subject.new(principal: user, ccache: "not_a_ccache")
137
+ }.to raise_error(TypeError)
138
+ end
139
+
140
+ it 'raises an error for a destroyed credentials cache' do
141
+ dead_ccache = Kerberos::Krb5::CredentialsCache.new
142
+ dead_ccache.destroy
143
+ expect {
144
+ subject.new(principal: user, ccache: dead_ccache)
145
+ }.to raise_error(Kerberos::Krb5::Exception)
146
+ end
147
+
148
+ it 'raises ArgumentError when both password and ccache are given' do
149
+ expect {
150
+ subject.new(principal: user, password: pass, ccache: cache)
151
+ }.to raise_error(ArgumentError)
152
+ end
153
+
154
+ it 'raises ArgumentError when both keytab and ccache are given' do
155
+ expect {
156
+ subject.new(principal: user, keytab: true, ccache: cache)
157
+ }.to raise_error(ArgumentError)
158
+ end
159
+ end
160
+ end
161
+
162
+ describe '#close' do
163
+ it 'makes all administrative operations fail safely' do
164
+ kadm5 = subject.new(principal: user, password: pass)
165
+ kadm5.close
166
+ policy = Kerberos::Kadm5::Policy.new(name: 'closed_policy')
167
+
168
+ operations = [
169
+ -> { kadm5.get_privileges },
170
+ -> { kadm5.get_principals },
171
+ -> { kadm5.get_policies },
172
+ -> { kadm5.create_policy(policy) },
173
+ -> { kadm5.modify_policy(policy) },
174
+ -> { kadm5.delete_policy('closed_policy') }
175
+ ]
176
+
177
+ operations.each do |operation|
178
+ expect(&operation).to raise_error(Kerberos::Kadm5::Exception, /no administrative context/)
179
+ end
180
+ end
61
181
  end
62
182
 
63
183
  describe '#get_privileges' do
@@ -100,4 +220,198 @@ RSpec.describe 'Kerberos::Kadm5', :kadm5 do
100
220
  expect(result).to include('GET')
101
221
  end
102
222
  end
223
+
224
+ describe '#create_policy' do
225
+ before(:each) do
226
+ @kadm5 = subject.new(principal: user, password: pass)
227
+ @policy_name = "keyword_policy_#{Process.pid}"
228
+ end
229
+
230
+ after(:each) do
231
+ @kadm5.delete_policy(@policy_name) rescue nil
232
+ @kadm5.close
233
+ end
234
+
235
+ it 'accepts policy attributes as keywords' do
236
+ expect {
237
+ @kadm5.create_policy(name: @policy_name, min_length: 8)
238
+ }.not_to raise_error
239
+ expect(@kadm5.get_policy(@policy_name).min_length).to eq(8)
240
+ end
241
+
242
+ it 'accepts a Policy object positionally' do
243
+ policy = Kerberos::Kadm5::Policy.new(name: @policy_name, min_length: 9)
244
+ expect { @kadm5.create_policy(policy) }.not_to raise_error
245
+ expect(@kadm5.get_policy(@policy_name).min_length).to eq(9)
246
+ end
247
+
248
+ it 'rejects positional option hashes and unknown keywords' do
249
+ expect { @kadm5.create_policy({name: @policy_name}) }.to raise_error(TypeError)
250
+ expect {
251
+ @kadm5.create_policy(name: @policy_name, unknown: true)
252
+ }.to raise_error(ArgumentError, /unknown keyword/)
253
+ end
254
+
255
+ it 'modifies values assigned through Policy writers' do
256
+ @kadm5.create_policy(name: @policy_name, min_length: 8, history_num: 1)
257
+ policy = @kadm5.get_policy(@policy_name)
258
+ policy.min_length = 12
259
+ policy.history_num = 3
260
+
261
+ expect(@kadm5.modify_policy(policy)).to equal(@kadm5)
262
+
263
+ modified = @kadm5.get_policy(@policy_name)
264
+ expect(modified.min_length).to eq(12)
265
+ expect(modified.history_num).to eq(3)
266
+ end
267
+ end
268
+
269
+ describe '#create_principal' do
270
+ before(:each) do
271
+ @kadm5 = subject.new(principal: user, password: pass)
272
+ @realm = Kerberos::Kadm5::Config.new.realm
273
+ @created = []
274
+ end
275
+
276
+ after(:each) do
277
+ @created.each do |name|
278
+ @kadm5.delete_principal(name) rescue nil
279
+ end
280
+ @kadm5.close
281
+ end
282
+
283
+ it 'creates a principal with name: and password:' do
284
+ pname = "create_test1@#{@realm}"
285
+ @kadm5.create_principal(name: pname, password: 'Test1234!')
286
+ @created << pname
287
+ p = @kadm5.find_principal(pname)
288
+ expect(p).to be_a(Kerberos::Krb5::Principal)
289
+ expect(p.principal).to eq(pname)
290
+ end
291
+
292
+ it 'returns self for chaining' do
293
+ pname = "create_test2@#{@realm}"
294
+ result = @kadm5.create_principal(name: pname, password: 'Test1234!')
295
+ @created << pname
296
+ expect(result).to equal(@kadm5)
297
+ end
298
+
299
+ it 'accepts a nil db_args option' do
300
+ pname = "create_test3@#{@realm}"
301
+ expect {
302
+ @kadm5.create_principal(name: pname, password: 'Test1234!', db_args: nil)
303
+ }.not_to raise_error
304
+ @created << pname
305
+ end
306
+
307
+ it 'raises ArgumentError when password: is missing' do
308
+ expect {
309
+ @kadm5.create_principal(name: "create_nopass@#{@realm}")
310
+ }.to raise_error(ArgumentError)
311
+ end
312
+
313
+ it 'raises ArgumentError when neither name: nor principal: is given' do
314
+ expect {
315
+ @kadm5.create_principal(password: 'Test1234!')
316
+ }.to raise_error(ArgumentError)
317
+ end
318
+
319
+ it 'raises ArgumentError with no arguments' do
320
+ expect {
321
+ @kadm5.create_principal
322
+ }.to raise_error(ArgumentError)
323
+ end
324
+
325
+ it 'rejects positional arguments and unknown keywords' do
326
+ expect {
327
+ @kadm5.create_principal("positional@#{@realm}", 'Test1234!')
328
+ }.to raise_error(ArgumentError)
329
+ expect {
330
+ @kadm5.create_principal(name: "unknown_kw@#{@realm}", password: 'Test1234!', unknown: true)
331
+ }.to raise_error(ArgumentError, /unknown keyword/)
332
+ end
333
+
334
+ it 'raises ArgumentError when both name: and principal: are given' do
335
+ p = Kerberos::Krb5::Principal.new(name: "create_both@#{@realm}")
336
+ expect {
337
+ @kadm5.create_principal(name: "create_both@#{@realm}", principal: p, password: 'Test1234!')
338
+ }.to raise_error(ArgumentError)
339
+ end
340
+
341
+ it 'raises an error for a duplicate principal' do
342
+ pname = "create_dup@#{@realm}"
343
+ @kadm5.create_principal(name: pname, password: 'Test1234!')
344
+ @created << pname
345
+ expect {
346
+ @kadm5.create_principal(name: pname, password: 'Test1234!')
347
+ }.to raise_error(Kerberos::Kadm5::Exception)
348
+ end
349
+
350
+ context 'with a Principal object' do
351
+ it 'creates a principal from a Principal object' do
352
+ pname = "create_obj1@#{@realm}"
353
+ p = Kerberos::Krb5::Principal.new(name: pname)
354
+ @kadm5.create_principal(principal: p, password: 'Test1234!')
355
+ @created << pname
356
+ found = @kadm5.find_principal(pname)
357
+ expect(found).to be_a(Kerberos::Krb5::Principal)
358
+ expect(found.principal).to eq(pname)
359
+ end
360
+
361
+ it 'raises TypeError if principal: is not a Principal object' do
362
+ expect {
363
+ @kadm5.create_principal(principal: "notaobj", password: 'Test1234!')
364
+ }.to raise_error(TypeError)
365
+ end
366
+
367
+ it 'raises ArgumentError if the principal has no name' do
368
+ p = Kerberos::Krb5::Principal.new
369
+ expect {
370
+ @kadm5.create_principal(principal: p, password: 'Test1234!')
371
+ }.to raise_error(ArgumentError)
372
+ end
373
+
374
+ it 'forwards the policy attribute' do
375
+ pname = "create_pol@#{@realm}"
376
+ p = Kerberos::Krb5::Principal.new(name: pname)
377
+ p.policy = 'strict_policy'
378
+ @kadm5.create_principal(principal: p, password: 'Changeme1!')
379
+ @created << pname
380
+ found = @kadm5.find_principal(pname)
381
+ expect(found.policy).to eq('strict_policy')
382
+ end
383
+
384
+ it 'forwards expire_time' do
385
+ pname = "create_exp@#{@realm}"
386
+ p = Kerberos::Krb5::Principal.new(name: pname)
387
+ future = Time.now + 86400 * 365
388
+ p.expire_time = future
389
+ @kadm5.create_principal(principal: p, password: 'Test1234!')
390
+ @created << pname
391
+ found = @kadm5.find_principal(pname)
392
+ # Allow a small delta for timestamp rounding
393
+ expect(found.expire_time.to_i).to be_within(1).of(future.to_i)
394
+ end
395
+
396
+ it 'forwards max_life' do
397
+ pname = "create_maxlife@#{@realm}"
398
+ p = Kerberos::Krb5::Principal.new(name: pname)
399
+ p.max_life = 7200
400
+ @kadm5.create_principal(principal: p, password: 'Test1234!')
401
+ @created << pname
402
+ found = @kadm5.find_principal(pname)
403
+ expect(found.max_life).to eq(7200)
404
+ end
405
+
406
+ it 'forwards max_renewable_life' do
407
+ pname = "create_maxrenew@#{@realm}"
408
+ p = Kerberos::Krb5::Principal.new(name: pname)
409
+ p.max_renewable_life = 14400
410
+ @kadm5.create_principal(principal: p, password: 'Test1234!')
411
+ @created << pname
412
+ found = @kadm5.find_principal(pname)
413
+ expect(found.max_renewable_life).to eq(14400)
414
+ end
415
+ end
416
+ end
103
417
  end