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