Commit ed2a646a authored by Ziirish's avatar Ziirish

allow to exclude some clients from matching ACL rules (see #305)

parent b558c117
Pipeline #1686 passed with stages
in 10 minutes and 33 seconds
......@@ -12,6 +12,7 @@ Current
- Add: new ``bui-monitor`` processes pool + ``async`` backend to parallelize some requests `#278 <https://git.ziirish.me/ziirish/burp-ui/issues/278>`_
- Add: new `listen` and `listen_status` options in burp-2.2.10 `#279 <https://git.ziirish.me/ziirish/burp-ui/issues/279>`_
- Add: new `order` keyword in ACL definitions in order to decide whether `rw` should be evaluated first or not `#305 <https://git.ziirish.me/ziirish/burp-ui/issues/305>`__
- Add: new `exclude` keyword in ACL definitions in order to exclude some clients from the rules `#305 <https://git.ziirish.me/ziirish/burp-ui/issues/305>`__
- Add: allow to hide selected clients/servers `#282 <https://git.ziirish.me/ziirish/burp-ui/issues/282>`_
- Add: allow to delete clients data upon removal `#232 <https://git.ziirish.me/ziirish/burp-ui/issues/232>`_
- Add: allow to create clients from templates in one call `#266 <https://git.ziirish.me/ziirish/burp-ui/issues/266>`_
......
......@@ -16,25 +16,37 @@ import re
import json
import fnmatch
PARSE_EXCLUDE_KEYS = ['agents', 'clients', 'ro', 'rw', 'order']
PARSE_RESERVED_KEYS = ['ro', 'rw', 'order']
DEFAULT_EVAL_ORDER = ['rw', 'ro']
PARSE_EXCLUDE_KEYS = ['agents', 'clients', 'ro', 'rw', 'order', 'exclude']
PARSE_RESERVED_KEYS = ['ro', 'rw', 'order', 'exclude']
DEFAULT_EVAL_ORDER = ['exclude', 'rw', 'ro']
MODE_RETURN = {
'ro': False,
'rw': True,
}
def _get_order(data, key):
def _extract_key(data, key, name, default=[], fallback='clients'):
if not isinstance(data, dict):
return DEFAULT_EVAL_ORDER
order = data.get('order', {})
if key in order:
return order[key]
elif 'clients' in order:
return order['clients']
else:
return DEFAULT_EVAL_ORDER
return default
ret = None
extract = data.get(key, {})
if isinstance(name, list):
for nm in name:
if nm in extract:
ret = make_list(extract[nm])
elif name:
if name in extract:
ret = make_list(extract[name])
if ret:
if key == 'order':
for odr in DEFAULT_EVAL_ORDER:
if odr not in ret:
ret.append(odr)
return ret
return extract.get(fallback, default)
class BUImetaGrant(object):
......@@ -115,7 +127,7 @@ class BUImetaGrant(object):
par = key
cl2, ag2, ad2 = self._parse_clients(data[key], md, parent=par)
agents = self._merge_data(agents, ag2)
if not md or md not in ['order']:
if not md or md not in ['order', 'exclude']:
clients = self._merge_data(clients, cl2)
if parent and parent not in PARSE_EXCLUDE_KEYS:
ro = ad2.get('ro')
......@@ -445,7 +457,7 @@ class BUIgrantHandler(BUImetaGrant, BUIacl):
return is_admin or self.opt('assume_rw', True)
for adv in advanced:
order = _get_order(adv, server)
order = _extract_key(adv, 'order', [server] + server_match, DEFAULT_EVAL_ORDER)
for adv2 in advanced:
# the whole agent is rw and we did not find explicit entry for
# client_match
......@@ -489,10 +501,10 @@ class BUIgrantHandler(BUImetaGrant, BUIacl):
for adv in advanced:
if server:
key = server
key = [server] + self._server_match(username, server)
else:
key = 'clients'
order = _get_order(adv, key)
key = None
order = _extract_key(adv, 'order', key, DEFAULT_EVAL_ORDER)
for odr in order:
eval_clients = adv.get(odr, {}).get('clients', [])
......@@ -531,29 +543,46 @@ class BUIgrantHandler(BUImetaGrant, BUIacl):
if advanced is not False:
for idx, adv in enumerate(advanced):
if all(x not in adv for x in server_match) and \
any(x in y
for x in server_match
for y in self._extract_advanced_mode(username, 'ro', 'agents', idx)
) or \
any(x in y
for x in server_match
for y in self._extract_advanced_mode(username, 'rw', 'agents', idx)
):
return True
order = _extract_key(adv, 'order', [server] + server_match, DEFAULT_EVAL_ORDER)
excludes = _extract_key(adv, 'exclude', [server] + server_match, fallback='agents')
if all(x not in adv for x in server_match):
for odr in order:
if odr == 'exclude' and (
any(x in excludes for x in client_match) or
client in excludes):
return False
elif any(x in y
for x in server_match
for y in self._extract_advanced_mode(username, odr, 'agents', idx)
):
return True
tmp = set(adv.get(server, []))
for srv in server_match:
tmp |= set(adv.get(srv, []))
adv2 = list(tmp)
if client_match is not False and \
(any(x in adv2 for x in client_match) or
client in adv2):
return True
excludes = _extract_key(adv, 'exclude', [server] + server_match)
for odr in order:
if odr == 'exclude' and (
any(x in excludes for x in client_match) or
client in excludes):
return False
elif client_match is not False and \
(any(x in adv2 for x in client_match) or
client in adv2):
return True
return False
return client_match is not False or is_admin
order = _extract_key(adv, 'order', None, DEFAULT_EVAL_ORDER)
excludes = _extract_key(adv, 'exclude', None)
for odr in order:
if odr == 'exclude' and client_match and (
any(x in excludes for x in client_match) or
client in excludes):
return False
return client_match is not False or is_admin
def is_server_rw(self, username=None, server=None):
"""See :func:`burpui.misc.acl.interface.BUIacl.is_server_rw`"""
......@@ -576,7 +605,7 @@ class BUIgrantHandler(BUImetaGrant, BUIacl):
advanced = self._extract_advanced(username)
for adv in advanced:
order = _get_order(adv, server)
order = _extract_key(adv, 'order', [server] + server_match, DEFAULT_EVAL_ORDER)
for odr in order:
if any(x in adv.get(odr, {}).get('agents', []) for x in server_match):
return MODE_RETURN.get(odr, False)
......
......@@ -801,6 +801,33 @@ With the above rule, the engine will treat ``client.specific.test`` as ``ro``
whereas without the ``order`` keywoard, ``client.specific.test`` would have
matched the ``rw`` rule first and thus would be considered as ``rw``.
There is also a new ``exclude`` keyword that supports excluding clients from
the matching rules.
Here is an example:
::
# rule is: myuser = '{"agents": {"agent1": {"exclude": ["client.test1"], "ro": ["client.specific.*"], "rw": ["client.*", "server.*"]}, "agent2": {"rw": ["client.*"]}}}'
In [3]: meta_grants.is_client_rw('myuser', 'client.specific.test1', 'agent1')
Out[3]: False
In [4]: meta_grants.is_client_rw('myuser', 'client.test1', 'agent1')
Out[4]: False
In [5]: meta_grants.is_client_rw('myuser', 'client.test2', 'agent1')
Out[5]: True
In [6]: meta_grants.is_client_rw('myuser', 'client.test1', 'agent2')
Out[6]: True
In [7]: meta_grants.is_client_allowed('myuser', 'client.test1', 'agent1')
Out[7]: False
In [8]: meta_grants.is_client_allowed('myuser', 'client.specific.test1', 'agent1')
Out[8]: True
About the ``inverse_inheritance`` option, here is a concrete example. We assume
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment