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