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
19 from sfa.trust.speaksfor_util import determine_speaks_for
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()
39 def checkCredentials(self, creds, operation, hrn = None, options = {}):
41 def log_invalid_cred(cred):
42 #cred_obj=Credential(string=cred)
43 #logger.debug("failed to validate credential - dump=%s"%cred_obj.dump_string(dump_parents=True))
44 error = sys.exc_info()[:2]
48 if not isinstance(creds, list):
51 # if speaks for gid matches caller cert then we've found a valid
52 # speaks for credential
53 speaks_for_gid = determine_speaks_for(logger, creds, self.peer_cert, \
54 options, self.trusted_cert_list)
55 if self.peer_cert and \
56 not self.peer_cert.is_pubkey(speaks_for_gid.get_pubkey()):
61 self.check(cred, operation, hrn)
64 error = log_invalid_cred(cred)
67 raise InsufficientRights('Access denied: %s -- %s' % (error[0],error[1]))
72 def check(self, cred, operation, hrn = None):
74 Check the credential against the peer cert (callerGID included
75 in the credential matches the caller that is connected to the
76 HTTPS connection, check if the credential was signed by a
77 trusted cert and check if the credential is allowed to perform
78 the specified operation.
80 self.client_cred = Credential(string = cred)
81 self.client_gid = self.client_cred.get_gid_caller()
82 self.object_gid = self.client_cred.get_gid_object()
84 # make sure the client_gid is not blank
85 if not self.client_gid:
86 raise MissingCallerGID(self.client_cred.get_subject())
88 # validate the client cert if it exists
90 self.verifyPeerCert(self.peer_cert, self.client_gid)
92 # make sure the client is allowed to perform the operation
94 if not self.client_cred.can_perform(operation):
95 raise InsufficientRights(operation)
97 if self.trusted_cert_list:
98 self.client_cred.verify(self.trusted_cert_file_list, self.config.SFA_CREDENTIAL_SCHEMA)
100 raise MissingTrustedRoots(self.config.get_trustedroots_dir())
102 # Make sure the credential's target matches the specified hrn.
103 # This check does not apply to trusted peers
104 trusted_peers = [gid.get_hrn() for gid in self.trusted_cert_list]
105 if hrn and self.client_gid.get_hrn() not in trusted_peers:
106 target_hrn = self.object_gid.get_hrn()
107 if not hrn == target_hrn:
108 raise PermissionError("Target hrn: %s doesn't match specified hrn: %s " % \
112 def check_ticket(self, ticket):
114 Check if the tickt was signed by a trusted cert
116 if self.trusted_cert_list:
117 client_ticket = SfaTicket(string=ticket)
118 client_ticket.verify_chain(self.trusted_cert_list)
120 raise MissingTrustedRoots(self.config.get_trustedroots_dir())
124 def verifyPeerCert(self, cert, gid):
125 # make sure the client_gid matches client's certificate
126 if not cert.is_pubkey(gid.get_pubkey()):
127 raise ConnectionKeyGIDMismatch(gid.get_subject()+":"+cert.get_subject())
129 def verifyGidRequestHash(self, gid, hash, arglist):
130 key = gid.get_pubkey()
131 if not key.verify_string(str(arglist), hash):
132 raise BadRequestHash(hash)
134 def verifyCredRequestHash(self, cred, hash, arglist):
135 gid = cred.get_gid_caller()
136 self.verifyGidRequestHash(gid, hash, arglist)
138 def validateGid(self, gid):
139 if self.trusted_cert_list:
140 gid.verify_chain(self.trusted_cert_list)
142 def validateCred(self, cred):
143 if self.trusted_cert_list:
144 cred.verify(self.trusted_cert_file_list)
146 def authenticateGid(self, gidStr, argList, requestHash=None):
147 gid = GID(string = gidStr)
148 self.validateGid(gid)
149 # request_hash is optional
151 self.verifyGidRequestHash(gid, requestHash, argList)
154 def authenticateCred(self, credStr, argList, requestHash=None):
155 cred = Credential(string = credStr)
156 self.validateCred(cred)
157 # request hash is optional
159 self.verifyCredRequestHash(cred, requestHash, argList)
162 def authenticateCert(self, certStr, requestHash):
163 cert = Certificate(string=certStr)
164 # xxx should be validateCred ??
165 self.validateCred(cert)
167 def gidNoop(self, gidStr, value, requestHash):
168 self.authenticateGid(gidStr, [gidStr, value], requestHash)
171 def credNoop(self, credStr, value, requestHash):
172 self.authenticateCred(credStr, [credStr, value], requestHash)
175 def verify_cred_is_me(self, credential):
177 cred = Credential(string=credential)
178 caller_gid = cred.get_gid_caller()
179 caller_hrn = caller_gid.get_hrn()
180 if caller_hrn != self.config.SFA_INTERFACE_HRN:
181 raise SfaPermissionDenied(self.config.SFA_INTEFACE_HRN)
185 def get_auth_info(self, auth_hrn):
187 Given an authority name, return the information for that authority.
188 This is basically a stub that calls the hierarchy module.
190 @param auth_hrn human readable name of authority
193 return self.hierarchy.get_auth_info(auth_hrn)
196 def veriry_auth_belongs_to_me(self, name):
198 Verify that an authority belongs to our hierarchy.
199 This is basically left up to the implementation of the hierarchy
200 module. If the specified name does not belong, ane exception is
201 thrown indicating the caller should contact someone else.
203 @param auth_name human readable name of authority
206 # get auth info will throw an exception if the authority doesnt exist
207 self.get_auth_info(name)
210 def verify_object_belongs_to_me(self, name):
212 Verify that an object belongs to our hierarchy. By extension,
213 this implies that the authority that owns the object belongs
214 to our hierarchy. If it does not an exception is thrown.
216 @param name human readable name of object
218 auth_name = self.get_authority(name)
221 if name == self.config.SFA_INTERFACE_HRN:
223 self.verify_auth_belongs_to_me(auth_name)
225 def verify_auth_belongs_to_me(self, name):
226 # get auth info will throw an exception if the authority doesnt exist
227 self.get_auth_info(name)
230 def verify_object_permission(self, name):
232 Verify that the object gid that was specified in the credential
233 allows permission to the object 'name'. This is done by a simple
234 prefix test. For example, an object_gid for plc.arizona would
235 match the objects plc.arizona.slice1 and plc.arizona.
237 @param name human readable name to test
239 object_hrn = self.object_gid.get_hrn()
240 if object_hrn == name:
242 if name.startswith(object_hrn + "."):
244 #if name.startswith(get_authority(name)):
247 raise PermissionError(name)
249 def determine_user_rights(self, caller_hrn, reg_record):
251 Given a user credential and a record, determine what set of rights the
252 user should have to that record.
254 This is intended to replace determine_user_rights() and
255 verify_cancreate_credential()
259 type = reg_record.type
261 logger.debug("entering determine_user_rights with record %s and caller_hrn %s"%(reg_record, caller_hrn))
264 # researchers in the slice are in the DB as-is
265 researcher_hrns = [ user.hrn for user in reg_record.reg_researchers ]
266 # locating PIs attached to that slice
267 slice_pis=reg_record.get_pis()
268 pi_hrns = [ user.hrn for user in slice_pis ]
269 if (caller_hrn in researcher_hrns + pi_hrns):
276 elif type == 'authority':
277 pi_hrns = [ user.hrn for user in reg_record.reg_pis ]
278 if (caller_hrn == self.config.SFA_INTERFACE_HRN):
282 if (caller_hrn in pi_hrns):
285 # NOTE: for the PL implementation, this 'operators' list
286 # amounted to users with 'tech' role in that site
287 # it seems like this is not needed any longer, so for now I just drop that
288 # operator_hrns = reg_record.get('operator',[])
289 # if (caller_hrn in operator_hrns):
290 # rl.add('authority')
303 def get_authority(self, hrn):
304 return get_authority(hrn)
306 def filter_creds_by_caller(self, creds, caller_hrn_list):
308 Returns a list of creds who's gid caller matches the
311 if not isinstance(creds, list):
314 if not isinstance(caller_hrn_list, list):
315 caller_hrn_list = [caller_hrn_list]
318 tmp_cred = Credential(string=cred)
319 if tmp_cred.get_gid_caller().get_hrn() in [caller_hrn_list]: