mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
Merge pull request #797 from YunoHost/permission-visitors
'visitors' mechanics (replace the old public/private mechanic) + integrate the permission system with SSOwat
This commit is contained in:
commit
55aff6aaa1
12 changed files with 459 additions and 207 deletions
|
@ -176,6 +176,8 @@ else:
|
|||
elif action == "set":
|
||||
if key in ['redirected_urls', 'redirected_regex']:
|
||||
value = yaml.load(value)
|
||||
if key in ["unprotected_uris", "unprotected_regex", "protected_uris", "protected_regex"]:
|
||||
logger.warning("/!\\ Packagers! This app is using the legacy permission system. Please delete these legacy settings and use the new helpers 'ynh_permission_{create,urls,update,delete}' and the 'visitors' group to manage public/private access.")
|
||||
settings[key] = value
|
||||
else:
|
||||
raise ValueError("action should either be get, set or delete")
|
||||
|
@ -230,29 +232,51 @@ ynh_webpath_register () {
|
|||
|
||||
# Create a new permission for the app
|
||||
#
|
||||
# usage: ynh_permission_create --permission "permission" [--urls "url" ["url" ...]]
|
||||
# | arg: permission - the name for the permission (by default a permission named "main" already exist)
|
||||
# | arg: urls - (optional) a list of FULL urls for the permission (e.g. domain.tld/apps/admin)
|
||||
# example: ynh_permission_create --permission admin --url /admin --allowed alice bob
|
||||
#
|
||||
# usage: ynh_permission_create --permission "permission" [--url "url"] [--allowed group1 group2]
|
||||
# | arg: permission - the name for the permission (by default a permission named "main" already exist)
|
||||
# | arg: url - (optional) URL for which access will be allowed/forbidden
|
||||
# | arg: allowed - (optional) A list of group/user to allow for the permission
|
||||
#
|
||||
# If provided, 'url' is assumed to be relative to the app domain/path if they
|
||||
# start with '/'. For example:
|
||||
# / -> domain.tld/app
|
||||
# /admin -> domain.tld/app/admin
|
||||
# domain.tld/app/api -> domain.tld/app/api
|
||||
#
|
||||
# 'url' can be later treated as a regex if it starts with "re:".
|
||||
# For example:
|
||||
# re:/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$
|
||||
# re:domain.tld/app/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$
|
||||
#
|
||||
# example: ynh_permission_create --permission admin --urls domain.tld/blog/admin
|
||||
ynh_permission_create() {
|
||||
declare -Ar args_array=( [p]=permission= [u]=urls= )
|
||||
declare -Ar args_array=( [p]=permission= [u]=url= [a]=allowed= )
|
||||
local permission
|
||||
local urls
|
||||
local url
|
||||
local allowed
|
||||
ynh_handle_getopts_args "$@"
|
||||
|
||||
if [[ -n ${urls:-} ]]; then
|
||||
urls=",urls=['${urls//';'/"','"}']"
|
||||
if [[ -n ${url:-} ]]; then
|
||||
url="'$url'"
|
||||
else
|
||||
url="None"
|
||||
fi
|
||||
yunohost tools shell -c "from yunohost.permission import permission_create; permission_create('$app.$permission' ${urls:-}, sync_perm=False)"
|
||||
|
||||
if [[ -n ${allowed:-} ]]; then
|
||||
allowed=",allowed=['${allowed//';'/"','"}']"
|
||||
fi
|
||||
|
||||
yunohost tools shell -c "from yunohost.permission import permission_create; permission_create('$app.$permission', url=$url ${allowed:-} , sync_perm=False)"
|
||||
}
|
||||
|
||||
# Remove a permission for the app (note that when the app is removed all permission is automatically removed)
|
||||
#
|
||||
# usage: ynh_permission_remove --permission "permission"
|
||||
# example: ynh_permission_delete --permission editors
|
||||
#
|
||||
# usage: ynh_permission_delete --permission "permission"
|
||||
# | arg: permission - the name for the permission (by default a permission named "main" is removed automatically when the app is removed)
|
||||
#
|
||||
# example: ynh_permission_delete --permission editors
|
||||
ynh_permission_delete() {
|
||||
declare -Ar args_array=( [p]=permission= )
|
||||
local permission
|
||||
|
@ -261,30 +285,28 @@ ynh_permission_delete() {
|
|||
yunohost tools shell -c "from yunohost.permission import permission_delete; permission_delete('$app.$permission', sync_perm=False)"
|
||||
}
|
||||
|
||||
# Manage urls related to a permission
|
||||
# Redefine the url associated to a permission
|
||||
#
|
||||
# usage: ynh_permission_urls --permission "permission" --add "url" ["url" ...] --remove "url" ["url" ...]
|
||||
# usage: ynh_permission_url --permission "permission" --url "url"
|
||||
# | arg: permission - the name for the permission (by default a permission named "main" is removed automatically when the app is removed)
|
||||
# | arg: add - (optional) a list of FULL urls to add to the permission (e.g. domain.tld/apps/admin)
|
||||
# | arg: remove - (optional) a list of FULL urls to remove from the permission (e.g. other.tld/apps/admin)
|
||||
# | arg: url - (optional) URL for which access will be allowed/forbidden
|
||||
#
|
||||
ynh_permission_urls() {
|
||||
declare -Ar args_array=([p]=permission= [a]=add= [r]=remove=)
|
||||
ynh_permission_url() {
|
||||
declare -Ar args_array=([p]=permission= [u]=url=)
|
||||
local permission
|
||||
local add
|
||||
local remove
|
||||
local url
|
||||
ynh_handle_getopts_args "$@"
|
||||
|
||||
if [[ -n ${add:-} ]]; then
|
||||
add=",add=['${add//';'/"','"}']"
|
||||
fi
|
||||
if [[ -n ${remove:-} ]]; then
|
||||
remove=",remove=['${remove//';'/"','"}']"
|
||||
if [[ -n ${url:-} ]]; then
|
||||
url="'$url'"
|
||||
else
|
||||
url="None"
|
||||
fi
|
||||
|
||||
yunohost tools shell -c "from yunohost.permission import permission_urls; permission_urls('$app.$permission' ${add:-} ${remove:-})"
|
||||
yunohost tools shell -c "from yunohost.permission import permission_url; permission_url('$app.$permission', url=$url)"
|
||||
}
|
||||
|
||||
|
||||
# Update a permission for the app
|
||||
#
|
||||
# usage: ynh_permission_update --permission "permission" --add "group" ["group" ...] --remove "group" ["group" ...]
|
||||
|
|
|
@ -57,6 +57,12 @@ children:
|
|||
objectClass:
|
||||
- posixGroup
|
||||
- groupOfNamesYnh
|
||||
cn=visitors,ou=groups:
|
||||
cn: visitors
|
||||
gidNumber: "4003"
|
||||
objectClass:
|
||||
- posixGroup
|
||||
- groupOfNamesYnh
|
||||
|
||||
depends_children:
|
||||
cn=mail.main,ou=permission:
|
||||
|
|
|
@ -222,6 +222,9 @@
|
|||
"group_already_exist_on_system": "Group {group} already exists in the system groups",
|
||||
"group_created": "Group '{group}' created",
|
||||
"group_creation_failed": "Could not create the group '{group}': {error}",
|
||||
"group_cannot_edit_all_users": "The group 'all_users' cannot be edited manually. It is a special group meant to contain all users registered in YunoHost",
|
||||
"group_cannot_edit_visitors": "The group 'visitors' cannot be edited manually. It is a special group representing anonymous visitors",
|
||||
"group_cannot_edit_primary_group": "The group '{group}' cannot be edited manually. It is the primary group meant to contain only one specific user.",
|
||||
"group_cannot_be_edited": "The group {group} cannot be edited manually.",
|
||||
"group_cannot_be_deleted": "The group {group} cannot be deleted manually.",
|
||||
"group_deleted": "Group '{group}' deleted",
|
||||
|
@ -267,7 +270,7 @@
|
|||
"log_letsencrypt_cert_install": "Install a Let's encrypt certificate on '{}' domain",
|
||||
"log_permission_create": "Create permission '{}'",
|
||||
"log_permission_delete": "Delete permission '{}'",
|
||||
"log_permission_urls": "Update urls related to permission '{}'",
|
||||
"log_permission_url": "Update url related to permission '{}'",
|
||||
"log_selfsigned_cert_install": "Install self signed certificate on '{}' domain",
|
||||
"log_letsencrypt_cert_renew": "Renew '{}' Let's encrypt certificate",
|
||||
"log_regen_conf": "Regenerate system configurations '{}'",
|
||||
|
@ -345,7 +348,7 @@
|
|||
"migration_0011_can_not_backup_before_migration": "The backup of the system before the migration failed. Migration failed. Error: {error:s}",
|
||||
"migration_0011_create_group": "Creating a group for each user…",
|
||||
"migration_0011_done": "Migration successful. You are now able to manage usergroups.",
|
||||
"migration_0011_LDAP_config_dirty": "It look like that you customized your LDAP configuration. For this migration the LDAP configuration needs to be updated.\nYou need to save your current configuration, reintialize the original configuration by running 'yunohost tools regen-conf -f' and retry the migration",
|
||||
"migration_0011_slapd_config_will_be_overwritten": "It looks like you manually edited the slapd configuration. For this critical migration, YunoHost needs to force the update of the slapd configuration. The original files will be backuped in {conf_backup_folder}.",
|
||||
"migration_0011_LDAP_update_failed": "Could not update LDAP. Error: {error:s}",
|
||||
"migration_0011_migrate_permission": "Migrating permissions from apps settings to LDAP…",
|
||||
"migration_0011_migration_failed_trying_to_rollback": "Migration failed… trying to roll back the system.",
|
||||
|
@ -414,9 +417,12 @@
|
|||
"permission_already_allowed": "Group '{group}' already has permission '{permission}' enabled'",
|
||||
"permission_already_disallowed": "Group '{group}' already has permission '{permission}' disabled'",
|
||||
"permission_already_exist": "Permission '{permission}' already exists",
|
||||
"permission_already_up_to_date": "The permission was not updated because the addition/removal requests already match the current state.",
|
||||
"permission_cannot_remove_main": "Removing a main permission is not allowed",
|
||||
"permission_created": "Permission '{permission:s}' created",
|
||||
"permission_creation_failed": "Could not create permission '{permission}': {error}",
|
||||
"permission_currently_allowed_for_visitors": "This permission is currently granted to visitors in addition to other groups. You probably want to either remove the 'visitors' permission or remove the other groups it is currently granted to.",
|
||||
"permission_currently_allowed_for_all_users": "This permission is currently granted to all users in addition to other groups. You probably want to either remove the 'all_users' permission or remove the other groups it is currently granted to.",
|
||||
"permission_deleted": "Permission '{permission:s}' deleted",
|
||||
"permission_deletion_failed": "Could not delete permission '{permission}': {error}",
|
||||
"permission_not_found": "Permission '{permission:s}' not found",
|
||||
|
|
|
@ -41,7 +41,8 @@ from datetime import datetime
|
|||
|
||||
from moulinette import msignals, m18n, msettings
|
||||
from moulinette.utils.log import getActionLogger
|
||||
from moulinette.utils.filesystem import read_json, read_toml, write_to_json
|
||||
from moulinette.utils.filesystem import read_json, read_toml, read_yaml, write_to_json
|
||||
from moulinette.utils.filesystem import read_json, read_toml
|
||||
|
||||
from yunohost.service import service_log, service_status, _run_service_command
|
||||
from yunohost.utils import packages
|
||||
|
@ -427,23 +428,85 @@ def app_map(app=None, raw=False, user=None):
|
|||
if 'path' not in app_settings:
|
||||
# we assume that an app that doesn't have a path doesn't have an HTTP api
|
||||
continue
|
||||
# This 'no_sso' settings sound redundant to not having $path defined ....
|
||||
# At least from what I can see, all apps using it don't have a path defined ...
|
||||
if 'no_sso' in app_settings: # I don't think we need to check for the value here
|
||||
continue
|
||||
if user and user not in permissions[app_id + ".main"]["corresponding_users"]:
|
||||
continue
|
||||
# Users must at least have access to the main permission to have access to extra permissions
|
||||
if user:
|
||||
if not app_id + ".main" in permissions:
|
||||
logger.warning("Uhoh, no main permission was found for app %s ... sounds like an app was only partially removed due to another bug :/" % app_id)
|
||||
continue
|
||||
main_perm = permissions[app_id + ".main"]
|
||||
if user not in main_perm["corresponding_users"] and "visitors" not in main_perm["allowed"]:
|
||||
continue
|
||||
|
||||
domain = app_settings['domain']
|
||||
path = app_settings['path']
|
||||
path = app_settings['path'].rstrip('/')
|
||||
label = app_settings['label']
|
||||
|
||||
if raw:
|
||||
if domain not in result:
|
||||
result[domain] = {}
|
||||
result[domain][path] = {
|
||||
'label': app_settings['label'],
|
||||
'id': app_settings['id']
|
||||
}
|
||||
else:
|
||||
result[domain + path] = app_settings['label']
|
||||
def _sanitized_absolute_url(perm_url):
|
||||
# Nominal case : url is relative to the app's path
|
||||
if perm_url.startswith("/"):
|
||||
perm_domain = domain
|
||||
perm_path = path + perm_url.rstrip("/")
|
||||
# Otherwise, the urls starts with a domain name, like domain.tld/foo/bar
|
||||
# We want perm_domain = domain.tld and perm_path = "/foo/bar"
|
||||
else:
|
||||
perm_domain, perm_path = perm_url.split("/", 1)
|
||||
perm_path = "/" + perm_path.rstrip("/")
|
||||
|
||||
return perm_domain, perm_path
|
||||
|
||||
this_app_perms = {p: i for p, i in permissions.items() if p.startswith(app_id + ".") and i["url"]}
|
||||
for perm_name, perm_info in this_app_perms.items():
|
||||
# If we're building the map for a specific user, check the user
|
||||
# actually is allowed for this specific perm
|
||||
if user and user not in perm_info["corresponding_users"] and "visitors" not in perm_info["allowed"]:
|
||||
continue
|
||||
if perm_info["url"].startswith("re:"):
|
||||
# Here, we have an issue if the chosen url is a regex, because
|
||||
# the url we want to add to the dict is going to be turned into
|
||||
# a clickable link (or analyzed by other parts of yunohost
|
||||
# code...). To put it otherwise : in the current code of ssowat,
|
||||
# you can't give access a user to a regex.
|
||||
#
|
||||
# Instead, as drafted by Josue, we could rework the ssowat logic
|
||||
# about how routes and their permissions are defined. So for example,
|
||||
# have a dict of
|
||||
# { "/route1": ["visitors", "user1", "user2", ...], # Public route
|
||||
# "/route2_with_a_regex$": ["user1", "user2"], # Private route
|
||||
# "/route3": None, # Skipped route idk
|
||||
# }
|
||||
# then each time a user try to request and url, we only keep the
|
||||
# longest matching rule and check the user is allowed etc...
|
||||
#
|
||||
# The challenge with this is (beside actually implementing it)
|
||||
# is that it creates a whole new mechanism that ultimately
|
||||
# replace all the existing logic about
|
||||
# protected/unprotected/skipped uris and regexes and we gotta
|
||||
# handle / migrate all the legacy stuff somehow if we don't
|
||||
# want to end up with a total mess in the future idk
|
||||
logger.error("Permission %s can't be added to the SSOwat configuration because it doesn't support regexes so far..." % perm_name)
|
||||
continue
|
||||
|
||||
perm_domain, perm_path = _sanitized_absolute_url(perm_info["url"])
|
||||
|
||||
if perm_name.endswith(".main"):
|
||||
perm_label = label
|
||||
else:
|
||||
# e.g. if perm_name is wordpress.admin, we want "Blog (Admin)" (where Blog is the label of this app)
|
||||
perm_label = "%s (%s)" % (label, perm_name.rsplit(".")[-1].replace("_", " ").title())
|
||||
|
||||
if raw:
|
||||
if domain not in result:
|
||||
result[perm_domain] = {}
|
||||
result[perm_domain][perm_path] = {
|
||||
'label': perm_label,
|
||||
'id': app_id
|
||||
}
|
||||
else:
|
||||
result[perm_domain + perm_path] = perm_label
|
||||
|
||||
return result
|
||||
|
||||
|
@ -461,7 +524,6 @@ def app_change_url(operation_logger, app, domain, path):
|
|||
"""
|
||||
from yunohost.hook import hook_exec, hook_callback
|
||||
from yunohost.domain import _normalize_domain_path, _get_conflicting_apps
|
||||
from yunohost.permission import permission_urls
|
||||
|
||||
installed = _is_installed(app)
|
||||
if not installed:
|
||||
|
@ -551,8 +613,6 @@ def app_change_url(operation_logger, app, domain, path):
|
|||
app_setting(app, 'domain', value=domain)
|
||||
app_setting(app, 'path', value=path)
|
||||
|
||||
permission_urls(app+".main", add=[domain+path], remove=[old_domain+old_path], sync_perm=True)
|
||||
|
||||
# avoid common mistakes
|
||||
if _run_service_command("reload", "nginx") is False:
|
||||
# grab nginx errors
|
||||
|
@ -763,7 +823,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu
|
|||
|
||||
from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback
|
||||
from yunohost.log import OperationLogger
|
||||
from yunohost.permission import user_permission_list, permission_create, permission_urls, permission_delete, permission_sync_to_user
|
||||
from yunohost.permission import user_permission_list, permission_create, permission_url, permission_delete, permission_sync_to_user, user_permission_update
|
||||
|
||||
# Fetch or extract sources
|
||||
if not os.path.exists(INSTALL_TMP):
|
||||
|
@ -920,10 +980,9 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu
|
|||
if os.path.exists(os.path.join(extracted_app_folder, file_to_copy)):
|
||||
os.system('cp -R %s/%s %s' % (extracted_app_folder, file_to_copy, app_setting_path))
|
||||
|
||||
# Create permission before the install (useful if the install script redefine the permission)
|
||||
# Note that sync_perm is disabled to avoid triggering a whole bunch of code and messages
|
||||
# can't be sure that we don't have one case when it's needed
|
||||
permission_create(app_instance_name+".main", sync_perm=False)
|
||||
# Initialize the main permission for the app
|
||||
# After the install, if apps don't have a domain and path defined, the default url '/' is removed from the permission
|
||||
permission_create(app_instance_name+".main", url="/", allowed=["all_users"])
|
||||
|
||||
# Execute the app install script
|
||||
install_failed = True
|
||||
|
@ -1036,12 +1095,17 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu
|
|||
os.system('chown -R root: %s' % app_setting_path)
|
||||
os.system('chown -R admin: %s/scripts' % app_setting_path)
|
||||
|
||||
# Add path in permission if it's defined in the app install script
|
||||
# If an app doesn't have at least a domain and a path, assume it's not a webapp and remove the default "/" permission
|
||||
app_settings = _get_app_settings(app_instance_name)
|
||||
domain = app_settings.get('domain', None)
|
||||
path = app_settings.get('path', None)
|
||||
if domain and path:
|
||||
permission_urls(app_instance_name+".main", add=[domain+path], sync_perm=False)
|
||||
if not (domain and path):
|
||||
permission_url(app_instance_name + ".main", url=None, sync_perm=False)
|
||||
|
||||
# Migrate classic public app still using the legacy unprotected_uris
|
||||
if app_settings.get("unprotected_uris", None) == "/":
|
||||
user_permission_update(app_instance_name + ".main", remove="all_users", add="visitors", sync_perm=False)
|
||||
|
||||
permission_sync_to_user()
|
||||
|
||||
logger.success(m18n.n('installation_complete'))
|
||||
|
@ -1139,6 +1203,8 @@ def app_addaccess(apps, users=[]):
|
|||
"""
|
||||
from yunohost.permission import user_permission_update
|
||||
|
||||
logger.warning("/!\\ Packagers ! This app is using the legacy permission system. Please use the new helpers ynh_permission_{create,url,update,delete} and the 'visitors' group to manage permissions.")
|
||||
|
||||
output = {}
|
||||
for app in apps:
|
||||
permission = user_permission_update(app+".main", add=users, remove="all_users")
|
||||
|
@ -1158,6 +1224,8 @@ def app_removeaccess(apps, users=[]):
|
|||
"""
|
||||
from yunohost.permission import user_permission_update
|
||||
|
||||
logger.warning("/!\\ Packagers ! This app is using the legacy permission system. Please use the new helpers ynh_permission_{create,url,update,delete} and the 'visitors' group to manage permissions.")
|
||||
|
||||
output = {}
|
||||
for app in apps:
|
||||
permission = user_permission_update(app+".main", remove=users)
|
||||
|
@ -1176,6 +1244,8 @@ def app_clearaccess(apps):
|
|||
"""
|
||||
from yunohost.permission import user_permission_reset
|
||||
|
||||
logger.warning("/!\\ Packagers ! This app is using the legacy permission system. Please use the new helpers ynh_permission_{create,url,update,delete} and the 'visitors' group to manage permissions.")
|
||||
|
||||
output = {}
|
||||
for app in apps:
|
||||
permission = user_permission_reset(app+".main")
|
||||
|
@ -1279,6 +1349,8 @@ def app_setting(app, key, value=None, delete=False):
|
|||
# FIXME: Allow multiple values for some keys?
|
||||
if key in ['redirected_urls', 'redirected_regex']:
|
||||
value = yaml.load(value)
|
||||
if key in ["unprotected_uris", "unprotected_regex", "protected_uris", "protected_regex"]:
|
||||
logger.warning("/!\ Packagers ! This app is using the legacy permission system. Please delete these legacy settings and use the new helpers ynh_permission_{create,url,update,delete} and the 'visitors' group to manage public/private access.")
|
||||
app_settings[key] = value
|
||||
_set_app_settings(app, app_settings)
|
||||
|
||||
|
@ -1443,6 +1515,7 @@ def app_ssowatconf():
|
|||
|
||||
main_domain = _get_maindomain()
|
||||
domains = domain_list()['domains']
|
||||
all_permissions = user_permission_list(full=True)['permissions']
|
||||
|
||||
skipped_urls = []
|
||||
skipped_regex = []
|
||||
|
@ -1464,34 +1537,70 @@ def app_ssowatconf():
|
|||
return s.split(',') if s else []
|
||||
|
||||
for app in apps_list:
|
||||
with open(APPS_SETTING_PATH + app['id'] + '/settings.yml') as f:
|
||||
app_settings = yaml.load(f)
|
||||
|
||||
if 'no_sso' in app_settings:
|
||||
app_settings = read_yaml(APPS_SETTING_PATH + app['id'] + '/settings.yml')
|
||||
|
||||
if 'domain' not in app_settings:
|
||||
continue
|
||||
if 'path' not in app_settings:
|
||||
continue
|
||||
|
||||
# This 'no_sso' settings sound redundant to not having $path defined ....
|
||||
# At least from what I can see, all apps using it don't have a path defined ...
|
||||
if 'no_sso' in app_settings:
|
||||
continue
|
||||
|
||||
domain = app_settings['domain']
|
||||
path = app_settings['path'].rstrip('/')
|
||||
|
||||
def _sanitized_absolute_url(perm_url):
|
||||
# Nominal case : url is relative to the app's path
|
||||
if perm_url.startswith("/"):
|
||||
perm_domain = domain
|
||||
perm_path = path + perm_url.rstrip("/")
|
||||
# Otherwise, the urls starts with a domain name, like domain.tld/foo/bar
|
||||
# We want perm_domain = domain.tld and perm_path = "/foo/bar"
|
||||
else:
|
||||
perm_domain, perm_path = perm_url.split("/", 1)
|
||||
perm_path = "/" + perm_path.rstrip("/")
|
||||
|
||||
return perm_domain + perm_path
|
||||
|
||||
# Skipped
|
||||
skipped_urls += [_sanitized_absolute_url(uri) for uri in _get_setting(app_settings, 'skipped_uris')]
|
||||
skipped_regex += _get_setting(app_settings, 'skipped_regex')
|
||||
|
||||
# Redirected
|
||||
redirected_urls.update(app_settings.get('redirected_urls', {}))
|
||||
redirected_regex.update(app_settings.get('redirected_regex', {}))
|
||||
|
||||
# Legacy permission system using (un)protected_uris and _regex managed in app settings...
|
||||
unprotected_urls += [_sanitized_absolute_url(uri) for uri in _get_setting(app_settings, 'unprotected_uris')]
|
||||
protected_urls += [_sanitized_absolute_url(uri) for uri in _get_setting(app_settings, 'protected_uris')]
|
||||
unprotected_regex += _get_setting(app_settings, 'unprotected_regex')
|
||||
protected_regex += _get_setting(app_settings, 'protected_regex')
|
||||
|
||||
# New permission system
|
||||
this_app_perms = {name: info for name, info in all_permissions.items() if name.startswith(app['id'] + ".")}
|
||||
for perm_name, perm_info in this_app_perms.items():
|
||||
|
||||
# Ignore permissions for which there's no url defined
|
||||
if not perm_info["url"]:
|
||||
continue
|
||||
|
||||
for item in _get_setting(app_settings, 'skipped_uris'):
|
||||
if item[-1:] == '/':
|
||||
item = item[:-1]
|
||||
skipped_urls.append(app_settings['domain'] + app_settings['path'].rstrip('/') + item)
|
||||
for item in _get_setting(app_settings, 'skipped_regex'):
|
||||
skipped_regex.append(item)
|
||||
for item in _get_setting(app_settings, 'unprotected_uris'):
|
||||
if item[-1:] == '/':
|
||||
item = item[:-1]
|
||||
unprotected_urls.append(app_settings['domain'] + app_settings['path'].rstrip('/') + item)
|
||||
for item in _get_setting(app_settings, 'unprotected_regex'):
|
||||
unprotected_regex.append(item)
|
||||
for item in _get_setting(app_settings, 'protected_uris'):
|
||||
if item[-1:] == '/':
|
||||
item = item[:-1]
|
||||
protected_urls.append(app_settings['domain'] + app_settings['path'].rstrip('/') + item)
|
||||
for item in _get_setting(app_settings, 'protected_regex'):
|
||||
protected_regex.append(item)
|
||||
if 'redirected_urls' in app_settings:
|
||||
redirected_urls.update(app_settings['redirected_urls'])
|
||||
if 'redirected_regex' in app_settings:
|
||||
redirected_regex.update(app_settings['redirected_regex'])
|
||||
# FIXME : gotta handle regex-urls here... meh
|
||||
url = _sanitized_absolute_url(perm_info["url"])
|
||||
if "visitors" in perm_info["allowed"]:
|
||||
unprotected_urls.append(url)
|
||||
|
||||
# Legacy stuff : we remove now unprotected-urls that might have been declared as protected earlier...
|
||||
protected_urls = [u for u in protected_urls if u != url]
|
||||
else:
|
||||
# TODO : small optimization to implement : we don't need to explictly add all the app roots
|
||||
protected_urls.append(url)
|
||||
|
||||
# Legacy stuff : we remove now unprotected-urls that might have been declared as protected earlier...
|
||||
unprotected_urls = [u for u in unprotected_urls if u != url]
|
||||
|
||||
for domain in domains:
|
||||
skipped_urls.extend([domain + '/yunohost/admin', domain + '/yunohost/api'])
|
||||
|
@ -1500,10 +1609,14 @@ def app_ssowatconf():
|
|||
skipped_regex.append("^[^/]*/%.well%-known/acme%-challenge/.*$")
|
||||
skipped_regex.append("^[^/]*/%.well%-known/autoconfig/mail/config%-v1%.1%.xml.*$")
|
||||
|
||||
|
||||
permissions_per_url = {}
|
||||
for permission_name, permission_infos in user_permission_list(full=True)['permissions'].items():
|
||||
for url in permission_infos["urls"]:
|
||||
permissions_per_url[url] = permission_infos['corresponding_users']
|
||||
for perm_name, perm_info in all_permissions.items():
|
||||
# Ignore permissions for which there's no url defined
|
||||
if not perm_info["url"]:
|
||||
continue
|
||||
permissions_per_url[perm_info["url"]] = perm_info['corresponding_users']
|
||||
|
||||
|
||||
conf_dict = {
|
||||
'portal_domain': main_domain,
|
||||
|
@ -2683,10 +2796,8 @@ def _parse_args_in_yunohost_format(args, action_args):
|
|||
if arg_value not in domain_list()['domains']:
|
||||
raise YunohostError('app_argument_invalid', name=arg_name, error=m18n.n('domain_unknown'))
|
||||
elif arg_type == 'user':
|
||||
try:
|
||||
user_info(arg_value)
|
||||
except YunohostError as e:
|
||||
raise YunohostError('app_argument_invalid', name=arg_name, error=e)
|
||||
if not arg_value in user_list()["users"].keys():
|
||||
raise YunohostError('app_argument_invalid', name=arg_name, error=m18n.n('user_unknown', user=arg_value))
|
||||
elif arg_type == 'app':
|
||||
if not _is_installed(arg_value):
|
||||
raise YunohostError('app_argument_invalid', name=arg_name, error=m18n.n('app_unknown'))
|
||||
|
|
|
@ -1189,7 +1189,7 @@ class RestoreManager():
|
|||
return
|
||||
|
||||
from yunohost.user import user_group_list
|
||||
from yunohost.permission import permission_create, permission_delete, user_permission_update, user_permission_list
|
||||
from yunohost.permission import permission_create, permission_delete, user_permission_update, user_permission_list, permission_sync_to_user
|
||||
|
||||
# Backup old permission for apps
|
||||
# We need to do that because in case of an app is installed we can't remove the permission for this app
|
||||
|
@ -1245,14 +1245,16 @@ class RestoreManager():
|
|||
|
||||
# Remove all permission for all app which is still in the LDAP
|
||||
for permission_name in user_permission_list(ignore_system_perms=True)["permissions"].keys():
|
||||
permission_delete(permission_name, force=True)
|
||||
permission_delete(permission_name, force=True, sync_perm=False)
|
||||
|
||||
# Restore permission for the app which is installed
|
||||
for permission_name, permission_infos in old_apps_permission.items():
|
||||
app_name = permission_name.split(".")[0]
|
||||
if _is_installed(app_name):
|
||||
permission_create(permission_name, urls=permission_infos["urls"], sync_perm=False)
|
||||
user_permission_update(permission_name, remove="all_users", add=permission_infos["allowed"])
|
||||
permission_create(permission_name, url=permission_infos["url"], allowed=permission_infos["allowed"], sync_perm=False)
|
||||
|
||||
permission_sync_to_user()
|
||||
|
||||
|
||||
def _restore_apps(self):
|
||||
"""Restore all apps targeted"""
|
||||
|
@ -1290,7 +1292,7 @@ class RestoreManager():
|
|||
restore_app_failed -- Raised if the restore bash script failed
|
||||
"""
|
||||
from yunohost.user import user_group_list
|
||||
from yunohost.permission import permission_create, permission_delete, user_permission_list, user_permission_update
|
||||
from yunohost.permission import permission_create, permission_delete, user_permission_list, user_permission_update, permission_sync_to_user
|
||||
|
||||
def copytree(src, dst, symlinks=False, ignore=None):
|
||||
for item in os.listdir(src):
|
||||
|
@ -1362,15 +1364,15 @@ class RestoreManager():
|
|||
|
||||
for permission_name, permission_infos in permissions.items():
|
||||
|
||||
permission_create(permission_name, urls=permission_infos.get("urls", []))
|
||||
|
||||
if "allowed" not in permission_infos:
|
||||
logger.warning("'allowed' key corresponding to allowed groups for permission %s not found when restoring app %s … You might have to reconfigure permissions yourself." % (permission_name, app_instance_name))
|
||||
should_be_allowed = ["all_users"]
|
||||
else:
|
||||
should_be_allowed = [g for g in permission_infos["allowed"] if g in existing_groups]
|
||||
current_allowed = user_permission_list()["permissions"][permission_name]["allowed"]
|
||||
if should_be_allowed != current_allowed:
|
||||
user_permission_update(permission_name, remove=current_allowed, add=should_be_allowed)
|
||||
|
||||
permission_create(permission_name, url=permission_infos.get("url", None), allowed=should_be_allowed, sync_perm=False)
|
||||
|
||||
permission_sync_to_user()
|
||||
|
||||
os.remove('%s/permissions.yml' % app_settings_new_path)
|
||||
else:
|
||||
|
|
|
@ -9,7 +9,7 @@ from moulinette.utils.filesystem import read_yaml
|
|||
from yunohost.tools import Migration
|
||||
from yunohost.user import user_group_create, user_group_update
|
||||
from yunohost.app import app_setting, app_list
|
||||
from yunohost.regenconf import regen_conf
|
||||
from yunohost.regenconf import regen_conf, BACKUP_CONF_DIR
|
||||
from yunohost.permission import permission_create, user_permission_update, permission_sync_to_user
|
||||
|
||||
logger = getActionLogger('yunohost.migration')
|
||||
|
@ -60,16 +60,21 @@ class MyMigration(Migration):
|
|||
ldap_map = read_yaml('/usr/share/yunohost/yunohost-config/moulinette/ldap_scheme.yml')
|
||||
|
||||
try:
|
||||
self.remove_if_exists("cn=sftpusers,ou=groups")
|
||||
self.remove_if_exists("ou=permission")
|
||||
self.remove_if_exists('cn=all_users,ou=groups')
|
||||
self.remove_if_exists('ou=groups')
|
||||
|
||||
attr_dict = ldap_map['parents']['ou=permission']
|
||||
ldap.add('ou=permission', attr_dict)
|
||||
|
||||
attr_dict = ldap_map['parents']['ou=groups']
|
||||
ldap.add('ou=groups', attr_dict)
|
||||
|
||||
attr_dict = ldap_map['children']['cn=all_users,ou=groups']
|
||||
ldap.add('cn=all_users,ou=groups', attr_dict)
|
||||
|
||||
attr_dict = ldap_map['children']['cn=visitors,ou=groups']
|
||||
ldap.add('cn=visitors,ou=groups', attr_dict)
|
||||
|
||||
for rdn, attr_dict in ldap_map['depends_children'].items():
|
||||
ldap.add(rdn, attr_dict)
|
||||
except Exception as e:
|
||||
|
@ -102,13 +107,21 @@ class MyMigration(Migration):
|
|||
path = app_setting(app, 'path')
|
||||
domain = app_setting(app, 'domain')
|
||||
|
||||
urls = [domain + path] if domain and path else None
|
||||
permission_create(app+".main", urls=urls, sync_perm=False)
|
||||
url = "/" if domain and path else None
|
||||
if permission:
|
||||
allowed_group = permission.split(',')
|
||||
user_permission_update(app+".main", remove="all_users", add=allowed_group, sync_perm=False)
|
||||
allowed_groups = permission.split(',')
|
||||
else:
|
||||
allowed_groups = ["all_users"]
|
||||
permission_create(app+".main", url=url, allowed=allowed_groups, sync_perm=False)
|
||||
|
||||
app_setting(app, 'allowed_users', delete=True)
|
||||
|
||||
# Migrate classic public app still using the legacy unprotected_uris
|
||||
if app_setting(app, "unprotected_uris") == "/":
|
||||
user_permission_update(app+".main", remove="all_users", add="visitors", sync_perm=False)
|
||||
|
||||
permission_sync_to_user()
|
||||
|
||||
def run(self):
|
||||
|
||||
# FIXME : what do we really want to do here ...
|
||||
|
@ -119,7 +132,7 @@ class MyMigration(Migration):
|
|||
ldap_regen_conf_status = regen_conf(names=['slapd'], dry_run=True)
|
||||
# By this we check if the have been customized
|
||||
if ldap_regen_conf_status and ldap_regen_conf_status['slapd']['pending']:
|
||||
raise YunohostError("migration_0011_LDAP_config_dirty")
|
||||
logger.warning(m18n.n("migration_0011_slapd_config_will_be_overwritten", conf_backup_folder=BACKUP_CONF_DIR))
|
||||
|
||||
# Backup LDAP and the apps settings before to do the migration
|
||||
logger.info(m18n.n("migration_0011_backup_before_migration"))
|
||||
|
|
|
@ -73,7 +73,7 @@ def user_permission_list(short=False, full=False, ignore_system_perms=False):
|
|||
|
||||
if full:
|
||||
permissions[name]["corresponding_users"] = [_ldap_path_extract(p, "uid") for p in infos.get('inheritPermission', [])]
|
||||
permissions[name]["urls"] = infos.get("URL", [])
|
||||
permissions[name]["url"] = infos.get("URL", [None])[0]
|
||||
|
||||
if short:
|
||||
permissions = permissions.keys()
|
||||
|
@ -142,15 +142,15 @@ def user_permission_update(operation_logger, permission, add=None, remove=None,
|
|||
# we shall warn the users that they should probably choose between one or the other,
|
||||
# because the current situation is probably not what they expect / is temporary ?
|
||||
|
||||
if len(new_allowed_groups) > 1 and "all_users" in new_allowed_groups:
|
||||
# FIXME : i18n
|
||||
# FIXME : write a better explanation ?
|
||||
logger.warning("This permission is currently enabled for all users in addition to other groups. You probably want to either remove the 'all_users' permission or remove the specific groups currently allowed.")
|
||||
if len(new_allowed_groups) > 1:
|
||||
if "all_users" in new_allowed_groups:
|
||||
logger.warning(m18n.n("permission_currently_allowed_for_all_users"))
|
||||
if "visitors" in new_allowed_groups:
|
||||
logger.warning(m18n.n("permission_currently_allowed_for_visitors"))
|
||||
|
||||
# Don't update LDAP if we update exactly the same values
|
||||
if set(new_allowed_groups) == set(current_allowed_groups):
|
||||
# FIXME : i18n
|
||||
logger.warning("The permission was not updated all addition/removal requests already match the current state.")
|
||||
logger.warning("permission_already_up_to_date")
|
||||
return
|
||||
|
||||
# Commit the new allowed group list
|
||||
|
@ -212,6 +212,10 @@ def user_permission_reset(operation_logger, permission, sync_perm=True):
|
|||
if existing_permission is None:
|
||||
raise YunohostError('permission_not_found', permission=permission)
|
||||
|
||||
if existing_permission["allowed"] == ["all_users"]:
|
||||
logger.warning(m18n.n("permission_already_up_to_date"))
|
||||
return
|
||||
|
||||
# Update permission with default (all_users)
|
||||
|
||||
operation_logger.related_to.append(('app', permission.split(".")[0]))
|
||||
|
@ -251,21 +255,34 @@ def user_permission_reset(operation_logger, permission, sync_perm=True):
|
|||
#
|
||||
# The followings methods are *not* directly exposed.
|
||||
# They are used to create/delete the permissions (e.g. during app install/remove)
|
||||
# and by some app helpers to possibly add additional permissions and tweak the urls
|
||||
# and by some app helpers to possibly add additional permissions
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
@is_unit_operation()
|
||||
def permission_create(operation_logger, permission, urls=None, sync_perm=True):
|
||||
def permission_create(operation_logger, permission, url=None, allowed=None, sync_perm=True):
|
||||
"""
|
||||
Create a new permission for a specific application
|
||||
|
||||
Keyword argument:
|
||||
permission -- Name of the permission (e.g. mail or nextcloud or wordpress.editors)
|
||||
urls -- list of urls to specify for the permission
|
||||
url -- (optional) URL for which access will be allowed/forbidden
|
||||
allowed -- (optional) A list of group/user to allow for the permission
|
||||
|
||||
If provided, 'url' is assumed to be relative to the app domain/path if they
|
||||
start with '/'. For example:
|
||||
/ -> domain.tld/app
|
||||
/admin -> domain.tld/app/admin
|
||||
domain.tld/app/api -> domain.tld/app/api
|
||||
|
||||
'url' can be later treated as a regex if it starts with "re:".
|
||||
For example:
|
||||
re:/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$
|
||||
re:domain.tld/app/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$
|
||||
"""
|
||||
|
||||
from yunohost.user import user_group_list
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
ldap = _get_ldap_interface()
|
||||
|
||||
|
@ -292,12 +309,22 @@ def permission_create(operation_logger, permission, urls=None, sync_perm=True):
|
|||
'gidNumber': gid,
|
||||
}
|
||||
|
||||
# If who should be allowed is explicitly provided, use this info
|
||||
if allowed:
|
||||
if not isinstance(allowed, list):
|
||||
allowed = [allowed]
|
||||
# (though first we validate that the targets actually exist)
|
||||
all_existing_groups = user_group_list()['groups'].keys()
|
||||
for g in allowed:
|
||||
if g not in all_existing_groups:
|
||||
raise YunohostError('group_unknown', group=g)
|
||||
attr_dict['groupPermission'] = ['cn=%s,ou=groups,dc=yunohost,dc=org' % g for g in allowed]
|
||||
# For main permission, we add all users by default
|
||||
if permission.endswith(".main"):
|
||||
elif permission.endswith(".main"):
|
||||
attr_dict['groupPermission'] = ['cn=all_users,ou=groups,dc=yunohost,dc=org']
|
||||
|
||||
if urls:
|
||||
attr_dict['URL'] = [_normalize_url(url) for url in urls]
|
||||
if url:
|
||||
attr_dict['URL'] = url
|
||||
|
||||
operation_logger.related_to.append(('app', permission.split(".")[0]))
|
||||
operation_logger.start()
|
||||
|
@ -315,15 +342,13 @@ def permission_create(operation_logger, permission, urls=None, sync_perm=True):
|
|||
|
||||
|
||||
@is_unit_operation()
|
||||
def permission_urls(operation_logger, permission, add=None, remove=None, sync_perm=True):
|
||||
def permission_url(operation_logger, permission, url=None, sync_perm=True):
|
||||
"""
|
||||
Update urls related to a permission for a specific application
|
||||
|
||||
Keyword argument:
|
||||
permission -- Name of the permission (e.g. mail or nextcloud or wordpress.editors)
|
||||
add -- List of urls to add
|
||||
remove -- List of urls to remove
|
||||
|
||||
url -- (optional) URL for which access will be allowed/forbidden
|
||||
"""
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
ldap = _get_ldap_interface()
|
||||
|
@ -335,19 +360,9 @@ def permission_urls(operation_logger, permission, add=None, remove=None, sync_pe
|
|||
raise YunohostError('permission_not_found', permission=permission)
|
||||
|
||||
# Compute new url list
|
||||
old_url = existing_permission["url"]
|
||||
|
||||
new_urls = copy.copy(existing_permission["urls"])
|
||||
|
||||
if add:
|
||||
urls_to_add = [add] if not isinstance(add, list) else add
|
||||
urls_to_add = [_normalize_url(url) for url in urls_to_add]
|
||||
new_urls += urls_to_add
|
||||
if remove:
|
||||
urls_to_remove = [remove] if not isinstance(remove, list) else remove
|
||||
urls_to_remove = [_normalize_url(url) for url in urls_to_remove]
|
||||
new_urls = [u for u in new_urls if u not in urls_to_remove]
|
||||
|
||||
if set(new_urls) == set(existing_permission["urls"]):
|
||||
if old_url == url:
|
||||
logger.warning(m18n.n('permission_update_nothing_to_do'))
|
||||
return existing_permission
|
||||
|
||||
|
@ -357,7 +372,7 @@ def permission_urls(operation_logger, permission, add=None, remove=None, sync_pe
|
|||
operation_logger.start()
|
||||
|
||||
try:
|
||||
ldap.update('cn=%s,ou=permission' % permission, {'URL': new_urls})
|
||||
ldap.update('cn=%s,ou=permission' % permission, {'URL': [url]})
|
||||
except Exception as e:
|
||||
raise YunohostError('permission_update_failed', permission=permission, error=e)
|
||||
|
||||
|
@ -452,11 +467,3 @@ def permission_sync_to_user():
|
|||
# Reload unscd, otherwise the group ain't propagated to the LDAP database
|
||||
os.system('nscd --invalidate=passwd')
|
||||
os.system('nscd --invalidate=group')
|
||||
|
||||
|
||||
def _normalize_url(url):
|
||||
from yunohost.domain import _normalize_domain_path
|
||||
domain = url[:url.index('/')]
|
||||
path = url[url.index('/'):]
|
||||
domain, path = _normalize_domain_path(domain, path)
|
||||
return domain + path
|
||||
|
|
|
@ -119,10 +119,10 @@ def app_is_exposed_on_http(domain, path, message_in_page):
|
|||
return False
|
||||
|
||||
|
||||
def install_legacy_app(domain, path):
|
||||
def install_legacy_app(domain, path, public=True):
|
||||
|
||||
app_install("./tests/apps/legacy_app_ynh",
|
||||
args="domain=%s&path=%s" % (domain, path),
|
||||
args="domain=%s&path=%s&is_public=%s" % (domain, path, 1 if public else 0),
|
||||
force=True)
|
||||
|
||||
|
||||
|
@ -180,13 +180,7 @@ def test_legacy_app_install_secondary_domain_on_root(secondary_domain):
|
|||
|
||||
def test_legacy_app_install_private(secondary_domain):
|
||||
|
||||
install_legacy_app(secondary_domain, "/legacy")
|
||||
|
||||
settings = open("/etc/yunohost/apps/legacy_app/settings.yml", "r").read()
|
||||
new_settings = settings.replace("\nunprotected_uris: /", "")
|
||||
assert new_settings != settings
|
||||
open("/etc/yunohost/apps/legacy_app/settings.yml", "w").write(new_settings)
|
||||
app_ssowatconf()
|
||||
install_legacy_app(secondary_domain, "/legacy", public=False)
|
||||
|
||||
assert app_is_installed(secondary_domain, "legacy_app")
|
||||
assert not app_is_exposed_on_http(secondary_domain, "/legacy", "This is a dummy app")
|
||||
|
|
|
@ -529,11 +529,11 @@ def test_backup_and_restore_permission_app():
|
|||
assert "permissions_app.main" in res
|
||||
assert "permissions_app.admin" in res
|
||||
assert "permissions_app.dev" in res
|
||||
assert res['permissions_app.main']['urls'] == [maindomain + "/urlpermissionapp"]
|
||||
assert res['permissions_app.admin']['urls'] == [maindomain + "/urlpermissionapp/admin"]
|
||||
assert res['permissions_app.dev']['urls'] == [maindomain + "/urlpermissionapp/dev"]
|
||||
assert res['permissions_app.main']['url'] == "/"
|
||||
assert res['permissions_app.admin']['url'] == "/admin"
|
||||
assert res['permissions_app.dev']['url'] == "/dev"
|
||||
|
||||
assert res['permissions_app.main']['allowed'] == ["all_users"]
|
||||
assert res['permissions_app.main']['allowed'] == ["visitors"]
|
||||
assert res['permissions_app.admin']['allowed'] == ["alice"]
|
||||
assert res['permissions_app.dev']['allowed'] == []
|
||||
|
||||
|
@ -543,11 +543,11 @@ def test_backup_and_restore_permission_app():
|
|||
assert "permissions_app.main" in res
|
||||
assert "permissions_app.admin" in res
|
||||
assert "permissions_app.dev" in res
|
||||
assert res['permissions_app.main']['urls'] == [maindomain + "/urlpermissionapp"]
|
||||
assert res['permissions_app.admin']['urls'] == [maindomain + "/urlpermissionapp/admin"]
|
||||
assert res['permissions_app.dev']['urls'] == [maindomain + "/urlpermissionapp/dev"]
|
||||
assert res['permissions_app.main']['url'] == "/"
|
||||
assert res['permissions_app.admin']['url'] == "/admin"
|
||||
assert res['permissions_app.dev']['url'] == "/dev"
|
||||
|
||||
assert res['permissions_app.main']['allowed'] == ["all_users"]
|
||||
assert res['permissions_app.main']['allowed'] == ["visitors"]
|
||||
assert res['permissions_app.admin']['allowed'] == ["alice"]
|
||||
assert res['permissions_app.dev']['allowed'] == []
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import requests
|
||||
import pytest
|
||||
|
||||
from yunohost.app import app_install, app_remove, app_change_url, app_list, app_map
|
||||
|
@ -5,19 +6,20 @@ from yunohost.app import app_install, app_remove, app_change_url, app_list, app_
|
|||
from yunohost.user import user_list, user_info, user_create, user_delete, user_update, \
|
||||
user_group_list, user_group_create, user_group_delete, user_group_update, user_group_info
|
||||
from yunohost.permission import user_permission_update, user_permission_list, user_permission_reset, \
|
||||
permission_create, permission_urls, permission_delete
|
||||
permission_create, permission_delete, permission_url
|
||||
from yunohost.domain import _get_maindomain
|
||||
from yunohost.utils.error import YunohostError
|
||||
|
||||
# Get main domain
|
||||
maindomain = _get_maindomain()
|
||||
dummy_password = "test123Ynh"
|
||||
|
||||
def clean_user_groups_permission():
|
||||
for u in user_list()['users']:
|
||||
user_delete(u)
|
||||
|
||||
for g in user_group_list()['groups']:
|
||||
if g != "all_users":
|
||||
if g not in ["all_users", "visitors"]:
|
||||
user_group_delete(g)
|
||||
|
||||
for p in user_permission_list()['permissions']:
|
||||
|
@ -27,9 +29,9 @@ def clean_user_groups_permission():
|
|||
def setup_function(function):
|
||||
clean_user_groups_permission()
|
||||
|
||||
user_create("alice", "Alice", "White", "alice@" + maindomain, "test123Ynh")
|
||||
user_create("bob", "Bob", "Snow", "bob@" + maindomain, "test123Ynh")
|
||||
permission_create("wiki.main", urls=[maindomain + "/wiki"], sync_perm=False)
|
||||
user_create("alice", "Alice", "White", "alice@" + maindomain, dummy_password)
|
||||
user_create("bob", "Bob", "Snow", "bob@" + maindomain, dummy_password)
|
||||
permission_create("wiki.main", url="/", sync_perm=False)
|
||||
permission_create("blog.main", sync_perm=False)
|
||||
user_permission_update("blog.main", remove="all_users", add="alice")
|
||||
|
||||
|
@ -39,6 +41,10 @@ def teardown_function(function):
|
|||
app_remove("permissions_app")
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
app_remove("legacy_app")
|
||||
except:
|
||||
pass
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def check_LDAP_db_integrity_call():
|
||||
|
@ -156,6 +162,31 @@ def check_permission_for_apps():
|
|||
|
||||
assert installed_apps == app_perms_prefix
|
||||
|
||||
|
||||
def can_access_webpage(webpath, logged_as=None):
|
||||
|
||||
webpath = webpath.rstrip("/")
|
||||
sso_url = "https://"+maindomain+"/yunohost/sso/"
|
||||
|
||||
# Anonymous access
|
||||
if not logged_as:
|
||||
r = requests.get(webpath, verify=False)
|
||||
# Login as a user using dummy password
|
||||
else:
|
||||
with requests.Session() as session:
|
||||
session.post(sso_url,
|
||||
data={"user": logged_as,
|
||||
"password": dummy_password},
|
||||
headers={"Referer": sso_url,
|
||||
"Content-Type": "application/x-www-form-urlencoded"},
|
||||
verify=False)
|
||||
# We should have some cookies related to authentication now
|
||||
assert session.cookies
|
||||
r = session.get(webpath, verify=False)
|
||||
|
||||
# If we can't access it, we got redirected to the SSO
|
||||
return not r.url.startswith(sso_url)
|
||||
|
||||
#
|
||||
# List functions
|
||||
#
|
||||
|
@ -171,7 +202,7 @@ def test_permission_list():
|
|||
assert res['blog.main']['allowed'] == ["alice"]
|
||||
assert set(res['wiki.main']['corresponding_users']) == set(["alice", "bob"])
|
||||
assert res['blog.main']['corresponding_users'] == ["alice"]
|
||||
assert res['wiki.main']['urls'] == [maindomain + "/wiki"]
|
||||
assert res['wiki.main']['url'] == "/"
|
||||
|
||||
#
|
||||
# Create - Remove functions
|
||||
|
@ -195,6 +226,15 @@ def test_permission_create_extra():
|
|||
assert "all_users" not in res['site.test']['allowed']
|
||||
assert res['site.test']['corresponding_users'] == []
|
||||
|
||||
|
||||
def test_permission_create_with_allowed():
|
||||
permission_create("site.test", allowed=["alice"])
|
||||
|
||||
res = user_permission_list(full=True)['permissions']
|
||||
assert "site.test" in res
|
||||
assert res['site.test']['allowed'] == ["alice"]
|
||||
|
||||
|
||||
def test_permission_delete():
|
||||
permission_delete("wiki.main", force=True)
|
||||
|
||||
|
@ -275,6 +315,17 @@ def test_permission_reset():
|
|||
assert res['blog.main']['allowed'] == ["all_users"]
|
||||
assert set(res['blog.main']['corresponding_users']) == set(["alice", "bob"])
|
||||
|
||||
|
||||
def test_permission_reset_idempotency():
|
||||
# Reset permission
|
||||
user_permission_reset("blog.main")
|
||||
user_permission_reset("blog.main")
|
||||
|
||||
res = user_permission_list(full=True)['permissions']
|
||||
assert res['blog.main']['allowed'] == ["all_users"]
|
||||
assert set(res['blog.main']['corresponding_users']) == set(["alice", "bob"])
|
||||
|
||||
|
||||
#
|
||||
# Error on update function
|
||||
#
|
||||
|
@ -291,41 +342,19 @@ def test_permission_update_permission_that_doesnt_exist():
|
|||
with pytest.raises(YunohostError):
|
||||
user_permission_update("doesnt.exist", add="alice")
|
||||
|
||||
|
||||
# Permission url management
|
||||
|
||||
def test_permission_add_url():
|
||||
permission_urls("blog.main", add=[maindomain + "/testA"])
|
||||
def test_permission_redefine_url():
|
||||
permission_url("blog.main", url="/pwet")
|
||||
|
||||
res = user_permission_list(full=True)['permissions']
|
||||
assert res["blog.main"]["urls"] == [maindomain + "/testA"]
|
||||
|
||||
def test_permission_add_second_url():
|
||||
permission_urls("wiki.main", add=[maindomain + "/testA"])
|
||||
|
||||
res = user_permission_list(full=True)['permissions']
|
||||
assert set(res["wiki.main"]["urls"]) == set([maindomain + "/testA", maindomain + "/wiki"])
|
||||
assert res["blog.main"]["url"] == "/pwet"
|
||||
|
||||
def test_permission_remove_url():
|
||||
permission_urls("wiki.main", remove=[maindomain + "/wiki"])
|
||||
permission_url("blog.main", url=None)
|
||||
|
||||
res = user_permission_list(full=True)['permissions']
|
||||
assert res["wiki.main"]["urls"] == []
|
||||
|
||||
def test_permission_add_url_already_added():
|
||||
res = user_permission_list(full=True)['permissions']
|
||||
assert res["wiki.main"]["urls"] == [maindomain + "/wiki"]
|
||||
|
||||
permission_urls("wiki.main", add=[maindomain + "/wiki"])
|
||||
|
||||
res = user_permission_list(full=True)['permissions']
|
||||
assert res["wiki.main"]["urls"] == [maindomain + "/wiki"]
|
||||
|
||||
def test_permission_remove_url_not_added():
|
||||
permission_urls("wiki.main", remove=[maindomain + "/doesnt_exist"])
|
||||
|
||||
res = user_permission_list(full=True)['permissions']
|
||||
assert res['wiki.main']['urls'] == [maindomain + "/wiki"]
|
||||
assert res["blog.main"]["url"] is None
|
||||
|
||||
#
|
||||
# Application interaction
|
||||
|
@ -333,15 +362,15 @@ def test_permission_remove_url_not_added():
|
|||
|
||||
def test_permission_app_install():
|
||||
app_install("./tests/apps/permissions_app_ynh",
|
||||
args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True)
|
||||
args="domain=%s&path=%s&is_public=0&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True)
|
||||
|
||||
res = user_permission_list(full=True)['permissions']
|
||||
assert "permissions_app.main" in res
|
||||
assert "permissions_app.admin" in res
|
||||
assert "permissions_app.dev" in res
|
||||
assert res['permissions_app.main']['urls'] == [maindomain + "/urlpermissionapp"]
|
||||
assert res['permissions_app.admin']['urls'] == [maindomain + "/urlpermissionapp/admin"]
|
||||
assert res['permissions_app.dev']['urls'] == [maindomain + "/urlpermissionapp/dev"]
|
||||
assert res['permissions_app.main']['url'] == "/"
|
||||
assert res['permissions_app.admin']['url'] == "/admin"
|
||||
assert res['permissions_app.dev']['url'] == "/dev"
|
||||
|
||||
assert res['permissions_app.main']['allowed'] == ["all_users"]
|
||||
assert set(res['permissions_app.main']['corresponding_users']) == set(["alice", "bob"])
|
||||
|
@ -361,7 +390,7 @@ def test_permission_app_install():
|
|||
|
||||
def test_permission_app_remove():
|
||||
app_install("./tests/apps/permissions_app_ynh",
|
||||
args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True)
|
||||
args="domain=%s&path=%s&is_public=0&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True)
|
||||
app_remove("permissions_app")
|
||||
|
||||
# Check all permissions for this app got deleted
|
||||
|
@ -372,14 +401,66 @@ def test_permission_app_change_url():
|
|||
app_install("./tests/apps/permissions_app_ynh",
|
||||
args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True)
|
||||
|
||||
# FIXME : should rework this test to look for differences in the generated app map / app tiles ...
|
||||
res = user_permission_list(full=True)['permissions']
|
||||
assert res['permissions_app.main']['urls'] == [maindomain + "/urlpermissionapp"]
|
||||
assert res['permissions_app.admin']['urls'] == [maindomain + "/urlpermissionapp/admin"]
|
||||
assert res['permissions_app.dev']['urls'] == [maindomain + "/urlpermissionapp/dev"]
|
||||
assert res['permissions_app.main']['url'] == "/"
|
||||
assert res['permissions_app.admin']['url'] == "/admin"
|
||||
assert res['permissions_app.dev']['url'] == "/dev"
|
||||
|
||||
app_change_url("permissions_app", maindomain, "/newchangeurl")
|
||||
|
||||
res = user_permission_list(full=True)['permissions']
|
||||
assert res['permissions_app.main']['urls'] == [maindomain + "/newchangeurl"]
|
||||
assert res['permissions_app.admin']['urls'] == [maindomain + "/newchangeurl/admin"]
|
||||
assert res['permissions_app.dev']['urls'] == [maindomain + "/newchangeurl/dev"]
|
||||
assert res['permissions_app.main']['url'] == "/"
|
||||
assert res['permissions_app.admin']['url'] == "/admin"
|
||||
assert res['permissions_app.dev']['url'] == "/dev"
|
||||
|
||||
|
||||
def test_permission_app_propagation_on_ssowat():
|
||||
|
||||
app_install("./tests/apps/permissions_app_ynh",
|
||||
args="domain=%s&path=%s&is_public=1&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True)
|
||||
|
||||
res = user_permission_list(full=True)['permissions']
|
||||
assert res['permissions_app.main']['allowed'] == ["visitors"]
|
||||
|
||||
app_webroot = "https://%s/urlpermissionapp" % maindomain
|
||||
assert can_access_webpage(app_webroot, logged_as=None)
|
||||
assert can_access_webpage(app_webroot, logged_as="alice")
|
||||
|
||||
user_permission_update("permissions_app.main", remove="visitors", add="bob")
|
||||
res = user_permission_list(full=True)['permissions']
|
||||
|
||||
assert not can_access_webpage(app_webroot, logged_as=None)
|
||||
assert not can_access_webpage(app_webroot, logged_as="alice")
|
||||
assert can_access_webpage(app_webroot, logged_as="bob")
|
||||
|
||||
# Test admin access, as configured during install, only alice should be able to access it
|
||||
|
||||
# alice gotta be allowed on the main permission to access the admin tho
|
||||
user_permission_update("permissions_app.main", remove="bob", add="all_users")
|
||||
|
||||
assert not can_access_webpage(app_webroot+"/admin", logged_as=None)
|
||||
assert can_access_webpage(app_webroot+"/admin", logged_as="alice")
|
||||
assert not can_access_webpage(app_webroot+"/admin", logged_as="bob")
|
||||
|
||||
def test_permission_legacy_app_propagation_on_ssowat():
|
||||
|
||||
app_install("./tests/apps/legacy_app_ynh",
|
||||
args="domain=%s&path=%s" % (maindomain, "/legacy"), force=True)
|
||||
|
||||
# App is configured as public by default using the legacy unprotected_uri mechanics
|
||||
# It should automatically be migrated during the install
|
||||
res = user_permission_list(full=True)['permissions']
|
||||
assert res['legacy_app.main']['allowed'] == ["visitors"]
|
||||
|
||||
app_webroot = "https://%s/legacy" % maindomain
|
||||
|
||||
assert can_access_webpage(app_webroot, logged_as=None)
|
||||
assert can_access_webpage(app_webroot, logged_as="alice")
|
||||
|
||||
# Try to update the permission and check that permissions are still consistent
|
||||
user_permission_update("legacy_app.main", remove="visitors", add="bob")
|
||||
|
||||
assert not can_access_webpage(app_webroot, logged_as=None)
|
||||
assert not can_access_webpage(app_webroot, logged_as="alice")
|
||||
assert can_access_webpage(app_webroot, logged_as="bob")
|
||||
|
|
|
@ -14,7 +14,7 @@ def clean_user_groups():
|
|||
user_delete(u)
|
||||
|
||||
for g in user_group_list()['groups']:
|
||||
if g != "all_users":
|
||||
if g not in ["all_users", "visitors"]:
|
||||
user_group_delete(g)
|
||||
|
||||
def setup_function(function):
|
||||
|
|
|
@ -265,7 +265,12 @@ def user_delete(operation_logger, username, purge=False):
|
|||
# remove the member from the group
|
||||
if username != group and username in infos["members"]:
|
||||
user_group_update(group, remove=username, sync_perm=False)
|
||||
user_group_delete(username, force=True, sync_perm=True)
|
||||
|
||||
# Delete primary group if it exists (why wouldnt it exists ? because some
|
||||
# epic bug happened somewhere else and only a partial removal was
|
||||
# performed...)
|
||||
if username in user_group_list()['groups'].keys():
|
||||
user_group_delete(username, force=True, sync_perm=True)
|
||||
|
||||
ldap = _get_ldap_interface()
|
||||
try:
|
||||
|
@ -632,7 +637,7 @@ def user_group_delete(operation_logger, groupname, force=False, sync_perm=True):
|
|||
#
|
||||
# We also can't delete "all_users" because that's a special group...
|
||||
existing_users = user_list()['users'].keys()
|
||||
undeletable_groups = existing_users + ["all_users", "admins"]
|
||||
undeletable_groups = existing_users + ["all_users", "visitors"]
|
||||
if groupname in undeletable_groups and not force:
|
||||
raise YunohostError('group_cannot_be_deleted', group=groupname)
|
||||
|
||||
|
@ -667,13 +672,18 @@ def user_group_update(operation_logger, groupname, add=None, remove=None, force=
|
|||
from yunohost.permission import permission_sync_to_user
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
|
||||
existing_users = user_list()['users'].keys()
|
||||
|
||||
# Refuse to edit a primary group of a user (e.g. group 'sam' related to user 'sam')
|
||||
# Those kind of group should only ever contain the user (e.g. sam) and only this one.
|
||||
# We also can't edit "all_users" without the force option because that's a special group...
|
||||
existing_users = user_list()['users'].keys()
|
||||
uneditable_groups = existing_users + ["all_users", "admins"]
|
||||
if groupname in uneditable_groups and not force:
|
||||
raise YunohostError('group_cannot_be_edited', group=groupname)
|
||||
if not force:
|
||||
if groupname == "all_users":
|
||||
raise YunohostError('group_cannot_edit_all_users')
|
||||
elif groupname == "visitors":
|
||||
raise YunohostError('group_cannot_edit_visitors')
|
||||
elif groupname in existing_users:
|
||||
raise YunohostError('group_cannot_edit_primary_group', group=groupname)
|
||||
|
||||
# We extract the uid for each member of the group to keep a simple flat list of members
|
||||
current_group = user_group_info(groupname)["members"]
|
||||
|
|
Loading…
Add table
Reference in a new issue