2 # SfaAPI authentication
6 from sfa.util.faults import InsufficientRights, MissingCallerGID, MissingTrustedRoots, PermissionError, \
7 BadRequestHash, ConnectionKeyGIDMismatch, SfaPermissionDenied, CredentialNotVerifiable, Forbidden, \
9 from sfa.util.sfalogging import logger
10 from sfa.util.config import Config
11 from sfa.util.xrn import Xrn, get_authority
13 from sfa.trust.gid import GID
14 from sfa.trust.rights import Rights
15 from sfa.trust.certificate import Keypair, Certificate
16 from sfa.trust.credential import Credential
17 from sfa.trust.trustedroots import TrustedRoots
18 from sfa.trust.hierarchy import Hierarchy
19 from sfa.trust.sfaticket import SfaTicket
24 Credential based authentication
27 def __init__(self, peer_cert = None, config = None ):
28 self.peer_cert = peer_cert
29 self.hierarchy = Hierarchy()
31 self.config = Config()
32 self.load_trusted_certs()
34 def load_trusted_certs(self):
35 self.trusted_cert_list = TrustedRoots(self.config.get_trustedroots_dir()).get_list()
36 self.trusted_cert_file_list = TrustedRoots(self.config.get_trustedroots_dir()).get_file_list()
38 def checkCredentials(self, creds, operation, xrns=[], check_sliver_callback=None):
39 if not isinstance(xrns, list):
42 slice_xrns = Xrn.filter_type(xrns, 'slice')
43 sliver_xrns = Xrn.filter_type(xrns, 'sliver')
45 # we are not able to validate slivers in the traditional way so
46 # we make sure not to include sliver urns/hrns in the core validation loop
47 hrns = [Xrn(xrn).hrn for xrn in xrns if xrn not in sliver_xrns]
49 if not isinstance(creds, list):
51 logger.debug("Auth.checkCredentials with %d creds on hrns=%s"%(len(creds),hrns))
52 # won't work if either creds or hrns is empty - let's make it more explicit
53 if not creds: raise Forbidden("no credential provided")
54 if not hrns: hrns = [None]
57 # if an hrn is specified it cannot be None or empty string
59 raise BadArgs("Invalid urn or hrn: %s" % hrn)
62 self.check(cred, operation, hrn)
65 cred_obj=Credential(cred=cred)
66 logger.debug("failed to validate credential - dump=%s"%cred_obj.dump_string(dump_parents=True))
67 error = sys.exc_info()[:2]
70 # make sure all sliver xrns are validated against the valid credentials
72 if not check_sliver_callback:
73 msg = "sliver verification callback method not found."
74 msg += " Unable to validate sliver xrns: %s" % sliver_xrns
76 check_sliver_callback(valid, sliver_xrns)
79 raise Forbidden("Invalid credential")
84 def check(self, credential, operation, hrn = None):
86 Check the credential against the peer cert (callerGID included
87 in the credential matches the caller that is connected to the
88 HTTPS connection, check if the credential was signed by a
89 trusted cert and check if the credential is allowed to perform
90 the specified operation.
92 cred = Credential(cred=credential)
93 self.client_cred = cred
94 logger.debug("Auth.check: handling hrn=%s and credential=%s"%\
95 (hrn,cred.get_summary_tostring()))
97 if cred.type not in ['geni_sfa']:
98 raise CredentialNotVerifiable(cred.type, "%s not supported" % cred.type)
99 self.client_gid = self.client_cred.get_gid_caller()
100 self.object_gid = self.client_cred.get_gid_object()
102 # make sure the client_gid is not blank
103 if not self.client_gid:
104 raise MissingCallerGID(self.client_cred.get_subject())
106 # validate the client cert if it exists
108 self.verifyPeerCert(self.peer_cert, self.client_gid)
110 # make sure the client is allowed to perform the operation
112 if not self.client_cred.can_perform(operation):
113 raise InsufficientRights(operation)
115 if self.trusted_cert_list:
116 self.client_cred.verify(self.trusted_cert_file_list, self.config.SFA_CREDENTIAL_SCHEMA)
118 raise MissingTrustedRoots(self.config.get_trustedroots_dir())
120 # Make sure the credential's target matches the specified hrn.
121 # This check does not apply to trusted peers
122 trusted_peers = [gid.get_hrn() for gid in self.trusted_cert_list]
123 if hrn and self.client_gid.get_hrn() not in trusted_peers:
124 target_hrn = self.object_gid.get_hrn()
125 if not hrn == target_hrn:
126 raise PermissionError("Target hrn: %s doesn't match specified hrn: %s " % \
130 def check_ticket(self, ticket):
132 Check if the tickt was signed by a trusted cert
134 if self.trusted_cert_list:
135 client_ticket = SfaTicket(string=ticket)
136 client_ticket.verify_chain(self.trusted_cert_list)
138 raise MissingTrustedRoots(self.config.get_trustedroots_dir())
142 def verifyPeerCert(self, cert, gid):
143 # make sure the client_gid matches client's certificate
144 if not cert.is_pubkey(gid.get_pubkey()):
145 raise ConnectionKeyGIDMismatch(gid.get_subject()+":"+cert.get_subject())
147 def verifyGidRequestHash(self, gid, hash, arglist):
148 key = gid.get_pubkey()
149 if not key.verify_string(str(arglist), hash):
150 raise BadRequestHash(hash)
152 def verifyCredRequestHash(self, cred, hash, arglist):
153 gid = cred.get_gid_caller()
154 self.verifyGidRequestHash(gid, hash, arglist)
156 def validateGid(self, gid):
157 if self.trusted_cert_list:
158 gid.verify_chain(self.trusted_cert_list)
160 def validateCred(self, cred):
161 if self.trusted_cert_list:
162 cred.verify(self.trusted_cert_file_list)
164 def authenticateGid(self, gidStr, argList, requestHash=None):
165 gid = GID(string = gidStr)
166 self.validateGid(gid)
167 # request_hash is optional
169 self.verifyGidRequestHash(gid, requestHash, argList)
172 def authenticateCred(self, credStr, argList, requestHash=None):
173 cred = Credential(string = credStr)
174 self.validateCred(cred)
175 # request hash is optional
177 self.verifyCredRequestHash(cred, requestHash, argList)
180 def authenticateCert(self, certStr, requestHash):
181 cert = Certificate(string=certStr)
182 # xxx should be validateCred ??
183 self.validateCred(cert)
185 def gidNoop(self, gidStr, value, requestHash):
186 self.authenticateGid(gidStr, [gidStr, value], requestHash)
189 def credNoop(self, credStr, value, requestHash):
190 self.authenticateCred(credStr, [credStr, value], requestHash)
193 def verify_cred_is_me(self, credential):
195 cred = Credential(string=credential)
196 caller_gid = cred.get_gid_caller()
197 caller_hrn = caller_gid.get_hrn()
198 if caller_hrn != self.config.SFA_INTERFACE_HRN:
199 raise SfaPermissionDenied(self.config.SFA_INTEFACE_HRN)
203 def get_auth_info(self, auth_hrn):
205 Given an authority name, return the information for that authority.
206 This is basically a stub that calls the hierarchy module.
208 @param auth_hrn human readable name of authority
211 return self.hierarchy.get_auth_info(auth_hrn)
214 def veriry_auth_belongs_to_me(self, name):
216 Verify that an authority belongs to our hierarchy.
217 This is basically left up to the implementation of the hierarchy
218 module. If the specified name does not belong, ane exception is
219 thrown indicating the caller should contact someone else.
221 @param auth_name human readable name of authority
224 # get auth info will throw an exception if the authority doesnt exist
225 self.get_auth_info(name)
228 def verify_object_belongs_to_me(self, name):
230 Verify that an object belongs to our hierarchy. By extension,
231 this implies that the authority that owns the object belongs
232 to our hierarchy. If it does not an exception is thrown.
234 @param name human readable name of object
236 auth_name = self.get_authority(name)
239 if name == self.config.SFA_INTERFACE_HRN:
241 self.verify_auth_belongs_to_me(auth_name)
243 def verify_auth_belongs_to_me(self, name):
244 # get auth info will throw an exception if the authority doesnt exist
245 self.get_auth_info(name)
248 def verify_object_permission(self, name):
250 Verify that the object gid that was specified in the credential
251 allows permission to the object 'name'. This is done by a simple
252 prefix test. For example, an object_gid for plc.arizona would
253 match the objects plc.arizona.slice1 and plc.arizona.
255 @param name human readable name to test
257 object_hrn = self.object_gid.get_hrn()
258 if object_hrn == name:
260 if name.startswith(object_hrn + "."):
262 #if name.startswith(get_authority(name)):
265 raise PermissionError(name)
267 def determine_user_rights(self, caller_hrn, reg_record):
269 Given a user credential and a record, determine what set of rights the
270 user should have to that record.
272 This is intended to replace determine_user_rights() and
273 verify_cancreate_credential()
277 type = reg_record.type
279 logger.debug("entering determine_user_rights with record %s and caller_hrn %s"%(reg_record, caller_hrn))
282 # researchers in the slice are in the DB as-is
283 researcher_hrns = [ user.hrn for user in reg_record.reg_researchers ]
284 # locating PIs attached to that slice
285 slice_pis=reg_record.get_pis()
286 pi_hrns = [ user.hrn for user in slice_pis ]
287 if (caller_hrn in researcher_hrns + pi_hrns):
294 elif type == 'authority':
295 pi_hrns = [ user.hrn for user in reg_record.reg_pis ]
296 if (caller_hrn == self.config.SFA_INTERFACE_HRN):
300 if (caller_hrn in pi_hrns):
303 # NOTE: for the PL implementation, this 'operators' list
304 # amounted to users with 'tech' role in that site
305 # it seems like this is not needed any longer, so for now I just drop that
306 # operator_hrns = reg_record.get('operator',[])
307 # if (caller_hrn in operator_hrns):
308 # rl.add('authority')
321 def get_authority(self, hrn):
322 return get_authority(hrn)
324 def filter_creds_by_caller(self, creds, caller_hrn_list):
326 Returns a list of creds who's gid caller matches the
329 if not isinstance(creds, list):
332 if not isinstance(caller_hrn_list, list):
333 caller_hrn_list = [caller_hrn_list]
336 tmp_cred = Credential(string=cred)
337 if tmp_cred.get_gid_caller().get_hrn() in [caller_hrn_list]: