Merge branch 'master' of ssh://git.planet-lab.org/git/sfa
[sfa.git] / sfa / trust / auth.py
1 #
2 # SfaAPI authentication 
3 #
4 ### $Id$
5 ### $URL$
6 #
7
8
9 from sfa.trust.certificate import Keypair, Certificate
10 from sfa.trust.credential import Credential
11 from sfa.trust.trustedroot import TrustedRootList
12 from sfa.util.faults import *
13 from sfa.trust.hierarchy import Hierarchy
14 from sfa.util.config import *
15 from sfa.util.namespace import *
16 from sfa.util.sfaticket import *
17 import sys
18
19 class Auth:
20     """
21     Credential based authentication
22     """
23
24     def __init__(self, peer_cert = None, config = None ):
25         self.peer_cert = peer_cert
26         self.hierarchy = Hierarchy()
27         if not config:
28             self.config = Config()
29         self.load_trusted_certs()
30
31     def load_trusted_certs(self):
32         self.trusted_cert_list = TrustedRootList(self.config.get_trustedroots_dir()).get_list()
33         self.trusted_cert_file_list = TrustedRootList(self.config.get_trustedroots_dir()).get_file_list()
34
35         
36         
37     def checkCredentials(self, creds, operation, hrn = None):
38         valid = []
39         if not isinstance(creds, list):
40             creds = [creds]
41         for cred in creds:
42             try:
43                 self.check(cred, operation, hrn)
44                 valid.append(cred)
45             except:
46                 error = sys.exc_info()[:2]
47                 continue
48             
49         if not len(valid):
50             raise InsufficientRights('Access denied: %s -- %s' % (error[0],error[1]))
51         
52         return valid
53         
54         
55     def check(self, cred, operation, hrn = None):
56         """
57         Check the credential against the peer cert (callerGID included 
58         in the credential matches the caller that is connected to the 
59         HTTPS connection, check if the credential was signed by a 
60         trusted cert and check if the credential is allowd to perform 
61         the specified operation.    
62         """
63         self.client_cred = Credential(string = cred)
64         self.client_gid = self.client_cred.get_gid_caller()
65         self.object_gid = self.client_cred.get_gid_object()
66         
67         # make sure the client_gid is not blank
68         if not self.client_gid:
69             raise MissingCallerGID(self.client_cred.get_subject())
70        
71         # validate the client cert if it exists
72         if self.peer_cert:
73             self.verifyPeerCert(self.peer_cert, self.client_gid)                   
74
75         # make sure the client is allowed to perform the operation
76         if operation:
77             if not self.client_cred.can_perform(operation):
78                 raise InsufficientRights(operation)
79
80         if self.trusted_cert_list:
81             self.client_cred.verify(self.trusted_cert_file_list)
82         else:
83            raise MissingTrustedRoots(self.config.get_trustedroots_dir())
84        
85         # Make sure the credential's target matches the specified hrn. 
86         # This check does not apply to trusted peers 
87         trusted_peers = [gid.get_hrn() for gid in self.trusted_cert_list]
88         if hrn and self.client_gid.get_hrn() not in trusted_peers:
89             target_hrn = self.object_gid.get_hrn()
90             if not hrn == target_hrn:
91                 raise PermissionError("Target hrn: %s doesn't match specified hrn: %s " % \
92                                        (target_hrn, hrn) )       
93         return True
94
95     def check_ticket(self, ticket):
96         """
97         Check if the tickt was signed by a trusted cert
98         """
99         if self.trusted_cert_list:
100             client_ticket = SfaTicket(string=ticket)
101             client_ticket.verify_chain(self.trusted_cert_list)
102         else:
103            raise MissingTrustedRoots(self.config.get_trustedroots_dir())
104
105         return True 
106
107     def verifyPeerCert(self, cert, gid):
108         # make sure the client_gid matches client's certificate
109         if not cert.is_pubkey(gid.get_pubkey()):
110             raise ConnectionKeyGIDMismatch(gid.get_subject()+":"+cert.get_subject())            
111
112     def verifyGidRequestHash(self, gid, hash, arglist):
113         key = gid.get_pubkey()
114         if not key.verify_string(str(arglist), hash):
115             raise BadRequestHash(hash)
116
117     def verifyCredRequestHash(self, cred, hash, arglist):
118         gid = cred.get_gid_caller()
119         self.verifyGidRequestHash(gid, hash, arglist)
120
121     def validateGid(self, gid):
122         if self.trusted_cert_list:
123             gid.verify_chain(self.trusted_cert_list)
124
125     def validateCred(self, cred):
126         if self.trusted_cert_list:
127             cred.verify(self.trusted_cert_file_list)
128
129     def authenticateGid(self, gidStr, argList, requestHash=None):
130         gid = GID(string = gidStr)
131         self.validateGid(gid)
132         # request_hash is optional
133         if requestHash:
134             self.verifyGidRequestHash(gid, requestHash, argList)
135         return gid
136
137     def authenticateCred(self, credStr, argList, requestHash=None):
138         cred = Credential(string = credStr)
139         self.validateCred(cred)
140         # request hash is optional
141         if requestHash:
142             self.verifyCredRequestHash(cred, requestHash, argList)
143         return cred
144
145     def authenticateCert(self, certStr, requestHash):
146         cert = Certificate(string=certStr)
147         self.validateCert(self, cert)   
148
149     def gidNoop(self, gidStr, value, requestHash):
150         self.authenticateGid(gidStr, [gidStr, value], requestHash)
151         return value
152
153     def credNoop(self, credStr, value, requestHash):
154         self.authenticateCred(credStr, [credStr, value], requestHash)
155         return value
156
157     def verify_cred_is_me(self, credential):
158         is_me = False 
159         cred = Credential(string=credential)
160         caller_gid = cred.get_gid_caller()
161         caller_hrn = caller_gid.get_hrn()
162         if caller_hrn != self.config.SFA_INTERFACE_HRN:
163             raise SfaPermissionDenied(self.config.SFA_INTEFACE_HRN)
164
165         return   
166         
167     def get_auth_info(self, auth_hrn):
168         """
169         Given an authority name, return the information for that authority.
170         This is basically a stub that calls the hierarchy module.
171         
172         @param auth_hrn human readable name of authority  
173         """
174
175         return self.hierarchy.get_auth_info(auth_hrn)
176
177
178     def veriry_auth_belongs_to_me(self, name):
179         """
180         Verify that an authority belongs to our hierarchy. 
181         This is basically left up to the implementation of the hierarchy
182         module. If the specified name does not belong, ane exception is 
183         thrown indicating the caller should contact someone else.
184
185         @param auth_name human readable name of authority
186         """
187
188         # get auth info will throw an exception if the authority doesnt exist
189         self.get_auth_info(name)
190
191
192     def verify_object_belongs_to_me(self, name):
193         """
194         Verify that an object belongs to our hierarchy. By extension,
195         this implies that the authority that owns the object belongs
196         to our hierarchy. If it does not an exception is thrown.
197     
198         @param name human readable name of object        
199         """
200         auth_name = self.get_authority(name)
201         if not auth_name:
202             auth_name = name 
203         if name == self.config.SFA_INTERFACE_HRN:
204             return
205         self.verify_auth_belongs_to_me(auth_name) 
206              
207     def verify_auth_belongs_to_me(self, name):
208         # get auth info will throw an exception if the authority doesnt exist
209         self.get_auth_info(name) 
210
211
212     def verify_object_permission(self, name):
213         """
214         Verify that the object gid that was specified in the credential
215         allows permission to the object 'name'. This is done by a simple
216         prefix test. For example, an object_gid for plc.arizona would 
217         match the objects plc.arizona.slice1 and plc.arizona.
218     
219         @param name human readable name to test  
220         """
221         object_hrn = self.object_gid.get_hrn()
222         if object_hrn == name:
223             return
224         if name.startswith(object_hrn + "."):
225             return
226         #if name.startswith(get_authority(name)):
227             #return
228     
229         raise PermissionError(name)
230
231     def determine_user_rights(self, caller_hrn, record):
232         """
233         Given a user credential and a record, determine what set of rights the
234         user should have to that record.
235         
236         This is intended to replace determine_rights() and
237         verify_cancreate_credential()
238         """
239
240         rl = RightList()
241         type = record['type']
242
243
244         if type=="slice":
245             researchers = record.get("researcher", [])
246             pis = record.get("PI", [])
247             if (caller_hrn in researchers + pis):
248                 rl.add("refresh")
249                 rl.add("embed")
250                 rl.add("bind")
251                 rl.add("control")
252                 rl.add("info")
253
254         elif type == "authority":
255             pis = record.get("PI", [])
256             operators = record.get("operator", [])
257             if (caller_hrn == self.config.SFA_INTERFACE_HRN):
258                 rl.add("authority")
259                 rl.add("sa")
260                 rl.add("ma")
261             if (caller_hrn in pis):
262                 rl.add("authority")
263                 rl.add("sa")
264             if (caller_hrn in operators):
265                 rl.add("authority")
266                 rl.add("ma")
267
268         elif type == "user":
269             rl.add("refresh")
270             rl.add("resolve")
271             rl.add("info")
272
273         elif type == "node":
274             rl.add("operator")
275
276         return rl
277
278     def verify_cancreate_credential(self, src_cred, record):
279         """
280         Verify that a user can retrive a particular type of credential.
281         For slices, the user must be on the researcher list. For SA and
282         MA the user must be on the pi and operator lists respectively
283         """
284
285         type = record.get_type()
286         cred_object_hrn = src_cred.get_gid_object().get_hrn()
287         if cred_object_hrn in [self.config.SFA_REGISTRY_ROOT_AUTH]:
288             return
289         if type=="slice":
290             researchers = record.get("researcher", [])
291             if not (cred_object_hrn in researchers):
292                 raise PermissionError(cred_object_hrn + " is not in researcher list for " + record.get_name())
293         elif type == "sa":
294             pis = record.get("pi", [])
295             if not (cred_object_hrn in pis):
296                 raise PermissionError(cred_object_hrn + " is not in pi list for " + record.get_name())
297         elif type == "ma":
298             operators = record.get("operator", [])
299             if not (cred_object_hrn in operators):
300                 raise PermissionError(cred_object_hrn + " is not in operator list for " + record.get_name())
301
302     def get_authority(self, hrn):
303         return get_authority(hrn)
304
305     def filter_creds_by_caller(self, creds, caller_hrn):
306         """
307         Returns a list of creds who's gid caller matches the 
308         specified caller hrn
309         """
310         if not isinstance(creds, list):
311             creds = [creds]
312         creds = []
313         for cred in creds:
314             try:
315                 tmp_cred = Credential(string=cred)
316                 if tmp_cred.get_gid_caller().get_hrn() == caller_hrn:
317                     creds.append(cred)
318             except: pass
319         return creds
320