2 # SfaAPI authentication
6 from sfa.util.faults import InsufficientRights, MissingCallerGID, MissingTrustedRoots, PermissionError, \
7 BadRequestHash, ConnectionKeyGIDMismatch, SfaPermissionDenied
8 from sfa.util.sfalogging import logger
9 from sfa.util.config import Config
10 from sfa.util.xrn import get_authority, Xrn
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()
38 def checkCredentials(self, creds, operation, hrn = None, options = {}):
40 def log_invalid_cred(cred):
41 #cred_obj=Credential(string=cred)
42 #logger.debug("failed to validate credential - dump=%s"%cred_obj.dump_string(dump_parents=True))
43 error = sys.exc_info()[:2]
47 speaking_for = options.get('geni_speaking_for', None)
48 speaks_for_cred = None
50 if not isinstance(creds, list):
52 logger.debug("Auth.checkCredentials with %d creds"%len(creds))
55 self.check(cred, operation, hrn)
58 # check if credential is a 'speaks for credential'
61 speaking_for_xrn = Xrn(speaking_for)
62 speaking_for_hrn = speaking_for_xrn.get_hrn()
63 self.check(cred, operation, speaking_for_hrn)
64 speaks_for_cred = cred
67 error = log_invalid_cred(cred)
69 error = log_invalid_cred(cred)
73 raise InsufficientRights('Access denied: %s -- %s' % (error[0],error[1]))
75 if speaking_for and not speaks_for_cred:
76 raise InsufficientRights('Access denied: "geni_speaking_for" option specified but no valid speaks for credential found: %s -- %s' % (error[0],error[1]))
82 def check(self, cred, operation, hrn = None):
84 Check the credential against the peer cert (callerGID included
85 in the credential matches the caller that is connected to the
86 HTTPS connection, check if the credential was signed by a
87 trusted cert and check if the credential is allowed to perform
88 the specified operation.
90 self.client_cred = Credential(string = cred)
91 self.client_gid = self.client_cred.get_gid_caller()
92 self.object_gid = self.client_cred.get_gid_object()
94 # make sure the client_gid is not blank
95 if not self.client_gid:
96 raise MissingCallerGID(self.client_cred.get_subject())
98 # validate the client cert if it exists
100 self.verifyPeerCert(self.peer_cert, self.client_gid)
102 # make sure the client is allowed to perform the operation
104 if not self.client_cred.can_perform(operation):
105 raise InsufficientRights(operation)
107 if self.trusted_cert_list:
108 self.client_cred.verify(self.trusted_cert_file_list, self.config.SFA_CREDENTIAL_SCHEMA)
110 raise MissingTrustedRoots(self.config.get_trustedroots_dir())
112 # Make sure the credential's target matches the specified hrn.
113 # This check does not apply to trusted peers
114 trusted_peers = [gid.get_hrn() for gid in self.trusted_cert_list]
115 if hrn and self.client_gid.get_hrn() not in trusted_peers:
116 target_hrn = self.object_gid.get_hrn()
117 if not hrn == target_hrn:
118 raise PermissionError("Target hrn: %s doesn't match specified hrn: %s " % \
122 def check_ticket(self, ticket):
124 Check if the tickt was signed by a trusted cert
126 if self.trusted_cert_list:
127 client_ticket = SfaTicket(string=ticket)
128 client_ticket.verify_chain(self.trusted_cert_list)
130 raise MissingTrustedRoots(self.config.get_trustedroots_dir())
134 def verifyPeerCert(self, cert, gid):
135 # make sure the client_gid matches client's certificate
136 if not cert.is_pubkey(gid.get_pubkey()):
137 raise ConnectionKeyGIDMismatch(gid.get_subject()+":"+cert.get_subject())
139 def verifyGidRequestHash(self, gid, hash, arglist):
140 key = gid.get_pubkey()
141 if not key.verify_string(str(arglist), hash):
142 raise BadRequestHash(hash)
144 def verifyCredRequestHash(self, cred, hash, arglist):
145 gid = cred.get_gid_caller()
146 self.verifyGidRequestHash(gid, hash, arglist)
148 def validateGid(self, gid):
149 if self.trusted_cert_list:
150 gid.verify_chain(self.trusted_cert_list)
152 def validateCred(self, cred):
153 if self.trusted_cert_list:
154 cred.verify(self.trusted_cert_file_list)
156 def authenticateGid(self, gidStr, argList, requestHash=None):
157 gid = GID(string = gidStr)
158 self.validateGid(gid)
159 # request_hash is optional
161 self.verifyGidRequestHash(gid, requestHash, argList)
164 def authenticateCred(self, credStr, argList, requestHash=None):
165 cred = Credential(string = credStr)
166 self.validateCred(cred)
167 # request hash is optional
169 self.verifyCredRequestHash(cred, requestHash, argList)
172 def authenticateCert(self, certStr, requestHash):
173 cert = Certificate(string=certStr)
174 # xxx should be validateCred ??
175 self.validateCred(cert)
177 def gidNoop(self, gidStr, value, requestHash):
178 self.authenticateGid(gidStr, [gidStr, value], requestHash)
181 def credNoop(self, credStr, value, requestHash):
182 self.authenticateCred(credStr, [credStr, value], requestHash)
185 def verify_cred_is_me(self, credential):
187 cred = Credential(string=credential)
188 caller_gid = cred.get_gid_caller()
189 caller_hrn = caller_gid.get_hrn()
190 if caller_hrn != self.config.SFA_INTERFACE_HRN:
191 raise SfaPermissionDenied(self.config.SFA_INTEFACE_HRN)
195 def get_auth_info(self, auth_hrn):
197 Given an authority name, return the information for that authority.
198 This is basically a stub that calls the hierarchy module.
200 @param auth_hrn human readable name of authority
203 return self.hierarchy.get_auth_info(auth_hrn)
206 def veriry_auth_belongs_to_me(self, name):
208 Verify that an authority belongs to our hierarchy.
209 This is basically left up to the implementation of the hierarchy
210 module. If the specified name does not belong, ane exception is
211 thrown indicating the caller should contact someone else.
213 @param auth_name human readable name of authority
216 # get auth info will throw an exception if the authority doesnt exist
217 self.get_auth_info(name)
220 def verify_object_belongs_to_me(self, name):
222 Verify that an object belongs to our hierarchy. By extension,
223 this implies that the authority that owns the object belongs
224 to our hierarchy. If it does not an exception is thrown.
226 @param name human readable name of object
228 auth_name = self.get_authority(name)
231 if name == self.config.SFA_INTERFACE_HRN:
233 self.verify_auth_belongs_to_me(auth_name)
235 def verify_auth_belongs_to_me(self, name):
236 # get auth info will throw an exception if the authority doesnt exist
237 self.get_auth_info(name)
240 def verify_object_permission(self, name):
242 Verify that the object gid that was specified in the credential
243 allows permission to the object 'name'. This is done by a simple
244 prefix test. For example, an object_gid for plc.arizona would
245 match the objects plc.arizona.slice1 and plc.arizona.
247 @param name human readable name to test
249 object_hrn = self.object_gid.get_hrn()
250 if object_hrn == name:
252 if name.startswith(object_hrn + "."):
254 #if name.startswith(get_authority(name)):
257 raise PermissionError(name)
259 def determine_user_rights(self, caller_hrn, reg_record):
261 Given a user credential and a record, determine what set of rights the
262 user should have to that record.
264 This is intended to replace determine_user_rights() and
265 verify_cancreate_credential()
269 type = reg_record.type
271 logger.debug("entering determine_user_rights with record %s and caller_hrn %s"%(reg_record, caller_hrn))
274 # researchers in the slice are in the DB as-is
275 researcher_hrns = [ user.hrn for user in reg_record.reg_researchers ]
276 # locating PIs attached to that slice
277 slice_pis=reg_record.get_pis()
278 pi_hrns = [ user.hrn for user in slice_pis ]
279 if (caller_hrn in researcher_hrns + pi_hrns):
286 elif type == 'authority':
287 pi_hrns = [ user.hrn for user in reg_record.reg_pis ]
288 if (caller_hrn == self.config.SFA_INTERFACE_HRN):
292 if (caller_hrn in pi_hrns):
295 # NOTE: for the PL implementation, this 'operators' list
296 # amounted to users with 'tech' role in that site
297 # it seems like this is not needed any longer, so for now I just drop that
298 # operator_hrns = reg_record.get('operator',[])
299 # if (caller_hrn in operator_hrns):
300 # rl.add('authority')
313 def get_authority(self, hrn):
314 return get_authority(hrn)
316 def filter_creds_by_caller(self, creds, caller_hrn_list):
318 Returns a list of creds who's gid caller matches the
321 if not isinstance(creds, list):
324 if not isinstance(caller_hrn_list, list):
325 caller_hrn_list = [caller_hrn_list]
328 tmp_cred = Credential(string=cred)
329 if tmp_cred.get_gid_caller().get_hrn() in [caller_hrn_list]: