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