sfaresetgids updated to reset the slice GIDs
[sfa.git] / sfa / client / sfaresetgids.py
1 #!/usr/bin/python
2
3 from __future__ import print_function
4
5 from argparse import ArgumentParser
6
7 from sfa.util.config import Config
8 from sfa.storage.alchemy import alchemy
9 from sfa.storage.model import RegRecord
10 from sfa.trust.hierarchy import Hierarchy
11 from sfa.trust.certificate import convert_public_key, Keypair
12
13 """
14 WARNING : This script is not exactly thoroughly tested
15
16 BEFORE YOU USE
17 * backup the sfa db
18 * backup the /var/lib/sfa/authorities tree
19 * make sure to keep all this for a while 
20   in case for example the old uuids turn out to be needed
21
22 PURPOSE
23
24 Regenerates gids; mostly useful when your root gid is old or obsolete
25 for any other reason (like, it's still md5 signed)
26
27 POLICIES
28
29 all  : regenerate everything no matter what, including toplevel gid
30 safe : regenerate everything *except* toplevel 
31 incremental : was useful during development mostly; 
32
33 WHAT IT DOES
34
35 authorities : a new gid is issued
36 users : a new gid is created with email, pubkey, and uuid restored from the previous ones
37
38 HOW TO USE
39
40 . do the backups - see above
41 . shutdown your sfa service
42 . rm -rf /var/lib/sfa/authorities
43 . run the script (run directly with python, no entry point is installed in PATH)
44 . restart the service
45
46 . on the client side, trash any old sscert or gids or similar
47
48 """
49
50
51 class SfaResetGids:
52     def __init__(self, session, tophrn):
53         self.session = session
54         self.tophrn = tophrn
55
56     def load_local_records(self):
57         """
58         read the database for records that start with tophrn.*
59         and sort them by hrn
60         """
61         # just making sure we don't mess with anything else
62         # than our own local business
63         self.records = self.session.query(RegRecord)\
64                        .filter(RegRecord.hrn.op('~')("{}.*".format(self.tophrn)))\
65                        .order_by(RegRecord.hrn)
66
67     def regenerate(self, policy='safe'):
68         """
69         For all local records, gid gets regenerated
70         policy parameter works as follows
71         * all :
72           all gids get renewed, including toplevel hrn, no matter what
73         * safe : 
74           gid for toplevel hrn gets regenerated only if not yet existing
75           in SFA_DATA_DIR (i.e. /var/lib/sfa/authorities/<hrn>/hrn.{gid,key})
76           all others are redone
77         * incremental :
78           recreate only for entities not present in SFA_DATA_DIR
79         """
80
81         count_auths, count_users, count_slices = 0, 0, 0
82         hierarchy = Hierarchy()
83         for record in self.records:
84             print(record.hrn)
85             ########## not an autority nor a user nor a slice: ignored
86             # Just wondering what other type it could be...
87             if record.type not in ['authority', 'user', 'slice']:
88                 message = ''
89                 if record.gid:
90                     message = '[GID cleared]'
91                     record.gid = None
92                 print("SKP (non-auth) {} {} : {}"
93                       .format(message, record.type, record.hrn))
94                 continue
95             ########## toplevel : be careful 
96             if record.hrn == self.tophrn:
97                 if policy != 'all':
98                     print("SKP (toplevel) - type={} - policy={}"
99                           .format(record.type, policy))
100                     continue
101             ########## user : rebuild a gid from pubkey and email
102             if record.type == 'user':
103                 hrn = str(record.hrn)
104                 gid = record.get_gid_object()
105                 uuid = gid.get_uuid()
106                 pub = gid.get_pubkey()
107                 email = gid.get_email()
108                 print("pub {} uuid {}... email {}".format(pub, str(uuid)[:6], email))
109                 new_gid = hierarchy.create_gid(hrn, uuid, pub, email=email)
110                 new_gid_str = new_gid.save_to_string()
111                 record.gid = new_gid_str
112                 print("NEW {} {} [{}]".format(record.type, record.hrn, email))
113                 count_users += 1
114                 continue
115             ########## authorities
116             if record.type == 'authority':
117                 if policy in ('all', 'safe'):
118                     redo = True
119                 else:
120                     redo = not hierarchy.auth_exists(record.hrn)
121                 if not redo:
122                     print("IGN (existing) {}".format(record.hrn))
123                 else:
124                     print("NEW {} {}".format(record.type, record.hrn))
125                     # because we have it sorted we should not need create_parents
126                     gid = hierarchy.create_auth(str(record.hrn))
127                     record.gid = gid
128                     count_auths += 1
129             ########## slices
130             if record.type == 'slice':
131                 hrn = str(record.hrn)
132                 gid = record.get_gid_object()
133                 uuid = gid.get_uuid()
134                 pub = gid.get_pubkey()
135                 print("pub {} uuid {}...".format(pub, str(uuid)[:6]))
136                 new_gid = hierarchy.create_gid(hrn, uuid, pub)
137                 new_gid_str = new_gid.save_to_string()
138                 record.gid = new_gid_str
139                 print("NEW {} {}".format(record.type, record.hrn))
140                 count_slices += 1
141                 continue
142         #
143         print("Committing to the DB {} new auth gids and {} new user gids and {} new slice gids"
144               .format(count_auths, count_users, count_slices))
145         self.session.commit()
146         return True
147
148     def main(self):
149         parser = ArgumentParser()
150         parser.add_argument("--policy", choices=('all', 'safe', 'incremental'),
151                             default='safe')
152         args = parser.parse_args()
153
154         self.load_local_records()
155         return 0 if self.regenerate(args.policy) else 1
156
157 ###
158 if __name__ == '__main__':
159     session = alchemy.session()
160     tophrn = Config().SFA_REGISTRY_ROOT_AUTH
161     print(tophrn)
162     SfaResetGids(session, tophrn).main()