Removed remaining problems from merge.
[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.util.sfaticket import *
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         strname = str(name).strip("['']")
224         
225         if object_hrn == strname:
226             return
227         if strname.startswith((object_hrn + ".")) is True:
228             return
229         #if name.startswith(get_authority(name)):
230             #return
231         print>>sys.stderr, " \r\n \t AUTH.PY  verify_object_permission GROSECHECDELENFER "
232         raise PermissionError(name)
233
234     def determine_user_rights(self, caller_hrn, record):
235         """
236         Given a user credential and a record, determine what set of rights the
237         user should have to that record.
238         
239         This is intended to replace determine_rights() and
240         verify_cancreate_credential()
241         """
242
243         rl = Rights()
244         type = record['type']
245
246
247         if type=="slice":
248             researchers = record.get("researcher", [])
249             pis = record.get("PI", [])
250             if (caller_hrn in researchers + pis):
251                 rl.add("refresh")
252                 rl.add("embed")
253                 rl.add("bind")
254                 rl.add("control")
255                 rl.add("info")
256
257         elif type == "authority":
258             pis = record.get("PI", [])
259             operators = record.get("operator", [])
260             if (caller_hrn == self.config.SFA_INTERFACE_HRN):
261                 rl.add("authority")
262                 rl.add("sa")
263                 rl.add("ma")
264             if (caller_hrn in pis):
265                 rl.add("authority")
266                 rl.add("sa")
267             if (caller_hrn in operators):
268                 rl.add("authority")
269                 rl.add("ma")
270
271         elif type == "user":
272             rl.add("refresh")
273             rl.add("resolve")
274             rl.add("info")
275
276         elif type == "node":
277             rl.add("operator")
278
279         return rl
280
281     def verify_cancreate_credential(self, src_cred, record):
282         """
283         Verify that a user can retrive a particular type of credential.
284         For slices, the user must be on the researcher list. For SA and
285         MA the user must be on the pi and operator lists respectively
286         """
287
288         type = record.get_type()
289         cred_object_hrn = src_cred.get_gid_object().get_hrn()
290         if cred_object_hrn in [self.config.SFA_REGISTRY_ROOT_AUTH]:
291             return
292         if type=="slice":
293             researchers = record.get("researcher", [])
294             if not (cred_object_hrn in researchers):
295                 raise PermissionError(cred_object_hrn + " is not in researcher list for " + record.get_name())
296         elif type == "sa":
297             pis = record.get("pi", [])
298             if not (cred_object_hrn in pis):
299                 raise PermissionError(cred_object_hrn + " is not in pi list for " + record.get_name())
300         elif type == "ma":
301             operators = record.get("operator", [])
302             if not (cred_object_hrn in operators):
303                 raise PermissionError(cred_object_hrn + " is not in operator list for " + record.get_name())
304
305     def get_authority(self, hrn):
306         return get_authority(hrn)
307
308     def filter_creds_by_caller(self, creds, caller_hrn_list):
309         """
310         Returns a list of creds who's gid caller matches the 
311         specified caller hrn
312         """
313         if not isinstance(creds, list):
314             creds = [creds]
315         creds = []
316         if not isinistance(caller_hrn_list, list):
317             caller_hrn_list = [caller_hrn_list]
318         for cred in creds:
319             try:
320                 tmp_cred = Credential(string=cred)
321                 if tmp_cred.get_gid_caller().get_hrn() in [caller_hrn_list]:
322                     creds.append(cred)
323             except: pass
324         return creds
325