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()
38 def checkCredentials(self, creds, operation, xrns=[]):
39 if not isinstance(xrns, list):
41 hrns = [Xrn(xrn).hrn for xrn in xrns]
43 if not isinstance(creds, list):
45 logger.debug("Auth.checkCredentials with %d creds on hrns=%s"%(len(creds),hrns))
46 # won't work if either creds or hrns is empty - let's make it more explicit
47 if not creds: raise InsufficientRights("Access denied - no credential provided")
48 if not hrns: raise InsufficientRights("Access denied - no subject xrn provided")
52 self.check(cred, operation, hrn)
55 cred_obj=Credential(string=cred)
56 logger.debug("failed to validate credential - dump=%s"%cred_obj.dump_string(dump_parents=True))
57 error = sys.exc_info()[:2]
61 raise InsufficientRights('Access denied: %s -- %s' % (error[0],error[1]))
66 def check(self, cred, operation, hrn = None):
68 Check the credential against the peer cert (callerGID included
69 in the credential matches the caller that is connected to the
70 HTTPS connection, check if the credential was signed by a
71 trusted cert and check if the credential is allowed to perform
72 the specified operation.
74 self.client_cred = Credential(string = cred)
75 logger.debug("Auth.check: handling hrn=%s and credential=%s"%\
76 (hrn,cred.get_summary_tostring()))
77 self.client_gid = self.client_cred.get_gid_caller()
78 self.object_gid = self.client_cred.get_gid_object()
80 # make sure the client_gid is not blank
81 if not self.client_gid:
82 raise MissingCallerGID(self.client_cred.get_subject())
84 # validate the client cert if it exists
86 self.verifyPeerCert(self.peer_cert, self.client_gid)
88 # make sure the client is allowed to perform the operation
90 if not self.client_cred.can_perform(operation):
91 raise InsufficientRights(operation)
93 if self.trusted_cert_list:
94 self.client_cred.verify(self.trusted_cert_file_list, self.config.SFA_CREDENTIAL_SCHEMA)
96 raise MissingTrustedRoots(self.config.get_trustedroots_dir())
98 # Make sure the credential's target matches the specified hrn.
99 # This check does not apply to trusted peers
100 trusted_peers = [gid.get_hrn() for gid in self.trusted_cert_list]
101 if hrn and self.client_gid.get_hrn() not in trusted_peers:
102 target_hrn = self.object_gid.get_hrn()
103 if not hrn == target_hrn:
104 raise PermissionError("Target hrn: %s doesn't match specified hrn: %s " % \
108 def check_ticket(self, ticket):
110 Check if the tickt was signed by a trusted cert
112 if self.trusted_cert_list:
113 client_ticket = SfaTicket(string=ticket)
114 client_ticket.verify_chain(self.trusted_cert_list)
116 raise MissingTrustedRoots(self.config.get_trustedroots_dir())
120 def verifyPeerCert(self, cert, gid):
121 # make sure the client_gid matches client's certificate
122 if not cert.is_pubkey(gid.get_pubkey()):
123 raise ConnectionKeyGIDMismatch(gid.get_subject()+":"+cert.get_subject())
125 def verifyGidRequestHash(self, gid, hash, arglist):
126 key = gid.get_pubkey()
127 if not key.verify_string(str(arglist), hash):
128 raise BadRequestHash(hash)
130 def verifyCredRequestHash(self, cred, hash, arglist):
131 gid = cred.get_gid_caller()
132 self.verifyGidRequestHash(gid, hash, arglist)
134 def validateGid(self, gid):
135 if self.trusted_cert_list:
136 gid.verify_chain(self.trusted_cert_list)
138 def validateCred(self, cred):
139 if self.trusted_cert_list:
140 cred.verify(self.trusted_cert_file_list)
142 def authenticateGid(self, gidStr, argList, requestHash=None):
143 gid = GID(string = gidStr)
144 self.validateGid(gid)
145 # request_hash is optional
147 self.verifyGidRequestHash(gid, requestHash, argList)
150 def authenticateCred(self, credStr, argList, requestHash=None):
151 cred = Credential(string = credStr)
152 self.validateCred(cred)
153 # request hash is optional
155 self.verifyCredRequestHash(cred, requestHash, argList)
158 def authenticateCert(self, certStr, requestHash):
159 cert = Certificate(string=certStr)
160 # xxx should be validateCred ??
161 self.validateCred(cert)
163 def gidNoop(self, gidStr, value, requestHash):
164 self.authenticateGid(gidStr, [gidStr, value], requestHash)
167 def credNoop(self, credStr, value, requestHash):
168 self.authenticateCred(credStr, [credStr, value], requestHash)
171 def verify_cred_is_me(self, credential):
173 cred = Credential(string=credential)
174 caller_gid = cred.get_gid_caller()
175 caller_hrn = caller_gid.get_hrn()
176 if caller_hrn != self.config.SFA_INTERFACE_HRN:
177 raise SfaPermissionDenied(self.config.SFA_INTEFACE_HRN)
181 def get_auth_info(self, auth_hrn):
183 Given an authority name, return the information for that authority.
184 This is basically a stub that calls the hierarchy module.
186 @param auth_hrn human readable name of authority
189 return self.hierarchy.get_auth_info(auth_hrn)
192 def veriry_auth_belongs_to_me(self, name):
194 Verify that an authority belongs to our hierarchy.
195 This is basically left up to the implementation of the hierarchy
196 module. If the specified name does not belong, ane exception is
197 thrown indicating the caller should contact someone else.
199 @param auth_name human readable name of authority
202 # get auth info will throw an exception if the authority doesnt exist
203 self.get_auth_info(name)
206 def verify_object_belongs_to_me(self, name):
208 Verify that an object belongs to our hierarchy. By extension,
209 this implies that the authority that owns the object belongs
210 to our hierarchy. If it does not an exception is thrown.
212 @param name human readable name of object
214 auth_name = self.get_authority(name)
217 if name == self.config.SFA_INTERFACE_HRN:
219 self.verify_auth_belongs_to_me(auth_name)
221 def verify_auth_belongs_to_me(self, name):
222 # get auth info will throw an exception if the authority doesnt exist
223 self.get_auth_info(name)
226 def verify_object_permission(self, name):
228 Verify that the object gid that was specified in the credential
229 allows permission to the object 'name'. This is done by a simple
230 prefix test. For example, an object_gid for plc.arizona would
231 match the objects plc.arizona.slice1 and plc.arizona.
233 @param name human readable name to test
235 object_hrn = self.object_gid.get_hrn()
236 if object_hrn == name:
238 if name.startswith(object_hrn + "."):
240 #if name.startswith(get_authority(name)):
243 raise PermissionError(name)
245 def determine_user_rights(self, caller_hrn, reg_record):
247 Given a user credential and a record, determine what set of rights the
248 user should have to that record.
250 This is intended to replace determine_user_rights() and
251 verify_cancreate_credential()
255 type = reg_record.type
257 logger.debug("entering determine_user_rights with record %s and caller_hrn %s"%(reg_record, caller_hrn))
260 # researchers in the slice are in the DB as-is
261 researcher_hrns = [ user.hrn for user in reg_record.reg_researchers ]
262 # locating PIs attached to that slice
263 slice_pis=reg_record.get_pis()
264 pi_hrns = [ user.hrn for user in slice_pis ]
265 if (caller_hrn in researcher_hrns + pi_hrns):
272 elif type == 'authority':
273 pi_hrns = [ user.hrn for user in reg_record.reg_pis ]
274 if (caller_hrn == self.config.SFA_INTERFACE_HRN):
278 if (caller_hrn in pi_hrns):
281 # NOTE: for the PL implementation, this 'operators' list
282 # amounted to users with 'tech' role in that site
283 # it seems like this is not needed any longer, so for now I just drop that
284 # operator_hrns = reg_record.get('operator',[])
285 # if (caller_hrn in operator_hrns):
286 # rl.add('authority')
299 def get_authority(self, hrn):
300 return get_authority(hrn)
302 def filter_creds_by_caller(self, creds, caller_hrn_list):
304 Returns a list of creds who's gid caller matches the
307 if not isinstance(creds, list):
310 if not isinstance(caller_hrn_list, list):
311 caller_hrn_list = [caller_hrn_list]
314 tmp_cred = Credential(string=cred)
315 if tmp_cred.get_gid_caller().get_hrn() in [caller_hrn_list]: