Skip to content

Commit

Permalink
Attribute filter - Allow multiple values for PostgreSQL layers and ac…
Browse files Browse the repository at this point in the history
…tive option
  • Loading branch information
mdouchin committed Aug 22, 2024
1 parent 9bbd904 commit 37c2c4e
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 17 deletions.
41 changes: 27 additions & 14 deletions lizmap_server/lizmap_accesscontrol.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,19 +391,25 @@ def get_lizmap_layer_filter(self, layer: QgsVectorLayer, filter_type: FilterType

return login_filter

login_filter = self._filter_by_login(cfg_layer_login_filter, groups, user_login)
login_filter = self._filter_by_login(
cfg_layer_login_filter,
groups,
user_login,
layer.dataProvider().name()
)
if polygon_filter:
return f'{polygon_filter} AND {login_filter}'

return login_filter

@staticmethod
def _filter_by_login(cfg_layer_login_filter: dict, groups: tuple, login: str) -> str:
def _filter_by_login(cfg_layer_login_filter: dict, groups: tuple, login: str, provider: str) -> str:
""" Build the string according to the filter by login configuration.
:param cfg_layer_login_filter: The Lizmap Filter by login configuration.
:param groups: List of groups for the current user
:param login: The current user
:param provider: The layer data provider ('postgres' for example)
"""
# List of values for expression
values = []
Expand All @@ -420,7 +426,9 @@ def _filter_by_login(cfg_layer_login_filter: dict, groups: tuple, login: str) ->

# Since LWC 3.8, we allow to have a list of groups (or logins)
# separated by comma, with NO SPACES
# e.g. field "filter_field" can contain 'group_a,group_b,group_c'
# only for PostgreSQL layers and if the option allow_multiple_acl_values
# is set to True
# For example the field can contain 'group_a,group_b,group_c'
# To use only pure SQL allowed by QGIS, we can use LIKE items
# For big dataset, a GIN index with pg_trgm must be used for the
# filter field to improve performance
Expand All @@ -442,17 +450,22 @@ def _filter_by_login(cfg_layer_login_filter: dict, groups: tuple, login: str) ->
# equality
filters.append(f'{quoted_field} = {quoted_value}')

# begins with value & comma
quoted_like_value = QgsExpression.quotedString(f'{value},%')
filters.append(f'{quoted_field} LIKE {quoted_like_value}')

# ends with comma & value
quoted_like_value = QgsExpression.quotedString(f'%,{value}')
filters.append(f'{quoted_field} LIKE {quoted_like_value}')

# value between two commas
quoted_like_value = QgsExpression.quotedString(f'%,{value},%')
filters.append(f'{quoted_field} LIKE {quoted_like_value}')
if (
provider == 'postgres' and
'allow_multiple_acl_values' in cfg_layer_login_filter and
cfg_layer_login_filter['allow_multiple_acl_values']
):
# begins with value & comma
quoted_like_value = QgsExpression.quotedString(f'{value},%')
filters.append(f'{quoted_field} LIKE {quoted_like_value}')

# ends with comma & value
quoted_like_value = QgsExpression.quotedString(f'%,{value}')
filters.append(f'{quoted_field} LIKE {quoted_like_value}')

# value between two commas
quoted_like_value = QgsExpression.quotedString(f'%,{value},%')
filters.append(f'{quoted_field} LIKE {quoted_like_value}')

# Build the filter for this value
value_filters.append(' OR '.join(filters))
Expand Down
36 changes: 33 additions & 3 deletions test/test_lizmap_accesscontrol.py
Original file line number Diff line number Diff line change
Expand Up @@ -595,14 +595,44 @@ def test_tos_strict_layers_true(client):
assert "bing-satellite" not in content


def test_filter_by_login():
""" Test about comma separated list of values with the current user."""
def test_filter_by_login_simple_values():
""" Test filter with simple values in field with the current user."""
config = {
'filterPrivate': ['a', 'b'],
'filterAttribute': 'f',
'allow_multiple_acl_values': False,
}
output = LizmapAccessControlFilter._filter_by_login(config, ('grp_1', 'grp_2'), 'a')
output = LizmapAccessControlFilter._filter_by_login(config, ('grp_1', 'grp_2'), 'a', 'other_provider')
assert (
"\"f\" = 'a' OR \"f\" = 'all'"
) == output, output


def test_filter_by_login_postgres_multiple_values():
""" Test filter with comma separated list of values with the current user."""
config = {
'filterPrivate': ['a', 'b'],
'filterAttribute': 'f',
'allow_multiple_acl_values': True,
}
output = LizmapAccessControlFilter._filter_by_login(config, ('grp_1', 'grp_2'), 'a', 'postgres')
assert (
"\"f\" = 'a' OR \"f\" LIKE 'a,%' OR \"f\" LIKE '%,a' OR \"f\" LIKE '%,a,%' OR \"f\" = 'all' "
"OR \"f\" LIKE 'all,%' OR \"f\" LIKE '%,all' OR \"f\" LIKE '%,all,%'"
) == output, output


def test_filter_by_login_postgres_multiple_values_deactivated():
"""
Test filter with comma separated list of values with the current user
but with option disabled
"""
config = {
'filterPrivate': ['a', 'b'],
'filterAttribute': 'f',
'allow_multiple_acl_values': False,
}
output = LizmapAccessControlFilter._filter_by_login(config, ('grp_1', 'grp_2'), 'a', 'postgres')
assert (
"\"f\" = 'a' OR \"f\" = 'all'"
) == output, output

0 comments on commit 37c2c4e

Please sign in to comment.