Skip to content

Commit

Permalink
app/permissions: Move permissions data out of LDAP
Browse files Browse the repository at this point in the history
  • Loading branch information
alexAubin committed Nov 6, 2024
1 parent c9d1e0e commit 9340666
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 91 deletions.
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
79 changes: 79 additions & 0 deletions src/migrations/0031_rework_permission_infos.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
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.1"
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():
print(app)
print(permissions)
app_setting(app, "_permissions", permissions)

permission_sync_to_user()

# FIXME : dummy excepton to trigger the rollback while debugging everything etc
raise Exception

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": list(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

0 comments on commit 9340666

Please sign in to comment.