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
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, speaking_for_hrn = None):
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 speaks_for_cred = None
49 if not isinstance(creds, list):
51 logger.debug("Auth.checkCredentials with %d creds"%len(creds))
54 self.check(cred, operation, hrn)
57 # check if credential is a 'speaks for credential'
60 self.check(cred, operation, speaking_for_hrn)
61 speaks_for_cred = cred
64 error = log_invalid_cred(cred)
66 error = log_invalid_cred(cred)
70 raise InsufficientRights('Access denied: %s -- %s' % (error[0],error[1]))
72 if speaking_for_hrn and not speaks_for_cred:
73 raise InsufficientRights('Access denied: "geni_speaking_for" option specified but no valid speaks for credential found: %s -- %s' % (error[0],error[1]))
79 def check(self, cred, 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 self.client_cred = Credential(string = cred)
88 self.client_gid = self.client_cred.get_gid_caller()
89 self.object_gid = self.client_cred.get_gid_object()
91 # make sure the client_gid is not blank
92 if not self.client_gid:
93 raise MissingCallerGID(self.client_cred.get_subject())
95 # validate the client cert if it exists
97 self.verifyPeerCert(self.peer_cert, self.client_gid)
99 # make sure the client is allowed to perform the operation
101 if not self.client_cred.can_perform(operation):
102 raise InsufficientRights(operation)
104 if self.trusted_cert_list:
105 self.client_cred.verify(self.trusted_cert_file_list, self.config.SFA_CREDENTIAL_SCHEMA)
107 raise MissingTrustedRoots(self.config.get_trustedroots_dir())
109 # Make sure the credential's target matches the specified hrn.
110 # This check does not apply to trusted peers
111 trusted_peers = [gid.get_hrn() for gid in self.trusted_cert_list]
112 if hrn and self.client_gid.get_hrn() not in trusted_peers:
113 target_hrn = self.object_gid.get_hrn()
114 if not hrn == target_hrn:
115 raise PermissionError("Target hrn: %s doesn't match specified hrn: %s " % \
119 def check_ticket(self, ticket):
121 Check if the tickt was signed by a trusted cert
123 if self.trusted_cert_list:
124 client_ticket = SfaTicket(string=ticket)
125 client_ticket.verify_chain(self.trusted_cert_list)
127 raise MissingTrustedRoots(self.config.get_trustedroots_dir())
131 def verifyPeerCert(self, cert, gid):
132 # make sure the client_gid matches client's certificate
133 if not cert.is_pubkey(gid.get_pubkey()):
134 raise ConnectionKeyGIDMismatch(gid.get_subject()+":"+cert.get_subject())
136 def verifyGidRequestHash(self, gid, hash, arglist):
137 key = gid.get_pubkey()
138 if not key.verify_string(str(arglist), hash):
139 raise BadRequestHash(hash)
141 def verifyCredRequestHash(self, cred, hash, arglist):
142 gid = cred.get_gid_caller()
143 self.verifyGidRequestHash(gid, hash, arglist)
145 def validateGid(self, gid):
146 if self.trusted_cert_list:
147 gid.verify_chain(self.trusted_cert_list)
149 def validateCred(self, cred):
150 if self.trusted_cert_list:
151 cred.verify(self.trusted_cert_file_list)
153 def authenticateGid(self, gidStr, argList, requestHash=None):
154 gid = GID(string = gidStr)
155 self.validateGid(gid)
156 # request_hash is optional
158 self.verifyGidRequestHash(gid, requestHash, argList)
161 def authenticateCred(self, credStr, argList, requestHash=None):
162 cred = Credential(string = credStr)
163 self.validateCred(cred)
164 # request hash is optional
166 self.verifyCredRequestHash(cred, requestHash, argList)
169 def authenticateCert(self, certStr, requestHash):
170 cert = Certificate(string=certStr)
171 # xxx should be validateCred ??
172 self.validateCred(cert)
174 def gidNoop(self, gidStr, value, requestHash):
175 self.authenticateGid(gidStr, [gidStr, value], requestHash)
178 def credNoop(self, credStr, value, requestHash):
179 self.authenticateCred(credStr, [credStr, value], requestHash)
182 def verify_cred_is_me(self, credential):
184 cred = Credential(string=credential)
185 caller_gid = cred.get_gid_caller()
186 caller_hrn = caller_gid.get_hrn()
187 if caller_hrn != self.config.SFA_INTERFACE_HRN:
188 raise SfaPermissionDenied(self.config.SFA_INTEFACE_HRN)
192 def get_auth_info(self, auth_hrn):
194 Given an authority name, return the information for that authority.
195 This is basically a stub that calls the hierarchy module.
197 @param auth_hrn human readable name of authority
200 return self.hierarchy.get_auth_info(auth_hrn)
203 def veriry_auth_belongs_to_me(self, name):
205 Verify that an authority belongs to our hierarchy.
206 This is basically left up to the implementation of the hierarchy
207 module. If the specified name does not belong, ane exception is
208 thrown indicating the caller should contact someone else.
210 @param auth_name human readable name of authority
213 # get auth info will throw an exception if the authority doesnt exist
214 self.get_auth_info(name)
217 def verify_object_belongs_to_me(self, name):
219 Verify that an object belongs to our hierarchy. By extension,
220 this implies that the authority that owns the object belongs
221 to our hierarchy. If it does not an exception is thrown.
223 @param name human readable name of object
225 auth_name = self.get_authority(name)
228 if name == self.config.SFA_INTERFACE_HRN:
230 self.verify_auth_belongs_to_me(auth_name)
232 def verify_auth_belongs_to_me(self, name):
233 # get auth info will throw an exception if the authority doesnt exist
234 self.get_auth_info(name)
237 def verify_object_permission(self, name):
239 Verify that the object gid that was specified in the credential
240 allows permission to the object 'name'. This is done by a simple
241 prefix test. For example, an object_gid for plc.arizona would
242 match the objects plc.arizona.slice1 and plc.arizona.
244 @param name human readable name to test
246 object_hrn = self.object_gid.get_hrn()
247 if object_hrn == name:
249 if name.startswith(object_hrn + "."):
251 #if name.startswith(get_authority(name)):
254 raise PermissionError(name)
256 def determine_user_rights(self, caller_hrn, reg_record):
258 Given a user credential and a record, determine what set of rights the
259 user should have to that record.
261 This is intended to replace determine_user_rights() and
262 verify_cancreate_credential()
266 type = reg_record.type
268 logger.debug("entering determine_user_rights with record %s and caller_hrn %s"%(reg_record, caller_hrn))
271 # researchers in the slice are in the DB as-is
272 researcher_hrns = [ user.hrn for user in reg_record.reg_researchers ]
273 # locating PIs attached to that slice
274 slice_pis=reg_record.get_pis()
275 pi_hrns = [ user.hrn for user in slice_pis ]
276 if (caller_hrn in researcher_hrns + pi_hrns):
283 elif type == 'authority':
284 pi_hrns = [ user.hrn for user in reg_record.reg_pis ]
285 if (caller_hrn == self.config.SFA_INTERFACE_HRN):
289 if (caller_hrn in pi_hrns):
292 # NOTE: for the PL implementation, this 'operators' list
293 # amounted to users with 'tech' role in that site
294 # it seems like this is not needed any longer, so for now I just drop that
295 # operator_hrns = reg_record.get('operator',[])
296 # if (caller_hrn in operator_hrns):
297 # rl.add('authority')
310 def get_authority(self, hrn):
311 return get_authority(hrn)
313 def filter_creds_by_caller(self, creds, caller_hrn_list):
315 Returns a list of creds who's gid caller matches the
318 if not isinstance(creds, list):
321 if not isinstance(caller_hrn_list, list):
322 caller_hrn_list = [caller_hrn_list]
325 tmp_cred = Credential(string=cred)
326 if tmp_cred.get_gid_caller().get_hrn() in [caller_hrn_list]: