85ddc68d94cae57666664d765585e076e350057d
[sfa.git] / sfa / trust / credential_legacy.py
1 #----------------------------------------------------------------------\r
2 # Copyright (c) 2008 Board of Trustees, Princeton University\r
3 #\r
4 # Permission is hereby granted, free of charge, to any person obtaining\r
5 # a copy of this software and/or hardware specification (the "Work") to\r
6 # deal in the Work without restriction, including without limitation the\r
7 # rights to use, copy, modify, merge, publish, distribute, sublicense,\r
8 # and/or sell copies of the Work, and to permit persons to whom the Work\r
9 # is furnished to do so, subject to the following conditions:\r
10 #\r
11 # The above copyright notice and this permission notice shall be\r
12 # included in all copies or substantial portions of the Work.\r
13 #\r
14 # THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS \r
15 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF \r
16 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
17 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT \r
18 # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, \r
19 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
20 # OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS \r
21 # IN THE WORK.\r
22 #----------------------------------------------------------------------\r
23 ##\r
24 # Implements SFA Credentials\r
25 #\r
26 # Credentials are layered on top of certificates, and are essentially a\r
27 # certificate that stores a tuple of parameters.\r
28 ##\r
29 \r
30 \r
31 import xmlrpclib\r
32 \r
33 from sfa.util.faults import MissingDelegateBit, ChildRightsNotSubsetOfParent\r
34 from sfa.trust.certificate import Certificate\r
35 from sfa.trust.gid import GID\r
36 \r
37 ##\r
38 # Credential is a tuple:\r
39 #     (GIDCaller, GIDObject, LifeTime, Privileges, Delegate)\r
40 #\r
41 # These fields are encoded using xmlrpc into the subjectAltName field of the\r
42 # x509 certificate. Note: Call encode() once the fields have been filled in\r
43 # to perform this encoding.\r
44 \r
45 class CredentialLegacy(Certificate):\r
46     gidCaller = None\r
47     gidObject = None\r
48     lifeTime = None\r
49     privileges = None\r
50     delegate = False\r
51 \r
52     ##\r
53     # Create a Credential object\r
54     #\r
55     # @param create If true, create a blank x509 certificate\r
56     # @param subject If subject!=None, create an x509 cert with the subject name\r
57     # @param string If string!=None, load the credential from the string\r
58     # @param filename If filename!=None, load the credential from the file\r
59 \r
60     def __init__(self, create=False, subject=None, string=None, filename=None):\r
61         Certificate.__init__(self, create, subject, string, filename)\r
62 \r
63     ##\r
64     # set the GID of the caller\r
65     #\r
66     # @param gid GID object of the caller\r
67 \r
68     def set_gid_caller(self, gid):\r
69         self.gidCaller = gid\r
70         # gid origin caller is the caller's gid by default\r
71         self.gidOriginCaller = gid\r
72 \r
73     ##\r
74     # get the GID of the object\r
75 \r
76     def get_gid_caller(self):\r
77         if not self.gidCaller:\r
78             self.decode()\r
79         return self.gidCaller\r
80 \r
81     ##\r
82     # set the GID of the object\r
83     #\r
84     # @param gid GID object of the object\r
85 \r
86     def set_gid_object(self, gid):\r
87         self.gidObject = gid\r
88 \r
89     ##\r
90     # get the GID of the object\r
91 \r
92     def get_gid_object(self):\r
93         if not self.gidObject:\r
94             self.decode()\r
95         return self.gidObject\r
96 \r
97     ##\r
98     # set the lifetime of this credential\r
99     #\r
100     # @param lifetime lifetime of credential\r
101 \r
102     def set_lifetime(self, lifeTime):\r
103         self.lifeTime = lifeTime\r
104 \r
105     ##\r
106     # get the lifetime of the credential\r
107 \r
108     def get_lifetime(self):\r
109         if not self.lifeTime:\r
110             self.decode()\r
111         return self.lifeTime\r
112 \r
113     ##\r
114     # set the delegate bit\r
115     #\r
116     # @param delegate boolean (True or False)\r
117 \r
118     def set_delegate(self, delegate):\r
119         self.delegate = delegate\r
120 \r
121     ##\r
122     # get the delegate bit\r
123 \r
124     def get_delegate(self):\r
125         if not self.delegate:\r
126             self.decode()\r
127         return self.delegate\r
128 \r
129     ##\r
130     # set the privileges\r
131     #\r
132     # @param privs either a comma-separated list of privileges of a Rights object\r
133 \r
134     def set_privileges(self, privs):\r
135         if isinstance(privs, str):\r
136             self.privileges = Rights(string = privs)\r
137         else:\r
138             self.privileges = privs\r
139 \r
140     ##\r
141     # return the privileges as a Rights object\r
142 \r
143     def get_privileges(self):\r
144         if not self.privileges:\r
145             self.decode()\r
146         return self.privileges\r
147 \r
148     ##\r
149     # determine whether the credential allows a particular operation to be\r
150     # performed\r
151     #\r
152     # @param op_name string specifying name of operation ("lookup", "update", etc)\r
153 \r
154     def can_perform(self, op_name):\r
155         rights = self.get_privileges()\r
156         if not rights:\r
157             return False\r
158         return rights.can_perform(op_name)\r
159 \r
160     ##\r
161     # Encode the attributes of the credential into a string and store that\r
162     # string in the alt-subject-name field of the X509 object. This should be\r
163     # done immediately before signing the credential.\r
164 \r
165     def encode(self):\r
166         dict = {"gidCaller": None,\r
167                 "gidObject": None,\r
168                 "lifeTime": self.lifeTime,\r
169                 "privileges": None,\r
170                 "delegate": self.delegate}\r
171         if self.gidCaller:\r
172             dict["gidCaller"] = self.gidCaller.save_to_string(save_parents=True)\r
173         if self.gidObject:\r
174             dict["gidObject"] = self.gidObject.save_to_string(save_parents=True)\r
175         if self.privileges:\r
176             dict["privileges"] = self.privileges.save_to_string()\r
177         str = xmlrpclib.dumps((dict,), allow_none=True)\r
178         self.set_data('URI:http://' + str)\r
179 \r
180     ##\r
181     # Retrieve the attributes of the credential from the alt-subject-name field\r
182     # of the X509 certificate. This is automatically done by the various\r
183     # get_* methods of this class and should not need to be called explicitly.\r
184 \r
185     def decode(self):\r
186         data = self.get_data().lstrip('URI:http://')\r
187         \r
188         if data:\r
189             dict = xmlrpclib.loads(data)[0][0]\r
190         else:\r
191             dict = {}\r
192 \r
193         self.lifeTime = dict.get("lifeTime", None)\r
194         self.delegate = dict.get("delegate", None)\r
195 \r
196         privStr = dict.get("privileges", None)\r
197         if privStr:\r
198             self.privileges = Rights(string = privStr)\r
199         else:\r
200             self.privileges = None\r
201 \r
202         gidCallerStr = dict.get("gidCaller", None)\r
203         if gidCallerStr:\r
204             self.gidCaller = GID(string=gidCallerStr)\r
205         else:\r
206             self.gidCaller = None\r
207 \r
208         gidObjectStr = dict.get("gidObject", None)\r
209         if gidObjectStr:\r
210             self.gidObject = GID(string=gidObjectStr)\r
211         else:\r
212             self.gidObject = None\r
213 \r
214     ##\r
215     # Verify that a chain of credentials is valid (see cert.py:verify). In\r
216     # addition to the checks for ordinary certificates, verification also\r
217     # ensures that the delegate bit was set by each parent in the chain. If\r
218     # a delegate bit was not set, then an exception is thrown.\r
219     #\r
220     # Each credential must be a subset of the rights of the parent.\r
221 \r
222     def verify_chain(self, trusted_certs = None):\r
223         # do the normal certificate verification stuff\r
224         Certificate.verify_chain(self, trusted_certs)\r
225 \r
226         if self.parent:\r
227             # make sure the parent delegated rights to the child\r
228             if not self.parent.get_delegate():\r
229                 raise MissingDelegateBit(self.parent.get_subject())\r
230 \r
231             # make sure the rights given to the child are a subset of the\r
232             # parents rights\r
233             if not self.parent.get_privileges().is_superset(self.get_privileges()):\r
234                 raise ChildRightsNotSubsetOfParent(self.get_subject() \r
235                                                    + " " + self.parent.get_privileges().save_to_string()\r
236                                                    + " " + self.get_privileges().save_to_string())\r
237 \r
238         return\r
239 \r
240     ##\r
241     # Dump the contents of a credential to stdout in human-readable format\r
242     #\r
243     # @param dump_parents If true, also dump the parent certificates\r
244 \r
245     def dump(self, *args, **kwargs):\r
246         print self.dump_string(*args,**kwargs)\r
247 \r
248     def dump_string(self, dump_parents=False):\r
249         result=""\r
250         result += "CREDENTIAL %s\n" % self.get_subject()\r
251 \r
252         result += "      privs: %s\n" % self.get_privileges().save_to_string()\r
253 \r
254         gidCaller = self.get_gid_caller()\r
255         if gidCaller:\r
256             result += "  gidCaller:\n"\r
257             gidCaller.dump(8, dump_parents)\r
258 \r
259         gidObject = self.get_gid_object()\r
260         if gidObject:\r
261             result += "  gidObject:\n"\r
262             result += gidObject.dump_string(8, dump_parents)\r
263 \r
264         result += "   delegate: %s" % self.get_delegate()\r
265 \r
266         if self.parent and dump_parents:\r
267             result += "PARENT\n"\r
268             result += self.parent.dump_string(dump_parents)\r
269 \r
270         return result\r