Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: app/permissions: Move permissions data out of LDAP #1917

Draft
wants to merge 1 commit into
base: bookworm
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 0 additions & 12 deletions conf/slapd/db_init.ldif
Original file line number Diff line number Diff line change
Expand Up @@ -68,28 +68,16 @@ groupPermission: cn=all_users,ou=groups,dc=yunohost,dc=org
cn: mail.main
objectClass: posixGroup
objectClass: permissionYnh
isProtected: TRUE
label: E-mail
gidNumber: 5001
showTile: FALSE
authHeader: FALSE

dn: cn=ssh.main,ou=permission,dc=yunohost,dc=org
cn: ssh.main
objectClass: posixGroup
objectClass: permissionYnh
isProtected: TRUE
label: SSH
gidNumber: 5003
showTile: FALSE
authHeader: FALSE

dn: cn=sftp.main,ou=permission,dc=yunohost,dc=org
cn: sftp.main
objectClass: posixGroup
objectClass: permissionYnh
isProtected: TRUE
label: SFTP
gidNumber: 5004
showTile: FALSE
authHeader: FALSE
22 changes: 2 additions & 20 deletions conf/slapd/permission.ldif
Original file line number Diff line number Diff line change
Expand Up @@ -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'
DESC 'YunoHost permission for user on permission side'
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
# For Applications
olcObjectClasses: ( 1.3.6.1.4.1.17953.9.2.1 NAME 'groupOfNamesYnh'
Expand All @@ -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'
DESC 'a YunoHost application'
SUP top AUXILIARY
MUST ( cn $ authHeader $ label $ showTile $ isProtected )
MAY ( groupPermission $ inheritPermission $ URL $ additionalUrls ) )
MUST ( cn )
MAY ( groupPermission $ inheritPermission ) )
# For User
olcObjectClasses: ( 1.3.6.1.4.1.17953.9.2.3 NAME 'userPermissionYnh'
DESC 'a YunoHost application'
Expand Down
74 changes: 74 additions & 0 deletions src/migrations/0031_rework_permission_infos.py
Original file line number Diff line number Diff line change
@@ -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
127 changes: 68 additions & 59 deletions src/permission.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,13 @@ def user_permission_list(
from yunohost.utils.ldap import _get_ldap_interface, _ldap_path_extract

ldap = _get_ldap_interface()
permissions_infos = ldap.search(
ldap_permissions_infos = ldap.search(
"ou=permission",
"(objectclass=permissionYnh)",
[
"cn",
"groupPermission",
"inheritPermission",
"URL",
"additionalUrls",
"authHeader",
"label",
"showTile",
"isProtected",
],
)

Expand All @@ -78,30 +72,29 @@ def user_permission_list(
}

permissions = {}
for infos in permissions_infos:
for infos in ldap_permissions_infos:
name = infos["cn"][0]
app = name.split(".")[0]
app, subperm = name.split(".")

if ignore_system_perms and app in SYSTEM_PERMS:
continue
if filter_ and app not in apps:
continue

perm = {}
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", [])
]
perm["auth_header"] = infos.get("authHeader", [False])[0] == "TRUE"
perm["label"] = infos.get("label", [None])[0]
perm["show_tile"] = infos.get("showTile", [False])[0] == "TRUE"
perm["protected"] = infos.get("isProtected", [False])[0] == "TRUE"
perm["url"] = infos.get("URL", [None])[0]
perm["additional_urls"] = infos.get("additionalUrls", [])
if full and app not in SYSTEM_PERMS:
# Default stuff
perm = {
"url": None,
"additional_urls": [],
"auth_header": True,
"show_tile": None, # To be automagically set to True by default if an url is defined and show_tile not provided
"protected": False,
}
perm_settings = (app_setting(app, "_permissions") or {}).get(subperm, {})
perm.update(perm_settings)
if perm["show_tile"] is None and perm["url"] is not None:
perm["show_tile"] = True

if absolute_urls:
app_base_path = (
Expand All @@ -113,6 +106,14 @@ def user_permission_list(
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

# Make sure labels for sub-permissions are the form " Applabel (Sublabel) "
Expand Down Expand Up @@ -414,16 +415,6 @@ def permission_create(
"objectClass": ["top", "permissionYnh", "posixGroup"],
"cn": str(permission),
"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:
Expand All @@ -446,6 +437,8 @@ def permission_create(
"permission_creation_failed", permission=permission, error=e
)

label = str(label) if label else (subperm if subperm != "main" else app.title())

try:
permission_url(
permission,
Expand All @@ -463,6 +456,7 @@ def permission_create(
protected=protected,
sync_perm=sync_perm,
)

except Exception:
permission_delete(permission, force=True)
raise
Expand Down Expand Up @@ -496,15 +490,15 @@ def permission_url(
clear_urls -- (optional) Clean all urls (url and additional_urls)
"""
from yunohost.app import app_setting
from yunohost.utils.ldap import _get_ldap_interface

ldap = _get_ldap_interface()

# By default, manipulate main permission
if "." not in permission:
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:
domain = app_setting(app, "domain")
Expand Down Expand Up @@ -573,19 +567,20 @@ def permission_url(

# Actually commit the change

operation_logger.related_to.append(("app", permission.split(".")[0]))
operation_logger.related_to.append(("app", app))
operation_logger.start()

try:
ldap.update(
f"cn={permission},ou=permission",
{
"URL": [url] if url is not None else [],
"additionalUrls": new_additional_urls,
"authHeader": [str(auth_header).upper()],
"showTile": [str(show_tile).upper()],
},
)
perm_settings = app_setting(app, "_permissions") or {}
if sub_permission not in perm_settings:
perm_settings[sub_permission] = {}
perm_settings[sub_permission].update({
"url": url,
"additional_urls": new_additional_urls,
"auth_header": auth_header,
"show_tile": show_tile,
})
app_setting(app, "_permissions", perm_settings)
except Exception as e:
raise YunohostError("permission_update_failed", permission=permission, error=e)

Expand Down Expand Up @@ -714,48 +709,65 @@ def _update_ldap_group_permission(
- the 'allowed' list contains *existing* groups.
"""

from yunohost.app import app_setting
from yunohost.hook import hook_callback
from yunohost.utils.ldap import _get_ldap_interface

ldap = _get_ldap_interface()

app, sub_permission = permission.split(".")
existing_permission = user_permission_info(permission)

update = {}
update_ldap = {}
update_settings = {}

if allowed is not None:
allowed = [allowed] if not isinstance(allowed, list) else allowed
# Guarantee uniqueness of values in allowed, which would otherwise make ldap.update angry.
allowed = set(allowed)
update["groupPermission"] = [
update_ldap["groupPermission"] = [
"cn=" + g + ",ou=groups,dc=yunohost,dc=org" for g in allowed
]

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:
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 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"]:
logger.warning(
m18n.n(
"show_tile_cant_be_enabled_for_url_not_defined",
permission=permission,
)
)
show_tile = False
update_settings["show_tile"] = False
elif existing_permission["url"].startswith("re:"):
logger.warning(
m18n.n("show_tile_cant_be_enabled_for_regex", permission=permission)
)
show_tile = False
update["showTile"] = [str(show_tile).upper()]
update_settings["show_tile"] = False

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:
ldap.update(f"cn={permission},ou=permission", update)
ldap.update(f"cn={permission},ou=permission", update_ldap)
except Exception as e:
raise YunohostError("permission_update_failed", permission=permission, error=e)

Expand All @@ -768,9 +780,6 @@ def _update_ldap_group_permission(

# Trigger app callbacks

app = permission.split(".")[0]
sub_permission = permission.split(".")[1]

old_corresponding_users = set(existing_permission["corresponding_users"])
new_corresponding_users = set(new_permission["corresponding_users"])

Expand Down
Loading