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