mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
Merge pull request #585 from YunoHost/group_permission
Group permission
This commit is contained in:
commit
ac7102a0ca
21 changed files with 2232 additions and 207 deletions
|
@ -58,7 +58,7 @@ _global:
|
|||
# User #
|
||||
#############################
|
||||
user:
|
||||
category_help: Manage users
|
||||
category_help: Manage users and groups
|
||||
actions:
|
||||
|
||||
### user_list()
|
||||
|
@ -200,6 +200,172 @@ user:
|
|||
help: Username or email to get information
|
||||
|
||||
subcategories:
|
||||
group:
|
||||
subcategory_help: Manage group
|
||||
actions:
|
||||
### user_group_list()
|
||||
list:
|
||||
action_help: List group
|
||||
api: GET /users/groups
|
||||
arguments:
|
||||
--fields:
|
||||
help: fields to fetch
|
||||
nargs: "+"
|
||||
|
||||
### user_group_add()
|
||||
add:
|
||||
action_help: Create group
|
||||
api: POST /users/groups
|
||||
arguments:
|
||||
groupname:
|
||||
help: The unique group name to add
|
||||
extra:
|
||||
pattern: &pattern_groupname
|
||||
- !!str ^[a-z0-9_]+$
|
||||
- "pattern_groupname"
|
||||
|
||||
### user_group_delete()
|
||||
delete:
|
||||
action_help: Delete group
|
||||
api: DELETE /users/groups/<groupname>
|
||||
arguments:
|
||||
groupname:
|
||||
help: Username to delete
|
||||
extra:
|
||||
pattern: *pattern_groupname
|
||||
|
||||
### user_group_update()
|
||||
update:
|
||||
action_help: Update group
|
||||
api: PUT /users/groups/<groupname>
|
||||
arguments:
|
||||
groupname:
|
||||
help: Username to update
|
||||
extra:
|
||||
pattern: *pattern_groupname
|
||||
-a:
|
||||
full: --add-user
|
||||
help: User to add in group
|
||||
nargs: "*"
|
||||
metavar: USERNAME
|
||||
extra:
|
||||
pattern: *pattern_username
|
||||
-r:
|
||||
full: --remove-user
|
||||
help: User to remove in group
|
||||
nargs: "*"
|
||||
metavar: USERNAME
|
||||
extra:
|
||||
pattern: *pattern_username
|
||||
|
||||
### user_group_info()
|
||||
info:
|
||||
action_help: Get group information
|
||||
api: GET /users/groups/<groupname>
|
||||
arguments:
|
||||
groupname:
|
||||
help: Groupname to get information
|
||||
extra:
|
||||
pattern: *pattern_username
|
||||
|
||||
permission:
|
||||
subcategory_help: Manage user permission
|
||||
actions:
|
||||
### user_permission_list()
|
||||
list:
|
||||
action_help: List access to user and group
|
||||
api: GET /users/permission/<app>
|
||||
arguments:
|
||||
-a:
|
||||
full: --app
|
||||
help: Application to manage the permission
|
||||
nargs: "*"
|
||||
metavar: APP
|
||||
-p:
|
||||
full: --permission
|
||||
help: Name of permission (main by default)
|
||||
nargs: "*"
|
||||
metavar: PERMISSION
|
||||
-u:
|
||||
full: --username
|
||||
help: Username
|
||||
nargs: "*"
|
||||
metavar: USER
|
||||
-g:
|
||||
full: --group
|
||||
help: Group name
|
||||
nargs: "*"
|
||||
metavar: GROUP
|
||||
|
||||
### user_permission_add()
|
||||
add:
|
||||
action_help: Grant access right to users and group
|
||||
api: POST /users/permission/<app>
|
||||
arguments:
|
||||
app:
|
||||
help: Application to manage the permission
|
||||
nargs: "+"
|
||||
-p:
|
||||
full: --permission
|
||||
help: Name of permission (main by default)
|
||||
nargs: "*"
|
||||
metavar: PERMISSION
|
||||
-u:
|
||||
full: --username
|
||||
help: Username
|
||||
nargs: "*"
|
||||
metavar: USER
|
||||
extra:
|
||||
pattern: *pattern_username
|
||||
-g:
|
||||
full: --group
|
||||
help: Group name
|
||||
nargs: "*"
|
||||
metavar: GROUP
|
||||
extra:
|
||||
pattern: *pattern_username
|
||||
|
||||
### user_permission_remove()
|
||||
remove:
|
||||
action_help: Revoke access right to users and group
|
||||
api: PUT /users/permission/<app>
|
||||
arguments:
|
||||
app:
|
||||
help: Application to manage the permission
|
||||
nargs: "+"
|
||||
-p:
|
||||
full: --permission
|
||||
help: Name of permission (main by default)
|
||||
nargs: "*"
|
||||
metavar: PERMISSION
|
||||
-u:
|
||||
full: --username
|
||||
help: Username
|
||||
nargs: "*"
|
||||
metavar: USER
|
||||
extra:
|
||||
pattern: *pattern_username
|
||||
-g:
|
||||
full: --group
|
||||
help: Group name
|
||||
nargs: "*"
|
||||
metavar: GROUP
|
||||
extra:
|
||||
pattern: *pattern_username
|
||||
|
||||
## user_permission_clear()
|
||||
clear:
|
||||
action_help: Reset access rights for the app
|
||||
api: DELETE /users/permission/<app>
|
||||
arguments:
|
||||
app:
|
||||
help: Application to manage the permission
|
||||
nargs: "+"
|
||||
-p:
|
||||
full: --permission
|
||||
help: Name of permission (main by default)
|
||||
nargs: "*"
|
||||
metavar: PERMISSION
|
||||
|
||||
ssh:
|
||||
subcategory_help: Manage ssh access
|
||||
|
@ -1419,8 +1585,6 @@ tools:
|
|||
postinstall:
|
||||
action_help: YunoHost post-install
|
||||
api: POST /postinstall
|
||||
configuration:
|
||||
authenticate: false
|
||||
arguments:
|
||||
-d:
|
||||
full: --domain
|
||||
|
|
|
@ -227,3 +227,73 @@ ynh_webpath_register () {
|
|||
|
||||
sudo yunohost app register-url $app $domain $path_url
|
||||
}
|
||||
|
||||
# Create a new permission for the app
|
||||
#
|
||||
# usage: ynh_permission_create --app "app" --permission "permission" --defaultdisallow [--urls "url" ["url" ...]]
|
||||
# | arg: app - the application id
|
||||
# | arg: permission - the name for the permission (by default a permission named "main" already exist)
|
||||
# | arg: defaultdisallow - define if all user will be allowed by default
|
||||
# | arg: urls - the list of urls for the the permission
|
||||
ynh_permission_create() {
|
||||
declare -Ar args_array=( [a]=app= [p]=permission= [d]=defaultdisallow [u]=urls= )
|
||||
local app
|
||||
local permission
|
||||
local defaultdisallow
|
||||
local urls
|
||||
ynh_handle_getopts_args "$@"
|
||||
if [[ -n ${defaultdisallow:-} ]]; then
|
||||
defaultdisallow=",default_allow=False"
|
||||
fi
|
||||
|
||||
if [[ -n ${urls:-} ]]; then
|
||||
urls=",urls=['${urls//';'/"','"}']"
|
||||
fi
|
||||
yunohost tools shell -c "from yunohost.permission import permission_add; permission_add('$app', '$permission' ${defaultdisallow:-} ${urls:-}, 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 --app "app" --permission "permission"
|
||||
# | arg: app - the application id
|
||||
# | arg: permission - the name for the permission (by default a permission named "main" is removed automatically when the app is removed)
|
||||
ynh_permission_remove() {
|
||||
declare -Ar args_array=( [a]=app= [p]=permission= )
|
||||
local app
|
||||
local permission
|
||||
ynh_handle_getopts_args "$@"
|
||||
|
||||
yunohost tools shell -c "from yunohost.permission import permission_remove; permission_remove('$app', '$permission', sync_perm=False)"
|
||||
}
|
||||
|
||||
# Add a path managed by the SSO
|
||||
#
|
||||
# usage: ynh_permission_add_path --app "app" --permission "permission" --url "url" ["url" ...]
|
||||
# | arg: app - the application id
|
||||
# | arg: permission - the name for the permission
|
||||
# | arg: url - the FULL url for the the permission (ex domain.tld/apps/admin)
|
||||
ynh_permission_add_path() {
|
||||
declare -Ar args_array=( [a]=app= [p]=permission= [u]=url= )
|
||||
local app
|
||||
local permission
|
||||
local url
|
||||
ynh_handle_getopts_args "$@"
|
||||
|
||||
yunohost tools shell -c "from yunohost.permission import permission_update; permission_update('$app', '$permission', add_url=['${url//';'/"','"}'], sync_perm=False)"
|
||||
}
|
||||
|
||||
# Remove a path managed by the SSO
|
||||
#
|
||||
# usage: ynh_permission_del_path --app "app" --permission "permission" --url "url" ["url" ...]
|
||||
# | arg: app - the application id
|
||||
# | arg: permission - the name for the permission
|
||||
# | arg: url - the FULL url for the the permission (ex domain.tld/apps/admin)
|
||||
ynh_permission_del_path() {
|
||||
declare -Ar args_array=( [a]=app= [p]=permission= [u]=url= )
|
||||
local app
|
||||
local permission
|
||||
local url
|
||||
ynh_handle_getopts_args "$@"
|
||||
|
||||
yunohost tools shell -c "from yunohost.permission import permission_update; permission_update('$app', '$permission', remove_url=['${url//';'/"','"}'], sync_perm=False)"
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ do_pre_regen() {
|
|||
|
||||
# copy configuration files
|
||||
cp -a ldap.conf slapd.conf "$ldap_dir"
|
||||
cp -a sudo.schema mailserver.schema "$schema_dir"
|
||||
cp -a sudo.schema mailserver.schema yunohost.schema "$schema_dir"
|
||||
|
||||
install -D -m 644 slapd.default "${pending_dir}/etc/default/slapd"
|
||||
}
|
||||
|
|
|
@ -17,6 +17,12 @@ parents:
|
|||
- organizationalUnit
|
||||
- top
|
||||
|
||||
ou=permission:
|
||||
ou: permission
|
||||
objectClass:
|
||||
- organizationalUnit
|
||||
- top
|
||||
|
||||
ou=groups:
|
||||
ou: groups
|
||||
objectClass:
|
||||
|
@ -29,22 +35,6 @@ parents:
|
|||
- top
|
||||
|
||||
children:
|
||||
cn=admins,ou=groups:
|
||||
cn: admins
|
||||
gidNumber: "4001"
|
||||
memberUid: admin
|
||||
objectClass:
|
||||
- posixGroup
|
||||
- top
|
||||
|
||||
cn=sftpusers,ou=groups:
|
||||
cn: sftpusers
|
||||
gidNumber: "4002"
|
||||
memberUid: admin
|
||||
objectClass:
|
||||
- posixGroup
|
||||
- top
|
||||
|
||||
cn=admin,ou=sudo:
|
||||
cn: admin
|
||||
sudoUser: admin
|
||||
|
@ -54,3 +44,34 @@ children:
|
|||
objectClass:
|
||||
- sudoRole
|
||||
- top
|
||||
cn=admins,ou=groups:
|
||||
cn: admins
|
||||
gidNumber: "4001"
|
||||
memberUid: admin
|
||||
objectClass:
|
||||
- posixGroup
|
||||
- top
|
||||
cn=all_users,ou=groups:
|
||||
cn: all_users
|
||||
gidNumber: "4002"
|
||||
objectClass:
|
||||
- posixGroup
|
||||
- groupOfNamesYnh
|
||||
|
||||
depends_children:
|
||||
cn=main.mail,ou=permission:
|
||||
cn: main.mail
|
||||
gidNumber: "5001"
|
||||
objectClass:
|
||||
- posixGroup
|
||||
- permissionYnh
|
||||
groupPermission:
|
||||
- "cn=all_users,ou=groups,dc=yunohost,dc=org"
|
||||
cn=main.metronome,ou=permission:
|
||||
cn: main.metronome
|
||||
gidNumber: "5002"
|
||||
objectClass:
|
||||
- posixGroup
|
||||
- permissionYnh
|
||||
groupPermission:
|
||||
- "cn=all_users,ou=groups,dc=yunohost,dc=org"
|
||||
|
|
|
@ -3,7 +3,7 @@ auth_bind = yes
|
|||
ldap_version = 3
|
||||
base = ou=users,dc=yunohost,dc=org
|
||||
user_attrs = uidNumber=500,gidNumber=8,mailuserquota=quota_rule=*:bytes=%$
|
||||
user_filter = (&(objectClass=inetOrgPerson)(uid=%n))
|
||||
pass_filter = (&(objectClass=inetOrgPerson)(uid=%n))
|
||||
user_filter = (&(objectClass=inetOrgPerson)(uid=%n)(permission=cn=main.mail,ou=permission,dc=yunohost,dc=org))
|
||||
pass_filter = (&(objectClass=inetOrgPerson)(uid=%n)(permission=cn=main.mail,ou=permission,dc=yunohost,dc=org))
|
||||
default_pass_scheme = SSHA
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ VirtualHost "{{ domain }}"
|
|||
hostname = "localhost",
|
||||
user = {
|
||||
basedn = "ou=users,dc=yunohost,dc=org",
|
||||
filter = "(&(objectClass=posixAccount)(mail=*@{{ domain }}))",
|
||||
filter = "(&(objectClass=posixAccount)(mail=*@{{ domain }})(permission=cn=main.metronome,ou=permission,dc=yunohost,dc=org))",
|
||||
usernamefield = "mail",
|
||||
namefield = "cn",
|
||||
},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
server_host = localhost
|
||||
server_port = 389
|
||||
search_base = dc=yunohost,dc=org
|
||||
query_filter = (&(objectClass=mailAccount)(mail=%s))
|
||||
query_filter = (&(objectClass=mailAccount)(mail=%s)(permission=cn=main.mail,ou=permission,dc=yunohost,dc=org))
|
||||
result_attribute = uid
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
server_host = localhost
|
||||
server_port = 389
|
||||
search_base = dc=yunohost,dc=org
|
||||
query_filter = (&(objectClass=mailAccount)(mail=%s))
|
||||
query_filter = (&(objectClass=mailAccount)(mail=%s)(permission=cn=main.mail,ou=permission,dc=yunohost,dc=org))
|
||||
result_attribute = maildrop
|
||||
|
|
|
@ -14,6 +14,7 @@ include /etc/ldap/schema/nis.schema
|
|||
include /etc/ldap/schema/inetorgperson.schema
|
||||
include /etc/ldap/schema/mailserver.schema
|
||||
include /etc/ldap/schema/sudo.schema
|
||||
include /etc/ldap/schema/yunohost.schema
|
||||
|
||||
# Where the pid file is put. The init.d script
|
||||
# will not stop the server if you change this.
|
||||
|
@ -31,7 +32,7 @@ password-hash {SSHA}
|
|||
# Where the dynamically loaded modules are stored
|
||||
modulepath /usr/lib/ldap
|
||||
moduleload back_mdb
|
||||
moduleload memberof
|
||||
moduleload memberof
|
||||
|
||||
# The maximum number of entries that is returned for a search operation
|
||||
sizelimit 500
|
||||
|
@ -117,3 +118,32 @@ access to *
|
|||
by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write
|
||||
by group/groupOfNames/Member="cn=admin,ou=groups,dc=yunohost,dc=org" write
|
||||
by * read
|
||||
|
||||
# Configure Memberof Overlay (used for Yunohost permission)
|
||||
|
||||
# Link user <-> group
|
||||
#dn: olcOverlay={0}memberof,olcDatabase={1}mdb,cn=config
|
||||
overlay memberof
|
||||
memberof-group-oc groupOfNamesYnh
|
||||
memberof-member-ad member
|
||||
memberof-memberof-ad memberOf
|
||||
memberof-dangling error
|
||||
memberof-refint TRUE
|
||||
|
||||
# Link permission <-> groupes
|
||||
#dn: olcOverlay={1}memberof,olcDatabase={1}mdb,cn=config
|
||||
overlay memberof
|
||||
memberof-group-oc permissionYnh
|
||||
memberof-member-ad groupPermission
|
||||
memberof-memberof-ad permission
|
||||
memberof-dangling error
|
||||
memberof-refint TRUE
|
||||
|
||||
# Link permission <-> user
|
||||
#dn: olcOverlay={2}memberof,olcDatabase={1}mdb,cn=config
|
||||
overlay memberof
|
||||
memberof-group-oc permissionYnh
|
||||
memberof-member-ad inheritPermission
|
||||
memberof-memberof-ad permission
|
||||
memberof-dangling error
|
||||
memberof-refint TRUE
|
||||
|
|
33
data/templates/slapd/yunohost.schema
Normal file
33
data/templates/slapd/yunohost.schema
Normal file
|
@ -0,0 +1,33 @@
|
|||
#dn: cn=yunohost,cn=schema,cn=config
|
||||
#objectClass: olcSchemaConfig
|
||||
#cn: yunohost
|
||||
# ATTRIBUTES
|
||||
# For Permission
|
||||
attributetype ( 1.3.6.1.4.1.17953.9.1.1 NAME 'permission'
|
||||
DESC 'Yunohost permission on user and group side'
|
||||
SUP distinguishedName )
|
||||
attributetype ( 1.3.6.1.4.1.17953.9.1.2 NAME 'groupPermission'
|
||||
DESC 'Yunohost permission for a group on permission side'
|
||||
SUP distinguishedName )
|
||||
attributetype ( 1.3.6.1.4.1.17953.9.1.3 NAME 'inheritPermission'
|
||||
DESC 'Yunohost permission for user on permission side'
|
||||
SUP distinguishedName )
|
||||
attributetype ( 1.3.6.1.4.1.17953.9.1.4 NAME 'URL'
|
||||
DESC 'Yunohost application URL'
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
|
||||
# OBJECTCLASS
|
||||
# For Applications
|
||||
objectclass ( 1.3.6.1.4.1.17953.9.2.1 NAME 'groupOfNamesYnh'
|
||||
DESC 'Yunohost user group'
|
||||
SUP top AUXILIARY
|
||||
MAY ( member $ businessCategory $ seeAlso $ owner $ ou $ o $ permission ) )
|
||||
objectclass ( 1.3.6.1.4.1.17953.9.2.2 NAME 'permissionYnh'
|
||||
DESC 'a Yunohost application'
|
||||
SUP top AUXILIARY
|
||||
MUST cn
|
||||
MAY ( groupPermission $ inheritPermission $ URL ) )
|
||||
# For User
|
||||
objectclass ( 1.3.6.1.4.1.17953.9.2.3 NAME 'userPermissionYnh'
|
||||
DESC 'a Yunohost application'
|
||||
SUP top AUXILIARY
|
||||
MAY ( permission ) )
|
|
@ -49,6 +49,8 @@
|
|||
"app_upgrade_failed": "Unable to upgrade {app:s}",
|
||||
"app_upgrade_some_app_failed": "Unable to upgrade some applications",
|
||||
"app_upgraded": "{app:s} has been upgraded",
|
||||
"apps_permission_not_found": "No permission found for the installed apps",
|
||||
"apps_permission_restoration_failed": "Permission '{permission:s}' for app {app:s} restoration has failed",
|
||||
"appslist_corrupted_json": "Could not load the application lists. It looks like {filename:s} is corrupted.",
|
||||
"appslist_could_not_migrate": "Could not migrate app list {appslist:s}! Unable to parse the url… The old cron job has been kept in {bkp_file:s}.",
|
||||
"appslist_fetched": "The application list {appslist:s} has been fetched",
|
||||
|
@ -115,7 +117,8 @@
|
|||
"backup_output_directory_forbidden": "Forbidden output directory. Backups can't be created in /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var or /home/yunohost.backup/archives sub-folders",
|
||||
"backup_output_directory_not_empty": "The output directory is not empty",
|
||||
"backup_output_directory_required": "You must provide an output directory for the backup",
|
||||
"backup_output_symlink_dir_broken": "You have a broken symlink instead of your archives directory '{path:s}'. You may have a specific setup to backup your data on an other filesystem, in this case you probably forgot to remount or plug your hard drive or usb key.",
|
||||
"backup_output_symlink_dir_broken": "You have a broken symlink instead of your archives directory '{path:s}'. You may have a specific setup to backup your data on an other filesystem, in this case you probably forgot to remount or plug your hard dirve or usb key.",
|
||||
"backup_permission": "Backup permission for app {app:s}",
|
||||
"backup_php5_to_php7_migration_may_fail": "Could not convert your archive to support php7, your php apps may fail to restore (reason: {error:s})",
|
||||
"backup_running_hooks": "Running backup hooks…",
|
||||
"backup_system_part_failed": "Unable to backup the '{part:s}' system part",
|
||||
|
@ -193,6 +196,9 @@
|
|||
"dyndns_registration_failed": "Unable to register DynDNS domain: {error:s}",
|
||||
"dyndns_domain_not_provided": "Dyndns provider {provider:s} cannot provide domain {domain:s}.",
|
||||
"dyndns_unavailable": "Domain {domain:s} is not available.",
|
||||
"edit_group_not_allowed": "You are not allowed to edit the group {group:s}",
|
||||
"edit_permission_with_group_all_users_not_allowed": "You are not allowed to edit permission for group 'all_users', use 'yunohost user permission clear APP' or 'yunohost user permission add APP -u USER' instead.",
|
||||
"error_when_removing_sftpuser_group": "Error when trying remove sftpusers group",
|
||||
"executing_command": "Executing command '{command:s}'…",
|
||||
"executing_script": "Executing script '{script:s}'…",
|
||||
"extracting": "Extracting…",
|
||||
|
@ -224,6 +230,18 @@
|
|||
"global_settings_unknown_type": "Unexpected situation, the setting {setting:s} appears to have the type {unknown_type:s} but it's not a type supported by the system.",
|
||||
"good_practices_about_admin_password": "You are now about to define a new administration password. The password should be at least 8 characters - though it is good practice to use longer password (i.e. a passphrase) and/or to use various kind of characters (uppercase, lowercase, digits and special characters).",
|
||||
"good_practices_about_user_password": "You are now about to define a new user password. The password should be at least 8 characters - though it is good practice to use longer password (i.e. a passphrase) and/or to use various kind of characters (uppercase, lowercase, digits and special characters).",
|
||||
"group_already_allowed": "Group '{group:s}' already has permission '{permission:s}' enabled for app '{app:s}'",
|
||||
"group_already_disallowed": "Group '{group:s}' already has permissions '{permission:s}' disabled for app '{app:s}'",
|
||||
"group_name_already_exist": "Group {name:s} already exist",
|
||||
"group_created": "Group '{group}' successfully created",
|
||||
"group_creation_failed": "Group creation failed for group '{group}'",
|
||||
"group_deleted": "Group '{group}' deleted",
|
||||
"group_deletion_failed": "Group '{group} 'deletion failed",
|
||||
"group_deletion_not_allowed": "The group {group:s} cannot be deleted manually.",
|
||||
"group_info_failed": "Group info failed",
|
||||
"group_unknown": "Group {group:s} unknown",
|
||||
"group_updated": "Group '{group}' updated",
|
||||
"group_update_failed": "Group update failed for group '{group}'",
|
||||
"hook_exec_failed": "Script execution failed: {path:s}",
|
||||
"hook_exec_not_terminated": "Script execution did not finish properly: {path:s}",
|
||||
"hook_json_return_error": "Failed to read return from hook {path:s}. Error: {msg:s}. Raw content: {raw_content}",
|
||||
|
@ -262,13 +280,21 @@
|
|||
"log_dyndns_subscribe": "Subscribe to a YunoHost subdomain '{}'",
|
||||
"log_dyndns_update": "Update the ip associated with your YunoHost subdomain '{}'",
|
||||
"log_letsencrypt_cert_install": "Install Let's encrypt certificate on '{}' domain",
|
||||
"log_permission_add": "Add permission '{}' for app '{}'",
|
||||
"log_permission_remove": "Remove permission '{}'",
|
||||
"log_permission_update": "Update permission '{}' for app '{}'",
|
||||
"log_selfsigned_cert_install": "Install self signed certificate on '{}' domain",
|
||||
"log_letsencrypt_cert_renew": "Renew '{}' Let's encrypt certificate",
|
||||
"log_service_enable": "Enable '{}' service",
|
||||
"log_regen_conf": "Regenerate system configurations '{}'",
|
||||
"log_user_create": "Add '{}' user",
|
||||
"log_user_delete": "Delete '{}' user",
|
||||
"log_user_group_add": "Add '{}' group",
|
||||
"log_user_group_delete": "Delete '{}' group",
|
||||
"log_user_group_update": "Update '{}' group",
|
||||
"log_user_update": "Update information of '{}' user",
|
||||
"log_user_permission_add": "Update '{}' permission",
|
||||
"log_user_permission_remove": "Update '{}' permission",
|
||||
"log_tools_maindomain": "Make '{}' as main domain",
|
||||
"log_tools_migrations_migrate_forward": "Migrate forward",
|
||||
"log_tools_migrations_migrate_backward": "Migrate backward",
|
||||
|
@ -282,6 +308,7 @@
|
|||
"mail_alias_remove_failed": "Unable to remove mail alias '{mail:s}'",
|
||||
"mail_domain_unknown": "Unknown mail address domain '{domain:s}'",
|
||||
"mail_forward_remove_failed": "Unable to remove mail forward '{mail:s}'",
|
||||
"mailbox_disabled": "Mailbox disabled for user {user:s}",
|
||||
"mailbox_used_space_dovecot_down": "Dovecot mailbox service need to be up, if you want to get mailbox used space",
|
||||
"mail_unavailable": "This email address is reserved and shall be automatically allocated to the very first user",
|
||||
"maindomain_change_failed": "Unable to change the main domain",
|
||||
|
@ -304,6 +331,7 @@
|
|||
"migration_description_0008_ssh_conf_managed_by_yunohost_step2": "Let the SSH configuration be managed by YunoHost (step 2, manual)",
|
||||
"migration_description_0009_decouple_regenconf_from_services": "Decouple the regen-conf mechanism from services",
|
||||
"migration_description_0010_migrate_to_apps_json": "Remove deprecated appslists and use the new unified 'apps.json' list instead",
|
||||
"migration_description_0011_setup_group_permission": "Setup user group and setup permission for apps and services",
|
||||
"migration_0003_backward_impossible": "The stretch migration cannot be reverted.",
|
||||
"migration_0003_start": "Starting migration to Stretch. The logs will be available in {logfile}.",
|
||||
"migration_0003_patching_sources_list": "Patching the sources.lists…",
|
||||
|
@ -330,6 +358,17 @@
|
|||
"migration_0008_warning": "If you understand those warnings and agree to let YunoHost override your current configuration, run the migration. Otherwise, you can also skip the migration - though it is not recommended.",
|
||||
"migration_0008_no_warning": "No major risk has been indentified about overriding your SSH configuration - but we can't be absolutely sure ;)! If you agree to let YunoHost override your current configuration, run the migration. Otherwise, you can also skip the migration - though it is not recommended.",
|
||||
"migration_0009_not_needed": "This migration already happened somehow ? Skipping.",
|
||||
"migration_0011_backup_before_migration": "Creating a backup of LDAP database and apps settings prior to the actual migration.",
|
||||
"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 groups of users.",
|
||||
"migration_0011_LDAP_config_dirty": "It look like that you customized your LDAP configuration. For this migration the LDAP configuration need to be updated.\nYou need to save your actual configuration, reintialize the original configuration by the command 'yunohost tools regen-conf -f' and after retry the migration",
|
||||
"migration_0011_LDAP_update_failed": "LDAP update failed. 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 rollback the system.",
|
||||
"migration_0011_rollback_success": "Rollback succeeded.",
|
||||
"migration_0011_update_LDAP_database": "Updating LDAP database...",
|
||||
"migration_0011_update_LDAP_schema": "Updating LDAP schema...",
|
||||
"migrations_backward": "Migrating backward.",
|
||||
"migrations_bad_value_for_target": "Invalid number for target argument, available migrations numbers are 0 or {}",
|
||||
"migrations_cant_reach_migration_file": "Can't access migrations files at path %s",
|
||||
|
@ -358,6 +397,7 @@
|
|||
"mysql_db_creation_failed": "MySQL database creation failed",
|
||||
"mysql_db_init_failed": "MySQL database init failed",
|
||||
"mysql_db_initialized": "The MySQL database has been initialized",
|
||||
"need_define_permission_before": "You need to redefine the permission using 'yunohost user permission add -u USER' before removing an allowed group",
|
||||
"network_check_mx_ko": "DNS MX record is not set",
|
||||
"network_check_smtp_ko": "Outbound mail (SMTP port 25) seems to be blocked by your network",
|
||||
"network_check_smtp_ok": "Outbound mail (SMTP port 25) is not blocked",
|
||||
|
@ -391,11 +431,25 @@
|
|||
"pattern_positive_number": "Must be a positive number",
|
||||
"pattern_username": "Must be lower-case alphanumeric and underscore characters only",
|
||||
"pattern_password_app": "Sorry, passwords should not contain the following characters: {forbidden_chars}",
|
||||
"permission_already_clear": "Permission '{permission:s}' already clear for app {app:s}",
|
||||
"permission_already_exist": "Permission '{permission:s}' for app {app:s} already exist",
|
||||
"permission_created": "Permission '{permission:s}' for app {app:s} created",
|
||||
"permission_creation_failed": "Permission creation failed",
|
||||
"permission_deleted": "Permission '{permission:s}' for app {app:s} deleted",
|
||||
"permission_deletion_failed": "Permission '{permission:s}' for app {app:s} deletion failed",
|
||||
"permission_not_found": "Permission '{permission:s}' not found for application {app:s}",
|
||||
"permission_name_not_valid": "Permission name '{permission:s}' not valid",
|
||||
"permission_update_failed": "Permission update failed",
|
||||
"permission_generated": "The permission database has been updated",
|
||||
"permission_updated": "Permission '{permission:s}' for app {app:s} updated",
|
||||
"permission_update_nothing_to_do": "No permissions to update",
|
||||
"port_already_closed": "Port {port:d} is already closed for {ip_version:s} connections",
|
||||
"port_already_opened": "Port {port:d} is already opened for {ip_version:s} connections",
|
||||
"port_available": "Port {port:d} is available",
|
||||
"port_unavailable": "Port {port:d} is not available",
|
||||
"recommend_to_add_first_user": "The post-install is finished but YunoHost needs at least one user to work correctly, you should add one using 'yunohost user create $username' or the admin interface.",
|
||||
"remove_main_permission_not_allowed": "Removing the main permission is not allowed",
|
||||
"remove_user_of_group_not_allowed": "You are not allowed to remove the user {user:s} in the group {group:s}",
|
||||
"regenconf_file_backed_up": "The configuration file '{conf}' has been backed up to '{backup}'",
|
||||
"regenconf_file_copy_failed": "Unable to copy the new configuration file '{new}' to '{conf}'",
|
||||
"regenconf_file_kept_back": "The configuration file '{conf}' is expected to be deleted by regen-conf (category {category}) but has been kept back.",
|
||||
|
@ -481,6 +535,7 @@
|
|||
"ssowat_conf_updated": "The SSOwat configuration has been updated",
|
||||
"ssowat_persistent_conf_read_error": "Error while reading SSOwat persistent configuration: {error:s}. Edit /etc/ssowat/conf.json.persistent file to fix the JSON syntax",
|
||||
"ssowat_persistent_conf_write_error": "Error while saving SSOwat persistent configuration: {error:s}. Edit /etc/ssowat/conf.json.persistent file to fix the JSON syntax",
|
||||
"system_groupname_exists": "Groupname already exists in the system group",
|
||||
"system_upgraded": "The system has been upgraded",
|
||||
"system_username_exists": "Username already exists in the system users",
|
||||
"this_action_broke_dpkg": "This action broke dpkg/apt (the system package managers)... You can try to solve this issue by connecting through SSH and running `sudo dpkg --configure -a`.",
|
||||
|
@ -508,12 +563,14 @@
|
|||
"upnp_disabled": "UPnP has been disabled",
|
||||
"upnp_enabled": "UPnP has been enabled",
|
||||
"upnp_port_open_failed": "Unable to open UPnP ports",
|
||||
"user_already_in_group": "User {user:} already in group {group:s}",
|
||||
"user_created": "The user has been created",
|
||||
"user_creation_failed": "Unable to create user",
|
||||
"user_deleted": "The user has been deleted",
|
||||
"user_deletion_failed": "Unable to delete user",
|
||||
"user_home_creation_failed": "Unable to create user home folder",
|
||||
"user_info_failed": "Unable to retrieve user information",
|
||||
"user_not_in_group": "User {user:s} not in group {group:s}",
|
||||
"user_unknown": "Unknown user: {user:s}",
|
||||
"user_update_failed": "Unable to update user",
|
||||
"user_updated": "The user has been updated",
|
||||
|
|
|
@ -400,6 +400,9 @@ def app_map(app=None, raw=False, user=None):
|
|||
app -- Specific app to map
|
||||
|
||||
"""
|
||||
from yunohost.permission import user_permission_list
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
|
||||
apps = []
|
||||
result = {}
|
||||
|
||||
|
@ -419,11 +422,10 @@ def app_map(app=None, raw=False, user=None):
|
|||
if 'no_sso' in app_settings: # I don't think we need to check for the value here
|
||||
continue
|
||||
if user is not None:
|
||||
if ('mode' not in app_settings
|
||||
or ('mode' in app_settings
|
||||
and app_settings['mode'] == 'private')) \
|
||||
and 'allowed_users' in app_settings \
|
||||
and user not in app_settings['allowed_users'].split(','):
|
||||
ldap = _get_ldap_interface()
|
||||
if not ldap.search(base='ou=permission,dc=yunohost,dc=org',
|
||||
filter='(&(objectclass=permissionYnh)(cn=main.%s)(inheritPermission=uid=%s,ou=users,dc=yunohost,dc=org))' % (app_id, user),
|
||||
attrs=['cn']):
|
||||
continue
|
||||
|
||||
domain = app_settings['domain']
|
||||
|
@ -455,6 +457,7 @@ 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_update
|
||||
|
||||
installed = _is_installed(app)
|
||||
if not installed:
|
||||
|
@ -544,10 +547,10 @@ def app_change_url(operation_logger, app, domain, path):
|
|||
app_setting(app, 'domain', value=domain)
|
||||
app_setting(app, 'path', value=path)
|
||||
|
||||
app_ssowatconf()
|
||||
permission_update(app, permission="main", add_url=[domain+path], remove_url=[old_domain+old_path], sync_perm=True)
|
||||
|
||||
# avoid common mistakes
|
||||
if _run_service_command("reload", "nginx") == False:
|
||||
if _run_service_command("reload", "nginx") is False:
|
||||
# grab nginx errors
|
||||
# the "exit 0" is here to avoid check_output to fail because 'nginx -t'
|
||||
# will return != 0 since we are in a failed state
|
||||
|
@ -577,6 +580,7 @@ def app_upgrade(app=[], url=None, file=None):
|
|||
raise YunohostError("dpkg_is_broken")
|
||||
|
||||
from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback
|
||||
from yunohost.permission import permission_sync_to_user
|
||||
|
||||
# Retrieve interface
|
||||
is_api = msettings.get('interface') == 'api'
|
||||
|
@ -699,7 +703,7 @@ def app_upgrade(app=[], url=None, file=None):
|
|||
if not_upgraded_apps:
|
||||
raise YunohostError('app_not_upgraded', apps=', '.join(not_upgraded_apps))
|
||||
|
||||
app_ssowatconf()
|
||||
permission_sync_to_user()
|
||||
|
||||
logger.success(m18n.n('upgrade_complete'))
|
||||
|
||||
|
@ -719,8 +723,11 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu
|
|||
if packages.dpkg_is_broken():
|
||||
raise YunohostError("dpkg_is_broken")
|
||||
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback
|
||||
from yunohost.log import OperationLogger
|
||||
from yunohost.permission import permission_add, permission_update, permission_remove, permission_sync_to_user
|
||||
ldap = _get_ldap_interface()
|
||||
|
||||
# Fetch or extract sources
|
||||
try:
|
||||
|
@ -848,6 +855,11 @@ 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_add(app=app_instance_name, permission="main", sync_perm=False)
|
||||
|
||||
# Execute the app install script
|
||||
install_retcode = 1
|
||||
try:
|
||||
|
@ -880,6 +892,13 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu
|
|||
os.path.join(extracted_app_folder, 'scripts/remove'),
|
||||
args=[app_instance_name], env=env_dict_remove
|
||||
)[0]
|
||||
# Remove all permission in LDAP
|
||||
result = ldap.search(base='ou=permission,dc=yunohost,dc=org',
|
||||
filter='(&(objectclass=permissionYnh)(cn=*.%s))' % app_instance_name, attrs=['cn'])
|
||||
permission_list = [p['cn'][0] for p in result]
|
||||
for l in permission_list:
|
||||
permission_remove(app_instance_name, l.split('.')[0], force=True)
|
||||
|
||||
if remove_retcode != 0:
|
||||
msg = m18n.n('app_not_properly_removed',
|
||||
app=app_instance_name)
|
||||
|
@ -919,7 +938,14 @@ 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)
|
||||
|
||||
app_ssowatconf()
|
||||
# Add path in permission if it's defined in the app install script
|
||||
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_update(app_instance_name, permission="main", add_url=[domain+path], sync_perm=False)
|
||||
|
||||
permission_sync_to_user()
|
||||
|
||||
logger.success(m18n.n('installation_complete'))
|
||||
|
||||
|
@ -935,7 +961,9 @@ def app_remove(operation_logger, app):
|
|||
app -- App(s) to delete
|
||||
|
||||
"""
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
from yunohost.hook import hook_exec, hook_remove, hook_callback
|
||||
from yunohost.permission import permission_remove, permission_sync_to_user
|
||||
if not _is_installed(app):
|
||||
raise YunohostError('app_not_installed', app=app)
|
||||
|
||||
|
@ -979,13 +1007,23 @@ def app_remove(operation_logger, app):
|
|||
shutil.rmtree(app_setting_path)
|
||||
shutil.rmtree('/tmp/yunohost_remove')
|
||||
hook_remove(app)
|
||||
app_ssowatconf()
|
||||
|
||||
# Remove all permission in LDAP
|
||||
ldap = _get_ldap_interface()
|
||||
result = ldap.search(base='ou=permission,dc=yunohost,dc=org',
|
||||
filter='(&(objectclass=permissionYnh)(cn=*.%s))' % app, attrs=['cn'])
|
||||
permission_list = [p['cn'][0] for p in result]
|
||||
for l in permission_list:
|
||||
permission_remove(app, l.split('.')[0], force=True, sync_perm=False)
|
||||
|
||||
permission_sync_to_user()
|
||||
|
||||
if packages.dpkg_is_broken():
|
||||
raise YunohostError("this_action_broke_dpkg")
|
||||
|
||||
|
||||
def app_addaccess(apps, users=[]):
|
||||
@is_unit_operation(['permission','app'])
|
||||
def app_addaccess(operation_logger, apps, users=[]):
|
||||
"""
|
||||
Grant access right to users (everyone by default)
|
||||
|
||||
|
@ -994,64 +1032,17 @@ def app_addaccess(apps, users=[]):
|
|||
apps
|
||||
|
||||
"""
|
||||
from yunohost.user import user_list, user_info
|
||||
from yunohost.hook import hook_callback
|
||||
from yunohost.permission import user_permission_update
|
||||
|
||||
result = {}
|
||||
permission = user_permission_update(operation_logger, app=apps, permission="main", add_username=users)
|
||||
|
||||
if not users:
|
||||
users = user_list()['users'].keys()
|
||||
elif not isinstance(users, list):
|
||||
users = [users, ]
|
||||
if not isinstance(apps, list):
|
||||
apps = [apps, ]
|
||||
|
||||
for app in apps:
|
||||
|
||||
app_settings = _get_app_settings(app)
|
||||
if not app_settings:
|
||||
continue
|
||||
|
||||
if 'mode' not in app_settings:
|
||||
app_setting(app, 'mode', 'private')
|
||||
app_settings['mode'] = 'private'
|
||||
|
||||
if app_settings['mode'] == 'private':
|
||||
|
||||
# Start register change on system
|
||||
related_to = [('app', app)]
|
||||
operation_logger = OperationLogger('app_addaccess', related_to)
|
||||
operation_logger.start()
|
||||
|
||||
allowed_users = set()
|
||||
if 'allowed_users' in app_settings and app_settings['allowed_users']:
|
||||
allowed_users = set(app_settings['allowed_users'].split(','))
|
||||
|
||||
for allowed_user in users:
|
||||
if allowed_user not in allowed_users:
|
||||
try:
|
||||
user_info(allowed_user)
|
||||
except YunohostError:
|
||||
logger.warning(m18n.n('user_unknown', user=allowed_user))
|
||||
continue
|
||||
allowed_users.add(allowed_user)
|
||||
operation_logger.related_to.append(('user', allowed_user))
|
||||
|
||||
operation_logger.flush()
|
||||
new_users = ','.join(allowed_users)
|
||||
app_setting(app, 'allowed_users', new_users)
|
||||
hook_callback('post_app_addaccess', args=[app, new_users])
|
||||
|
||||
operation_logger.success()
|
||||
|
||||
result[app] = allowed_users
|
||||
|
||||
app_ssowatconf()
|
||||
result = {p : v['main']['allowed_users'] for p, v in permission['permissions'].items()}
|
||||
|
||||
return {'allowed_users': result}
|
||||
|
||||
|
||||
def app_removeaccess(apps, users=[]):
|
||||
@is_unit_operation(['permission','app'])
|
||||
def app_removeaccess(operation_logger, apps, users=[]):
|
||||
"""
|
||||
Revoke access right to users (everyone by default)
|
||||
|
||||
|
@ -1060,59 +1051,17 @@ def app_removeaccess(apps, users=[]):
|
|||
apps
|
||||
|
||||
"""
|
||||
from yunohost.user import user_list
|
||||
from yunohost.hook import hook_callback
|
||||
from yunohost.permission import user_permission_update
|
||||
|
||||
result = {}
|
||||
permission = user_permission_update(operation_logger, app=apps, permission="main", del_username=users)
|
||||
|
||||
remove_all = False
|
||||
if not users:
|
||||
remove_all = True
|
||||
elif not isinstance(users, list):
|
||||
users = [users, ]
|
||||
if not isinstance(apps, list):
|
||||
apps = [apps, ]
|
||||
|
||||
for app in apps:
|
||||
app_settings = _get_app_settings(app)
|
||||
if not app_settings:
|
||||
continue
|
||||
allowed_users = set()
|
||||
|
||||
if app_settings.get('skipped_uris', '') != '/':
|
||||
|
||||
# Start register change on system
|
||||
related_to = [('app', app)]
|
||||
operation_logger = OperationLogger('app_removeaccess', related_to)
|
||||
operation_logger.start()
|
||||
|
||||
if remove_all:
|
||||
pass
|
||||
elif 'allowed_users' in app_settings:
|
||||
for allowed_user in app_settings['allowed_users'].split(','):
|
||||
if allowed_user not in users:
|
||||
allowed_users.add(allowed_user)
|
||||
else:
|
||||
for allowed_user in user_list()['users'].keys():
|
||||
if allowed_user not in users:
|
||||
allowed_users.add(allowed_user)
|
||||
|
||||
operation_logger.related_to += [('user', x) for x in allowed_users]
|
||||
operation_logger.flush()
|
||||
new_users = ','.join(allowed_users)
|
||||
app_setting(app, 'allowed_users', new_users)
|
||||
hook_callback('post_app_removeaccess', args=[app, new_users])
|
||||
|
||||
result[app] = allowed_users
|
||||
|
||||
operation_logger.success()
|
||||
|
||||
app_ssowatconf()
|
||||
result = {p : v['main']['allowed_users'] for p, v in permission['permissions'].items()}
|
||||
|
||||
return {'allowed_users': result}
|
||||
|
||||
|
||||
def app_clearaccess(apps):
|
||||
@is_unit_operation(['permission','app'])
|
||||
def app_clearaccess(operation_logger, apps):
|
||||
"""
|
||||
Reset access rights for the app
|
||||
|
||||
|
@ -1120,33 +1069,13 @@ def app_clearaccess(apps):
|
|||
apps
|
||||
|
||||
"""
|
||||
from yunohost.hook import hook_callback
|
||||
from yunohost.permission import user_permission_clear
|
||||
|
||||
if not isinstance(apps, list):
|
||||
apps = [apps]
|
||||
permission = user_permission_clear(operation_logger, app=apps, permission="main")
|
||||
|
||||
for app in apps:
|
||||
app_settings = _get_app_settings(app)
|
||||
if not app_settings:
|
||||
continue
|
||||
|
||||
# Start register change on system
|
||||
related_to = [('app', app)]
|
||||
operation_logger = OperationLogger('app_clearaccess', related_to)
|
||||
operation_logger.start()
|
||||
|
||||
if 'mode' in app_settings:
|
||||
app_setting(app, 'mode', delete=True)
|
||||
|
||||
if 'allowed_users' in app_settings:
|
||||
app_setting(app, 'allowed_users', delete=True)
|
||||
|
||||
hook_callback('post_app_clearaccess', args=[app])
|
||||
|
||||
operation_logger.success()
|
||||
|
||||
app_ssowatconf()
|
||||
result = {p : v['main']['allowed_users'] for p, v in permission['permissions'].items()}
|
||||
|
||||
return {'allowed_users': result}
|
||||
|
||||
def app_debug(app):
|
||||
"""
|
||||
|
@ -1195,7 +1124,8 @@ def app_makedefault(operation_logger, app, domain=None):
|
|||
|
||||
operation_logger.start()
|
||||
if '/' in app_map(raw=True)[domain]:
|
||||
raise YunohostError('app_make_default_location_already_used', app=app, domain=app_domain, other_app=app_map(raw=True)[domain]["/"]["id"])
|
||||
raise YunohostError('app_make_default_location_already_used', app=app, domain=app_domain,
|
||||
other_app=app_map(raw=True)[domain]["/"]["id"])
|
||||
|
||||
try:
|
||||
with open('/etc/ssowat/conf.json.persistent') as json_conf:
|
||||
|
@ -1407,6 +1337,7 @@ def app_ssowatconf():
|
|||
"""
|
||||
from yunohost.domain import domain_list, _get_maindomain
|
||||
from yunohost.user import user_list
|
||||
from yunohost.permission import user_permission_list
|
||||
|
||||
main_domain = _get_maindomain()
|
||||
domains = domain_list()['domains']
|
||||
|
@ -1467,6 +1398,13 @@ def app_ssowatconf():
|
|||
skipped_regex.append("^[^/]*/%.well%-known/acme%-challenge/.*$")
|
||||
skipped_regex.append("^[^/]*/%.well%-known/autoconfig/mail/config%-v1%.1%.xml.*$")
|
||||
|
||||
permission = {}
|
||||
for a in user_permission_list()['permissions'].values():
|
||||
for p in a.values():
|
||||
if 'URL' in p:
|
||||
for u in p['URL']:
|
||||
permission[u] = p['allowed_users']
|
||||
|
||||
conf_dict = {
|
||||
'portal_domain': main_domain,
|
||||
'portal_path': '/yunohost/sso/',
|
||||
|
@ -1487,12 +1425,13 @@ def app_ssowatconf():
|
|||
'redirected_regex': redirected_regex,
|
||||
'users': {username: app_map(user=username)
|
||||
for username in user_list()['users'].keys()},
|
||||
'permission': permission,
|
||||
}
|
||||
|
||||
with open('/etc/ssowat/conf.json', 'w+') as f:
|
||||
json.dump(conf_dict, f, sort_keys=True, indent=4)
|
||||
|
||||
logger.success(m18n.n('ssowat_conf_generated'))
|
||||
logger.debug(m18n.n('ssowat_conf_generated'))
|
||||
|
||||
|
||||
def app_change_label(app, new_label):
|
||||
|
|
|
@ -701,6 +701,12 @@ class BackupManager():
|
|||
raise_on_error=True, chdir=tmp_app_bkp_dir, env=env_dict)[0]
|
||||
|
||||
self._import_to_list_to_backup(env_dict["YNH_BACKUP_CSV"])
|
||||
|
||||
# backup permissions
|
||||
logger.debug(m18n.n('backup_permission', app=app))
|
||||
ldap_url = "ldap:///dc=yunohost,dc=org???(&(objectClass=permissionYnh)(cn=*.%s))" % app
|
||||
os.system("slapcat -b dc=yunohost,dc=org -H '%s' -l '%s/permission.ldif'" % (ldap_url, settings_dir))
|
||||
|
||||
except:
|
||||
abs_tmp_app_dir = os.path.join(self.work_dir, 'apps/', app)
|
||||
shutil.rmtree(abs_tmp_app_dir, ignore_errors=True)
|
||||
|
@ -903,18 +909,17 @@ class RestoreManager():
|
|||
logger.debug("executing the post-install...")
|
||||
tools_postinstall(domain, 'Yunohost', True)
|
||||
|
||||
|
||||
def clean(self):
|
||||
"""
|
||||
End a restore operations by cleaning the working directory and
|
||||
regenerate ssowat conf (if some apps were restored)
|
||||
"""
|
||||
from permission import permission_sync_to_user
|
||||
|
||||
successfull_apps = self.targets.list("apps", include=["Success", "Warning"])
|
||||
|
||||
if successfull_apps != []:
|
||||
# Quickfix: the old app_ssowatconf(auth) instruction failed due to
|
||||
# ldap restore hooks
|
||||
os.system('sudo yunohost app ssowatconf')
|
||||
permission_sync_to_user(force=False)
|
||||
|
||||
if os.path.ismount(self.work_dir):
|
||||
ret = subprocess.call(["umount", self.work_dir])
|
||||
|
@ -1178,6 +1183,19 @@ class RestoreManager():
|
|||
if system_targets == []:
|
||||
return
|
||||
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
ldap = _get_ldap_interface()
|
||||
|
||||
# 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
|
||||
old_apps_permission = []
|
||||
try:
|
||||
old_apps_permission = ldap.search('ou=permission,dc=yunohost,dc=org',
|
||||
'(&(objectClass=permissionYnh)(!(cn=main.mail))(!(cn=main.metronome))(!(cn=main.sftp)))',
|
||||
['cn', 'objectClass', 'groupPermission', 'URL', 'gidNumber'])
|
||||
except:
|
||||
logger.info(m18n.n('apps_permission_not_found'))
|
||||
|
||||
# Start register change on system
|
||||
operation_logger = OperationLogger('backup_restore_system')
|
||||
operation_logger.start()
|
||||
|
@ -1214,12 +1232,44 @@ class RestoreManager():
|
|||
|
||||
regen_conf()
|
||||
|
||||
# Check if we need to do the migration 0009 : setup group and permission
|
||||
# Legacy code
|
||||
result = ldap.search('ou=groups,dc=yunohost,dc=org',
|
||||
'(&(objectclass=groupOfNamesYnh)(cn=all_users))',
|
||||
['cn'])
|
||||
if not result:
|
||||
from yunohost.tools import _get_migration_by_name
|
||||
setup_group_permission = _get_migration_by_name("setup_group_permission")
|
||||
# Update LDAP schema restart slapd
|
||||
logger.info(m18n.n("migration_0011_update_LDAP_schema"))
|
||||
regen_conf(names=['slapd'], force=True)
|
||||
setup_group_permission.migrate_LDAP_db()
|
||||
|
||||
# Remove all permission for all app which sill in the LDAP
|
||||
for per in ldap.search('ou=permission,dc=yunohost,dc=org',
|
||||
'(&(objectClass=permissionYnh)(!(cn=main.mail))(!(cn=main.metronome))(!(cn=main.sftp)))',
|
||||
['cn']):
|
||||
if not ldap.remove('cn=%s,ou=permission' % per['cn'][0]):
|
||||
raise YunohostError('permission_deletion_failed', permission=permission, app=app)
|
||||
|
||||
# Restore permission for the app which is installed
|
||||
for per in old_apps_permission:
|
||||
try:
|
||||
permission_name, app_name = per['cn'][0].split('.')
|
||||
except:
|
||||
logger.warning(m18n.n('permission_name_not_valid', permission=per['cn'][0]))
|
||||
if _is_installed(app_name):
|
||||
if not ldap.add('cn=%s,ou=permission' % per['cn'][0], per):
|
||||
raise YunohostError('apps_permission_restoration_failed', permission=permission_name, app=app_name)
|
||||
|
||||
|
||||
def _restore_apps(self):
|
||||
"""Restore all apps targeted"""
|
||||
|
||||
apps_targets = self.targets.list("apps", exclude=["Skipped"])
|
||||
|
||||
for app in apps_targets:
|
||||
print(app)
|
||||
self._restore_app(app)
|
||||
|
||||
def _restore_app(self, app_instance_name):
|
||||
|
@ -1249,6 +1299,12 @@ class RestoreManager():
|
|||
name already exists
|
||||
restore_app_failed -- Raised if the restore bash script failed
|
||||
"""
|
||||
from moulinette.utils.filesystem import read_ldif
|
||||
from yunohost.user import user_group_list
|
||||
from yunohost.permission import permission_remove
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
ldap = _get_ldap_interface()
|
||||
|
||||
def copytree(src, dst, symlinks=False, ignore=None):
|
||||
for item in os.listdir(src):
|
||||
s = os.path.join(src, item)
|
||||
|
@ -1311,6 +1367,26 @@ class RestoreManager():
|
|||
filesystem.chown(tmp_folder_for_app_restore, 'admin', None, True)
|
||||
restore_script = os.path.join(tmp_folder_for_app_restore, 'restore')
|
||||
|
||||
# Restore permissions
|
||||
if os.path.isfile(app_settings_in_archive + '/permission.ldif'):
|
||||
filtred_entries = ['entryUUID', 'creatorsName', 'createTimestamp', 'entryCSN', 'structuralObjectClass',
|
||||
'modifiersName', 'modifyTimestamp', 'inheritPermission', 'memberUid']
|
||||
entries = read_ldif('%s/permission.ldif' % app_settings_in_archive, filtred_entries)
|
||||
group_list = user_group_list(['cn'])['groups']
|
||||
for dn, entry in entries:
|
||||
# Remove the group which has been removed
|
||||
for group in entry['groupPermission']:
|
||||
group_name = group.split(',')[0].split('=')[1]
|
||||
if group_name not in group_list:
|
||||
entry['groupPermission'].remove(group)
|
||||
print(entry)
|
||||
if not ldap.add('cn=%s,ou=permission' % entry['cn'][0], entry):
|
||||
raise YunohostError('apps_permission_restoration_failed', permission=permission_name, app=app_name)
|
||||
else:
|
||||
from yunohost.tools import _get_migration_by_name
|
||||
setup_group_permission = _get_migration_by_name("setup_group_permission")
|
||||
setup_group_permission.migrate_app_permission(app=app_instance_name)
|
||||
|
||||
# Prepare env. var. to pass to script
|
||||
env_dict = self._get_env_var(app_instance_name)
|
||||
|
||||
|
@ -1357,6 +1433,13 @@ class RestoreManager():
|
|||
# Cleaning app directory
|
||||
shutil.rmtree(app_settings_new_path, ignore_errors=True)
|
||||
|
||||
# Remove all permission in LDAP
|
||||
result = ldap.search(base='ou=permission,dc=yunohost,dc=org',
|
||||
filter='(&(objectclass=permissionYnh)(cn=*.%s))' % app_instance_name, attrs=['cn'])
|
||||
permission_list = [p['cn'][0] for p in result]
|
||||
for l in permission_list:
|
||||
permission_remove(app_instance_name, l.split('.')[0], force=True)
|
||||
|
||||
# TODO Cleaning app hooks
|
||||
else:
|
||||
self.targets.set_result("apps", app_instance_name, "Success")
|
||||
|
|
142
src/yunohost/data_migrations/0011_setup_group_permission.py
Normal file
142
src/yunohost/data_migrations/0011_setup_group_permission.py
Normal file
|
@ -0,0 +1,142 @@
|
|||
import yaml
|
||||
import time
|
||||
import os
|
||||
|
||||
from moulinette import m18n
|
||||
from yunohost.utils.error import YunohostError
|
||||
from moulinette.utils.log import getActionLogger
|
||||
|
||||
from yunohost.tools import Migration
|
||||
from yunohost.user import user_group_add, user_group_update
|
||||
from yunohost.app import app_setting, app_list
|
||||
from yunohost.regenconf import regen_conf
|
||||
from yunohost.permission import permission_add, permission_sync_to_user
|
||||
from yunohost.user import user_permission_add
|
||||
|
||||
logger = getActionLogger('yunohost.migration')
|
||||
|
||||
###################################################
|
||||
# Tools used also for restoration
|
||||
###################################################
|
||||
|
||||
class MyMigration(Migration):
|
||||
"""
|
||||
Update the LDAP DB to be able to store the permission
|
||||
Create a group for each yunohost user
|
||||
Migrate app permission from apps setting to LDAP
|
||||
"""
|
||||
|
||||
required = True
|
||||
|
||||
def migrate_LDAP_db(self):
|
||||
|
||||
logger.info(m18n.n("migration_0011_update_LDAP_database"))
|
||||
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
ldap = _get_ldap_interface()
|
||||
|
||||
try:
|
||||
ldap.remove('cn=sftpusers,ou=groups')
|
||||
except:
|
||||
logger.warn(m18n.n("error_when_removing_sftpuser_group"))
|
||||
|
||||
with open('/usr/share/yunohost/yunohost-config/moulinette/ldap_scheme.yml') as f:
|
||||
ldap_map = yaml.load(f)
|
||||
|
||||
try:
|
||||
attr_dict = ldap_map['parents']['ou=permission']
|
||||
ldap.add('ou=permission', attr_dict)
|
||||
|
||||
attr_dict = ldap_map['children']['cn=all_users,ou=groups']
|
||||
ldap.add('cn=all_users,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_add(username, gid=user_info['uidNumber'][0], sync_perm=False)
|
||||
user_group_update(groupname=username, add_user=username, force=True, sync_perm=False)
|
||||
user_group_update(groupname='all_users', add_user=username, force=True, sync_perm=False)
|
||||
|
||||
|
||||
def migrate_app_permission(self, app=None):
|
||||
logger.info(m18n.n("migration_0011_migrate_permission"))
|
||||
|
||||
if app:
|
||||
apps = app_list(installed=True, filter=app)['apps']
|
||||
else:
|
||||
apps = app_list(installed=True)['apps']
|
||||
|
||||
for app_info in apps:
|
||||
app = app_info['id']
|
||||
permission = app_setting(app, 'allowed_users')
|
||||
path = app_setting(app, 'path')
|
||||
domain = app_setting(app, 'domain')
|
||||
|
||||
urls = [domain + path] if domain and path else None
|
||||
permission_add(app, permission='main', urls=urls, default_allow=True, sync_perm=False)
|
||||
if permission:
|
||||
allowed_group = permission.split(',')
|
||||
user_permission_add([app], permission='main', group=allowed_group, sync_perm=False)
|
||||
app_setting(app, 'allowed_users', delete=True)
|
||||
|
||||
|
||||
def migrate(self):
|
||||
# Check if the migration can be processed
|
||||
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")
|
||||
|
||||
# Backup LDAP and the apps settings before to do the migration
|
||||
logger.info(m18n.n("migration_0011_backup_before_migration"))
|
||||
try:
|
||||
backup_folder = "/home/yunohost.backup/premigration/" + time.strftime('%Y%m%d-%H%M%S', time.gmtime())
|
||||
os.makedirs(backup_folder, 0o750)
|
||||
os.system("systemctl stop slapd")
|
||||
os.system("cp -r --preserve /etc/ldap %s/ldap_config" % backup_folder)
|
||||
os.system("cp -r --preserve /var/lib/ldap %s/ldap_db" % backup_folder)
|
||||
os.system("cp -r --preserve /etc/yunohost/apps %s/apps_settings" % backup_folder)
|
||||
except Exception as e:
|
||||
raise YunohostError("migration_0011_can_not_backup_before_migration", error=e)
|
||||
finally:
|
||||
os.system("systemctl start slapd")
|
||||
|
||||
try:
|
||||
# Update LDAP schema restart slapd
|
||||
logger.info(m18n.n("migration_0011_update_LDAP_schema"))
|
||||
regen_conf(names=['slapd'], force=True)
|
||||
|
||||
# Update LDAP database
|
||||
self.migrate_LDAP_db()
|
||||
|
||||
# Migrate permission
|
||||
self.migrate_app_permission()
|
||||
|
||||
permission_sync_to_user()
|
||||
except Exception as e:
|
||||
logger.warn(m18n.n("migration_0011_migration_failed_trying_to_rollback"))
|
||||
os.system("systemctl stop slapd")
|
||||
os.system("rm -r /etc/ldap/slapd.d") # To be sure that we don't keep some part of the old config
|
||||
os.system("cp -r --preserve %s/ldap_config/. /etc/ldap/" % backup_folder)
|
||||
os.system("cp -r --preserve %s/ldap_db/. /var/lib/ldap/" % backup_folder)
|
||||
os.system("cp -r --preserve %s/apps_settings/. /etc/yunohost/apps/" % backup_folder)
|
||||
os.system("systemctl start slapd")
|
||||
os.system("rm -r " + backup_folder)
|
||||
logger.info(m18n.n("migration_0011_rollback_success"))
|
||||
raise
|
||||
else:
|
||||
os.system("rm -r " + backup_folder)
|
||||
|
||||
logger.info(m18n.n("migration_0011_done"))
|
527
src/yunohost/permission.py
Normal file
527
src/yunohost/permission.py
Normal file
|
@ -0,0 +1,527 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" License
|
||||
|
||||
Copyright (C) 2014 YUNOHOST.ORG
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, see http://www.gnu.org/licenses
|
||||
|
||||
"""
|
||||
|
||||
""" yunohost_permission.py
|
||||
|
||||
Manage permissions
|
||||
"""
|
||||
|
||||
import grp
|
||||
import random
|
||||
|
||||
from moulinette import m18n
|
||||
from moulinette.utils.log import getActionLogger
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.user import user_list
|
||||
from yunohost.log import is_unit_operation
|
||||
|
||||
logger = getActionLogger('yunohost.user')
|
||||
|
||||
|
||||
def user_permission_list(app=None, permission=None, username=None, group=None):
|
||||
"""
|
||||
List permission for specific application
|
||||
|
||||
Keyword argument:
|
||||
app -- an application OR sftp, xmpp (metronome), mail
|
||||
permission -- name of the permission ("main" by default)
|
||||
username -- Username to get informations
|
||||
group -- Groupname to get informations
|
||||
|
||||
"""
|
||||
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
ldap = _get_ldap_interface()
|
||||
|
||||
permission_attrs = [
|
||||
'cn',
|
||||
'groupPermission',
|
||||
'inheritPermission',
|
||||
'URL',
|
||||
]
|
||||
|
||||
# Normally app is alway defined but it should be possible to set it
|
||||
if app and not isinstance(app, list):
|
||||
app = [app]
|
||||
if permission and not isinstance(permission, list):
|
||||
permission = [permission]
|
||||
if not isinstance(username, list):
|
||||
username = [username]
|
||||
if not isinstance(group, list):
|
||||
group = [group]
|
||||
|
||||
permissions = {}
|
||||
|
||||
result = ldap.search('ou=permission,dc=yunohost,dc=org',
|
||||
'(objectclass=permissionYnh)', permission_attrs)
|
||||
|
||||
for res in result:
|
||||
try:
|
||||
permission_name, app_name = res['cn'][0].split('.')
|
||||
except:
|
||||
logger.warning(m18n.n('permission_name_not_valid', permission=res['cn'][0]))
|
||||
group_name = []
|
||||
if 'groupPermission' in res:
|
||||
for g in res['groupPermission']:
|
||||
group_name.append(g.split("=")[1].split(",")[0])
|
||||
user_name = []
|
||||
if 'inheritPermission' in res:
|
||||
for u in res['inheritPermission']:
|
||||
user_name.append(u.split("=")[1].split(",")[0])
|
||||
|
||||
# Don't show the result if the user defined a specific permission, user or group
|
||||
if app and app_name not in app:
|
||||
continue
|
||||
if permission and permission_name not in permission:
|
||||
continue
|
||||
if username[0] and not set(username) & set(user_name):
|
||||
continue
|
||||
if group[0] and not set(group) & set(group_name):
|
||||
continue
|
||||
|
||||
if app_name not in permissions:
|
||||
permissions[app_name] = {}
|
||||
|
||||
permissions[app_name][permission_name] = {'allowed_users': [], 'allowed_groups': []}
|
||||
for g in group_name:
|
||||
permissions[app_name][permission_name]['allowed_groups'].append(g)
|
||||
for u in user_name:
|
||||
permissions[app_name][permission_name]['allowed_users'].append(u)
|
||||
if 'URL' in res:
|
||||
permissions[app_name][permission_name]['URL'] = []
|
||||
for u in res['URL']:
|
||||
permissions[app_name][permission_name]['URL'].append(u)
|
||||
|
||||
return {'permissions': permissions}
|
||||
|
||||
|
||||
def user_permission_update(operation_logger, app=[], permission=None, add_username=None, add_group=None, del_username=None, del_group=None, sync_perm=True):
|
||||
"""
|
||||
Allow or Disallow a user or group to a permission for a specific application
|
||||
|
||||
Keyword argument:
|
||||
app -- an application OR sftp, xmpp (metronome), mail
|
||||
permission -- name of the permission ("main" by default)
|
||||
add_username -- Username to allow
|
||||
add_group -- Groupname to allow
|
||||
del_username -- Username to disallow
|
||||
del_group -- Groupname to disallow
|
||||
|
||||
"""
|
||||
from yunohost.hook import hook_callback
|
||||
from yunohost.user import user_group_list
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
ldap = _get_ldap_interface()
|
||||
|
||||
if permission:
|
||||
if not isinstance(permission, list):
|
||||
permission = [permission]
|
||||
else:
|
||||
permission = ["main"]
|
||||
|
||||
if add_group:
|
||||
if not isinstance(add_group, list):
|
||||
add_group = [add_group]
|
||||
else:
|
||||
add_group = []
|
||||
|
||||
if add_username:
|
||||
if not isinstance(add_username, list):
|
||||
add_username = [add_username]
|
||||
else:
|
||||
add_username = []
|
||||
|
||||
if del_group:
|
||||
if not isinstance(del_group, list):
|
||||
del_group = [del_group]
|
||||
else:
|
||||
del_group = []
|
||||
|
||||
if del_username:
|
||||
if not isinstance(del_username, list):
|
||||
del_username = [del_username]
|
||||
else:
|
||||
del_username = []
|
||||
|
||||
# Validate that the group exist
|
||||
for g in add_group:
|
||||
if g not in user_group_list(['cn'])['groups']:
|
||||
raise YunohostError('group_unknown', group=g)
|
||||
for u in add_username:
|
||||
if u not in user_list(['uid'])['users']:
|
||||
raise YunohostError('user_unknown', user=u)
|
||||
for g in del_group:
|
||||
if g not in user_group_list(['cn'])['groups']:
|
||||
raise YunohostError('group_unknown', group=g)
|
||||
for u in del_username:
|
||||
if u not in user_list(['uid'])['users']:
|
||||
raise YunohostError('user_unknown', user=u)
|
||||
|
||||
# Merge user and group (note that we consider all user as a group)
|
||||
add_group.extend(add_username)
|
||||
del_group.extend(del_username)
|
||||
|
||||
if 'all_users' in add_group or 'all_users' in del_group:
|
||||
raise YunohostError('edit_permission_with_group_all_users_not_allowed')
|
||||
|
||||
# Populate permission informations
|
||||
permission_attrs = [
|
||||
'cn',
|
||||
'groupPermission',
|
||||
]
|
||||
result = ldap.search('ou=permission,dc=yunohost,dc=org',
|
||||
'(objectclass=permissionYnh)', permission_attrs)
|
||||
result = {p['cn'][0]: p for p in result}
|
||||
|
||||
new_per_dict = {}
|
||||
|
||||
for a in app:
|
||||
for per in permission:
|
||||
permission_name = per + '.' + a
|
||||
if permission_name not in result:
|
||||
raise YunohostError('permission_not_found', permission=per, app=a)
|
||||
new_per_dict[permission_name] = set()
|
||||
if 'groupPermission' in result[permission_name]:
|
||||
new_per_dict[permission_name] = set(result[permission_name]['groupPermission'])
|
||||
|
||||
for g in del_group:
|
||||
if 'cn=all_users,ou=groups,dc=yunohost,dc=org' in new_per_dict[permission_name]:
|
||||
raise YunohostError('need_define_permission_before')
|
||||
group_name = 'cn=' + g + ',ou=groups,dc=yunohost,dc=org'
|
||||
if group_name not in new_per_dict[permission_name]:
|
||||
logger.warning(m18n.n('group_already_disallowed', permission=per, app=a, group=g))
|
||||
else:
|
||||
new_per_dict[permission_name].remove(group_name)
|
||||
|
||||
if 'cn=all_users,ou=groups,dc=yunohost,dc=org' in new_per_dict[permission_name]:
|
||||
new_per_dict[permission_name].remove('cn=all_users,ou=groups,dc=yunohost,dc=org')
|
||||
for g in add_group:
|
||||
group_name = 'cn=' + g + ',ou=groups,dc=yunohost,dc=org'
|
||||
if group_name in new_per_dict[permission_name]:
|
||||
logger.warning(m18n.n('group_already_allowed', permission=per, app=a, group=g))
|
||||
else:
|
||||
new_per_dict[permission_name].add(group_name)
|
||||
|
||||
operation_logger.start()
|
||||
|
||||
for per, val in new_per_dict.items():
|
||||
# Don't update LDAP if we update exactly the same values
|
||||
if val == set(result[per]['groupPermission'] if 'groupPermission' in result[per] else []):
|
||||
continue
|
||||
if ldap.update('cn=%s,ou=permission' % per, {'groupPermission': val}):
|
||||
p = per.split('.')
|
||||
logger.debug(m18n.n('permission_updated', permission=p[0], app=p[1]))
|
||||
else:
|
||||
raise YunohostError('permission_update_failed')
|
||||
|
||||
if sync_perm:
|
||||
permission_sync_to_user()
|
||||
|
||||
for a in app:
|
||||
allowed_users = set()
|
||||
disallowed_users = set()
|
||||
group_list = user_group_list(['member'])['groups']
|
||||
|
||||
for g in add_group:
|
||||
if 'members' in group_list[g]:
|
||||
allowed_users.union(group_list[g]['members'])
|
||||
for g in del_group:
|
||||
if 'members' in group_list[g]:
|
||||
disallowed_users.union(group_list[g]['members'])
|
||||
|
||||
allowed_users = ','.join(allowed_users)
|
||||
disallowed_users = ','.join(disallowed_users)
|
||||
if add_group:
|
||||
hook_callback('post_app_addaccess', args=[app, allowed_users])
|
||||
if del_group:
|
||||
hook_callback('post_app_removeaccess', args=[app, disallowed_users])
|
||||
|
||||
return user_permission_list(app, permission)
|
||||
|
||||
|
||||
def user_permission_clear(operation_logger, app=[], permission=None, sync_perm=True):
|
||||
"""
|
||||
Reset the permission for a specific application
|
||||
|
||||
Keyword argument:
|
||||
app -- an application OR sftp, xmpp (metronome), mail
|
||||
permission -- name of the permission ("main" by default)
|
||||
username -- Username to get informations (all by default)
|
||||
group -- Groupname to get informations (all by default)
|
||||
|
||||
"""
|
||||
from yunohost.hook import hook_callback
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
ldap = _get_ldap_interface()
|
||||
|
||||
if permission:
|
||||
if not isinstance(permission, list):
|
||||
permission = [permission]
|
||||
else:
|
||||
permission = ["main"]
|
||||
|
||||
default_permission = {'groupPermission': ['cn=all_users,ou=groups,dc=yunohost,dc=org']}
|
||||
|
||||
# Populate permission informations
|
||||
permission_attrs = [
|
||||
'cn',
|
||||
'groupPermission',
|
||||
]
|
||||
result = ldap.search('ou=permission,dc=yunohost,dc=org',
|
||||
'(objectclass=permissionYnh)', permission_attrs)
|
||||
result = {p['cn'][0]: p for p in result}
|
||||
|
||||
for a in app:
|
||||
for per in permission:
|
||||
permission_name = per + '.' + a
|
||||
if permission_name not in result:
|
||||
raise YunohostError('permission_not_found', permission=per, app=a)
|
||||
if 'groupPermission' in result[permission_name] and 'cn=all_users,ou=groups,dc=yunohost,dc=org' in result[permission_name]['groupPermission']:
|
||||
logger.warning(m18n.n('permission_already_clear', permission=per, app=a))
|
||||
continue
|
||||
if ldap.update('cn=%s,ou=permission' % permission_name, default_permission):
|
||||
logger.debug(m18n.n('permission_updated', permission=per, app=a))
|
||||
else:
|
||||
raise YunohostError('permission_update_failed')
|
||||
|
||||
permission_sync_to_user()
|
||||
|
||||
for a in app:
|
||||
permission_name = 'main.' + a
|
||||
result = ldap.search('ou=permission,dc=yunohost,dc=org',
|
||||
filter='cn=' + permission_name, attrs=['inheritPermission'])
|
||||
if result:
|
||||
allowed_users = result[0]['inheritPermission']
|
||||
new_user_list = ','.join(allowed_users)
|
||||
hook_callback('post_app_removeaccess', args=[app, new_user_list])
|
||||
|
||||
return user_permission_list(app, permission)
|
||||
|
||||
|
||||
@is_unit_operation(['permission', 'app'])
|
||||
def permission_add(operation_logger, app, permission, urls=None, default_allow=True, sync_perm=True):
|
||||
"""
|
||||
Create a new permission for a specific application
|
||||
|
||||
Keyword argument:
|
||||
app -- an application OR sftp, xmpp (metronome), mail
|
||||
permission -- name of the permission ("main" by default)
|
||||
urls -- list of urls to specify for the permission
|
||||
|
||||
"""
|
||||
from yunohost.domain import _normalize_domain_path
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
ldap = _get_ldap_interface()
|
||||
|
||||
# Validate uniqueness of permission in LDAP
|
||||
permission_name = str(permission + '.' + app) # str(...) Fix encoding issue
|
||||
conflict = ldap.get_conflict({
|
||||
'cn': permission_name
|
||||
}, base_dn='ou=permission,dc=yunohost,dc=org')
|
||||
if conflict:
|
||||
raise YunohostError('permission_already_exist', permission=permission, app=app)
|
||||
|
||||
# Get random GID
|
||||
all_gid = {x.gr_gid for x in grp.getgrall()}
|
||||
|
||||
uid_guid_found = False
|
||||
while not uid_guid_found:
|
||||
gid = str(random.randint(200, 99999))
|
||||
uid_guid_found = gid not in all_gid
|
||||
|
||||
attr_dict = {
|
||||
'objectClass': ['top', 'permissionYnh', 'posixGroup'],
|
||||
'cn': permission_name,
|
||||
'gidNumber': gid,
|
||||
}
|
||||
if default_allow:
|
||||
attr_dict['groupPermission'] = 'cn=all_users,ou=groups,dc=yunohost,dc=org'
|
||||
|
||||
if urls:
|
||||
attr_dict['URL'] = []
|
||||
for url in urls:
|
||||
domain = url[:url.index('/')]
|
||||
path = url[url.index('/'):]
|
||||
domain, path = _normalize_domain_path(domain, path)
|
||||
attr_dict['URL'].append(domain + path)
|
||||
|
||||
operation_logger.start()
|
||||
if ldap.add('cn=%s,ou=permission' % permission_name, attr_dict):
|
||||
if sync_perm:
|
||||
permission_sync_to_user()
|
||||
logger.debug(m18n.n('permission_created', permission=permission, app=app))
|
||||
return user_permission_list(app, permission)
|
||||
|
||||
raise YunohostError('permission_creation_failed')
|
||||
|
||||
|
||||
@is_unit_operation(['permission', 'app'])
|
||||
def permission_update(operation_logger, app, permission, add_url=None, remove_url=None, sync_perm=True):
|
||||
"""
|
||||
Update a permission for a specific application
|
||||
|
||||
Keyword argument:
|
||||
app -- an application OR sftp, xmpp (metronome), mail
|
||||
permission -- name of the permission ("main" by default)
|
||||
add_url -- Add a new url for a permission
|
||||
remove_url -- Remove a url for a permission
|
||||
|
||||
"""
|
||||
from yunohost.domain import _normalize_domain_path
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
ldap = _get_ldap_interface()
|
||||
|
||||
permission_name = str(permission + '.' + app) # str(...) Fix encoding issue
|
||||
|
||||
# Populate permission informations
|
||||
result = ldap.search(base='ou=permission,dc=yunohost,dc=org',
|
||||
filter='cn=' + permission_name, attrs=['URL'])
|
||||
if not result:
|
||||
raise YunohostError('permission_not_found', permission=permission, app=app)
|
||||
permission_obj = result[0]
|
||||
|
||||
if 'URL' not in permission_obj:
|
||||
permission_obj['URL'] = []
|
||||
|
||||
url = set(permission_obj['URL'])
|
||||
|
||||
if add_url:
|
||||
for u in add_url:
|
||||
domain = u[:u.index('/')]
|
||||
path = u[u.index('/'):]
|
||||
domain, path = _normalize_domain_path(domain, path)
|
||||
url.add(domain + path)
|
||||
if remove_url:
|
||||
for u in remove_url:
|
||||
domain = u[:u.index('/')]
|
||||
path = u[u.index('/'):]
|
||||
domain, path = _normalize_domain_path(domain, path)
|
||||
url.discard(domain + path)
|
||||
|
||||
if url == set(permission_obj['URL']):
|
||||
logger.warning(m18n.n('permission_update_nothing_to_do'))
|
||||
return user_permission_list(app, permission)
|
||||
|
||||
operation_logger.start()
|
||||
if ldap.update('cn=%s,ou=permission' % permission_name, {'cn': permission_name, 'URL': url}):
|
||||
if sync_perm:
|
||||
permission_sync_to_user()
|
||||
logger.debug(m18n.n('permission_updated', permission=permission, app=app))
|
||||
return user_permission_list(app, permission)
|
||||
|
||||
raise YunohostError('premission_update_failed')
|
||||
|
||||
|
||||
@is_unit_operation(['permission', 'app'])
|
||||
def permission_remove(operation_logger, app, permission, force=False, sync_perm=True):
|
||||
"""
|
||||
Remove a permission for a specific application
|
||||
|
||||
Keyword argument:
|
||||
app -- an application OR sftp, xmpp (metronome), mail
|
||||
permission -- name of the permission ("main" by default)
|
||||
|
||||
"""
|
||||
|
||||
if permission == "main" and not force:
|
||||
raise YunohostError('remove_main_permission_not_allowed')
|
||||
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
ldap = _get_ldap_interface()
|
||||
|
||||
operation_logger.start()
|
||||
if not ldap.remove('cn=%s,ou=permission' % str(permission + '.' + app)):
|
||||
raise YunohostError('permission_deletion_failed', permission=permission, app=app)
|
||||
if sync_perm:
|
||||
permission_sync_to_user()
|
||||
logger.debug(m18n.n('permission_deleted', permission=permission, app=app))
|
||||
|
||||
|
||||
def permission_sync_to_user(force=False):
|
||||
"""
|
||||
Sychronise the inheritPermission attribut in the permission object from the
|
||||
user<->group link and the group<->permission link
|
||||
|
||||
Keyword argument:
|
||||
force -- Force to recreate all attributes. Used generally with the
|
||||
backup which uses "slapadd" which doesnt' use the memberOf overlay.
|
||||
Note that by removing all value and adding a new time, we force the
|
||||
overlay to update all attributes
|
||||
"""
|
||||
# Note that a LDAP operation with the same value that is in LDAP crash SLAP.
|
||||
# So we need to check before each ldap operation that we really change something in LDAP
|
||||
import os
|
||||
from yunohost.app import app_ssowatconf
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
ldap = _get_ldap_interface()
|
||||
|
||||
permission_attrs = [
|
||||
'cn',
|
||||
'member',
|
||||
]
|
||||
group_info = ldap.search('ou=groups,dc=yunohost,dc=org',
|
||||
'(objectclass=groupOfNamesYnh)', permission_attrs)
|
||||
group_info = {g['cn'][0]: g for g in group_info}
|
||||
|
||||
for per in ldap.search('ou=permission,dc=yunohost,dc=org',
|
||||
'(objectclass=permissionYnh)',
|
||||
['cn', 'inheritPermission', 'groupPermission', 'memberUid']):
|
||||
if 'groupPermission' not in per:
|
||||
continue
|
||||
user_permission = set()
|
||||
for group in per['groupPermission']:
|
||||
group = group.split("=")[1].split(",")[0]
|
||||
if 'member' not in group_info[group]:
|
||||
continue
|
||||
for user in group_info[group]['member']:
|
||||
user_permission.add(user)
|
||||
|
||||
if 'inheritPermission' not in per:
|
||||
per['inheritPermission'] = []
|
||||
if 'memberUid' not in per:
|
||||
per['memberUid'] = []
|
||||
|
||||
uid_val = [v.split("=")[1].split(",")[0] for v in user_permission]
|
||||
if user_permission == set(per['inheritPermission']) and set(uid_val) == set(per['memberUid']) and not force:
|
||||
continue
|
||||
inheritPermission = {'inheritPermission': user_permission, 'memberUid': uid_val}
|
||||
if force:
|
||||
if per['groupPermission']:
|
||||
if not ldap.update('cn=%s,ou=permission' % per['cn'][0], {'groupPermission': []}):
|
||||
raise YunohostError('permission_update_failed_clear')
|
||||
if not ldap.update('cn=%s,ou=permission' % per['cn'][0], {'groupPermission': per['groupPermission']}):
|
||||
raise YunohostError('permission_update_failed_populate')
|
||||
if per['inheritPermission']:
|
||||
if not ldap.update('cn=%s,ou=permission' % per['cn'][0], {'inheritPermission': []}):
|
||||
raise YunohostError('permission_update_failed_clear')
|
||||
if user_permission:
|
||||
if not ldap.update('cn=%s,ou=permission' % per['cn'][0], inheritPermission):
|
||||
raise YunohostError('permission_update_failed')
|
||||
else:
|
||||
if not ldap.update('cn=%s,ou=permission' % per['cn'][0], inheritPermission):
|
||||
raise YunohostError('permission_update_failed')
|
||||
logger.debug(m18n.n('permission_generated'))
|
||||
|
||||
app_ssowatconf()
|
||||
|
||||
# Reload unscd, otherwise the group ain't propagated to the LDAP database
|
||||
os.system('nscd --invalidate=passwd')
|
||||
os.system('nscd --invalidate=group')
|
|
@ -4,7 +4,6 @@ from yunohost.utils.error import YunohostError
|
|||
from yunohost.app import app_install, app_remove
|
||||
from yunohost.domain import _get_maindomain, domain_url_available, _normalize_domain_path
|
||||
|
||||
|
||||
# Get main domain
|
||||
maindomain = _get_maindomain()
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@ from yunohost.app import _is_installed
|
|||
from yunohost.backup import backup_create, backup_restore, backup_list, backup_info, backup_delete, _recursive_umount
|
||||
from yunohost.domain import _get_maindomain
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.user import user_permission_list
|
||||
from yunohost.tests.test_permission import check_LDAP_db_integrity, check_permission_for_apps
|
||||
|
||||
# Get main domain
|
||||
maindomain = ""
|
||||
|
@ -72,6 +74,18 @@ def teardown_function(function):
|
|||
shutil.rmtree("/opt/test_backup_output_directory")
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def check_LDAP_db_integrity_call():
|
||||
check_LDAP_db_integrity()
|
||||
yield
|
||||
check_LDAP_db_integrity()
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def check_permission_for_apps_call():
|
||||
check_permission_for_apps()
|
||||
yield
|
||||
check_permission_for_apps()
|
||||
|
||||
#
|
||||
# Helpers #
|
||||
#
|
||||
|
@ -517,6 +531,7 @@ def _test_backup_and_restore_app(app):
|
|||
# Uninstall the app
|
||||
app_remove(app)
|
||||
assert not app_is_installed(app)
|
||||
assert app not in user_permission_list()['permissions']
|
||||
|
||||
# Restore the app
|
||||
backup_restore(system=None, name=archives[0],
|
||||
|
@ -524,6 +539,11 @@ def _test_backup_and_restore_app(app):
|
|||
|
||||
assert app_is_installed(app)
|
||||
|
||||
# Check permission
|
||||
per_list = user_permission_list()['permissions']
|
||||
assert app in per_list
|
||||
assert "main" in per_list[app]
|
||||
|
||||
#
|
||||
# Some edge cases #
|
||||
#
|
||||
|
|
419
src/yunohost/tests/test_permission.py
Normal file
419
src/yunohost/tests/test_permission.py
Normal file
|
@ -0,0 +1,419 @@
|
|||
import pytest
|
||||
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.app import app_install, app_remove, app_change_url, app_list
|
||||
from yunohost.user import user_list, user_create, user_permission_list, user_delete, user_group_list, user_group_delete, user_permission_add, user_permission_remove, user_permission_clear
|
||||
from yunohost.permission import permission_add, permission_update, permission_remove
|
||||
from yunohost.domain import _get_maindomain
|
||||
from yunohost.utils.error import YunohostError
|
||||
|
||||
# Get main domain
|
||||
maindomain = _get_maindomain()
|
||||
|
||||
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":
|
||||
user_group_delete(g)
|
||||
|
||||
for a, per in user_permission_list()['permissions'].items():
|
||||
if a in ['wiki', 'blog', 'site']:
|
||||
for p in per:
|
||||
permission_remove(a, p, force=True, sync_perm=False)
|
||||
|
||||
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_add("wiki", "main", [maindomain + "/wiki"], sync_perm=False)
|
||||
permission_add("blog", "main", sync_perm=False)
|
||||
|
||||
user_permission_add(["blog"], "main", group="alice")
|
||||
|
||||
def teardown_function(function):
|
||||
clean_user_groups_permission()
|
||||
try:
|
||||
app_remove("permissions_app")
|
||||
except:
|
||||
pass
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def check_LDAP_db_integrity_call():
|
||||
check_LDAP_db_integrity()
|
||||
yield
|
||||
check_LDAP_db_integrity()
|
||||
|
||||
def check_LDAP_db_integrity():
|
||||
# Here we check that all attributes in all object are sychronized.
|
||||
# Here is the list of attributes per object:
|
||||
# user : memberOf, permission
|
||||
# group : member, permission
|
||||
# permission : groupPermission, inheritPermission
|
||||
#
|
||||
# The idea is to check that all attributes on all sides of object are sychronized.
|
||||
# One part should be done automatically by the "memberOf" overlay of LDAP.
|
||||
# The other part is done by the the "permission_sync_to_user" function of the permission module
|
||||
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
ldap = _get_ldap_interface()
|
||||
|
||||
user_search = ldap.search('ou=users,dc=yunohost,dc=org',
|
||||
'(&(objectclass=person)(!(uid=root))(!(uid=nobody)))',
|
||||
['uid', 'memberOf', 'permission'])
|
||||
group_search = ldap.search('ou=groups,dc=yunohost,dc=org',
|
||||
'(objectclass=groupOfNamesYnh)',
|
||||
['cn', 'member', 'memberUid', 'permission'])
|
||||
permission_search = ldap.search('ou=permission,dc=yunohost,dc=org',
|
||||
'(objectclass=permissionYnh)',
|
||||
['cn', 'groupPermission', 'inheritPermission', 'memberUid'])
|
||||
|
||||
user_map = {u['uid'][0]: u for u in user_search}
|
||||
group_map = {g['cn'][0]: g for g in group_search}
|
||||
permission_map = {p['cn'][0]: p for p in permission_search}
|
||||
|
||||
for user in user_search:
|
||||
user_dn = 'uid=' + user['uid'][0] + ',ou=users,dc=yunohost,dc=org'
|
||||
group_list = [m.split("=")[1].split(",")[0] for m in user['memberOf']]
|
||||
permission_list = []
|
||||
if 'permission' in user:
|
||||
permission_list = [m.split("=")[1].split(",")[0] for m in user['permission']]
|
||||
|
||||
for group in group_list:
|
||||
assert user_dn in group_map[group]['member']
|
||||
for permission in permission_list:
|
||||
assert user_dn in permission_map[permission]['inheritPermission']
|
||||
|
||||
for permission in permission_search:
|
||||
permission_dn = 'cn=' + permission['cn'][0] + ',ou=permission,dc=yunohost,dc=org'
|
||||
user_list = []
|
||||
group_list = []
|
||||
if 'inheritPermission' in permission:
|
||||
user_list = [m.split("=")[1].split(",")[0] for m in permission['inheritPermission']]
|
||||
assert set(user_list) == set(permission['memberUid'])
|
||||
if 'groupPermission' in permission:
|
||||
group_list = [m.split("=")[1].split(",")[0] for m in permission['groupPermission']]
|
||||
|
||||
for user in user_list:
|
||||
assert permission_dn in user_map[user]['permission']
|
||||
for group in group_list:
|
||||
assert permission_dn in group_map[group]['permission']
|
||||
if 'member' in group_map[group]:
|
||||
user_list_in_group = [m.split("=")[1].split(",")[0] for m in group_map[group]['member']]
|
||||
assert set(user_list_in_group) <= set(user_list)
|
||||
|
||||
for group in group_search:
|
||||
group_dn = 'cn=' + group['cn'][0] + ',ou=groups,dc=yunohost,dc=org'
|
||||
user_list = []
|
||||
permission_list = []
|
||||
if 'member' in group:
|
||||
user_list = [m.split("=")[1].split(",")[0] for m in group['member']]
|
||||
if group['cn'][0] in user_list:
|
||||
# If it's the main group of the user it's normal that it is not in the memberUid
|
||||
g_list = list(user_list)
|
||||
g_list.remove(group['cn'][0])
|
||||
if 'memberUid' in group:
|
||||
assert set(g_list) == set(group['memberUid'])
|
||||
else:
|
||||
assert g_list == []
|
||||
else:
|
||||
assert set(user_list) == set(group['memberUid'])
|
||||
if 'permission' in group:
|
||||
permission_list = [m.split("=")[1].split(",")[0] for m in group['permission']]
|
||||
|
||||
for user in user_list:
|
||||
assert group_dn in user_map[user]['memberOf']
|
||||
for permission in permission_list:
|
||||
assert group_dn in permission_map[permission]['groupPermission']
|
||||
if 'inheritPermission' in permission_map:
|
||||
allowed_user_list = [m.split("=")[1].split(",")[0] for m in permission_map[permission]['inheritPermission']]
|
||||
assert set(user_list) <= set(allowed_user_list)
|
||||
|
||||
|
||||
def check_permission_for_apps():
|
||||
# We check that the for each installed apps we have at last the "main" permission
|
||||
# and we don't have any permission linked to no apps. The only exception who is not liked to an app
|
||||
# is mail, metronome, and sftp
|
||||
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
ldap = _get_ldap_interface()
|
||||
permission_search = ldap.search('ou=permission,dc=yunohost,dc=org',
|
||||
'(objectclass=permissionYnh)',
|
||||
['cn', 'groupPermission', 'inheritPermission', 'memberUid'])
|
||||
|
||||
installed_apps = {app['id'] for app in app_list(installed=True)['apps']}
|
||||
permission_list_set = {permission['cn'][0].split(".")[1] for permission in permission_search}
|
||||
|
||||
extra_service_permission = set(['mail', 'metronome'])
|
||||
if 'sftp' in permission_list_set:
|
||||
extra_service_permission.add('sftp')
|
||||
assert installed_apps == permission_list_set - extra_service_permission
|
||||
|
||||
#
|
||||
# List functions
|
||||
#
|
||||
|
||||
def test_list_permission():
|
||||
res = user_permission_list()['permissions']
|
||||
|
||||
assert "wiki" in res
|
||||
assert "main" in res['wiki']
|
||||
assert "blog" in res
|
||||
assert "main" in res['blog']
|
||||
assert "mail" in res
|
||||
assert "main" in res['mail']
|
||||
assert "metronome" in res
|
||||
assert "main" in res['metronome']
|
||||
assert ["all_users"] == res['wiki']['main']['allowed_groups']
|
||||
assert ["alice"] == res['blog']['main']['allowed_groups']
|
||||
assert set(["alice", "bob"]) == set(res['wiki']['main']['allowed_users'])
|
||||
assert ["alice"] == res['blog']['main']['allowed_users']
|
||||
assert [maindomain + "/wiki"] == res['wiki']['main']['URL']
|
||||
|
||||
#
|
||||
# Create - Remove functions
|
||||
#
|
||||
|
||||
def test_add_permission_1():
|
||||
permission_add("site", "test")
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert "site" in res
|
||||
assert "test" in res['site']
|
||||
assert "all_users" in res['site']['test']['allowed_groups']
|
||||
assert set(["alice", "bob"]) == set(res['site']['test']['allowed_users'])
|
||||
|
||||
def test_add_permission_2():
|
||||
permission_add("site", "main", default_allow=False)
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert "site" in res
|
||||
assert "main" in res['site']
|
||||
assert [] == res['site']['main']['allowed_groups']
|
||||
assert [] == res['site']['main']['allowed_users']
|
||||
|
||||
def test_remove_permission():
|
||||
permission_remove("wiki", "main", force=True)
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert "wiki" not in res
|
||||
|
||||
#
|
||||
# Error on create - remove function
|
||||
#
|
||||
|
||||
def test_add_bad_permission():
|
||||
# Create permission with same name
|
||||
with pytest.raises(YunohostError):
|
||||
permission_add("wiki", "main")
|
||||
|
||||
def test_remove_bad_permission():
|
||||
# Remove not existant permission
|
||||
with pytest.raises(MoulinetteError):
|
||||
permission_remove("non_exit", "main", force=True)
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert "wiki" in res
|
||||
assert "main" in res['wiki']
|
||||
assert "blog" in res
|
||||
assert "main" in res['blog']
|
||||
assert "mail" in res
|
||||
assert "main" in res ['mail']
|
||||
assert "metronome" in res
|
||||
assert "main" in res['metronome']
|
||||
|
||||
def test_remove_main_permission():
|
||||
with pytest.raises(YunohostError):
|
||||
permission_remove("blog", "main")
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert "mail" in res
|
||||
assert "main" in res['mail']
|
||||
|
||||
#
|
||||
# Update functions
|
||||
#
|
||||
|
||||
# user side functions
|
||||
|
||||
def test_allow_first_group():
|
||||
# Remove permission to all_users and define per users
|
||||
user_permission_add(["wiki"], "main", group="alice")
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert ['alice'] == res['wiki']['main']['allowed_users']
|
||||
assert ['alice'] == res['wiki']['main']['allowed_groups']
|
||||
|
||||
def test_allow_other_group():
|
||||
# Allow new user in a permission
|
||||
user_permission_add(["blog"], "main", group="bob")
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert set(["alice", "bob"]) == set(res['blog']['main']['allowed_users'])
|
||||
assert set(["alice", "bob"]) == set(res['blog']['main']['allowed_groups'])
|
||||
|
||||
def test_disallow_group_1():
|
||||
# Disallow a user in a permission
|
||||
user_permission_remove(["blog"], "main", group="alice")
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert [] == res['blog']['main']['allowed_users']
|
||||
assert [] == res['blog']['main']['allowed_groups']
|
||||
|
||||
def test_allow_group_1():
|
||||
# Allow a user when he is already allowed
|
||||
user_permission_add(["blog"], "main", group="alice")
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert ["alice"] == res['blog']['main']['allowed_users']
|
||||
assert ["alice"] == res['blog']['main']['allowed_groups']
|
||||
|
||||
def test_disallow_group_1():
|
||||
# Disallow a user when he is already disallowed
|
||||
user_permission_remove(["blog"], "main", group="bob")
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert ["alice"] == res['blog']['main']['allowed_users']
|
||||
assert ["alice"] == res['blog']['main']['allowed_groups']
|
||||
|
||||
def test_reset_permission():
|
||||
# Reset permission
|
||||
user_permission_clear(["blog"], "main")
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert set(["alice", "bob"]) == set(res['blog']['main']['allowed_users'])
|
||||
assert ["all_users"] == res['blog']['main']['allowed_groups']
|
||||
|
||||
# internal functions
|
||||
|
||||
def test_add_url_1():
|
||||
# Add URL in permission which hasn't any URL defined
|
||||
permission_update("blog", "main", add_url=[maindomain + "/testA"])
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert [maindomain + "/testA"] == res['blog']['main']['URL']
|
||||
|
||||
def test_add_url_2():
|
||||
# Add a second URL in a permission
|
||||
permission_update("wiki", "main", add_url=[maindomain + "/testA"])
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert set([maindomain + "/testA", maindomain + "/wiki"]) == set(res['wiki']['main']['URL'])
|
||||
|
||||
def test_remove_url_1():
|
||||
permission_update("wiki", "main", remove_url=[maindomain + "/wiki"])
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert 'URL' not in res['wiki']['main']
|
||||
|
||||
def test_add_url_3():
|
||||
# Add a url already added
|
||||
permission_update("wiki", "main", add_url=[maindomain + "/wiki"])
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert [maindomain + "/wiki"] == res['wiki']['main']['URL']
|
||||
|
||||
def test_remove_url_2():
|
||||
# Remove a url not added (with a permission which contain some URL)
|
||||
permission_update("wiki", "main", remove_url=[maindomain + "/not_exist"])
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert [maindomain + "/wiki"] == res['wiki']['main']['URL']
|
||||
|
||||
def test_remove_url_2():
|
||||
# Remove a url not added (with a permission which contain no URL)
|
||||
permission_update("blog", "main", remove_url=[maindomain + "/not_exist"])
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert 'URL' not in res['blog']['main']
|
||||
|
||||
#
|
||||
# Error on update function
|
||||
#
|
||||
|
||||
def test_disallow_bad_group_1():
|
||||
# Disallow a group when the group all_users is allowed
|
||||
with pytest.raises(YunohostError):
|
||||
user_permission_remove("wiki", "main", group="alice")
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert ["all_users"] == res['wiki']['main']['allowed_groups']
|
||||
assert set(["alice", "bob"]) == set(res['wiki']['main']['allowed_users'])
|
||||
|
||||
def test_allow_bad_user():
|
||||
# Allow a non existant group
|
||||
with pytest.raises(YunohostError):
|
||||
user_permission_add(["blog"], "main", group="not_exist")
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert ["alice"] == res['blog']['main']['allowed_groups']
|
||||
assert ["alice"] == res['blog']['main']['allowed_users']
|
||||
|
||||
def test_disallow_bad_group_2():
|
||||
# Disallow a non existant group
|
||||
with pytest.raises(YunohostError):
|
||||
user_permission_remove(["blog"], "main", group="not_exist")
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert ["alice"] == res['blog']['main']['allowed_groups']
|
||||
assert ["alice"] == res['blog']['main']['allowed_users']
|
||||
|
||||
def test_allow_bad_permission_1():
|
||||
# Allow a user to a non existant permission
|
||||
with pytest.raises(YunohostError):
|
||||
user_permission_add(["wiki"], "not_exit", group="alice")
|
||||
|
||||
def test_allow_bad_permission_2():
|
||||
# Allow a user to a non existant permission
|
||||
with pytest.raises(YunohostError):
|
||||
user_permission_add(["not_exit"], "main", group="alice")
|
||||
|
||||
#
|
||||
# Application interaction
|
||||
#
|
||||
|
||||
def test_install_app():
|
||||
app_install("./tests/apps/permissions_app_ynh",
|
||||
args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True)
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert "permissions_app" in res
|
||||
assert "main" in res['permissions_app']
|
||||
assert [maindomain + "/urlpermissionapp"] == res['permissions_app']['main']['URL']
|
||||
assert [maindomain + "/urlpermissionapp/admin"] == res['permissions_app']['admin']['URL']
|
||||
assert [maindomain + "/urlpermissionapp/dev"] == res['permissions_app']['dev']['URL']
|
||||
|
||||
assert ["all_users"] == res['permissions_app']['main']['allowed_groups']
|
||||
assert set(["alice", "bob"]) == set(res['permissions_app']['main']['allowed_users'])
|
||||
|
||||
assert ["alice"] == res['permissions_app']['admin']['allowed_groups']
|
||||
assert ["alice"] == res['permissions_app']['admin']['allowed_users']
|
||||
|
||||
assert ["all_users"] == res['permissions_app']['dev']['allowed_groups']
|
||||
assert set(["alice", "bob"]) == set(res['permissions_app']['dev']['allowed_users'])
|
||||
|
||||
def test_remove_app():
|
||||
app_install("./tests/apps/permissions_app_ynh",
|
||||
args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True)
|
||||
app_remove("permissions_app")
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert "permissions_app" not in res
|
||||
|
||||
def test_change_url():
|
||||
app_install("./tests/apps/permissions_app_ynh",
|
||||
args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True)
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert [maindomain + "/urlpermissionapp"] == res['permissions_app']['main']['URL']
|
||||
assert [maindomain + "/urlpermissionapp/admin"] == res['permissions_app']['admin']['URL']
|
||||
assert [maindomain + "/urlpermissionapp/dev"] == res['permissions_app']['dev']['URL']
|
||||
|
||||
app_change_url("permissions_app", maindomain, "/newchangeurl")
|
||||
|
||||
res = user_permission_list()['permissions']
|
||||
assert [maindomain + "/newchangeurl"] == res['permissions_app']['main']['URL']
|
||||
assert [maindomain + "/newchangeurl/admin"] == res['permissions_app']['admin']['URL']
|
||||
assert [maindomain + "/newchangeurl/dev"] == res['permissions_app']['dev']['URL']
|
210
src/yunohost/tests/test_user-group.py
Normal file
210
src/yunohost/tests/test_user-group.py
Normal file
|
@ -0,0 +1,210 @@
|
|||
import pytest
|
||||
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.user import user_list, user_info, user_group_list, user_create, user_delete, user_update, user_group_add, user_group_delete, user_group_update, user_group_info
|
||||
from yunohost.domain import _get_maindomain
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.tests.test_permission import check_LDAP_db_integrity
|
||||
|
||||
# Get main domain
|
||||
maindomain = _get_maindomain()
|
||||
|
||||
def clean_user_groups():
|
||||
for u in user_list()['users']:
|
||||
user_delete(u)
|
||||
|
||||
for g in user_group_list()['groups']:
|
||||
if g != "all_users":
|
||||
user_group_delete(g)
|
||||
|
||||
def setup_function(function):
|
||||
clean_user_groups()
|
||||
|
||||
user_create("alice", "Alice", "White", "alice@" + maindomain, "test123Ynh")
|
||||
user_create("bob", "Bob", "Snow", "bob@" + maindomain, "test123Ynh")
|
||||
user_create("jack", "Jack", "Black", "jack@" + maindomain, "test123Ynh")
|
||||
|
||||
user_group_add("dev")
|
||||
user_group_add("apps")
|
||||
user_group_update("dev", add_user=["alice"])
|
||||
user_group_update("apps", add_user=["bob"])
|
||||
|
||||
def teardown_function(function):
|
||||
clean_user_groups()
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def check_LDAP_db_integrity_call():
|
||||
check_LDAP_db_integrity()
|
||||
yield
|
||||
check_LDAP_db_integrity()
|
||||
|
||||
#
|
||||
# List functions
|
||||
#
|
||||
|
||||
def test_list_users():
|
||||
res = user_list()['users']
|
||||
|
||||
assert "alice" in res
|
||||
assert "bob" in res
|
||||
assert "jack" in res
|
||||
|
||||
def test_list_groups():
|
||||
res = user_group_list()['groups']
|
||||
|
||||
assert "all_users" in res
|
||||
assert "alice" in res
|
||||
assert "bob" in res
|
||||
assert "jack" in res
|
||||
for u in ["alice", "bob", "jack"]:
|
||||
assert u in res
|
||||
assert u in res[u]['members']
|
||||
assert u in res["all_users"]['members']
|
||||
|
||||
#
|
||||
# Create - Remove functions
|
||||
#
|
||||
|
||||
def test_create_user():
|
||||
user_create("albert", "Albert", "Good", "alber@" + maindomain, "test123Ynh")
|
||||
|
||||
group_res = user_group_list()['groups']
|
||||
assert "albert" in user_list()['users']
|
||||
assert "albert" in group_res
|
||||
assert "albert" in group_res['albert']['members']
|
||||
assert "albert" in group_res['all_users']['members']
|
||||
|
||||
def test_del_user():
|
||||
user_delete("alice")
|
||||
|
||||
group_res = user_group_list()['groups']
|
||||
assert "alice" not in user_list()
|
||||
assert "alice" not in group_res
|
||||
assert "alice" not in group_res['all_users']['members']
|
||||
|
||||
def test_add_group():
|
||||
user_group_add("adminsys")
|
||||
|
||||
group_res = user_group_list()['groups']
|
||||
assert "adminsys" in group_res
|
||||
assert "members" not in group_res['adminsys']
|
||||
|
||||
def test_del_group():
|
||||
user_group_delete("dev")
|
||||
|
||||
group_res = user_group_list()['groups']
|
||||
assert "dev" not in group_res
|
||||
|
||||
#
|
||||
# Error on create / remove function
|
||||
#
|
||||
|
||||
def test_add_bad_user_1():
|
||||
# Check email already exist
|
||||
with pytest.raises(MoulinetteError):
|
||||
user_create("alice2", "Alice", "White", "alice@" + maindomain, "test123Ynh")
|
||||
|
||||
def test_add_bad_user_2():
|
||||
# Check to short password
|
||||
with pytest.raises(MoulinetteError):
|
||||
user_create("other", "Alice", "White", "other@" + maindomain, "12")
|
||||
|
||||
def test_add_bad_user_3():
|
||||
# Check user already exist
|
||||
with pytest.raises(MoulinetteError):
|
||||
user_create("alice", "Alice", "White", "other@" + maindomain, "test123Ynh")
|
||||
|
||||
def test_del_bad_user_1():
|
||||
# Check user not found
|
||||
with pytest.raises(MoulinetteError):
|
||||
user_delete("not_exit")
|
||||
|
||||
def test_add_bad_group_1():
|
||||
# Check groups already exist with special group "all_users"
|
||||
with pytest.raises(YunohostError):
|
||||
user_group_add("all_users")
|
||||
|
||||
def test_add_bad_group_2():
|
||||
# Check groups already exist (for standard groups)
|
||||
with pytest.raises(MoulinetteError):
|
||||
user_group_add("dev")
|
||||
|
||||
def test_del_bad_group_1():
|
||||
# Check not allowed to remove this groups
|
||||
with pytest.raises(YunohostError):
|
||||
user_group_delete("all_users")
|
||||
|
||||
def test_del_bad_group_2():
|
||||
# Check groups not found
|
||||
with pytest.raises(MoulinetteError):
|
||||
user_group_delete("not_exit")
|
||||
|
||||
#
|
||||
# Update function
|
||||
#
|
||||
|
||||
def test_update_user_1():
|
||||
user_update("alice", firstname="NewName", lastname="NewLast")
|
||||
|
||||
info = user_info("alice")
|
||||
assert "NewName" == info['firstname']
|
||||
assert "NewLast" == info['lastname']
|
||||
|
||||
def test_update_group_1():
|
||||
user_group_update("dev", add_user=["bob"])
|
||||
|
||||
group_res = user_group_list()['groups']
|
||||
assert set(["alice", "bob"]) == set(group_res['dev']['members'])
|
||||
|
||||
def test_update_group_2():
|
||||
# Try to add a user in a group when the user is already in
|
||||
user_group_update("apps", add_user=["bob"])
|
||||
|
||||
group_res = user_group_list()['groups']
|
||||
assert ["bob"] == group_res['apps']['members']
|
||||
|
||||
def test_update_group_3():
|
||||
# Try to remove a user in a group
|
||||
user_group_update("apps", remove_user=["bob"])
|
||||
|
||||
group_res = user_group_list()['groups']
|
||||
assert "members" not in group_res['apps']
|
||||
|
||||
def test_update_group_4():
|
||||
# Try to remove a user in a group when it is not already in
|
||||
user_group_update("apps", remove_user=["jack"])
|
||||
|
||||
group_res = user_group_list()['groups']
|
||||
assert ["bob"] == group_res['apps']['members']
|
||||
|
||||
#
|
||||
# Error on update functions
|
||||
#
|
||||
|
||||
def test_bad_update_user_1():
|
||||
# Check user not found
|
||||
with pytest.raises(YunohostError):
|
||||
user_update("not_exit", firstname="NewName", lastname="NewLast")
|
||||
|
||||
|
||||
def bad_update_group_1():
|
||||
# Check groups not found
|
||||
with pytest.raises(YunohostError):
|
||||
user_group_update("not_exit", add_user=["alice"])
|
||||
|
||||
def test_bad_update_group_2():
|
||||
# Check remove user in groups "all_users" not allowed
|
||||
with pytest.raises(YunohostError):
|
||||
user_group_update("all_users", remove_user=["alice"])
|
||||
|
||||
def test_bad_update_group_3():
|
||||
# Check remove user in it own group not allowed
|
||||
with pytest.raises(YunohostError):
|
||||
user_group_update("alice", remove_user=["alice"])
|
||||
|
||||
def test_bad_update_group_1():
|
||||
# Check add bad user in group
|
||||
with pytest.raises(YunohostError):
|
||||
user_group_update("dev", add_user=["not_exist"])
|
||||
|
||||
assert "not_exist" not in user_group_list()["groups"]["dev"]
|
|
@ -82,6 +82,12 @@ def tools_ldapinit():
|
|||
except Exception as e:
|
||||
logger.warn("Error when trying to inject '%s' -> '%s' into ldap: %s" % (rdn, attr_dict, e))
|
||||
|
||||
for rdn, attr_dict in ldap_map['depends_children'].items():
|
||||
try:
|
||||
ldap.add(rdn, attr_dict)
|
||||
except Exception as e:
|
||||
logger.warn("Error when trying to inject '%s' -> '%s' into ldap: %s" % (rdn, attr_dict, e))
|
||||
|
||||
admin_dict = {
|
||||
'cn': 'admin',
|
||||
'uid': 'admin',
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
import os
|
||||
import re
|
||||
import pwd
|
||||
import grp
|
||||
import json
|
||||
import crypt
|
||||
import random
|
||||
|
@ -117,7 +118,6 @@ def user_create(operation_logger, username, firstname, lastname, mail, password,
|
|||
"""
|
||||
from yunohost.domain import domain_list, _get_maindomain
|
||||
from yunohost.hook import hook_callback
|
||||
from yunohost.app import app_ssowatconf
|
||||
from yunohost.utils.password import assert_password_is_strong_enough
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
|
||||
|
@ -129,7 +129,8 @@ def user_create(operation_logger, username, firstname, lastname, mail, password,
|
|||
# Validate uniqueness of username and mail in LDAP
|
||||
ldap.validate_uniqueness({
|
||||
'uid': username,
|
||||
'mail': mail
|
||||
'mail': mail,
|
||||
'cn': username
|
||||
})
|
||||
|
||||
# Validate uniqueness of username in system users
|
||||
|
@ -156,7 +157,7 @@ def user_create(operation_logger, username, firstname, lastname, mail, password,
|
|||
|
||||
# Get random UID/GID
|
||||
all_uid = {x.pw_uid for x in pwd.getpwall()}
|
||||
all_gid = {x.pw_gid for x in pwd.getpwall()}
|
||||
all_gid = {x.gr_gid for x in grp.getgrall()}
|
||||
|
||||
uid_guid_found = False
|
||||
while not uid_guid_found:
|
||||
|
@ -166,7 +167,7 @@ def user_create(operation_logger, username, firstname, lastname, mail, password,
|
|||
# Adapt values for LDAP
|
||||
fullname = '%s %s' % (firstname, lastname)
|
||||
attr_dict = {
|
||||
'objectClass': ['mailAccount', 'inetOrgPerson', 'posixAccount'],
|
||||
'objectClass': ['mailAccount', 'inetOrgPerson', 'posixAccount', 'userPermissionYnh'],
|
||||
'givenName': firstname,
|
||||
'sn': lastname,
|
||||
'displayName': fullname,
|
||||
|
@ -207,25 +208,27 @@ def user_create(operation_logger, username, firstname, lastname, mail, password,
|
|||
# Invalidate passwd to take user creation into account
|
||||
subprocess.call(['nscd', '-i', 'passwd'])
|
||||
|
||||
# Update SFTP user group
|
||||
memberlist = ldap.search(filter='cn=sftpusers', attrs=['memberUid'])[0]['memberUid']
|
||||
memberlist.append(username)
|
||||
if ldap.update('cn=sftpusers,ou=groups', {'memberUid': memberlist}):
|
||||
try:
|
||||
# Attempt to create user home folder
|
||||
subprocess.check_call(
|
||||
['su', '-', username, '-c', "''"])
|
||||
except subprocess.CalledProcessError:
|
||||
if not os.path.isdir('/home/{0}'.format(username)):
|
||||
logger.warning(m18n.n('user_home_creation_failed'),
|
||||
exc_info=1)
|
||||
app_ssowatconf()
|
||||
# TODO: Send a welcome mail to user
|
||||
logger.success(m18n.n('user_created'))
|
||||
hook_callback('post_user_create',
|
||||
args=[username, mail, password, firstname, lastname])
|
||||
try:
|
||||
# Attempt to create user home folder
|
||||
subprocess.check_call(
|
||||
['su', '-', username, '-c', "''"])
|
||||
except subprocess.CalledProcessError:
|
||||
if not os.path.isdir('/home/{0}'.format(username)):
|
||||
logger.warning(m18n.n('user_home_creation_failed'),
|
||||
exc_info=1)
|
||||
|
||||
return {'fullname': fullname, 'username': username, 'mail': mail}
|
||||
# Create group for user and add to group 'all_users'
|
||||
user_group_add(groupname=username, gid=uid, sync_perm=False)
|
||||
user_group_update(groupname=username, add_user=username, force=True, sync_perm=False)
|
||||
user_group_update(groupname='all_users', add_user=username, force=True, sync_perm=True)
|
||||
|
||||
# TODO: Send a welcome mail to user
|
||||
logger.success(m18n.n('user_created'))
|
||||
|
||||
hook_callback('post_user_create',
|
||||
args=[username, mail, password, firstname, lastname])
|
||||
|
||||
return {'fullname': fullname, 'username': username, 'mail': mail}
|
||||
|
||||
raise YunohostError('user_creation_failed')
|
||||
|
||||
|
@ -240,7 +243,6 @@ def user_delete(operation_logger, username, purge=False):
|
|||
purge
|
||||
|
||||
"""
|
||||
from yunohost.app import app_ssowatconf
|
||||
from yunohost.hook import hook_callback
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
|
||||
|
@ -251,20 +253,24 @@ def user_delete(operation_logger, username, purge=False):
|
|||
# Invalidate passwd to take user deletion into account
|
||||
subprocess.call(['nscd', '-i', 'passwd'])
|
||||
|
||||
# Update SFTP user group
|
||||
memberlist = ldap.search(filter='cn=sftpusers', attrs=['memberUid'])[0]['memberUid']
|
||||
try:
|
||||
memberlist.remove(username)
|
||||
except:
|
||||
pass
|
||||
if ldap.update('cn=sftpusers,ou=groups', {'memberUid': memberlist}):
|
||||
if purge:
|
||||
subprocess.call(['rm', '-rf', '/home/{0}'.format(username)])
|
||||
subprocess.call(['rm', '-rf', '/var/mail/{0}'.format(username)])
|
||||
if purge:
|
||||
subprocess.call(['rm', '-rf', '/home/{0}'.format(username)])
|
||||
subprocess.call(['rm', '-rf', '/var/mail/{0}'.format(username)])
|
||||
else:
|
||||
raise YunohostError('user_deletion_failed')
|
||||
|
||||
app_ssowatconf()
|
||||
user_group_delete(username, force=True, sync_perm=True)
|
||||
|
||||
group_list = ldap.search('ou=groups,dc=yunohost,dc=org',
|
||||
'(&(objectclass=groupOfNamesYnh)(memberUid=%s))'
|
||||
% username, ['cn'])
|
||||
for group in group_list:
|
||||
user_list = ldap.search('ou=groups,dc=yunohost,dc=org',
|
||||
'cn=' + group['cn'][0],
|
||||
['memberUid'])[0]
|
||||
user_list['memberUid'].remove(username)
|
||||
if not ldap.update('cn=%s,ou=groups' % group['cn'][0], user_list):
|
||||
raise YunohostError('group_update_failed')
|
||||
|
||||
hook_callback('post_user_delete', args=[username, purge])
|
||||
|
||||
|
@ -295,18 +301,18 @@ def user_update(operation_logger, username, firstname=None, lastname=None, mail=
|
|||
from yunohost.utils.password import assert_password_is_strong_enough
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
|
||||
ldap = _get_ldap_interface()
|
||||
attrs_to_fetch = ['givenName', 'sn', 'mail', 'maildrop']
|
||||
new_attr_dict = {}
|
||||
domains = domain_list()['domains']
|
||||
|
||||
# Populate user informations
|
||||
ldap = _get_ldap_interface()
|
||||
attrs_to_fetch = ['givenName', 'sn', 'mail', 'maildrop']
|
||||
result = ldap.search(base='ou=users,dc=yunohost,dc=org', filter='uid=' + username, attrs=attrs_to_fetch)
|
||||
if not result:
|
||||
raise YunohostError('user_unknown', user=username)
|
||||
user = result[0]
|
||||
|
||||
# Get modifications from arguments
|
||||
new_attr_dict = {}
|
||||
if firstname:
|
||||
new_attr_dict['givenName'] = firstname # TODO: Validate
|
||||
new_attr_dict['cn'] = new_attr_dict['displayName'] = firstname + ' ' + user['sn'][0]
|
||||
|
@ -409,7 +415,7 @@ def user_info(username):
|
|||
'cn', 'mail', 'uid', 'maildrop', 'givenName', 'sn', 'mailuserquota'
|
||||
]
|
||||
|
||||
if len(username.split('@')) is 2:
|
||||
if len(username.split('@')) == 2:
|
||||
filter = 'mail=' + username
|
||||
else:
|
||||
filter = 'uid=' + username
|
||||
|
@ -447,6 +453,8 @@ def user_info(username):
|
|||
|
||||
if service_status("dovecot")["status"] != "running":
|
||||
logger.warning(m18n.n('mailbox_used_space_dovecot_down'))
|
||||
elif not user_permission_list(app="mail", permission="main", username=username)['permissions']:
|
||||
logger.warning(m18n.n('mailbox_disabled', user=username))
|
||||
else:
|
||||
cmd = 'doveadm -f flow quota get -u %s' % user['uid'][0]
|
||||
cmd_result = subprocess.check_output(cmd, stderr=subprocess.STDOUT,
|
||||
|
@ -477,10 +485,307 @@ def user_info(username):
|
|||
else:
|
||||
raise YunohostError('user_info_failed')
|
||||
|
||||
|
||||
#
|
||||
# Group subcategory
|
||||
#
|
||||
def user_group_list(fields=None):
|
||||
"""
|
||||
List users
|
||||
|
||||
Keyword argument:
|
||||
filter -- LDAP filter used to search
|
||||
offset -- Starting number for user fetching
|
||||
limit -- Maximum number of user fetched
|
||||
fields -- fields to fetch
|
||||
|
||||
"""
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
ldap = _get_ldap_interface()
|
||||
group_attr = {
|
||||
'cn': 'groupname',
|
||||
'member': 'members',
|
||||
'permission': 'permission'
|
||||
}
|
||||
attrs = ['cn']
|
||||
groups = {}
|
||||
|
||||
if fields:
|
||||
keys = group_attr.keys()
|
||||
for attr in fields:
|
||||
if attr in keys:
|
||||
attrs.append(attr)
|
||||
else:
|
||||
raise YunohostError('field_invalid', attr)
|
||||
else:
|
||||
attrs = ['cn', 'member']
|
||||
|
||||
result = ldap.search('ou=groups,dc=yunohost,dc=org',
|
||||
'(objectclass=groupOfNamesYnh)',
|
||||
attrs)
|
||||
|
||||
for group in result:
|
||||
# The group "admins" should be hidden for the user
|
||||
if group_attr['cn'] == "admins":
|
||||
continue
|
||||
entry = {}
|
||||
for attr, values in group.items():
|
||||
if values:
|
||||
if attr == "member":
|
||||
entry[group_attr[attr]] = []
|
||||
for v in values:
|
||||
entry[group_attr[attr]].append(v.split("=")[1].split(",")[0])
|
||||
elif attr == "permission":
|
||||
entry[group_attr[attr]] = {}
|
||||
for v in values:
|
||||
permission = v.split("=")[1].split(",")[0].split(".")[1]
|
||||
pType = v.split("=")[1].split(",")[0].split(".")[0]
|
||||
if permission in entry[group_attr[attr]]:
|
||||
entry[group_attr[attr]][permission].append(pType)
|
||||
else:
|
||||
entry[group_attr[attr]][permission] = [pType]
|
||||
else:
|
||||
entry[group_attr[attr]] = values[0]
|
||||
|
||||
groupname = entry[group_attr['cn']]
|
||||
groups[groupname] = entry
|
||||
|
||||
return {'groups': groups}
|
||||
|
||||
|
||||
@is_unit_operation([('groupname', 'user')])
|
||||
def user_group_add(operation_logger, groupname, gid=None, sync_perm=True):
|
||||
"""
|
||||
Create group
|
||||
|
||||
Keyword argument:
|
||||
groupname -- Must be unique
|
||||
|
||||
"""
|
||||
from yunohost.permission import permission_sync_to_user
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
|
||||
operation_logger.start()
|
||||
|
||||
ldap = _get_ldap_interface()
|
||||
|
||||
# Validate uniqueness of groupname in LDAP
|
||||
conflict = ldap.get_conflict({
|
||||
'cn': groupname
|
||||
}, base_dn='ou=groups,dc=yunohost,dc=org')
|
||||
if conflict:
|
||||
raise YunohostError('group_name_already_exist', name=groupname)
|
||||
|
||||
# Validate uniqueness of groupname in system group
|
||||
all_existing_groupnames = {x.gr_name for x in grp.getgrall()}
|
||||
if groupname in all_existing_groupnames:
|
||||
raise YunohostError('system_groupname_exists')
|
||||
|
||||
if not gid:
|
||||
# Get random GID
|
||||
all_gid = {x.gr_gid for x in grp.getgrall()}
|
||||
|
||||
uid_guid_found = False
|
||||
while not uid_guid_found:
|
||||
gid = str(random.randint(200, 99999))
|
||||
uid_guid_found = gid not in all_gid
|
||||
|
||||
attr_dict = {
|
||||
'objectClass': ['top', 'groupOfNamesYnh', 'posixGroup'],
|
||||
'cn': groupname,
|
||||
'gidNumber': gid,
|
||||
}
|
||||
|
||||
if ldap.add('cn=%s,ou=groups' % groupname, attr_dict):
|
||||
logger.success(m18n.n('group_created', group=groupname))
|
||||
if sync_perm:
|
||||
permission_sync_to_user()
|
||||
return {'name': groupname}
|
||||
|
||||
raise YunohostError('group_creation_failed', group=groupname)
|
||||
|
||||
|
||||
@is_unit_operation([('groupname', 'user')])
|
||||
def user_group_delete(operation_logger, groupname, force=False, sync_perm=True):
|
||||
"""
|
||||
Delete user
|
||||
|
||||
Keyword argument:
|
||||
groupname -- Groupname to delete
|
||||
|
||||
"""
|
||||
from yunohost.permission import permission_sync_to_user
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
|
||||
forbidden_groups = ["all_users", "admins"] + user_list(fields=['uid'])['users'].keys()
|
||||
if not force and groupname in forbidden_groups:
|
||||
raise YunohostError('group_deletion_not_allowed', group=groupname)
|
||||
|
||||
operation_logger.start()
|
||||
ldap = _get_ldap_interface()
|
||||
if not ldap.remove('cn=%s,ou=groups' % groupname):
|
||||
raise YunohostError('group_deletion_failed', group=groupname)
|
||||
|
||||
logger.success(m18n.n('group_deleted', group=groupname))
|
||||
if sync_perm:
|
||||
permission_sync_to_user()
|
||||
|
||||
|
||||
@is_unit_operation([('groupname', 'user')])
|
||||
def user_group_update(operation_logger, groupname, add_user=None, remove_user=None, force=False, sync_perm=True):
|
||||
"""
|
||||
Update user informations
|
||||
|
||||
Keyword argument:
|
||||
groupname -- Groupname to update
|
||||
add_user -- User to add in group
|
||||
remove_user -- User to remove in group
|
||||
|
||||
"""
|
||||
|
||||
from yunohost.permission import permission_sync_to_user
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
|
||||
if (groupname == 'all_users' or groupname == 'admins') and not force:
|
||||
raise YunohostError('edit_group_not_allowed', group=groupname)
|
||||
|
||||
ldap = _get_ldap_interface()
|
||||
|
||||
# Populate group informations
|
||||
attrs_to_fetch = ['member']
|
||||
result = ldap.search(base='ou=groups,dc=yunohost,dc=org',
|
||||
filter='cn=' + groupname, attrs=attrs_to_fetch)
|
||||
if not result:
|
||||
raise YunohostError('group_unknown', group=groupname)
|
||||
group = result[0]
|
||||
|
||||
new_group_list = {'member': set(), 'memberUid': set()}
|
||||
if 'member' in group:
|
||||
new_group_list['member'] = set(group['member'])
|
||||
else:
|
||||
group['member'] = []
|
||||
|
||||
existing_users = user_list(fields=['uid'])['users'].keys()
|
||||
|
||||
if add_user:
|
||||
if not isinstance(add_user, list):
|
||||
add_user = [add_user]
|
||||
|
||||
for user in add_user:
|
||||
if user not in existing_users:
|
||||
raise YunohostError('user_unknown', user=user)
|
||||
|
||||
for user in add_user:
|
||||
userDN = "uid=" + user + ",ou=users,dc=yunohost,dc=org"
|
||||
if userDN in group['member']:
|
||||
logger.warning(m18n.n('user_already_in_group', user=user, group=groupname))
|
||||
new_group_list['member'].add(userDN)
|
||||
|
||||
if remove_user:
|
||||
if not isinstance(remove_user, list):
|
||||
remove_user = [remove_user]
|
||||
|
||||
for user in remove_user:
|
||||
if user == groupname:
|
||||
raise YunohostError('remove_user_of_group_not_allowed', user=user, group=groupname)
|
||||
|
||||
for user in remove_user:
|
||||
userDN = "uid=" + user + ",ou=users,dc=yunohost,dc=org"
|
||||
if 'member' in group and userDN in group['member']:
|
||||
new_group_list['member'].remove(userDN)
|
||||
else:
|
||||
logger.warning(m18n.n('user_not_in_group', user=user, group=groupname))
|
||||
|
||||
# Sychronise memberUid with member (to keep the posix group structure)
|
||||
# In posixgroup the main group of each user is only written in the gid number of the user
|
||||
for member in new_group_list['member']:
|
||||
member_Uid = member.split("=")[1].split(",")[0]
|
||||
# Don't add main user in the group.
|
||||
# Note that in the Unix system the main user of the group is linked by the gid in the user attribute.
|
||||
# So the main user need to be not in the memberUid list of his own group.
|
||||
if member_Uid != groupname:
|
||||
new_group_list['memberUid'].add(member_Uid)
|
||||
|
||||
operation_logger.start()
|
||||
|
||||
if new_group_list['member'] != set(group['member']):
|
||||
if not ldap.update('cn=%s,ou=groups' % groupname, new_group_list):
|
||||
raise YunohostError('group_update_failed', group=groupname)
|
||||
|
||||
logger.success(m18n.n('group_updated', group=groupname))
|
||||
if sync_perm:
|
||||
permission_sync_to_user()
|
||||
return user_group_info(groupname)
|
||||
|
||||
|
||||
def user_group_info(groupname):
|
||||
"""
|
||||
Get user informations
|
||||
|
||||
Keyword argument:
|
||||
groupname -- Groupname to get informations
|
||||
|
||||
"""
|
||||
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
ldap = _get_ldap_interface()
|
||||
|
||||
group_attrs = [
|
||||
'cn', 'member', 'permission'
|
||||
]
|
||||
result = ldap.search('ou=groups,dc=yunohost,dc=org', "cn=" + groupname, group_attrs)
|
||||
|
||||
if not result:
|
||||
raise YunohostError('group_unknown', group=groupname)
|
||||
|
||||
group = result[0]
|
||||
|
||||
result_dict = {
|
||||
'groupname': group['cn'][0],
|
||||
'member': None
|
||||
}
|
||||
if 'member' in group:
|
||||
result_dict['member'] = {m.split("=")[1].split(",")[0] for m in group['member']}
|
||||
return result_dict
|
||||
|
||||
|
||||
#
|
||||
# Permission subcategory
|
||||
#
|
||||
|
||||
def user_permission_list(app=None, permission=None, username=None, group=None, sync_perm=True):
|
||||
import yunohost.permission
|
||||
return yunohost.permission.user_permission_list(app, permission, username, group)
|
||||
|
||||
|
||||
@is_unit_operation([('app', 'user')])
|
||||
def user_permission_add(operation_logger, app, permission="main", username=None, group=None, sync_perm=True):
|
||||
import yunohost.permission
|
||||
return yunohost.permission.user_permission_update(operation_logger, app, permission=permission,
|
||||
add_username=username, add_group=group,
|
||||
del_username=None, del_group=None,
|
||||
sync_perm=sync_perm)
|
||||
|
||||
|
||||
@is_unit_operation([('app', 'user')])
|
||||
def user_permission_remove(operation_logger, app, permission="main", username=None, group=None, sync_perm=True):
|
||||
import yunohost.permission
|
||||
return yunohost.permission.user_permission_update(operation_logger, app, permission=permission,
|
||||
add_username=None, add_group=None,
|
||||
del_username=username, del_group=group,
|
||||
sync_perm=sync_perm)
|
||||
|
||||
|
||||
@is_unit_operation([('app', 'user')])
|
||||
def user_permission_clear(operation_logger, app, permission=None, sync_perm=True):
|
||||
import yunohost.permission
|
||||
return yunohost.permission.user_permission_clear(operation_logger, app, permission,
|
||||
sync_perm=sync_perm)
|
||||
|
||||
|
||||
#
|
||||
# SSH subcategory
|
||||
#
|
||||
#
|
||||
import yunohost.ssh
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue