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