added tickets
[sfa.git] / util / hierarchy.py
1 # hierarchy.py
2 #
3 # hierarchy of GENI authorities
4 #
5 # This correspond's almost identically to the functionality of Soner's
6 # "tree" module. Each component of an HRN is stored in a different subdirectory.
7 # Inside this subdirectory are:
8 #      *.GID - GID file
9 #      *.PKEY - private key file
10 #      *.DBINFO - database info
11
12 import os
13 import report
14 from cert import *
15 from credential import *
16 from gid import *
17 from misc import *
18 from config import *
19 from geniticket import *
20
21 class AuthInfo():
22     hrn = None
23     gid_object = None
24     gid_filename = None
25     privkey_filename = None
26     dbinfo_filename = None
27
28     def __init__(self, hrn, gid_filename, privkey_filename, dbinfo_filename):
29         self.hrn = hrn
30         self.set_gid_filename(gid_filename)
31         self.privkey_filename = privkey_filename
32         self.dbinfo_filename = dbinfo_filename
33
34     def set_gid_filename(self, fn):
35         self.gid_filename = fn
36         self.gid_object = None
37
38     def get_gid_object(self):
39         if not self.gid_object:
40             self.gid_object = GID(filename = self.gid_filename)
41         return self.gid_object
42
43     def get_pkey_object(self):
44         return Keypair(filename = self.privkey_filename)
45
46     def get_dbinfo(self):
47         f = file(self.dbinfo_filename)
48         dict = eval(f.read())\r
49         f.close()\r
50         return dict\r
51 \r
52     def update_gid_object(self, gid):\r
53         gid.save_to_file(self.gid_filename)\r
54         self.gid_object = gid\r
55
56 class Hierarchy():
57     def __init__(self, basedir="."):
58         self.basedir = os.path.join(basedir, "authorities")
59
60     def get_auth_filenames(self, hrn):
61         leaf = get_leaf(hrn)
62         parent_hrn = get_authority(hrn)
63         directory = os.path.join(self.basedir, hrn.replace(".", "/"))
64
65         gid_filename = os.path.join(directory, leaf+".gid")
66         privkey_filename = os.path.join(directory, leaf+".pkey")
67         dbinfo_filename = os.path.join(directory, leaf+".dbinfo")
68
69         return (directory, gid_filename, privkey_filename, dbinfo_filename)
70
71     def auth_exists(self, hrn):
72         (directory, gid_filename, privkey_filename, dbinfo_filename) = \
73             self.get_auth_filenames(hrn)
74
75         return os.path.exists(gid_filename) and \
76                os.path.exists(privkey_filename) and \
77                os.path.exists(dbinfo_filename)
78
79     def create_auth(self, hrn, create_parents=False):
80         report.trace("Hierarchy: creating authority: " + hrn)
81
82         # create the parent authority if necessary
83         parent_hrn = get_authority(hrn)
84         if (parent_hrn) and (not self.auth_exists(parent_hrn)) and (create_parents):
85             self.create_auth(parent_hrn, create_parents)
86
87         (directory, gid_filename, privkey_filename, dbinfo_filename) = \
88             self.get_auth_filenames(hrn)
89
90         # create the directory to hold the files
91         try:
92             os.makedirs(directory)\r
93         # if the path already exists then pass\r
94         except OSError, (errno, strerr):\r
95             if errno == 17:\r
96                 pass
97
98         pkey = Keypair(create = True)
99         pkey.save_to_file(privkey_filename)
100
101         gid = self.create_gid(hrn, create_uuid(), pkey)
102         gid.save_to_file(gid_filename, save_parents=True)
103
104         # XXX TODO: think up a better way for the dbinfo to work
105
106         dbinfo = get_default_dbinfo()
107         dbinfo_file = file(dbinfo_filename, "w")
108         dbinfo_file.write(str(dbinfo))\r
109         dbinfo_file.close()
110
111     def get_auth_info(self, hrn):
112         #report.trace("Hierarchy: getting authority: " + hrn)
113
114         if not self.auth_exists(hrn):
115             raise MissingAuthority(hrn)
116
117         (directory, gid_filename, privkey_filename, dbinfo_filename) = \
118             self.get_auth_filenames(hrn)
119
120         auth_info = AuthInfo(hrn, gid_filename, privkey_filename, dbinfo_filename)
121
122         # check the GID and see if it needs to be refreshed
123         gid = auth_info.get_gid_object()
124         gid_refreshed = self.refresh_gid(gid)
125         if gid != gid_refreshed:
126             auth_info.update_gid_object(gid_refreshed)
127
128         return auth_info
129
130     def create_gid(self, hrn, uuid, pkey):
131         gid = GID(subject=hrn, uuid=uuid, hrn=hrn)
132
133         parent_hrn = get_authority(hrn)
134         if not parent_hrn:
135             # if there is no parent hrn, then it must be self-signed. this
136             # is where we terminate the recursion
137             gid.set_issuer(pkey, hrn)
138         else:
139             # we need the parent's private key in order to sign this GID
140             parent_auth_info = self.get_auth_info(parent_hrn)
141             gid.set_issuer(parent_auth_info.get_pkey_object(), parent_auth_info.hrn)
142             gid.set_parent(parent_auth_info.get_gid_object())
143
144         gid.set_pubkey(pkey)
145         gid.encode()
146         gid.sign()
147
148         return gid
149
150     def refresh_gid(self, gid, hrn=None, uuid=None, pubkey=None):
151         # TODO: compute expiration time of GID, refresh it if necessary
152         gid_is_expired = False
153
154         # update the gid if we need to
155         if gid_is_expired or hrn or uuid or pubkey:
156             if not hrn:
157                 hrn = gid.get_hrn()
158             if not uuid:
159                 uuid = gid.get_uuid()
160             if not pubkey:
161                 pubkey = gid.get_pubkey()
162
163             gid = self.create_gid(hrn, uuid, pubkey)
164
165         return gid
166
167     def get_auth_cred(self, hrn):
168         auth_info = self.get_auth_info(hrn)
169         gid = auth_info.get_gid_object()
170
171         cred = Credential(subject=hrn)
172         cred.set_gid_caller(gid)
173         cred.set_gid_object(gid)
174         cred.set_privileges("authority")
175         cred.set_delegate(True)
176         cred.set_pubkey(auth_info.get_gid_object().get_pubkey())
177
178         parent_hrn = get_authority(hrn)
179         if not parent_hrn:
180             # if there is no parent hrn, then it must be self-signed. this
181             # is where we terminate the recursion
182             cred.set_issuer(auth_info.get_pkey_object(), hrn)
183         else:
184             # we need the parent's private key in order to sign this GID
185             parent_auth_info = self.get_auth_info(parent_hrn)
186             cred.set_issuer(parent_auth_info.get_pkey_object(), parent_auth_info.hrn)
187             cred.set_parent(self.get_auth_cred(parent_hrn))
188
189         cred.encode()
190         cred.sign()
191
192         return cred
193
194     # this looks almost the same as get_auth_cred, but works for tickets
195     # XXX does similarity imply there should be more code re-use?
196     def get_auth_ticket(self, hrn):
197         auth_info = self.get_auth_info(hrn)
198         gid = auth_info.get_gid_object()
199
200         ticket = Ticket(subject=hrn)
201         ticket.set_gid_caller(gid)
202         ticket.set_gid_object(gid)
203         ticket.set_delegate(True)
204         ticket.set_pubkey(auth_info.get_gid_object().get_pubkey())
205
206         parent_hrn = get_authority(hrn)
207         if not parent_hrn:
208             # if there is no parent hrn, then it must be self-signed. this
209             # is where we terminate the recursion
210             ticket.set_issuer(auth_info.get_pkey_object(), hrn)
211         else:
212             # we need the parent's private key in order to sign this GID
213             parent_auth_info = self.get_auth_info(parent_hrn)
214             ticket.set_issuer(parent_auth_info.get_pkey_object(), parent_auth_info.hrn)
215             ticket.set_parent(self.get_auth_cred(parent_hrn))
216
217         ticket.encode()
218         ticket.sign()
219
220         return ticket
221