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