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