def main(): print('Starting PfSense Parental Control App') print('Finding LAN devices') devices = find_devices() print('Finding Rule Overides') overrides = get_overrides() print('Finding blocked users') blocked = find_blocked(devices, overrides) print('Resolving IP addresses') blocked_ip = [[build_descr(b), devices.get(b['MAC'])] for b in blocked] print("Configuring Faux API") api = PfsenseFauxapi(PFSENSE_HOST, apikey, apisecret) api.proto = 'http' print("Requesting config") current_block_rules, default_rules = get_rules(api) current_block_ip = dict( (r['descr'], r['source']['address']) for r in current_block_rules) new_rules = build_new_rules(blocked_ip, current_block_ip) new_patch = create_filter_rule_patch(new_rules + default_rules) pprint.pprint(new_patch) print("patching") api.config_patch(new_patch) api.function_call({ 'function': 'filter_configure_sync', 'args': [False], 'includes': ['shaper.inc'], })
def test_config_patch(mock_requests_post): fauxapi = PfsenseFauxapi(host=None, apikey=None, apisecret=None) mock_requests_post.return_value.status_code = 200 mock_requests_post.return_value.text = '{"callid": "5c8dbe47090f8", "action": "config_patch", "message": "ok", "data": {"do_backup": true, "do_reload": true, "previous_config_file": "/cf/conf/backup/config-1552793159.xml"}}' response = fauxapi.config_patch(config={}) assert response['action'] == 'config_patch' assert response['message'] == 'ok' assert response['data'] is not None
config_aliases = FauxapiLib.config_get('aliases') print(json.dumps( FauxapiLib.config_set(config_aliases, 'aliases')) ) # config_patch # ============================================================================= # in this example we patch a specific set of configuration parameters and then revert them back to what they were config_patch = { 'system': { 'dnsserver': ['8.8.8.8', '8.8.4.4'], 'hostname': 'testing' } } print(json.dumps( FauxapiLib.config_patch(config_patch) )) # config_patch - set dnsserver to what it was originally config_patch = { 'system': { 'dnsserver': config['system']['dnsserver'] if 'dnsserver' in config['system'] else [''], 'hostname': config['system']['hostname'], } } print(json.dumps( FauxapiLib.config_patch(config_patch) )) # config get the full configuration again so we can manually confirm it has been restored config = FauxapiLib.config_get()
class UserGroupManagementFauxapi(): fauxapi_host = None fauxapi_apikey = None fauxapi_apisecret = None system_config = None FauxapiLib = None def __init__(self, fauxapi_host, fauxapi_apikey, fauxapi_apisecret, debug=False): self.FauxapiLib = PfsenseFauxapi(fauxapi_host, fauxapi_apikey, fauxapi_apisecret, debug) # user functions # ========================================================================= def get_users(self): self._reload_system_config() response_data = {} for user in self.system_config['system']['user']: response_data[user['name']] = user del (response_data[user['name']]['name']) return response_data def add_user(self, username): self._reload_system_config() user_index, user = self._get_entity('user', username) if user_index is not None: raise UserGroupManagementFauxapiException('user already exists', username) user = { 'scope': 'user', 'bcrypt-hash': 'no-password-set', 'descr': '', 'name': username, 'expires': '', 'dashboardcolumns': '2', 'authorizedkeys': '', 'ipsecpsk': '', 'webguicss': 'pfSense.css', 'uid': self._get_next_id('uid'), } patch_system_user = { 'system': { 'user': self.system_config['system']['user'] } } patch_system_user['system']['user'].append(user) response = self.FauxapiLib.config_patch(patch_system_user) if response['message'] != 'ok': raise UserGroupManagementFauxapiException('unable to add user', response['message']) self._increment_next_id('uid') return user def manage_user(self, username, attributes): self._reload_system_config() valid_attributes = [ 'password', 'descr', 'expires', 'dashboardcolumns', 'authorizedkeys', 'ipsecpsk', 'webguicss', 'disabled', 'priv' ] user_index, user = self._get_entity('user', username) if user_index is None: raise UserGroupManagementFauxapiException('user does not exist', username) if type(attributes) != dict: raise UserGroupManagementFauxapiException( 'attributes is incorrect type') for attribute, value in attributes.items(): if attribute not in valid_attributes: raise UserGroupManagementFauxapiException( 'unsupported attribute type', attribute) if attribute == 'disabled': if value is True: user[attribute] = '' else: if attribute in user: del (user[attribute]) elif attribute == 'password': user['bcrypt-hash'] = bcrypt.hashpw( value.encode('utf8'), bcrypt.gensalt()).decode('utf8') else: if len(value) == 0 and attribute in user: del (user[attribute]) elif len(value) > 0: user[attribute] = value patch_system_user = { 'system': { 'user': self.system_config['system']['user'] } } patch_system_user['system']['user'][user_index] = user response = self.FauxapiLib.config_patch(patch_system_user) if response['message'] != 'ok': raise UserGroupManagementFauxapiException('unable to manage user', response['message']) return user def remove_user(self, username): self._reload_system_config() user_index, user = self._get_entity('user', username) if user_index is None: raise UserGroupManagementFauxapiException('user does not exist', username) patch_system_user = { 'system': { 'user': self.system_config['system']['user'] } } del (patch_system_user['system']['user'][user_index]) response = self.FauxapiLib.config_patch(patch_system_user) if response['message'] != 'ok': raise UserGroupManagementFauxapiException('unable to remove user', response['message']) return user # group functions # ========================================================================= def get_groups(self): self._reload_system_config() response_data = {} for group in self.system_config['system']['group']: response_data[group['name']] = group del (response_data[group['name']]['name']) return response_data def add_group(self, groupname): self._reload_system_config() group_index, group = self._get_entity('group', groupname) if group_index is not None: raise UserGroupManagementFauxapiException('group already exists', groupname) group = { 'scope': 'local', 'description': '', 'name': groupname, 'gid': self._get_next_id('gid'), } patch_system_group = { 'system': { 'group': self.system_config['system']['group'] } } patch_system_group['system']['group'].append(group) response = self.FauxapiLib.config_patch(patch_system_group) if response['message'] != 'ok': raise UserGroupManagementFauxapiException('unable to add group', response['message']) self._increment_next_id('gid') return group def manage_group(self, groupname, attributes): self._reload_system_config() valid_attributes = ['description', 'member', 'priv'] group_index, group = self._get_entity('group', groupname) if group_index is None: raise UserGroupManagementFauxapiException('group does not exist', groupname) if type(attributes) != dict: raise UserGroupManagementFauxapiException( 'attributes is incorrect type') for attribute, value in attributes.items(): if attribute not in valid_attributes: raise UserGroupManagementFauxapiException( 'unsupported attribute type', attribute) if attribute == 'member': if type(value) != list: raise UserGroupManagementFauxapiException( 'member attribute is incorrect type') elif attribute == 'priv': if type(value) != list: raise UserGroupManagementFauxapiException( 'priv attribute is incorrect type') if len(value) == 0 and attribute in group: del (group[attribute]) elif len(value) > 0: group[attribute] = value patch_system_group = { 'system': { 'group': self.system_config['system']['group'] } } patch_system_group['system']['group'][group_index] = group response = self.FauxapiLib.config_patch(patch_system_group) if response['message'] != 'ok': raise UserGroupManagementFauxapiException('unable to manage group', response['message']) return group def remove_group(self, groupname): self._reload_system_config() group_index, group = self._get_entity('group', groupname) if group_index is None: raise UserGroupManagementFauxapiException('group does not exist', groupname) patch_system_group = { 'system': { 'group': self.system_config['system']['group'] } } del (patch_system_group['system']['group'][group_index]) response = self.FauxapiLib.config_patch(patch_system_group) if response['message'] != 'ok': raise UserGroupManagementFauxapiException('unable to remove group', response['message']) return group # internal helper functions # ========================================================================= def _get_entity(self, entity_type, entity_name): entity = None entity_index = 0 for entity_data in self.system_config['system'][entity_type]: if entity_data['name'] == entity_name: entity = entity_data break entity_index += 1 if entity is None: return None, None return entity_index, entity def _get_next_id(self, id_type): id_name = 'next{}'.format(id_type) return self.system_config['system'][id_name] def _increment_next_id(self, id_type): id_name = 'next{}'.format(id_type) next_id = int(self._get_next_id(id_type)) + 1 patch_system_nextid = {'system': {id_name: str(next_id)}} response = self.FauxapiLib.config_patch(patch_system_nextid) if response['message'] != 'ok': raise UserGroupManagementFauxapiException( 'unable to increment the nextid', id_type) return next_id def _reload_system_config(self): self.system_config = self.FauxapiLib.config_get()
# NB: again, nothing amazing happening in this example, we are are again only writing back the same (section) # configuration, the developer more likely wants to perform some kind of operation on `config_aliases` before calling # the config_set function again. config_aliases = FauxapiLib.config_get('aliases') print(json.dumps(FauxapiLib.config_set(config_aliases, 'aliases'))) # config_patch # ============================================================================= # in this example we patch a specific set of configuration parameters and then revert them back to what they were config_patch = { 'system': { 'dnsserver': ['8.8.8.8', '8.8.4.4'], 'hostname': 'testing' } } print(json.dumps(FauxapiLib.config_patch(config_patch))) # config_patch - set dnsserver to what it was originally config_patch = { 'system': { 'dnsserver': config['system']['dnsserver'] if 'dnsserver' in config['system'] else [''], 'hostname': config['system']['hostname'], } } print(json.dumps(FauxapiLib.config_patch(config_patch))) # config get the full configuration again so we can manually confirm it has been restored config = FauxapiLib.config_get()