merged master onto senslab2, manually solved conflicts in setup.py
[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         
72         # make sure the client_gid is not blank
73         if not self.client_gid:
74             raise MissingCallerGID(self.client_cred.get_subject())
75        
76         # validate the client cert if it exists
77         if self.peer_cert:
78             self.verifyPeerCert(self.peer_cert, self.client_gid)                   
79
80         # make sure the client is allowed to perform the operation
81         if operation:
82             if not self.client_cred.can_perform(operation):
83                 raise InsufficientRights(operation)
84
85         if self.trusted_cert_list:
86             self.client_cred.verify(self.trusted_cert_file_list, self.config.SFA_CREDENTIAL_SCHEMA)
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             target_hrn = self.object_gid.get_hrn()
95             if not hrn == target_hrn:
96                 raise PermissionError("Target hrn: %s doesn't match specified hrn: %s " % \
97                                        (target_hrn, hrn) )       
98         return True
99
100     def check_ticket(self, ticket):
101         """
102         Check if the tickt was signed by a trusted cert
103         """
104         if self.trusted_cert_list:
105             client_ticket = SfaTicket(string=ticket)
106             client_ticket.verify_chain(self.trusted_cert_list)
107         else:
108            raise MissingTrustedRoots(self.config.get_trustedroots_dir())
109
110         return True 
111
112     def verifyPeerCert(self, cert, gid):
113         # make sure the client_gid matches client's certificate
114         if not cert.is_pubkey(gid.get_pubkey()):
115             raise ConnectionKeyGIDMismatch(gid.get_subject()+":"+cert.get_subject())            
116
117     def verifyGidRequestHash(self, gid, hash, arglist):
118         key = gid.get_pubkey()
119         if not key.verify_string(str(arglist), hash):
120             raise BadRequestHash(hash)
121
122     def verifyCredRequestHash(self, cred, hash, arglist):
123         gid = cred.get_gid_caller()
124         self.verifyGidRequestHash(gid, hash, arglist)
125
126     def validateGid(self, gid):
127         if self.trusted_cert_list:
128             gid.verify_chain(self.trusted_cert_list)
129
130     def validateCred(self, cred):
131         if self.trusted_cert_list:
132             cred.verify(self.trusted_cert_file_list)
133
134     def authenticateGid(self, gidStr, argList, requestHash=None):
135         gid = GID(string = gidStr)
136         self.validateGid(gid)
137         # request_hash is optional
138         if requestHash:
139             self.verifyGidRequestHash(gid, requestHash, argList)
140         return gid
141
142     def authenticateCred(self, credStr, argList, requestHash=None):
143         cred = Credential(string = credStr)
144         self.validateCred(cred)
145         # request hash is optional
146         if requestHash:
147             self.verifyCredRequestHash(cred, requestHash, argList)
148         return cred
149
150     def authenticateCert(self, certStr, requestHash):
151         cert = Certificate(string=certStr)
152         # xxx should be validateCred ??
153         self.validateCred(cert)   
154
155     def gidNoop(self, gidStr, value, requestHash):
156         self.authenticateGid(gidStr, [gidStr, value], requestHash)
157         return value
158
159     def credNoop(self, credStr, value, requestHash):
160         self.authenticateCred(credStr, [credStr, value], requestHash)
161         return value
162
163     def verify_cred_is_me(self, credential):
164         is_me = False 
165         cred = Credential(string=credential)
166         caller_gid = cred.get_gid_caller()
167         caller_hrn = caller_gid.get_hrn()
168         if caller_hrn != self.config.SFA_INTERFACE_HRN:
169             raise SfaPermissionDenied(self.config.SFA_INTEFACE_HRN)
170
171         return   
172         
173     def get_auth_info(self, auth_hrn):
174         """
175         Given an authority name, return the information for that authority.
176         This is basically a stub that calls the hierarchy module.
177         
178         @param auth_hrn human readable name of authority  
179         """
180
181         return self.hierarchy.get_auth_info(auth_hrn)
182
183
184     def veriry_auth_belongs_to_me(self, name):
185         """
186         Verify that an authority belongs to our hierarchy. 
187         This is basically left up to the implementation of the hierarchy
188         module. If the specified name does not belong, ane exception is 
189         thrown indicating the caller should contact someone else.
190
191         @param auth_name human readable name of authority
192         """
193
194         # get auth info will throw an exception if the authority doesnt exist
195         self.get_auth_info(name)
196
197
198     def verify_object_belongs_to_me(self, name):
199         """
200         Verify that an object belongs to our hierarchy. By extension,
201         this implies that the authority that owns the object belongs
202         to our hierarchy. If it does not an exception is thrown.
203     
204         @param name human readable name of object        
205         """
206         auth_name = self.get_authority(name)
207         if not auth_name:
208             auth_name = name 
209         if name == self.config.SFA_INTERFACE_HRN:
210             return
211         self.verify_auth_belongs_to_me(auth_name) 
212              
213     def verify_auth_belongs_to_me(self, name):
214         # get auth info will throw an exception if the authority doesnt exist
215         self.get_auth_info(name) 
216
217
218     def verify_object_permission(self, name):
219         """
220         Verify that the object gid that was specified in the credential
221         allows permission to the object 'name'. This is done by a simple
222         prefix test. For example, an object_gid for plc.arizona would 
223         match the objects plc.arizona.slice1 and plc.arizona.
224     
225         @param name human readable name to test  
226         """
227         object_hrn = self.object_gid.get_hrn()
228         strname = str(name).strip("['']")
229         
230         if object_hrn == strname:
231             return
232         if strname.startswith((object_hrn + ".")) is True:
233             return
234         #if name.startswith(get_authority(name)):
235             #return
236         print>>sys.stderr, " \r\n \t AUTH.PY  verify_object_permission GROSECHECDELENFER "
237         raise PermissionError(name)
238
239     def determine_user_rights(self, caller_hrn, record):
240         """
241         Given a user credential and a record, determine what set of rights the
242         user should have to that record.
243         
244         This is intended to replace determine_rights() and
245         verify_cancreate_credential()
246         """
247
248         rl = Rights()
249         type = record['type']
250
251
252         if type=="slice":
253             researchers = record.get("researcher", [])
254             pis = record.get("PI", [])
255             if (caller_hrn in researchers + pis):
256                 rl.add("refresh")
257                 rl.add("embed")
258                 rl.add("bind")
259                 rl.add("control")
260                 rl.add("info")
261
262         elif type == "authority":
263             pis = record.get("PI", [])
264             operators = record.get("operator", [])
265             if (caller_hrn == self.config.SFA_INTERFACE_HRN):
266                 rl.add("authority")
267                 rl.add("sa")
268                 rl.add("ma")
269             if (caller_hrn in pis):
270                 rl.add("authority")
271                 rl.add("sa")
272             if (caller_hrn in operators):
273                 rl.add("authority")
274                 rl.add("ma")
275
276         elif type == "user":
277             rl.add("refresh")
278             rl.add("resolve")
279             rl.add("info")
280
281         elif type == "node":
282             rl.add("operator")
283
284         return rl
285
286     def verify_cancreate_credential(self, src_cred, record):
287         """
288         Verify that a user can retrive a particular type of credential.
289         For slices, the user must be on the researcher list. For SA and
290         MA the user must be on the pi and operator lists respectively
291         """
292
293         type = record.get_type()
294         cred_object_hrn = src_cred.get_gid_object().get_hrn()
295         if cred_object_hrn in [self.config.SFA_REGISTRY_ROOT_AUTH]:
296             return
297         if type=="slice":
298             researchers = record.get("researcher", [])
299             if not (cred_object_hrn in researchers):
300                 raise PermissionError(cred_object_hrn + " is not in researcher list for " + record.get_name())
301         elif type == "sa":
302             pis = record.get("pi", [])
303             if not (cred_object_hrn in pis):
304                 raise PermissionError(cred_object_hrn + " is not in pi list for " + record.get_name())
305         elif type == "ma":
306             operators = record.get("operator", [])
307             if not (cred_object_hrn in operators):
308                 raise PermissionError(cred_object_hrn + " is not in operator list for " + record.get_name())
309
310     def get_authority(self, hrn):
311         return get_authority(hrn)
312
313     def filter_creds_by_caller(self, creds, caller_hrn_list):
314         """
315         Returns a list of creds who's gid caller matches the 
316         specified caller hrn
317         """
318         if not isinstance(creds, list):
319             creds = [creds]
320         creds = []
321         if not isinstance(caller_hrn_list, list):
322             caller_hrn_list = [caller_hrn_list]
323         for cred in creds:
324             try:
325                 tmp_cred = Credential(string=cred)
326                 if tmp_cred.get_gid_caller().get_hrn() in [caller_hrn_list]:
327                     creds.append(cred)
328             except: pass
329         return creds
330