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 self.client_gid = self.client_cred.get_gid_caller()
75 self.object_gid = self.client_cred.get_gid_object()
77 # make sure the client_gid is not blank
78 if not self.client_gid:
79 raise MissingCallerGID(self.client_cred.get_subject())
81 # validate the client cert if it exists
83 self.verifyPeerCert(self.peer_cert, self.client_gid)
85 # make sure the client is allowed to perform the operation
87 if not self.client_cred.can_perform(operation):
88 raise InsufficientRights(operation)
90 if self.trusted_cert_list:
91 self.client_cred.verify(self.trusted_cert_file_list, self.config.SFA_CREDENTIAL_SCHEMA)
93 raise MissingTrustedRoots(self.config.get_trustedroots_dir())
95 # Make sure the credential's target matches the specified hrn.
96 # This check does not apply to trusted peers
97 trusted_peers = [gid.get_hrn() for gid in self.trusted_cert_list]
98 if hrn and self.client_gid.get_hrn() not in trusted_peers:
99 target_hrn = self.object_gid.get_hrn()
100 if not hrn == target_hrn:
101 raise PermissionError("Target hrn: %s doesn't match specified hrn: %s " % \
105 def check_ticket(self, ticket):
107 Check if the tickt was signed by a trusted cert
109 if self.trusted_cert_list:
110 client_ticket = SfaTicket(string=ticket)
111 client_ticket.verify_chain(self.trusted_cert_list)
113 raise MissingTrustedRoots(self.config.get_trustedroots_dir())
117 def verifyPeerCert(self, cert, gid):
118 # make sure the client_gid matches client's certificate
119 if not cert.is_pubkey(gid.get_pubkey()):
120 raise ConnectionKeyGIDMismatch(gid.get_subject()+":"+cert.get_subject())
122 def verifyGidRequestHash(self, gid, hash, arglist):
123 key = gid.get_pubkey()
124 if not key.verify_string(str(arglist), hash):
125 raise BadRequestHash(hash)
127 def verifyCredRequestHash(self, cred, hash, arglist):
128 gid = cred.get_gid_caller()
129 self.verifyGidRequestHash(gid, hash, arglist)
131 def validateGid(self, gid):
132 if self.trusted_cert_list:
133 gid.verify_chain(self.trusted_cert_list)
135 def validateCred(self, cred):
136 if self.trusted_cert_list:
137 cred.verify(self.trusted_cert_file_list)
139 def authenticateGid(self, gidStr, argList, requestHash=None):
140 gid = GID(string = gidStr)
141 self.validateGid(gid)
142 # request_hash is optional
144 self.verifyGidRequestHash(gid, requestHash, argList)
147 def authenticateCred(self, credStr, argList, requestHash=None):
148 cred = Credential(string = credStr)
149 self.validateCred(cred)
150 # request hash is optional
152 self.verifyCredRequestHash(cred, requestHash, argList)
155 def authenticateCert(self, certStr, requestHash):
156 cert = Certificate(string=certStr)
157 # xxx should be validateCred ??
158 self.validateCred(cert)
160 def gidNoop(self, gidStr, value, requestHash):
161 self.authenticateGid(gidStr, [gidStr, value], requestHash)
164 def credNoop(self, credStr, value, requestHash):
165 self.authenticateCred(credStr, [credStr, value], requestHash)
168 def verify_cred_is_me(self, credential):
170 cred = Credential(string=credential)
171 caller_gid = cred.get_gid_caller()
172 caller_hrn = caller_gid.get_hrn()
173 if caller_hrn != self.config.SFA_INTERFACE_HRN:
174 raise SfaPermissionDenied(self.config.SFA_INTEFACE_HRN)
178 def get_auth_info(self, auth_hrn):
180 Given an authority name, return the information for that authority.
181 This is basically a stub that calls the hierarchy module.
183 @param auth_hrn human readable name of authority
186 return self.hierarchy.get_auth_info(auth_hrn)
189 def veriry_auth_belongs_to_me(self, name):
191 Verify that an authority belongs to our hierarchy.
192 This is basically left up to the implementation of the hierarchy
193 module. If the specified name does not belong, ane exception is
194 thrown indicating the caller should contact someone else.
196 @param auth_name human readable name of authority
199 # get auth info will throw an exception if the authority doesnt exist
200 self.get_auth_info(name)
203 def verify_object_belongs_to_me(self, name):
205 Verify that an object belongs to our hierarchy. By extension,
206 this implies that the authority that owns the object belongs
207 to our hierarchy. If it does not an exception is thrown.
209 @param name human readable name of object
211 auth_name = self.get_authority(name)
214 if name == self.config.SFA_INTERFACE_HRN:
216 self.verify_auth_belongs_to_me(auth_name)
218 def verify_auth_belongs_to_me(self, name):
219 # get auth info will throw an exception if the authority doesnt exist
220 self.get_auth_info(name)
223 def verify_object_permission(self, name):
225 Verify that the object gid that was specified in the credential
226 allows permission to the object 'name'. This is done by a simple
227 prefix test. For example, an object_gid for plc.arizona would
228 match the objects plc.arizona.slice1 and plc.arizona.
230 @param name human readable name to test
232 object_hrn = self.object_gid.get_hrn()
233 if object_hrn == name:
235 if name.startswith(object_hrn + "."):
237 #if name.startswith(get_authority(name)):
240 raise PermissionError(name)
242 def determine_user_rights(self, caller_hrn, reg_record):
244 Given a user credential and a record, determine what set of rights the
245 user should have to that record.
247 This is intended to replace determine_user_rights() and
248 verify_cancreate_credential()
252 type = reg_record.type
254 logger.debug("entering determine_user_rights with record %s and caller_hrn %s"%(reg_record, caller_hrn))
257 # researchers in the slice are in the DB as-is
258 researcher_hrns = [ user.hrn for user in reg_record.reg_researchers ]
259 # locating PIs attached to that slice
260 slice_pis=reg_record.get_pis()
261 pi_hrns = [ user.hrn for user in slice_pis ]
262 if (caller_hrn in researcher_hrns + pi_hrns):
269 elif type == 'authority':
270 pi_hrns = [ user.hrn for user in reg_record.reg_pis ]
271 if (caller_hrn == self.config.SFA_INTERFACE_HRN):
275 if (caller_hrn in pi_hrns):
278 # NOTE: for the PL implementation, this 'operators' list
279 # amounted to users with 'tech' role in that site
280 # it seems like this is not needed any longer, so for now I just drop that
281 # operator_hrns = reg_record.get('operator',[])
282 # if (caller_hrn in operator_hrns):
283 # rl.add('authority')
296 def get_authority(self, hrn):
297 return get_authority(hrn)
299 def filter_creds_by_caller(self, creds, caller_hrn_list):
301 Returns a list of creds who's gid caller matches the
304 if not isinstance(creds, list):
307 if not isinstance(caller_hrn_list, list):
308 caller_hrn_list = [caller_hrn_list]
311 tmp_cred = Credential(string=cred)
312 if tmp_cred.get_gid_caller().get_hrn() in [caller_hrn_list]: