mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
app/permissions: Move permissions data out of LDAP
This commit is contained in:
parent
54fd311bec
commit
c5580a0551
5 changed files with 153 additions and 91 deletions
|
@ -68,28 +68,16 @@ groupPermission: cn=all_users,ou=groups,dc=yunohost,dc=org
|
||||||
cn: mail.main
|
cn: mail.main
|
||||||
objectClass: posixGroup
|
objectClass: posixGroup
|
||||||
objectClass: permissionYnh
|
objectClass: permissionYnh
|
||||||
isProtected: TRUE
|
|
||||||
label: E-mail
|
|
||||||
gidNumber: 5001
|
gidNumber: 5001
|
||||||
showTile: FALSE
|
|
||||||
authHeader: FALSE
|
|
||||||
|
|
||||||
dn: cn=ssh.main,ou=permission,dc=yunohost,dc=org
|
dn: cn=ssh.main,ou=permission,dc=yunohost,dc=org
|
||||||
cn: ssh.main
|
cn: ssh.main
|
||||||
objectClass: posixGroup
|
objectClass: posixGroup
|
||||||
objectClass: permissionYnh
|
objectClass: permissionYnh
|
||||||
isProtected: TRUE
|
|
||||||
label: SSH
|
|
||||||
gidNumber: 5003
|
gidNumber: 5003
|
||||||
showTile: FALSE
|
|
||||||
authHeader: FALSE
|
|
||||||
|
|
||||||
dn: cn=sftp.main,ou=permission,dc=yunohost,dc=org
|
dn: cn=sftp.main,ou=permission,dc=yunohost,dc=org
|
||||||
cn: sftp.main
|
cn: sftp.main
|
||||||
objectClass: posixGroup
|
objectClass: posixGroup
|
||||||
objectClass: permissionYnh
|
objectClass: permissionYnh
|
||||||
isProtected: TRUE
|
|
||||||
label: SFTP
|
|
||||||
gidNumber: 5004
|
gidNumber: 5004
|
||||||
showTile: FALSE
|
|
||||||
authHeader: FALSE
|
|
||||||
|
|
|
@ -14,24 +14,6 @@ olcAttributeTypes: ( 1.3.6.1.4.1.17953.9.1.2 NAME 'groupPermission'
|
||||||
olcAttributeTypes: ( 1.3.6.1.4.1.17953.9.1.3 NAME 'inheritPermission'
|
olcAttributeTypes: ( 1.3.6.1.4.1.17953.9.1.3 NAME 'inheritPermission'
|
||||||
DESC 'YunoHost permission for user on permission side'
|
DESC 'YunoHost permission for user on permission side'
|
||||||
SUP distinguishedName )
|
SUP distinguishedName )
|
||||||
olcAttributeTypes: ( 1.3.6.1.4.1.17953.9.1.4 NAME 'URL'
|
|
||||||
DESC 'YunoHost permission main URL'
|
|
||||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} SINGLE-VALUE )
|
|
||||||
olcAttributeTypes: ( 1.3.6.1.4.1.17953.9.1.5 NAME 'additionalUrls'
|
|
||||||
DESC 'YunoHost permission additionnal URL'
|
|
||||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
|
|
||||||
olcAttributeTypes: ( 1.3.6.1.4.1.17953.9.1.6 NAME 'authHeader'
|
|
||||||
DESC 'YunoHost application, enable authentication header'
|
|
||||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
|
|
||||||
olcAttributeTypes: ( 1.3.6.1.4.1.17953.9.1.7 NAME 'label'
|
|
||||||
DESC 'YunoHost permission label, also used for the tile name in the SSO'
|
|
||||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} SINGLE-VALUE )
|
|
||||||
olcAttributeTypes: ( 1.3.6.1.4.1.17953.9.1.8 NAME 'showTile'
|
|
||||||
DESC 'YunoHost application, show/hide the tile in the SSO for this permission'
|
|
||||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
|
|
||||||
olcAttributeTypes: ( 1.3.6.1.4.1.17953.9.1.9 NAME 'isProtected'
|
|
||||||
DESC 'YunoHost application permission protection'
|
|
||||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
|
|
||||||
# OBJECTCLASS
|
# OBJECTCLASS
|
||||||
# For Applications
|
# For Applications
|
||||||
olcObjectClasses: ( 1.3.6.1.4.1.17953.9.2.1 NAME 'groupOfNamesYnh'
|
olcObjectClasses: ( 1.3.6.1.4.1.17953.9.2.1 NAME 'groupOfNamesYnh'
|
||||||
|
@ -41,8 +23,8 @@ olcObjectClasses: ( 1.3.6.1.4.1.17953.9.2.1 NAME 'groupOfNamesYnh'
|
||||||
olcObjectClasses: ( 1.3.6.1.4.1.17953.9.2.2 NAME 'permissionYnh'
|
olcObjectClasses: ( 1.3.6.1.4.1.17953.9.2.2 NAME 'permissionYnh'
|
||||||
DESC 'a YunoHost application'
|
DESC 'a YunoHost application'
|
||||||
SUP top AUXILIARY
|
SUP top AUXILIARY
|
||||||
MUST ( cn $ authHeader $ label $ showTile $ isProtected )
|
MUST ( cn )
|
||||||
MAY ( groupPermission $ inheritPermission $ URL $ additionalUrls ) )
|
MAY ( groupPermission $ inheritPermission ) )
|
||||||
# For User
|
# For User
|
||||||
olcObjectClasses: ( 1.3.6.1.4.1.17953.9.2.3 NAME 'userPermissionYnh'
|
olcObjectClasses: ( 1.3.6.1.4.1.17953.9.2.3 NAME 'userPermissionYnh'
|
||||||
DESC 'a YunoHost application'
|
DESC 'a YunoHost application'
|
||||||
|
|
74
src/migrations/0031_rework_permission_infos.py
Normal file
74
src/migrations/0031_rework_permission_infos.py
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
from logging import getLogger
|
||||||
|
|
||||||
|
from yunohost.tools import Migration
|
||||||
|
from yunohost.permission import permission_sync_to_user
|
||||||
|
from yunohost.app import app_setting
|
||||||
|
|
||||||
|
logger = getLogger("yunohost.migration")
|
||||||
|
|
||||||
|
###################################################
|
||||||
|
# Tools used also for restoration
|
||||||
|
###################################################
|
||||||
|
|
||||||
|
|
||||||
|
class MyMigration(Migration):
|
||||||
|
|
||||||
|
introduced_in_version = "12.0" # FIXME ?
|
||||||
|
dependencies = []
|
||||||
|
|
||||||
|
ldap_migration_started = False
|
||||||
|
|
||||||
|
@Migration.ldap_migration
|
||||||
|
def run(self, *args):
|
||||||
|
|
||||||
|
self.ldap_migration_started = True
|
||||||
|
|
||||||
|
permissions_per_app = self.read_legacy_permissions_per_app()
|
||||||
|
for app, permissions in permissions_per_app.items():
|
||||||
|
app_setting(app, "_permissions", permissions)
|
||||||
|
|
||||||
|
permission_sync_to_user()
|
||||||
|
|
||||||
|
def run_after_system_restore(self):
|
||||||
|
self.run()
|
||||||
|
|
||||||
|
def read_legacy_permissions_per_app(self):
|
||||||
|
|
||||||
|
from yunohost.utils.ldap import _get_ldap_interface
|
||||||
|
SYSTEM_PERMS = ["mail", "sftp", "ssh"]
|
||||||
|
|
||||||
|
ldap = _get_ldap_interface()
|
||||||
|
permissions_infos = ldap.search(
|
||||||
|
"ou=permission",
|
||||||
|
"(objectclass=permissionYnh)",
|
||||||
|
[
|
||||||
|
"cn",
|
||||||
|
"URL",
|
||||||
|
"additionalUrls",
|
||||||
|
"authHeader",
|
||||||
|
"label",
|
||||||
|
"showTile",
|
||||||
|
"isProtected",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
permissions_per_app = {}
|
||||||
|
for infos in permissions_infos:
|
||||||
|
app, name = infos["cn"][0].split(".")
|
||||||
|
|
||||||
|
if app in SYSTEM_PERMS:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if app not in permissions_per_app:
|
||||||
|
permissions_per_app[app] = {}
|
||||||
|
|
||||||
|
permissions_per_app[app][name] = {
|
||||||
|
"label": infos.get("label", [None])[0],
|
||||||
|
"show_tile": infos.get("showTile", [False])[0] == "TRUE",
|
||||||
|
"auth_header": infos.get("authHeader", [False])[0] == "TRUE",
|
||||||
|
"protected": infos.get("isProtected", [False])[0] == "TRUE",
|
||||||
|
"url": infos.get("URL", [None])[0],
|
||||||
|
"additional_urls": infos.get("additionalUrls", []),
|
||||||
|
}
|
||||||
|
|
||||||
|
return permissions_per_app
|
|
@ -49,19 +49,13 @@ def user_permission_list(
|
||||||
from yunohost.utils.ldap import _get_ldap_interface, _ldap_path_extract
|
from yunohost.utils.ldap import _get_ldap_interface, _ldap_path_extract
|
||||||
|
|
||||||
ldap = _get_ldap_interface()
|
ldap = _get_ldap_interface()
|
||||||
permissions_infos = ldap.search(
|
ldap_permissions_infos = ldap.search(
|
||||||
"ou=permission",
|
"ou=permission",
|
||||||
"(objectclass=permissionYnh)",
|
"(objectclass=permissionYnh)",
|
||||||
[
|
[
|
||||||
"cn",
|
"cn",
|
||||||
"groupPermission",
|
"groupPermission",
|
||||||
"inheritPermission",
|
"inheritPermission",
|
||||||
"URL",
|
|
||||||
"additionalUrls",
|
|
||||||
"authHeader",
|
|
||||||
"label",
|
|
||||||
"showTile",
|
|
||||||
"isProtected",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -78,9 +72,9 @@ def user_permission_list(
|
||||||
}
|
}
|
||||||
|
|
||||||
permissions = {}
|
permissions = {}
|
||||||
for infos in permissions_infos:
|
for infos in ldap_permissions_infos:
|
||||||
name = infos["cn"][0]
|
name = infos["cn"][0]
|
||||||
app = name.split(".")[0]
|
app, subperm = name.split(".")
|
||||||
|
|
||||||
if ignore_system_perms and app in SYSTEM_PERMS:
|
if ignore_system_perms and app in SYSTEM_PERMS:
|
||||||
continue
|
continue
|
||||||
|
@ -88,20 +82,19 @@ def user_permission_list(
|
||||||
continue
|
continue
|
||||||
|
|
||||||
perm = {}
|
perm = {}
|
||||||
perm["allowed"] = [
|
if full and app not in SYSTEM_PERMS:
|
||||||
_ldap_path_extract(p, "cn") for p in infos.get("groupPermission", [])
|
# Default stuff
|
||||||
]
|
perm = {
|
||||||
|
"url": None,
|
||||||
if full:
|
"additional_urls": [],
|
||||||
perm["corresponding_users"] = [
|
"auth_header": True,
|
||||||
_ldap_path_extract(p, "uid") for p in infos.get("inheritPermission", [])
|
"show_tile": None, # To be automagically set to True by default if an url is defined and show_tile not provided
|
||||||
]
|
"protected": False,
|
||||||
perm["auth_header"] = infos.get("authHeader", [False])[0] == "TRUE"
|
}
|
||||||
perm["label"] = infos.get("label", [None])[0]
|
perm_settings = (app_setting(app, "_permissions") or {}).get(subperm, {})
|
||||||
perm["show_tile"] = infos.get("showTile", [False])[0] == "TRUE"
|
perm.update(perm_settings)
|
||||||
perm["protected"] = infos.get("isProtected", [False])[0] == "TRUE"
|
if perm["show_tile"] is None and perm["url"] is not None:
|
||||||
perm["url"] = infos.get("URL", [None])[0]
|
perm["show_tile"] = True
|
||||||
perm["additional_urls"] = infos.get("additionalUrls", [])
|
|
||||||
|
|
||||||
if absolute_urls:
|
if absolute_urls:
|
||||||
app_base_path = (
|
app_base_path = (
|
||||||
|
@ -113,6 +106,14 @@ def user_permission_list(
|
||||||
for url in perm["additional_urls"]
|
for url in perm["additional_urls"]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
perm["allowed"] = [
|
||||||
|
_ldap_path_extract(p, "cn") for p in infos.get("groupPermission", [])
|
||||||
|
]
|
||||||
|
if full:
|
||||||
|
perm["corresponding_users"] = [
|
||||||
|
_ldap_path_extract(p, "uid") for p in infos.get("inheritPermission", [])
|
||||||
|
]
|
||||||
|
|
||||||
permissions[name] = perm
|
permissions[name] = perm
|
||||||
|
|
||||||
# Make sure labels for sub-permissions are the form " Applabel (Sublabel) "
|
# Make sure labels for sub-permissions are the form " Applabel (Sublabel) "
|
||||||
|
@ -414,16 +415,6 @@ def permission_create(
|
||||||
"objectClass": ["top", "permissionYnh", "posixGroup"],
|
"objectClass": ["top", "permissionYnh", "posixGroup"],
|
||||||
"cn": str(permission),
|
"cn": str(permission),
|
||||||
"gidNumber": gid,
|
"gidNumber": gid,
|
||||||
"authHeader": ["TRUE"],
|
|
||||||
"label": [
|
|
||||||
str(label) if label else (subperm if subperm != "main" else app.title())
|
|
||||||
],
|
|
||||||
"showTile": [
|
|
||||||
"FALSE"
|
|
||||||
], # Dummy value, it will be fixed when we call '_update_ldap_group_permission'
|
|
||||||
"isProtected": [
|
|
||||||
"FALSE"
|
|
||||||
], # Dummy value, it will be fixed when we call '_update_ldap_group_permission'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if allowed is not None:
|
if allowed is not None:
|
||||||
|
@ -446,6 +437,8 @@ def permission_create(
|
||||||
"permission_creation_failed", permission=permission, error=e
|
"permission_creation_failed", permission=permission, error=e
|
||||||
)
|
)
|
||||||
|
|
||||||
|
label = str(label) if label else (subperm if subperm != "main" else app.title())
|
||||||
|
|
||||||
try:
|
try:
|
||||||
permission_url(
|
permission_url(
|
||||||
permission,
|
permission,
|
||||||
|
@ -463,6 +456,7 @@ def permission_create(
|
||||||
protected=protected,
|
protected=protected,
|
||||||
sync_perm=sync_perm,
|
sync_perm=sync_perm,
|
||||||
)
|
)
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
permission_delete(permission, force=True)
|
permission_delete(permission, force=True)
|
||||||
raise
|
raise
|
||||||
|
@ -496,15 +490,15 @@ def permission_url(
|
||||||
clear_urls -- (optional) Clean all urls (url and additional_urls)
|
clear_urls -- (optional) Clean all urls (url and additional_urls)
|
||||||
"""
|
"""
|
||||||
from yunohost.app import app_setting
|
from yunohost.app import app_setting
|
||||||
from yunohost.utils.ldap import _get_ldap_interface
|
|
||||||
|
|
||||||
ldap = _get_ldap_interface()
|
|
||||||
|
|
||||||
# By default, manipulate main permission
|
# By default, manipulate main permission
|
||||||
if "." not in permission:
|
if "." not in permission:
|
||||||
permission = permission + ".main"
|
permission = permission + ".main"
|
||||||
|
|
||||||
app = permission.split(".")[0]
|
app, sub_permission = permission.split(".")
|
||||||
|
|
||||||
|
if app in SYSTEM_PERMS:
|
||||||
|
logger.warning(f"Cannot change urls / auth_header for system perm {permission}")
|
||||||
|
|
||||||
if url or add_url:
|
if url or add_url:
|
||||||
domain = app_setting(app, "domain")
|
domain = app_setting(app, "domain")
|
||||||
|
@ -573,19 +567,20 @@ def permission_url(
|
||||||
|
|
||||||
# Actually commit the change
|
# Actually commit the change
|
||||||
|
|
||||||
operation_logger.related_to.append(("app", permission.split(".")[0]))
|
operation_logger.related_to.append(("app", app))
|
||||||
operation_logger.start()
|
operation_logger.start()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ldap.update(
|
perm_settings = app_setting(app, "_permissions") or {}
|
||||||
f"cn={permission},ou=permission",
|
if sub_permission not in perm_settings:
|
||||||
{
|
perm_settings[sub_permission] = {}
|
||||||
"URL": [url] if url is not None else [],
|
perm_settings[sub_permission].update({
|
||||||
"additionalUrls": new_additional_urls,
|
"url": url,
|
||||||
"authHeader": [str(auth_header).upper()],
|
"additional_urls": new_additional_urls,
|
||||||
"showTile": [str(show_tile).upper()],
|
"auth_header": auth_header,
|
||||||
},
|
"show_tile": show_tile,
|
||||||
)
|
})
|
||||||
|
app_setting(app, "_permissions", perm_settings)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise YunohostError("permission_update_failed", permission=permission, error=e)
|
raise YunohostError("permission_update_failed", permission=permission, error=e)
|
||||||
|
|
||||||
|
@ -714,31 +709,42 @@ def _update_ldap_group_permission(
|
||||||
- the 'allowed' list contains *existing* groups.
|
- the 'allowed' list contains *existing* groups.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from yunohost.app import app_setting
|
||||||
from yunohost.hook import hook_callback
|
from yunohost.hook import hook_callback
|
||||||
from yunohost.utils.ldap import _get_ldap_interface
|
from yunohost.utils.ldap import _get_ldap_interface
|
||||||
|
|
||||||
ldap = _get_ldap_interface()
|
ldap = _get_ldap_interface()
|
||||||
|
|
||||||
|
app, sub_permission = permission.split(".")
|
||||||
existing_permission = user_permission_info(permission)
|
existing_permission = user_permission_info(permission)
|
||||||
|
|
||||||
update = {}
|
update_ldap = {}
|
||||||
|
update_settings = {}
|
||||||
|
|
||||||
if allowed is not None:
|
if allowed is not None:
|
||||||
allowed = [allowed] if not isinstance(allowed, list) else allowed
|
allowed = [allowed] if not isinstance(allowed, list) else allowed
|
||||||
# Guarantee uniqueness of values in allowed, which would otherwise make ldap.update angry.
|
# Guarantee uniqueness of values in allowed, which would otherwise make ldap.update angry.
|
||||||
allowed = set(allowed)
|
allowed = set(allowed)
|
||||||
update["groupPermission"] = [
|
update_ldap["groupPermission"] = [
|
||||||
"cn=" + g + ",ou=groups,dc=yunohost,dc=org" for g in allowed
|
"cn=" + g + ",ou=groups,dc=yunohost,dc=org" for g in allowed
|
||||||
]
|
]
|
||||||
|
|
||||||
if label is not None:
|
if label is not None:
|
||||||
update["label"] = [str(label)]
|
if app in SYSTEM_PERMS:
|
||||||
|
logger.warning(f"Can't change 'label' for system permission {permission}")
|
||||||
|
else:
|
||||||
|
update_settings["label"] = str(label)
|
||||||
|
|
||||||
if protected is not None:
|
if protected is not None:
|
||||||
update["isProtected"] = [str(protected).upper()]
|
if app in SYSTEM_PERMS:
|
||||||
|
logger.warning(f"Can't change 'protected' for system permission {permission}")
|
||||||
|
else:
|
||||||
|
update_settings["protected"] = protected
|
||||||
|
|
||||||
if show_tile is not None:
|
if show_tile is not None:
|
||||||
if show_tile is True:
|
if app in SYSTEM_PERMS:
|
||||||
|
logger.warning(f"Can't change 'show_tile' for system permission {permission}")
|
||||||
|
elif show_tile is True:
|
||||||
if not existing_permission["url"]:
|
if not existing_permission["url"]:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
m18n.n(
|
m18n.n(
|
||||||
|
@ -746,16 +752,22 @@ def _update_ldap_group_permission(
|
||||||
permission=permission,
|
permission=permission,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
show_tile = False
|
update_settings["show_tile"] = False
|
||||||
elif existing_permission["url"].startswith("re:"):
|
elif existing_permission["url"].startswith("re:"):
|
||||||
logger.warning(
|
logger.warning(
|
||||||
m18n.n("show_tile_cant_be_enabled_for_regex", permission=permission)
|
m18n.n("show_tile_cant_be_enabled_for_regex", permission=permission)
|
||||||
)
|
)
|
||||||
show_tile = False
|
update_settings["show_tile"] = False
|
||||||
update["showTile"] = [str(show_tile).upper()]
|
|
||||||
|
if app not in SYSTEM_PERMS:
|
||||||
|
perm_settings = app_setting(app, "_permissions") or {}
|
||||||
|
if sub_permission not in perm_settings:
|
||||||
|
perm_settings[sub_permission] = {}
|
||||||
|
perm_settings[sub_permission].update(update_settings)
|
||||||
|
app_setting(app, "_permissions", perm_settings)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ldap.update(f"cn={permission},ou=permission", update)
|
ldap.update(f"cn={permission},ou=permission", update_ldap)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise YunohostError("permission_update_failed", permission=permission, error=e)
|
raise YunohostError("permission_update_failed", permission=permission, error=e)
|
||||||
|
|
||||||
|
@ -768,9 +780,6 @@ def _update_ldap_group_permission(
|
||||||
|
|
||||||
# Trigger app callbacks
|
# Trigger app callbacks
|
||||||
|
|
||||||
app = permission.split(".")[0]
|
|
||||||
sub_permission = permission.split(".")[1]
|
|
||||||
|
|
||||||
old_corresponding_users = set(existing_permission["corresponding_users"])
|
old_corresponding_users = set(existing_permission["corresponding_users"])
|
||||||
new_corresponding_users = set(new_permission["corresponding_users"])
|
new_corresponding_users = set(new_permission["corresponding_users"])
|
||||||
|
|
||||||
|
|
|
@ -288,6 +288,15 @@ def tools_regen_conf(
|
||||||
names=[], with_diff=False, force=False, dry_run=False, list_pending=False
|
names=[], with_diff=False, force=False, dry_run=False, list_pending=False
|
||||||
):
|
):
|
||||||
|
|
||||||
|
# Make sure the permission infos are migrated before running the regenconf,
|
||||||
|
# which may otherwise fuck things up because the slapd conf will be regenerated etc
|
||||||
|
# We do this here because the regen-conf is called before the migration in debian/postinst
|
||||||
|
if os.system(f"grep --quiet 0031_rework_permission_infos {MIGRATIONS_STATE_PATH}") != 0:
|
||||||
|
try:
|
||||||
|
tools_migrations_run(["0031_rework_permission_infos"])
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
|
||||||
from yunohost.regenconf import regen_conf
|
from yunohost.regenconf import regen_conf
|
||||||
return regen_conf(names, with_diff, force, dry_run, list_pending)
|
return regen_conf(names, with_diff, force, dry_run, list_pending)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue