diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 8a6c10b5f..b463df646 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -208,9 +208,14 @@ user: action_help: List group api: GET /users/groups arguments: - --fields: - help: fields to fetch - nargs: "+" + -n: + full: --names-only + help: Only list the name of the groups without any additional info + action: store_true + -f: + full: --full + help: List all the info available for each groups + action: store_true ### user_group_create() create: diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 9e90ece2a..f96146ea0 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -1374,7 +1374,7 @@ class RestoreManager(): filtred_entries = ['entryUUID', 'creatorsName', 'createTimestamp', 'entryCSN', 'structuralObjectClass', 'modifiersName', 'modifyTimestamp', 'inheritPermission', 'memberUid'] entries = read_ldif('%s/permission.ldif' % app_settings_in_archive, filtred_entries) - group_list = user_group_list(['cn'])['groups'] + group_list = user_group_list()['groups'] for dn, entry in entries: # Remove the group which has been removed for group in entry['groupPermission']: diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 1a5465674..0b77a3e5c 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -169,13 +169,13 @@ def user_permission_update(operation_logger, app=[], permission=None, add_userna # Validate that the group exist for g in add_group: - if g not in user_group_list(['cn'])['groups']: + if g not in user_group_list()['groups']: raise YunohostError('group_unknown', group=g) for u in add_username: if u not in user_list(['uid'])['users']: raise YunohostError('user_unknown', user=u) for g in del_group: - if g not in user_group_list(['cn'])['groups']: + if g not in user_group_list()['groups']: raise YunohostError('group_unknown', group=g) for u in del_username: if u not in user_list(['uid'])['users']: @@ -244,14 +244,12 @@ def user_permission_update(operation_logger, app=[], permission=None, add_userna for a in app: allowed_users = set() disallowed_users = set() - group_list = user_group_list(['member'])['groups'] + group_list = user_group_list()['groups'] for g in add_group: - if 'members' in group_list[g]: - allowed_users.union(group_list[g]['members']) + allowed_users.union(group_list[g]['members']) for g in del_group: - if 'members' in group_list[g]: - disallowed_users.union(group_list[g]['members']) + disallowed_users.union(group_list[g]['members']) allowed_users = ','.join(allowed_users) disallowed_users = ','.join(disallowed_users) diff --git a/src/yunohost/user.py b/src/yunohost/user.py index e5480ca92..787058fbc 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -489,66 +489,44 @@ def user_info(username): # # Group subcategory # -def user_group_list(fields=None): +def user_group_list(names_only=False, full=False): """ List users Keyword argument: - filter -- LDAP filter used to search - offset -- Starting number for user fetching - limit -- Maximum number of user fetched - fields -- fields to fetch - + names-only -- Only list the name of the groups without any additional info + full -- List all the info available for each groups """ - from yunohost.utils.ldap import _get_ldap_interface + + # Fetch relevant informations + + from yunohost.utils.ldap import _get_ldap_interface, _ldap_path_extract ldap = _get_ldap_interface() - group_attr = { - 'cn': 'groupname', - 'member': 'members', - 'permission': 'permission' - } - attrs = ['cn'] - groups = {} - if fields: - keys = group_attr.keys() - for attr in fields: - if attr in keys: - attrs.append(attr) - else: - raise YunohostError('field_invalid', attr) + if names_only: + fields_to_fetch = ["cn"] + elif full: + fields_to_fetch = ["cn", "member", "permission"] else: - attrs = ['cn', 'member'] + fields_to_fetch = ["cn", "member"] - result = ldap.search('ou=groups,dc=yunohost,dc=org', - '(objectclass=groupOfNamesYnh)', - attrs) + groups_infos = ldap.search('ou=groups,dc=yunohost,dc=org', + '(objectclass=groupOfNamesYnh)', + fields_to_fetch) - for group in result: - # The group "admins" should be hidden for the user - if group_attr['cn'] == "admins": - continue - entry = {} - for attr, values in group.items(): - if values: - if attr == "member": - entry[group_attr[attr]] = [] - for v in values: - entry[group_attr[attr]].append(v.split("=")[1].split(",")[0]) - elif attr == "permission": - entry[group_attr[attr]] = {} - for v in values: - permission = v.split("=")[1].split(",")[0].split(".")[1] - pType = v.split("=")[1].split(",")[0].split(".")[0] - if permission in entry[group_attr[attr]]: - entry[group_attr[attr]][permission].append(pType) - else: - entry[group_attr[attr]][permission] = [pType] - else: - entry[group_attr[attr]] = values[0] + # Parse / organize information to be outputed - groupname = entry[group_attr['cn']] - groups[groupname] = entry + groups = {} + for infos in groups_infos: + name = infos["cn"][0] + groups[name] = {} + if "member" in fields_to_fetch: + groups[name]["members"] = [_ldap_path_extract(p, "uid") for p in infos.get("member", [])] + if "permission" in fields_to_fetch: + groups[name]["permissions"] = [_ldap_path_extract(p, "cn") for p in infos.get("permission", [])] + + if names_only: + groups = groups.keys() return {'groups': groups} diff --git a/src/yunohost/utils/ldap.py b/src/yunohost/utils/ldap.py index 186cdbdec..c3b5065a1 100644 --- a/src/yunohost/utils/ldap.py +++ b/src/yunohost/utils/ldap.py @@ -40,6 +40,20 @@ def _get_ldap_interface(): return _ldap_interface + +# We regularly want to extract stuff like 'bar' in ldap path like +# foo=bar,dn=users.example.org,ou=example.org,dc=org so this small helper allow +# to do this without relying of dozens of mysterious string.split()[0] +# +# e.g. using _ldap_path_extract(path, "foo") on the previous example will +# return bar + +def _ldap_path_extract(path, info): + for element in path.split(","): + if element.startswith(info + "="): + return element[len(info + "="):] + + # Add this to properly close / delete the ldap interface / authenticator # when Python exits ... # Otherwise there's a risk that some funky error appears at the very end