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