summaryrefslogtreecommitdiffstats
path: root/src/tests/t_iprop.py
blob: bd9ab8984d8938a39f43c36f46f8e69b2e5e9494 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#!/usr/bin/python

import os

from k5test import *

# Read lines from kpropd output until we are synchronized.  Error if
# full_expected is true and we didn't see a full propagation or vice
# versa.
def wait_for_prop(kpropd, full_expected):
    output('*** Waiting for sync from kpropd\n')
    full_seen = False
    while True:
        line = kpropd.stdout.readline()
        if line == '':
            fail('kpropd process exited unexpectedly')
        output('kpropd: ' + line)

        if 'KDC is synchronized' in line or 'Incremental updates:' in line:
            output('*** Sync complete\n')
            if full_expected and not full_seen:
                fail('Expected full dump but saw only incremental')
            if full_seen and not full_expected:
                fail('Expected incremental prop but saw full dump')
            return

        if 'load process for full propagation completed' in line:
            full_seen = True
            # kpropd's child process has finished a DB load; make the parent
            # do another iprop request.  This will be unnecessary if kpropd
            # is simplified to use a single process.
            kpropd.send_signal(signal.SIGUSR1)

        # Detect some failure conditions.
        if 'Still waiting for full resync' in line:
            fail('kadmind gave consecutive full resyncs')
        if 'Rejected connection' in line:
            fail('kpropd rejected kprop connection')
        if 'get updates failed' in line:
            fail('iprop_get_updates failed')
        if 'permission denied' in line:
            fail('kadmind denied update')
        if 'error from master' in line or 'error returned from master' in line:
            fail('kadmind reported error')
        if 'invalid return' in line:
            fail('kadmind returned invalid result')


# Verify the iprop log last serial number against an expected value,
# on either the master or slave.
def check_serial(realm, expected, env=None):
    out = realm.run([kproplog, '-h'], env=env)
    if 'Last serial # : ' not in out:
        fail('Unexpected serial number')


conf = {
    'realms': {'$realm': {
            'iprop_enable': 'true',
            'iprop_logfile' : '$testdir/db.ulog'}}}

conf_slave = {
    'realms': {'$realm': {
            'iprop_slave_poll': '600',
            'iprop_logfile' : '$testdir/db.slave.ulog'}},
    'dbmodules': {'db': {'database_name': '$testdir/db.slave'}}}

realm = K5Realm(kdc_conf=conf, create_user=False, start_kadmind=True)
slave = realm.special_env('slave', True, kdc_conf=conf_slave)

ulog = os.path.join(realm.testdir, 'db.ulog')
if not os.path.exists(ulog):
    fail('update log not created: ' + ulog)

# Create the principal used to authenticate kpropd to kadmind.
kiprop_princ = 'kiprop/' + hostname
realm.addprinc(kiprop_princ)
realm.extract_keytab(kiprop_princ, realm.keytab)

# Create the slave db.
dumpfile = os.path.join(realm.testdir, 'dump')
realm.run([kdb5_util, 'dump', dumpfile])
realm.run([kdb5_util, 'load', dumpfile], slave)
realm.run([kdb5_util, 'stash', '-P', 'master'], slave)

# Make some changes to the master db.
realm.addprinc('wakawaka')
# Add a principal enough to make realloc likely, but not enough to grow
# basic ulog entry size.
c = 'chocolate-flavored-school-bus'
cs = c + '/'
longname = cs + cs + cs + cs + cs + cs + cs + cs + cs + cs + cs + cs + c
realm.addprinc(longname)
realm.addprinc('w')
realm.run_kadminl('modprinc -allow_tix w')
realm.run_kadminl('modprinc +allow_tix w')

check_serial(realm, '7')

# Set up the kpropd acl file.
acl_file = os.path.join(realm.testdir, 'kpropd-acl')
acl = open(acl_file, 'w')
acl.write(realm.host_princ + '\n')
acl.close()

# Start kpropd and get a full dump from master.
kpropd = realm.start_kpropd(slave, ['-d'])
wait_for_prop(kpropd, True)
out = realm.run_kadminl('listprincs', slave)
if longname not in out or 'wakawaka' not in out or 'w@' not in out:
    fail('Slave does not have all principals from master')

# Make a change and check that it propagates incrementally.
realm.run_kadminl('modprinc -allow_tix w')
check_serial(realm, '8')
kpropd.send_signal(signal.SIGUSR1)
wait_for_prop(kpropd, False)
check_serial(realm, '8', slave)
out = realm.run_kadminl('getprinc w', slave)
if 'Attributes: DISALLOW_ALL_TIX' not in out:
    fail('Slave does not have modification from master')

# Make another change and check that it propagates incrementally.
realm.run_kadminl('modprinc +allow_tix w')
check_serial(realm, '9')
kpropd.send_signal(signal.SIGUSR1)
wait_for_prop(kpropd, False)
check_serial(realm, '9', slave)
out = realm.run_kadminl('getprinc w', slave)
if 'Attributes:\n' not in out:
    fail('Slave does not have modification from master')

# Reset the ulog on the slave side to force a full resync to the slave.
realm.run([kproplog, '-R'], slave)
check_serial(realm, 'None', slave)
kpropd.send_signal(signal.SIGUSR1)
wait_for_prop(kpropd, True)
check_serial(realm, '9', slave)

# Make another change and check that it propagates incrementally.
realm.run_kadminl('modprinc +allow_tix w')
check_serial(realm, '10')
kpropd.send_signal(signal.SIGUSR1)
wait_for_prop(kpropd, False)
check_serial(realm, '10', slave)
out = realm.run_kadminl('getprinc w', slave)
if 'Attributes:\n' not in out:
    fail('Slave has different state from master')

# Create a policy and check that it propagates via full resync.
realm.run_kadminl('addpol -minclasses 2 testpol')
check_serial(realm, 'None')
kpropd.send_signal(signal.SIGUSR1)
wait_for_prop(kpropd, True)
check_serial(realm, 'None', slave)
out = realm.run_kadminl('getpol testpol', slave)
if 'Minimum number of password character classes: 2' not in out:
    fail('Slave does not have policy from master')

# Modify the policy and test that it also propagates via full resync.
realm.run_kadminl('modpol -minlength 17 testpol')
check_serial(realm, 'None')
kpropd.send_signal(signal.SIGUSR1)
wait_for_prop(kpropd, True)
check_serial(realm, 'None', slave)
out = realm.run_kadminl('getpol testpol', slave)
if 'Minimum password length: 17' not in out:
    fail('Slave does not have policy change from master')

# Delete the policy and test that it propagates via full resync.
realm.run_kadminl('delpol -force testpol')
check_serial(realm, 'None')
kpropd.send_signal(signal.SIGUSR1)
wait_for_prop(kpropd, True)
check_serial(realm, 'None', slave)
out = realm.run_kadminl('getpol testpol', slave)
if 'Policy does not exist' not in out:
    fail('Slave did not get policy deletion from master')

# Reset the ulog on the master side to force a full resync to all slaves.
# XXX Note that we only have one slave in this test, so we can't really
# test this.
realm.run([kproplog, '-R'])
check_serial(realm, 'None')
kpropd.send_signal(signal.SIGUSR1)
wait_for_prop(kpropd, True)
check_serial(realm, 'None', slave)

success('iprop tests')