Merge pull request #1203 from YunoHost/smarter-migration-during-restore

Drop support for backups from prior 3.8, introduce hooks in migrations to apply migrations during restore
This commit is contained in:
Alexandre Aubin 2021-04-05 16:52:26 +02:00 committed by GitHub
commit 5db621bd19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 142 additions and 264 deletions

View file

@ -420,12 +420,7 @@
"migration_description_0017_postgresql_9p6_to_11": "Migrate databases from PostgreSQL 9.6 to 11", "migration_description_0017_postgresql_9p6_to_11": "Migrate databases from PostgreSQL 9.6 to 11",
"migration_description_0018_xtable_to_nftable": "Migrate old network traffic rules to the new nftable system", "migration_description_0018_xtable_to_nftable": "Migrate old network traffic rules to the new nftable system",
"migration_description_0019_extend_permissions_features": "Extend/rework the app permission management system", "migration_description_0019_extend_permissions_features": "Extend/rework the app permission management system",
"migration_0011_create_group": "Creating a group for each user...", "migration_update_LDAP_schema": "Updating LDAP schema...",
"migration_0011_LDAP_update_failed": "Unable to update LDAP. Error: {error:s}",
"migration_0011_migrate_permission": "Migrating permissions from apps settings to LDAP...",
"migration_0011_update_LDAP_database": "Updating LDAP database...",
"migration_0011_update_LDAP_schema": "Updating LDAP schema...",
"migration_0011_failed_to_remove_stale_object": "Unable to remove stale object {dn}: {error}",
"migration_0015_start" : "Starting migration to Buster", "migration_0015_start" : "Starting migration to Buster",
"migration_0015_patching_sources_list": "Patching the sources.lists...", "migration_0015_patching_sources_list": "Patching the sources.lists...",
"migration_0015_main_upgrade": "Starting main upgrade...", "migration_0015_main_upgrade": "Starting main upgrade...",
@ -529,6 +524,7 @@
"restore_already_installed_app": "An app with the ID '{app:s}' is already installed", "restore_already_installed_app": "An app with the ID '{app:s}' is already installed",
"restore_already_installed_apps": "The following apps can't be restored because they are already installed: {apps}", "restore_already_installed_apps": "The following apps can't be restored because they are already installed: {apps}",
"restore_app_failed": "Could not restore {app:s}", "restore_app_failed": "Could not restore {app:s}",
"restore_backup_too_old": "This backup archive can not be restored because it comes from a too-old YunoHost version.",
"restore_cleaning_failed": "Could not clean up the temporary restoration directory", "restore_cleaning_failed": "Could not clean up the temporary restoration directory",
"restore_complete": "Restoration completed", "restore_complete": "Restoration completed",
"restore_confirm_yunohost_installed": "Do you really want to restore an already installed system? [{answers:s}]", "restore_confirm_yunohost_installed": "Do you really want to restore an already installed system? [{answers:s}]",

View file

@ -36,6 +36,7 @@ from datetime import datetime
from glob import glob from glob import glob
from collections import OrderedDict from collections import OrderedDict
from functools import reduce from functools import reduce
from packaging import version
from moulinette import msignals, m18n, msettings from moulinette import msignals, m18n, msettings
from moulinette.utils import filesystem from moulinette.utils import filesystem
@ -60,7 +61,7 @@ from yunohost.hook import (
hook_exec, hook_exec,
CUSTOM_HOOK_FOLDER, CUSTOM_HOOK_FOLDER,
) )
from yunohost.tools import tools_postinstall from yunohost.tools import tools_postinstall, _tools_migrations_run_after_system_restore, _tools_migrations_run_before_app_restore
from yunohost.regenconf import regen_conf from yunohost.regenconf import regen_conf
from yunohost.log import OperationLogger from yunohost.log import OperationLogger
from yunohost.utils.error import YunohostError, YunohostValidationError from yunohost.utils.error import YunohostError, YunohostValidationError
@ -858,6 +859,9 @@ class RestoreManager:
# FIXME this way to get the info is not compatible with copy or custom # FIXME this way to get the info is not compatible with copy or custom
# backup methods # backup methods
self.info = backup_info(name, with_details=True) self.info = backup_info(name, with_details=True)
if not self.info["from_yunohost_version"] or version.parse(self.info["from_yunohost_version"]) < version.parse("3.8.0"):
raise YunohostValidationError("restore_backup_too_old")
self.archive_path = self.info["path"] self.archive_path = self.info["path"]
self.name = name self.name = name
self.method = BackupMethod.create(method, self) self.method = BackupMethod.create(method, self)
@ -1215,7 +1219,6 @@ class RestoreManager:
if system_targets == []: if system_targets == []:
return return
from yunohost.user import user_group_list
from yunohost.permission import ( from yunohost.permission import (
permission_create, permission_create,
permission_delete, permission_delete,
@ -1278,25 +1281,15 @@ class RestoreManager:
regen_conf() regen_conf()
# Check that at least a group exists (all_users) to know if we need to _tools_migrations_run_after_system_restore(backup_version=self.info["from_yunohost_version"])
# do the migration 0011 : setup group and permission
#
# Legacy code
if "all_users" not in user_group_list()["groups"].keys():
from yunohost.utils.legacy import SetupGroupPermissions
# Update LDAP schema restart slapd # Remove all permission for all app still in the LDAP
logger.info(m18n.n("migration_0011_update_LDAP_schema"))
regen_conf(names=["slapd"], force=True)
SetupGroupPermissions.migrate_LDAP_db()
# Remove all permission for all app which is still in the LDAP
for permission_name in user_permission_list(ignore_system_perms=True)[ for permission_name in user_permission_list(ignore_system_perms=True)[
"permissions" "permissions"
].keys(): ].keys():
permission_delete(permission_name, force=True, sync_perm=False) permission_delete(permission_name, force=True, sync_perm=False)
# Restore permission for the app which is installed # Restore permission for apps installed
for permission_name, permission_infos in old_apps_permission.items(): for permission_name, permission_infos in old_apps_permission.items():
app_name, perm_name = permission_name.split(".") app_name, perm_name = permission_name.split(".")
if _is_installed(app_name): if _is_installed(app_name):
@ -1347,7 +1340,6 @@ class RestoreManager:
name should be already install) name should be already install)
""" """
from yunohost.user import user_group_list from yunohost.user import user_group_list
from yunohost.app import app_setting
from yunohost.permission import ( from yunohost.permission import (
permission_create, permission_create,
permission_delete, permission_delete,
@ -1421,67 +1413,47 @@ class RestoreManager:
restore_script = os.path.join(tmp_folder_for_app_restore, "restore") restore_script = os.path.join(tmp_folder_for_app_restore, "restore")
# Restore permissions # Restore permissions
if os.path.isfile("%s/permissions.yml" % app_settings_new_path): if not os.path.isfile("%s/permissions.yml" % app_settings_new_path):
raise YunohostError("Didnt find a permssions.yml for the app !?", raw_msg=True)
permissions = read_yaml("%s/permissions.yml" % app_settings_new_path) permissions = read_yaml("%s/permissions.yml" % app_settings_new_path)
existing_groups = user_group_list()["groups"] existing_groups = user_group_list()["groups"]
for permission_name, permission_infos in permissions.items(): for permission_name, permission_infos in permissions.items():
if "allowed" not in permission_infos: if "allowed" not in permission_infos:
logger.warning( logger.warning(
"'allowed' key corresponding to allowed groups for permission %s not found when restoring app %s … You might have to reconfigure permissions yourself." "'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) % (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
]
perm_name = permission_name.split(".")[1]
permission_create(
permission_name,
allowed=should_be_allowed,
url=permission_infos.get("url"),
additional_urls=permission_infos.get("additional_urls"),
auth_header=permission_infos.get("auth_header"),
label=permission_infos.get("label")
if perm_name == "main"
else permission_infos.get("sublabel"),
show_tile=permission_infos.get("show_tile", True),
protected=permission_infos.get("protected", False),
sync_perm=False,
) )
should_be_allowed = ["all_users"]
else:
should_be_allowed = [
g
for g in permission_infos["allowed"]
if g in existing_groups
]
permission_sync_to_user() perm_name = permission_name.split(".")[1]
permission_create(
permission_name,
allowed=should_be_allowed,
url=permission_infos.get("url"),
additional_urls=permission_infos.get("additional_urls"),
auth_header=permission_infos.get("auth_header"),
label=permission_infos.get("label")
if perm_name == "main"
else permission_infos.get("sublabel"),
show_tile=permission_infos.get("show_tile", True),
protected=permission_infos.get("protected", False),
sync_perm=False,
)
os.remove("%s/permissions.yml" % app_settings_new_path) permission_sync_to_user()
else:
# Otherwise, we need to migrate the legacy permissions of this
# app (included in its settings.yml)
from yunohost.utils.legacy import SetupGroupPermissions
SetupGroupPermissions.migrate_app_permission(app=app_instance_name) os.remove("%s/permissions.yml" % app_settings_new_path)
# Migrate old settings _tools_migrations_run_before_app_restore(backup_version=self.info["from_yunohost_version"], app_id=app_instance_name)
legacy_permission_settings = [
"skipped_uris",
"unprotected_uris",
"protected_uris",
"skipped_regex",
"unprotected_regex",
"protected_regex",
]
if any(
app_setting(app_instance_name, setting) is not None
for setting in legacy_permission_settings
):
from yunohost.utils.legacy import migrate_legacy_permission_settings
migrate_legacy_permission_settings(app=app_instance_name)
# Prepare env. var. to pass to script # Prepare env. var. to pass to script
env_dict = _make_environment_for_app_script(app_instance_name) env_dict = _make_environment_for_app_script(app_instance_name)
@ -2446,7 +2418,7 @@ def backup_info(name, with_details=False, human_readable=False):
try: try:
files_in_archive = tar.getnames() files_in_archive = tar.getnames()
except IOError as e: except (IOError, EOFError) as e:
raise YunohostError( raise YunohostError(
"backup_archive_corrupted", archive=archive_file, error=str(e) "backup_archive_corrupted", archive=archive_file, error=str(e)
) )
@ -2530,6 +2502,7 @@ def backup_info(name, with_details=False, human_readable=False):
result["apps"] = info["apps"] result["apps"] = info["apps"]
result["system"] = info[system_key] result["system"] = info[system_key]
result["from_yunohost_version"] = info.get("from_yunohost_version")
return result return result
@ -2559,6 +2532,8 @@ def backup_delete(name):
files_to_delete.append(actual_archive) files_to_delete.append(actual_archive)
for backup_file in files_to_delete: for backup_file in files_to_delete:
if not os.path.exists(backup_file):
continue
try: try:
os.remove(backup_file) os.remove(backup_file)
except Exception: except Exception:

View file

@ -36,7 +36,7 @@ class MyMigration(Migration):
) )
# Update LDAP schema restart slapd # Update LDAP schema restart slapd
logger.info(m18n.n("migration_0011_update_LDAP_schema")) logger.info(m18n.n("migration_update_LDAP_schema"))
regen_conf(names=["slapd"], force=True) regen_conf(names=["slapd"], force=True)
logger.info(m18n.n("migration_0019_add_new_attributes_in_ldap")) logger.info(m18n.n("migration_0019_add_new_attributes_in_ldap"))
@ -78,6 +78,32 @@ class MyMigration(Migration):
ldap.update("cn=%s,ou=permission" % permission, update) ldap.update("cn=%s,ou=permission" % permission, update)
introduced_in_version = "4.1"
def run_after_system_restore(self):
# Update LDAP database
self.add_new_ldap_attributes()
def run_before_system_restore(self, app_id):
from yunohost.app import app_setting
from yunohost.utils.legacy import migrate_legacy_permission_settings
# Migrate old settings
legacy_permission_settings = [
"skipped_uris",
"unprotected_uris",
"protected_uris",
"skipped_regex",
"unprotected_regex",
"protected_regex",
]
if any(
app_setting(app_id, setting) is not None
for setting in legacy_permission_settings
):
migrate_legacy_permission_settings(app=app_id)
def run(self): def run(self):
# FIXME : what do we really want to do here ... # FIXME : what do we really want to do here ...

View file

@ -47,8 +47,8 @@ def setup_function(function):
for m in function.__dict__.get("pytestmark", []) for m in function.__dict__.get("pytestmark", [])
} }
if "with_wordpress_archive_from_2p4" in markers: if "with_wordpress_archive_from_3p8" in markers:
add_archive_wordpress_from_2p4() add_archive_wordpress_from_3p8()
assert len(backup_list()["archives"]) == 1 assert len(backup_list()["archives"]) == 1
if "with_legacy_app_installed" in markers: if "with_legacy_app_installed" in markers:
@ -70,8 +70,8 @@ def setup_function(function):
) )
assert app_is_installed("backup_recommended_app") assert app_is_installed("backup_recommended_app")
if "with_system_archive_from_2p4" in markers: if "with_system_archive_from_3p8" in markers:
add_archive_system_from_2p4() add_archive_system_from_3p8()
assert len(backup_list()["archives"]) == 1 assert len(backup_list()["archives"]) == 1
if "with_permission_app_installed" in markers: if "with_permission_app_installed" in markers:
@ -107,7 +107,8 @@ def teardown_function(function):
if "with_custom_domain" in markers: if "with_custom_domain" in markers:
domain = markers["with_custom_domain"]["args"][0] domain = markers["with_custom_domain"]["args"][0]
domain_remove(domain) if domain != maindomain:
domain_remove(domain)
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
@ -147,7 +148,7 @@ def backup_test_dependencies_are_met():
# Dummy test apps (or backup archives) # Dummy test apps (or backup archives)
assert os.path.exists( assert os.path.exists(
os.path.join(get_test_apps_dir(), "backup_wordpress_from_2p4") os.path.join(get_test_apps_dir(), "backup_wordpress_from_3p8")
) )
assert os.path.exists(os.path.join(get_test_apps_dir(), "legacy_app_ynh")) assert os.path.exists(os.path.join(get_test_apps_dir(), "legacy_app_ynh"))
assert os.path.exists( assert os.path.exists(
@ -216,39 +217,25 @@ def install_app(app, path, additionnal_args=""):
) )
def add_archive_wordpress_from_2p4(): def add_archive_wordpress_from_3p8():
os.system("mkdir -p /home/yunohost.backup/archives") os.system("mkdir -p /home/yunohost.backup/archives")
os.system( os.system(
"cp " "cp "
+ os.path.join( + os.path.join(get_test_apps_dir(), "backup_wordpress_from_3p8/backup.tar.gz")
get_test_apps_dir(), "backup_wordpress_from_2p4/backup.info.json" + " /home/yunohost.backup/archives/backup_wordpress_from_3p8.tar.gz"
)
+ " /home/yunohost.backup/archives/backup_wordpress_from_2p4.info.json"
)
os.system(
"cp "
+ os.path.join(get_test_apps_dir(), "backup_wordpress_from_2p4/backup.tar.gz")
+ " /home/yunohost.backup/archives/backup_wordpress_from_2p4.tar.gz"
) )
def add_archive_system_from_2p4(): def add_archive_system_from_3p8():
os.system("mkdir -p /home/yunohost.backup/archives") os.system("mkdir -p /home/yunohost.backup/archives")
os.system( os.system(
"cp " "cp "
+ os.path.join(get_test_apps_dir(), "backup_system_from_2p4/backup.info.json") + os.path.join(get_test_apps_dir(), "backup_system_from_3p8/backup.tar.gz")
+ " /home/yunohost.backup/archives/backup_system_from_2p4.info.json" + " /home/yunohost.backup/archives/backup_system_from_3p8.tar.gz"
)
os.system(
"cp "
+ os.path.join(get_test_apps_dir(), "backup_system_from_2p4/backup.tar.gz")
+ " /home/yunohost.backup/archives/backup_system_from_2p4.tar.gz"
) )
@ -314,12 +301,12 @@ def test_backup_and_restore_all_sys(mocker):
# #
# System restore from 2.4 # # System restore from 3.8 #
# #
@pytest.mark.with_system_archive_from_2p4 @pytest.mark.with_system_archive_from_3p8
def test_restore_system_from_Ynh2p4(monkeypatch, mocker): def test_restore_system_from_Ynh3p8(monkeypatch, mocker):
# Backup current system # Backup current system
with message(mocker, "backup_created"): with message(mocker, "backup_created"):
@ -327,7 +314,7 @@ def test_restore_system_from_Ynh2p4(monkeypatch, mocker):
archives = backup_list()["archives"] archives = backup_list()["archives"]
assert len(archives) == 2 assert len(archives) == 2
# Restore system archive from 2.4 # Restore system archive from 3.8
try: try:
with message(mocker, "restore_complete"): with message(mocker, "restore_complete"):
backup_restore( backup_restore(
@ -464,9 +451,9 @@ def test_backup_using_copy_method(mocker):
# #
@pytest.mark.with_wordpress_archive_from_2p4 @pytest.mark.with_wordpress_archive_from_3p8
@pytest.mark.with_custom_domain("yolo.test") @pytest.mark.with_custom_domain("yolo.test")
def test_restore_app_wordpress_from_Ynh2p4(mocker): def test_restore_app_wordpress_from_Ynh3p8(mocker):
with message(mocker, "restore_complete"): with message(mocker, "restore_complete"):
backup_restore( backup_restore(
@ -474,7 +461,7 @@ def test_restore_app_wordpress_from_Ynh2p4(mocker):
) )
@pytest.mark.with_wordpress_archive_from_2p4 @pytest.mark.with_wordpress_archive_from_3p8
@pytest.mark.with_custom_domain("yolo.test") @pytest.mark.with_custom_domain("yolo.test")
def test_restore_app_script_failure_handling(monkeypatch, mocker): def test_restore_app_script_failure_handling(monkeypatch, mocker):
def custom_hook_exec(name, *args, **kwargs): def custom_hook_exec(name, *args, **kwargs):
@ -495,7 +482,7 @@ def test_restore_app_script_failure_handling(monkeypatch, mocker):
assert not _is_installed("wordpress") assert not _is_installed("wordpress")
@pytest.mark.with_wordpress_archive_from_2p4 @pytest.mark.with_wordpress_archive_from_3p8
def test_restore_app_not_enough_free_space(monkeypatch, mocker): def test_restore_app_not_enough_free_space(monkeypatch, mocker):
def custom_free_space_in_directory(dirpath): def custom_free_space_in_directory(dirpath):
return 0 return 0
@ -514,7 +501,7 @@ def test_restore_app_not_enough_free_space(monkeypatch, mocker):
assert not _is_installed("wordpress") assert not _is_installed("wordpress")
@pytest.mark.with_wordpress_archive_from_2p4 @pytest.mark.with_wordpress_archive_from_3p8
def test_restore_app_not_in_backup(mocker): def test_restore_app_not_in_backup(mocker):
assert not _is_installed("wordpress") assert not _is_installed("wordpress")
@ -530,7 +517,7 @@ def test_restore_app_not_in_backup(mocker):
assert not _is_installed("yoloswag") assert not _is_installed("yoloswag")
@pytest.mark.with_wordpress_archive_from_2p4 @pytest.mark.with_wordpress_archive_from_3p8
@pytest.mark.with_custom_domain("yolo.test") @pytest.mark.with_custom_domain("yolo.test")
def test_restore_app_already_installed(mocker): def test_restore_app_already_installed(mocker):
@ -648,18 +635,18 @@ def test_restore_archive_with_no_json(mocker):
backup_restore(name="badbackup", force=True) backup_restore(name="badbackup", force=True)
@pytest.mark.with_wordpress_archive_from_2p4 @pytest.mark.with_wordpress_archive_from_3p8
def test_restore_archive_with_bad_archive(mocker): def test_restore_archive_with_bad_archive(mocker):
# Break the archive # Break the archive
os.system( os.system(
"head -n 1000 /home/yunohost.backup/archives/backup_wordpress_from_2p4.tar.gz > /home/yunohost.backup/archives/backup_wordpress_from_2p4.tar.gz" "head -n 1000 /home/yunohost.backup/archives/backup_wordpress_from_3p8.tar.gz > /home/yunohost.backup/archives/backup_wordpress_from_3p8_bad.tar.gz"
) )
assert "backup_wordpress_from_2p4" in backup_list()["archives"] assert "backup_wordpress_from_3p8_bad" in backup_list()["archives"]
with raiseYunohostError(mocker, "backup_archive_open_failed"): with raiseYunohostError(mocker, "backup_archive_corrupted"):
backup_restore(name="backup_wordpress_from_2p4", force=True) backup_restore(name="backup_wordpress_from_3p8_bad", force=True)
clean_tmp_backup_directory() clean_tmp_backup_directory()

View file

@ -29,6 +29,7 @@ import yaml
import subprocess import subprocess
import pwd import pwd
from importlib import import_module from importlib import import_module
from packaging import version
from moulinette import msignals, m18n from moulinette import msignals, m18n
from moulinette.utils.log import getActionLogger from moulinette.utils.log import getActionLogger
@ -1101,6 +1102,44 @@ def _skip_all_migrations():
write_to_yaml(MIGRATIONS_STATE_PATH, new_states) write_to_yaml(MIGRATIONS_STATE_PATH, new_states)
def _tools_migrations_run_after_system_restore(backup_version):
all_migrations = _get_migrations_list()
for migration in all_migrations:
if hasattr(migration, "introduced_in_version") \
and version.parse(migration.introduced_in_version) > version.parse(backup_version) \
and hasattr(migration, "run_after_system_restore"):
try:
logger.info(m18n.n("migrations_running_forward", id=migration.id))
migration.run_after_system_restore()
except Exception as e:
msg = m18n.n(
"migrations_migration_has_failed", exception=e, id=migration.id
)
logger.error(msg, exc_info=1)
raise
def _tools_migrations_run_before_app_restore(backup_version, app_id):
all_migrations = _get_migrations_list()
for migration in all_migrations:
if hasattr(migration, "introduced_in_version") \
and version.parse(migration.introduced_in_version) > version.parse(backup_version) \
and hasattr(migration, "run_before_app_restore"):
try:
logger.info(m18n.n("migrations_running_forward", id=migration.id))
migration.run_before_app_restore(app_id)
except Exception as e:
msg = m18n.n(
"migrations_migration_has_failed", exception=e, id=migration.id
)
logger.error(msg, exc_info=1)
raise
class Migration(object): class Migration(object):
# Those are to be implemented by daughter classes # Those are to be implemented by daughter classes

View file

@ -1,12 +1,10 @@
import os import os
from moulinette import m18n from moulinette import m18n
from yunohost.utils.error import YunohostError
from moulinette.utils.log import getActionLogger from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import write_to_json, read_yaml from moulinette.utils.filesystem import write_to_json, read_yaml
from yunohost.user import user_list, user_group_create, user_group_update from yunohost.user import user_list
from yunohost.app import ( from yunohost.app import (
app_setting,
_installed_apps, _installed_apps,
_get_app_settings, _get_app_settings,
_set_app_settings, _set_app_settings,
@ -19,149 +17,6 @@ from yunohost.permission import (
logger = getActionLogger("yunohost.legacy") logger = getActionLogger("yunohost.legacy")
class SetupGroupPermissions:
@staticmethod
def remove_if_exists(target):
from yunohost.utils.ldap import _get_ldap_interface
ldap = _get_ldap_interface()
try:
objects = ldap.search(target + ",dc=yunohost,dc=org")
# ldap search will raise an exception if no corresponding object is found >.> ...
except Exception:
logger.debug("%s does not exist, no need to delete it" % target)
return
objects.reverse()
for o in objects:
for dn in o["dn"]:
dn = dn.replace(",dc=yunohost,dc=org", "")
logger.debug("Deleting old object %s ..." % dn)
try:
ldap.remove(dn)
except Exception as e:
raise YunohostError(
"migration_0011_failed_to_remove_stale_object", dn=dn, error=e
)
@staticmethod
def migrate_LDAP_db():
logger.info(m18n.n("migration_0011_update_LDAP_database"))
from yunohost.utils.ldap import _get_ldap_interface
ldap = _get_ldap_interface()
ldap_map = read_yaml(
"/usr/share/yunohost/yunohost-config/moulinette/ldap_scheme.yml"
)
try:
SetupGroupPermissions.remove_if_exists("ou=permission")
SetupGroupPermissions.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:
raise YunohostError("migration_0011_LDAP_update_failed", error=e)
logger.info(m18n.n("migration_0011_create_group"))
# Create a group for each yunohost user
user_list = ldap.search(
"ou=users,dc=yunohost,dc=org",
"(&(objectclass=person)(!(uid=root))(!(uid=nobody)))",
["uid", "uidNumber"],
)
for user_info in user_list:
username = user_info["uid"][0]
ldap.update(
"uid=%s,ou=users" % username,
{
"objectClass": [
"mailAccount",
"inetOrgPerson",
"posixAccount",
"userPermissionYnh",
]
},
)
user_group_create(
username,
gid=user_info["uidNumber"][0],
primary_group=True,
sync_perm=False,
)
user_group_update(
groupname="all_users", add=username, force=True, sync_perm=False
)
@staticmethod
def migrate_app_permission(app=None):
logger.info(m18n.n("migration_0011_migrate_permission"))
apps = _installed_apps()
if app:
if app not in apps:
logger.error(
"Can't migrate permission for app %s because it ain't installed..."
% app
)
apps = []
else:
apps = [app]
for app in apps:
permission = app_setting(app, "allowed_users")
path = app_setting(app, "path")
domain = app_setting(app, "domain")
url = "/" if domain and path else None
if permission:
known_users = list(user_list()["users"].keys())
allowed = [
user for user in permission.split(",") if user in known_users
]
else:
allowed = ["all_users"]
permission_create(
app + ".main",
url=url,
allowed=allowed,
show_tile=True,
protected=False,
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") == "/"
or app_setting(app, "skipped_uris") == "/"
):
user_permission_update(app + ".main", add="visitors", sync_perm=False)
permission_sync_to_user()
LEGACY_PERMISSION_LABEL = { LEGACY_PERMISSION_LABEL = {
("nextcloud", "skipped"): "api", # .well-known ("nextcloud", "skipped"): "api", # .well-known
("libreto", "skipped"): "pad access", # /[^/]+ ("libreto", "skipped"): "pad access", # /[^/]+