mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
Merge pull request #795 from YunoHost/improve-permission-interface
Polish group/permissions interface + simplify code
This commit is contained in:
commit
d063348d9b
20 changed files with 1159 additions and 1252 deletions
|
@ -201,24 +201,34 @@ user:
|
||||||
|
|
||||||
subcategories:
|
subcategories:
|
||||||
group:
|
group:
|
||||||
subcategory_help: Manage group
|
subcategory_help: Manage user groups
|
||||||
actions:
|
actions:
|
||||||
### user_group_list()
|
### user_group_list()
|
||||||
list:
|
list:
|
||||||
action_help: List group
|
action_help: List existing groups
|
||||||
api: GET /users/groups
|
api: GET /users/groups
|
||||||
arguments:
|
arguments:
|
||||||
--fields:
|
-s:
|
||||||
help: fields to fetch
|
full: --short
|
||||||
nargs: "+"
|
help: List only the names of groups
|
||||||
|
action: store_true
|
||||||
|
-f:
|
||||||
|
full: --full
|
||||||
|
help: Display all informations known about each groups
|
||||||
|
action: store_true
|
||||||
|
-p:
|
||||||
|
full: --include-primary-groups
|
||||||
|
help: Also display primary groups (each user has an eponym group that only contains itself)
|
||||||
|
action: store_true
|
||||||
|
default: false
|
||||||
|
|
||||||
### user_group_add()
|
### user_group_create()
|
||||||
add:
|
create:
|
||||||
action_help: Create group
|
action_help: Create group
|
||||||
api: POST /users/groups
|
api: POST /users/groups
|
||||||
arguments:
|
arguments:
|
||||||
groupname:
|
groupname:
|
||||||
help: The unique group name to add
|
help: Name of the group to be created
|
||||||
extra:
|
extra:
|
||||||
pattern: &pattern_groupname
|
pattern: &pattern_groupname
|
||||||
- !!str ^[a-z0-9_]+$
|
- !!str ^[a-z0-9_]+$
|
||||||
|
@ -230,7 +240,7 @@ user:
|
||||||
api: DELETE /users/groups/<groupname>
|
api: DELETE /users/groups/<groupname>
|
||||||
arguments:
|
arguments:
|
||||||
groupname:
|
groupname:
|
||||||
help: Username to delete
|
help: Name of the group to be deleted
|
||||||
extra:
|
extra:
|
||||||
pattern: *pattern_groupname
|
pattern: *pattern_groupname
|
||||||
|
|
||||||
|
@ -240,19 +250,19 @@ user:
|
||||||
api: PUT /users/groups/<groupname>
|
api: PUT /users/groups/<groupname>
|
||||||
arguments:
|
arguments:
|
||||||
groupname:
|
groupname:
|
||||||
help: Username to update
|
help: Name of the group to be updated
|
||||||
extra:
|
extra:
|
||||||
pattern: *pattern_groupname
|
pattern: *pattern_groupname
|
||||||
-a:
|
-a:
|
||||||
full: --add-user
|
full: --add
|
||||||
help: User to add in group
|
help: User(s) to add in the group
|
||||||
nargs: "*"
|
nargs: "*"
|
||||||
metavar: USERNAME
|
metavar: USERNAME
|
||||||
extra:
|
extra:
|
||||||
pattern: *pattern_username
|
pattern: *pattern_username
|
||||||
-r:
|
-r:
|
||||||
full: --remove-user
|
full: --remove
|
||||||
help: User to remove in group
|
help: User(s) to remove in the group
|
||||||
nargs: "*"
|
nargs: "*"
|
||||||
metavar: USERNAME
|
metavar: USERNAME
|
||||||
extra:
|
extra:
|
||||||
|
@ -260,112 +270,62 @@ user:
|
||||||
|
|
||||||
### user_group_info()
|
### user_group_info()
|
||||||
info:
|
info:
|
||||||
action_help: Get group information
|
action_help: Get information about a specific group
|
||||||
api: GET /users/groups/<groupname>
|
api: GET /users/groups/<groupname>
|
||||||
arguments:
|
arguments:
|
||||||
groupname:
|
groupname:
|
||||||
help: Groupname to get information
|
help: Name of the group to fetch info about
|
||||||
extra:
|
extra:
|
||||||
pattern: *pattern_username
|
pattern: *pattern_username
|
||||||
|
|
||||||
permission:
|
permission:
|
||||||
subcategory_help: Manage user permission
|
subcategory_help: Manage permissions
|
||||||
actions:
|
actions:
|
||||||
|
|
||||||
### user_permission_list()
|
### user_permission_list()
|
||||||
list:
|
list:
|
||||||
action_help: List access to user and group
|
action_help: List permissions and corresponding accesses
|
||||||
api: GET /users/permissions/<app>
|
api: GET /users/permissions/<permission>
|
||||||
arguments:
|
arguments:
|
||||||
|
-s:
|
||||||
|
full: --short
|
||||||
|
help: Only list permission names
|
||||||
|
action: store_true
|
||||||
|
-f:
|
||||||
|
full: --full
|
||||||
|
help: Display all info known about each permission, including the full user list of each group it is granted to.
|
||||||
|
action: store_true
|
||||||
|
|
||||||
|
|
||||||
|
### user_permission_update()
|
||||||
|
update:
|
||||||
|
action_help: Manage group or user permissions
|
||||||
|
api: POST /users/permissions/<permission>
|
||||||
|
arguments:
|
||||||
|
permission:
|
||||||
|
help: Permission to manage (e.g. mail or nextcloud or wordpress.editors)
|
||||||
-a:
|
-a:
|
||||||
full: --app
|
full: --add
|
||||||
help: Application to manage the permission
|
help: Group or usernames to grant this permission to
|
||||||
nargs: "*"
|
nargs: "*"
|
||||||
metavar: APP
|
metavar: GROUP_OR_USER
|
||||||
-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/permissions/<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:
|
extra:
|
||||||
pattern: *pattern_username
|
pattern: *pattern_username
|
||||||
-g:
|
-r:
|
||||||
full: --group
|
full: --remove
|
||||||
help: Group name
|
help: Group or usernames revoke this permission from
|
||||||
nargs: "*"
|
nargs: "*"
|
||||||
metavar: GROUP
|
metavar: GROUP_OR_USER
|
||||||
extra:
|
extra:
|
||||||
pattern: *pattern_username
|
pattern: *pattern_username
|
||||||
|
|
||||||
### user_permission_remove()
|
## user_permission_reset()
|
||||||
remove:
|
reset:
|
||||||
action_help: Revoke access right to users and group
|
action_help: Reset allowed groups to the default (all_users) for a given permission
|
||||||
api: PUT /users/permissions/<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/permissions/<app>
|
api: DELETE /users/permissions/<app>
|
||||||
arguments:
|
arguments:
|
||||||
app:
|
permission:
|
||||||
help: Application to manage the permission
|
help: Permission to manage (e.g. mail or nextcloud or wordpress.editors)
|
||||||
nargs: "+"
|
|
||||||
-p:
|
|
||||||
full: --permission
|
|
||||||
help: Name of permission (main by default)
|
|
||||||
nargs: "*"
|
|
||||||
metavar: PERMISSION
|
|
||||||
|
|
||||||
ssh:
|
ssh:
|
||||||
subcategory_help: Manage ssh access
|
subcategory_help: Manage ssh access
|
||||||
|
@ -832,6 +792,7 @@ app:
|
||||||
addaccess:
|
addaccess:
|
||||||
action_help: Grant access right to users (everyone by default)
|
action_help: Grant access right to users (everyone by default)
|
||||||
api: PUT /access
|
api: PUT /access
|
||||||
|
deprecated: true
|
||||||
arguments:
|
arguments:
|
||||||
apps:
|
apps:
|
||||||
nargs: "+"
|
nargs: "+"
|
||||||
|
@ -843,6 +804,7 @@ app:
|
||||||
removeaccess:
|
removeaccess:
|
||||||
action_help: Revoke access right to users (everyone by default)
|
action_help: Revoke access right to users (everyone by default)
|
||||||
api: DELETE /access
|
api: DELETE /access
|
||||||
|
deprecated: true
|
||||||
arguments:
|
arguments:
|
||||||
apps:
|
apps:
|
||||||
nargs: "+"
|
nargs: "+"
|
||||||
|
@ -854,6 +816,7 @@ app:
|
||||||
clearaccess:
|
clearaccess:
|
||||||
action_help: Reset access rights for the app
|
action_help: Reset access rights for the app
|
||||||
api: POST /access
|
api: POST /access
|
||||||
|
deprecated: true
|
||||||
arguments:
|
arguments:
|
||||||
apps:
|
apps:
|
||||||
nargs: "+"
|
nargs: "+"
|
||||||
|
|
|
@ -230,70 +230,82 @@ ynh_webpath_register () {
|
||||||
|
|
||||||
# Create a new permission for the app
|
# Create a new permission for the app
|
||||||
#
|
#
|
||||||
# usage: ynh_permission_create --app "app" --permission "permission" --defaultdisallow [--urls "url" ["url" ...]]
|
# usage: ynh_permission_create --permission "permission" [--urls "url" ["url" ...]]
|
||||||
# | arg: app - the application id
|
|
||||||
# | arg: permission - the name for the permission (by default a permission named "main" already exist)
|
# | 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 - (optional) a list of FULL urls for the permission (e.g. domain.tld/apps/admin)
|
||||||
# | arg: urls - the list of urls for the the permission
|
#
|
||||||
|
# example: ynh_permission_create --permission admin --urls domain.tld/blog/admin
|
||||||
ynh_permission_create() {
|
ynh_permission_create() {
|
||||||
declare -Ar args_array=( [a]=app= [p]=permission= [d]=defaultdisallow [u]=urls= )
|
declare -Ar args_array=( [p]=permission= [u]=urls= )
|
||||||
local app
|
|
||||||
local permission
|
local permission
|
||||||
local defaultdisallow
|
|
||||||
local urls
|
local urls
|
||||||
ynh_handle_getopts_args "$@"
|
ynh_handle_getopts_args "$@"
|
||||||
if [[ -n ${defaultdisallow:-} ]]; then
|
|
||||||
defaultdisallow=",default_allow=False"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -n ${urls:-} ]]; then
|
if [[ -n ${urls:-} ]]; then
|
||||||
urls=",urls=['${urls//';'/"','"}']"
|
urls=",urls=['${urls//';'/"','"}']"
|
||||||
fi
|
fi
|
||||||
yunohost tools shell -c "from yunohost.permission import permission_add; permission_add('$app', '$permission' ${defaultdisallow:-} ${urls:-}, sync_perm=False)"
|
yunohost tools shell -c "from yunohost.permission import permission_create; permission_create('$app.$permission' ${urls:-}, sync_perm=False)"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Remove a permission for the app (note that when the app is removed all permission is automatically removed)
|
# 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"
|
# usage: ynh_permission_remove --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)
|
# | 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" ...]
|
# example: ynh_permission_delete --permission editors
|
||||||
# | arg: app - the application id
|
ynh_permission_delete() {
|
||||||
# | arg: permission - the name for the permission
|
declare -Ar args_array=( [p]=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 permission
|
||||||
local url
|
|
||||||
ynh_handle_getopts_args "$@"
|
ynh_handle_getopts_args "$@"
|
||||||
|
|
||||||
yunohost tools shell -c "from yunohost.permission import permission_update; permission_update('$app', '$permission', add_url=['${url//';'/"','"}'], sync_perm=False)"
|
yunohost tools shell -c "from yunohost.permission import permission_delete; permission_delete('$app.$permission', sync_perm=False)"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Remove a path managed by the SSO
|
# Manage urls related to a permission
|
||||||
#
|
#
|
||||||
# usage: ynh_permission_del_path --app "app" --permission "permission" --url "url" ["url" ...]
|
# usage: ynh_permission_urls --permission "permission" --add "url" ["url" ...] --remove "url" ["url" ...]
|
||||||
# | 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)
|
||||||
# | arg: permission - the name for the permission
|
# | arg: add - (optional) a list of FULL urls to add to the permission (e.g. domain.tld/apps/admin)
|
||||||
# | arg: url - the FULL url for the the permission (ex domain.tld/apps/admin)
|
# | arg: remove - (optional) a list of FULL urls to remove from the permission (e.g. other.tld/apps/admin)
|
||||||
ynh_permission_del_path() {
|
#
|
||||||
declare -Ar args_array=( [a]=app= [p]=permission= [u]=url= )
|
ynh_permission_urls() {
|
||||||
local app
|
declare -Ar args_array=([p]=permission= [a]=add= [r]=remove=)
|
||||||
local permission
|
local permission
|
||||||
local url
|
local add
|
||||||
|
local remove
|
||||||
ynh_handle_getopts_args "$@"
|
ynh_handle_getopts_args "$@"
|
||||||
|
|
||||||
yunohost tools shell -c "from yunohost.permission import permission_update; permission_update('$app', '$permission', remove_url=['${url//';'/"','"}'], sync_perm=False)"
|
if [[ -n ${add:-} ]]; then
|
||||||
|
add=",add=['${add//';'/"','"}']"
|
||||||
|
fi
|
||||||
|
if [[ -n ${remove:-} ]]; then
|
||||||
|
remove=",remove=['${remove//';'/"','"}']"
|
||||||
|
fi
|
||||||
|
|
||||||
|
yunohost tools shell -c "from yunohost.permission import permission_urls; permission_urls('$app.$permission' ${add:-} ${remove:-})"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Update a permission for the app
|
||||||
|
#
|
||||||
|
# usage: ynh_permission_update --permission "permission" --add "group" ["group" ...] --remove "group" ["group" ...]
|
||||||
|
# | arg: permission - the name for the permission (by default a permission named "main" already exist)
|
||||||
|
# | arg: add - the list of group or users to enable add to the permission
|
||||||
|
# | arg: remove - the list of group or users to remove from the permission
|
||||||
|
#
|
||||||
|
# example: ynh_permission_update --permission admin --add samdoe --remove all_users
|
||||||
|
ynh_permission_update() {
|
||||||
|
declare -Ar args_array=( [p]=permission= [a]=add= [r]=remove= )
|
||||||
|
local permission
|
||||||
|
local add
|
||||||
|
local remove
|
||||||
|
ynh_handle_getopts_args "$@"
|
||||||
|
|
||||||
|
if [[ -n ${add:-} ]]; then
|
||||||
|
add="--add ${add//';'/" "}"
|
||||||
|
fi
|
||||||
|
if [[ -n ${remove:-} ]]; then
|
||||||
|
remove="--remove ${remove//';'/" "} "
|
||||||
|
fi
|
||||||
|
|
||||||
|
yunohost user permission update "$app.$permission" ${add:-} ${remove:-}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,5 @@ backup_dir="$1/conf/ynh/certs"
|
||||||
sudo mkdir -p /etc/yunohost/certs/
|
sudo mkdir -p /etc/yunohost/certs/
|
||||||
|
|
||||||
sudo cp -a $backup_dir/. /etc/yunohost/certs/
|
sudo cp -a $backup_dir/. /etc/yunohost/certs/
|
||||||
sudo yunohost app ssowatconf
|
|
||||||
sudo service nginx reload
|
sudo service nginx reload
|
||||||
sudo service metronome reload
|
sudo service metronome reload
|
||||||
|
|
|
@ -59,16 +59,16 @@ children:
|
||||||
- groupOfNamesYnh
|
- groupOfNamesYnh
|
||||||
|
|
||||||
depends_children:
|
depends_children:
|
||||||
cn=main.mail,ou=permission:
|
cn=mail.main,ou=permission:
|
||||||
cn: main.mail
|
cn: mail.main
|
||||||
gidNumber: "5001"
|
gidNumber: "5001"
|
||||||
objectClass:
|
objectClass:
|
||||||
- posixGroup
|
- posixGroup
|
||||||
- permissionYnh
|
- permissionYnh
|
||||||
groupPermission:
|
groupPermission:
|
||||||
- "cn=all_users,ou=groups,dc=yunohost,dc=org"
|
- "cn=all_users,ou=groups,dc=yunohost,dc=org"
|
||||||
cn=main.metronome,ou=permission:
|
cn=xmpp.main,ou=permission:
|
||||||
cn: main.metronome
|
cn: xmpp.main
|
||||||
gidNumber: "5002"
|
gidNumber: "5002"
|
||||||
objectClass:
|
objectClass:
|
||||||
- posixGroup
|
- posixGroup
|
||||||
|
|
|
@ -3,7 +3,7 @@ auth_bind = yes
|
||||||
ldap_version = 3
|
ldap_version = 3
|
||||||
base = ou=users,dc=yunohost,dc=org
|
base = ou=users,dc=yunohost,dc=org
|
||||||
user_attrs = uidNumber=500,gidNumber=8,mailuserquota=quota_rule=*:bytes=%$
|
user_attrs = uidNumber=500,gidNumber=8,mailuserquota=quota_rule=*:bytes=%$
|
||||||
user_filter = (&(objectClass=inetOrgPerson)(uid=%n)(permission=cn=main.mail,ou=permission,dc=yunohost,dc=org))
|
user_filter = (&(objectClass=inetOrgPerson)(uid=%n)(permission=cn=mail.main,ou=permission,dc=yunohost,dc=org))
|
||||||
pass_filter = (&(objectClass=inetOrgPerson)(uid=%n)(permission=cn=main.mail,ou=permission,dc=yunohost,dc=org))
|
pass_filter = (&(objectClass=inetOrgPerson)(uid=%n)(permission=cn=mail.main,ou=permission,dc=yunohost,dc=org))
|
||||||
default_pass_scheme = SSHA
|
default_pass_scheme = SSHA
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ VirtualHost "{{ domain }}"
|
||||||
hostname = "localhost",
|
hostname = "localhost",
|
||||||
user = {
|
user = {
|
||||||
basedn = "ou=users,dc=yunohost,dc=org",
|
basedn = "ou=users,dc=yunohost,dc=org",
|
||||||
filter = "(&(objectClass=posixAccount)(mail=*@{{ domain }})(permission=cn=main.metronome,ou=permission,dc=yunohost,dc=org))",
|
filter = "(&(objectClass=posixAccount)(mail=*@{{ domain }})(permission=cn=xmpp.main,ou=permission,dc=yunohost,dc=org))",
|
||||||
usernamefield = "mail",
|
usernamefield = "mail",
|
||||||
namefield = "cn",
|
namefield = "cn",
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
server_host = localhost
|
server_host = localhost
|
||||||
server_port = 389
|
server_port = 389
|
||||||
search_base = dc=yunohost,dc=org
|
search_base = dc=yunohost,dc=org
|
||||||
query_filter = (&(objectClass=mailAccount)(mail=%s)(permission=cn=main.mail,ou=permission,dc=yunohost,dc=org))
|
query_filter = (&(objectClass=mailAccount)(mail=%s)(permission=cn=mail.main,ou=permission,dc=yunohost,dc=org))
|
||||||
result_attribute = uid
|
result_attribute = uid
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
server_host = localhost
|
server_host = localhost
|
||||||
server_port = 389
|
server_port = 389
|
||||||
search_base = dc=yunohost,dc=org
|
search_base = dc=yunohost,dc=org
|
||||||
query_filter = (&(objectClass=mailAccount)(mail=%s)(permission=cn=main.mail,ou=permission,dc=yunohost,dc=org))
|
query_filter = (&(objectClass=mailAccount)(mail=%s)(permission=cn=mail.main,ou=permission,dc=yunohost,dc=org))
|
||||||
result_attribute = maildrop
|
result_attribute = maildrop
|
||||||
|
|
|
@ -51,10 +51,9 @@
|
||||||
"app_upgraded": "{app:s} upgraded",
|
"app_upgraded": "{app:s} upgraded",
|
||||||
"apps_already_up_to_date": "All applications are already up-to-date",
|
"apps_already_up_to_date": "All applications are already up-to-date",
|
||||||
"apps_permission_not_found": "No permission found for the installed apps",
|
"apps_permission_not_found": "No permission found for the installed apps",
|
||||||
"apps_permission_restoration_failed": "Grant the permission permission '{permission:s}' to restore {app:s}",
|
|
||||||
"appslist_corrupted_json": "Could not load the application lists. It looks like {filename:s} is damaged.",
|
"appslist_corrupted_json": "Could not load the application lists. It looks like {filename:s} is damaged.",
|
||||||
"appslist_could_not_migrate": "Could not migrate the app list {appslist:s}! Could not parse the URL… The old cron job was kept kept in {bkp_file:s}.",
|
"appslist_could_not_migrate": "Could not migrate the app list {appslist:s}! Could not parse the URL… The old cron job was kept kept in {bkp_file:s}.",
|
||||||
"appslist_fetched": "Updated application list {appslist:s} fetched",
|
"appslist_fetched": "Updated application list {appslist:s}",
|
||||||
"appslist_migrating": "Migrating application list {appslist:s}…",
|
"appslist_migrating": "Migrating application list {appslist:s}…",
|
||||||
"appslist_name_already_tracked": "A registered application list with name {name:s} already exists.",
|
"appslist_name_already_tracked": "A registered application list with name {name:s} already exists.",
|
||||||
"appslist_removed": "{appslist:s} application list removed",
|
"appslist_removed": "{appslist:s} application list removed",
|
||||||
|
@ -158,9 +157,9 @@
|
||||||
"domain_cannot_remove_main": "Cannot remove main domain. Set one first",
|
"domain_cannot_remove_main": "Cannot remove main domain. Set one first",
|
||||||
"domain_cert_gen_failed": "Could not generate certificate",
|
"domain_cert_gen_failed": "Could not generate certificate",
|
||||||
"domain_created": "Domain created",
|
"domain_created": "Domain created",
|
||||||
"domain_creation_failed": "Could not create domain",
|
"domain_creation_failed": "Could not create domain {domain}: {error}",
|
||||||
"domain_deleted": "Domain deleted",
|
"domain_deleted": "Domain deleted",
|
||||||
"domain_deletion_failed": "Could not delete domain",
|
"domain_deletion_failed": "Could not delete domain {domain}: {error}",
|
||||||
"domain_dns_conf_is_just_a_recommendation": "This command shows you the *recommended* configuration. It does not actually set up the DNS configuration for you. It is your responsability to configure your DNS zone in your registrar according to this recommendation.",
|
"domain_dns_conf_is_just_a_recommendation": "This command shows you the *recommended* configuration. It does not actually set up the DNS configuration for you. It is your responsability to configure your DNS zone in your registrar according to this recommendation.",
|
||||||
"domain_dyndns_already_subscribed": "You have already subscribed to a DynDNS domain",
|
"domain_dyndns_already_subscribed": "You have already subscribed to a DynDNS domain",
|
||||||
"domain_dyndns_root_unknown": "Unknown DynDNS root domain",
|
"domain_dyndns_root_unknown": "Unknown DynDNS root domain",
|
||||||
|
@ -186,9 +185,6 @@
|
||||||
"dyndns_registration_failed": "Could not register DynDNS domain: {error:s}",
|
"dyndns_registration_failed": "Could not register DynDNS domain: {error:s}",
|
||||||
"dyndns_domain_not_provided": "DynDNS provider {provider:s} cannot provide domain {domain:s}.",
|
"dyndns_domain_not_provided": "DynDNS provider {provider:s} cannot provide domain {domain:s}.",
|
||||||
"dyndns_unavailable": "The domain '{domain:s}' is unavailable.",
|
"dyndns_unavailable": "The domain '{domain:s}' is unavailable.",
|
||||||
"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 the group 'all_users', use 'yunohost user permission clear APP' or 'yunohost user permission add APP -u USER' instead.",
|
|
||||||
"error_when_removing_sftpuser_group": "Could not remove the sftpusers group",
|
|
||||||
"executing_command": "Executing command '{command:s}'…",
|
"executing_command": "Executing command '{command:s}'…",
|
||||||
"executing_script": "Executing script '{script:s}'…",
|
"executing_script": "Executing script '{script:s}'…",
|
||||||
"extracting": "Extracting…",
|
"extracting": "Extracting…",
|
||||||
|
@ -219,17 +215,19 @@
|
||||||
"global_settings_unknown_type": "Unexpected situation, the setting {setting:s} appears to have the type {unknown_type:s} but it is not a type supported by the system.",
|
"global_settings_unknown_type": "Unexpected situation, the setting {setting:s} appears to have the type {unknown_type:s} but it is 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 a longer password (i.e. a passphrase) and/or to use a variation of characters (uppercase, lowercase, digits and special characters).",
|
"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 a longer password (i.e. a passphrase) and/or to use a variation 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 a variation 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 a variation of characters (uppercase, lowercase, digits and special characters).",
|
||||||
"group_already_allowed": "Group '{group:s}' already has permission '{permission:s}' turned on for the app '{app:s}'",
|
"group_already_exist": "Group {group} already exists",
|
||||||
"group_already_disallowed": "Group '{group:s}' already has permissions '{permission:s}' turned off for the app '{app:s}'",
|
"group_already_exist_on_system": "Group {group} already exists in the system groups",
|
||||||
"group_name_already_exist": "Group {name:s} already exists",
|
|
||||||
"group_created": "Group '{group}' created",
|
"group_created": "Group '{group}' created",
|
||||||
"group_creation_failed": "Could not create the group '{group}'",
|
"group_creation_failed": "Could not create the group '{group}': {error}",
|
||||||
|
"group_cannot_be_edited": "The group {group} cannot be edited manually.",
|
||||||
|
"group_cannot_be_deleted": "The group {group} cannot be deleted manually.",
|
||||||
"group_deleted": "Group '{group}' deleted",
|
"group_deleted": "Group '{group}' deleted",
|
||||||
"group_deletion_failed": "Could not delete the group '{group}'",
|
"group_deletion_failed": "Could not delete the group '{group}': {error}",
|
||||||
"group_deletion_not_allowed": "The group {group:s} cannot be deleted manually.",
|
|
||||||
"group_unknown": "The group '{group:s}' is unknown",
|
"group_unknown": "The group '{group:s}' is unknown",
|
||||||
"group_updated": "Group '{group}' updated",
|
"group_updated": "Group '{group}' updated",
|
||||||
"group_update_failed": "Could not update the group '{group}'",
|
"group_update_failed": "Could not update the group '{group}': {error}",
|
||||||
|
"group_user_already_in_group": "User {user} is already in group {group}",
|
||||||
|
"group_user_not_in_group": "User {user} is not in group {group}",
|
||||||
"hook_exec_failed": "Could not run script: {path:s}",
|
"hook_exec_failed": "Could not run script: {path:s}",
|
||||||
"hook_exec_not_terminated": "Script did not finish properly: {path:s}",
|
"hook_exec_not_terminated": "Script did not finish properly: {path:s}",
|
||||||
"hook_json_return_error": "Could not read return from hook {path:s}. Error: {msg:s}. Raw content: {raw_content}",
|
"hook_json_return_error": "Could not read return from hook {path:s}. Error: {msg:s}. Raw content: {raw_content}",
|
||||||
|
@ -247,9 +245,6 @@
|
||||||
"log_help_to_get_failed_log": "The operation '{desc}' could not be completed. Please share the full log of this operation using the command 'yunohost log display {name} --share' to get help",
|
"log_help_to_get_failed_log": "The operation '{desc}' could not be completed. Please share the full log of this operation using the command 'yunohost log display {name} --share' to get help",
|
||||||
"log_does_exists": "There is not operation log with the name '{log}', use 'yunohost log list' to see all available operation logs",
|
"log_does_exists": "There is not operation log with the name '{log}', use 'yunohost log list' to see all available operation logs",
|
||||||
"log_operation_unit_unclosed_properly": "Operation unit has not been closed properly",
|
"log_operation_unit_unclosed_properly": "Operation unit has not been closed properly",
|
||||||
"log_app_addaccess": "Add access to '{}'",
|
|
||||||
"log_app_removeaccess": "Remove access to '{}'",
|
|
||||||
"log_app_clearaccess": "Remove all access to '{}'",
|
|
||||||
"log_app_fetchlist": "Add an application list",
|
"log_app_fetchlist": "Add an application list",
|
||||||
"log_app_removelist": "Remove an application list",
|
"log_app_removelist": "Remove an application list",
|
||||||
"log_app_change_url": "Change the URL of '{}' application",
|
"log_app_change_url": "Change the URL of '{}' application",
|
||||||
|
@ -267,20 +262,20 @@
|
||||||
"log_dyndns_subscribe": "Subscribe to a YunoHost subdomain '{}'",
|
"log_dyndns_subscribe": "Subscribe to a YunoHost subdomain '{}'",
|
||||||
"log_dyndns_update": "Update the IP associated with your YunoHost subdomain '{}'",
|
"log_dyndns_update": "Update the IP associated with your YunoHost subdomain '{}'",
|
||||||
"log_letsencrypt_cert_install": "Install a Let's encrypt certificate on '{}' domain",
|
"log_letsencrypt_cert_install": "Install a Let's encrypt certificate on '{}' domain",
|
||||||
"log_permission_add": "Add the '{}' permission for the app '{}'",
|
"log_permission_create": "Create permission '{}'",
|
||||||
"log_permission_remove": "Remove permission '{}'",
|
"log_permission_delete": "Delete permission '{}'",
|
||||||
"log_permission_update": "Update permission '{}' for app '{}'",
|
"log_permission_urls": "Update urls related to permission '{}'",
|
||||||
"log_selfsigned_cert_install": "Install self signed certificate on '{}' domain",
|
"log_selfsigned_cert_install": "Install self signed certificate on '{}' domain",
|
||||||
"log_letsencrypt_cert_renew": "Renew '{}' Let's encrypt certificate",
|
"log_letsencrypt_cert_renew": "Renew '{}' Let's encrypt certificate",
|
||||||
"log_regen_conf": "Regenerate system configurations '{}'",
|
"log_regen_conf": "Regenerate system configurations '{}'",
|
||||||
"log_user_create": "Add '{}' user",
|
"log_user_create": "Add '{}' user",
|
||||||
"log_user_delete": "Delete '{}' user",
|
"log_user_delete": "Delete '{}' user",
|
||||||
"log_user_group_add": "Add '{}' group",
|
"log_user_group_create": "Create '{}' group",
|
||||||
"log_user_group_delete": "Delete '{}' group",
|
"log_user_group_delete": "Delete '{}' group",
|
||||||
"log_user_group_update": "Update '{}' group",
|
"log_user_group_update": "Update '{}' group",
|
||||||
"log_user_update": "Update user info of '{}'",
|
"log_user_update": "Update user info of '{}'",
|
||||||
"log_user_permission_add": "Update '{}' permission",
|
"log_user_permission_update": "Update accesses for permission '{}'",
|
||||||
"log_user_permission_remove": "Update '{}' permission",
|
"log_user_permission_reset": "Reset permission '{}'",
|
||||||
"log_tools_maindomain": "Make '{}' the main domain",
|
"log_tools_maindomain": "Make '{}' the main domain",
|
||||||
"log_tools_migrations_migrate_forward": "Migrate forward",
|
"log_tools_migrations_migrate_forward": "Migrate forward",
|
||||||
"log_tools_postinstall": "Postinstall your YunoHost server",
|
"log_tools_postinstall": "Postinstall your YunoHost server",
|
||||||
|
@ -344,16 +339,17 @@
|
||||||
"migration_0008_no_warning": "No major risk indentified concerning overriding your SSH configuration—one can however not be absolutely sure ;)! Run the migration to override it. Otherwise, you can also skip the migration - though it is not recommended.",
|
"migration_0008_no_warning": "No major risk indentified concerning overriding your SSH configuration—one can however not be absolutely sure ;)! Run the migration to override it. Otherwise, you can also skip the migration - though it is not recommended.",
|
||||||
"migration_0009_not_needed": "This migration already happened somehow… (?) Skipping.",
|
"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_backup_before_migration": "Creating a backup of LDAP database and apps settings prior to the actual migration.",
|
||||||
"migration_0011_can_not_backup_before_migration": "Could not back up the system prior to migration. Error: {error:s}",
|
"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_create_group": "Creating a group for each user…",
|
||||||
"migration_0011_done": "Migration successful. You are now able to manage usergroups.",
|
"migration_0011_done": "Migration successful. You are now able to manage usergroups.",
|
||||||
"migration_0011_LDAP_config_dirty": "It look like that you customized your LDAP configuration. For this migration the LDAP configuration needs to be updated.\nYou need to save your actual configuration, reintialize the original configuration by running 'yunohost tools regen-conf -f' and retry the migration",
|
"migration_0011_LDAP_config_dirty": "It look like that you customized your LDAP configuration. For this migration the LDAP configuration needs to be updated.\nYou need to save your current configuration, reintialize the original configuration by running 'yunohost tools regen-conf -f' and retry the migration",
|
||||||
"migration_0011_LDAP_update_failed": "Could not update LDAP. Error: {error:s}",
|
"migration_0011_LDAP_update_failed": "Could not update LDAP. Error: {error:s}",
|
||||||
"migration_0011_migrate_permission": "Migrating permissions from apps settings to LDAP…",
|
"migration_0011_migrate_permission": "Migrating permissions from apps settings to LDAP…",
|
||||||
"migration_0011_migration_failed_trying_to_rollback": "Migration failed… trying to roll back the system.",
|
"migration_0011_migration_failed_trying_to_rollback": "Migration failed… trying to roll back the system.",
|
||||||
"migration_0011_rollback_success": "System rolled back.",
|
"migration_0011_rollback_success": "System rolled back.",
|
||||||
"migration_0011_update_LDAP_database": "Updating LDAP database…",
|
"migration_0011_update_LDAP_database": "Updating LDAP database…",
|
||||||
"migration_0011_update_LDAP_schema": "Updating LDAP schema…",
|
"migration_0011_update_LDAP_schema": "Updating LDAP schema…",
|
||||||
|
"migration_0011_failed_to_remove_stale_object": "Failed to remove stale object {dn}: {error}",
|
||||||
"migrations_already_ran": "Those migrations are already done: {ids}",
|
"migrations_already_ran": "Those migrations are already done: {ids}",
|
||||||
"migrations_cant_reach_migration_file": "Could not access migrations files at path %s",
|
"migrations_cant_reach_migration_file": "Could not access migrations files at path %s",
|
||||||
"migrations_dependencies_not_satisfied": "Cannot run migration {id} because first you need to run these migrations: {dependencies_id}",
|
"migrations_dependencies_not_satisfied": "Cannot run migration {id} because first you need to run these migrations: {dependencies_id}",
|
||||||
|
@ -384,7 +380,6 @@
|
||||||
"mysql_db_creation_failed": "MySQL database creation failed",
|
"mysql_db_creation_failed": "MySQL database creation failed",
|
||||||
"mysql_db_init_failed": "MySQL database init failed",
|
"mysql_db_init_failed": "MySQL database init failed",
|
||||||
"mysql_db_initialized": "The MySQL database now initialized",
|
"mysql_db_initialized": "The MySQL database now initialized",
|
||||||
"need_define_permission_before": "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_mx_ko": "DNS MX record is not set",
|
||||||
"network_check_smtp_ko": "Outbound e-mail (SMTP port 25) seems to be blocked by your network",
|
"network_check_smtp_ko": "Outbound e-mail (SMTP port 25) seems to be blocked by your network",
|
||||||
"network_check_smtp_ok": "Outbound e-mail (SMTP port 25) is not blocked",
|
"network_check_smtp_ok": "Outbound e-mail (SMTP port 25) is not blocked",
|
||||||
|
@ -413,25 +408,23 @@
|
||||||
"pattern_positive_number": "Must be a positive number",
|
"pattern_positive_number": "Must be a positive number",
|
||||||
"pattern_username": "Must be lower-case alphanumeric and underscore characters only",
|
"pattern_username": "Must be lower-case alphanumeric and underscore characters only",
|
||||||
"pattern_password_app": "Sorry, passwords can not contain the following characters: {forbidden_chars}",
|
"pattern_password_app": "Sorry, passwords can not contain the following characters: {forbidden_chars}",
|
||||||
"permission_already_clear": "Permission '{permission:s}' already clear for app {app:s}",
|
"permission_already_allowed": "Group '{group}' already has permission '{permission}' enabled'",
|
||||||
"permission_already_exist": "Permission '{permission:s}' for app {app:s} already exist",
|
"permission_already_disallowed": "Group '{group}' already has permission '{permission}' disabled'",
|
||||||
"permission_created": "Permission '{permission:s}' for app {app:s} created",
|
"permission_already_exist": "Permission '{permission}' already exists",
|
||||||
"permission_creation_failed": "Could not grant permission",
|
"permission_cannot_remove_main": "Removing a main permission is not allowed",
|
||||||
"permission_deleted": "Permission '{permission:s}' for app {app:s} deleted",
|
"permission_created": "Permission '{permission:s}' created",
|
||||||
"permission_deletion_failed": "Missing permission '{permission:s}' to delete the app '{app:s}'",
|
"permission_creation_failed": "Could not create permission '{permission}': {error}",
|
||||||
"permission_not_found": "Permission '{permission:s}' not found for the application '{app:s}'",
|
"permission_deleted": "Permission '{permission:s}' deleted",
|
||||||
"permission_name_not_valid": "Pick an allowed permission name for '{permission:s}'",
|
"permission_deletion_failed": "Could not delete permission '{permission}': {error}",
|
||||||
"permission_update_failed": "Could not update permission",
|
"permission_not_found": "Permission '{permission:s}' not found",
|
||||||
"permission_generated": "Permission database updated",
|
"permission_update_failed": "Could not update permission '{permission}' : {error}",
|
||||||
"permission_updated": "Permission '{permission:s}' for the app '{app:s}' updated",
|
"permission_updated": "Permission '{permission:s}' updated",
|
||||||
"permission_update_nothing_to_do": "No permissions to update",
|
"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_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_already_opened": "Port {port:d} is already opened for {ip_version:s} connections",
|
||||||
"port_available": "Port {port:d} is available",
|
"port_available": "Port {port:d} is available",
|
||||||
"port_unavailable": "Port {port:d} is not 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 do it from the admin interface.",
|
"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 do it from 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": "Configuration file '{conf}' backed up to '{backup}'",
|
"regenconf_file_backed_up": "Configuration file '{conf}' backed up to '{backup}'",
|
||||||
"regenconf_file_copy_failed": "Could not copy the new configuration file '{new}' to '{conf}'",
|
"regenconf_file_copy_failed": "Could not 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 was kept back.",
|
"regenconf_file_kept_back": "The configuration file '{conf}' is expected to be deleted by regen-conf (category {category}) but was kept back.",
|
||||||
|
@ -514,7 +507,6 @@
|
||||||
"ssowat_conf_updated": "SSOwat configuration updated",
|
"ssowat_conf_updated": "SSOwat configuration updated",
|
||||||
"ssowat_persistent_conf_read_error": "Could not read persistent SSOwat configuration: {error:s}. Edit /etc/ssowat/conf.json.persistent file to fix the JSON syntax",
|
"ssowat_persistent_conf_read_error": "Could not read persistent SSOwat configuration: {error:s}. Edit /etc/ssowat/conf.json.persistent file to fix the JSON syntax",
|
||||||
"ssowat_persistent_conf_write_error": "Could not save persistent SSOwat configuration: {error:s}. Edit /etc/ssowat/conf.json.persistent file to fix the JSON syntax",
|
"ssowat_persistent_conf_write_error": "Could not save persistent SSOwat 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": "System upgraded",
|
"system_upgraded": "System upgraded",
|
||||||
"system_username_exists": "Username already exists in the list of system users",
|
"system_username_exists": "Username already exists in the list of 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`.",
|
"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`.",
|
||||||
|
@ -543,16 +535,15 @@
|
||||||
"upnp_disabled": "UPnP turned off",
|
"upnp_disabled": "UPnP turned off",
|
||||||
"upnp_enabled": "UPnP turned on",
|
"upnp_enabled": "UPnP turned on",
|
||||||
"upnp_port_open_failed": "Could not open port via UPnP",
|
"upnp_port_open_failed": "Could not open port via UPnP",
|
||||||
"user_already_in_group": "The user '{user:}' is already in the '{group:s}' group",
|
"user_already_exists": "User {user} already exists",
|
||||||
"user_created": "User created",
|
"user_created": "User created",
|
||||||
"user_creation_failed": "Could not create user",
|
"user_creation_failed": "Could not create user {user}: {error}",
|
||||||
"user_deleted": "User deleted",
|
"user_deleted": "User deleted",
|
||||||
"user_deletion_failed": "Could not delete user",
|
"user_deletion_failed": "Could not delete user {user}: {error}",
|
||||||
"user_home_creation_failed": "Could not create 'home' folder for user",
|
"user_home_creation_failed": "Could not create 'home' folder for user",
|
||||||
"user_info_failed": "Could not retrieve user info",
|
"user_info_failed": "Could not retrieve user info",
|
||||||
"user_not_in_group": "The user '{user:s}' is not in the group {group:s}",
|
|
||||||
"user_unknown": "Unknown user: {user:s}",
|
"user_unknown": "Unknown user: {user:s}",
|
||||||
"user_update_failed": "Could not change user info",
|
"user_update_failed": "Could not update user {user}: {error}",
|
||||||
"user_updated": "User info changed",
|
"user_updated": "User info changed",
|
||||||
"users_available": "Available users:",
|
"users_available": "Available users:",
|
||||||
"yunohost_already_installed": "YunoHost is already installed",
|
"yunohost_already_installed": "YunoHost is already installed",
|
||||||
|
|
|
@ -406,10 +406,10 @@ def app_map(app=None, raw=False, user=None):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from yunohost.permission import user_permission_list
|
from yunohost.permission import user_permission_list
|
||||||
from yunohost.utils.ldap import _get_ldap_interface
|
|
||||||
|
|
||||||
apps = []
|
apps = []
|
||||||
result = {}
|
result = {}
|
||||||
|
permissions = user_permission_list(full=True)["permissions"]
|
||||||
|
|
||||||
if app is not None:
|
if app is not None:
|
||||||
if not _is_installed(app):
|
if not _is_installed(app):
|
||||||
|
@ -429,12 +429,8 @@ def app_map(app=None, raw=False, user=None):
|
||||||
continue
|
continue
|
||||||
if 'no_sso' in app_settings: # I don't think we need to check for the value here
|
if 'no_sso' in app_settings: # I don't think we need to check for the value here
|
||||||
continue
|
continue
|
||||||
if user is not None:
|
if user and user not in permissions[app_id + ".main"]["corresponding_users"]:
|
||||||
ldap = _get_ldap_interface()
|
continue
|
||||||
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']
|
domain = app_settings['domain']
|
||||||
path = app_settings['path']
|
path = app_settings['path']
|
||||||
|
@ -465,7 +461,7 @@ def app_change_url(operation_logger, app, domain, path):
|
||||||
"""
|
"""
|
||||||
from yunohost.hook import hook_exec, hook_callback
|
from yunohost.hook import hook_exec, hook_callback
|
||||||
from yunohost.domain import _normalize_domain_path, _get_conflicting_apps
|
from yunohost.domain import _normalize_domain_path, _get_conflicting_apps
|
||||||
from yunohost.permission import permission_update
|
from yunohost.permission import permission_urls
|
||||||
|
|
||||||
installed = _is_installed(app)
|
installed = _is_installed(app)
|
||||||
if not installed:
|
if not installed:
|
||||||
|
@ -555,7 +551,7 @@ def app_change_url(operation_logger, app, domain, path):
|
||||||
app_setting(app, 'domain', value=domain)
|
app_setting(app, 'domain', value=domain)
|
||||||
app_setting(app, 'path', value=path)
|
app_setting(app, 'path', value=path)
|
||||||
|
|
||||||
permission_update(app, permission="main", add_url=[domain+path], remove_url=[old_domain+old_path], sync_perm=True)
|
permission_urls(app+".main", add=[domain+path], remove=[old_domain+old_path], sync_perm=True)
|
||||||
|
|
||||||
# avoid common mistakes
|
# avoid common mistakes
|
||||||
if _run_service_command("reload", "nginx") is False:
|
if _run_service_command("reload", "nginx") is False:
|
||||||
|
@ -765,11 +761,9 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu
|
||||||
force -- Do not ask for confirmation when installing experimental / low-quality apps
|
force -- Do not ask for confirmation when installing experimental / low-quality apps
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from yunohost.utils.ldap import _get_ldap_interface
|
|
||||||
from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback
|
from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback
|
||||||
from yunohost.log import OperationLogger
|
from yunohost.log import OperationLogger
|
||||||
from yunohost.permission import permission_add, permission_update, permission_remove, permission_sync_to_user
|
from yunohost.permission import user_permission_list, permission_create, permission_urls, permission_delete, permission_sync_to_user
|
||||||
ldap = _get_ldap_interface()
|
|
||||||
|
|
||||||
# Fetch or extract sources
|
# Fetch or extract sources
|
||||||
if not os.path.exists(INSTALL_TMP):
|
if not os.path.exists(INSTALL_TMP):
|
||||||
|
@ -926,7 +920,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu
|
||||||
# Create permission before the install (useful if the install script redefine the permission)
|
# 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
|
# 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
|
# 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)
|
permission_create(app_instance_name+".main", sync_perm=False)
|
||||||
|
|
||||||
# Execute the app install script
|
# Execute the app install script
|
||||||
install_retcode = 1
|
install_retcode = 1
|
||||||
|
@ -970,11 +964,9 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu
|
||||||
args=[app_instance_name], env=env_dict_remove
|
args=[app_instance_name], env=env_dict_remove
|
||||||
)[0]
|
)[0]
|
||||||
# Remove all permission in LDAP
|
# Remove all permission in LDAP
|
||||||
result = ldap.search(base='ou=permission,dc=yunohost,dc=org',
|
for permission_name in user_permission_list()["permissions"].keys():
|
||||||
filter='(&(objectclass=permissionYnh)(cn=*.%s))' % app_instance_name, attrs=['cn'])
|
if permission_name.startswith(app_instance_name+"."):
|
||||||
permission_list = [p['cn'][0] for p in result]
|
permission_delete(permission_name, force=True)
|
||||||
for l in permission_list:
|
|
||||||
permission_remove(app_instance_name, l.split('.')[0], force=True)
|
|
||||||
|
|
||||||
if remove_retcode != 0:
|
if remove_retcode != 0:
|
||||||
msg = m18n.n('app_not_properly_removed',
|
msg = m18n.n('app_not_properly_removed',
|
||||||
|
@ -1022,8 +1014,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu
|
||||||
domain = app_settings.get('domain', None)
|
domain = app_settings.get('domain', None)
|
||||||
path = app_settings.get('path', None)
|
path = app_settings.get('path', None)
|
||||||
if domain and path:
|
if domain and path:
|
||||||
permission_update(app_instance_name, permission="main", add_url=[domain+path], sync_perm=False)
|
permission_urls(app_instance_name+".main", add=[domain+path], sync_perm=False)
|
||||||
|
|
||||||
permission_sync_to_user()
|
permission_sync_to_user()
|
||||||
|
|
||||||
logger.success(m18n.n('installation_complete'))
|
logger.success(m18n.n('installation_complete'))
|
||||||
|
@ -1040,9 +1031,8 @@ def app_remove(operation_logger, app):
|
||||||
app -- App(s) to delete
|
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.hook import hook_exec, hook_remove, hook_callback
|
||||||
from yunohost.permission import permission_remove, permission_sync_to_user
|
from yunohost.permission import user_permission_list, permission_delete, permission_sync_to_user
|
||||||
if not _is_installed(app):
|
if not _is_installed(app):
|
||||||
raise YunohostError('app_not_installed', app=app, all_apps=_get_all_installed_apps_id())
|
raise YunohostError('app_not_installed', app=app, all_apps=_get_all_installed_apps_id())
|
||||||
|
|
||||||
|
@ -1090,19 +1080,15 @@ def app_remove(operation_logger, app):
|
||||||
hook_remove(app)
|
hook_remove(app)
|
||||||
|
|
||||||
# Remove all permission in LDAP
|
# Remove all permission in LDAP
|
||||||
ldap = _get_ldap_interface()
|
for permission_name in user_permission_list()["permissions"].keys():
|
||||||
result = ldap.search(base='ou=permission,dc=yunohost,dc=org',
|
if permission_name.startswith(app+"."):
|
||||||
filter='(&(objectclass=permissionYnh)(cn=*.%s))' % app, attrs=['cn'])
|
permission_delete(permission_name, force=True, sync_perm=False)
|
||||||
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()
|
permission_sync_to_user()
|
||||||
_assert_system_is_sane_for_app(manifest, "post")
|
_assert_system_is_sane_for_app(manifest, "post")
|
||||||
|
|
||||||
|
|
||||||
@is_unit_operation(['permission','app'])
|
def app_addaccess(apps, users=[]):
|
||||||
def app_addaccess(operation_logger, apps, users=[]):
|
|
||||||
"""
|
"""
|
||||||
Grant access right to users (everyone by default)
|
Grant access right to users (everyone by default)
|
||||||
|
|
||||||
|
@ -1113,15 +1099,15 @@ def app_addaccess(operation_logger, apps, users=[]):
|
||||||
"""
|
"""
|
||||||
from yunohost.permission import user_permission_update
|
from yunohost.permission import user_permission_update
|
||||||
|
|
||||||
permission = user_permission_update(operation_logger, app=apps, permission="main", add_username=users)
|
output = {}
|
||||||
|
for app in apps:
|
||||||
|
permission = user_permission_update(app+".main", add=users, remove="all_users")
|
||||||
|
output[app] = permission["corresponding_users"]
|
||||||
|
|
||||||
result = {p : v['main']['allowed_users'] for p, v in permission['permissions'].items()}
|
return {'allowed_users': output}
|
||||||
|
|
||||||
return {'allowed_users': result}
|
|
||||||
|
|
||||||
|
|
||||||
@is_unit_operation(['permission','app'])
|
def app_removeaccess(apps, users=[]):
|
||||||
def app_removeaccess(operation_logger, apps, users=[]):
|
|
||||||
"""
|
"""
|
||||||
Revoke access right to users (everyone by default)
|
Revoke access right to users (everyone by default)
|
||||||
|
|
||||||
|
@ -1132,15 +1118,15 @@ def app_removeaccess(operation_logger, apps, users=[]):
|
||||||
"""
|
"""
|
||||||
from yunohost.permission import user_permission_update
|
from yunohost.permission import user_permission_update
|
||||||
|
|
||||||
permission = user_permission_update(operation_logger, app=apps, permission="main", del_username=users)
|
output = {}
|
||||||
|
for app in apps:
|
||||||
|
permission = user_permission_update(app+".main", remove=users)
|
||||||
|
output[app] = permission["corresponding_users"]
|
||||||
|
|
||||||
result = {p : v['main']['allowed_users'] for p, v in permission['permissions'].items()}
|
return {'allowed_users': output}
|
||||||
|
|
||||||
return {'allowed_users': result}
|
|
||||||
|
|
||||||
|
|
||||||
@is_unit_operation(['permission','app'])
|
def app_clearaccess(apps):
|
||||||
def app_clearaccess(operation_logger, apps):
|
|
||||||
"""
|
"""
|
||||||
Reset access rights for the app
|
Reset access rights for the app
|
||||||
|
|
||||||
|
@ -1148,13 +1134,15 @@ def app_clearaccess(operation_logger, apps):
|
||||||
apps
|
apps
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from yunohost.permission import user_permission_clear
|
from yunohost.permission import user_permission_reset
|
||||||
|
|
||||||
permission = user_permission_clear(operation_logger, app=apps, permission="main")
|
output = {}
|
||||||
|
for app in apps:
|
||||||
|
permission = user_permission_reset(app+".main")
|
||||||
|
output[app] = permission["corresponding_users"]
|
||||||
|
|
||||||
result = {p : v['main']['allowed_users'] for p, v in permission['permissions'].items()}
|
return {'allowed_users': output}
|
||||||
|
|
||||||
return {'allowed_users': result}
|
|
||||||
|
|
||||||
def app_debug(app):
|
def app_debug(app):
|
||||||
"""
|
"""
|
||||||
|
@ -1476,12 +1464,10 @@ def app_ssowatconf():
|
||||||
skipped_regex.append("^[^/]*/%.well%-known/acme%-challenge/.*$")
|
skipped_regex.append("^[^/]*/%.well%-known/acme%-challenge/.*$")
|
||||||
skipped_regex.append("^[^/]*/%.well%-known/autoconfig/mail/config%-v1%.1%.xml.*$")
|
skipped_regex.append("^[^/]*/%.well%-known/autoconfig/mail/config%-v1%.1%.xml.*$")
|
||||||
|
|
||||||
permission = {}
|
permissions_per_url = {}
|
||||||
for a in user_permission_list()['permissions'].values():
|
for permission_name, permission_infos in user_permission_list(full=True)['permissions'].items():
|
||||||
for p in a.values():
|
for url in permission_infos["urls"]:
|
||||||
if 'URL' in p:
|
permissions_per_url[url] = permission_infos['corresponding_users']
|
||||||
for u in p['URL']:
|
|
||||||
permission[u] = p['allowed_users']
|
|
||||||
|
|
||||||
conf_dict = {
|
conf_dict = {
|
||||||
'portal_domain': main_domain,
|
'portal_domain': main_domain,
|
||||||
|
@ -1503,7 +1489,7 @@ def app_ssowatconf():
|
||||||
'redirected_regex': redirected_regex,
|
'redirected_regex': redirected_regex,
|
||||||
'users': {username: app_map(user=username)
|
'users': {username: app_map(user=username)
|
||||||
for username in user_list()['users'].keys()},
|
for username in user_list()['users'].keys()},
|
||||||
'permission': permission,
|
'permissions': permissions_per_url,
|
||||||
}
|
}
|
||||||
|
|
||||||
with open('/etc/ssowat/conf.json', 'w+') as f:
|
with open('/etc/ssowat/conf.json', 'w+') as f:
|
||||||
|
|
|
@ -40,7 +40,7 @@ from moulinette import msignals, m18n
|
||||||
from yunohost.utils.error import YunohostError
|
from yunohost.utils.error import YunohostError
|
||||||
from moulinette.utils import filesystem
|
from moulinette.utils import filesystem
|
||||||
from moulinette.utils.log import getActionLogger
|
from moulinette.utils.log import getActionLogger
|
||||||
from moulinette.utils.filesystem import read_file, mkdir
|
from moulinette.utils.filesystem import read_file, mkdir, write_to_yaml, read_yaml
|
||||||
|
|
||||||
from yunohost.app import (
|
from yunohost.app import (
|
||||||
app_info, _is_installed, _parse_app_instance_name, _patch_php5
|
app_info, _is_installed, _parse_app_instance_name, _patch_php5
|
||||||
|
@ -677,6 +677,8 @@ class BackupManager():
|
||||||
backup_app_failed -- Raised at the end if the app backup script
|
backup_app_failed -- Raised at the end if the app backup script
|
||||||
execution failed
|
execution failed
|
||||||
"""
|
"""
|
||||||
|
from yunohost.permission import user_permission_list
|
||||||
|
|
||||||
app_setting_path = os.path.join('/etc/yunohost/apps/', app)
|
app_setting_path = os.path.join('/etc/yunohost/apps/', app)
|
||||||
|
|
||||||
# Prepare environment
|
# Prepare environment
|
||||||
|
@ -704,8 +706,9 @@ class BackupManager():
|
||||||
|
|
||||||
# backup permissions
|
# backup permissions
|
||||||
logger.debug(m18n.n('backup_permission', app=app))
|
logger.debug(m18n.n('backup_permission', app=app))
|
||||||
ldap_url = "ldap:///dc=yunohost,dc=org???(&(objectClass=permissionYnh)(cn=*.%s))" % app
|
permissions = user_permission_list(full=True)["permissions"]
|
||||||
os.system("slapcat -b dc=yunohost,dc=org -H '%s' -l '%s/permission.ldif'" % (ldap_url, settings_dir))
|
this_app_permissions = {name: infos for name, infos in permissions.items() if name.startswith(app + ".")}
|
||||||
|
write_to_yaml("%s/permissions.yml" % settings_dir, this_app_permissions)
|
||||||
|
|
||||||
except:
|
except:
|
||||||
abs_tmp_app_dir = os.path.join(self.work_dir, 'apps/', app)
|
abs_tmp_app_dir = os.path.join(self.work_dir, 'apps/', app)
|
||||||
|
@ -919,7 +922,7 @@ class RestoreManager():
|
||||||
|
|
||||||
successfull_apps = self.targets.list("apps", include=["Success", "Warning"])
|
successfull_apps = self.targets.list("apps", include=["Success", "Warning"])
|
||||||
|
|
||||||
permission_sync_to_user(force=False)
|
permission_sync_to_user()
|
||||||
|
|
||||||
if os.path.ismount(self.work_dir):
|
if os.path.ismount(self.work_dir):
|
||||||
ret = subprocess.call(["umount", self.work_dir])
|
ret = subprocess.call(["umount", self.work_dir])
|
||||||
|
@ -1131,6 +1134,8 @@ class RestoreManager():
|
||||||
|
|
||||||
self._restore_system()
|
self._restore_system()
|
||||||
self._restore_apps()
|
self._restore_apps()
|
||||||
|
except Exception as e:
|
||||||
|
raise YunohostError("The following critical error happened during restoration: %s" % e)
|
||||||
finally:
|
finally:
|
||||||
self.clean()
|
self.clean()
|
||||||
|
|
||||||
|
@ -1183,18 +1188,12 @@ class RestoreManager():
|
||||||
if system_targets == []:
|
if system_targets == []:
|
||||||
return
|
return
|
||||||
|
|
||||||
from yunohost.utils.ldap import _get_ldap_interface
|
from yunohost.user import user_group_list
|
||||||
ldap = _get_ldap_interface()
|
from yunohost.permission import permission_create, permission_delete, user_permission_update, user_permission_list
|
||||||
|
|
||||||
# Backup old permission for apps
|
# 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
|
# 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 = []
|
old_apps_permission = user_permission_list(ignore_system_perms=True, full=True)["permissions"]
|
||||||
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
|
# Start register change on system
|
||||||
operation_logger = OperationLogger('backup_restore_system')
|
operation_logger = OperationLogger('backup_restore_system')
|
||||||
|
@ -1232,12 +1231,11 @@ class RestoreManager():
|
||||||
|
|
||||||
regen_conf()
|
regen_conf()
|
||||||
|
|
||||||
# Check if we need to do the migration 0009 : setup group and permission
|
# Check that at least a group exists (all_users) to know if we need to
|
||||||
|
# do the migration 0011 : setup group and permission
|
||||||
|
#
|
||||||
# Legacy code
|
# Legacy code
|
||||||
result = ldap.search('ou=groups,dc=yunohost,dc=org',
|
if not "all_users" in user_group_list()["groups"].keys():
|
||||||
'(&(objectclass=groupOfNamesYnh)(cn=all_users))',
|
|
||||||
['cn'])
|
|
||||||
if not result:
|
|
||||||
from yunohost.tools import _get_migration_by_name
|
from yunohost.tools import _get_migration_by_name
|
||||||
setup_group_permission = _get_migration_by_name("setup_group_permission")
|
setup_group_permission = _get_migration_by_name("setup_group_permission")
|
||||||
# Update LDAP schema restart slapd
|
# Update LDAP schema restart slapd
|
||||||
|
@ -1245,25 +1243,16 @@ class RestoreManager():
|
||||||
regen_conf(names=['slapd'], force=True)
|
regen_conf(names=['slapd'], force=True)
|
||||||
setup_group_permission.migrate_LDAP_db()
|
setup_group_permission.migrate_LDAP_db()
|
||||||
|
|
||||||
# Remove all permission for all app which sill in the LDAP
|
# Remove all permission for all app which is still in the LDAP
|
||||||
for per in ldap.search('ou=permission,dc=yunohost,dc=org',
|
for permission_name in user_permission_list(ignore_system_perms=True)["permissions"].keys():
|
||||||
'(&(objectClass=permissionYnh)(!(cn=main.mail))(!(cn=main.metronome))(!(cn=main.sftp)))',
|
permission_delete(permission_name, force=True)
|
||||||
['cn']):
|
|
||||||
if not ldap.remove('cn=%s,ou=permission' % per['cn'][0]):
|
|
||||||
raise YunohostError('permission_deletion_failed',
|
|
||||||
permission=per['cn'][0].split('.')[0],
|
|
||||||
app=per['cn'][0].split('.')[1])
|
|
||||||
|
|
||||||
# Restore permission for the app which is installed
|
# Restore permission for the app which is installed
|
||||||
for per in old_apps_permission:
|
for permission_name, permission_infos in old_apps_permission.items():
|
||||||
try:
|
app_name = permission_name.split(".")[0]
|
||||||
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 _is_installed(app_name):
|
||||||
if not ldap.add('cn=%s,ou=permission' % per['cn'][0], per):
|
permission_create(permission_name, urls=permission_infos["urls"], sync_perm=False)
|
||||||
raise YunohostError('apps_permission_restoration_failed', permission=permission_name, app=app_name)
|
user_permission_update(permission_name, remove="all_users", add=permission_infos["allowed"])
|
||||||
|
|
||||||
|
|
||||||
def _restore_apps(self):
|
def _restore_apps(self):
|
||||||
"""Restore all apps targeted"""
|
"""Restore all apps targeted"""
|
||||||
|
@ -1271,7 +1260,6 @@ class RestoreManager():
|
||||||
apps_targets = self.targets.list("apps", exclude=["Skipped"])
|
apps_targets = self.targets.list("apps", exclude=["Skipped"])
|
||||||
|
|
||||||
for app in apps_targets:
|
for app in apps_targets:
|
||||||
print(app)
|
|
||||||
self._restore_app(app)
|
self._restore_app(app)
|
||||||
|
|
||||||
def _restore_app(self, app_instance_name):
|
def _restore_app(self, app_instance_name):
|
||||||
|
@ -1301,11 +1289,8 @@ class RestoreManager():
|
||||||
name already exists
|
name already exists
|
||||||
restore_app_failed -- Raised if the restore bash script failed
|
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.user import user_group_list
|
||||||
from yunohost.permission import permission_remove
|
from yunohost.permission import permission_create, permission_delete, user_permission_list, user_permission_update
|
||||||
from yunohost.utils.ldap import _get_ldap_interface
|
|
||||||
ldap = _get_ldap_interface()
|
|
||||||
|
|
||||||
def copytree(src, dst, symlinks=False, ignore=None):
|
def copytree(src, dst, symlinks=False, ignore=None):
|
||||||
for item in os.listdir(src):
|
for item in os.listdir(src):
|
||||||
|
@ -1370,22 +1355,27 @@ class RestoreManager():
|
||||||
restore_script = os.path.join(tmp_folder_for_app_restore, 'restore')
|
restore_script = os.path.join(tmp_folder_for_app_restore, 'restore')
|
||||||
|
|
||||||
# Restore permissions
|
# Restore permissions
|
||||||
if os.path.isfile(app_settings_in_archive + '/permission.ldif'):
|
if os.path.isfile('%s/permissions.yml' % app_settings_new_path):
|
||||||
filtred_entries = ['entryUUID', 'creatorsName', 'createTimestamp', 'entryCSN', 'structuralObjectClass',
|
|
||||||
'modifiersName', 'modifyTimestamp', 'inheritPermission', 'memberUid']
|
permissions = read_yaml('%s/permissions.yml' % app_settings_new_path)
|
||||||
entries = read_ldif('%s/permission.ldif' % app_settings_in_archive, filtred_entries)
|
existing_groups = user_group_list()['groups']
|
||||||
group_list = user_group_list(['cn'])['groups']
|
|
||||||
for dn, entry in entries:
|
for permission_name, permission_infos in permissions.items():
|
||||||
# Remove the group which has been removed
|
|
||||||
for group in entry['groupPermission']:
|
permission_create(permission_name, urls=permission_infos.get("urls", []))
|
||||||
group_name = group.split(',')[0].split('=')[1]
|
|
||||||
if group_name not in group_list:
|
if "allowed" not in permission_infos:
|
||||||
entry['groupPermission'].remove(group)
|
logger.warning("'allowed' key corresponding to allowed groups for permission %s not found when restoring app %s … You might have to reconfigure permissions yourself." % (permission_name, app_instance_name))
|
||||||
if not ldap.add('cn=%s,ou=permission' % entry['cn'][0], entry):
|
else:
|
||||||
raise YunohostError('apps_permission_restoration_failed',
|
should_be_allowed = [g for g in permission_infos["allowed"] if g in existing_groups]
|
||||||
permission=entry['cn'][0].split('.')[0],
|
current_allowed = user_permission_list()["permissions"][permission_name]["allowed"]
|
||||||
app=entry['cn'][0].split('.')[1])
|
if should_be_allowed != current_allowed:
|
||||||
|
user_permission_update(permission_name, remove=current_allowed, add=should_be_allowed)
|
||||||
|
|
||||||
|
os.remove('%s/permissions.yml' % app_settings_new_path)
|
||||||
else:
|
else:
|
||||||
|
# Otherwise, we need to migrate the legacy permissions of this
|
||||||
|
# app (included in its settings.yml)
|
||||||
from yunohost.tools import _get_migration_by_name
|
from yunohost.tools import _get_migration_by_name
|
||||||
setup_group_permission = _get_migration_by_name("setup_group_permission")
|
setup_group_permission = _get_migration_by_name("setup_group_permission")
|
||||||
setup_group_permission.migrate_app_permission(app=app_instance_name)
|
setup_group_permission.migrate_app_permission(app=app_instance_name)
|
||||||
|
@ -1424,7 +1414,6 @@ class RestoreManager():
|
||||||
operation_logger.start()
|
operation_logger.start()
|
||||||
|
|
||||||
# Execute remove script
|
# Execute remove script
|
||||||
# TODO: call app_remove instead
|
|
||||||
if hook_exec(remove_script, args=[app_instance_name],
|
if hook_exec(remove_script, args=[app_instance_name],
|
||||||
env=env_dict_remove)[0] != 0:
|
env=env_dict_remove)[0] != 0:
|
||||||
msg = m18n.n('app_not_properly_removed', app=app_instance_name)
|
msg = m18n.n('app_not_properly_removed', app=app_instance_name)
|
||||||
|
@ -1436,12 +1425,10 @@ class RestoreManager():
|
||||||
# Cleaning app directory
|
# Cleaning app directory
|
||||||
shutil.rmtree(app_settings_new_path, ignore_errors=True)
|
shutil.rmtree(app_settings_new_path, ignore_errors=True)
|
||||||
|
|
||||||
# Remove all permission in LDAP
|
# Remove all permission in LDAP for this app
|
||||||
result = ldap.search(base='ou=permission,dc=yunohost,dc=org',
|
for permission_name in user_permission_list()["permissions"].keys():
|
||||||
filter='(&(objectclass=permissionYnh)(cn=*.%s))' % app_instance_name, attrs=['cn'])
|
if permission_name.startswith(app_instance_name+"."):
|
||||||
permission_list = [p['cn'][0] for p in result]
|
permission_delete(permission_name, force=True)
|
||||||
for l in permission_list:
|
|
||||||
permission_remove(app_instance_name, l.split('.')[0], force=True)
|
|
||||||
|
|
||||||
# TODO Cleaning app hooks
|
# TODO Cleaning app hooks
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
import yaml
|
|
||||||
import time
|
import time
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from moulinette import m18n
|
from moulinette import m18n
|
||||||
from yunohost.utils.error import YunohostError
|
from yunohost.utils.error import YunohostError
|
||||||
from moulinette.utils.log import getActionLogger
|
from moulinette.utils.log import getActionLogger
|
||||||
|
from moulinette.utils.filesystem import read_yaml
|
||||||
|
|
||||||
from yunohost.tools import Migration
|
from yunohost.tools import Migration
|
||||||
from yunohost.user import user_group_add, user_group_update
|
from yunohost.user import user_group_create, user_group_update
|
||||||
from yunohost.app import app_setting, app_list
|
from yunohost.app import app_setting, app_list
|
||||||
from yunohost.regenconf import regen_conf
|
from yunohost.regenconf import regen_conf
|
||||||
from yunohost.permission import permission_add, permission_sync_to_user
|
from yunohost.permission import permission_create, user_permission_update, permission_sync_to_user
|
||||||
from yunohost.user import user_permission_add
|
|
||||||
|
|
||||||
logger = getActionLogger('yunohost.migration')
|
logger = getActionLogger('yunohost.migration')
|
||||||
|
|
||||||
|
@ -19,6 +18,7 @@ logger = getActionLogger('yunohost.migration')
|
||||||
# Tools used also for restoration
|
# Tools used also for restoration
|
||||||
###################################################
|
###################################################
|
||||||
|
|
||||||
|
|
||||||
class MyMigration(Migration):
|
class MyMigration(Migration):
|
||||||
"""
|
"""
|
||||||
Update the LDAP DB to be able to store the permission
|
Update the LDAP DB to be able to store the permission
|
||||||
|
@ -28,6 +28,28 @@ class MyMigration(Migration):
|
||||||
|
|
||||||
required = True
|
required = True
|
||||||
|
|
||||||
|
def remove_if_exists(self, target):
|
||||||
|
|
||||||
|
from yunohost.utils.ldap import _get_ldap_interface
|
||||||
|
ldap = _get_ldap_interface()
|
||||||
|
|
||||||
|
try:
|
||||||
|
objects = ldap.search(target + ",dc=yunohost,dc=org")
|
||||||
|
# ldap search will raise an exception if no corresponding object is found >.> ...
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug("%s does not exist, no need to delete it" % target)
|
||||||
|
return
|
||||||
|
|
||||||
|
objects.reverse()
|
||||||
|
for o in objects:
|
||||||
|
for dn in o["dn"]:
|
||||||
|
dn = dn.replace(",dc=yunohost,dc=org", "")
|
||||||
|
logger.debug("Deleting old object %s ..." % dn)
|
||||||
|
try:
|
||||||
|
ldap.remove(dn)
|
||||||
|
except Exception as e:
|
||||||
|
raise YunohostError("migration_0011_failed_to_remove_stale_object", dn=dn, error=e)
|
||||||
|
|
||||||
def migrate_LDAP_db(self):
|
def migrate_LDAP_db(self):
|
||||||
|
|
||||||
logger.info(m18n.n("migration_0011_update_LDAP_database"))
|
logger.info(m18n.n("migration_0011_update_LDAP_database"))
|
||||||
|
@ -35,15 +57,13 @@ class MyMigration(Migration):
|
||||||
from yunohost.utils.ldap import _get_ldap_interface
|
from yunohost.utils.ldap import _get_ldap_interface
|
||||||
ldap = _get_ldap_interface()
|
ldap = _get_ldap_interface()
|
||||||
|
|
||||||
try:
|
ldap_map = read_yaml('/usr/share/yunohost/yunohost-config/moulinette/ldap_scheme.yml')
|
||||||
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:
|
try:
|
||||||
|
self.remove_if_exists("cn=sftpusers,ou=groups")
|
||||||
|
self.remove_if_exists("ou=permission")
|
||||||
|
self.remove_if_exists('cn=all_users,ou=groups')
|
||||||
|
|
||||||
attr_dict = ldap_map['parents']['ou=permission']
|
attr_dict = ldap_map['parents']['ou=permission']
|
||||||
ldap.add('ou=permission', attr_dict)
|
ldap.add('ou=permission', attr_dict)
|
||||||
|
|
||||||
|
@ -65,10 +85,8 @@ class MyMigration(Migration):
|
||||||
username = user_info['uid'][0]
|
username = user_info['uid'][0]
|
||||||
ldap.update('uid=%s,ou=users' % username,
|
ldap.update('uid=%s,ou=users' % username,
|
||||||
{'objectClass': ['mailAccount', 'inetOrgPerson', 'posixAccount', 'userPermissionYnh']})
|
{'objectClass': ['mailAccount', 'inetOrgPerson', 'posixAccount', 'userPermissionYnh']})
|
||||||
user_group_add(username, gid=user_info['uidNumber'][0], sync_perm=False)
|
user_group_create(username, gid=user_info['uidNumber'][0], primary_group=True, sync_perm=False)
|
||||||
user_group_update(groupname=username, add_user=username, force=True, sync_perm=False)
|
user_group_update(groupname='all_users', add=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):
|
def migrate_app_permission(self, app=None):
|
||||||
logger.info(m18n.n("migration_0011_migrate_permission"))
|
logger.info(m18n.n("migration_0011_migrate_permission"))
|
||||||
|
@ -85,14 +103,18 @@ class MyMigration(Migration):
|
||||||
domain = app_setting(app, 'domain')
|
domain = app_setting(app, 'domain')
|
||||||
|
|
||||||
urls = [domain + path] if domain and path else None
|
urls = [domain + path] if domain and path else None
|
||||||
permission_add(app, permission='main', urls=urls, default_allow=True, sync_perm=False)
|
permission_create(app+".main", urls=urls, sync_perm=False)
|
||||||
if permission:
|
if permission:
|
||||||
allowed_group = permission.split(',')
|
allowed_group = permission.split(',')
|
||||||
user_permission_add([app], permission='main', group=allowed_group, sync_perm=False)
|
user_permission_update(app+".main", remove="all_users", add=allowed_group, sync_perm=False)
|
||||||
app_setting(app, 'allowed_users', delete=True)
|
app_setting(app, 'allowed_users', delete=True)
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|
||||||
|
# FIXME : what do we really want to do here ...
|
||||||
|
# Imho we should just force-regen the conf in all case, and maybe
|
||||||
|
# just display a warning if we detect that the conf was manually modified
|
||||||
|
|
||||||
# Check if the migration can be processed
|
# Check if the migration can be processed
|
||||||
ldap_regen_conf_status = regen_conf(names=['slapd'], dry_run=True)
|
ldap_regen_conf_status = regen_conf(names=['slapd'], dry_run=True)
|
||||||
# By this we check if the have been customized
|
# By this we check if the have been customized
|
||||||
|
|
|
@ -112,8 +112,10 @@ def domain_add(operation_logger, domain, dyndns=False):
|
||||||
'virtualdomain': domain,
|
'virtualdomain': domain,
|
||||||
}
|
}
|
||||||
|
|
||||||
if not ldap.add('virtualdomain=%s,ou=domains' % domain, attr_dict):
|
try:
|
||||||
raise YunohostError('domain_creation_failed')
|
ldap.add('virtualdomain=%s,ou=domains' % domain, attr_dict)
|
||||||
|
except Exception as e:
|
||||||
|
raise YunohostError('domain_creation_failed', domain=domain, error=e)
|
||||||
|
|
||||||
# Don't regen these conf if we're still in postinstall
|
# Don't regen these conf if we're still in postinstall
|
||||||
if os.path.exists('/etc/yunohost/installed'):
|
if os.path.exists('/etc/yunohost/installed'):
|
||||||
|
@ -167,10 +169,12 @@ def domain_remove(operation_logger, domain, force=False):
|
||||||
|
|
||||||
operation_logger.start()
|
operation_logger.start()
|
||||||
ldap = _get_ldap_interface()
|
ldap = _get_ldap_interface()
|
||||||
if ldap.remove('virtualdomain=' + domain + ',ou=domains') or force:
|
try:
|
||||||
os.system('rm -rf /etc/yunohost/certs/%s' % domain)
|
ldap.remove('virtualdomain=' + domain + ',ou=domains')
|
||||||
else:
|
except Exception as e:
|
||||||
raise YunohostError('domain_deletion_failed')
|
raise YunohostError('domain_deletion_failed', domain=domain, error=e)
|
||||||
|
|
||||||
|
os.system('rm -rf /etc/yunohost/certs/%s' % domain)
|
||||||
|
|
||||||
regen_conf(names=['nginx', 'metronome', 'dnsmasq', 'postfix'])
|
regen_conf(names=['nginx', 'metronome', 'dnsmasq', 'postfix'])
|
||||||
app_ssowatconf()
|
app_ssowatconf()
|
||||||
|
|
|
@ -44,7 +44,7 @@ CATEGORIES = ['operation', 'history', 'package', 'system', 'access', 'service',
|
||||||
'app']
|
'app']
|
||||||
METADATA_FILE_EXT = '.yml'
|
METADATA_FILE_EXT = '.yml'
|
||||||
LOG_FILE_EXT = '.log'
|
LOG_FILE_EXT = '.log'
|
||||||
RELATED_CATEGORIES = ['app', 'domain', 'service', 'user']
|
RELATED_CATEGORIES = ['app', 'domain', 'group', 'service', 'user']
|
||||||
|
|
||||||
logger = getActionLogger('yunohost.log')
|
logger = getActionLogger('yunohost.log')
|
||||||
|
|
||||||
|
@ -213,7 +213,7 @@ def log_display(path, number=None, share=False):
|
||||||
return infos
|
return infos
|
||||||
|
|
||||||
|
|
||||||
def is_unit_operation(entities=['app', 'domain', 'service', 'user'],
|
def is_unit_operation(entities=['app', 'domain', 'group', 'service', 'user'],
|
||||||
exclude=['password'], operation_key=None):
|
exclude=['password'], operation_key=None):
|
||||||
"""
|
"""
|
||||||
Configure quickly a unit operation
|
Configure quickly a unit operation
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
Manage permissions
|
Manage permissions
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import copy
|
||||||
import grp
|
import grp
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
@ -35,309 +36,247 @@ from yunohost.log import is_unit_operation
|
||||||
|
|
||||||
logger = getActionLogger('yunohost.user')
|
logger = getActionLogger('yunohost.user')
|
||||||
|
|
||||||
|
SYSTEM_PERMS = ["mail", "xmpp", "stfp"]
|
||||||
|
|
||||||
def user_permission_list(app=None, permission=None, username=None, group=None):
|
#
|
||||||
|
#
|
||||||
|
# The followings are the methods exposed through the "yunohost user permission" interface
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
def user_permission_list(short=False, full=False, ignore_system_perms=False):
|
||||||
"""
|
"""
|
||||||
List permission for specific application
|
List permissions and corresponding accesses
|
||||||
|
|
||||||
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
|
# Fetch relevant informations
|
||||||
|
|
||||||
|
from yunohost.utils.ldap import _get_ldap_interface, _ldap_path_extract
|
||||||
ldap = _get_ldap_interface()
|
ldap = _get_ldap_interface()
|
||||||
|
permissions_infos = ldap.search('ou=permission,dc=yunohost,dc=org',
|
||||||
|
'(objectclass=permissionYnh)',
|
||||||
|
["cn", 'groupPermission', 'inheritPermission', 'URL'])
|
||||||
|
|
||||||
permission_attrs = [
|
# Parse / organize information to be outputed
|
||||||
'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 = {}
|
permissions = {}
|
||||||
|
for infos in permissions_infos:
|
||||||
|
|
||||||
result = ldap.search('ou=permission,dc=yunohost,dc=org',
|
name = infos['cn'][0]
|
||||||
'(objectclass=permissionYnh)', permission_attrs)
|
|
||||||
|
|
||||||
for res in result:
|
if ignore_system_perms and name.split(".")[0] in SYSTEM_PERMS:
|
||||||
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
|
continue
|
||||||
|
|
||||||
if app_name not in permissions:
|
permissions[name] = {}
|
||||||
permissions[app_name] = {}
|
permissions[name]["allowed"] = [_ldap_path_extract(p, "cn") for p in infos.get('groupPermission', [])]
|
||||||
|
|
||||||
permissions[app_name][permission_name] = {'allowed_users': [], 'allowed_groups': []}
|
if full:
|
||||||
for g in group_name:
|
permissions[name]["corresponding_users"] = [_ldap_path_extract(p, "uid") for p in infos.get('inheritPermission', [])]
|
||||||
permissions[app_name][permission_name]['allowed_groups'].append(g)
|
permissions[name]["urls"] = infos.get("URL", [])
|
||||||
for u in user_name:
|
|
||||||
permissions[app_name][permission_name]['allowed_users'].append(u)
|
if short:
|
||||||
if 'URL' in res:
|
permissions = permissions.keys()
|
||||||
permissions[app_name][permission_name]['URL'] = []
|
|
||||||
for u in res['URL']:
|
|
||||||
permissions[app_name][permission_name]['URL'].append(u)
|
|
||||||
|
|
||||||
return {'permissions': permissions}
|
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):
|
@is_unit_operation()
|
||||||
|
def user_permission_update(operation_logger, permission, add=None, remove=None, sync_perm=True):
|
||||||
"""
|
"""
|
||||||
Allow or Disallow a user or group to a permission for a specific application
|
Allow or Disallow a user or group to a permission for a specific application
|
||||||
|
|
||||||
Keyword argument:
|
Keyword argument:
|
||||||
app -- an application OR sftp, xmpp (metronome), mail
|
permission -- Name of the permission (e.g. mail or or wordpress or wordpress.editors)
|
||||||
permission -- name of the permission ("main" by default)
|
add -- List of groups or usernames to add to this permission
|
||||||
add_username -- Username to allow
|
remove -- List of groups or usernames to remove from to this permission
|
||||||
add_group -- Groupname to allow
|
|
||||||
del_username -- Username to disallow
|
|
||||||
del_group -- Groupname to disallow
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from yunohost.hook import hook_callback
|
from yunohost.hook import hook_callback
|
||||||
from yunohost.user import user_group_list
|
from yunohost.user import user_group_list
|
||||||
from yunohost.utils.ldap import _get_ldap_interface
|
from yunohost.utils.ldap import _get_ldap_interface
|
||||||
ldap = _get_ldap_interface()
|
ldap = _get_ldap_interface()
|
||||||
|
|
||||||
if permission:
|
# By default, manipulate main permission
|
||||||
if not isinstance(permission, list):
|
if "." not in permission:
|
||||||
permission = [permission]
|
permission = permission + ".main"
|
||||||
else:
|
|
||||||
permission = ["main"]
|
|
||||||
|
|
||||||
if add_group:
|
# Fetch currently allowed groups for this permission
|
||||||
if not isinstance(add_group, list):
|
|
||||||
add_group = [add_group]
|
|
||||||
else:
|
|
||||||
add_group = []
|
|
||||||
|
|
||||||
if add_username:
|
existing_permission = user_permission_list(full=True)["permissions"].get(permission, None)
|
||||||
if not isinstance(add_username, list):
|
if existing_permission is None:
|
||||||
add_username = [add_username]
|
raise YunohostError('permission_not_found', permission=permission)
|
||||||
else:
|
|
||||||
add_username = []
|
|
||||||
|
|
||||||
if del_group:
|
current_allowed_groups = existing_permission["allowed"]
|
||||||
if not isinstance(del_group, list):
|
all_existing_groups = user_group_list()['groups'].keys()
|
||||||
del_group = [del_group]
|
operation_logger.related_to.append(('app', permission.split(".")[0]))
|
||||||
else:
|
|
||||||
del_group = []
|
|
||||||
|
|
||||||
if del_username:
|
# Compute new allowed group list (and make sure what we're doing make sense)
|
||||||
if not isinstance(del_username, list):
|
|
||||||
del_username = [del_username]
|
|
||||||
else:
|
|
||||||
del_username = []
|
|
||||||
|
|
||||||
# Validate that the group exist
|
new_allowed_groups = copy.copy(current_allowed_groups)
|
||||||
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)
|
if add:
|
||||||
add_group.extend(add_username)
|
groups_to_add = [add] if not isinstance(add, list) else add
|
||||||
del_group.extend(del_username)
|
for group in groups_to_add:
|
||||||
|
if group not in all_existing_groups:
|
||||||
|
raise YunohostError('group_unknown', group=group)
|
||||||
|
if group in current_allowed_groups:
|
||||||
|
logger.warning(m18n.n('permission_already_allowed', permission=permission, group=group))
|
||||||
|
else:
|
||||||
|
operation_logger.related_to.append(('group', group))
|
||||||
|
|
||||||
if 'all_users' in add_group or 'all_users' in del_group:
|
new_allowed_groups += groups_to_add
|
||||||
raise YunohostError('edit_permission_with_group_all_users_not_allowed')
|
|
||||||
|
|
||||||
# Populate permission informations
|
if remove:
|
||||||
permission_attrs = [
|
groups_to_remove = [remove] if not isinstance(remove, list) else remove
|
||||||
'cn',
|
for group in groups_to_remove:
|
||||||
'groupPermission',
|
if group not in all_existing_groups:
|
||||||
]
|
raise YunohostError('group_unknown', group=group)
|
||||||
result = ldap.search('ou=permission,dc=yunohost,dc=org',
|
if group not in current_allowed_groups:
|
||||||
'(objectclass=permissionYnh)', permission_attrs)
|
logger.warning(m18n.n('permission_already_disallowed', permission=permission, group=group))
|
||||||
result = {p['cn'][0]: p for p in result}
|
else:
|
||||||
|
operation_logger.related_to.append(('group', group))
|
||||||
|
|
||||||
new_per_dict = {}
|
new_allowed_groups = [g for g in new_allowed_groups if g not in groups_to_remove]
|
||||||
|
|
||||||
for a in app:
|
# If we end up with something like allowed groups is ["all_users", "volunteers"]
|
||||||
for per in permission:
|
# we shall warn the users that they should probably choose between one or the other,
|
||||||
permission_name = per + '.' + a
|
# because the current situation is probably not what they expect / is temporary ?
|
||||||
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 len(new_allowed_groups) > 1 and "all_users" in new_allowed_groups:
|
||||||
if 'cn=all_users,ou=groups,dc=yunohost,dc=org' in new_per_dict[permission_name]:
|
# FIXME : i18n
|
||||||
raise YunohostError('need_define_permission_before')
|
# FIXME : write a better explanation ?
|
||||||
group_name = 'cn=' + g + ',ou=groups,dc=yunohost,dc=org'
|
logger.warning("This permission is currently enabled for all users in addition to other groups. You probably want to either remove the 'all_users' permission or remove the specific groups currently allowed.")
|
||||||
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]:
|
# Don't update LDAP if we update exactly the same values
|
||||||
new_per_dict[permission_name].remove('cn=all_users,ou=groups,dc=yunohost,dc=org')
|
if set(new_allowed_groups) == set(current_allowed_groups):
|
||||||
for g in add_group:
|
# FIXME : i18n
|
||||||
group_name = 'cn=' + g + ',ou=groups,dc=yunohost,dc=org'
|
logger.warning("The permission was not updated all addition/removal requests already match the current state.")
|
||||||
if group_name in new_per_dict[permission_name]:
|
return
|
||||||
logger.warning(m18n.n('group_already_allowed', permission=per, app=a, group=g))
|
|
||||||
else:
|
# Commit the new allowed group list
|
||||||
new_per_dict[permission_name].add(group_name)
|
|
||||||
|
|
||||||
operation_logger.start()
|
operation_logger.start()
|
||||||
|
|
||||||
for per, val in new_per_dict.items():
|
try:
|
||||||
# Don't update LDAP if we update exactly the same values
|
ldap.update('cn=%s,ou=permission' % permission,
|
||||||
if val == set(result[per]['groupPermission'] if 'groupPermission' in result[per] else []):
|
{'groupPermission': ['cn=' + g + ',ou=groups,dc=yunohost,dc=org' for g in new_allowed_groups]})
|
||||||
continue
|
except Exception as e:
|
||||||
if ldap.update('cn=%s,ou=permission' % per, {'groupPermission': val}):
|
raise YunohostError('permission_update_failed', permission=permission, error=e)
|
||||||
p = per.split('.')
|
|
||||||
logger.debug(m18n.n('permission_updated', permission=p[0], app=p[1]))
|
logger.debug(m18n.n('permission_updated', permission=permission))
|
||||||
else:
|
|
||||||
raise YunohostError('permission_update_failed')
|
# Trigger permission sync if asked
|
||||||
|
|
||||||
if sync_perm:
|
if sync_perm:
|
||||||
permission_sync_to_user()
|
permission_sync_to_user()
|
||||||
|
|
||||||
for a in app:
|
new_permission = user_permission_list(full=True)["permissions"][permission]
|
||||||
allowed_users = set()
|
|
||||||
disallowed_users = set()
|
|
||||||
group_list = user_group_list(['member'])['groups']
|
|
||||||
|
|
||||||
for g in add_group:
|
# Trigger app callbacks
|
||||||
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)
|
app = permission.split(".")[0]
|
||||||
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)
|
old_allowed_users = set(existing_permission["corresponding_users"])
|
||||||
|
new_allowed_users = set(new_permission["corresponding_users"])
|
||||||
|
|
||||||
|
effectively_added_users = new_allowed_users - old_allowed_users
|
||||||
|
effectively_removed_users = old_allowed_users - new_allowed_users
|
||||||
|
|
||||||
|
if effectively_added_users:
|
||||||
|
hook_callback('post_app_addaccess', args=[app, ','.join(effectively_added_users)])
|
||||||
|
if effectively_removed_users:
|
||||||
|
hook_callback('post_app_removeaccess', args=[app, ','.join(effectively_removed_users)])
|
||||||
|
|
||||||
|
return new_permission
|
||||||
|
|
||||||
|
|
||||||
def user_permission_clear(operation_logger, app=[], permission=None, sync_perm=True):
|
@is_unit_operation()
|
||||||
|
def user_permission_reset(operation_logger, permission, sync_perm=True):
|
||||||
"""
|
"""
|
||||||
Reset the permission for a specific application
|
Reset a given permission to just 'all_users'
|
||||||
|
|
||||||
Keyword argument:
|
Keyword argument:
|
||||||
app -- an application OR sftp, xmpp (metronome), mail
|
permission -- Name of the permission (e.g. mail or nextcloud or wordpress.editors)
|
||||||
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.hook import hook_callback
|
||||||
from yunohost.utils.ldap import _get_ldap_interface
|
from yunohost.utils.ldap import _get_ldap_interface
|
||||||
ldap = _get_ldap_interface()
|
ldap = _get_ldap_interface()
|
||||||
|
|
||||||
if permission:
|
# By default, manipulate main permission
|
||||||
if not isinstance(permission, list):
|
if "." not in permission:
|
||||||
permission = [permission]
|
permission = permission + ".main"
|
||||||
else:
|
|
||||||
permission = ["main"]
|
# Fetch existing permission
|
||||||
|
|
||||||
|
existing_permission = user_permission_list(full=True)["permissions"].get(permission, None)
|
||||||
|
if existing_permission is None:
|
||||||
|
raise YunohostError('permission_not_found', permission=permission)
|
||||||
|
|
||||||
|
# Update permission with default (all_users)
|
||||||
|
|
||||||
|
operation_logger.related_to.append(('app', permission.split(".")[0]))
|
||||||
|
operation_logger.start()
|
||||||
|
|
||||||
default_permission = {'groupPermission': ['cn=all_users,ou=groups,dc=yunohost,dc=org']}
|
default_permission = {'groupPermission': ['cn=all_users,ou=groups,dc=yunohost,dc=org']}
|
||||||
|
try:
|
||||||
|
ldap.update('cn=%s,ou=permission' % permission, default_permission)
|
||||||
|
except Exception as e:
|
||||||
|
raise YunohostError('permission_update_failed', permission=permission, error=e)
|
||||||
|
|
||||||
# Populate permission informations
|
logger.debug(m18n.n('permission_updated', permission=permission))
|
||||||
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:
|
if sync_perm:
|
||||||
for per in permission:
|
permission_sync_to_user()
|
||||||
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()
|
new_permission = user_permission_list(full=True)["permissions"][permission]
|
||||||
|
|
||||||
for a in app:
|
# Trigger app callbacks
|
||||||
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)
|
app = permission.split(".")[0]
|
||||||
|
|
||||||
|
old_allowed_users = set(existing_permission["corresponding_users"])
|
||||||
|
new_allowed_users = set(new_permission["corresponding_users"])
|
||||||
|
|
||||||
|
effectively_added_users = new_allowed_users - old_allowed_users
|
||||||
|
effectively_removed_users = old_allowed_users - new_allowed_users
|
||||||
|
|
||||||
|
if effectively_added_users:
|
||||||
|
hook_callback('post_app_addaccess', args=[app, ','.join(effectively_added_users)])
|
||||||
|
if effectively_removed_users:
|
||||||
|
hook_callback('post_app_removeaccess', args=[app, ','.join(effectively_removed_users)])
|
||||||
|
|
||||||
|
return new_permission
|
||||||
|
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# The followings methods are *not* directly exposed.
|
||||||
|
# They are used to create/delete the permissions (e.g. during app install/remove)
|
||||||
|
# and by some app helpers to possibly add additional permissions and tweak the urls
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
@is_unit_operation(['permission', 'app'])
|
@is_unit_operation()
|
||||||
def permission_add(operation_logger, app, permission, urls=None, default_allow=True, sync_perm=True):
|
def permission_create(operation_logger, permission, urls=None, sync_perm=True):
|
||||||
"""
|
"""
|
||||||
Create a new permission for a specific application
|
Create a new permission for a specific application
|
||||||
|
|
||||||
Keyword argument:
|
Keyword argument:
|
||||||
app -- an application OR sftp, xmpp (metronome), mail
|
permission -- Name of the permission (e.g. mail or nextcloud or wordpress.editors)
|
||||||
permission -- name of the permission ("main" by default)
|
|
||||||
urls -- list of urls to specify for the permission
|
urls -- list of urls to specify for the permission
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from yunohost.domain import _normalize_domain_path
|
|
||||||
from yunohost.utils.ldap import _get_ldap_interface
|
from yunohost.utils.ldap import _get_ldap_interface
|
||||||
ldap = _get_ldap_interface()
|
ldap = _get_ldap_interface()
|
||||||
|
|
||||||
|
# By default, manipulate main permission
|
||||||
|
if "." not in permission:
|
||||||
|
permission = permission + ".main"
|
||||||
|
|
||||||
# Validate uniqueness of permission in LDAP
|
# Validate uniqueness of permission in LDAP
|
||||||
permission_name = str(permission + '.' + app) # str(...) Fix encoding issue
|
if ldap.get_conflict({'cn': permission},
|
||||||
conflict = ldap.get_conflict({
|
base_dn='ou=permission,dc=yunohost,dc=org'):
|
||||||
'cn': permission_name
|
raise YunohostError('permission_already_exist', permission=permission)
|
||||||
}, base_dn='ou=permission,dc=yunohost,dc=org')
|
|
||||||
if conflict:
|
|
||||||
raise YunohostError('permission_already_exist', permission=permission, app=app)
|
|
||||||
|
|
||||||
# Get random GID
|
# Get random GID
|
||||||
all_gid = {x.gr_gid for x in grp.getgrall()}
|
all_gid = {x.gr_gid for x in grp.getgrall()}
|
||||||
|
@ -349,180 +288,175 @@ def permission_add(operation_logger, app, permission, urls=None, default_allow=T
|
||||||
|
|
||||||
attr_dict = {
|
attr_dict = {
|
||||||
'objectClass': ['top', 'permissionYnh', 'posixGroup'],
|
'objectClass': ['top', 'permissionYnh', 'posixGroup'],
|
||||||
'cn': permission_name,
|
'cn': str(permission),
|
||||||
'gidNumber': gid,
|
'gidNumber': gid,
|
||||||
}
|
}
|
||||||
if default_allow:
|
|
||||||
attr_dict['groupPermission'] = 'cn=all_users,ou=groups,dc=yunohost,dc=org'
|
# For main permission, we add all users by default
|
||||||
|
if permission.endswith(".main"):
|
||||||
|
attr_dict['groupPermission'] = ['cn=all_users,ou=groups,dc=yunohost,dc=org']
|
||||||
|
|
||||||
if urls:
|
if urls:
|
||||||
attr_dict['URL'] = []
|
attr_dict['URL'] = [_normalize_url(url) for url in urls]
|
||||||
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.related_to.append(('app', permission.split(".")[0]))
|
||||||
operation_logger.start()
|
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')
|
try:
|
||||||
|
ldap.add('cn=%s,ou=permission' % permission, attr_dict)
|
||||||
|
except Exception as e:
|
||||||
|
raise YunohostError('permission_creation_failed', permission=permission, error=e)
|
||||||
|
|
||||||
|
|
||||||
@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:
|
if sync_perm:
|
||||||
permission_sync_to_user()
|
permission_sync_to_user()
|
||||||
logger.debug(m18n.n('permission_deleted', permission=permission, app=app))
|
|
||||||
|
logger.debug(m18n.n('permission_created', permission=permission))
|
||||||
|
return user_permission_list(full=True)["permissions"][permission]
|
||||||
|
|
||||||
|
|
||||||
def permission_sync_to_user(force=False):
|
@is_unit_operation()
|
||||||
|
def permission_urls(operation_logger, permission, add=None, remove=None, sync_perm=True):
|
||||||
|
"""
|
||||||
|
Update urls related to a permission for a specific application
|
||||||
|
|
||||||
|
Keyword argument:
|
||||||
|
permission -- Name of the permission (e.g. mail or nextcloud or wordpress.editors)
|
||||||
|
add -- List of urls to add
|
||||||
|
remove -- List of urls to remove
|
||||||
|
|
||||||
|
"""
|
||||||
|
from yunohost.utils.ldap import _get_ldap_interface
|
||||||
|
ldap = _get_ldap_interface()
|
||||||
|
|
||||||
|
# Fetch existing permission
|
||||||
|
|
||||||
|
existing_permission = user_permission_list(full=True)["permissions"].get(permission, None)
|
||||||
|
if not existing_permission:
|
||||||
|
raise YunohostError('permission_not_found', permission=permission)
|
||||||
|
|
||||||
|
# Compute new url list
|
||||||
|
|
||||||
|
new_urls = copy.copy(existing_permission["urls"])
|
||||||
|
|
||||||
|
if add:
|
||||||
|
urls_to_add = [add] if not isinstance(add, list) else add
|
||||||
|
urls_to_add = [_normalize_url(url) for url in urls_to_add]
|
||||||
|
new_urls += urls_to_add
|
||||||
|
if remove:
|
||||||
|
urls_to_remove = [remove] if not isinstance(remove, list) else remove
|
||||||
|
urls_to_remove = [_normalize_url(url) for url in urls_to_remove]
|
||||||
|
new_urls = [u for u in new_urls if u not in urls_to_remove]
|
||||||
|
|
||||||
|
if set(new_urls) == set(existing_permission["urls"]):
|
||||||
|
logger.warning(m18n.n('permission_update_nothing_to_do'))
|
||||||
|
return existing_permission
|
||||||
|
|
||||||
|
# Actually commit the change
|
||||||
|
|
||||||
|
operation_logger.related_to.append(('app', permission.split(".")[0]))
|
||||||
|
operation_logger.start()
|
||||||
|
|
||||||
|
try:
|
||||||
|
ldap.update('cn=%s,ou=permission' % permission, {'URL': new_urls})
|
||||||
|
except Exception as e:
|
||||||
|
raise YunohostError('permission_update_failed', permission=permission, error=e)
|
||||||
|
|
||||||
|
if sync_perm:
|
||||||
|
permission_sync_to_user()
|
||||||
|
|
||||||
|
logger.debug(m18n.n('permission_updated', permission=permission))
|
||||||
|
return user_permission_list(full=True)["permissions"][permission]
|
||||||
|
|
||||||
|
|
||||||
|
@is_unit_operation()
|
||||||
|
def permission_delete(operation_logger, permission, force=False, sync_perm=True):
|
||||||
|
"""
|
||||||
|
Delete a permission
|
||||||
|
|
||||||
|
Keyword argument:
|
||||||
|
permission -- Name of the permission (e.g. mail or nextcloud or wordpress.editors)
|
||||||
|
"""
|
||||||
|
|
||||||
|
# By default, manipulate main permission
|
||||||
|
if "." not in permission:
|
||||||
|
permission = permission + ".main"
|
||||||
|
|
||||||
|
if permission.endswith(".main") and not force:
|
||||||
|
raise YunohostError('permission_cannot_remove_main')
|
||||||
|
|
||||||
|
from yunohost.utils.ldap import _get_ldap_interface
|
||||||
|
ldap = _get_ldap_interface()
|
||||||
|
|
||||||
|
# Make sure this permission exists
|
||||||
|
|
||||||
|
existing_permission = user_permission_list(full=True)["permissions"].get(permission, None)
|
||||||
|
if not existing_permission:
|
||||||
|
raise YunohostError('permission_not_found', permission=permission)
|
||||||
|
|
||||||
|
# Actually delete the permission
|
||||||
|
|
||||||
|
operation_logger.related_to.append(('app', permission.split(".")[0]))
|
||||||
|
operation_logger.start()
|
||||||
|
|
||||||
|
try:
|
||||||
|
ldap.remove('cn=%s,ou=permission' % permission)
|
||||||
|
except Exception as e:
|
||||||
|
raise YunohostError('permission_deletion_failed', permission=permission, error=e)
|
||||||
|
|
||||||
|
if sync_perm:
|
||||||
|
permission_sync_to_user()
|
||||||
|
logger.debug(m18n.n('permission_deleted', permission=permission))
|
||||||
|
|
||||||
|
|
||||||
|
def permission_sync_to_user():
|
||||||
"""
|
"""
|
||||||
Sychronise the inheritPermission attribut in the permission object from the
|
Sychronise the inheritPermission attribut in the permission object from the
|
||||||
user<->group link and the group<->permission link
|
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
|
import os
|
||||||
from yunohost.app import app_ssowatconf
|
from yunohost.app import app_ssowatconf
|
||||||
|
from yunohost.user import user_group_list
|
||||||
from yunohost.utils.ldap import _get_ldap_interface
|
from yunohost.utils.ldap import _get_ldap_interface
|
||||||
ldap = _get_ldap_interface()
|
ldap = _get_ldap_interface()
|
||||||
|
|
||||||
permission_attrs = [
|
groups = user_group_list(full=True)["groups"]
|
||||||
'cn',
|
permissions = user_permission_list(full=True)["permissions"]
|
||||||
'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',
|
for permission_name, permission_infos in permissions.items():
|
||||||
'(objectclass=permissionYnh)',
|
|
||||||
['cn', 'inheritPermission', 'groupPermission', 'memberUid']):
|
|
||||||
|
|
||||||
if 'groupPermission' not in per:
|
# These are the users currently allowed because there's an 'inheritPermission' object corresponding to it
|
||||||
per['groupPermission'] = []
|
currently_allowed_users = set(permission_infos["corresponding_users"])
|
||||||
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:
|
# These are the users that should be allowed because they are member of a group that is allowed for this permission ...
|
||||||
per['inheritPermission'] = []
|
should_be_allowed_users = set([user for group in permission_infos["allowed"] for user in groups[group]["members"]])
|
||||||
if 'memberUid' not in per:
|
|
||||||
per['memberUid'] = []
|
|
||||||
|
|
||||||
uid_val = [v.split("=")[1].split(",")[0] for v in user_permission]
|
# Note that a LDAP operation with the same value that is in LDAP crash SLAP.
|
||||||
if user_permission == set(per['inheritPermission']) and set(uid_val) == set(per['memberUid']) and not force:
|
# So we need to check before each ldap operation that we really change something in LDAP
|
||||||
|
if currently_allowed_users == should_be_allowed_users:
|
||||||
|
# We're all good, this permission is already correctly synchronized !
|
||||||
continue
|
continue
|
||||||
inheritPermission = {'inheritPermission': user_permission, 'memberUid': uid_val}
|
|
||||||
if force:
|
new_inherited_perms = {'inheritPermission': ["uid=%s,ou=users,dc=yunohost,dc=org" % u for u in should_be_allowed_users],
|
||||||
if per['groupPermission']:
|
'memberUid': should_be_allowed_users}
|
||||||
if not ldap.update('cn=%s,ou=permission' % per['cn'][0], {'groupPermission': []}):
|
|
||||||
raise YunohostError('permission_update_failed_clear')
|
# Commit the change with the new inherited stuff
|
||||||
if not ldap.update('cn=%s,ou=permission' % per['cn'][0], {'groupPermission': per['groupPermission']}):
|
try:
|
||||||
raise YunohostError('permission_update_failed_populate')
|
ldap.update('cn=%s,ou=permission' % permission_name, new_inherited_perms)
|
||||||
if per['inheritPermission']:
|
except Exception as e:
|
||||||
if not ldap.update('cn=%s,ou=permission' % per['cn'][0], {'inheritPermission': []}):
|
raise YunohostError('permission_update_failed', permission=permission_name, error=e)
|
||||||
raise YunohostError('permission_update_failed_clear')
|
|
||||||
if user_permission:
|
logger.debug("The permission database has been resynchronized")
|
||||||
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()
|
app_ssowatconf()
|
||||||
|
|
||||||
# Reload unscd, otherwise the group ain't propagated to the LDAP database
|
# Reload unscd, otherwise the group ain't propagated to the LDAP database
|
||||||
os.system('nscd --invalidate=passwd')
|
os.system('nscd --invalidate=passwd')
|
||||||
os.system('nscd --invalidate=group')
|
os.system('nscd --invalidate=group')
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_url(url):
|
||||||
|
from yunohost.domain import _normalize_domain_path
|
||||||
|
domain = url[:url.index('/')]
|
||||||
|
path = url[url.index('/'):]
|
||||||
|
domain, path = _normalize_domain_path(domain, path)
|
||||||
|
return domain + path
|
||||||
|
|
|
@ -10,7 +10,7 @@ from yunohost.app import _is_installed
|
||||||
from yunohost.backup import backup_create, backup_restore, backup_list, backup_info, backup_delete, _recursive_umount
|
from yunohost.backup import backup_create, backup_restore, backup_list, backup_info, backup_delete, _recursive_umount
|
||||||
from yunohost.domain import _get_maindomain
|
from yunohost.domain import _get_maindomain
|
||||||
from yunohost.utils.error import YunohostError
|
from yunohost.utils.error import YunohostError
|
||||||
from yunohost.user import user_permission_list
|
from yunohost.user import user_permission_list, user_create, user_list, user_delete
|
||||||
from yunohost.tests.test_permission import check_LDAP_db_integrity, check_permission_for_apps
|
from yunohost.tests.test_permission import check_LDAP_db_integrity, check_permission_for_apps
|
||||||
|
|
||||||
# Get main domain
|
# Get main domain
|
||||||
|
@ -38,10 +38,10 @@ def setup_function(function):
|
||||||
add_archive_wordpress_from_2p4()
|
add_archive_wordpress_from_2p4()
|
||||||
assert len(backup_list()["archives"]) == 1
|
assert len(backup_list()["archives"]) == 1
|
||||||
|
|
||||||
if "with_backup_legacy_app_installed" in markers:
|
if "with_legacy_app_installed" in markers:
|
||||||
assert not app_is_installed("backup_legacy_app")
|
assert not app_is_installed("legacy_app")
|
||||||
install_app("backup_legacy_app_ynh", "/yolo")
|
install_app("legacy_app_ynh", "/yolo")
|
||||||
assert app_is_installed("backup_legacy_app")
|
assert app_is_installed("legacy_app")
|
||||||
|
|
||||||
if "with_backup_recommended_app_installed" in markers:
|
if "with_backup_recommended_app_installed" in markers:
|
||||||
assert not app_is_installed("backup_recommended_app")
|
assert not app_is_installed("backup_recommended_app")
|
||||||
|
@ -59,6 +59,13 @@ def setup_function(function):
|
||||||
add_archive_system_from_2p4()
|
add_archive_system_from_2p4()
|
||||||
assert len(backup_list()["archives"]) == 1
|
assert len(backup_list()["archives"]) == 1
|
||||||
|
|
||||||
|
if "with_permission_app_installed" in markers:
|
||||||
|
assert not app_is_installed("permissions_app")
|
||||||
|
user_create("alice", "Alice", "White", "alice@" + maindomain, "test123Ynh")
|
||||||
|
install_app("permissions_app_ynh", "/urlpermissionapp"
|
||||||
|
"&admin=alice")
|
||||||
|
assert app_is_installed("permissions_app")
|
||||||
|
|
||||||
|
|
||||||
def teardown_function(function):
|
def teardown_function(function):
|
||||||
|
|
||||||
|
@ -73,6 +80,9 @@ def teardown_function(function):
|
||||||
if "clean_opt_dir" in markers:
|
if "clean_opt_dir" in markers:
|
||||||
shutil.rmtree("/opt/test_backup_output_directory")
|
shutil.rmtree("/opt/test_backup_output_directory")
|
||||||
|
|
||||||
|
if "alice" in user_list()["users"]:
|
||||||
|
user_delete("alice")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def check_LDAP_db_integrity_call():
|
def check_LDAP_db_integrity_call():
|
||||||
|
@ -92,6 +102,9 @@ def check_permission_for_apps_call():
|
||||||
|
|
||||||
def app_is_installed(app):
|
def app_is_installed(app):
|
||||||
|
|
||||||
|
if app == "permissions_app":
|
||||||
|
return _is_installed(app)
|
||||||
|
|
||||||
# These are files we know should be installed by the app
|
# These are files we know should be installed by the app
|
||||||
app_files = []
|
app_files = []
|
||||||
app_files.append("/etc/nginx/conf.d/%s.d/%s.conf" % (maindomain, app))
|
app_files.append("/etc/nginx/conf.d/%s.d/%s.conf" % (maindomain, app))
|
||||||
|
@ -105,7 +118,7 @@ def backup_test_dependencies_are_met():
|
||||||
|
|
||||||
# Dummy test apps (or backup archives)
|
# Dummy test apps (or backup archives)
|
||||||
assert os.path.exists("./tests/apps/backup_wordpress_from_2p4")
|
assert os.path.exists("./tests/apps/backup_wordpress_from_2p4")
|
||||||
assert os.path.exists("./tests/apps/backup_legacy_app_ynh")
|
assert os.path.exists("./tests/apps/legacy_app_ynh")
|
||||||
assert os.path.exists("./tests/apps/backup_recommended_app_ynh")
|
assert os.path.exists("./tests/apps/backup_recommended_app_ynh")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -155,14 +168,9 @@ def delete_all_backups():
|
||||||
|
|
||||||
def uninstall_test_apps_if_needed():
|
def uninstall_test_apps_if_needed():
|
||||||
|
|
||||||
if _is_installed("backup_legacy_app"):
|
for app in ["legacy_app", "backup_recommended_app", "wordpress", "permissions_app"]:
|
||||||
app_remove("backup_legacy_app")
|
if _is_installed(app):
|
||||||
|
app_remove(app)
|
||||||
if _is_installed("backup_recommended_app"):
|
|
||||||
app_remove("backup_recommended_app")
|
|
||||||
|
|
||||||
if _is_installed("wordpress"):
|
|
||||||
app_remove("wordpress")
|
|
||||||
|
|
||||||
|
|
||||||
def install_app(app, path, additionnal_args=""):
|
def install_app(app, path, additionnal_args=""):
|
||||||
|
@ -497,10 +505,10 @@ def test_restore_app_already_installed(mocker):
|
||||||
assert _is_installed("wordpress")
|
assert _is_installed("wordpress")
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.with_backup_legacy_app_installed
|
@pytest.mark.with_legacy_app_installed
|
||||||
def test_backup_and_restore_legacy_app():
|
def test_backup_and_restore_legacy_app():
|
||||||
|
|
||||||
_test_backup_and_restore_app("backup_legacy_app")
|
_test_backup_and_restore_app("legacy_app")
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.with_backup_recommended_app_installed
|
@pytest.mark.with_backup_recommended_app_installed
|
||||||
|
@ -514,6 +522,35 @@ def test_backup_and_restore_with_ynh_restore():
|
||||||
|
|
||||||
_test_backup_and_restore_app("backup_recommended_app")
|
_test_backup_and_restore_app("backup_recommended_app")
|
||||||
|
|
||||||
|
@pytest.mark.with_permission_app_installed
|
||||||
|
def test_backup_and_restore_permission_app():
|
||||||
|
|
||||||
|
res = user_permission_list(full=True)['permissions']
|
||||||
|
assert "permissions_app.main" in res
|
||||||
|
assert "permissions_app.admin" in res
|
||||||
|
assert "permissions_app.dev" in res
|
||||||
|
assert res['permissions_app.main']['urls'] == [maindomain + "/urlpermissionapp"]
|
||||||
|
assert res['permissions_app.admin']['urls'] == [maindomain + "/urlpermissionapp/admin"]
|
||||||
|
assert res['permissions_app.dev']['urls'] == [maindomain + "/urlpermissionapp/dev"]
|
||||||
|
|
||||||
|
assert res['permissions_app.main']['allowed'] == ["all_users"]
|
||||||
|
assert res['permissions_app.admin']['allowed'] == ["alice"]
|
||||||
|
assert res['permissions_app.dev']['allowed'] == []
|
||||||
|
|
||||||
|
_test_backup_and_restore_app("permissions_app")
|
||||||
|
|
||||||
|
res = user_permission_list(full=True)['permissions']
|
||||||
|
assert "permissions_app.main" in res
|
||||||
|
assert "permissions_app.admin" in res
|
||||||
|
assert "permissions_app.dev" in res
|
||||||
|
assert res['permissions_app.main']['urls'] == [maindomain + "/urlpermissionapp"]
|
||||||
|
assert res['permissions_app.admin']['urls'] == [maindomain + "/urlpermissionapp/admin"]
|
||||||
|
assert res['permissions_app.dev']['urls'] == [maindomain + "/urlpermissionapp/dev"]
|
||||||
|
|
||||||
|
assert res['permissions_app.main']['allowed'] == ["all_users"]
|
||||||
|
assert res['permissions_app.admin']['allowed'] == ["alice"]
|
||||||
|
assert res['permissions_app.dev']['allowed'] == []
|
||||||
|
|
||||||
|
|
||||||
def _test_backup_and_restore_app(app):
|
def _test_backup_and_restore_app(app):
|
||||||
|
|
||||||
|
@ -531,7 +568,7 @@ def _test_backup_and_restore_app(app):
|
||||||
# Uninstall the app
|
# Uninstall the app
|
||||||
app_remove(app)
|
app_remove(app)
|
||||||
assert not app_is_installed(app)
|
assert not app_is_installed(app)
|
||||||
assert app not in user_permission_list()['permissions']
|
assert app+".main" not in user_permission_list()['permissions']
|
||||||
|
|
||||||
# Restore the app
|
# Restore the app
|
||||||
backup_restore(system=None, name=archives[0],
|
backup_restore(system=None, name=archives[0],
|
||||||
|
@ -541,8 +578,7 @@ def _test_backup_and_restore_app(app):
|
||||||
|
|
||||||
# Check permission
|
# Check permission
|
||||||
per_list = user_permission_list()['permissions']
|
per_list = user_permission_list()['permissions']
|
||||||
assert app in per_list
|
assert app+".main" in per_list
|
||||||
assert "main" in per_list[app]
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Some edge cases #
|
# Some edge cases #
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from moulinette.core import MoulinetteError
|
from yunohost.app import app_install, app_remove, app_change_url, app_list, app_map
|
||||||
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.user import user_list, user_info, user_create, user_delete, user_update, \
|
||||||
from yunohost.permission import permission_add, permission_update, permission_remove
|
user_group_list, user_group_create, user_group_delete, user_group_update, user_group_info
|
||||||
|
from yunohost.permission import user_permission_update, user_permission_list, user_permission_reset, \
|
||||||
|
permission_create, permission_urls, permission_delete
|
||||||
from yunohost.domain import _get_maindomain
|
from yunohost.domain import _get_maindomain
|
||||||
from yunohost.utils.error import YunohostError
|
from yunohost.utils.error import YunohostError
|
||||||
|
|
||||||
|
@ -18,20 +20,18 @@ def clean_user_groups_permission():
|
||||||
if g != "all_users":
|
if g != "all_users":
|
||||||
user_group_delete(g)
|
user_group_delete(g)
|
||||||
|
|
||||||
for a, per in user_permission_list()['permissions'].items():
|
for p in user_permission_list()['permissions']:
|
||||||
if a in ['wiki', 'blog', 'site']:
|
if any(p.startswith(name) for name in ["wiki", "blog", "site", "permissions_app"]):
|
||||||
for p in per:
|
permission_delete(p, force=True, sync_perm=False)
|
||||||
permission_remove(a, p, force=True, sync_perm=False)
|
|
||||||
|
|
||||||
def setup_function(function):
|
def setup_function(function):
|
||||||
clean_user_groups_permission()
|
clean_user_groups_permission()
|
||||||
|
|
||||||
user_create("alice", "Alice", "White", "alice@" + maindomain, "test123Ynh")
|
user_create("alice", "Alice", "White", "alice@" + maindomain, "test123Ynh")
|
||||||
user_create("bob", "Bob", "Snow", "bob@" + maindomain, "test123Ynh")
|
user_create("bob", "Bob", "Snow", "bob@" + maindomain, "test123Ynh")
|
||||||
permission_add("wiki", "main", [maindomain + "/wiki"], sync_perm=False)
|
permission_create("wiki.main", urls=[maindomain + "/wiki"], sync_perm=False)
|
||||||
permission_add("blog", "main", sync_perm=False)
|
permission_create("blog.main", sync_perm=False)
|
||||||
|
user_permission_update("blog.main", remove="all_users", add="alice")
|
||||||
user_permission_add(["blog"], "main", group="alice")
|
|
||||||
|
|
||||||
def teardown_function(function):
|
def teardown_function(function):
|
||||||
clean_user_groups_permission()
|
clean_user_groups_permission()
|
||||||
|
@ -57,7 +57,7 @@ def check_LDAP_db_integrity():
|
||||||
# One part should be done automatically by the "memberOf" overlay of LDAP.
|
# 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
|
# 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
|
from yunohost.utils.ldap import _get_ldap_interface, _ldap_path_extract
|
||||||
ldap = _get_ldap_interface()
|
ldap = _get_ldap_interface()
|
||||||
|
|
||||||
user_search = ldap.search('ou=users,dc=yunohost,dc=org',
|
user_search = ldap.search('ou=users,dc=yunohost,dc=org',
|
||||||
|
@ -76,161 +76,155 @@ def check_LDAP_db_integrity():
|
||||||
|
|
||||||
for user in user_search:
|
for user in user_search:
|
||||||
user_dn = 'uid=' + user['uid'][0] + ',ou=users,dc=yunohost,dc=org'
|
user_dn = 'uid=' + user['uid'][0] + ',ou=users,dc=yunohost,dc=org'
|
||||||
group_list = [m.split("=")[1].split(",")[0] for m in user['memberOf']]
|
group_list = [_ldap_path_extract(m, "cn") for m in user['memberOf']]
|
||||||
permission_list = []
|
permission_list = [_ldap_path_extract(m, "cn") for m in user.get('permission', [])]
|
||||||
if 'permission' in user:
|
|
||||||
permission_list = [m.split("=")[1].split(",")[0] for m in user['permission']]
|
|
||||||
|
|
||||||
|
# This user's DN sould be found in all groups it is a member of
|
||||||
for group in group_list:
|
for group in group_list:
|
||||||
assert user_dn in group_map[group]['member']
|
assert user_dn in group_map[group]['member']
|
||||||
|
|
||||||
|
# This user's DN should be found in all perms it has access to
|
||||||
for permission in permission_list:
|
for permission in permission_list:
|
||||||
assert user_dn in permission_map[permission]['inheritPermission']
|
assert user_dn in permission_map[permission]['inheritPermission']
|
||||||
|
|
||||||
for permission in permission_search:
|
for permission in permission_search:
|
||||||
permission_dn = 'cn=' + permission['cn'][0] + ',ou=permission,dc=yunohost,dc=org'
|
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']]
|
|
||||||
|
|
||||||
|
# inheritPermission uid's should match memberUids
|
||||||
|
user_list = [_ldap_path_extract(m, "uid") for m in permission.get('inheritPermission', [])]
|
||||||
|
assert set(user_list) == set(permission.get('memberUid', []))
|
||||||
|
|
||||||
|
# This perm's DN should be found on all related users it is related to
|
||||||
for user in user_list:
|
for user in user_list:
|
||||||
assert permission_dn in user_map[user]['permission']
|
assert permission_dn in user_map[user]['permission']
|
||||||
|
|
||||||
|
# Same for groups : we should find the permission's DN for all related groups
|
||||||
|
group_list = [_ldap_path_extract(m, "cn") for m in permission.get('groupPermission', [])]
|
||||||
for group in group_list:
|
for group in group_list:
|
||||||
assert permission_dn in group_map[group]['permission']
|
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']]
|
# The list of user in the group should be a subset of all users related to the current permission
|
||||||
assert set(user_list_in_group) <= set(user_list)
|
users_in_group = [_ldap_path_extract(m, "uid") for m in group_map[group].get("member", [])]
|
||||||
|
assert set(users_in_group) <= set(user_list)
|
||||||
|
|
||||||
for group in group_search:
|
for group in group_search:
|
||||||
group_dn = 'cn=' + group['cn'][0] + ',ou=groups,dc=yunohost,dc=org'
|
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']]
|
|
||||||
|
|
||||||
|
user_list = [_ldap_path_extract(m, "uid") for m in group.get("member", [])]
|
||||||
|
# For primary groups, we should find that :
|
||||||
|
# - len(user_list) is 1 (a primary group has only 1 member)
|
||||||
|
# - the group name should be an existing yunohost user
|
||||||
|
# - memberUid is empty (meaning no other member than the corresponding user)
|
||||||
|
if group['cn'][0] in user_list:
|
||||||
|
assert len(user_list) == 1
|
||||||
|
assert group["cn"][0] in user_map
|
||||||
|
assert group.get('memberUid', []) == []
|
||||||
|
# Otherwise, user_list and memberUid should have the same content
|
||||||
|
else:
|
||||||
|
assert set(user_list) == set(group.get('memberUid', []))
|
||||||
|
|
||||||
|
# For all users members, this group should be in the "memberOf" on the other side
|
||||||
for user in user_list:
|
for user in user_list:
|
||||||
assert group_dn in user_map[user]['memberOf']
|
assert group_dn in user_map[user]['memberOf']
|
||||||
|
|
||||||
|
# For all the permissions of this group, the group should be among the "groupPermission" on the other side
|
||||||
|
permission_list = [_ldap_path_extract(m, "cn") for m in group.get('permission', [])]
|
||||||
for permission in permission_list:
|
for permission in permission_list:
|
||||||
assert group_dn in permission_map[permission]['groupPermission']
|
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']]
|
# And the list of user of this group (user_list) should be a subset of all allowed users for this perm...
|
||||||
assert set(user_list) <= set(allowed_user_list)
|
allowed_user_list = [_ldap_path_extract(m, "uid") for m in permission_map[permission].get('inheritPermission', [])]
|
||||||
|
assert set(user_list) <= set(allowed_user_list)
|
||||||
|
|
||||||
|
|
||||||
def check_permission_for_apps():
|
def check_permission_for_apps():
|
||||||
# We check that the for each installed apps we have at last the "main" permission
|
# 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
|
# 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
|
# is mail, xmpp, and sftp
|
||||||
|
|
||||||
from yunohost.utils.ldap import _get_ldap_interface
|
app_perms = user_permission_list(ignore_system_perms=True)["permissions"].keys()
|
||||||
ldap = _get_ldap_interface()
|
|
||||||
permission_search = ldap.search('ou=permission,dc=yunohost,dc=org',
|
# Keep only the prefix so that
|
||||||
'(objectclass=permissionYnh)',
|
# ["foo.main", "foo.pwet", "bar.main"]
|
||||||
['cn', 'groupPermission', 'inheritPermission', 'memberUid'])
|
# becomes
|
||||||
|
# {"bar", "foo"}
|
||||||
|
# and compare this to the list of installed apps ...
|
||||||
|
|
||||||
|
app_perms_prefix = set(p.split(".")[0] for p in app_perms)
|
||||||
|
|
||||||
installed_apps = {app['id'] for app in app_list(installed=True)['apps']}
|
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'])
|
assert installed_apps == app_perms_prefix
|
||||||
if 'sftp' in permission_list_set:
|
|
||||||
extra_service_permission.add('sftp')
|
|
||||||
assert installed_apps == permission_list_set - extra_service_permission
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# List functions
|
# List functions
|
||||||
#
|
#
|
||||||
|
|
||||||
def test_list_permission():
|
def test_permission_list():
|
||||||
res = user_permission_list()['permissions']
|
res = user_permission_list(full=True)['permissions']
|
||||||
|
|
||||||
assert "wiki" in res
|
assert "wiki.main" in res
|
||||||
assert "main" in res['wiki']
|
assert "blog.main" in res
|
||||||
assert "blog" in res
|
assert "mail.main" in res
|
||||||
assert "main" in res['blog']
|
assert "xmpp.main" in res
|
||||||
assert "mail" in res
|
assert res['wiki.main']['allowed'] == ["all_users"]
|
||||||
assert "main" in res['mail']
|
assert res['blog.main']['allowed'] == ["alice"]
|
||||||
assert "metronome" in res
|
assert set(res['wiki.main']['corresponding_users']) == set(["alice", "bob"])
|
||||||
assert "main" in res['metronome']
|
assert res['blog.main']['corresponding_users'] == ["alice"]
|
||||||
assert ["all_users"] == res['wiki']['main']['allowed_groups']
|
assert res['wiki.main']['urls'] == [maindomain + "/wiki"]
|
||||||
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
|
# Create - Remove functions
|
||||||
#
|
#
|
||||||
|
|
||||||
def test_add_permission_1():
|
def test_permission_create_main():
|
||||||
permission_add("site", "test")
|
permission_create("site.main")
|
||||||
|
|
||||||
|
res = user_permission_list(full=True)['permissions']
|
||||||
|
assert "site.main" in res
|
||||||
|
assert res['site.main']['allowed'] == ["all_users"]
|
||||||
|
assert set(res['site.main']['corresponding_users']) == set(["alice", "bob"])
|
||||||
|
|
||||||
|
|
||||||
|
def test_permission_create_extra():
|
||||||
|
permission_create("site.test")
|
||||||
|
|
||||||
|
res = user_permission_list(full=True)['permissions']
|
||||||
|
assert "site.test" in res
|
||||||
|
# all_users is only enabled by default on .main perms
|
||||||
|
assert "all_users" not in res['site.test']['allowed']
|
||||||
|
assert res['site.test']['corresponding_users'] == []
|
||||||
|
|
||||||
|
def test_permission_delete():
|
||||||
|
permission_delete("wiki.main", force=True)
|
||||||
|
|
||||||
res = user_permission_list()['permissions']
|
res = user_permission_list()['permissions']
|
||||||
assert "site" in res
|
assert "wiki.main" not 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
|
# Error on create - remove function
|
||||||
#
|
#
|
||||||
|
|
||||||
def test_add_bad_permission():
|
def test_permission_create_already_existing():
|
||||||
# Create permission with same name
|
|
||||||
with pytest.raises(YunohostError):
|
with pytest.raises(YunohostError):
|
||||||
permission_add("wiki", "main")
|
permission_create("wiki.main")
|
||||||
|
|
||||||
def test_remove_bad_permission():
|
def test_permission_delete_doesnt_existing():
|
||||||
# Remove not existant permission
|
with pytest.raises(YunohostError):
|
||||||
with pytest.raises(MoulinetteError):
|
permission_delete("doesnt.exist", force=True)
|
||||||
permission_remove("non_exit", "main", force=True)
|
|
||||||
|
|
||||||
res = user_permission_list()['permissions']
|
res = user_permission_list()['permissions']
|
||||||
assert "wiki" in res
|
assert "wiki.main" in res
|
||||||
assert "main" in res['wiki']
|
assert "blog.main" in res
|
||||||
assert "blog" in res
|
assert "mail.main" in res
|
||||||
assert "main" in res['blog']
|
assert "xmpp.main" in res
|
||||||
assert "mail" in res
|
|
||||||
assert "main" in res ['mail']
|
|
||||||
assert "metronome" in res
|
|
||||||
assert "main" in res['metronome']
|
|
||||||
|
|
||||||
def test_remove_main_permission():
|
def test_permission_delete_main_without_force():
|
||||||
with pytest.raises(YunohostError):
|
with pytest.raises(YunohostError):
|
||||||
permission_remove("blog", "main")
|
permission_delete("blog.main")
|
||||||
|
|
||||||
res = user_permission_list()['permissions']
|
res = user_permission_list()['permissions']
|
||||||
assert "mail" in res
|
assert "blog.main" in res
|
||||||
assert "main" in res['mail']
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Update functions
|
# Update functions
|
||||||
|
@ -238,182 +232,154 @@ def test_remove_main_permission():
|
||||||
|
|
||||||
# user side functions
|
# user side functions
|
||||||
|
|
||||||
def test_allow_first_group():
|
def test_permission_add_group():
|
||||||
# Remove permission to all_users and define per users
|
user_permission_update("wiki.main", add="alice")
|
||||||
user_permission_add(["wiki"], "main", group="alice")
|
|
||||||
|
|
||||||
res = user_permission_list()['permissions']
|
res = user_permission_list(full=True)['permissions']
|
||||||
assert ['alice'] == res['wiki']['main']['allowed_users']
|
assert set(res['wiki.main']['allowed']) == set(["all_users", "alice"])
|
||||||
assert ['alice'] == res['wiki']['main']['allowed_groups']
|
assert set(res['wiki.main']['corresponding_users']) == set(["alice", "bob"])
|
||||||
|
|
||||||
def test_allow_other_group():
|
def test_permission_remove_group():
|
||||||
# Allow new user in a permission
|
user_permission_update("blog.main", remove="alice")
|
||||||
user_permission_add(["blog"], "main", group="bob")
|
|
||||||
|
|
||||||
res = user_permission_list()['permissions']
|
res = user_permission_list(full=True)['permissions']
|
||||||
assert set(["alice", "bob"]) == set(res['blog']['main']['allowed_users'])
|
assert res['blog.main']['allowed'] == []
|
||||||
assert set(["alice", "bob"]) == set(res['blog']['main']['allowed_groups'])
|
assert res['blog.main']['corresponding_users'] == []
|
||||||
|
|
||||||
def test_disallow_group_1():
|
def test_permission_add_and_remove_group():
|
||||||
# Disallow a user in a permission
|
user_permission_update("wiki.main", add="alice", remove="all_users")
|
||||||
user_permission_remove(["blog"], "main", group="alice")
|
|
||||||
|
|
||||||
res = user_permission_list()['permissions']
|
res = user_permission_list(full=True)['permissions']
|
||||||
assert [] == res['blog']['main']['allowed_users']
|
assert res['wiki.main']['allowed'] == ["alice"]
|
||||||
assert [] == res['blog']['main']['allowed_groups']
|
assert res['wiki.main']['corresponding_users'] == ["alice"]
|
||||||
|
|
||||||
def test_allow_group_1():
|
def test_permission_add_group_already_allowed():
|
||||||
# Allow a user when he is already allowed
|
user_permission_update("blog.main", add="alice")
|
||||||
user_permission_add(["blog"], "main", group="alice")
|
|
||||||
|
|
||||||
res = user_permission_list()['permissions']
|
res = user_permission_list(full=True)['permissions']
|
||||||
assert ["alice"] == res['blog']['main']['allowed_users']
|
assert res['blog.main']['allowed'] == ["alice"]
|
||||||
assert ["alice"] == res['blog']['main']['allowed_groups']
|
assert res['blog.main']['corresponding_users'] == ["alice"]
|
||||||
|
|
||||||
def test_disallow_group_1():
|
def test_permission_remove_group_already_not_allowed():
|
||||||
# Disallow a user when he is already disallowed
|
user_permission_update("blog.main", remove="bob")
|
||||||
user_permission_remove(["blog"], "main", group="bob")
|
|
||||||
|
|
||||||
res = user_permission_list()['permissions']
|
res = user_permission_list(full=True)['permissions']
|
||||||
assert ["alice"] == res['blog']['main']['allowed_users']
|
assert res['blog.main']['allowed'] == ["alice"]
|
||||||
assert ["alice"] == res['blog']['main']['allowed_groups']
|
assert res['blog.main']['corresponding_users'] == ["alice"]
|
||||||
|
|
||||||
def test_reset_permission():
|
def test_permission_reset():
|
||||||
# Reset permission
|
# Reset permission
|
||||||
user_permission_clear(["blog"], "main")
|
user_permission_reset("blog.main")
|
||||||
|
|
||||||
res = user_permission_list()['permissions']
|
res = user_permission_list(full=True)['permissions']
|
||||||
assert set(["alice", "bob"]) == set(res['blog']['main']['allowed_users'])
|
assert res['blog.main']['allowed'] == ["all_users"]
|
||||||
assert ["all_users"] == res['blog']['main']['allowed_groups']
|
assert set(res['blog.main']['corresponding_users']) == set(["alice", "bob"])
|
||||||
|
|
||||||
# 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
|
# Error on update function
|
||||||
#
|
#
|
||||||
|
|
||||||
def test_disallow_bad_group_1():
|
def test_permission_add_group_that_doesnt_exist():
|
||||||
# Disallow a group when the group all_users is allowed
|
|
||||||
with pytest.raises(YunohostError):
|
with pytest.raises(YunohostError):
|
||||||
user_permission_remove("wiki", "main", group="alice")
|
user_permission_update("blog.main", add="doesnt_exist")
|
||||||
|
|
||||||
res = user_permission_list()['permissions']
|
res = user_permission_list(full=True)['permissions']
|
||||||
assert ["all_users"] == res['wiki']['main']['allowed_groups']
|
assert res['blog.main']['allowed'] == ["alice"]
|
||||||
assert set(["alice", "bob"]) == set(res['wiki']['main']['allowed_users'])
|
assert res['blog.main']['corresponding_users'] == ["alice"]
|
||||||
|
|
||||||
def test_allow_bad_user():
|
def test_permission_update_permission_that_doesnt_exist():
|
||||||
# Allow a non existant group
|
|
||||||
with pytest.raises(YunohostError):
|
with pytest.raises(YunohostError):
|
||||||
user_permission_add(["blog"], "main", group="not_exist")
|
user_permission_update("doesnt.exist", add="alice")
|
||||||
|
|
||||||
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():
|
# Permission url management
|
||||||
# Disallow a non existant group
|
|
||||||
with pytest.raises(YunohostError):
|
|
||||||
user_permission_remove(["blog"], "main", group="not_exist")
|
|
||||||
|
|
||||||
res = user_permission_list()['permissions']
|
def test_permission_add_url():
|
||||||
assert ["alice"] == res['blog']['main']['allowed_groups']
|
permission_urls("blog.main", add=[maindomain + "/testA"])
|
||||||
assert ["alice"] == res['blog']['main']['allowed_users']
|
|
||||||
|
|
||||||
def test_allow_bad_permission_1():
|
res = user_permission_list(full=True)['permissions']
|
||||||
# Allow a user to a non existant permission
|
assert res["blog.main"]["urls"] == [maindomain + "/testA"]
|
||||||
with pytest.raises(YunohostError):
|
|
||||||
user_permission_add(["wiki"], "not_exit", group="alice")
|
|
||||||
|
|
||||||
def test_allow_bad_permission_2():
|
def test_permission_add_second_url():
|
||||||
# Allow a user to a non existant permission
|
permission_urls("wiki.main", add=[maindomain + "/testA"])
|
||||||
with pytest.raises(YunohostError):
|
|
||||||
user_permission_add(["not_exit"], "main", group="alice")
|
res = user_permission_list(full=True)['permissions']
|
||||||
|
assert set(res["wiki.main"]["urls"]) == set([maindomain + "/testA", maindomain + "/wiki"])
|
||||||
|
|
||||||
|
def test_permission_remove_url():
|
||||||
|
permission_urls("wiki.main", remove=[maindomain + "/wiki"])
|
||||||
|
|
||||||
|
res = user_permission_list(full=True)['permissions']
|
||||||
|
assert res["wiki.main"]["urls"] == []
|
||||||
|
|
||||||
|
def test_permission_add_url_already_added():
|
||||||
|
res = user_permission_list(full=True)['permissions']
|
||||||
|
assert res["wiki.main"]["urls"] == [maindomain + "/wiki"]
|
||||||
|
|
||||||
|
permission_urls("wiki.main", add=[maindomain + "/wiki"])
|
||||||
|
|
||||||
|
res = user_permission_list(full=True)['permissions']
|
||||||
|
assert res["wiki.main"]["urls"] == [maindomain + "/wiki"]
|
||||||
|
|
||||||
|
def test_permission_remove_url_not_added():
|
||||||
|
permission_urls("wiki.main", remove=[maindomain + "/doesnt_exist"])
|
||||||
|
|
||||||
|
res = user_permission_list(full=True)['permissions']
|
||||||
|
assert res['wiki.main']['urls'] == [maindomain + "/wiki"]
|
||||||
|
|
||||||
#
|
#
|
||||||
# Application interaction
|
# Application interaction
|
||||||
#
|
#
|
||||||
|
|
||||||
def test_install_app():
|
def test_permission_app_install():
|
||||||
app_install("./tests/apps/permissions_app_ynh",
|
app_install("./tests/apps/permissions_app_ynh",
|
||||||
args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True)
|
args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True)
|
||||||
|
|
||||||
res = user_permission_list()['permissions']
|
res = user_permission_list(full=True)['permissions']
|
||||||
assert "permissions_app" in res
|
assert "permissions_app.main" in res
|
||||||
assert "main" in res['permissions_app']
|
assert "permissions_app.admin" in res
|
||||||
assert [maindomain + "/urlpermissionapp"] == res['permissions_app']['main']['URL']
|
assert "permissions_app.dev" in res
|
||||||
assert [maindomain + "/urlpermissionapp/admin"] == res['permissions_app']['admin']['URL']
|
assert res['permissions_app.main']['urls'] == [maindomain + "/urlpermissionapp"]
|
||||||
assert [maindomain + "/urlpermissionapp/dev"] == res['permissions_app']['dev']['URL']
|
assert res['permissions_app.admin']['urls'] == [maindomain + "/urlpermissionapp/admin"]
|
||||||
|
assert res['permissions_app.dev']['urls'] == [maindomain + "/urlpermissionapp/dev"]
|
||||||
|
|
||||||
assert ["all_users"] == res['permissions_app']['main']['allowed_groups']
|
assert res['permissions_app.main']['allowed'] == ["all_users"]
|
||||||
assert set(["alice", "bob"]) == set(res['permissions_app']['main']['allowed_users'])
|
assert set(res['permissions_app.main']['corresponding_users']) == set(["alice", "bob"])
|
||||||
|
|
||||||
assert ["alice"] == res['permissions_app']['admin']['allowed_groups']
|
assert res['permissions_app.admin']['allowed'] == ["alice"]
|
||||||
assert ["alice"] == res['permissions_app']['admin']['allowed_users']
|
assert res['permissions_app.admin']['corresponding_users'] == ["alice"]
|
||||||
|
|
||||||
assert ["all_users"] == res['permissions_app']['dev']['allowed_groups']
|
assert res['permissions_app.dev']['allowed'] == []
|
||||||
assert set(["alice", "bob"]) == set(res['permissions_app']['dev']['allowed_users'])
|
assert set(res['permissions_app.dev']['corresponding_users']) == set()
|
||||||
|
|
||||||
def test_remove_app():
|
# Check that we get the right stuff in app_map, which is used to generate the ssowatconf
|
||||||
|
assert maindomain + "/urlpermissionapp" in app_map(user="alice").keys()
|
||||||
|
user_permission_update("permissions_app.main", remove="all_users", add="bob")
|
||||||
|
assert maindomain + "/urlpermissionapp" not in app_map(user="alice").keys()
|
||||||
|
assert maindomain + "/urlpermissionapp" in app_map(user="bob").keys()
|
||||||
|
|
||||||
|
|
||||||
|
def test_permission_app_remove():
|
||||||
app_install("./tests/apps/permissions_app_ynh",
|
app_install("./tests/apps/permissions_app_ynh",
|
||||||
args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True)
|
args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True)
|
||||||
app_remove("permissions_app")
|
app_remove("permissions_app")
|
||||||
|
|
||||||
res = user_permission_list()['permissions']
|
# Check all permissions for this app got deleted
|
||||||
assert "permissions_app" not in res
|
res = user_permission_list(full=True)['permissions']
|
||||||
|
assert not any(p.startswith("permissions_app.") for p in res.keys())
|
||||||
|
|
||||||
def test_change_url():
|
def test_permission_app_change_url():
|
||||||
app_install("./tests/apps/permissions_app_ynh",
|
app_install("./tests/apps/permissions_app_ynh",
|
||||||
args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True)
|
args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True)
|
||||||
|
|
||||||
res = user_permission_list()['permissions']
|
res = user_permission_list(full=True)['permissions']
|
||||||
assert [maindomain + "/urlpermissionapp"] == res['permissions_app']['main']['URL']
|
assert res['permissions_app.main']['urls'] == [maindomain + "/urlpermissionapp"]
|
||||||
assert [maindomain + "/urlpermissionapp/admin"] == res['permissions_app']['admin']['URL']
|
assert res['permissions_app.admin']['urls'] == [maindomain + "/urlpermissionapp/admin"]
|
||||||
assert [maindomain + "/urlpermissionapp/dev"] == res['permissions_app']['dev']['URL']
|
assert res['permissions_app.dev']['urls'] == [maindomain + "/urlpermissionapp/dev"]
|
||||||
|
|
||||||
app_change_url("permissions_app", maindomain, "/newchangeurl")
|
app_change_url("permissions_app", maindomain, "/newchangeurl")
|
||||||
|
|
||||||
res = user_permission_list()['permissions']
|
res = user_permission_list(full=True)['permissions']
|
||||||
assert [maindomain + "/newchangeurl"] == res['permissions_app']['main']['URL']
|
assert res['permissions_app.main']['urls'] == [maindomain + "/newchangeurl"]
|
||||||
assert [maindomain + "/newchangeurl/admin"] == res['permissions_app']['admin']['URL']
|
assert res['permissions_app.admin']['urls'] == [maindomain + "/newchangeurl/admin"]
|
||||||
assert [maindomain + "/newchangeurl/dev"] == res['permissions_app']['dev']['URL']
|
assert res['permissions_app.dev']['urls'] == [maindomain + "/newchangeurl/dev"]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from moulinette.core import MoulinetteError
|
from yunohost.user import user_list, user_info, user_create, user_delete, user_update, \
|
||||||
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
|
user_group_list, user_group_create, user_group_delete, user_group_update, user_group_info
|
||||||
from yunohost.domain import _get_maindomain
|
from yunohost.domain import _get_maindomain
|
||||||
from yunohost.utils.error import YunohostError
|
from yunohost.utils.error import YunohostError
|
||||||
from yunohost.tests.test_permission import check_LDAP_db_integrity
|
from yunohost.tests.test_permission import check_LDAP_db_integrity
|
||||||
|
@ -24,10 +24,10 @@ def setup_function(function):
|
||||||
user_create("bob", "Bob", "Snow", "bob@" + maindomain, "test123Ynh")
|
user_create("bob", "Bob", "Snow", "bob@" + maindomain, "test123Ynh")
|
||||||
user_create("jack", "Jack", "Black", "jack@" + maindomain, "test123Ynh")
|
user_create("jack", "Jack", "Black", "jack@" + maindomain, "test123Ynh")
|
||||||
|
|
||||||
user_group_add("dev")
|
user_group_create("dev")
|
||||||
user_group_add("apps")
|
user_group_create("apps")
|
||||||
user_group_update("dev", add_user=["alice"])
|
user_group_update("dev", add=["alice"])
|
||||||
user_group_update("apps", add_user=["bob"])
|
user_group_update("apps", add=["bob"])
|
||||||
|
|
||||||
def teardown_function(function):
|
def teardown_function(function):
|
||||||
clean_user_groups()
|
clean_user_groups()
|
||||||
|
@ -82,12 +82,13 @@ def test_del_user():
|
||||||
assert "alice" not in group_res
|
assert "alice" not in group_res
|
||||||
assert "alice" not in group_res['all_users']['members']
|
assert "alice" not in group_res['all_users']['members']
|
||||||
|
|
||||||
def test_add_group():
|
def test_create_group():
|
||||||
user_group_add("adminsys")
|
user_group_create("adminsys")
|
||||||
|
|
||||||
group_res = user_group_list()['groups']
|
group_res = user_group_list()['groups']
|
||||||
assert "adminsys" in group_res
|
assert "adminsys" in group_res
|
||||||
assert "members" not in group_res['adminsys']
|
assert "members" in group_res['adminsys'].keys()
|
||||||
|
assert group_res["adminsys"]["members"] == []
|
||||||
|
|
||||||
def test_del_group():
|
def test_del_group():
|
||||||
user_group_delete("dev")
|
user_group_delete("dev")
|
||||||
|
@ -99,112 +100,106 @@ def test_del_group():
|
||||||
# Error on create / remove function
|
# Error on create / remove function
|
||||||
#
|
#
|
||||||
|
|
||||||
def test_add_bad_user_1():
|
def test_create_user_with_mail_address_already_taken():
|
||||||
# Check email already exist
|
with pytest.raises(YunohostError):
|
||||||
with pytest.raises(MoulinetteError):
|
|
||||||
user_create("alice2", "Alice", "White", "alice@" + maindomain, "test123Ynh")
|
user_create("alice2", "Alice", "White", "alice@" + maindomain, "test123Ynh")
|
||||||
|
|
||||||
def test_add_bad_user_2():
|
def test_create_user_with_password_too_simple():
|
||||||
# Check to short password
|
with pytest.raises(YunohostError):
|
||||||
with pytest.raises(MoulinetteError):
|
|
||||||
user_create("other", "Alice", "White", "other@" + maindomain, "12")
|
user_create("other", "Alice", "White", "other@" + maindomain, "12")
|
||||||
|
|
||||||
def test_add_bad_user_3():
|
def test_create_user_already_exists():
|
||||||
# Check user already exist
|
with pytest.raises(YunohostError):
|
||||||
with pytest.raises(MoulinetteError):
|
|
||||||
user_create("alice", "Alice", "White", "other@" + maindomain, "test123Ynh")
|
user_create("alice", "Alice", "White", "other@" + maindomain, "test123Ynh")
|
||||||
|
|
||||||
def test_del_bad_user_1():
|
def test_update_user_with_mail_address_already_taken():
|
||||||
# Check user not found
|
with pytest.raises(YunohostError):
|
||||||
with pytest.raises(MoulinetteError):
|
user_update("bob", add_mailalias="alice@" + maindomain)
|
||||||
user_delete("not_exit")
|
|
||||||
|
|
||||||
def test_add_bad_group_1():
|
def test_del_user_that_does_not_exist():
|
||||||
|
with pytest.raises(YunohostError):
|
||||||
|
user_delete("doesnt_exist")
|
||||||
|
|
||||||
|
def test_create_group_all_users():
|
||||||
# Check groups already exist with special group "all_users"
|
# Check groups already exist with special group "all_users"
|
||||||
with pytest.raises(YunohostError):
|
with pytest.raises(YunohostError):
|
||||||
user_group_add("all_users")
|
user_group_create("all_users")
|
||||||
|
|
||||||
def test_add_bad_group_2():
|
def test_create_group_already_exists():
|
||||||
# Check groups already exist (for standard groups)
|
# Check groups already exist (regular groups)
|
||||||
with pytest.raises(MoulinetteError):
|
with pytest.raises(YunohostError):
|
||||||
user_group_add("dev")
|
user_group_create("dev")
|
||||||
|
|
||||||
def test_del_bad_group_1():
|
def test_del_group_all_users():
|
||||||
# Check not allowed to remove this groups
|
|
||||||
with pytest.raises(YunohostError):
|
with pytest.raises(YunohostError):
|
||||||
user_group_delete("all_users")
|
user_group_delete("all_users")
|
||||||
|
|
||||||
def test_del_bad_group_2():
|
def test_del_group_that_does_not_exist():
|
||||||
# Check groups not found
|
with pytest.raises(YunohostError):
|
||||||
with pytest.raises(MoulinetteError):
|
user_group_delete("doesnt_exist")
|
||||||
user_group_delete("not_exit")
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Update function
|
# Update function
|
||||||
#
|
#
|
||||||
|
|
||||||
def test_update_user_1():
|
def test_update_user():
|
||||||
user_update("alice", firstname="NewName", lastname="NewLast")
|
user_update("alice", firstname="NewName", lastname="NewLast")
|
||||||
|
|
||||||
info = user_info("alice")
|
info = user_info("alice")
|
||||||
assert "NewName" == info['firstname']
|
assert info['firstname'] == "NewName"
|
||||||
assert "NewLast" == info['lastname']
|
assert info['lastname'] == "NewLast"
|
||||||
|
|
||||||
def test_update_group_1():
|
def test_update_group_add_user():
|
||||||
user_group_update("dev", add_user=["bob"])
|
user_group_update("dev", add=["bob"])
|
||||||
|
|
||||||
group_res = user_group_list()['groups']
|
group_res = user_group_list()['groups']
|
||||||
assert set(["alice", "bob"]) == set(group_res['dev']['members'])
|
assert set(group_res['dev']['members']) == set(["alice", "bob"])
|
||||||
|
|
||||||
def test_update_group_2():
|
def test_update_group_add_user_already_in():
|
||||||
# Try to add a user in a group when the user is already in
|
user_group_update("apps", add=["bob"])
|
||||||
user_group_update("apps", add_user=["bob"])
|
|
||||||
|
|
||||||
group_res = user_group_list()['groups']
|
group_res = user_group_list()['groups']
|
||||||
assert ["bob"] == group_res['apps']['members']
|
assert group_res['apps']['members'] == ["bob"]
|
||||||
|
|
||||||
def test_update_group_3():
|
def test_update_group_remove_user():
|
||||||
# Try to remove a user in a group
|
user_group_update("apps", remove=["bob"])
|
||||||
user_group_update("apps", remove_user=["bob"])
|
|
||||||
|
|
||||||
group_res = user_group_list()['groups']
|
group_res = user_group_list()['groups']
|
||||||
assert "members" not in group_res['apps']
|
assert group_res['apps']['members'] == []
|
||||||
|
|
||||||
def test_update_group_4():
|
def test_update_group_remove_user_not_already_in():
|
||||||
# Try to remove a user in a group when it is not already in
|
user_group_update("apps", remove=["jack"])
|
||||||
user_group_update("apps", remove_user=["jack"])
|
|
||||||
|
|
||||||
group_res = user_group_list()['groups']
|
group_res = user_group_list()['groups']
|
||||||
assert ["bob"] == group_res['apps']['members']
|
assert group_res['apps']['members'] == ["bob"]
|
||||||
|
|
||||||
#
|
#
|
||||||
# Error on update functions
|
# Error on update functions
|
||||||
#
|
#
|
||||||
|
|
||||||
def test_bad_update_user_1():
|
def test_update_user_that_doesnt_exist():
|
||||||
# Check user not found
|
|
||||||
with pytest.raises(YunohostError):
|
with pytest.raises(YunohostError):
|
||||||
user_update("not_exit", firstname="NewName", lastname="NewLast")
|
user_update("doesnt_exist", firstname="NewName", lastname="NewLast")
|
||||||
|
|
||||||
|
def test_update_group_that_doesnt_exist():
|
||||||
def bad_update_group_1():
|
|
||||||
# Check groups not found
|
# Check groups not found
|
||||||
with pytest.raises(YunohostError):
|
with pytest.raises(YunohostError):
|
||||||
user_group_update("not_exit", add_user=["alice"])
|
user_group_update("doesnt_exist", add=["alice"])
|
||||||
|
|
||||||
def test_bad_update_group_2():
|
def test_update_group_all_users_manually():
|
||||||
# Check remove user in groups "all_users" not allowed
|
|
||||||
with pytest.raises(YunohostError):
|
with pytest.raises(YunohostError):
|
||||||
user_group_update("all_users", remove_user=["alice"])
|
user_group_update("all_users", remove=["alice"])
|
||||||
|
|
||||||
def test_bad_update_group_3():
|
assert "alice" in user_group_list()["groups"]["all_users"]["members"]
|
||||||
# Check remove user in it own group not allowed
|
|
||||||
|
def test_update_group_primary_manually():
|
||||||
with pytest.raises(YunohostError):
|
with pytest.raises(YunohostError):
|
||||||
user_group_update("alice", remove_user=["alice"])
|
user_group_update("alice", remove=["alice"])
|
||||||
|
assert "alice" in user_group_list()["groups"]["alice"]["members"]
|
||||||
|
|
||||||
def test_bad_update_group_1():
|
def test_update_group_add_user_that_doesnt_exist():
|
||||||
# Check add bad user in group
|
# Check add bad user in group
|
||||||
with pytest.raises(YunohostError):
|
with pytest.raises(YunohostError):
|
||||||
user_group_update("dev", add_user=["not_exist"])
|
user_group_update("dev", add=["doesnt_exist"])
|
||||||
|
|
||||||
assert "not_exist" not in user_group_list()["groups"]["dev"]
|
assert "doesnt_exist" not in user_group_list()["groups"]["dev"]["members"]
|
||||||
|
|
|
@ -32,6 +32,7 @@ import crypt
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import copy
|
||||||
|
|
||||||
from moulinette import m18n
|
from moulinette import m18n
|
||||||
from yunohost.utils.error import YunohostError
|
from yunohost.utils.error import YunohostError
|
||||||
|
@ -126,12 +127,18 @@ def user_create(operation_logger, username, firstname, lastname, mail, password,
|
||||||
|
|
||||||
ldap = _get_ldap_interface()
|
ldap = _get_ldap_interface()
|
||||||
|
|
||||||
|
if username in user_list()["users"]:
|
||||||
|
raise YunohostError("user_already_exists", user=username)
|
||||||
|
|
||||||
# Validate uniqueness of username and mail in LDAP
|
# Validate uniqueness of username and mail in LDAP
|
||||||
ldap.validate_uniqueness({
|
try:
|
||||||
'uid': username,
|
ldap.validate_uniqueness({
|
||||||
'mail': mail,
|
'uid': username,
|
||||||
'cn': username
|
'mail': mail,
|
||||||
})
|
'cn': username
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
raise YunohostError('user_creation_failed', user=username, error=e)
|
||||||
|
|
||||||
# Validate uniqueness of username in system users
|
# Validate uniqueness of username in system users
|
||||||
all_existing_usernames = {x.pw_name for x in pwd.getpwall()}
|
all_existing_usernames = {x.pw_name for x in pwd.getpwall()}
|
||||||
|
@ -204,33 +211,34 @@ def user_create(operation_logger, username, firstname, lastname, mail, password,
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
raise YunohostError('ssowat_persistent_conf_write_error', error=e.strerror)
|
raise YunohostError('ssowat_persistent_conf_write_error', error=e.strerror)
|
||||||
|
|
||||||
if ldap.add('uid=%s,ou=users' % username, attr_dict):
|
try:
|
||||||
# Invalidate passwd to take user creation into account
|
ldap.add('uid=%s,ou=users' % username, attr_dict)
|
||||||
subprocess.call(['nscd', '-i', 'passwd'])
|
except Exception as e:
|
||||||
|
raise YunohostError('user_creation_failed', user=username, error=e)
|
||||||
|
|
||||||
try:
|
# Invalidate passwd to take user creation into account
|
||||||
# Attempt to create user home folder
|
subprocess.call(['nscd', '-i', 'passwd'])
|
||||||
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)
|
|
||||||
|
|
||||||
# Create group for user and add to group 'all_users'
|
try:
|
||||||
user_group_add(groupname=username, gid=uid, sync_perm=False)
|
# Attempt to create user home folder
|
||||||
user_group_update(groupname=username, add_user=username, force=True, sync_perm=False)
|
subprocess.check_call(
|
||||||
user_group_update(groupname='all_users', add_user=username, force=True, sync_perm=True)
|
['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)
|
||||||
|
|
||||||
# TODO: Send a welcome mail to user
|
# Create group for user and add to group 'all_users'
|
||||||
logger.success(m18n.n('user_created'))
|
user_group_create(groupname=username, gid=uid, primary_group=True, sync_perm=False)
|
||||||
|
user_group_update(groupname='all_users', add=username, force=True, sync_perm=True)
|
||||||
|
|
||||||
hook_callback('post_user_create',
|
# TODO: Send a welcome mail to user
|
||||||
args=[username, mail, password, firstname, lastname])
|
logger.success(m18n.n('user_created'))
|
||||||
|
|
||||||
return {'fullname': fullname, 'username': username, 'mail': mail}
|
hook_callback('post_user_create',
|
||||||
|
args=[username, mail, password, firstname, lastname])
|
||||||
|
|
||||||
raise YunohostError('user_creation_failed')
|
return {'fullname': fullname, 'username': username, 'mail': mail}
|
||||||
|
|
||||||
|
|
||||||
@is_unit_operation([('username', 'user')])
|
@is_unit_operation([('username', 'user')])
|
||||||
|
@ -245,32 +253,35 @@ def user_delete(operation_logger, username, purge=False):
|
||||||
"""
|
"""
|
||||||
from yunohost.hook import hook_callback
|
from yunohost.hook import hook_callback
|
||||||
from yunohost.utils.ldap import _get_ldap_interface
|
from yunohost.utils.ldap import _get_ldap_interface
|
||||||
|
from yunohost.permission import permission_sync_to_user
|
||||||
|
|
||||||
|
if username not in user_list()["users"]:
|
||||||
|
raise YunohostError('user_unknown', user=username)
|
||||||
|
|
||||||
operation_logger.start()
|
operation_logger.start()
|
||||||
|
|
||||||
ldap = _get_ldap_interface()
|
user_group_update("all_users", remove=username, force=True, sync_perm=False)
|
||||||
if ldap.remove('uid=%s,ou=users' % username):
|
for group, infos in user_group_list()["groups"].items():
|
||||||
# Invalidate passwd to take user deletion into account
|
if group == "all_users":
|
||||||
subprocess.call(['nscd', '-i', 'passwd'])
|
continue
|
||||||
|
# If the user is in this group (and it's not the primary group),
|
||||||
if purge:
|
# remove the member from the group
|
||||||
subprocess.call(['rm', '-rf', '/home/{0}'.format(username)])
|
if username != group and username in infos["members"]:
|
||||||
subprocess.call(['rm', '-rf', '/var/mail/{0}'.format(username)])
|
user_group_update(group, remove=username, sync_perm=False)
|
||||||
else:
|
|
||||||
raise YunohostError('user_deletion_failed')
|
|
||||||
|
|
||||||
user_group_delete(username, force=True, sync_perm=True)
|
user_group_delete(username, force=True, sync_perm=True)
|
||||||
|
|
||||||
group_list = ldap.search('ou=groups,dc=yunohost,dc=org',
|
ldap = _get_ldap_interface()
|
||||||
'(&(objectclass=groupOfNamesYnh)(memberUid=%s))'
|
try:
|
||||||
% username, ['cn'])
|
ldap.remove('uid=%s,ou=users' % username)
|
||||||
for group in group_list:
|
except Exception as e:
|
||||||
user_list = ldap.search('ou=groups,dc=yunohost,dc=org',
|
raise YunohostError('user_deletion_failed', user=username, error=e)
|
||||||
'cn=' + group['cn'][0],
|
|
||||||
['memberUid'])[0]
|
# Invalidate passwd to take user deletion into account
|
||||||
user_list['memberUid'].remove(username)
|
subprocess.call(['nscd', '-i', 'passwd'])
|
||||||
if not ldap.update('cn=%s,ou=groups' % group['cn'][0], user_list):
|
|
||||||
raise YunohostError('group_update_failed')
|
if purge:
|
||||||
|
subprocess.call(['rm', '-rf', '/home/{0}'.format(username)])
|
||||||
|
subprocess.call(['rm', '-rf', '/var/mail/{0}'.format(username)])
|
||||||
|
|
||||||
hook_callback('post_user_delete', args=[username, purge])
|
hook_callback('post_user_delete', args=[username, purge])
|
||||||
|
|
||||||
|
@ -338,7 +349,10 @@ def user_update(operation_logger, username, firstname=None, lastname=None, mail=
|
||||||
'webmaster@' + main_domain,
|
'webmaster@' + main_domain,
|
||||||
'postmaster@' + main_domain,
|
'postmaster@' + main_domain,
|
||||||
]
|
]
|
||||||
ldap.validate_uniqueness({'mail': mail})
|
try:
|
||||||
|
ldap.validate_uniqueness({'mail': mail})
|
||||||
|
except Exception as e:
|
||||||
|
raise YunohostError('user_update_failed', user=username, error=e)
|
||||||
if mail[mail.find('@') + 1:] not in domains:
|
if mail[mail.find('@') + 1:] not in domains:
|
||||||
raise YunohostError('mail_domain_unknown', domain=mail[mail.find('@') + 1:])
|
raise YunohostError('mail_domain_unknown', domain=mail[mail.find('@') + 1:])
|
||||||
if mail in aliases:
|
if mail in aliases:
|
||||||
|
@ -351,7 +365,10 @@ def user_update(operation_logger, username, firstname=None, lastname=None, mail=
|
||||||
if not isinstance(add_mailalias, list):
|
if not isinstance(add_mailalias, list):
|
||||||
add_mailalias = [add_mailalias]
|
add_mailalias = [add_mailalias]
|
||||||
for mail in add_mailalias:
|
for mail in add_mailalias:
|
||||||
ldap.validate_uniqueness({'mail': mail})
|
try:
|
||||||
|
ldap.validate_uniqueness({'mail': mail})
|
||||||
|
except Exception as e:
|
||||||
|
raise YunohostError('user_update_failed', user=username, error=e)
|
||||||
if mail[mail.find('@') + 1:] not in domains:
|
if mail[mail.find('@') + 1:] not in domains:
|
||||||
raise YunohostError('mail_domain_unknown', domain=mail[mail.find('@') + 1:])
|
raise YunohostError('mail_domain_unknown', domain=mail[mail.find('@') + 1:])
|
||||||
user['mail'].append(mail)
|
user['mail'].append(mail)
|
||||||
|
@ -391,12 +408,14 @@ def user_update(operation_logger, username, firstname=None, lastname=None, mail=
|
||||||
|
|
||||||
operation_logger.start()
|
operation_logger.start()
|
||||||
|
|
||||||
if ldap.update('uid=%s,ou=users' % username, new_attr_dict):
|
try:
|
||||||
logger.success(m18n.n('user_updated'))
|
ldap.update('uid=%s,ou=users' % username, new_attr_dict)
|
||||||
app_ssowatconf()
|
except Exception as e:
|
||||||
return user_info(username)
|
raise YunohostError('user_update_failed', user=username, error=e)
|
||||||
else:
|
|
||||||
raise YunohostError('user_update_failed')
|
logger.success(m18n.n('user_updated'))
|
||||||
|
app_ssowatconf()
|
||||||
|
return user_info(username)
|
||||||
|
|
||||||
|
|
||||||
def user_info(username):
|
def user_info(username):
|
||||||
|
@ -453,7 +472,7 @@ def user_info(username):
|
||||||
|
|
||||||
if service_status("dovecot")["status"] != "running":
|
if service_status("dovecot")["status"] != "running":
|
||||||
logger.warning(m18n.n('mailbox_used_space_dovecot_down'))
|
logger.warning(m18n.n('mailbox_used_space_dovecot_down'))
|
||||||
elif not user_permission_list(app="mail", permission="main", username=username)['permissions']:
|
elif username not in user_permission_list(full=True)["permissions"]["mail.main"]["corresponding_users"]:
|
||||||
logger.warning(m18n.n('mailbox_disabled', user=username))
|
logger.warning(m18n.n('mailbox_disabled', user=username))
|
||||||
else:
|
else:
|
||||||
cmd = 'doveadm -f flow quota get -u %s' % user['uid'][0]
|
cmd = 'doveadm -f flow quota get -u %s' % user['uid'][0]
|
||||||
|
@ -480,81 +499,59 @@ def user_info(username):
|
||||||
'use': storage_use
|
'use': storage_use
|
||||||
}
|
}
|
||||||
|
|
||||||
if result:
|
return result_dict
|
||||||
return result_dict
|
|
||||||
else:
|
|
||||||
raise YunohostError('user_info_failed')
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Group subcategory
|
# Group subcategory
|
||||||
#
|
#
|
||||||
def user_group_list(fields=None):
|
def user_group_list(short=False, full=False, include_primary_groups=True):
|
||||||
"""
|
"""
|
||||||
List users
|
List users
|
||||||
|
|
||||||
Keyword argument:
|
Keyword argument:
|
||||||
filter -- LDAP filter used to search
|
short -- Only list the name of the groups without any additional info
|
||||||
offset -- Starting number for user fetching
|
full -- List all the info available for each groups
|
||||||
limit -- Maximum number of user fetched
|
include_primary_groups -- Include groups corresponding to users (which should always only contains this user)
|
||||||
fields -- fields to fetch
|
This option is set to false by default in the action map because we don't want to have
|
||||||
|
these displayed when the user runs `yunohost user group list`, but internally we do want
|
||||||
|
to list them when called from other functions
|
||||||
"""
|
"""
|
||||||
from yunohost.utils.ldap import _get_ldap_interface
|
|
||||||
|
# Fetch relevant informations
|
||||||
|
|
||||||
|
from yunohost.utils.ldap import _get_ldap_interface, _ldap_path_extract
|
||||||
ldap = _get_ldap_interface()
|
ldap = _get_ldap_interface()
|
||||||
group_attr = {
|
groups_infos = ldap.search('ou=groups,dc=yunohost,dc=org',
|
||||||
'cn': 'groupname',
|
'(objectclass=groupOfNamesYnh)',
|
||||||
'member': 'members',
|
["cn", "member", "permission"])
|
||||||
'permission': 'permission'
|
|
||||||
}
|
# Parse / organize information to be outputed
|
||||||
attrs = ['cn']
|
|
||||||
|
users = user_list()["users"]
|
||||||
groups = {}
|
groups = {}
|
||||||
|
for infos in groups_infos:
|
||||||
|
|
||||||
if fields:
|
name = infos["cn"][0]
|
||||||
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',
|
if not include_primary_groups and name in users:
|
||||||
'(objectclass=groupOfNamesYnh)',
|
|
||||||
attrs)
|
|
||||||
|
|
||||||
for group in result:
|
|
||||||
# The group "admins" should be hidden for the user
|
|
||||||
if group_attr['cn'] == "admins":
|
|
||||||
continue
|
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[name] = {}
|
||||||
groups[groupname] = entry
|
|
||||||
|
groups[name]["members"] = [_ldap_path_extract(p, "uid") for p in infos.get("member", [])]
|
||||||
|
|
||||||
|
if full:
|
||||||
|
groups[name]["permissions"] = [_ldap_path_extract(p, "cn") for p in infos.get("permission", [])]
|
||||||
|
|
||||||
|
if short:
|
||||||
|
groups = groups.keys()
|
||||||
|
|
||||||
return {'groups': groups}
|
return {'groups': groups}
|
||||||
|
|
||||||
|
|
||||||
@is_unit_operation([('groupname', 'user')])
|
@is_unit_operation([('groupname', 'group')])
|
||||||
def user_group_add(operation_logger, groupname, gid=None, sync_perm=True):
|
def user_group_create(operation_logger, groupname, gid=None, primary_group=False, sync_perm=True):
|
||||||
"""
|
"""
|
||||||
Create group
|
Create group
|
||||||
|
|
||||||
|
@ -565,8 +562,6 @@ def user_group_add(operation_logger, groupname, gid=None, sync_perm=True):
|
||||||
from yunohost.permission import permission_sync_to_user
|
from yunohost.permission import permission_sync_to_user
|
||||||
from yunohost.utils.ldap import _get_ldap_interface
|
from yunohost.utils.ldap import _get_ldap_interface
|
||||||
|
|
||||||
operation_logger.start()
|
|
||||||
|
|
||||||
ldap = _get_ldap_interface()
|
ldap = _get_ldap_interface()
|
||||||
|
|
||||||
# Validate uniqueness of groupname in LDAP
|
# Validate uniqueness of groupname in LDAP
|
||||||
|
@ -574,12 +569,12 @@ def user_group_add(operation_logger, groupname, gid=None, sync_perm=True):
|
||||||
'cn': groupname
|
'cn': groupname
|
||||||
}, base_dn='ou=groups,dc=yunohost,dc=org')
|
}, base_dn='ou=groups,dc=yunohost,dc=org')
|
||||||
if conflict:
|
if conflict:
|
||||||
raise YunohostError('group_name_already_exist', name=groupname)
|
raise YunohostError('group_already_exist', group=groupname)
|
||||||
|
|
||||||
# Validate uniqueness of groupname in system group
|
# Validate uniqueness of groupname in system group
|
||||||
all_existing_groupnames = {x.gr_name for x in grp.getgrall()}
|
all_existing_groupnames = {x.gr_name for x in grp.getgrall()}
|
||||||
if groupname in all_existing_groupnames:
|
if groupname in all_existing_groupnames:
|
||||||
raise YunohostError('system_groupname_exists')
|
raise YunohostError('group_already_exist_on_system', group=groupname)
|
||||||
|
|
||||||
if not gid:
|
if not gid:
|
||||||
# Get random GID
|
# Get random GID
|
||||||
|
@ -596,16 +591,30 @@ def user_group_add(operation_logger, groupname, gid=None, sync_perm=True):
|
||||||
'gidNumber': gid,
|
'gidNumber': gid,
|
||||||
}
|
}
|
||||||
|
|
||||||
if ldap.add('cn=%s,ou=groups' % groupname, attr_dict):
|
# Here we handle the creation of a primary group
|
||||||
|
# We want to initialize this group to contain the corresponding user
|
||||||
|
# (then we won't be able to add/remove any user in this group)
|
||||||
|
if primary_group:
|
||||||
|
attr_dict["member"] = ["uid=" + groupname + ",ou=users,dc=yunohost,dc=org"]
|
||||||
|
|
||||||
|
operation_logger.start()
|
||||||
|
try:
|
||||||
|
ldap.add('cn=%s,ou=groups' % groupname, attr_dict)
|
||||||
|
except Exception as e:
|
||||||
|
raise YunohostError('group_creation_failed', group=groupname, error=e)
|
||||||
|
|
||||||
|
if sync_perm:
|
||||||
|
permission_sync_to_user()
|
||||||
|
|
||||||
|
if not primary_group:
|
||||||
logger.success(m18n.n('group_created', group=groupname))
|
logger.success(m18n.n('group_created', group=groupname))
|
||||||
if sync_perm:
|
else:
|
||||||
permission_sync_to_user()
|
logger.debug(m18n.n('group_created', group=groupname))
|
||||||
return {'name': groupname}
|
|
||||||
|
|
||||||
raise YunohostError('group_creation_failed', group=groupname)
|
return {'name': groupname}
|
||||||
|
|
||||||
|
|
||||||
@is_unit_operation([('groupname', 'user')])
|
@is_unit_operation([('groupname', 'group')])
|
||||||
def user_group_delete(operation_logger, groupname, force=False, sync_perm=True):
|
def user_group_delete(operation_logger, groupname, force=False, sync_perm=True):
|
||||||
"""
|
"""
|
||||||
Delete user
|
Delete user
|
||||||
|
@ -617,102 +626,104 @@ def user_group_delete(operation_logger, groupname, force=False, sync_perm=True):
|
||||||
from yunohost.permission import permission_sync_to_user
|
from yunohost.permission import permission_sync_to_user
|
||||||
from yunohost.utils.ldap import _get_ldap_interface
|
from yunohost.utils.ldap import _get_ldap_interface
|
||||||
|
|
||||||
forbidden_groups = ["all_users", "admins"] + user_list(fields=['uid'])['users'].keys()
|
existing_groups = user_group_list()['groups'].keys()
|
||||||
if not force and groupname in forbidden_groups:
|
if groupname not in existing_groups:
|
||||||
raise YunohostError('group_deletion_not_allowed', group=groupname)
|
raise YunohostError('group_unknown', group=groupname)
|
||||||
|
|
||||||
|
# Refuse to delete primary groups of a user (e.g. group 'sam' related to user 'sam')
|
||||||
|
# without the force option...
|
||||||
|
#
|
||||||
|
# We also can't delete "all_users" because that's a special group...
|
||||||
|
existing_users = user_list()['users'].keys()
|
||||||
|
undeletable_groups = existing_users + ["all_users", "admins"]
|
||||||
|
if groupname in undeletable_groups and not force:
|
||||||
|
raise YunohostError('group_cannot_be_deleted', group=groupname)
|
||||||
|
|
||||||
operation_logger.start()
|
operation_logger.start()
|
||||||
ldap = _get_ldap_interface()
|
ldap = _get_ldap_interface()
|
||||||
if not ldap.remove('cn=%s,ou=groups' % groupname):
|
try:
|
||||||
raise YunohostError('group_deletion_failed', group=groupname)
|
ldap.remove('cn=%s,ou=groups' % groupname)
|
||||||
|
except Exception as e:
|
||||||
|
raise YunohostError('group_deletion_failed', group=groupname, error=e)
|
||||||
|
|
||||||
logger.success(m18n.n('group_deleted', group=groupname))
|
|
||||||
if sync_perm:
|
if sync_perm:
|
||||||
permission_sync_to_user()
|
permission_sync_to_user()
|
||||||
|
|
||||||
|
if groupname not in existing_users:
|
||||||
|
logger.success(m18n.n('group_deleted', group=groupname))
|
||||||
|
else:
|
||||||
|
logger.debug(m18n.n('group_deleted', group=groupname))
|
||||||
|
|
||||||
@is_unit_operation([('groupname', 'user')])
|
|
||||||
def user_group_update(operation_logger, groupname, add_user=None, remove_user=None, force=False, sync_perm=True):
|
@is_unit_operation([('groupname', 'group')])
|
||||||
|
def user_group_update(operation_logger, groupname, add=None, remove=None, force=False, sync_perm=True):
|
||||||
"""
|
"""
|
||||||
Update user informations
|
Update user informations
|
||||||
|
|
||||||
Keyword argument:
|
Keyword argument:
|
||||||
groupname -- Groupname to update
|
groupname -- Groupname to update
|
||||||
add_user -- User to add in group
|
add -- User(s) to add in group
|
||||||
remove_user -- User to remove in group
|
remove -- User(s) to remove in group
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from yunohost.permission import permission_sync_to_user
|
from yunohost.permission import permission_sync_to_user
|
||||||
from yunohost.utils.ldap import _get_ldap_interface
|
from yunohost.utils.ldap import _get_ldap_interface
|
||||||
|
|
||||||
if (groupname == 'all_users' or groupname == 'admins') and not force:
|
# Refuse to edit a primary group of a user (e.g. group 'sam' related to user 'sam')
|
||||||
raise YunohostError('edit_group_not_allowed', group=groupname)
|
# Those kind of group should only ever contain the user (e.g. sam) and only this one.
|
||||||
|
# We also can't edit "all_users" without the force option because that's a special group...
|
||||||
|
existing_users = user_list()['users'].keys()
|
||||||
|
uneditable_groups = existing_users + ["all_users", "admins"]
|
||||||
|
if groupname in uneditable_groups and not force:
|
||||||
|
raise YunohostError('group_cannot_be_edited', group=groupname)
|
||||||
|
|
||||||
ldap = _get_ldap_interface()
|
# We extract the uid for each member of the group to keep a simple flat list of members
|
||||||
|
current_group = user_group_info(groupname)["members"]
|
||||||
|
new_group = copy.copy(current_group)
|
||||||
|
|
||||||
# Populate group informations
|
if add:
|
||||||
attrs_to_fetch = ['member']
|
users_to_add = [add] if not isinstance(add, list) else add
|
||||||
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()}
|
for user in users_to_add:
|
||||||
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:
|
if user not in existing_users:
|
||||||
raise YunohostError('user_unknown', user=user)
|
raise YunohostError('user_unknown', user=user)
|
||||||
|
|
||||||
for user in add_user:
|
if user in current_group:
|
||||||
userDN = "uid=" + user + ",ou=users,dc=yunohost,dc=org"
|
logger.warning(m18n.n('group_user_already_in_group', user=user, group=groupname))
|
||||||
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:
|
else:
|
||||||
logger.warning(m18n.n('user_not_in_group', user=user, group=groupname))
|
operation_logger.related_to.append(('user', user))
|
||||||
|
|
||||||
# Sychronise memberUid with member (to keep the posix group structure)
|
new_group += users_to_add
|
||||||
# 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 remove:
|
||||||
|
users_to_remove = [remove] if not isinstance(remove, list) else remove
|
||||||
|
|
||||||
if new_group_list['member'] != set(group['member']):
|
for user in users_to_remove:
|
||||||
if not ldap.update('cn=%s,ou=groups' % groupname, new_group_list):
|
if user not in current_group:
|
||||||
raise YunohostError('group_update_failed', group=groupname)
|
logger.warning(m18n.n('group_user_not_in_group', user=user, group=groupname))
|
||||||
|
else:
|
||||||
|
operation_logger.related_to.append(('user', user))
|
||||||
|
|
||||||
|
# Remove users_to_remove from new_group
|
||||||
|
# Kinda like a new_group -= users_to_remove
|
||||||
|
new_group = [u for u in new_group if u not in users_to_remove]
|
||||||
|
|
||||||
|
new_group_dns = ["uid=" + user + ",ou=users,dc=yunohost,dc=org" for user in new_group]
|
||||||
|
|
||||||
|
if set(new_group) != set(current_group):
|
||||||
|
operation_logger.start()
|
||||||
|
ldap = _get_ldap_interface()
|
||||||
|
try:
|
||||||
|
ldap.update('cn=%s,ou=groups' % groupname, {"member": set(new_group_dns), "memberUid": set(new_group)})
|
||||||
|
except Exception as e:
|
||||||
|
raise YunohostError('group_update_failed', group=groupname, error=e)
|
||||||
|
|
||||||
|
if groupname != "all_users":
|
||||||
|
logger.success(m18n.n('group_updated', group=groupname))
|
||||||
|
else:
|
||||||
|
logger.debug(m18n.n('group_updated', group=groupname))
|
||||||
|
|
||||||
logger.success(m18n.n('group_updated', group=groupname))
|
|
||||||
if sync_perm:
|
if sync_perm:
|
||||||
permission_sync_to_user()
|
permission_sync_to_user()
|
||||||
return user_group_info(groupname)
|
return user_group_info(groupname)
|
||||||
|
@ -727,59 +738,46 @@ def user_group_info(groupname):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from yunohost.utils.ldap import _get_ldap_interface
|
from yunohost.utils.ldap import _get_ldap_interface, _ldap_path_extract
|
||||||
ldap = _get_ldap_interface()
|
ldap = _get_ldap_interface()
|
||||||
|
|
||||||
group_attrs = [
|
# Fetch info for this group
|
||||||
'cn', 'member', 'permission'
|
result = ldap.search('ou=groups,dc=yunohost,dc=org',
|
||||||
]
|
"cn=" + groupname,
|
||||||
result = ldap.search('ou=groups,dc=yunohost,dc=org', "cn=" + groupname, group_attrs)
|
["cn", "member", "permission"])
|
||||||
|
|
||||||
if not result:
|
if not result:
|
||||||
raise YunohostError('group_unknown', group=groupname)
|
raise YunohostError('group_unknown', group=groupname)
|
||||||
|
|
||||||
group = result[0]
|
infos = result[0]
|
||||||
|
|
||||||
result_dict = {
|
# Format data
|
||||||
'groupname': group['cn'][0],
|
|
||||||
'member': None
|
return {
|
||||||
|
'members': [_ldap_path_extract(p, "uid") for p in infos.get("member", [])],
|
||||||
|
'permissions': [_ldap_path_extract(p, "cn") for p in infos.get("permission", [])]
|
||||||
}
|
}
|
||||||
if 'member' in group:
|
|
||||||
result_dict['member'] = {m.split("=")[1].split(",")[0] for m in group['member']}
|
|
||||||
return result_dict
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Permission subcategory
|
# Permission subcategory
|
||||||
#
|
#
|
||||||
|
|
||||||
def user_permission_list(app=None, permission=None, username=None, group=None, sync_perm=True):
|
def user_permission_list(short=False, full=False):
|
||||||
import yunohost.permission
|
import yunohost.permission
|
||||||
return yunohost.permission.user_permission_list(app, permission, username, group)
|
return yunohost.permission.user_permission_list(short, full)
|
||||||
|
|
||||||
|
|
||||||
@is_unit_operation([('app', 'user')])
|
def user_permission_update(permission, add=None, remove=None, sync_perm=True):
|
||||||
def user_permission_add(operation_logger, app, permission="main", username=None, group=None, sync_perm=True):
|
|
||||||
import yunohost.permission
|
import yunohost.permission
|
||||||
return yunohost.permission.user_permission_update(operation_logger, app, permission=permission,
|
return yunohost.permission.user_permission_update(permission,
|
||||||
add_username=username, add_group=group,
|
add=add, remove=remove,
|
||||||
del_username=None, del_group=None,
|
|
||||||
sync_perm=sync_perm)
|
sync_perm=sync_perm)
|
||||||
|
|
||||||
|
|
||||||
@is_unit_operation([('app', 'user')])
|
def user_permission_reset(permission, sync_perm=True):
|
||||||
def user_permission_remove(operation_logger, app, permission="main", username=None, group=None, sync_perm=True):
|
|
||||||
import yunohost.permission
|
import yunohost.permission
|
||||||
return yunohost.permission.user_permission_update(operation_logger, app, permission=permission,
|
return yunohost.permission.user_permission_reset(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)
|
sync_perm=sync_perm)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,20 @@ def _get_ldap_interface():
|
||||||
|
|
||||||
return _ldap_interface
|
return _ldap_interface
|
||||||
|
|
||||||
|
|
||||||
|
# We regularly want to extract stuff like 'bar' in ldap path like
|
||||||
|
# foo=bar,dn=users.example.org,ou=example.org,dc=org so this small helper allow
|
||||||
|
# to do this without relying of dozens of mysterious string.split()[0]
|
||||||
|
#
|
||||||
|
# e.g. using _ldap_path_extract(path, "foo") on the previous example will
|
||||||
|
# return bar
|
||||||
|
|
||||||
|
def _ldap_path_extract(path, info):
|
||||||
|
for element in path.split(","):
|
||||||
|
if element.startswith(info + "="):
|
||||||
|
return element[len(info + "="):]
|
||||||
|
|
||||||
|
|
||||||
# Add this to properly close / delete the ldap interface / authenticator
|
# Add this to properly close / delete the ldap interface / authenticator
|
||||||
# when Python exits ...
|
# when Python exits ...
|
||||||
# Otherwise there's a risk that some funky error appears at the very end
|
# Otherwise there's a risk that some funky error appears at the very end
|
||||||
|
|
Loading…
Add table
Reference in a new issue