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