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 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()
39 def checkCredentials(self, creds, operation, xrns=[]):
40 if not isinstance(xrns, list):
42 hrns = [Xrn(xrn).hrn for xrn in xrns]
44 if not isinstance(creds, list):
46 logger.debug("Auth.checkCredentials with %d creds"%len(creds))
47 error=[ "no credential","was given"]
51 self.check(cred, operation, hrn)
54 cred_obj=Credential(string=cred)
55 logger.debug("failed to validate credential - dump=%s"%cred_obj.dump_string(dump_parents=True))
56 error = sys.exc_info()[:2]
60 raise InsufficientRights('Access denied: %s -- %s' % (error[0],error[1]))
65 def check(self, cred, operation, hrn = None):
67 Check the credential against the peer cert (callerGID included
68 in the credential matches the caller that is connected to the
69 HTTPS connection, check if the credential was signed by a
70 trusted cert and check if the credential is allowed to perform
71 the specified operation.
73 self.client_cred = Credential(string = cred)
74 logger.debug("Auth.check: handling hrn=%s and credential=%s"%\
75 (hrn,cred.get_summary_tostring()))
76 self.client_gid = self.client_cred.get_gid_caller()
77 self.object_gid = self.client_cred.get_gid_object()
79 # make sure the client_gid is not blank
80 if not self.client_gid:
81 raise MissingCallerGID(self.client_cred.get_subject())
83 # validate the client cert if it exists
85 self.verifyPeerCert(self.peer_cert, self.client_gid)
87 # make sure the client is allowed to perform the operation
89 if not self.client_cred.can_perform(operation):
90 raise InsufficientRights(operation)
92 if self.trusted_cert_list:
93 self.client_cred.verify(self.trusted_cert_file_list, self.config.SFA_CREDENTIAL_SCHEMA)
95 raise MissingTrustedRoots(self.config.get_trustedroots_dir())
97 # Make sure the credential's target matches the specified hrn.
98 # This check does not apply to trusted peers
99 trusted_peers = [gid.get_hrn() for gid in self.trusted_cert_list]
100 if hrn and self.client_gid.get_hrn() not in trusted_peers:
101 target_hrn = self.object_gid.get_hrn()
102 if not hrn == target_hrn:
103 raise PermissionError("Target hrn: %s doesn't match specified hrn: %s " % \
107 def check_ticket(self, ticket):
109 Check if the tickt was signed by a trusted cert
111 if self.trusted_cert_list:
112 client_ticket = SfaTicket(string=ticket)
113 client_ticket.verify_chain(self.trusted_cert_list)
115 raise MissingTrustedRoots(self.config.get_trustedroots_dir())
119 def verifyPeerCert(self, cert, gid):
120 # make sure the client_gid matches client's certificate
121 if not cert.is_pubkey(gid.get_pubkey()):
122 raise ConnectionKeyGIDMismatch(gid.get_subject()+":"+cert.get_subject())
124 def verifyGidRequestHash(self, gid, hash, arglist):
125 key = gid.get_pubkey()
126 if not key.verify_string(str(arglist), hash):
127 raise BadRequestHash(hash)
129 def verifyCredRequestHash(self, cred, hash, arglist):
130 gid = cred.get_gid_caller()
131 self.verifyGidRequestHash(gid, hash, arglist)
133 def validateGid(self, gid):
134 if self.trusted_cert_list:
135 gid.verify_chain(self.trusted_cert_list)
137 def validateCred(self, cred):
138 if self.trusted_cert_list:
139 cred.verify(self.trusted_cert_file_list)
141 def authenticateGid(self, gidStr, argList, requestHash=None):
142 gid = GID(string = gidStr)
143 self.validateGid(gid)
144 # request_hash is optional
146 self.verifyGidRequestHash(gid, requestHash, argList)
149 def authenticateCred(self, credStr, argList, requestHash=None):
150 cred = Credential(string = credStr)
151 self.validateCred(cred)
152 # request hash is optional
154 self.verifyCredRequestHash(cred, requestHash, argList)
157 def authenticateCert(self, certStr, requestHash):
158 cert = Certificate(string=certStr)
159 # xxx should be validateCred ??
160 self.validateCred(cert)
162 def gidNoop(self, gidStr, value, requestHash):
163 self.authenticateGid(gidStr, [gidStr, value], requestHash)
166 def credNoop(self, credStr, value, requestHash):
167 self.authenticateCred(credStr, [credStr, value], requestHash)
170 def verify_cred_is_me(self, credential):
172 cred = Credential(string=credential)
173 caller_gid = cred.get_gid_caller()
174 caller_hrn = caller_gid.get_hrn()
175 if caller_hrn != self.config.SFA_INTERFACE_HRN:
176 raise SfaPermissionDenied(self.config.SFA_INTEFACE_HRN)
180 def get_auth_info(self, auth_hrn):
182 Given an authority name, return the information for that authority.
183 This is basically a stub that calls the hierarchy module.
185 @param auth_hrn human readable name of authority
188 return self.hierarchy.get_auth_info(auth_hrn)
191 def veriry_auth_belongs_to_me(self, name):
193 Verify that an authority belongs to our hierarchy.
194 This is basically left up to the implementation of the hierarchy
195 module. If the specified name does not belong, ane exception is
196 thrown indicating the caller should contact someone else.
198 @param auth_name human readable name of authority
201 # get auth info will throw an exception if the authority doesnt exist
202 self.get_auth_info(name)
205 def verify_object_belongs_to_me(self, name):
207 Verify that an object belongs to our hierarchy. By extension,
208 this implies that the authority that owns the object belongs
209 to our hierarchy. If it does not an exception is thrown.
211 @param name human readable name of object
213 auth_name = self.get_authority(name)
216 if name == self.config.SFA_INTERFACE_HRN:
218 self.verify_auth_belongs_to_me(auth_name)
220 def verify_auth_belongs_to_me(self, name):
221 # get auth info will throw an exception if the authority doesnt exist
222 self.get_auth_info(name)
225 def verify_object_permission(self, name):
227 Verify that the object gid that was specified in the credential
228 allows permission to the object 'name'. This is done by a simple
229 prefix test. For example, an object_gid for plc.arizona would
230 match the objects plc.arizona.slice1 and plc.arizona.
232 @param name human readable name to test
234 object_hrn = self.object_gid.get_hrn()
235 if object_hrn == name:
237 if name.startswith(object_hrn + "."):
239 #if name.startswith(get_authority(name)):
242 raise PermissionError(name)
244 def determine_user_rights(self, caller_hrn, reg_record):
246 Given a user credential and a record, determine what set of rights the
247 user should have to that record.
249 This is intended to replace determine_user_rights() and
250 verify_cancreate_credential()
254 type = reg_record.type
256 logger.debug("entering determine_user_rights with record %s and caller_hrn %s"%(reg_record, caller_hrn))
259 # researchers in the slice are in the DB as-is
260 researcher_hrns = [ user.hrn for user in reg_record.reg_researchers ]
261 # locating PIs attached to that slice
262 slice_pis=reg_record.get_pis()
263 pi_hrns = [ user.hrn for user in slice_pis ]
264 if (caller_hrn in researcher_hrns + pi_hrns):
271 elif type == 'authority':
272 pi_hrns = [ user.hrn for user in reg_record.reg_pis ]
273 if (caller_hrn == self.config.SFA_INTERFACE_HRN):
277 if (caller_hrn in pi_hrns):
280 # NOTE: for the PL implementation, this 'operators' list
281 # amounted to users with 'tech' role in that site
282 # it seems like this is not needed any longer, so for now I just drop that
283 # operator_hrns = reg_record.get('operator',[])
284 # if (caller_hrn in operator_hrns):
285 # rl.add('authority')
298 def get_authority(self, hrn):
299 return get_authority(hrn)
301 def filter_creds_by_caller(self, creds, caller_hrn_list):
303 Returns a list of creds who's gid caller matches the
306 if not isinstance(creds, list):
309 if not isinstance(caller_hrn_list, list):
310 caller_hrn_list = [caller_hrn_list]
313 tmp_cred = Credential(string=cred)
314 if tmp_cred.get_gid_caller().get_hrn() in [caller_hrn_list]: