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()
39 def checkCredentials(self, creds, operation, hrn = None):
41 if not isinstance(creds, list):
43 logger.debug("Auth.checkCredentials with %d creds"%len(creds))
46 self.check(cred, operation, hrn)
49 cred_obj=Credential(string=cred)
50 logger.debug("failed to validate credential - dump=%s"%cred_obj.dump_string(dump_parents=True))
51 error = sys.exc_info()[:2]
55 raise InsufficientRights('Access denied: %s -- %s' % (error[0],error[1]))
60 def check(self, cred, operation, hrn = None):
62 Check the credential against the peer cert (callerGID included
63 in the credential matches the caller that is connected to the
64 HTTPS connection, check if the credential was signed by a
65 trusted cert and check if the credential is allowed to perform
66 the specified operation.
68 self.client_cred = Credential(string = cred)
69 self.client_gid = self.client_cred.get_gid_caller()
70 self.object_gid = self.client_cred.get_gid_object()
71 # make sure the client_gid is not blank
72 if not self.client_gid:
73 raise MissingCallerGID(self.client_cred.get_subject())
75 # validate the client cert if it exists
77 self.verifyPeerCert(self.peer_cert, self.client_gid)
79 # make sure the client is allowed to perform the operation
81 if not self.client_cred.can_perform(operation):
82 raise InsufficientRights(operation)
84 if self.trusted_cert_list:
85 self.client_cred.verify(self.trusted_cert_file_list, self.config.SFA_CREDENTIAL_SCHEMA)
88 raise MissingTrustedRoots(self.config.get_trustedroots_dir())
90 # Make sure the credential's target matches the specified hrn.
91 # This check does not apply to trusted peers
92 trusted_peers = [gid.get_hrn() for gid in self.trusted_cert_list]
93 if hrn and self.client_gid.get_hrn() not in trusted_peers:
95 target_hrn = self.object_gid.get_hrn()
96 if not hrn == target_hrn:
97 raise PermissionError("Target hrn: %s doesn't match specified hrn: %s " % \
101 def check_ticket(self, ticket):
103 Check if the tickt was signed by a trusted cert
105 if self.trusted_cert_list:
106 client_ticket = SfaTicket(string=ticket)
107 client_ticket.verify_chain(self.trusted_cert_list)
109 raise MissingTrustedRoots(self.config.get_trustedroots_dir())
113 def verifyPeerCert(self, cert, gid):
114 # make sure the client_gid matches client's certificate
115 if not cert.is_pubkey(gid.get_pubkey()):
116 raise ConnectionKeyGIDMismatch(gid.get_subject()+":"+cert.get_subject())
118 def verifyGidRequestHash(self, gid, hash, arglist):
119 key = gid.get_pubkey()
120 if not key.verify_string(str(arglist), hash):
121 raise BadRequestHash(hash)
123 def verifyCredRequestHash(self, cred, hash, arglist):
124 gid = cred.get_gid_caller()
125 self.verifyGidRequestHash(gid, hash, arglist)
127 def validateGid(self, gid):
128 if self.trusted_cert_list:
129 gid.verify_chain(self.trusted_cert_list)
131 def validateCred(self, cred):
132 if self.trusted_cert_list:
133 cred.verify(self.trusted_cert_file_list)
135 def authenticateGid(self, gidStr, argList, requestHash=None):
136 gid = GID(string = gidStr)
137 self.validateGid(gid)
138 # request_hash is optional
140 self.verifyGidRequestHash(gid, requestHash, argList)
143 def authenticateCred(self, credStr, argList, requestHash=None):
144 cred = Credential(string = credStr)
145 self.validateCred(cred)
146 # request hash is optional
148 self.verifyCredRequestHash(cred, requestHash, argList)
151 def authenticateCert(self, certStr, requestHash):
152 cert = Certificate(string=certStr)
153 # xxx should be validateCred ??
154 self.validateCred(cert)
156 def gidNoop(self, gidStr, value, requestHash):
157 self.authenticateGid(gidStr, [gidStr, value], requestHash)
160 def credNoop(self, credStr, value, requestHash):
161 self.authenticateCred(credStr, [credStr, value], requestHash)
164 def verify_cred_is_me(self, credential):
166 cred = Credential(string=credential)
167 caller_gid = cred.get_gid_caller()
168 caller_hrn = caller_gid.get_hrn()
169 if caller_hrn != self.config.SFA_INTERFACE_HRN:
170 raise SfaPermissionDenied(self.config.SFA_INTEFACE_HRN)
174 def get_auth_info(self, auth_hrn):
176 Given an authority name, return the information for that authority.
177 This is basically a stub that calls the hierarchy module.
179 @param auth_hrn human readable name of authority
182 return self.hierarchy.get_auth_info(auth_hrn)
185 def veriry_auth_belongs_to_me(self, name):
187 Verify that an authority belongs to our hierarchy.
188 This is basically left up to the implementation of the hierarchy
189 module. If the specified name does not belong, ane exception is
190 thrown indicating the caller should contact someone else.
192 @param auth_name human readable name of authority
195 # get auth info will throw an exception if the authority doesnt exist
196 self.get_auth_info(name)
199 def verify_object_belongs_to_me(self, name):
201 Verify that an object belongs to our hierarchy. By extension,
202 this implies that the authority that owns the object belongs
203 to our hierarchy. If it does not an exception is thrown.
205 @param name human readable name of object
207 auth_name = self.get_authority(name)
210 if name == self.config.SFA_INTERFACE_HRN:
212 self.verify_auth_belongs_to_me(auth_name)
214 def verify_auth_belongs_to_me(self, name):
215 # get auth info will throw an exception if the authority doesnt exist
216 self.get_auth_info(name)
219 def verify_object_permission(self, name):
221 Verify that the object gid that was specified in the credential
222 allows permission to the object 'name'. This is done by a simple
223 prefix test. For example, an object_gid for plc.arizona would
224 match the objects plc.arizona.slice1 and plc.arizona.
226 @param name human readable name to test
228 object_hrn = self.object_gid.get_hrn()
229 #strname = str(name).strip("['']")
230 if object_hrn == name:
231 #if object_hrn == strname:
233 if name.startswith(object_hrn + ".") :
234 #if strname.startswith((object_hrn + ".")) is True:
236 #if name.startswith(get_authority(name)):
239 raise PermissionError(name)
241 def determine_user_rights(self, caller_hrn, reg_record):
243 Given a user credential and a record, determine what set of rights the
244 user should have to that record.
246 This is intended to replace determine_user_rights() and
247 verify_cancreate_credential()
251 type = reg_record.type
253 logger.debug("entering determine_user_rights with record %s and caller_hrn %s"%(reg_record, caller_hrn))
256 # researchers in the slice are in the DB as-is
257 researcher_hrns = [ user.hrn for user in reg_record.reg_researchers ]
258 # locating PIs attached to that slice
259 slice_pis=reg_record.get_pis()
260 pi_hrns = [ user.hrn for user in slice_pis ]
261 if (caller_hrn in researcher_hrns + pi_hrns):
268 elif type == 'authority':
269 pi_hrns = [ user.hrn for user in reg_record.reg_pis ]
270 if (caller_hrn == self.config.SFA_INTERFACE_HRN):
274 if (caller_hrn in pi_hrns):
277 # NOTE: for the PL implementation, this 'operators' list
278 # amounted to users with 'tech' role in that site
279 # it seems like this is not needed any longer, so for now I just drop that
280 # operator_hrns = reg_record.get('operator',[])
281 # if (caller_hrn in operator_hrns):
282 # rl.add('authority')
295 def get_authority(self, hrn):
296 return get_authority(hrn)
298 def filter_creds_by_caller(self, creds, caller_hrn_list):
300 Returns a list of creds who's gid caller matches the
303 if not isinstance(creds, list):
306 if not isinstance(caller_hrn_list, list):
307 caller_hrn_list = [caller_hrn_list]
310 tmp_cred = Credential(string=cred)
311 if tmp_cred.get_gid_caller().get_hrn() in [caller_hrn_list]: