def __perform(self, action, capability, vetted=False, value=None): # Don't perform the access again if self.__performed: raise HTTPForbidden('Cannot perform access again') allowed = True try: if capability is None: at = ENTER else: at = capability.access_type # Verify that the action is allowed by the capability if not vetted: cons = capability.constraint if cons is not None and not cons.allows(action, self): raise HTTPForbidden('Invoked capability does not allow this access') if at in EXIT: # Make sure that the action has been filtered before making any attempt to # process its execution if not vetted and not self.filtered(action): raise HTTPForbidden('Action not available for processing') # If the access is an approval for processing, check that the action has not # been processed before, then perform it if at == EXIT[0]: if self.processed(action, None): raise HTTPForbidden('Action not available for processing') return action.perform(self.request) # Otherwise, if this action has already been approved, this is a revocation if self.processed(action): return action.revoke(self.request) # Otherwise, logging this access is enough to mark the action as denied return HTTPForbidden(FILTER_DENY) if vetted else 'Request marked as denied' elif at in FILTER: # Filtering is only done automatically if not vetted: raise RuntimeError('Request filtering must be automatic') if at == FILTER[0]: # If this access is letting the action through the filter, failure to # take further automatic action should simply stop execution. Possible # automatic accesses are drawn from the EXIT list types = EXIT filters = [] # If this is an authenticated request, try proceeding with the requestor's # positive access capabilities. If none exist, then move on to auto-filters if self.user is not None: filters = self.own_processes(action) if not filters: filters = self.processes(action, True) def fail(msg): return value else: # Otherwise, logging this access is enough to mark the action as rejected return HTTPForbidden(FILTER_REJECT) elif at == ENTER: # If this access is a new request, failure should raise an unauthorized # exception and store the failed access. Possible automatic accesses are # drawn from the FILTER list types = FILTER filters = [] # If this is an authenticated request, try proceeding with the requestor's # positive access capabilities. If none exist, then move on to auto-filters if self.user is not None: filters = self.own_filters(action) if not filters: filters = self.filters(action, True) value = HTTPAccepted(action.serial) def fail(msg): raise HTTPForbidden(msg) else: raise RuntimeError('%s is not a valid access type' % str(at)) # If no filters actually match the action, then the attempt at automatic # access should fail closed immediately (lack of a no does not mean yes) if not filters: return fail(NO_FILTERS) pos = [cap for cap in filters if cap.access_type == types[0]] neg = [cap for cap in filters if cap.access_type == types[1]] if neg: # If any negative filters were triggered, perform a negative access value = self.__perform(action, neg[0], True, value) DBSession.add_all((self.__record(action, cap, True) for cap in neg[1:])) DBSession.add_all((self.__record(action, cap, False) for cap in pos)) else: # Otherwise, perform a positive access value = self.__perform(action, pos[0], True, value) DBSession.add_all((self.__record(action, cap, True) for cap in pos[1:])) except: allowed = False raise finally: self.__save(action, capability, allowed) if value is None: raise RuntimeException('Illegal return state') return value