enforce rights subsets in credentials
[sfa.git] / util / credential.py
1 ##
2 #
3 # Implements Geni Credentials
4 #
5 # Credentials are layered on top of certificates, and are essentially a
6 # certificate that stores a tuple of parameters.
7
8 from cert import *
9 from rights import *
10 from gid import *
11 import xmlrpclib
12
13 ##
14 # Credential is a tuple:
15 #     (GIDCaller, GIDObject, LifeTime, Privileges, Delegate)
16 #
17 # These fields are encoded using xmlrpc into the subjectAltName field of the
18 # x509 certificate. Note: Call encode() once the fields have been filled in
19 # to perform this encoding.
20
21 class Credential(Certificate):
22     gidCaller = None
23     gidObject = None
24     lifeTime = None
25     privileges = None
26     delegate = False
27
28     ##
29     # Create a Credential object
30     #
31     # @param create If true, create a blank x509 certificate
32     # @param subject If subject!=None, create an x509 cert with the subject name
33     # @param string If string!=None, load the credential from the string
34     # @param filename If filename!=None, load the credential from the file
35
36     def __init__(self, create=False, subject=None, string=None, filename=None):
37         Certificate.__init__(self, create, subject, string, filename)
38
39     ##
40     # set the GID of the caller
41     #
42     # @param gid GID object of the caller
43
44     def set_gid_caller(self, gid):
45         self.gidCaller = gid
46
47     ##
48     # get the GID of the object
49
50     def get_gid_caller(self):
51         if not self.gidCaller:
52             self.decode()
53         return self.gidCaller
54
55     ##
56     # set the GID of the object
57     #
58     # @param gid GID object of the object
59
60     def set_gid_object(self, gid):
61         self.gidObject = gid
62
63     ##
64     # get the GID of the object
65
66     def get_gid_object(self):
67         if not self.gidObject:
68             self.decode()
69         return self.gidObject
70
71     ##
72     # set the lifetime of this credential
73     #
74     # @param lifetime lifetime of credential
75
76     def set_lifetime(self, lifeTime):
77         self.lifeTime = lifeTime
78
79     ##
80     # get the lifetime of the credential
81
82     def get_lifetime(self):
83         if not self.lifeTime:
84             self.decode()
85         return self.lifeTime
86
87     ##
88     # set the delegate bit
89     #
90     # @param delegate boolean (True or False)
91
92     def set_delegate(self, delegate):
93         self.delegate = delegate
94
95     ##
96     # get the delegate bit
97
98     def get_delegate(self):
99         if not self.delegate:
100             self.decode()
101         return self.delegate
102
103     ##
104     # set the privileges
105     #
106     # @param privs either a comma-separated list of privileges of a RightList object
107
108     def set_privileges(self, privs):
109         if isinstance(privs, str):
110             self.privileges = RightList(string = privs)
111         else:
112             self.privileges = privs
113
114     ##
115     # return the privileges as a RightList object
116
117     def get_privileges(self):
118         if not self.privileges:
119             self.decode()
120         return self.privileges
121
122     ##
123     # determine whether the credential allows a particular operation to be
124     # performed
125     #
126     # @param op_name string specifying name of operation ("lookup", "update", etc)
127
128     def can_perform(self, op_name):
129         rights = self.get_privileges()
130         if not rights:
131             return False
132         return rights.can_perform(op_name)
133
134     ##
135     # Encode the attributes of the credential into a string and store that
136     # string in the alt-subject-name field of the X509 object. This should be
137     # done immediately before signing the credential.
138
139     def encode(self):
140         dict = {"gidCaller": None,
141                 "gidObject": None,
142                 "lifeTime": self.lifeTime,
143                 "privileges": None,
144                 "delegate": self.delegate}
145         if self.gidCaller:
146             dict["gidCaller"] = self.gidCaller.save_to_string(save_parents=True)
147         if self.gidObject:
148             dict["gidObject"] = self.gidObject.save_to_string(save_parents=True)
149         if self.privileges:
150             dict["privileges"] = self.privileges.save_to_string()
151         str = xmlrpclib.dumps((dict,), allow_none=True)
152         self.set_data(str)
153
154     ##
155     # Retrieve the attributes of the credential from the alt-subject-name field
156     # of the X509 certificate. This is automatically done by the various
157     # get_* methods of this class and should not need to be called explicitly.
158
159     def decode(self):
160         data = self.get_data()
161         if data:
162             dict = xmlrpclib.loads(self.get_data())[0][0]
163         else:
164             dict = {}
165
166         self.lifeTime = dict.get("lifeTime", None)
167         self.delegate = dict.get("delegate", None)
168
169         privStr = dict.get("privileges", None)
170         if privStr:
171             self.privileges = RightList(string = privStr)
172         else:
173             self.privileges = None
174
175         gidCallerStr = dict.get("gidCaller", None)
176         if gidCallerStr:
177             self.gidCaller = GID(string=gidCallerStr)
178         else:
179             self.gidCaller = None
180
181         gidObjectStr = dict.get("gidObject", None)
182         if gidObjectStr:
183             self.gidObject = GID(string=gidObjectStr)
184         else:
185             self.gidObject = None
186
187     ##
188     # Verify that a chain of credentials is valid (see cert.py:verify). In
189     # addition to the checks for ordinary certificates, verification also
190     # ensures that the delegate bit was set by each parent in the chain. If
191     # a delegate bit was not set, then an exception is thrown.
192
193     def verify_chain(self, trusted_certs = None):
194         # do the normal certificate verification stuff
195         Certificate.verify_chain(self, trusted_certs)
196
197         if self.parent:
198             # make sure the parent delegated rights to the child
199             if not self.parent.get_delegate():
200                 raise MissingDelegateBit(self.parent.get_subject())
201
202             # make sure the rights given to the child are a subset of the
203             # parents rights
204             if not self.parent.get_privileges().is_superset(self.get_privileges()):
205                 raise ChildRightsNotSubsetOfParent(self.get_subject())
206
207         return
208
209     ##
210     # Dump the contents of a credential to stdout in human-readable format
211     #
212     # @param dump_parents If true, also dump the parent certificates
213
214     def dump(self, dump_parents=False):
215         print "CREDENTIAL", self.get_subject()
216
217         print "      privs:", self.get_privileges().save_to_string()
218
219         print "  gidCaller:"
220         gidCaller = self.get_gid_caller()
221         if gidCaller:
222             gidCaller.dump(8, dump_parents)
223
224         print "  gidObject:"
225         gidObject = self.get_gid_object()
226         if gidObject:
227             gidObject.dump(8, dump_parents)
228
229         print "   delegate:", self.get_delegate()
230
231         if self.parent and dump_parents:
232            print "PARENT",
233            self.parent.dump(dump_parents)
234