2 # SfaAPI authentication
6 from sfa.util.faults import InsufficientRights, MissingCallerGID, MissingTrustedRoots, PermissionError, \
7 BadRequestHash, ConnectionKeyGIDMismatch, SfaPermissionDenied, CredentialNotVerifiable, Forbidden
8 from sfa.util.sfalogging import logger
9 from sfa.util.config import Config
10 from sfa.util.xrn import Xrn, get_authority
12 from sfa.trust.gid import GID
13 from sfa.trust.rights import Rights
14 from sfa.trust.certificate import Keypair, Certificate
15 from sfa.trust.credential import Credential
16 from sfa.trust.trustedroots import TrustedRoots
17 from sfa.trust.hierarchy import Hierarchy
18 from sfa.trust.sfaticket import SfaTicket
23 Credential based authentication
26 def __init__(self, peer_cert = None, config = None ):
27 self.peer_cert = peer_cert
28 self.hierarchy = Hierarchy()
30 self.config = Config()
31 self.load_trusted_certs()
33 def load_trusted_certs(self):
34 self.trusted_cert_list = TrustedRoots(self.config.get_trustedroots_dir()).get_list()
35 self.trusted_cert_file_list = TrustedRoots(self.config.get_trustedroots_dir()).get_file_list()
37 def checkCredentials(self, creds, operation, xrns=[], check_sliver_callback=None):
38 if not isinstance(xrns, list):
41 slice_xrns = Xrn.filter_type(xrns, 'slice')
42 sliver_xrns = Xrn.filter_type(xrns, 'sliver')
44 # we are not able to validate slivers in the traditional way so
45 # we make sure not to include sliver urns/hrns in the core validation loop
46 hrns = [Xrn(xrn).hrn for xrn in xrns if xrn not in sliver_xrns]
48 if not isinstance(creds, list):
50 logger.debug("Auth.checkCredentials with %d creds on hrns=%s"%(len(creds),hrns))
51 # won't work if either creds or hrns is empty - let's make it more explicit
52 if not creds: raise Forbidden("no credential provided")
53 if not hrns: hrns = [None]
57 self.check(cred, operation, hrn)
60 cred_obj=Credential(cred=cred)
61 logger.debug("failed to validate credential - dump=%s"%cred_obj.dump_string(dump_parents=True))
62 error = sys.exc_info()[:2]
65 # make sure all sliver xrns are validated against the valid credentials
67 if not check_sliver_callback:
68 msg = "sliver verification callback method not found."
69 msg += " Unable to validate sliver xrns: %s" % sliver_xrns
71 check_sliver_callback(valid, sliver_xrns)
74 raise Forbidden("Invalid credential")
79 def check(self, credential, operation, hrn = None):
81 Check the credential against the peer cert (callerGID included
82 in the credential matches the caller that is connected to the
83 HTTPS connection, check if the credential was signed by a
84 trusted cert and check if the credential is allowed to perform
85 the specified operation.
87 cred = Credential(cred=credential)
88 self.client_cred = cred
89 logger.debug("Auth.check: handling hrn=%s and credential=%s"%\
90 (hrn,cred.get_summary_tostring()))
92 if cred.type not in ['geni_sfa']:
93 raise CredentialNotVerifiable(cred.type, "%s not supported" % cred.type)
94 self.client_gid = self.client_cred.get_gid_caller()
95 self.object_gid = self.client_cred.get_gid_object()
97 # make sure the client_gid is not blank
98 if not self.client_gid:
99 raise MissingCallerGID(self.client_cred.get_subject())
101 # validate the client cert if it exists
103 self.verifyPeerCert(self.peer_cert, self.client_gid)
105 # make sure the client is allowed to perform the operation
107 if not self.client_cred.can_perform(operation):
108 raise InsufficientRights(operation)
110 if self.trusted_cert_list:
111 self.client_cred.verify(self.trusted_cert_file_list, self.config.SFA_CREDENTIAL_SCHEMA)
113 raise MissingTrustedRoots(self.config.get_trustedroots_dir())
115 # Make sure the credential's target matches the specified hrn.
116 # This check does not apply to trusted peers
117 trusted_peers = [gid.get_hrn() for gid in self.trusted_cert_list]
118 if hrn and self.client_gid.get_hrn() not in trusted_peers:
119 target_hrn = self.object_gid.get_hrn()
120 if not hrn == target_hrn:
121 raise PermissionError("Target hrn: %s doesn't match specified hrn: %s " % \
125 def check_ticket(self, ticket):
127 Check if the tickt was signed by a trusted cert
129 if self.trusted_cert_list:
130 client_ticket = SfaTicket(string=ticket)
131 client_ticket.verify_chain(self.trusted_cert_list)
133 raise MissingTrustedRoots(self.config.get_trustedroots_dir())
137 def verifyPeerCert(self, cert, gid):
138 # make sure the client_gid matches client's certificate
139 if not cert.is_pubkey(gid.get_pubkey()):
140 raise ConnectionKeyGIDMismatch(gid.get_subject()+":"+cert.get_subject())
142 def verifyGidRequestHash(self, gid, hash, arglist):
143 key = gid.get_pubkey()
144 if not key.verify_string(str(arglist), hash):
145 raise BadRequestHash(hash)
147 def verifyCredRequestHash(self, cred, hash, arglist):
148 gid = cred.get_gid_caller()
149 self.verifyGidRequestHash(gid, hash, arglist)
151 def validateGid(self, gid):
152 if self.trusted_cert_list:
153 gid.verify_chain(self.trusted_cert_list)
155 def validateCred(self, cred):
156 if self.trusted_cert_list:
157 cred.verify(self.trusted_cert_file_list)
159 def authenticateGid(self, gidStr, argList, requestHash=None):
160 gid = GID(string = gidStr)
161 self.validateGid(gid)
162 # request_hash is optional
164 self.verifyGidRequestHash(gid, requestHash, argList)
167 def authenticateCred(self, credStr, argList, requestHash=None):
168 cred = Credential(string = credStr)
169 self.validateCred(cred)
170 # request hash is optional
172 self.verifyCredRequestHash(cred, requestHash, argList)
175 def authenticateCert(self, certStr, requestHash):
176 cert = Certificate(string=certStr)
177 # xxx should be validateCred ??
178 self.validateCred(cert)
180 def gidNoop(self, gidStr, value, requestHash):
181 self.authenticateGid(gidStr, [gidStr, value], requestHash)
184 def credNoop(self, credStr, value, requestHash):
185 self.authenticateCred(credStr, [credStr, value], requestHash)
188 def verify_cred_is_me(self, credential):
190 cred = Credential(string=credential)
191 caller_gid = cred.get_gid_caller()
192 caller_hrn = caller_gid.get_hrn()
193 if caller_hrn != self.config.SFA_INTERFACE_HRN:
194 raise SfaPermissionDenied(self.config.SFA_INTEFACE_HRN)
198 def get_auth_info(self, auth_hrn):
200 Given an authority name, return the information for that authority.
201 This is basically a stub that calls the hierarchy module.
203 @param auth_hrn human readable name of authority
206 return self.hierarchy.get_auth_info(auth_hrn)
209 def veriry_auth_belongs_to_me(self, name):
211 Verify that an authority belongs to our hierarchy.
212 This is basically left up to the implementation of the hierarchy
213 module. If the specified name does not belong, ane exception is
214 thrown indicating the caller should contact someone else.
216 @param auth_name human readable name of authority
219 # get auth info will throw an exception if the authority doesnt exist
220 self.get_auth_info(name)
223 def verify_object_belongs_to_me(self, name):
225 Verify that an object belongs to our hierarchy. By extension,
226 this implies that the authority that owns the object belongs
227 to our hierarchy. If it does not an exception is thrown.
229 @param name human readable name of object
231 auth_name = self.get_authority(name)
234 if name == self.config.SFA_INTERFACE_HRN:
236 self.verify_auth_belongs_to_me(auth_name)
238 def verify_auth_belongs_to_me(self, name):
239 # get auth info will throw an exception if the authority doesnt exist
240 self.get_auth_info(name)
243 def verify_object_permission(self, name):
245 Verify that the object gid that was specified in the credential
246 allows permission to the object 'name'. This is done by a simple
247 prefix test. For example, an object_gid for plc.arizona would
248 match the objects plc.arizona.slice1 and plc.arizona.
250 @param name human readable name to test
252 object_hrn = self.object_gid.get_hrn()
253 if object_hrn == name:
255 if name.startswith(object_hrn + "."):
257 #if name.startswith(get_authority(name)):
260 raise PermissionError(name)
262 def determine_user_rights(self, caller_hrn, reg_record):
264 Given a user credential and a record, determine what set of rights the
265 user should have to that record.
267 This is intended to replace determine_user_rights() and
268 verify_cancreate_credential()
272 type = reg_record.type
274 logger.debug("entering determine_user_rights with record %s and caller_hrn %s"%(reg_record, caller_hrn))
277 # researchers in the slice are in the DB as-is
278 researcher_hrns = [ user.hrn for user in reg_record.reg_researchers ]
279 # locating PIs attached to that slice
280 slice_pis=reg_record.get_pis()
281 pi_hrns = [ user.hrn for user in slice_pis ]
282 if (caller_hrn in researcher_hrns + pi_hrns):
289 elif type == 'authority':
290 pi_hrns = [ user.hrn for user in reg_record.reg_pis ]
291 if (caller_hrn == self.config.SFA_INTERFACE_HRN):
295 if (caller_hrn in pi_hrns):
298 # NOTE: for the PL implementation, this 'operators' list
299 # amounted to users with 'tech' role in that site
300 # it seems like this is not needed any longer, so for now I just drop that
301 # operator_hrns = reg_record.get('operator',[])
302 # if (caller_hrn in operator_hrns):
303 # rl.add('authority')
316 def get_authority(self, hrn):
317 return get_authority(hrn)
319 def filter_creds_by_caller(self, creds, caller_hrn_list):
321 Returns a list of creds who's gid caller matches the
324 if not isinstance(creds, list):
327 if not isinstance(caller_hrn_list, list):
328 caller_hrn_list = [caller_hrn_list]
331 tmp_cred = Credential(string=cred)
332 if tmp_cred.get_gid_caller().get_hrn() in [caller_hrn_list]: