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