Merge branch 'stretch-unstable' into better-handling-of-app-install-and-remove-failures

This commit is contained in:
Alexandre Aubin 2019-10-14 19:08:15 +02:00 committed by GitHub
commit 1f2da5c26b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 2322 additions and 1515 deletions

View file

@ -201,24 +201,34 @@ user:
subcategories:
group:
subcategory_help: Manage group
subcategory_help: Manage user groups
actions:
### user_group_list()
list:
action_help: List group
action_help: List existing groups
api: GET /users/groups
arguments:
--fields:
help: fields to fetch
nargs: "+"
-s:
full: --short
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()
add:
### user_group_create()
create:
action_help: Create group
api: POST /users/groups
arguments:
groupname:
help: The unique group name to add
help: Name of the group to be created
extra:
pattern: &pattern_groupname
- !!str ^[a-z0-9_]+$
@ -230,7 +240,7 @@ user:
api: DELETE /users/groups/<groupname>
arguments:
groupname:
help: Username to delete
help: Name of the group to be deleted
extra:
pattern: *pattern_groupname
@ -240,19 +250,19 @@ user:
api: PUT /users/groups/<groupname>
arguments:
groupname:
help: Username to update
help: Name of the group to be updated
extra:
pattern: *pattern_groupname
-a:
full: --add-user
help: User to add in group
full: --add
help: User(s) to add in the group
nargs: "*"
metavar: USERNAME
extra:
pattern: *pattern_username
-r:
full: --remove-user
help: User to remove in group
full: --remove
help: User(s) to remove in the group
nargs: "*"
metavar: USERNAME
extra:
@ -260,112 +270,62 @@ user:
### user_group_info()
info:
action_help: Get group information
action_help: Get information about a specific group
api: GET /users/groups/<groupname>
arguments:
groupname:
help: Groupname to get information
help: Name of the group to fetch info about
extra:
pattern: *pattern_username
permission:
subcategory_help: Manage user permission
subcategory_help: Manage permissions
actions:
### user_permission_list()
list:
action_help: List access to user and group
api: GET /users/permissions/<app>
action_help: List permissions and corresponding accesses
api: GET /users/permissions/<permission>
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:
full: --app
help: Application to manage the permission
full: --add
help: Group or usernames to grant this permission to
nargs: "*"
metavar: APP
-p:
full: --permission
help: Name of permission (main by default)
nargs: "*"
metavar: PERMISSION
-u:
full: --username
help: Username
nargs: "*"
metavar: USER
-g:
full: --group
help: Group name
nargs: "*"
metavar: GROUP
### user_permission_add()
add:
action_help: Grant access right to users and group
api: POST /users/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
metavar: GROUP_OR_USER
extra:
pattern: *pattern_username
-g:
full: --group
help: Group name
-r:
full: --remove
help: Group or usernames revoke this permission from
nargs: "*"
metavar: GROUP
metavar: GROUP_OR_USER
extra:
pattern: *pattern_username
### user_permission_remove()
remove:
action_help: Revoke access right to users and group
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
## user_permission_reset()
reset:
action_help: Reset allowed groups to the default (all_users) for a given permission
api: DELETE /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
permission:
help: Permission to manage (e.g. mail or nextcloud or wordpress.editors)
ssh:
subcategory_help: Manage ssh access
@ -832,6 +792,7 @@ app:
addaccess:
action_help: Grant access right to users (everyone by default)
api: PUT /access
deprecated: true
arguments:
apps:
nargs: "+"
@ -843,6 +804,7 @@ app:
removeaccess:
action_help: Revoke access right to users (everyone by default)
api: DELETE /access
deprecated: true
arguments:
apps:
nargs: "+"
@ -854,6 +816,7 @@ app:
clearaccess:
action_help: Reset access rights for the app
api: POST /access
deprecated: true
arguments:
apps:
nargs: "+"

View file

@ -218,6 +218,27 @@ ynh_install_app_dependencies () {
fi
local dep_app=${app//_/-} # Replace all '_' by '-'
#
# Epic ugly hack to fix the goddamn dependency nightmare of sury
# Sponsored by the "Djeezusse Fokin Kraiste Why Do Adminsys Has To Be So Fucking Complicated I Should Go Grow Potatoes Instead Of This Shit" collective
# https://github.com/YunoHost/issues/issues/1407
#
# If we require to install php dependency
if echo $dependencies | grep -q 'php';
then
# And we have packages from sury installed (7.0.33-10+weirdshiftafter instead of 7.0.33-0 on debian)
if dpkg --list | grep "php7.0" | grep -q -v "7.0.33-0+deb9u5"
then
# And sury ain't already installed
if ! grep -nrq "sury" /etc/apt/sources.list*
then
# Re-add sury
echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/sury.list
wget -O /etc/apt/trusted.gpg.d/sury.gpg https://packages.sury.org/php/apt.gpg
fi
fi
fi
cat > /tmp/${dep_app}-ynh-deps.control << EOF # Make a control file for equivs-build
Section: misc
Priority: optional

View file

@ -40,10 +40,13 @@ ynh_use_logrotate () {
fi
if [ $# -gt 0 ] && [ "$(echo ${1:0:1})" != "-" ]; then
if [ "$(echo ${1##*.})" == "log" ]; then # Keep only the extension to check if it's a logfile
local logfile=$1 # In this case, focus logrotate on the logfile
# If the given logfile parameter already exists as a file, or if it ends up with ".log",
# we just want to manage a single file
if [ -f "$1" ] || [ "$(echo ${1##*.})" == "log" ]; then
local logfile=$1
# Otherwise we assume we want to manage a directory and all its .log file inside
else
local logfile=$1/*.log # Else, uses the directory and all logfile into it.
local logfile=$1/*.log
fi
fi
# LEGACY CODE
@ -54,7 +57,7 @@ ynh_use_logrotate () {
fi
if [ -n "$logfile" ]
then
if [ "$(echo ${logfile##*.})" != "log" ]; then # Keep only the extension to check if it's a logfile
if [ ! -f "$1" ] && [ "$(echo ${logfile##*.})" != "log" ]; then # Keep only the extension to check if it's a logfile
local logfile="$logfile/*.log" # Else, uses the directory and all logfile into it.
fi
else

View file

@ -230,70 +230,82 @@ ynh_webpath_register () {
# Create a new permission for the app
#
# usage: ynh_permission_create --app "app" --permission "permission" --defaultdisallow [--urls "url" ["url" ...]]
# | arg: app - the application id
# usage: ynh_permission_create --permission "permission" [--urls "url" ["url" ...]]
# | arg: permission - the name for the permission (by default a permission named "main" already exist)
# | arg: defaultdisallow - define if all user will be allowed by default
# | arg: urls - the list of urls for the the permission
# | arg: urls - (optional) a list of FULL urls for the permission (e.g. domain.tld/apps/admin)
#
# example: ynh_permission_create --permission admin --urls domain.tld/blog/admin
ynh_permission_create() {
declare -Ar args_array=( [a]=app= [p]=permission= [d]=defaultdisallow [u]=urls= )
local app
declare -Ar args_array=( [p]=permission= [u]=urls= )
local permission
local defaultdisallow
local urls
ynh_handle_getopts_args "$@"
if [[ -n ${defaultdisallow:-} ]]; then
defaultdisallow=",default_allow=False"
fi
if [[ -n ${urls:-} ]]; then
urls=",urls=['${urls//';'/"','"}']"
fi
yunohost tools shell -c "from yunohost.permission import permission_add; permission_add('$app', '$permission' ${defaultdisallow:-} ${urls:-}, sync_perm=False)"
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)
#
# usage: ynh_permission_remove --app "app" --permission "permission"
# | arg: app - the application id
# usage: ynh_permission_remove --permission "permission"
# | arg: permission - the name for the permission (by default a permission named "main" is removed automatically when the app is removed)
ynh_permission_remove() {
declare -Ar args_array=( [a]=app= [p]=permission= )
local app
local permission
ynh_handle_getopts_args "$@"
yunohost tools shell -c "from yunohost.permission import permission_remove; permission_remove('$app', '$permission', sync_perm=False)"
}
# Add a path managed by the SSO
#
# usage: ynh_permission_add_path --app "app" --permission "permission" --url "url" ["url" ...]
# | arg: app - the application id
# | arg: permission - the name for the permission
# | arg: url - the FULL url for the the permission (ex domain.tld/apps/admin)
ynh_permission_add_path() {
declare -Ar args_array=( [a]=app= [p]=permission= [u]=url= )
local app
# example: ynh_permission_delete --permission editors
ynh_permission_delete() {
declare -Ar args_array=( [p]=permission= )
local permission
local url
ynh_handle_getopts_args "$@"
yunohost tools shell -c "from yunohost.permission import permission_update; permission_update('$app', '$permission', add_url=['${url//';'/"','"}'], sync_perm=False)"
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" ...]
# | arg: app - the application id
# | arg: permission - the name for the permission
# | arg: url - the FULL url for the the permission (ex domain.tld/apps/admin)
ynh_permission_del_path() {
declare -Ar args_array=( [a]=app= [p]=permission= [u]=url= )
local app
# usage: ynh_permission_urls --permission "permission" --add "url" ["url" ...] --remove "url" ["url" ...]
# | arg: permission - the name for the permission (by default a permission named "main" is removed automatically when the app is removed)
# | arg: add - (optional) a list of FULL urls to add to the permission (e.g. 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_urls() {
declare -Ar args_array=([p]=permission= [a]=add= [r]=remove=)
local permission
local url
local add
local remove
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:-}
}

View file

@ -53,9 +53,6 @@ do_pre_regen() {
else
sudo cp services.yml /etc/yunohost/services.yml
fi
mkdir -p "$pending_dir"/etc/etckeeper/
cp etckeeper.conf "$pending_dir"/etc/etckeeper/
}
_update_services() {

View file

@ -3,6 +3,5 @@ backup_dir="$1/conf/ynh/certs"
sudo mkdir -p /etc/yunohost/certs/
sudo cp -a $backup_dir/. /etc/yunohost/certs/
sudo yunohost app ssowatconf
sudo service nginx reload
sudo service metronome reload

View file

@ -59,16 +59,16 @@ children:
- groupOfNamesYnh
depends_children:
cn=main.mail,ou=permission:
cn: main.mail
cn=mail.main,ou=permission:
cn: mail.main
gidNumber: "5001"
objectClass:
- posixGroup
- permissionYnh
groupPermission:
- "cn=all_users,ou=groups,dc=yunohost,dc=org"
cn=main.metronome,ou=permission:
cn: main.metronome
cn=xmpp.main,ou=permission:
cn: xmpp.main
gidNumber: "5002"
objectClass:
- posixGroup

View file

@ -3,7 +3,7 @@ auth_bind = yes
ldap_version = 3
base = ou=users,dc=yunohost,dc=org
user_attrs = uidNumber=500,gidNumber=8,mailuserquota=quota_rule=*:bytes=%$
user_filter = (&(objectClass=inetOrgPerson)(uid=%n)(permission=cn=main.mail,ou=permission,dc=yunohost,dc=org))
pass_filter = (&(objectClass=inetOrgPerson)(uid=%n)(permission=cn=main.mail,ou=permission,dc=yunohost,dc=org))
user_filter = (&(objectClass=inetOrgPerson)(uid=%n)(permission=cn=mail.main,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

View file

@ -63,7 +63,7 @@ bantime = 600
findtime = 600
# "maxretry" is the number of failures before a host get banned.
maxretry = 5
maxretry = 10
# "backend" specifies the backend used to get files modification.
# Available options are "pyinotify", "gamin", "polling", "systemd" and "auto".

View file

@ -29,4 +29,3 @@ protocol = tcp
filter = yunohost
logpath = /var/log/nginx/*error.log
/var/log/nginx/*access.log
maxretry = 10

View file

@ -8,7 +8,7 @@ VirtualHost "{{ domain }}"
hostname = "localhost",
user = {
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",
namefield = "cn",
},

View file

@ -1,5 +1,5 @@
server_host = localhost
server_port = 389
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

View file

@ -1,5 +1,5 @@
server_host = localhost
server_port = 389
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

View file

@ -1,43 +0,0 @@
# The VCS to use.
#VCS="hg"
VCS="git"
#VCS="bzr"
#VCS="darcs"
# Options passed to git commit when run by etckeeper.
GIT_COMMIT_OPTIONS="--quiet"
# Options passed to hg commit when run by etckeeper.
HG_COMMIT_OPTIONS=""
# Options passed to bzr commit when run by etckeeper.
BZR_COMMIT_OPTIONS=""
# Options passed to darcs record when run by etckeeper.
DARCS_COMMIT_OPTIONS="-a"
# Uncomment to avoid etckeeper committing existing changes
# to /etc automatically once per day.
#AVOID_DAILY_AUTOCOMMITS=1
# Uncomment the following to avoid special file warning
# (the option is enabled automatically by cronjob regardless).
#AVOID_SPECIAL_FILE_WARNING=1
# Uncomment to avoid etckeeper committing existing changes to
# /etc before installation. It will cancel the installation,
# so you can commit the changes by hand.
#AVOID_COMMIT_BEFORE_INSTALL=1
# The high-level package manager that's being used.
# (apt, pacman-g2, yum, zypper etc)
HIGHLEVEL_PACKAGE_MANAGER=apt
# The low-level package manager that's being used.
# (dpkg, rpm, pacman, pacman-g2, etc)
LOWLEVEL_PACKAGE_MANAGER=dpkg
# To push each commit to a remote, put the name of the remote here.
# (eg, "origin" for git). Space-separated lists of multiple remotes
# also work (eg, "origin gitlab github" for git).
PUSH_REMOTE=""

21
debian/changelog vendored
View file

@ -1,3 +1,24 @@
yunohost (3.6.5.2) stable; urgency=low
- [fix] Alex was drunk and released an epic stupid bug in stable (2623d385)
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 10 Oct 2019 01:00:00 +0000
yunohost (3.6.5.1) stable; urgency=low
- [mod] Change maxretry of fail2ban from 6 to 10 (fe8fd1b)
-- Alexandre Aubin <alex.aubin@mailoo.org> Tue, 08 Oct 2019 20:00:00 +0000
yunohost (3.6.5) stable; urgency=low
- [enh] Detect and warn early about unavailable full domains... (#798)
- [mod] Change maxretry of fail2ban from 6 to 10 (#802)
- [fix] Epicly ugly workaround for the goddamn dependency nighmare about sury fucking up php7.0 dependencies (#809)
- [fix] Support logfiles not ending with .log in logrotate ... (#810)
-- Alexandre Aubin <alex.aubin@mailoo.org> Tue, 08 Oct 2019 19:00:00 +0000
yunohost (3.6.4.6) stable; urgency=low
- [fix] Hopefully fix the issue about corrupted logs metadata files (d507d447, 1cec9d78)

2
debian/control vendored
View file

@ -31,7 +31,7 @@ Depends: ${python:Depends}, ${misc:Depends}
, equivs, lsof
Recommends: yunohost-admin
, openssh-server, ntp, inetutils-ping | iputils-ping
, bash-completion, rsyslog, etckeeper
, bash-completion, rsyslog
, php-gd, php-curl, php-gettext, php-mcrypt
, python-pip
, unattended-upgrades

View file

@ -4,7 +4,7 @@
"admin_password_change_failed": "Passwort kann nicht geändert werden",
"admin_password_changed": "Das Administrator-Kennwort wurde geändert",
"app_already_installed": "{app:s} ist schon installiert",
"app_argument_choice_invalid": "Verwende einen der folgenden Werte {choices:s}",
"app_argument_choice_invalid": "Wähle einen der folgenden Werte '{choices:s}' für das Argument '{name:s}'",
"app_argument_invalid": "Wähle einen gültigen Wert für das Argument '{name: s}': {error: s}",
"app_argument_required": "Argument '{name:s}' wird benötigt",
"app_extraction_failed": "Installationsdateien konnten nicht entpackt werden",
@ -20,9 +20,9 @@
"app_sources_fetch_failed": "Quelldateien konnten nicht abgerufen werden, ist die URL korrekt?",
"app_unknown": "Unbekannte App",
"app_upgrade_failed": "{app:s} konnte nicht aktualisiert werden",
"app_upgraded": "{app:s} wurde erfolgreich aktualisiert",
"appslist_fetched": "Appliste {appslist:s} wurde erfolgreich heruntergelanden",
"appslist_removed": "Appliste {appslist:s} wurde erfolgreich entfernt",
"app_upgraded": "{app:s} aktualisiert",
"appslist_fetched": "Appliste {appslist:s} wurde erfolgreich gelanden",
"appslist_removed": "Appliste {appslist:s} wurde entfernt",
"appslist_retrieve_error": "Entfernte Appliste {appslist:s} kann nicht empfangen werden: {error:s}",
"appslist_unknown": "Appliste {appslist:s} ist unbekannt.",
"ask_current_admin_password": "Derzeitiges Administrator-Kennwort",
@ -34,23 +34,23 @@
"ask_new_admin_password": "Neues Verwaltungskennwort",
"ask_password": "Passwort",
"backup_action_required": "Du musst etwas zum Speichern auswählen",
"backup_app_failed": "Konnte keine Sicherung für '{app:s}' erstellen",
"backup_app_failed": "Konnte keine Sicherung für die App '{app:s}' erstellen",
"backup_archive_app_not_found": "App '{app:s}' konnte in keiner Datensicherung gefunden werden",
"backup_archive_hook_not_exec": "Hook '{hook:s}' konnte für diese Datensicherung nicht ausgeführt werden",
"backup_archive_name_exists": "Datensicherung mit dem selben Namen existiert bereits",
"backup_archive_name_exists": "Datensicherung mit dem selben Namen existiert bereits.",
"backup_archive_name_unknown": "Unbekanntes lokale Datensicherung mit Namen '{name:s}' gefunden",
"backup_archive_open_failed": "Kann Sicherungsarchiv nicht öfnen",
"backup_cleaning_failed": "Temporäres Sicherungsverzeichnis konnte nicht geleert werden",
"backup_created": "Datensicherung komplett",
"backup_creating_archive": "Datensicherung wird erstellt…",
"backup_delete_error": "Pfad '{path:s}' konnte nicht gelöscht werden",
"backup_deleted": "Datensicherung wurde entfernt",
"backup_deleted": "Backup wurde entfernt",
"backup_extracting_archive": "Entpacke Sicherungsarchiv...",
"backup_hook_unknown": "Datensicherungshook '{hook:s}' unbekannt",
"backup_invalid_archive": "Ungültige Datensicherung",
"backup_nothings_done": "Es gibt keine Änderungen zur Speicherung",
"backup_output_directory_forbidden": "Verbotenes Ausgabeverzeichnis. Datensicherung können nicht in /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var oder in Unterordnern von /home/yunohost.backup/archives erstellt werden",
"backup_output_directory_not_empty": "Ausgabeordner ist nicht leer",
"backup_hook_unknown": "Der Datensicherungshook '{hook:s}' unbekannt",
"backup_invalid_archive": "Dies ist kein Backup-Archiv",
"backup_nothings_done": "Keine Änderungen zur Speicherung",
"backup_output_directory_forbidden": "Wähle ein anderes Ausgabeverzeichnis. Datensicherung können nicht in /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var oder in Unterordnern von /home/yunohost.backup/archives erstellt werden",
"backup_output_directory_not_empty": "Der gewählte Ausgabeordner sollte leer sein",
"backup_output_directory_required": "Für die Datensicherung muss ein Zielverzeichnis angegeben werden",
"backup_running_app_script": "Datensicherung für App '{app:s}' wurd durchgeführt...",
"backup_running_hooks": "Datensicherunghook wird ausgeführt…",
@ -213,7 +213,7 @@
"app_not_properly_removed": "{app:s} wurde nicht ordnungsgemäß entfernt",
"service_regenconf_failed": "Konnte die Konfiguration für folgende Dienste nicht neu erzeugen: {services}",
"not_enough_disk_space": "Zu wenig freier Speicherplatz unter '{path:s}' verfügbar",
"backup_creation_failed": "Erstellen des Backups fehlgeschlagen",
"backup_creation_failed": "Konnte Backup-Archiv nicht erstellen",
"service_conf_up_to_date": "Die Konfiguration für den Dienst '{service}' ist bereits aktuell",
"package_not_installed": "Das Paket '{pkgname}' ist nicht installiert",
"pattern_positive_number": "Muss eine positive Zahl sein",
@ -277,28 +277,28 @@
"service_regenconf_pending_applying": "Überprüfe ausstehende Konfigurationen, die für den Server '{service}' notwendig sind...",
"certmanager_http_check_timeout": "Eine Zeitüberschreitung ist aufgetreten als der Server versuchte sich selbst über HTTP mit der öffentlichen IP (Domain {domain:s} mit der IP {ip:s}) zu erreichen. Möglicherweise ist dafür hairpinning oder eine falsch konfigurierte Firewall/Router deines Servers dafür verantwortlich.",
"certmanager_couldnt_fetch_intermediate_cert": "Eine Zeitüberschreitung ist aufgetreten als der Server versuchte die Teilzertifikate von Let's Encrypt zusammenzusetzen. Die Installation/Erneuerung des Zertifikats wurde abgebrochen - bitte versuche es später erneut.",
"appslist_retrieve_bad_format": "Die empfangene Datei der Appliste {appslist:s} ist ungültig",
"appslist_retrieve_bad_format": "Die geladene Appliste {appslist:s} ist ungültig",
"domain_hostname_failed": "Erstellen des neuen Hostnamens fehlgeschlagen",
"appslist_name_already_tracked": "Es gibt bereits eine registrierte App-Liste mit Namen {name:s}.",
"appslist_url_already_tracked": "Es gibt bereits eine registrierte Anwendungsliste mit dem URL {url:s}.",
"appslist_url_already_tracked": "Es gibt bereits eine registrierte Anwendungsliste mit der URL {url:s}.",
"appslist_migrating": "Migriere Anwendungsliste {appslist:s} …",
"appslist_could_not_migrate": "Konnte Anwendungsliste {appslist:s} nicht migrieren. Konnte die URL nicht verarbeiten... Der alte Cron-Job wurde unter {bkp_file:s} beibehalten.",
"appslist_corrupted_json": "Konnte die Anwendungslisten. Es scheint, dass {filename:s} beschädigt ist.",
"appslist_could_not_migrate": "Konnte die Anwendungsliste {appslist:s} nicht migrieren. Konnte die URL nicht verarbeiten... Der alte Cron-Job wurde unter {bkp_file:s} beibehalten.",
"appslist_corrupted_json": "Anwendungslisten konnte nicht geladen werden. Es scheint, dass {filename:s} beschädigt ist.",
"yunohost_ca_creation_success": "Die lokale Zertifizierungs-Authorität wurde angelegt.",
"app_already_installed_cant_change_url": "Diese Application ist bereits installiert. Die URL kann durch diese Funktion nicht modifiziert werden. Überprüfe ob `app changeurl` verfügbar ist.",
"app_change_no_change_url_script": "Die Application {app_name:s} unterstützt das anpassen der URL noch nicht. Sie muss gegebenenfalls erweitert werden.",
"app_change_url_failed_nginx_reload": "NGINX konnte nicht neu gestartet werden. Hier ist der Output von 'nginx -t':\n{nginx_errors:s}",
"app_change_url_identical_domains": "Die alte und neue domain/url_path sind identisch: ('{domain:s} {path:s}'). Es gibt nichts zu tun.",
"app_already_up_to_date": "{app:s} ist schon aktuell",
"app_already_up_to_date": "{app:s} ist bereits aktuell",
"backup_abstract_method": "Diese Backup-Methode wird noch nicht unterstützt",
"backup_applying_method_tar": "Erstellen des Backup-tar Archives...",
"backup_applying_method_tar": "Erstellen des Backup-tar Archives",
"backup_applying_method_copy": "Kopiere alle Dateien ins Backup…",
"app_change_url_no_script": "Die Anwendung '{app_name:s}' unterstützt bisher keine URL-Modufikation. Vielleicht gibt es eine Aktualisierung.",
"app_location_unavailable": "Diese URL ist entweder nicht verfügbar oder steht in Konflikt mit den bereits installierten Apps:\n{apps: s}",
"app_location_unavailable": "Diese URL ist nicht verfügbar oder wird von einer installierten Anwendung genutzt:\n{apps:s}",
"backup_applying_method_custom": "Rufe die benutzerdefinierte Backup-Methode '{method:s}' auf…",
"backup_archive_system_part_not_available": "Der System-Teil '{part:s}' ist in diesem Backup nicht enthalten",
"backup_archive_mount_failed": "Das Einbinden des Backup-Archives ist fehlgeschlagen",
"backup_archive_writing_error": "Die Dateien konnten nicht in der komprimierte Archiv-Backup hinzugefügt werden",
"backup_archive_writing_error": "Die Dateien '{source:s} (im Ordner '{dest:s}') konnten nicht in das komprimierte Archiv-Backup '{archive:s}' hinzugefügt werden",
"app_change_url_success": "{app:s} URL ist nun {domain:s}{path:s}",
"backup_applying_method_borg": "Sende alle Dateien zur Sicherung ins borg-backup repository…",
"invalid_url_format": "ungültiges URL Format",
@ -325,22 +325,22 @@
"backup_permission": "Sicherungsberechtigung für App {app: s}",
"backup_output_symlink_dir_broken": "Sie haben einen fehlerhaften Symlink anstelle Ihres Archivverzeichnisses '{path: s}'. Möglicherweise haben Sie ein spezielles Setup, um Ihre Daten auf einem anderen Dateisystem zu sichern. In diesem Fall haben Sie wahrscheinlich vergessen, Ihre Festplatte oder Ihren USB-Schlüssel erneut einzuhängen oder anzuschließen.",
"backup_mount_archive_for_restore": "Archiv für Wiederherstellung vorbereiten…",
"backup_method_tar_finished": "Sicherungs-Tar-Archiv erstellt",
"backup_method_tar_finished": "Tar-Backup-Archiv erstellt",
"backup_method_custom_finished": "Benutzerdefinierte Sicherungsmethode '{method: s}' beendet",
"backup_method_copy_finished": "Sicherungskopie beendet",
"backup_method_borg_finished": "Backup in Borg beendet",
"backup_custom_need_mount_error": "Bei der benutzerdefinierten Sicherungsmethode ist beim Arbeitsschritt \"Braucht ein Einhängen/Verbinden\" (need_mount) ein Fehler aufgetreten",
"backup_custom_mount_error": "Bei der benutzerdefinierten Sicherungsmethode ist beim Arbeitsschritt \"Einhängen/Verbinden\" ein Fehler aufgetreten",
"backup_custom_backup_error": "Bei der benutzerdefinierten Sicherungsmethode ist beim Arbeitsschritt \"Sicherung\" ein Fehler aufgetreten",
"backup_csv_creation_failed": "Die CSV-Datei, die für zukünftige Wiederherstellungsvorgänge erforderlich ist, kann nicht erstellt werden",
"backup_csv_creation_failed": "Die zur Wiederherstellung erforderliche CSV-Datei kann nicht erstellt werden",
"backup_couldnt_bind": "{Src: s} konnte nicht an {dest: s} angebunden werden.",
"backup_borg_not_implemented": "Die Borg-Sicherungsmethode ist noch nicht implementiert",
"backup_ask_for_copying_if_needed": "Einige Dateien konnten mit der Methode, die es vermeidet vorübergehend Speicherplatz auf dem System zu verschwenden, nicht gesichert werden. Zur Durchführung der Sicherung sollten vorübergehend {size: s} MB verwendet werden. Sind Sie einverstanden?",
"backup_actually_backuping": "Erstelle nun ein Backup-Archiv aus den gesammelten Dateien …",
"backup_actually_backuping": "Erstellt ein Backup-Archiv aus den gesammelten Dateien …",
"ask_path": "Pfad",
"ask_new_path": "Neuer Pfad",
"ask_new_domain": "Neue Domain",
"apps_permission_restoration_failed": "Die Berechtigung '{permission: s}' für die Wiederherstellung der App {app: s} ist fehlgeschlagen",
"apps_permission_restoration_failed": "Erteilen der Berechtigung '{permission: s}' für die Wiederherstellung der App {app: s} erforderlich",
"apps_permission_not_found": "Keine Berechtigung für die installierten Apps gefunden",
"app_upgrade_some_app_failed": "Einige Anwendungen können nicht aktualisiert werden",
"app_upgrade_app_name": "{App} wird jetzt aktualisiert…",
@ -354,5 +354,64 @@
"aborting": "Breche ab.",
"app_action_cannot_be_ran_because_required_services_down": "Diese App erfordert einige Dienste, die derzeit nicht verfügbar sind. Bevor Sie fortfahren, sollten Sie versuchen, die folgenden Dienste neu zu starten (und möglicherweise untersuchen, warum sie nicht verfügbar sind): {services}",
"already_up_to_date": "Nichts zu tun. Alles ist bereits auf dem neusten Stand.",
"admin_password_too_long": "Bitte ein Passwort kürzer als 127 Zeichen wählen"
"admin_password_too_long": "Bitte ein Passwort kürzer als 127 Zeichen wählen",
"app_action_broke_system": "Diese Aktion hat anscheinend diese wichtigen Services gestört: {services}",
"apps_already_up_to_date": "Alle Apps sind bereits aktuell",
"backup_copying_to_organize_the_archive": "Kopieren von {size: s} MB, um das Archiv zu organisieren",
"app_upgrade_stopped": "Das Upgrade aller Anwendungen wurde gestoppt, um mögliche Schäden zu vermeiden, da das Upgrade der vorherigen Anwendung fehlgeschlagen ist",
"group_already_disallowed": "Die Gruppe '{group:s}' hat bereits die Berechtigungen '{permission:s}' für die App '{app:s}' deaktiviert",
"global_settings_setting_security_ssh_compatibility": "Kompatibilität vs. Sicherheitskompromiss für den SSH-Server. Beeinflusst die Chiffren (und andere sicherheitsrelevante Aspekte)",
"group_deleted": "Gruppe '{group}' gelöscht",
"group_deletion_failed": "Kann Gruppe '{group}' nicht löschen",
"group_deletion_not_allowed": "Die Gruppe {group:s} kann nicht manuell gelöscht werden.",
"dyndns_provider_unreachable": "Dyndns-Anbieter {provider} kann nicht erreicht werden: Entweder ist dein YunoHost nicht korrekt mit dem Internet verbunden oder der Dynette-Server ist ausgefallen.",
"group_already_allowed": "Gruppe '{group:s}' hat bereits die Berechtigung '{permission:s}' für die App '{app:s}' eingeschaltet",
"group_name_already_exist": "Gruppe {name:s} existiert bereits",
"group_created": "Gruppe '{group}' angelegt",
"group_creation_failed": "Kann Gruppe '{group}' nicht anlegen",
"group_unknown": "Die Gruppe '{group:s}' ist unbekannt",
"group_updated": "Gruppe '{group:s}' erneuert",
"group_update_failed": "Kann Gruppe '{group:s}' nicht anpassen",
"log_does_exists": "Es gibt kein Operationsprotokoll mit dem Namen'{log}', verwende'yunohost log list', um alle verfügbaren Operationsprotokolle anzuzeigen",
"log_app_removelist": "Entferne eine Applikationsliste",
"log_operation_unit_unclosed_properly": "Die Operationseinheit wurde nicht richtig geschlossen",
"log_app_removeaccess": "Entziehe Zugriff auf '{}'",
"global_settings_setting_security_postfix_compatibility": "Kompatibilität vs. Sicherheitskompromiss für den Postfix-Server. Beeinflusst die Chiffren (und andere sicherheitsrelevante Aspekte)",
"log_category_404": "Die Log-Kategorie '{category}' existiert nicht",
"global_settings_unknown_type": "Unerwartete Situation, die Einstellung {setting:s} scheint den Typ {unknown_type:s} zu haben, ist aber kein vom System unterstützter Typ.",
"dpkg_is_broken": "Du kannst das gerade nicht tun, weil dpkg/APT (der Systempaketmanager) in einem defekten Zustand zu sein scheint.... Du kannst versuchen, dieses Problem zu lösen, indem du dich über SSH verbindest und `sudo dpkg --configure -a` ausführst.",
"global_settings_unknown_setting_from_settings_file": "Unbekannter Schlüssel in den Einstellungen: '{setting_key:s}', verwerfen und speichern in /etc/yunohost/settings-unknown.json",
"log_link_to_log": "Vollständiges Log dieser Operation: '<a href=\"#/tools/logs/{name}\" style=\"text-decoration:underline\">{desc}</a>'",
"global_settings_setting_example_bool": "Beispiel einer booleschen Option",
"log_app_fetchlist": "Füge eine Applikationsliste hinzu",
"log_help_to_get_log": "Um das Protokoll der Operation '{desc}' anzuzeigen, verwende den Befehl 'yunohost log display {name}'",
"global_settings_setting_security_nginx_compatibility": "Kompatibilität vs. Sicherheitskompromiss für den Webserver NGINX. Beeinflusst die Chiffren (und andere sicherheitsrelevante Aspekte)",
"backup_php5_to_php7_migration_may_fail": "Dein Archiv konnte nicht für PHP 7 konvertiert werden, Du kannst deine PHP-Anwendungen möglicherweise nicht wiederherstellen (Grund: {error:s})",
"global_settings_setting_service_ssh_allow_deprecated_dsa_hostkey": "Erlaubt die Verwendung eines (veralteten) DSA-Hostkeys für die SSH-Daemon-Konfiguration",
"global_settings_setting_example_string": "Beispiel einer string Option",
"log_app_addaccess": "Füge Zugriff auf '{}' hinzu",
"log_app_remove": "Entferne die Anwendung '{}'",
"global_settings_setting_example_int": "Beispiel einer int Option",
"global_settings_cant_open_settings": "Einstellungsdatei konnte nicht geöffnet werden, Grund: {reason:s}",
"global_settings_cant_write_settings": "Einstellungsdatei konnte nicht gespeichert werden, Grund: {reason:s}",
"log_app_install": "Installiere die Anwendung '{}'",
"global_settings_reset_success": "Frühere Einstellungen werden nun auf {path:s} gesichert",
"log_app_upgrade": "Upgrade der Anwendung '{}'",
"good_practices_about_admin_password": "Sie sind nun dabei, ein neues Administrationspasswort zu definieren. Das Passwort sollte mindestens 8 Zeichen lang sein - obwohl es sinnvoll ist, ein längeres Passwort (z.B. eine Passphrase) und/oder eine Variation von Zeichen (Groß- und Kleinschreibung, Ziffern und Sonderzeichen) zu verwenden.",
"log_corrupted_md_file": "Die mit Protokollen verknüpfte YAML-Metadatendatei ist beschädigt: '{md_file}\nFehler: {error}''",
"global_settings_cant_serialize_settings": "Einstellungsdaten konnten nicht serialisiert werden, Grund: {reason:s}",
"log_help_to_get_failed_log": "Der Vorgang'{desc}' konnte nicht abgeschlossen werden. Bitte teile das vollständige Protokoll dieser Operation mit dem Befehl 'yunohost log display {name} --share', um Hilfe zu erhalten",
"backup_no_uncompress_archive_dir": "Dieses unkomprimierte Archivverzeichnis gibt es nicht",
"log_app_change_url": "Ändere die URL der Anwendung '{}'",
"global_settings_setting_security_password_user_strength": "Stärke des Benutzerpassworts",
"good_practices_about_user_password": "Du bist nun dabei, ein neues Benutzerpasswort zu definieren. Das Passwort sollte mindestens 8 Zeichen lang sein - obwohl es ratsam ist, ein längeres Passwort (z.B. eine Passphrase) und/oder eine Variation von Zeichen (Groß- und Kleinschreibung, Ziffern und Sonderzeichen) zu verwenden.",
"global_settings_setting_example_enum": "Beispiel einer enum Option",
"log_link_to_failed_log": "Der Vorgang konnte nicht abgeschlossen werden '{desc}'. Bitte gib das vollständige Protokoll dieser Operation mit <a href=\"#/tools/logs/{name}\">Klicken Sie hier</a> an, um Hilfe zu erhalten",
"backup_cant_mount_uncompress_archive": "Das unkomprimierte Archiv konnte nicht als schreibgeschützt gemountet werden",
"backup_csv_addition_failed": "Es konnten keine Dateien zur Sicherung in die CSV-Datei hinzugefügt werden",
"log_app_clearaccess": "Entziehe alle Zugriffe auf '{}'",
"global_settings_setting_security_password_admin_strength": "Stärke des Admin-Passworts",
"global_settings_key_doesnt_exists": "Der Schlüssel'{settings_key:s}' existiert nicht in den globalen Einstellungen, du kannst alle verfügbaren Schlüssel sehen, indem du 'yunohost settings list' ausführst",
"log_app_makedefault": "Mache '{}' zur Standard-Anwendung",
"hook_json_return_error": "Konnte die Rückkehr vom Einsprungpunkt {path:s} nicht lesen. Fehler: {msg:s}. Unformatierter Inhalt: {raw_content}"
}

View file

@ -19,6 +19,7 @@
"app_change_url_no_script": "This application '{app_name:s}' doesn't support URL modification yet. Maybe you should upgrade it.",
"app_change_url_success": "{app:s} URL is now {domain:s}{path:s}",
"app_extraction_failed": "Could not extract the installation files",
"app_full_domain_unavailable": "Sorry, this application requires a full domain to be installed on, but some other apps are already installed on domain '{domain}'. One possible solution is to add and use a subdomain dedicated to this application instead.",
"app_id_invalid": "Invalid app ID",
"app_incompatible": "The app {app} is incompatible with your YunoHost version",
"app_install_files_invalid": "These files cannot be installed",
@ -53,10 +54,9 @@
"app_upgraded": "{app:s} upgraded",
"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_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_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_name_already_tracked": "A registered application list with name {name:s} already exists.",
"appslist_removed": "{appslist:s} application list removed",
@ -146,8 +146,8 @@
"certmanager_self_ca_conf_file_not_found": "Could not find configuration file for self-signing authority (file: {file:s})",
"certmanager_unable_to_parse_self_CA_name": "Could not parse name of self-signing authority (file: {file:s})",
"confirm_app_install_warning": "Warning: This application may work, but is not well-integrated in YunoHost. Some features such as single sign-on and backup/restore might not be available. Install anyway? [{answers:s}] ",
"confirm_app_install_danger": "WARNING! This application is still experimental (if not explicitly not working) and it is likely to break your system! You should probably NOT install it unless you know what you are doing. Are you willing to take that risk? [{answers:s}] ",
"confirm_app_install_thirdparty": "WARNING! Installing third-party applications may compromise the integrity and security of your system. You should probably NOT install it unless you know what you are doing. Are you willing to take that risk? [{answers:s}] ",
"confirm_app_install_danger": "DANGER! This application is known to be still experimental (if not explicitly not working)! You should probably NOT install it unless you know what you are doing. NO SUPPORT will be provided if this app doesn't work or break your system... If you are willing to take that risk anyway, type '{answers:s}'",
"confirm_app_install_thirdparty": "DANGER! This application is not part of Yunohost's application catalog. Installing third-party applications may compromise the integrity and security of your system. You should probably NOT install it unless you know what you are doing. NO SUPPORT will be provided if this app doesn't work or break your system... If you are willing to take that risk anyway, type '{answers:s}'",
"custom_app_url_required": "You must provide a URL to upgrade your custom app {app:s}",
"custom_appslist_name_required": "You must provide a name for your custom app list",
"diagnosis_debian_version_error": "Could not retrieve the Debian version: {error}",
@ -160,9 +160,9 @@
"domain_cannot_remove_main": "Cannot remove main domain. Set one first",
"domain_cert_gen_failed": "Could not generate certificate",
"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_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_dyndns_already_subscribed": "You have already subscribed to a DynDNS domain",
"domain_dyndns_root_unknown": "Unknown DynDNS root domain",
@ -188,9 +188,6 @@
"dyndns_registration_failed": "Could not register DynDNS domain: {error:s}",
"dyndns_domain_not_provided": "DynDNS provider {provider:s} cannot provide domain {domain:s}.",
"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_script": "Executing script '{script:s}'…",
"extracting": "Extracting…",
@ -221,17 +218,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.",
"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).",
"group_already_allowed": "Group '{group:s}' already has permission '{permission:s}' turned on for the app '{app:s}'",
"group_already_disallowed": "Group '{group:s}' already has permissions '{permission:s}' turned off for the app '{app:s}'",
"group_name_already_exist": "Group {name:s} already exists",
"group_already_exist": "Group {group} already exists",
"group_already_exist_on_system": "Group {group} already exists in the system groups",
"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_deletion_failed": "Could not delete the group '{group}'",
"group_deletion_not_allowed": "The group {group:s} cannot be deleted manually.",
"group_deletion_failed": "Could not delete the group '{group}': {error}",
"group_unknown": "The group '{group:s}' is unknown",
"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_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}",
@ -249,9 +248,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_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_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_removelist": "Remove an application list",
"log_app_change_url": "Change the URL of '{}' application",
@ -269,20 +265,20 @@
"log_dyndns_subscribe": "Subscribe to a 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_permission_add": "Add the '{}' permission for the app '{}'",
"log_permission_remove": "Remove permission '{}'",
"log_permission_update": "Update permission '{}' for app '{}'",
"log_permission_create": "Create permission '{}'",
"log_permission_delete": "Delete permission '{}'",
"log_permission_urls": "Update urls related to permission '{}'",
"log_selfsigned_cert_install": "Install self signed certificate on '{}' domain",
"log_letsencrypt_cert_renew": "Renew '{}' Let's encrypt certificate",
"log_regen_conf": "Regenerate system configurations '{}'",
"log_user_create": "Add '{}' user",
"log_user_delete": "Delete '{}' user",
"log_user_group_add": "Add '{}' group",
"log_user_group_create": "Create '{}' group",
"log_user_group_delete": "Delete '{}' group",
"log_user_group_update": "Update '{}' group",
"log_user_update": "Update user info of '{}'",
"log_user_permission_add": "Update '{}' permission",
"log_user_permission_remove": "Update '{}' permission",
"log_user_permission_update": "Update accesses for permission '{}'",
"log_user_permission_reset": "Reset permission '{}'",
"log_tools_maindomain": "Make '{}' the main domain",
"log_tools_migrations_migrate_forward": "Migrate forward",
"log_tools_postinstall": "Postinstall your YunoHost server",
@ -346,16 +342,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_0009_not_needed": "This migration already happened somehow… (?) Skipping.",
"migration_0011_backup_before_migration": "Creating a backup of LDAP database and apps settings prior to the actual migration.",
"migration_0011_can_not_backup_before_migration": "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_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_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_rollback_success": "System rolled back.",
"migration_0011_update_LDAP_database": "Updating LDAP database…",
"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_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}",
@ -386,7 +383,6 @@
"mysql_db_creation_failed": "MySQL database creation failed",
"mysql_db_init_failed": "MySQL database init failed",
"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_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",
@ -415,25 +411,23 @@
"pattern_positive_number": "Must be a positive number",
"pattern_username": "Must be lower-case alphanumeric and underscore characters only",
"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_exist": "Permission '{permission:s}' for app {app:s} already exist",
"permission_created": "Permission '{permission:s}' for app {app:s} created",
"permission_creation_failed": "Could not grant permission",
"permission_deleted": "Permission '{permission:s}' for app {app:s} deleted",
"permission_deletion_failed": "Missing permission '{permission:s}' to delete the app '{app:s}'",
"permission_not_found": "Permission '{permission:s}' not found for the application '{app:s}'",
"permission_name_not_valid": "Pick an allowed permission name for '{permission:s}'",
"permission_update_failed": "Could not update permission",
"permission_generated": "Permission database updated",
"permission_updated": "Permission '{permission:s}' for the app '{app:s}' updated",
"permission_already_allowed": "Group '{group}' already has permission '{permission}' enabled'",
"permission_already_disallowed": "Group '{group}' already has permission '{permission}' disabled'",
"permission_already_exist": "Permission '{permission}' already exists",
"permission_cannot_remove_main": "Removing a main permission is not allowed",
"permission_created": "Permission '{permission:s}' created",
"permission_creation_failed": "Could not create permission '{permission}': {error}",
"permission_deleted": "Permission '{permission:s}' deleted",
"permission_deletion_failed": "Could not delete permission '{permission}': {error}",
"permission_not_found": "Permission '{permission:s}' not found",
"permission_update_failed": "Could not update permission '{permission}' : {error}",
"permission_updated": "Permission '{permission:s}' updated",
"permission_update_nothing_to_do": "No permissions to update",
"port_already_closed": "Port {port:d} is already closed for {ip_version:s} connections",
"port_already_opened": "Port {port:d} is already opened for {ip_version:s} connections",
"port_available": "Port {port:d} is available",
"port_unavailable": "Port {port:d} is not available",
"recommend_to_add_first_user": "The post-install is finished, but YunoHost needs at least one user to work correctly, you should add one using 'yunohost user create <username>' or 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_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.",
@ -516,7 +510,6 @@
"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_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_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`.",
@ -545,16 +538,15 @@
"upnp_disabled": "UPnP turned off",
"upnp_enabled": "UPnP turned on",
"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_creation_failed": "Could not create user",
"user_creation_failed": "Could not create user {user}: {error}",
"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_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_update_failed": "Could not change user info",
"user_update_failed": "Could not update user {user}: {error}",
"user_updated": "User info changed",
"users_available": "Available users:",
"yunohost_already_installed": "YunoHost is already installed",

View file

@ -1,43 +1,557 @@
{
"admin_password_change_failed": "Malebla ŝanĝi pasvorton",
"admin_password_changed": "Pasvorto de la estro estas ŝanĝita",
"admin_password_change_failed": "Ne eblas ŝanĝi pasvorton",
"admin_password_changed": "La pasvorto de administrado ŝanĝiĝis",
"app_already_installed": "{app:s} estas jam instalita",
"app_already_up_to_date": "{app:s} estas ĝisdata",
"app_already_up_to_date": "{app:s} estas jam ĝisdata",
"app_argument_required": "Parametro {name:s} estas bezonata",
"app_change_url_identical_domains": "Malnovaj kaj novaj domajno/URL estas la sama ('{domain:s}{path:s}'), nenio fareblas.",
"app_change_url_success": "URL de appo {app:s} ŝanĝita al {domain:s}{path:s}",
"app_extraction_failed": "Malebla malkompaktigi instaldosierojn",
"app_id_invalid": "Nevalida apo id",
"app_change_url_success": "{app:s} URL nun estas {domain:s} {path:s}",
"app_extraction_failed": "Ne povis ĉerpi la instalajn dosierojn",
"app_id_invalid": "Nevalida apo ID",
"app_incompatible": "Apo {app} ne estas kongrua kun via YunoHost versio",
"app_install_files_invalid": "Nevalidaj instaldosieroj",
"app_location_already_used": "Apo {app} jam estas instalita al tiu loco ({path})",
"user_updated": "Uzanto estas ĝisdatita",
"app_install_files_invalid": "Ĉi tiuj dosieroj ne povas esti instalitaj",
"app_location_already_used": "La app '{app}' jam estas instalita en ({path})",
"user_updated": "Uzantinformoj ŝanĝis",
"users_available": "Uzantoj disponeblaj :",
"yunohost_already_installed": "YunoHost estas jam instalita",
"yunohost_ca_creation_failed": "Ne eblas krei atestan aŭtoritaton",
"yunohost_ca_creation_success": "Loka atesta aŭtoritato estas kreita.",
"yunohost_ca_creation_failed": "Ne povis krei atestan aŭtoritaton",
"yunohost_ca_creation_success": "Loka atestila aŭtoritato kreiĝis.",
"yunohost_installing": "Instalante YunoHost…",
"service_description_glances": "monitoras sisteminformojn de via servilo",
"service_description_metronome": "mastrumas XMPP tujmesaĝilon kontojn",
"service_description_mysql": "stokas aplikaĵojn datojn (SQL datumbazo)",
"service_description_nginx": "servas aŭ permesas atingi ĉiujn retejojn gastigita sur via servilo",
"service_description_nslcd": "mastrumas Yunohost uzantojn konektojn per komanda linio",
"service_description_php7.0-fpm": "rulas aplikaĵojn skibita en PHP kun nginx",
"service_description_postfix": "uzita por sendi kaj ricevi retpoŝtojn",
"service_description_redis-server": "specialita datumbazo uzita por rapida datumo atingo, atendovicoj kaj komunikadoj inter programoj",
"service_description_rmilter": "kontrolas diversajn parametrojn en retpoŝtoj",
"service_description_rspamd": "filtras trudmesaĝojn, kaj aliaj funkcioj rilate al retpoŝto",
"service_description_slapd": "stokas uzantojn, domajnojn kaj rilatajn informojn",
"service_description_ssh": "permesas al vi konekti al via servilo kun fora terminalo (SSH protokolo)",
"service_description_yunohost-api": "mastrumas interagojn inter la YunoHost retinterfaco kaj la sistemo",
"service_description_yunohost-firewall": "mastrumas malfermitajn kaj fermitajn konektejojn al servoj",
"service_disable_failed": "Neebla malaktivigi servon '{service:s}'\n\nFreŝaj protokoloj de la servo : {logs:s}",
"service_disabled": "Servo '{service:s}' estas malaktivigita",
"service_description_glances": "Monitoras sistemajn informojn en via servilo",
"service_description_metronome": "Mastrumas XMPP tujmesaĝilon kontojn",
"service_description_mysql": "Stokas aplikaĵojn datojn (SQL datumbazo)",
"service_description_nginx": "Servas aŭ permesas atingi ĉiujn retejojn gastigita sur via servilo",
"service_description_nslcd": "Mastrumas Yunohost uzantojn konektojn per komanda linio",
"service_description_php7.0-fpm": "Rulas aplikaĵojn skibita en PHP kun nginx",
"service_description_postfix": "Uzita por sendi kaj ricevi retpoŝtojn",
"service_description_redis-server": "Specialita datumbazo uzita por rapida datumo atingo, atendovicoj kaj komunikadoj inter programoj",
"service_description_rmilter": "Kontrolas diversajn parametrojn en retpoŝtoj",
"service_description_rspamd": "Filtras trudmesaĝojn, kaj aliaj funkcioj rilate al retpoŝto",
"service_description_slapd": "Stokas uzantojn, domajnojn kaj rilatajn informojn",
"service_description_ssh": "Permesas al vi konekti al via servilo kun fora terminalo (SSH protokolo)",
"service_description_yunohost-api": "Mastrumas interagojn inter la YunoHost retinterfaco kaj la sistemo",
"service_description_yunohost-firewall": "Mastrumas malfermitajn kaj fermitajn konektejojn al servoj",
"service_disable_failed": "Ne povis malŝalti la servon '{service:s}'\n\nFreŝaj protokoloj de la servo : {logs:s}",
"service_disabled": "'{service: s}' servo malŝaltita",
"action_invalid": "Nevalida ago « {action:s} »",
"admin_password": "Pasvorto de la estro",
"admin_password_too_long": "Bonvolu elekti pasvorton pli mallonga ol 127 signoj",
"already_up_to_date": "Neniu estas farenda! Ĉiu jam estas ĝisdata!",
"app_argument_choice_invalid": "Nevalida elekto por argumento « {name:s} », ĝi devas esti unu el {choices:s}",
"app_argument_invalid": "Nevalida valoro por argumento « {name:s} » : {error:s}",
"app_change_url_failed_nginx_reload": "Reŝargi nginx malsuksesis. Jen la eligo de « nginx -t » :\n{nginx_errors:s}"
"already_up_to_date": "Nenio por fari. Ĉio estas jam ĝisdatigita.",
"app_argument_choice_invalid": "Uzu unu el ĉi tiuj elektoj '{choices:s}' por la argumento '{name:s}'",
"app_argument_invalid": "Elektu validan valoron por la argumento '{name:s}': {error:s}",
"app_change_url_failed_nginx_reload": "Ne eblis reŝarĝi NGINX. Jen la eligo de 'nginx -t':\n{nginx_errors:s}",
"appslist_url_already_tracked": "Estas jam registrita aplika listo kun la URL {url:s}.",
"ask_new_admin_password": "Nova administrada pasvorto",
"app_action_broke_system": "Ĉi tiu ago ŝajne rompis ĉi tiujn gravajn servojn: {services}",
"app_unsupported_remote_type": "Malkontrolita fora speco uzita por la apliko",
"backup_archive_system_part_not_available": "Sistemo parto '{part:s}' ne haveblas en ĉi tiu rezervo",
"apps_permission_not_found": "Neniu permeso trovita por la instalitaj programoj",
"apps_permission_restoration_failed": "Donu la rajtigan permeson '{permission:s}' por restarigi {app:s}",
"backup_abstract_method": "Ĉi tiu rezerva metodo ankoraŭ efektiviĝis",
"apps_already_up_to_date": "Ĉiuj aplikoj estas jam ĝisdatigitaj",
"backup_borg_not_implemented": "La kopia metodo de Borg ankoraŭ ne estas efektivigita",
"app_upgrade_stopped": "La ĝisdatigo de ĉiuj aplikoj estis ĉesigita por eviti eblajn damaĝojn ĉar la antaŭa apliko ne sukcesis ĝisdatigi",
"app_location_unavailable": "Ĉi tiu URL aŭ ne haveblas, aŭ konfliktas kun la jam instalita (j) apliko (j):\n{apps:s}",
"backup_archive_app_not_found": "Ne povis trovi la programon '{app:s}' en la rezerva ar archiveivo",
"backup_actually_backuping": "Krei rezervan ar archiveivon el la kolektitaj dosieroj …",
"backup_method_borg_finished": "Sekurkopio en Borg finiĝis",
"appslist_removed": "{appslist:s} aplika listo forigita",
"app_change_url_no_script": "Ĉi tiu apliko '{app_name:s}' ankoraŭ ne subtenas URL-modifon. Eble vi devus altgradigi ĝin.",
"app_start_install": "Instalanta aplikon {app} …",
"backup_created": "Sekurkopio kreita",
"app_make_default_location_already_used": "Ne povas igi la aplikon '{app}' defaŭlta sur la domajno, {domain} jam uziĝas de la alia app '{other_app}'",
"backup_method_copy_finished": "Rezerva kopio finis",
"app_not_properly_removed": "{app:s} ne estis ĝuste forigita",
"backup_archive_broken_link": "Ne povis aliri la rezervan ar archiveivon (rompita ligilo al {path:s})",
"app_requirements_checking": "Kontrolante postulatajn pakaĵojn por {app} …",
"app_not_installed": "Ne povis trovi la aplikon '{app:s}' en la listo de instalitaj programoj: {all_apps}",
"app_location_install_failed": "Ne eblas instali la aplikon tie ĉar ĝi konfliktas kun la '{other_app}' jam instalita en '{other_path}'",
"ask_new_path": "Nova vojo",
"backup_custom_mount_error": "Propra rezerva metodo ne povis preterpasi la paŝon 'monto'",
"app_upgrade_app_name": "Nun ĝisdatiganta {app} …",
"app_manifest_invalid": "Io misas pri la aplika manifesto: {error}",
"backup_cleaning_failed": "Ne povis purigi la provizoran rezervan dosierujon",
"backup_invalid_archive": "Ĉi tio ne estas rezerva ar archiveivo",
"ask_current_admin_password": "Pasvorto pri aktuala administrado",
"backup_creation_failed": "Ne povis krei la rezervan ar archiveivon",
"backup_hook_unknown": "La rezerva hoko '{hoko:s}' estas nekonata",
"backup_custom_backup_error": "Propra rezerva metodo ne povis preterpasi la paŝon \"sekurkopio\"",
"ask_main_domain": "Ĉefa domajno",
"backup_method_tar_finished": "TAR-rezerva ar archiveivo kreita",
"appslist_unknown": "Aplika listo {appslist:s} nekonata.",
"ask_list_to_remove": "Listo por forigi",
"backup_cant_mount_uncompress_archive": "Ne povis munti la nekompresitan ar archiveivon kiel protektita kontraŭ skribo",
"appslist_retrieve_bad_format": "Ne povis legi la elprenitan liston {appslist:s}",
"appslist_corrupted_json": "Ne povis ŝarĝi la aplikajn listojn. Ĝi aspektas kiel {filename:s} estas damaĝita.",
"app_action_cannot_be_ran_because_required_services_down": "Ĉi tiu app postulas iujn servojn, kiuj nuntempe malleviĝas. Antaŭ ol daŭrigi, vi provu rekomenci la jenajn servojn (kaj eventuale esploru kial ili malsukcesas): {services}",
"backup_copying_to_organize_the_archive": "Kopiante {size:s} MB por organizi la ar archiveivon",
"backup_output_directory_forbidden": "Elektu malsaman elirejan dosierujon. Sekurkopioj ne povas esti kreitaj en sub-dosierujoj / bin, / boot, / dev, / ktp, / lib, / root, / run, / sbin, / sys, / usr, / var aŭ /home/yunohost.backup/archives",
"appslist_could_not_migrate": "Ne povis migri la liston de aplikoj {appslist:s}! Ne eblis analizi la URL ... La malnova cron-laboro konserviĝis en {bkp_file:s}.",
"app_requirements_failed": "Certaines exigences ne sont pas remplies pour {app}: {error}",
"backup_no_uncompress_archive_dir": "Ne ekzistas tia nekompremita arkiva dosierujo",
"password_too_simple_1": "Pasvorto devas esti almenaŭ 8 signojn longa",
"app_upgrade_failed": "Ne povis ĝisdatigi {app:s}",
"app_upgrade_several_apps": "La sekvaj apliko estos altgradigitaj: {apps}",
"backup_archive_open_failed": "Ne povis malfermi la rezervan ar archiveivon",
"ask_lastname": "Familia nomo",
"app_start_backup": "Kolekti dosierojn por esti subtenata por {app} …",
"backup_archive_name_exists": "Rezerva arkivo kun ĉi tiu nomo jam ekzistas.",
"backup_applying_method_tar": "Krei la rezervan TAR-ar archiveivon …",
"backup_method_custom_finished": "Propra rezerva metodo '{metodo:s}' finiĝis",
"appslist_retrieve_error": "Ne eblas retrovi la forajn aplikajn listojn {appslist:s}: {eraro:s}",
"app_already_installed_cant_change_url": "Ĉi tiu app estas jam instalita. La URL ne povas esti ŝanĝita nur per ĉi tiu funkcio. Rigardu \"app changeurl\" se ĝi haveblas.",
"app_not_correctly_installed": "{app:s} ŝajnas esti malĝuste instalita",
"app_removed": "{app:s} forigita",
"backup_delete_error": "Ne povis forigi '{path: s}'",
"app_package_need_update": "La pakaĵo {app} devas esti ĝisdatigita por sekvi YunoHost-ŝanĝojn",
"backup_nothings_done": "Nenio por ŝpari",
"backup_applying_method_custom": "Nomante la kutiman rezervan metodon '{metodo:s}' …",
"appslist_fetched": "Ĝisdatigita aplika listo {appslist:s}",
"backup_app_failed": "Ne eblis rezervi la programon '{app:s}'",
"app_upgrade_some_app_failed": "Iuj aplikoj ne povis esti altgradigitaj",
"app_start_remove": "Forigo de apliko {app} …",
"backup_output_directory_not_empty": "Vi devas elekti malplenan eligitan dosierujon",
"backup_archive_writing_error": "Ne povis aldoni la dosierojn '{source:s}' (nomitaj en la ar theivo '{dest:s}') por esti rezervitaj en la kunpremita arkivo '{archive:s}'",
"ask_email": "Retpoŝta adreso",
"app_start_restore": "Restarigi aplikon {app} …",
"backup_applying_method_copy": "Kopiante ĉiujn dosierojn al sekurkopio …",
"backup_couldnt_bind": "Ne povis ligi {src:s} al {dest:s}.",
"ask_password": "Pasvorto",
"app_requirements_unmeet": "Postuloj ne estas renkontitaj por {app}, la pakaĵo {pkgname} ({version}) devas esti {spec}",
"ask_firstname": "Antaŭnomo",
"backup_ask_for_copying_if_needed": "Iuj dosieroj ne povus esti pretigitaj por sekurkopio uzante la metodon, kiu evitas portempe malŝpari spacon en la sistemo. Por plenumi la sekurkopion, {size:s} MB estos provizore. Ĉu vi konsentas?",
"backup_mount_archive_for_restore": "Preparante arkivon por restarigo …",
"appslist_migrating": "Migra aplika listo {appslist:s} …",
"backup_csv_creation_failed": "Ne povis krei la CSV-dosieron bezonatan por restarigo",
"backup_archive_name_unknown": "Nekonata loka rezerva ar archiveivo nomata '{name:s}'",
"backup_applying_method_borg": "Sendado de ĉiuj dosieroj al sekurkopio en borg-rezerva deponejo …",
"app_sources_fetch_failed": "Ne povis akiri fontajn dosierojn, ĉu la URL estas ĝusta?",
"appslist_name_already_tracked": "Registrita aplika listo kun nomo {name:s} jam ekzistas.",
"ask_new_domain": "Nova domajno",
"app_unknown": "Nekonata apliko",
"app_not_upgraded": "La aplikaĵo '{failed_app}' ne ĝisdatigis, kaj pro tio la sekvaj ĝisdatigoj de aplikoj estis nuligitaj: {apps}",
"aborting": "Aborti.",
"ask_path": "Pado",
"app_upgraded": "{app:s} altgradigita",
"backup_deleted": "Rezerva forigita",
"backup_csv_addition_failed": "Ne povis aldoni dosierojn al sekurkopio en la CSV-dosiero",
"dpkg_lock_not_available": "Ĉi tiu komando ne povas funkcii nun ĉar alia programo uzas la seruron de dpkg (la administrilo de paka sistemo)",
"migration_0003_yunohost_upgrade": "Komenci la ĝisdatigon de YunoHost-pako ... La migrado finiĝos, sed la efektiva ĝisdatigo okazos tuj poste. Post kiam la operacio finiĝos, vi eble devos ensaluti denove sur la retpaĝo.",
"domain_dyndns_root_unknown": "Nekonata radika domajno DynDNS",
"field_invalid": "Nevalida kampo '{:s}'",
"log_app_makedefault": "Faru '{}' la defaŭlta apliko",
"migration_0003_still_on_jessie_after_main_upgrade": "Io okazis malbone dum la ĉefa ĝisdatigo: Ĉu la sistemo ankoraŭ estas en Jessie‽ Por esplori la aferon, bonvolu rigardi {log}:s …",
"migration_0011_can_not_backup_before_migration": "La sekurkopio de la sistemo antaŭ la migrado malsukcesis. Migrado malsukcesis. Eraro: {error:s}",
"migration_0011_create_group": "Krei grupon por ĉiu uzanto…",
"backup_system_part_failed": "Ne eblis sekurkopi la sistemon de '{part:s}'",
"global_settings_setting_security_postfix_compatibility": "Kongruo vs sekureca kompromiso por la Postfix-servilo. Afektas la ĉifradojn (kaj aliajn aspektojn pri sekureco)",
"group_unknown": "La grupo '{group:s}' estas nekonata",
"mailbox_disabled": "Retpoŝto malŝaltita por uzanto {user:s}",
"migration_description_0011_setup_group_permission": "Agordu uzantogrupon kaj starigu permeson por programoj kaj servoj",
"migration_0011_backup_before_migration": "Krei sekurkopion de LDAP-datumbazo kaj agordojn antaŭ la efektiva migrado.",
"migration_0011_LDAP_config_dirty": "Similas ke vi agordis vian LDAP-agordon. Por ĉi tiu migrado la LDAP-agordo bezonas esti ĝisdatigita.\nVi devas konservi vian aktualan agordon, reintaligi la originalan agordon per funkciado de \"yunohost iloj regen-conf -f\" kaj reprovi la migradon",
"migration_0011_migrate_permission": "Migrado de permesoj de agordoj al aplikoj al LDAP…",
"migration_0011_migration_failed_trying_to_rollback": "Migrado malsukcesis ... provante reverti la sistemon.",
"migrations_dependencies_not_satisfied": "Ne eblas kuri migradon {id} ĉar unue vi devas ruli ĉi tiujn migradojn: {dependencies_id}",
"migrations_failed_to_load_migration": "Ne povis ŝarĝi migradon {id}: {error}",
"migrations_exclusive_options": "'--auto', '--skip' kaj '--force-rerun' estas reciproke ekskluzivaj ebloj.",
"migrations_must_provide_explicit_targets": "Vi devas provizi eksplicitajn celojn kiam vi uzas '--skip' aŭ '--force-rerun'",
"permission_update_failed": "Ne povis ĝisdatigi permeson '{permission}': {error}",
"permission_updated": "Ĝisdatigita \"{permission:s}\" rajtigita",
"permission_update_nothing_to_do": "Neniuj permesoj ĝisdatigi",
"tools_upgrade_cant_hold_critical_packages": "Ne povis teni kritikajn pakojn…",
"upnp_dev_not_found": "Neniu UPnP-aparato trovita",
"migration_description_0012_postgresql_password_to_md5_authentication": "Devigu PostgreSQL-aŭtentigon uzi MD5 por lokaj ligoj",
"migration_0011_done": "Migrado sukcesis. Vi nun kapablas administri uzantajn grupojn.",
"migration_0011_LDAP_update_failed": "Ne povis ĝisdatigi LDAP. Eraro: {error:s}",
"pattern_password": "Devas esti almenaŭ 3 signoj longaj",
"root_password_desynchronized": "La pasvorta administranto estis ŝanĝita, sed YunoHost ne povis propagandi ĉi tion al la radika pasvorto!",
"service_remove_failed": "Ne povis forigi la servon '{service:s}'",
"migration_0003_fail2ban_upgrade": "Komenci la ĝisdatigon Fail2Ban…",
"backup_permission": "Rezerva permeso por app {app:s}",
"log_user_group_delete": "Forigi grupon '{}'",
"log_user_group_update": "Ĝisdatigi grupon '{}'",
"migration_0005_postgresql_94_not_installed": "PostgreSQL ne estis instalita en via sistemo. Nenio por fari.",
"dyndns_provider_unreachable": "Ne povas atingi Dyndns-provizanton {provider}: ĉu via YunoHost ne estas ĝuste konektita al la interreto aŭ la dynette-servilo malŝaltiĝas.",
"good_practices_about_user_password": "Vi nun estas por difini novan uzantan pasvorton. La pasvorto devas esti almenaŭ 8 signoj - kvankam estas bone praktiki uzi pli longan pasvorton (t.e. pasfrazon) kaj / aŭ variaĵon de signoj (majuskloj, minuskloj, ciferoj kaj specialaj signoj).",
"group_updated": "Ĝisdatigita \"{group}\" grupo",
"group_already_exist": "Grupo {group} jam ekzistas",
"group_already_exist_on_system": "Grupo {group} jam ekzistas en la sistemaj grupoj",
"group_cannot_be_edited": "La grupo {group} ne povas esti redaktita permane.",
"group_cannot_be_deleted": "La grupo {group} ne povas esti forigita permane.",
"group_update_failed": "Ne povis ĝisdatigi la grupon '{group}': {error}",
"group_user_already_in_group": "Uzanto {user} jam estas en grupo {group}",
"group_user_not_in_group": "Uzanto {user} ne estas en grupo {group}",
"installation_complete": "Kompleta instalado",
"log_category_404": "La loga kategorio '{category}' ne ekzistas",
"log_permission_create": "Krei permeson '{}'",
"log_permission_delete": "Forigi permeson '{}'",
"log_permission_urls": "Ĝisdatigu URLojn rilatajn al permeso '{}'",
"log_user_group_create": "Krei grupon '{}'",
"log_user_permission_update": "Mise à jour des accès pour la permission '{}'",
"log_user_permission_reset": "Restarigi permeson '{}'",
"mail_forward_remove_failed": "Ne povis forigi retpoŝton plusendante '{mail:s}'",
"migration_0011_rollback_success": "Sistemo ruliĝis reen.",
"migration_0011_update_LDAP_database": "Ĝisdatigante LDAP-datumbazon…",
"migration_0011_update_LDAP_schema": "Ĝisdatigante LDAP-skemon…",
"migration_0011_failed_to_remove_stale_object": "Malsukcesis forigi neokazan objekton {dn}: {error}",
"migrations_already_ran": "Tiuj migradoj estas jam faritaj: {ids}",
"migrations_no_such_migration": "Estas neniu migrado nomata {id}",
"permission_already_allowed": "Grupo '{group}' jam havas permeson '{permission}' ebligita'",
"permission_already_disallowed": "Grupo '{group}' jam havas permeson '{permission}' malebligita'",
"permission_cannot_remove_main": "Forigo de ĉefa permeso ne rajtas",
"permission_creation_failed": "Ne povis krei permeson '{permission}': {error}",
"tools_update_failed_to_app_fetchlist": "Ne povis ĝisdatigi la aparatojn de YunoHost ĉar: {error}",
"user_already_exists": "Uzanto {uzanto} jam ekzistas",
"migrations_pending_cant_rerun": "Tiuj migradoj ankoraŭ estas pritraktataj, do ne plu rajtas esti ekzekutitaj: {ids}",
"migrations_running_forward": "Kuranta migrado {id}…",
"migrations_success_forward": "Migrado {id} kompletigita",
"operation_interrupted": "La operacio estis permane interrompita?",
"permission_created": "Permesita '{permission:s}' kreita",
"permission_deleted": "Permesita \"{permission:s}\" forigita",
"permission_deletion_failed": "Ne povis forigi permeson '{permission}': {error}",
"permission_not_found": "Permesita \"{permission:s}\" ne trovita",
"restore_not_enough_disk_space": "Ne sufiĉa spaco (spaco: {free_space:d} B, necesa spaco: {needed_space:d} B, sekureca marĝeno: {margin:d} B)",
"tools_upgrade_regular_packages": "Nun ĝisdatigi 'regulajn' (ne-yunohost-rilatajn) pakojn …",
"tools_upgrade_special_packages_explanation": "Ĉi tiu ago finiĝos, sed la fakta speciala ĝisdatigo daŭros en fono. Bonvolu ne komenci iun alian agon en via servilo en la sekvaj ~ 10 minutoj (depende de via aparata rapideco). Unufoje mi plenumis, vi eble devos ensaluti en la retpaĝo. La ĝisdatiga registro estos havebla en Iloj → Madero (sur la retpaĝo) aŭ tra 'yunohost-registro-listo' (el la komandlinio).",
"unrestore_app": "App '{app:s}' ne restarigos",
"group_created": "Grupo '{group}' kreita",
"group_creation_failed": "Ne povis krei la grupon '{group}': {error}",
"group_deleted": "Grupo '{group}' forigita",
"group_deletion_failed": "Ne povis forigi la grupon '{group}': {error}",
"migrations_not_pending_cant_skip": "Tiuj migradoj ankoraŭ ne estas pritraktataj, do ne eblas preterlasi: {ids}",
"permission_already_exist": "Permesita '{permission}' jam ekzistas",
"domain_created": "Domajno kreita",
"migrate_tsig_wait_2": "2 minutoj …",
"log_user_create": "Aldonu uzanton '{}'",
"ip6tables_unavailable": "Vi ne povas ludi kun ip6tabloj ĉi tie. Vi estas en ujo aŭ via kerno ne subtenas ĝin",
"mail_unavailable": "Ĉi tiu retpoŝta adreso estas rezervita kaj aŭtomate estos atribuita al la unua uzanto",
"certmanager_domain_dns_ip_differs_from_public_ip": "La DNS 'A' rekordo por la domajno '{domain:s}' diferencas de ĉi tiu IP-servilo. Se vi lastatempe modifis vian A-registron, bonvolu atendi ĝin propagandi (iuj DNS-disvastigaj kontroliloj estas disponeblaj interrete). (Se vi scias, kion vi faras, uzu '--no-checks' por malŝalti tiujn ĉekojn.)",
"tools_upgrade_special_packages_completed": "Plenumis la ĝisdatigon de pakaĵoj de YunoHost.\nPremu [Enter] por retrovi la komandlinion",
"log_remove_on_failed_install": "Forigu '{}' post malsukcesa instalado",
"regenconf_file_manually_modified": "La agorddosiero '{conf}' estis modifita permane kaj ne estos ĝisdatigita",
"regenconf_would_be_updated": "La agordo estus aktualigita por la kategorio '{category}'",
"certmanager_cert_install_success_selfsigned": "Mem-subskribita atestilo nun instalita por la domajno '{domain:s}'",
"global_settings_unknown_setting_from_settings_file": "Nekonata ŝlosilo en agordoj: '{setting_key:s}', forĵetu ĝin kaj konservu ĝin en /etc/yunohost/settings-unknown.json",
"regenconf_file_backed_up": "Agordodosiero '{conf}' estis rezervita al '{backup}'",
"migration_0007_cannot_restart": "SSH ne rekomencas post provi nuligi la migradan numeron 6.",
"migration_description_0006_sync_admin_and_root_passwords": "Sinkronigu admin kaj radikajn pasvortojn",
"updating_app_lists": "Akirante haveblajn ĝisdatigojn por aplikoj…",
"iptables_unavailable": "Vi ne povas ludi kun iptables ĉi tie. Vi estas en ujo aŭ via kerno ne subtenas ĝin",
"global_settings_cant_write_settings": "Ne eblis konservi agordojn, tial: {reason:s}",
"service_added": "La servo '{service:s}' aldonis",
"upnp_disabled": "UPnP malŝaltis",
"service_started": "Servo '{service:s}' komenciĝis",
"port_already_opened": "Haveno {port:d} estas jam malfermita por {ip_version:s} rilatoj",
"installation_failed": "Io okazis malbone kun la instalado",
"network_check_mx_ko": "DNS MX-rekordo ne estas agordita",
"migrate_tsig_wait_3": "1 minuto …",
"certmanager_conflicting_nginx_file": "Ne povis prepari domajnon por ACME-defio: la agordo de NGINX {filepath:s} konfliktas kaj unue devas esti forigita",
"upgrading_packages": "Ĝisdatigi pakojn…",
"custom_app_url_required": "Vi devas provizi URL por altgradigi vian kutimon app {app: s}",
"service_reload_failed": "Ne povis reŝargi la servon '{service:s}'\n\nLastatempaj servaj protokoloj: {logs:s}",
"packages_upgrade_failed": "Ne povis ĝisdatigi ĉiujn pakojn",
"hook_json_return_error": "Ne povis legi revenon de hoko {path:s}. Eraro: {msg:s}. Kruda enhavo: {raw_content}",
"dyndns_cron_removed": "DynDNS cron-laboro forigita",
"dyndns_key_not_found": "DNS-ŝlosilo ne trovita por la domajno",
"custom_appslist_name_required": "Vi devas doni nomon por via kutima app-listo",
"tools_upgrade_regular_packages_failed": "Ne povis ĝisdatigi pakojn: {packages_list}",
"service_start_failed": "Ne povis komenci la servon '{service:s}'\n\nLastatempaj servaj protokoloj: {logs:s}",
"service_reloaded": "Servo '{service:s}' reŝargita",
"system_upgraded": "Sistemo ĝisdatigita",
"domain_deleted": "Domajno forigita",
"certmanager_acme_not_configured_for_domain": "Atestilo por la domajno '{domain:s}' ne ŝajnas esti ĝuste instalita. Bonvolu ekzekuti 'cert-instali' por ĉi tiu regado unue.",
"migration_description_0009_decouple_regenconf_from_services": "Malkonstruu la regen-konf-mekanismon de servoj",
"user_update_failed": "Ne povis ĝisdatigi uzanton {user}: {error}",
"migration_description_0008_ssh_conf_managed_by_yunohost_step2": "Lasu la SSH-agordon estu administrata de YunoHost (paŝo 2, manlibro)",
"restore_confirm_yunohost_installed": "Ĉu vi vere volas restarigi jam instalitan sistemon? [{answers:s}]",
"pattern_positive_number": "Devas esti pozitiva nombro",
"monitor_stats_file_not_found": "Statistika dosiero ne trovita",
"certmanager_error_no_A_record": "Neniu DNS 'A' rekordo trovita por '{domain:s}'. Vi bezonas atentigi vian domajnan nomon al via maŝino por povi instali atestilon Lasu-Ĉifri. (Se vi scias, kion vi faras, uzu '--no-checks' por malŝalti tiujn ĉekojn.)",
"update_apt_cache_failed": "Ne eblis ĝisdatigi la kaŝmemoron de APT (paka administranto de Debian). Jen rubujo de la sources.list-linioj, kiuj povus helpi identigi problemajn liniojn:\n{sourcelist}",
"migrations_no_migrations_to_run": "Neniuj migradoj por funkcii",
"executing_command": "Plenumanta komandon '{command:s}' …",
"diagnosis_no_apps": "Neniu instalita apliko",
"certmanager_attempt_to_renew_nonLE_cert": "La atestilo por la domajno '{domain:s}' ne estas elsendita de Let's Encrypt. Ne eblas renovigi ĝin aŭtomate!",
"global_settings_setting_example_bool": "Ekzemplo bulea elekto",
"domain_dyndns_already_subscribed": "Vi jam abonis DynDNS-domajnon",
"log_letsencrypt_cert_renew": "Renovigu '{}' Ni ĉifru atestilon",
"migrate_tsig_start": "Detektita ŝlosila algoritmo nesufiĉa por TSIG-subskribo de la domajno '{domain}', komencanta migradon al la pli sekura HMAC-SHA-512",
"ldap_init_failed_to_create_admin": "LDAP-iniciato ne povis krei administran uzanton",
"backup_output_directory_required": "Vi devas provizi elirejan dosierujon por la sekurkopio",
"tools_upgrade_cant_unhold_critical_packages": "Ne povis malhelpi kritikajn pakojn…",
"diagnosis_monitor_disk_error": "Ne povis monitori diskojn: {error}",
"log_link_to_log": "Plena ŝtipo de ĉi tiu operacio: '<a href=\"#/tools/logs/{name}\" style=\"text-decoration:underline\"> {desc} </a>'",
"service_no_log": "Neniu registro por montri por servo '{service:s}'",
"global_settings_cant_serialize_settings": "Ne eblis serialigi datumojn pri agordoj, motivo: {reason:s}",
"backup_running_hooks": "Kurado de apogaj hokoj …",
"package_not_installed": "Pako '{pkgname}' ne estas instalita",
"certmanager_domain_unknown": "Nekonata domajno '{domain:s}'",
"unexpected_error": "Io neatendita iris malbone: {error}",
"password_listed": "Ĉi tiu pasvorto estas inter la plej uzataj pasvortoj en la mondo. Bonvolu elekti ion pli unikan.",
"ssowat_persistent_conf_write_error": "Ne povis konservi konstantan SSOwat-agordon: {error:s}. Redakti /etc/ssowat/conf.json.persistent dosiero por ripari la Jaks-sintakson",
"migration_description_0007_ssh_conf_managed_by_yunohost_step1": "Lasu la SSH-agordon estu administrata de YunoHost (paŝo 1, aŭtomata)",
"migration_0009_not_needed": "Ĉi tiu migrado jam iel okazis ... (?) Saltado.",
"ssowat_conf_generated": "SSOwat-agordo generita",
"migrate_tsig_wait": "Atendante tri minutojn por ke la servilo DynDNS enkalkulu la novan ŝlosilon …",
"log_remove_on_failed_restore": "Forigu '{}' post malsukcesa restarigo de rezerva ar archiveivo",
"dpkg_is_broken": "Vi ne povas fari ĉi tion nun ĉar dpkg/APT (la administrantoj pri pakaĵaj sistemoj) ŝajnas esti rompita stato ... Vi povas provi solvi ĉi tiun problemon per konekto per SSH kaj funkcianta `sudo dpkg --configure -a`.",
"recommend_to_add_first_user": "La postinstalo finiĝis, sed YunoHost bezonas almenaŭ unu uzanton por funkcii ĝuste, vi devas aldoni unu uzante 'yunohost user create <username>' aŭ fari ĝin de la administra interfaco.",
"certmanager_cert_signing_failed": "Ne povis subskribi la novan atestilon",
"migration_description_0003_migrate_to_stretch": "Altgradigu la sistemon al Debian Stretch kaj YunoHost 3.0",
"log_tools_upgrade": "Ĝisdatigu sistemajn pakaĵojn",
"network_check_smtp_ko": "Ekstera retpoŝto (SMTP-haveno 25) ŝajnas esti blokita de via reto",
"log_available_on_yunopaste": "Ĉi tiu protokolo nun haveblas per {url}",
"certmanager_http_check_timeout": "Ekdifinita kiam servilo provis kontakti sin per HTTP per publika IP-adreso (domajno '{domain:s}' kun IP '{ip:s}'). Vi eble spertas haŭtoproblemon, aŭ la fajroŝirmilo / enkursigilo antaŭ via servilo miskonfiguras.",
"pattern_port_or_range": "Devas esti valida haveno-nombro (t.e. 0-65535) aŭ gamo da havenoj (t.e. 100:200)",
"migrations_loading_migration": "Ŝarĝante migradon {id}…",
"port_available": "Haveno {port:d} estas havebla",
"pattern_mailbox_quota": "Devas esti grandeco kun la sufikso b/k/M/G/T aŭ 0 por ne havi kvoton",
"migration_0008_general_disclaimer": "Por plibonigi la sekurecon de via servilo, rekomendas lasi YunoHost administri la SSH-agordon. Via nuna SSH-aranĝo diferencas de la rekomendo. Se vi lasas YunoHost agordi ĝin, la maniero per kiu vi konektas al via servilo per SSH ŝanĝiĝos tiel:",
"user_deletion_failed": "Ne povis forigi uzanton {user}: {error}",
"backup_with_no_backup_script_for_app": "La app '{app:s}' ne havas sekretan skripton. Ignorante.",
"service_regen_conf_is_deprecated": "'yunohost service regen-conf' malakceptas! Bonvolu uzi anstataŭe 'yunohost tools regen-conf'.",
"global_settings_key_doesnt_exists": "La ŝlosilo '{settings_key:s}' ne ekzistas en la tutmondaj agordoj, vi povas vidi ĉiujn disponeblajn klavojn per uzado de 'yunohost settings list'",
"dyndns_no_domain_registered": "Neniu domajno registrita ĉe DynDNS",
"dyndns_could_not_check_available": "Ne povis kontroli ĉu {domain:s} haveblas sur {provider:s}.",
"log_app_removelist": "Forigu aplikan liston",
"global_settings_setting_example_enum": "Ekzemplo enum elekto",
"hook_exec_not_terminated": "Skripto ne finiĝis ĝuste: {path:s}",
"service_stopped": "'{service:s}' servo ĉesis",
"restore_failed": "Ne povis restarigi sistemon",
"confirm_app_install_danger": "Danĝero! Ĉi tiu apliko estas konata ankoraŭ eksperimenta (se ne eksplicite ne funkcias)! Vi probable ne devas instali ĝin krom se vi scias kion vi faras. NENIU SUBTENO estos provizita se ĉi tiu app ne funkcias aŭ rompas vian sistemon ... Se vi pretas riski ĉiuokaze, tajpu '{answers: s}'",
"log_operation_unit_unclosed_properly": "Operaciumo ne estis fermita ĝuste",
"upgrade_complete": "Ĝisdatigo kompleta",
"upnp_enabled": "UPnP ŝaltis",
"mailbox_used_space_dovecot_down": "La retpoŝta servo de Dovecot devas funkcii, se vi volas akcepti uzitan poŝtan spacon",
"restore_system_part_failed": "Ne povis restarigi la sisteman parton '{part:s}'",
"diagnosis_monitor_system_error": "Ne povis monitori sistemon: {error}",
"service_stop_failed": "Ne povis maldaŭrigi la servon '{service:s}'\n\nLastatempaj servaj protokoloj: {logs:s}",
"unbackup_app": "App '{app:s}' ne konserviĝos",
"updating_apt_cache": "Akirante haveblajn ĝisdatigojn por sistemaj pakoj…",
"tools_upgrade_at_least_one": "Bonvolu specifi '--apps' aŭ '--system'",
"service_already_stopped": "La servo '{service:s}' jam ĉesis",
"unit_unknown": "Nekonata unuo '{unit:s}'",
"migration_0003_modified_files": "Bonvolu noti, ke la jenaj dosieroj estis trovitaj mane kaj modifitaj kaj povus esti anstataŭigitaj sekve de la ĝisdatigo: {manual_modified_files}",
"tools_upgrade_cant_both": "Ne eblas ĝisdatigi ambaŭ sistemon kaj programojn samtempe",
"restore_extracting": "Eltirante bezonatajn dosierojn el la ar theivo…",
"upnp_port_open_failed": "Ne povis malfermi havenon per UPnP",
"log_app_upgrade": "Ĝisdatigu la aplikon '{}'",
"log_help_to_get_failed_log": "La operacio '{desc}' ne povis finiĝi. Bonvolu dividi la plenan ŝtipon de ĉi tiu operacio per la komando 'yunohost log display {name} --share' por akiri helpon",
"migration_description_0002_migrate_to_tsig_sha256": "Plibonigu sekurecon de DynDNS TSIG-ĝisdatigoj per SHA-512 anstataŭ MD5",
"monitor_disabled": "Servila monitorado nun malŝaltis",
"pattern_port": "Devas esti valida havena numero (t.e. 0-65535)",
"port_already_closed": "Haveno {port:d} estas jam fermita por {ip_version:s} rilatoj",
"hook_name_unknown": "Nekonata hoko-nomo '{name:s}'",
"migration_0003_system_not_fully_up_to_date": "Via sistemo ne estas plene ĝisdata. Bonvolu plenumi regulan ĝisdatigon antaŭ ol ruli la migradon al Stretch.",
"dyndns_could_not_check_provide": "Ne povis kontroli ĉu {provider:s} povas provizi {domain:s}.",
"dyndns_cron_remove_failed": "Ne povis forigi la cron-laboron DynDNS ĉar: {error}",
"pattern_listname": "Devas esti nur alfanumeraj kaj substrekaj signoj",
"restore_nothings_done": "Nenio estis restarigita",
"log_tools_postinstall": "Afiŝu vian servilon YunoHost",
"dyndns_unavailable": "La domajno '{domain:s}' ne haveblas.",
"experimental_feature": "Averto: Ĉi tiu funkcio estas eksperimenta kaj ne konsiderata stabila, vi ne uzu ĝin krom se vi scias kion vi faras.",
"root_password_replaced_by_admin_password": "Via radika pasvorto estis anstataŭigita per via administra pasvorto.",
"ssowat_persistent_conf_read_error": "Ne povis legi konstantan SSOwat-agordon: {error:s}. Redakti /etc/ssowat/conf.json.persistent dosiero por ripari la Jaks-sintakson",
"migration_description_0005_postgresql_9p4_to_9p6": "Migru datumbazojn de PostgreSQL 9.4 al 9.6",
"migration_0008_root": "• Vi ne povos konekti kiel radiko per SSH. Anstataŭe vi uzu la administran uzanton;",
"package_unknown": "Nekonata pako '{pkgname}'",
"domain_unknown": "Nekonata domajno",
"global_settings_setting_security_password_user_strength": "Uzanto pasvorta forto",
"restore_may_be_not_enough_disk_space": "Via sistemo ŝajnas ne havi sufiĉe da spaco (free:{free_space:d} B, necesa spaco: {needed_space:d} B, sekureca marĝeno: {margin:d} B)",
"log_corrupted_md_file": "La YAD-metadata dosiero asociita kun protokoloj estas damaĝita: '{md_file}\nEraro: {error} '",
"downloading": "Elŝutante …",
"user_deleted": "Uzanto forigita",
"service_enable_failed": "Ne eblis ŝalti la servon '{service:s}'\n\nLastatempaj servaj protokoloj: {logs:s}",
"tools_upgrade_special_packages": "Nun ĝisdatigi 'specialajn' (rilatajn al yunohost)…",
"domains_available": "Haveblaj domajnoj:",
"dyndns_registered": "Registrita domajno DynDNS",
"service_description_fail2ban": "Protektas kontraŭ bruta forto kaj aliaj specoj de atakoj de la interreto",
"file_does_not_exist": "La dosiero {path:s} ne ekzistas.",
"yunohost_not_installed": "YunoHost estas malĝuste aŭ ne ĝuste instalita. Bonvolu prilabori 'yunohost tools postinstall'",
"migration_0005_postgresql_96_not_installed": "PostgreSQL 9.4 estas instalita, sed ne postgresql 9.6‽ Io stranga eble okazis en via sistemo: (…",
"restore_removing_tmp_dir_failed": "Ne povis forigi malnovan provizoran dosierujon",
"certmanager_cannot_read_cert": "Io malbona okazis, kiam mi provis malfermi aktualan atestilon por domajno {domain:s} (dosiero: {file:s}), kialo: {reason:s}",
"service_removed": "'{service:s}' servo forigita",
"certmanager_hit_rate_limit": "Tro multaj atestiloj jam eldonitaj por ĉi tiu ĝusta aro de domajnoj {domain:s} antaŭ nelonge. Bonvolu reprovi poste. Vidu https://letsencrypt.org/docs/rate-limits/ por pliaj detaloj",
"migration_0005_not_enough_space": "Disponigu sufiĉan spacon en {path} por ruli la migradon.",
"pattern_firstname": "Devas esti valida antaŭnomo",
"migration_description_0010_migrate_to_apps_json": "Forigu malvalorigitajn aparatojn kaj uzu anstataŭe la novan unuigitan liston \"apps.json\"",
"domain_cert_gen_failed": "Ne povis generi atestilon",
"regenconf_file_kept_back": "La agorda dosiero '{conf}' estas atendita forigi per regen-conf (kategorio {category}), sed ĝi estis konservita.",
"migrate_tsig_wait_4": "30 sekundoj …",
"backup_with_no_restore_script_for_app": "La apliko \"{app:s}\" ne havas restarigan skripton, vi ne povos aŭtomate restarigi la sekurkopion de ĉi tiu apliko.",
"log_letsencrypt_cert_install": "Instalu atestilon Ni ĉifru sur '{}' regado",
"log_dyndns_update": "Ĝisdatigu la IP asociita kun via subdominio YunoHost '{}'",
"firewall_reload_failed": "Ne eblis reŝargi la firewall",
"confirm_app_install_warning": "Averto: Ĉi tiu aplikaĵo povas funkcii, sed ne bone integras en YunoHost. Iuj funkcioj kiel ekzemple aliĝilo kaj sekurkopio / restarigo eble ne haveblos. Instali ĉiuokaze? [{answers: s}] ",
"log_user_delete": "Forigi uzanton '{}'",
"dyndns_ip_updated": "Ĝisdatigis vian IP sur DynDNS",
"regenconf_up_to_date": "La agordo jam estas ĝisdatigita por kategorio '{category}'",
"migration_0003_patching_sources_list": "Patching the sources.lists …",
"global_settings_setting_security_ssh_compatibility": "Kongruo vs sekureca kompromiso por la SSH-servilo. Afektas la ĉifradojn (kaj aliajn aspektojn pri sekureco)",
"migrations_need_to_accept_disclaimer": "Por funkciigi la migradon {id}, via devas akcepti la sekvan malakcepton:\n---\n{malavantaĝo}\n---\nSe vi akceptas funkcii la migradon, bonvolu rekonduki la komandon kun la opcio '--accept-disclaimer'.",
"regenconf_file_remove_failed": "Ne povis forigi la agordodosieron '{conf}'",
"not_enough_disk_space": "Ne sufiĉe libera spaco sur '{path:s}'",
"migration_0006_disclaimer": "YunoHost nun atendas ke pasvortoj kaj administrantoj estu sinkronigitaj. Per ekzekuto de ĉi tiu migrado, via radika pasvorto estos anstataŭigita per administra pasvorto.",
"dyndns_ip_update_failed": "Ne povis ĝisdatigi IP-adreson al DynDNS",
"migration_description_0004_php5_to_php7_pools": "Rekonfigu la PHP-naĝejojn por uzi PHP 7 anstataŭ 5",
"monitor_glances_con_failed": "Ne povis konektiĝi al servilo de Glances",
"ssowat_conf_updated": "SSOwat-agordo ĝisdatigita",
"log_link_to_failed_log": "Ne povis plenumi la operacion '{desc}'. Bonvolu provizi la plenan protokolon de ĉi tiu operacio per <a href=\"#/tools/logs/{name}\"> alklakante ĉi tie </a> por akiri helpon",
"log_app_fetchlist": "Aldonu liston de aplikoj",
"user_home_creation_failed": "Ne povis krei dosierujon \"home\" por uzanto",
"pattern_backup_archive_name": "Devas esti valida dosiernomo kun maksimume 30 signoj, alfanombraj kaj -_. signoj nur",
"restore_cleaning_failed": "Ne eblis purigi la adresaron de provizora restarigo",
"dyndns_registration_failed": "Ne povis registri DynDNS-domajnon: {error:s}",
"migration_0003_not_jessie": "La nuna Debian-distribuo ne estas Jessie!",
"user_unknown": "Nekonata uzanto: {user:s}",
"migrations_to_be_ran_manually": "Migrado {id} devas funkcii permane. Bonvolu iri al Iloj → Migradoj en la retpaĝa paĝo, aŭ kuri `yunohost tools migrations migrate`.",
"migration_0008_warning": "Se vi komprenas tiujn avertojn kaj konsentas lasi YunoHost pretervidi vian nunan agordon, faru la migradon. Alie, vi ankaŭ povas salti la migradon - kvankam ĝi ne rekomendas.",
"certmanager_cert_renew_success": "Ni Ĉifru atestilon renovigitan por la domajno '{domain:s}'",
"global_settings_reset_success": "Antaŭaj agordoj nun estas rezervitaj al {path:s}",
"pattern_domain": "Devas esti valida domajna nomo (t.e. mia-domino.org)",
"package_unexpected_error": "Neatendita eraro okazis prilaborante la pakon '{pkgname}'",
"dyndns_key_generating": "Generi DNS-ŝlosilon ... Eble daŭros iom da tempo.",
"restore_running_app_script": "Restarigi la programon '{app:s}'…",
"migrations_skip_migration": "Salti migradon {id}…",
"mysql_db_init_failed": "MysQL-datumbazo init malsukcesis",
"regenconf_file_removed": "Agordodosiero '{conf}' forigita",
"log_tools_shutdown": "Enŝaltu vian servilon",
"password_too_simple_3": "La pasvorto bezonas almenaŭ 8 signojn kaj enhavas ciferon, majusklon, pli malaltan kaj specialajn signojn",
"migration_0003_general_warning": "Bonvolu noti, ke ĉi tiu migrado estas delikata operacio. La teamo de YunoHost faris sian plej bonan revizii kaj testi ĝin, sed la migrado eble ankoraŭ rompos partojn de la sistemo aŭ ĝiaj programoj.\n\nTial oni rekomendas al:\n - Elfari kopion de iuj kritikaj datumoj aŭ app. Pliaj informoj pri https://yunohost.org/backup;\n - Paciencu post lanĉo de la migrado: Depende de via interreta konekto kaj aparataro, eble daŭros kelkaj horoj ĝis ĉio ĝisdatigi.\n\nAldone, la haveno por SMTP, uzata de eksteraj retpoŝtaj klientoj (kiel Thunderbird aŭ K9-Mail) estis ŝanĝita de 465 (SSL / TLS) al 587 (STARTTLS). La malnova haveno (465) aŭtomate fermiĝos, kaj la nova haveno (587) malfermiĝos en la fajrejo. Vi kaj viaj uzantoj * devos adapti la agordon de viaj retpoŝtaj klientoj laŭe.",
"diagnosis_kernel_version_error": "Ne povis akiri la kernan version: {error}",
"global_settings_setting_example_int": "Ekzemple int elekto",
"backup_output_symlink_dir_broken": "Vi havas rompitan simbolon anstataŭ via arkiva dosierujo '{path:s}'. Vi eble havas specifan agordon por sekurkopi viajn datumojn en alia dosiersistemo, ĉi-kaze vi probable forgesis remeti aŭ enŝovi vian malmolan disko aŭ ŝlosilon USB.",
"good_practices_about_admin_password": "Vi nun estas por difini novan administran pasvorton. La pasvorto devas esti almenaŭ 8 signoj - kvankam estas bone praktiki uzi pli longan pasvorton (t.e. pasfrazon) kaj / aŭ uzi variaĵon de signoj (majuskloj, minuskloj, ciferoj kaj specialaj signoj).",
"certmanager_attempt_to_renew_valid_cert": "La atestilo por la domajno '{domain:s}' ne finiĝos! (Vi eble uzos --force se vi scias kion vi faras)",
"restore_running_hooks": "Kurantaj restarigaj hokoj…",
"regenconf_pending_applying": "Aplikante pritraktata agordo por kategorio '{category}'…",
"service_description_dovecot": "Permesas al retpoŝtaj klientoj aliri / serĉi retpoŝton (per IMAP kaj POP3)",
"domain_dns_conf_is_just_a_recommendation": "Ĉi tiu komando montras al vi la *rekomenditan* agordon. Ĝi efektive ne agordas la DNS-agordon por vi. Via respondeco agordi vian DNS-zonon en via registristo laŭ ĉi tiu rekomendo.",
"backup_php5_to_php7_migration_may_fail": "Ne povis konverti vian ar archiveivon por subteni PHP 7, vi eble ne povas restarigi viajn PHP-programojn (kialo: {error:s})",
"log_backup_restore_system": "Restarigi sistemon de rezerva arkivo",
"log_app_change_url": "Ŝanĝu la URL de apliko '{}'",
"service_already_started": "La servo '{service:s}' estas jam komencita",
"license_undefined": "nedifinita",
"global_settings_setting_security_password_admin_strength": "Admin pasvorta forto",
"service_reload_or_restart_failed": "Ne povis reŝargi aŭ rekomenci la servon '{service:s}'\n\nLastatempaj servaj protokoloj: {logs:s}",
"migrations_list_conflict_pending_done": "Vi ne povas uzi ambaŭ '--previous' kaj '--done' samtempe.",
"maindomain_changed": "La ĉefa domajno nun ŝanĝiĝis",
"server_shutdown_confirm": "La servilo haltos tuj, ĉu vi certas? [{answers:s}]",
"monitor_period_invalid": "Nevalida tempoperiodo",
"log_backup_restore_app": "Restarigu '{}' de rezerva ar archiveivo",
"log_does_exists": "Ne estas operacio-registro kun la nomo '{log}', uzu 'yunohost loglist' por vidi ĉiujn disponeblajn operaciojn",
"service_add_failed": "Ne povis aldoni la servon '{service:s}'",
"pattern_password_app": "Bedaŭrinde, pasvortoj ne povas enhavi jenajn signojn: {forbidden_chars}",
"this_action_broke_dpkg": "Ĉi tiu ago rompis dpkg / APT (la administrantoj pri la paka sistemo) ... Vi povas provi solvi ĉi tiun problemon per konekto per SSH kaj funkcianta `sudo dpkg --configure -a`.",
"log_regen_conf": "Regeneri sistemajn agordojn '{}'",
"restore_hook_unavailable": "La restariga skripto por '{part:s}' ne haveblas en via sistemo kaj ankaŭ ne en la ar theivo",
"network_check_smtp_ok": "Eksteren retpoŝto (SMTP-haveno 25) ne estas blokita",
"log_dyndns_subscribe": "Aboni al YunoHost-subdominio '{}'",
"password_too_simple_4": "La pasvorto bezonas almenaŭ 12 signojn kaj enhavas ciferon, majuskle, pli malaltan kaj specialajn signojn",
"migration_0003_main_upgrade": "Komencanta ĉefa ĝisdatigo …",
"user_info_failed": "Ne povis akiri informojn pri uzanto",
"regenconf_file_updated": "Agordodosiero '{conf}' ĝisdatigita",
"log_help_to_get_log": "Por vidi la protokolon de la operacio '{desc}', uzu la komandon 'yunohost log display {name}'",
"global_settings_setting_security_nginx_compatibility": "Kongruo vs sekureca kompromiso por la TTT-servilo NGINX. Afektas la ĉifradojn (kaj aliajn aspektojn pri sekureco)",
"no_internet_connection": "Servilo ne konektita al la interreto",
"migration_0008_dsa": "• La DSA-ŝlosilo estos malŝaltita. Tial vi eble bezonos nuligi spuran averton de via SSH-kliento kaj revizii la fingrospuron de via servilo;",
"migration_0003_restoring_origin_nginx_conf": "Fileia dosiero /etc/nginx/nginx.conf estis iel redaktita. La migrado reaperos unue al sia originala stato ... La antaŭa dosiero estos havebla kiel {backup_dest}.",
"migrate_tsig_end": "Migrado al HMAC-SHA-512 finiĝis",
"restore_complete": "Restarigita",
"certmanager_couldnt_fetch_intermediate_cert": "Ekvilibrigita kiam vi provis ricevi interajn atestilojn de Let's Encrypt. Atestita instalado / renovigo nuligita - bonvolu reprovi poste.",
"hook_exec_failed": "Ne povis funkcii skripto: {path:s}",
"global_settings_cant_open_settings": "Ne eblis malfermi agordojn, tial: {reason:s}",
"user_created": "Uzanto kreita",
"service_description_avahi-daemon": "Permesas al vi atingi vian servilon uzante 'yunohost.local' en via loka reto",
"certmanager_attempt_to_replace_valid_cert": "Vi provas anstataŭigi bonan kaj validan atestilon por domajno {domajno:s}! (Uzu --forte pretervidi)",
"monitor_stats_period_unavailable": "Ne ekzistas disponeblaj statistikoj por la periodo",
"regenconf_updated": "Agordo por kategorio '{category}' ĝisdatigita",
"update_apt_cache_warning": "Io iris malbone dum la ĝisdatigo de la kaŝmemoro de APT (paka administranto de Debian). Jen rubujo de la sources.list-linioj, kiuj povus helpi identigi problemajn liniojn:\n{sourcelist}",
"regenconf_dry_pending_applying": "Kontrolado de pritraktata agordo, kiu estus aplikita por kategorio '{category}'…",
"regenconf_file_copy_failed": "Ne povis kopii la novan agordodosieron '{new}' al '{conf}'",
"global_settings_setting_example_string": "Ekzemple korda elekto",
"restore_already_installed_app": "App kun la ID '{app:s}' estas jam instalita",
"mountpoint_unknown": "Nekonata montpunkto",
"log_tools_maindomain": "Faru de '{}' la ĉefa domajno",
"maindomain_change_failed": "Ne povis ŝanĝi la ĉefan domajnon",
"mail_domain_unknown": "Nekonata retpoŝtadreso por domajno '{domain:s}'",
"migrations_cant_reach_migration_file": "Ne povis aliri migrajn dosierojn ĉe la vojo% s",
"pattern_email": "Devas esti valida retpoŝtadreso (t.e.iu@domain.org)",
"mail_alias_remove_failed": "Ne povis forigi retpoŝton alias '{mail:s}'",
"regenconf_file_manually_removed": "La dosiero de agordo '{conf}' estis forigita permane, kaj ne estos kreita",
"monitor_enabled": "Servila monitorado nun ŝaltis",
"domain_exists": "La domajno jam ekzistas",
"migration_description_0001_change_cert_group_to_sslcert": "Ŝanĝu grupajn permesojn de 'metronomo' al 'ssl-cert'",
"mysql_db_creation_failed": "MySQL-datumbazkreado malsukcesis",
"ldap_initialized": "LDAP inicializis",
"migrate_tsig_not_needed": "Vi ne ŝajnas uzi DynDNS-domajnon, do neniu migrado necesas.",
"certmanager_domain_cert_not_selfsigned": "La atestilo por domajno {domajno:s} ne estas mem-subskribita. Ĉu vi certas, ke vi volas anstataŭigi ĝin? (Uzu '--force' por fari tion.)",
"certmanager_unable_to_parse_self_CA_name": "Ne povis trapasi nomon de mem-subskribinta aŭtoritato (dosiero: {file: s})",
"log_selfsigned_cert_install": "Instalu mem-subskribitan atestilon sur '{}' domajno",
"log_tools_reboot": "Reklamu vian servilon",
"certmanager_cert_install_success": "Ni Ĉifru atestilon nun instalitan por la domajno '{domain:s}'",
"global_settings_bad_choice_for_enum": "Malbona elekto por agordo {setting:s}, ricevita '{choice:s}', sed disponeblaj elektoj estas: {available_choices:s}",
"server_shutdown": "La servilo haltos",
"log_tools_migrations_migrate_forward": "Migri antaŭen",
"migration_0008_no_warning": "Neniu grava risko identigita pri superregado de via SSH-agordo, tamen oni ne povas esti absolute certa;)! Ekfunkciu la migradon por superregi ĝin. Alie, vi ankaŭ povas salti la migradon - kvankam ĝi ne rekomendas.",
"regenconf_now_managed_by_yunohost": "La agorda dosiero '{conf}' nun estas administrata de YunoHost (kategorio {category}).",
"server_reboot_confirm": "Ĉu la servilo rekomencos tuj, ĉu vi certas? [{answers:s}]",
"log_app_install": "Instalu la aplikon '{}'",
"service_description_dnsmasq": "Traktas rezolucion de domajna nomo (DNS)",
"global_settings_unknown_type": "Neatendita situacio, la agordo {setting:s} ŝajnas havi la tipon {unknown_type:s} sed ĝi ne estas tipo subtenata de la sistemo.",
"migration_0003_problematic_apps_warning": "Bonvolu noti, ke la sekvaj eventuale problemaj instalitaj apps estis detektitaj. Ĝi aspektas, ke tiuj ne estis instalitaj de aparato aŭ ne estas markitaj kiel \"funkciantaj\". Tial ne eblas garantii, ke ili ankoraŭ funkcios post la ĝisdatigo: {problematic_apps}",
"domain_hostname_failed": "Ne povis agordi novan gastigilon. Ĉi tio eble kaŭzos problemon poste (eble bone).",
"server_reboot": "La servilo rekomenciĝos",
"regenconf_failed": "Ne povis regeneri la agordon por kategorio(j): {categories}",
"domain_uninstall_app_first": "Unu aŭ pluraj programoj estas instalitaj en ĉi tiu domajno. Bonvolu malinstali ilin antaŭ ol daŭrigi la domajnan forigon",
"port_unavailable": "Haveno {port:d} ne haveblas",
"service_unknown": "Nekonata servo '{service:s}'",
"migration_0003_start": "Komencante migradon al Stretch. La protokoloj haveblos en {logfile}.",
"monitor_stats_no_update": "Neniuj monitoradaj statistikoj ĝisdatigi",
"domain_deletion_failed": "Ne povis forigi domajnon {domain}: {error}",
"log_user_update": "Ĝisdatigu uzantinformojn de '{}'",
"user_creation_failed": "Ne povis krei uzanton {user}: {error}",
"migrations_migration_has_failed": "Migrado {id} ne kompletigis, abolis. Eraro: {exception}",
"done": "Farita",
"log_domain_remove": "Forigi domon '{}' de agordo de sistemo",
"monitor_not_enabled": "Servila monitorado estas malŝaltita",
"diagnosis_debian_version_error": "Ne povis retrovi la Debianan version: {error}",
"hook_list_by_invalid": "Ĉi tiu posedaĵo ne povas esti uzata por listigi hokojn",
"confirm_app_install_thirdparty": "Danĝero! Ĉi tiu apliko ne estas parto de la aplika katalogo de Yunohost. Instali triajn aplikojn povas kompromiti la integrecon kaj sekurecon de via sistemo. Vi probable ne devas instali ĝin krom se vi scias kion vi faras. NENIU SUBTENO estos provizita se ĉi tiu app ne funkcias aŭ rompas vian sistemon ... Se vi pretas riski ĉiuokaze, tajpu '{answers: s}'",
"global_settings_setting_service_ssh_allow_deprecated_dsa_hostkey": "Permesu uzon de (malaktuala) DSA-hostkey por la agordo de daemon SSH",
"dyndns_domain_not_provided": "Provizanto DynDNS {provider:s} ne povas provizi domajnon {domain:s}.",
"backup_unable_to_organize_files": "Ne povis uzi la rapidan metodon por organizi dosierojn en la ar archiveivo",
"password_too_simple_2": "La pasvorto bezonas almenaŭ 8 signojn kaj enhavas ciferon, majusklojn kaj minusklojn",
"executing_script": "Plenumanta skripto '{script:s}' …",
"service_cmd_exec_failed": "Ne povis plenumi la komandon '{command:s}'",
"migration_0007_cancelled": "YunoHost ne plibonigis la administradon de via SSH-konf.",
"migrate_tsig_failed": "Ne povis migri la DynDNS-domajnon '{domain}' al HMAC-SHA-512, ruliĝante. Eraro: {error_code}, {error}",
"pattern_lastname": "Devas esti valida familinomo",
"service_enabled": "'{service:s}' servo malŝaltita",
"certmanager_no_cert_file": "Ne povis legi la atestan dosieron por la domajno {domain:s} (dosiero: {file:s})",
"migration_0008_port": "• Vi devos konekti uzante la havenon 22 anstataŭ via nuna kutimo SSH-haveno. Sentu vin libera reconfiguri ĝin;",
"domain_creation_failed": "Ne povis krei domajnon {domain}: {error}",
"certmanager_domain_http_not_working": "Ŝajnas ke la domajno {domain:s} ne atingeblas per HTTP. Kontrolu, ke via DNS kaj NGINX-agordo ĝustas",
"domain_cannot_remove_main": "Ne eblas forigi ĉefan domajnon. Fiksu unu unue",
"service_reloaded_or_restarted": "'{service:s}' servo reŝarĝis aŭ rekomencis",
"mysql_db_initialized": "La datumbazo MySQL jam estas pravalorizita",
"log_domain_add": "Aldonu '{}' domajnon en sisteman agordon",
"global_settings_bad_type_for_setting": "Malbona tipo por agordo {setting:s}, ricevita {received_type:s}, atendata {expected_type:s}",
"unlimit": "Neniu kvoto",
"dyndns_cron_installed": "Kreita laboro DynDNS cron",
"system_username_exists": "Uzantnomo jam ekzistas en la listo de uzantoj de sistemo",
"firewall_reloaded": "Fajroŝirmilo reŝarĝis",
"service_restarted": "'{service:s}' servo rekomencis",
"pattern_username": "Devas esti minuskulaj literoj kaj minuskloj nur",
"extracting": "Eltirante…",
"restore_app_failed": "Ne povis restarigi la programon '{app:s}'",
"yunohost_configured": "YunoHost nun agordis",
"certmanager_self_ca_conf_file_not_found": "Ne povis trovi agorddosieron por mem-subskriba aŭtoritato (dosiero: {file:s})",
"log_app_remove": "Forigu la aplikon '{}'",
"service_restart_failed": "Ne povis rekomenci la servon '{service:s}'\n\nLastatempaj servaj protokoloj: {logs:s}",
"firewall_rules_cmd_failed": "Iuj komandoj pri fajroŝirmilo malsukcesis. Pliaj informoj en ensaluto.",
"certmanager_certificate_fetching_or_enabling_failed": "Provante uzi la novan atestilon por {domain:s} ne funkciis …",
"app_full_domain_unavailable": "Bedaŭrinde, ĉi tiu apliko postulas plenan domajnon esti instalita, sed iuj aliaj programoj jam estas instalitaj sur '{domain}'. Unu ebla solvo estas aldoni kaj uzi subdomajnon dediĉitan al ĉi tiu aplikaĵo anstataŭe."
}

View file

@ -29,7 +29,7 @@
"app_unsupported_remote_type": "Tipo remoto no soportado por la aplicación",
"app_upgrade_failed": "No se pudo actualizar {app:s}",
"app_upgraded": "Actualizado {app:s}",
"appslist_fetched": "Obtenida lista de aplicaciones {appslist:s} actualizada",
"appslist_fetched": "Lista de aplicaciones {appslist:s} actualizada",
"appslist_removed": "Eliminada la lista de aplicaciones {appslist:s}",
"appslist_retrieve_error": "No se puede recuperar la lista remota de aplicaciones {appslist:s}: {error:s}",
"appslist_unknown": "Lista de aplicaciones {appslist:s} desconocida.",
@ -74,9 +74,9 @@
"dnsmasq_isnt_installed": "Parece que dnsmasq no está instalado, ejecute «apt-get remove bind9 && apt-get install it»",
"domain_cert_gen_failed": "No se pudo generar el certificado",
"domain_created": "Dominio creado",
"domain_creation_failed": "No se pudo crear el dominio",
"domain_creation_failed": "No se pudo crear el dominio {domain}: {error}",
"domain_deleted": "Dominio eliminado",
"domain_deletion_failed": "No se pudo eliminar el dominio",
"domain_deletion_failed": "No se pudo eliminar el dominio {domain}: {error}",
"domain_dyndns_already_subscribed": "Ya se ha suscrito a un dominio de DynDNS",
"domain_dyndns_invalid": "Este dominio no se puede usar con DynDNS",
"domain_dyndns_root_unknown": "Dominio raíz de DynDNS desconocido",
@ -228,13 +228,13 @@
"upnp_enabled": "UPnP activado",
"upnp_port_open_failed": "No se pudo abrir el puerto vía UPnP",
"user_created": "Usuario creado",
"user_creation_failed": "No se pudo crear el usuario",
"user_creation_failed": "No se pudo crear el usuario {user}: {error}",
"user_deleted": "Usuario eliminado",
"user_deletion_failed": "No se pudo eliminar el usuario",
"user_deletion_failed": "No se pudo eliminar el usuario {user}: {error}",
"user_home_creation_failed": "No se pudo crear la carpeta «home» para el usuario",
"user_info_failed": "No se pudo obtener la información del usuario",
"user_unknown": "Usuario desconocido: {user:s}",
"user_update_failed": "No se pudo cambiar la información del usuario",
"user_update_failed": "No se pudo actualizar el usuario {user}: {error}",
"user_updated": "Cambiada la información de usuario",
"yunohost_already_installed": "YunoHost ya está instalado",
"yunohost_ca_creation_failed": "No se pudo crear la autoridad de certificación",
@ -320,7 +320,7 @@
"experimental_feature": "Aviso : esta funcionalidad es experimental y no se considera estable, no debería usarla a menos que sepa lo que está haciendo.",
"good_practices_about_user_password": "Está a punto de establecer una nueva contraseña de usuario. La contraseña debería de ser de al menos 8 caracteres, aunque es una buena práctica usar una contraseña más extensa (básicamente una frase) y/o usar caracteres de varias clases (mayúsculas, minúsculas, números y caracteres especiales).",
"password_listed": "Esta contraseña es una de las más usadas en el mundo. Elija algo más único.",
"password_too_simple_1": "La contraseña tiene que ser de al menos 8 caracteres de longitud",
"password_too_simple_1": "La contraseña debe tener al menos 8 caracteres de longitud",
"password_too_simple_2": "La contraseña tiene que ser de al menos 8 caracteres de longitud e incluir un número y caracteres en mayúsculas y minúsculas",
"password_too_simple_3": "La contraseña tiene que ser de al menos 8 caracteres de longitud e incluir un número, mayúsculas, minúsculas y caracteres especiales",
"password_too_simple_4": "La contraseña tiene que ser de al menos 12 caracteres de longitud e incluir un número, mayúsculas, minúsculas y caracteres especiales",
@ -398,16 +398,16 @@
"remove_main_permission_not_allowed": "No se permite eliminar el permiso principal",
"recommend_to_add_first_user": "La posinstalación ha terminado pero YunoHost necesita al menos un usuario para funcionar correctamente, debe añadir uno ejecutando «yunohost user create <nombredeusuario>» o usando la interfaz de administración.",
"permission_update_nothing_to_do": "No hay permisos para actualizar",
"permission_updated": "Actualizado el permiso «{permission:s}» para la aplicación «{app:s}»",
"permission_updated": "Actualizado el permiso «{permission:s}»",
"permission_generated": "Actualizada la base de datos de permisos",
"permission_update_failed": "No se pudo actualizar el permiso",
"permission_update_failed": "No se pudo actualizar el permiso «{permission}» : {error}",
"permission_name_not_valid": "Elija un nombre de permiso permitido para «{permission:s}",
"permission_not_found": "No se encontró el permiso «{permission:s}» para la aplicación «{app:s}»",
"permission_deletion_failed": "Falta el permiso «{permission:s}» para eliminar la aplicación «{app:s}»",
"permission_deleted": "Eliminado el permiso «{permission:s}» para la aplicación {app:s}",
"permission_creation_failed": "No se pudo conceder el permiso",
"permission_created": "Creado el permiso «{permission:s}» para la aplicación {app:s}",
"permission_already_exist": "El permiso «{permission:s}» para la aplicación {app:s} ya existe",
"permission_not_found": "No se encontró el permiso «{permission:s}»",
"permission_deletion_failed": "No se pudo eliminar el permiso «{permission}»: {error}",
"permission_deleted": "Eliminado el permiso «{permission:s}»",
"permission_creation_failed": "No se pudo crear el permiso «{permission}»: {error}",
"permission_created": "Creado el permiso «{permission:s}»",
"permission_already_exist": "El permiso «{permission}» ya existe",
"permission_already_clear": "El permiso «{permission:s}» ya está definido para la aplicación {app:s}",
"pattern_password_app": "Las contraseñas no pueden incluir los siguientes caracteres: {forbidden_chars}",
"need_define_permission_before": "Redefina los permisos ejecutando «yunohost user permission add -u USUARIO» antes de eliminar un grupo permitido",
@ -438,7 +438,7 @@
"migration_0011_LDAP_config_dirty": "Parece que ha personalizado la configuración de LDAP. Para esta migración se necesita actualizar la configuración de LDAP.\nNecesita guardar su configuración actual, reiniciar la configuración original ejecutando «yunohost tools regen-conf -f» y reintentar la migración",
"migration_0011_done": "Migración correcta. Ahora puede gestionar los grupos de usuarios.",
"migration_0011_create_group": "Creando un grupo para cada usuario…",
"migration_0011_can_not_backup_before_migration": "No se pudo respaldar el sistema antes de la migración. Error: {error:s}",
"migration_0011_can_not_backup_before_migration": "Falló el respaldo del sistema antes de la migración. Fallo de migración. Error: {error:s}",
"migration_0011_backup_before_migration": "Creando un respaldo de la base de datos de LDAP y de la configuración de las aplicaciones antes de la migración real.",
"migration_0009_not_needed": "La migración ya ocurrió de algún modo… (?) Omitiendo.",
"migration_0008_no_warning": "No se ha detectado ningún riesgo importante con respecto a la anulación de su configuración SSH ¡sin embargo uno nunca puede estar absolutamente seguro ;)! Ejecute la migración para anularla. Por otra parte, puede omitir la migración aunque no esté recomendado.",
@ -536,14 +536,14 @@
"log_category_404": "La categoría de registro «{category}» no existe",
"log_corrupted_md_file": "El archivo de metadatos YAML asociado con el registro está dañado: «{md_file}\nError: {error}»",
"hook_json_return_error": "No se pudo leer la respuesta del gancho {path:s}. Error: {msg:s}. Contenido sin procesar: {raw_content}",
"group_update_failed": "No se pudo actualizar el grupo «{group}»",
"group_update_failed": "No se pudo actualizar el grupo «{group}»: {error}",
"group_updated": "Grupo «{group}» actualizado",
"group_unknown": "El grupo «{group:s}» es desconocido",
"group_info_failed": "No se pudo mostrar la información del grupo",
"group_deletion_not_allowed": "No se puede eliminar el grupo {group:s} manualmente.",
"group_deletion_failed": "No se pudo eliminar el grupo «{group}»",
"group_deletion_failed": "No se pudo eliminar el grupo «{group}»: {error}",
"group_deleted": "Eliminado el grupo «{group}»",
"group_creation_failed": "No se pudo crear el grupo «{group}»",
"group_creation_failed": "No se pudo crear el grupo «{group}»: {error}",
"group_created": "Creado el grupo «{group}»",
"group_name_already_exist": "El grupo {name:s} ya existe",
"group_already_disallowed": "El grupo «{group:s}» ya tiene desactivado el permiso «{permission:s}» para la aplicación «{app:s}»",
@ -577,8 +577,8 @@
"domain_dns_conf_is_just_a_recommendation": "Esta orden muestra la configuración *recomendada*. No configura el DNS en realidad. Es su responsabilidad configurar la zona de DNS en su registrador según esta recomendación.",
"dpkg_lock_not_available": "Esta orden no se puede ejecutar en este momento porque otro programa parece que está usando el bloqueo de dpkg (el gestor de paquetes del sistema)",
"dpkg_is_broken": "No puede hacer esto en este momento porque dpkg/apt (los gestores de paquetes del sistema) parecen estar en un estado roto... Puede tratar de solucionar este problema conectando a través de SSH y ejecutando `sudo dpkg --configure -a`.",
"confirm_app_install_thirdparty": AVISO! Instalar aplicaciones de terceros podría comprometer la integridad y seguridad de su sistema. Probablemente NO debería instalarlas salvo que sepa lo que está haciendo. ¿Está dispuesto a correr ese riesgo? [{answers:s}] ",
"confirm_app_install_danger": AVISO! Esta aplicación es aún experimental (si no está funcionando expresamente) y ¡es probable que rompa su sistema! Probablemente NO debería instalarla salvo que sepa lo que está haciendo. ¿Está dispuesto a correr ese riesgo? [{answers:s}] ",
"confirm_app_install_thirdparty": PELIGRO! Esta aplicación no forma parte del catálogo de aplicaciones de YunoHost. Instalar aplicaciones de terceros podría comprometer la integridad y seguridad de su sistema. Probablemente NO debería instalarla salvo que sepa lo que está haciendo. No tendrá NINGUNA AYUDA si esta aplicación no funciona o rompe su sistema... Si está dispuesto a aceptar ese riesgo de todas formas, escriba «{answers:s}»",
"confirm_app_install_danger": PELIGRO! ¡Esta aplicación es conocida por ser aún experimental (o no funciona explícitamente)! Probablemente NO debería instalarla salvo que sepa lo que está haciendo. No tendrá NINGUNA AYUDA si esta aplicación no funciona o rompe su sistema... Si está dispuesto a aceptar ese riesgo de todas formas, escriba «{answers:s}»",
"confirm_app_install_warning": "Aviso: esta aplicación puede funcionar pero no está bien integrada en YunoHost. Algunas herramientas como la autentificación única y respaldo/restauración podrían no estar disponibles. ¿Instalar de todos modos? [{answers:s}] ",
"backup_unable_to_organize_files": "No se pudo usar el método rápido de organización de los archivos en el archivo",
"backup_permission": "Permiso de respaldo para la aplicación {app:s}",
@ -606,5 +606,25 @@
"aborting": "Cancelando.",
"app_upgrade_stopped": "Se ha detenido la actualización de todas las aplicaciones para prevenir un posible daño porque la aplicación anterior no se pudo actualizar",
"app_action_broke_system": "Esta acción parece que ha roto estos importantes servicios: {services}",
"operation_interrupted": "¿Ha sido interrumpida la operación manualmente?"
"operation_interrupted": "¿Ha sido interrumpida la operación manualmente?",
"apps_already_up_to_date": "Todas las aplicaciones están ya actualizadas",
"dyndns_provider_unreachable": "No se puede conectar con el proveedor de Dyndns {provider}: o su YunoHost no está correctamente conectado a Internet o el servidor de dynette está caído.",
"group_already_exist": "El grupo {group} ya existe",
"group_already_exist_on_system": "El grupo {group} ya existe en los grupos del sistema",
"group_cannot_be_edited": "El grupo {group} no se puede editar manualmente.",
"group_cannot_be_deleted": "El grupo {group} no se puede eliminar manualmente.",
"group_user_already_in_group": "El usuario {user} ya está en el grupo {group}",
"group_user_not_in_group": "El usuario {user} no está en el grupo {group}",
"log_permission_create": "Crear permiso «{}»",
"log_permission_delete": "Eliminar permiso «{}»",
"log_permission_urls": "Actualizar URLs relacionadas con el permiso «{}»",
"log_user_group_create": "Crear grupo «{}»",
"log_user_permission_update": "Actualizar los accesos para el permiso «{}»",
"log_user_permission_reset": "Restablecer permiso «{}»",
"migration_0011_failed_to_remove_stale_object": "No se pudo eliminar el objeto obsoleto {dn}: {error}",
"permission_already_allowed": "El grupo «{group}» ya tiene el permiso «{permission}» activado",
"permission_already_disallowed": "El grupo «{group}» ya tiene el permiso «{permission}» desactivado",
"permission_cannot_remove_main": "No está permitido eliminar un permiso principal",
"user_already_exists": "El usuario {user} ya existe",
"app_full_domain_unavailable": "Lamentablemente esta aplicación necesita un dominio completo para ser instalada pero ya hay otras aplicaciones instaladas en el dominio «{domain}». Una solución posible es añadir y usar un subdominio dedicado a esta aplicación."
}

View file

@ -17,11 +17,11 @@
"app_manifest_invalid": "Manifeste dapplication incorrect : {error}",
"app_no_upgrade": "Aucune application à mettre à jour",
"app_not_correctly_installed": "{app:s} semble être mal installé",
"app_not_installed": "L'application « {app:s} » nest pas installée. Voici la liste des applications installées: {all_apps}",
"app_not_installed": "Nous navons pas trouvé lapplication « {app:s} » dans la liste des applications installées: {all_apps}",
"app_not_properly_removed": "{app:s} na pas été supprimé correctement",
"app_package_need_update": "Le paquet de lapplication {app} doit être mis à jour pour être en adéquation avec les changements de YunoHost",
"app_recent_version_required": "{app:s} nécessite une version plus récente de YunoHost",
"app_removed": "{app:s} a été supprimé",
"app_removed": "{app:s} supprimé",
"app_requirements_checking": "Vérification des paquets requis pour {app} …",
"app_requirements_failed": "Impossible de satisfaire les pré-requis pour {app} : {error}",
"app_requirements_unmeet": "Les pré-requis de {app} ne sont pas satisfaits, le paquet {pkgname} ({version}) doit être {spec}",
@ -29,8 +29,8 @@
"app_unknown": "Application inconnue",
"app_unsupported_remote_type": "Ce type de commande à distance utilisé pour cette application n'est pas supporté",
"app_upgrade_failed": "Impossible de mettre à jour {app:s}",
"app_upgraded": "{app:s} a été mis à jour",
"appslist_fetched": "La liste dapplications {appslist:s} a été récupérée",
"app_upgraded": "{app:s} mis à jour",
"appslist_fetched": "La liste dapplications mise à jour {appslist:s}",
"appslist_removed": "La liste dapplications {appslist:s} a été supprimée",
"appslist_retrieve_error": "Impossible de récupérer la liste dapplications distante {appslist:s} : {error:s}",
"appslist_unknown": "La liste dapplications {appslist:s} est inconnue.",
@ -46,13 +46,13 @@
"backup_app_failed": "Impossible de sauvegarder lapplication '{app:s}'",
"backup_archive_app_not_found": "Lapplication '{app:s}' na pas été trouvée dans larchive de la sauvegarde",
"backup_archive_hook_not_exec": "Le script « {hook:s} » n'a pas été exécuté dans cette sauvegarde",
"backup_archive_name_exists": "Une archive de sauvegarde avec ce nom existe déjà",
"backup_archive_name_exists": "Une archive de sauvegarde avec ce nom existe déjà.",
"backup_archive_name_unknown": "Larchive locale de sauvegarde nommée '{name:s}' est inconnue",
"backup_archive_open_failed": "Impossible douvrir larchive de sauvegarde",
"backup_archive_open_failed": "Impossible douvrir larchive de la sauvegarde",
"backup_cleaning_failed": "Impossible de nettoyer le dossier temporaire de sauvegarde",
"backup_created": "Sauvegarde terminée",
"backup_creating_archive": "Création de larchive de sauvegarde …",
"backup_creation_failed": "Impossible de créer la sauvegarde",
"backup_creation_failed": "Impossible de créer l'archive de la sauvegarde",
"backup_delete_error": "Impossible de supprimer '{path:s}'",
"backup_deleted": "La sauvegarde a été supprimée",
"backup_extracting_archive": "Extraction de larchive de sauvegarde …",
@ -75,9 +75,9 @@
"dnsmasq_isnt_installed": "dnsmasq ne semble pas être installé, veuillez lancer 'apt-get remove bind9 && apt-get install dnsmasq'",
"domain_cert_gen_failed": "Impossible de générer le certificat",
"domain_created": "Le domaine a été créé",
"domain_creation_failed": "Impossible de créer le domaine",
"domain_creation_failed": "Impossible de créer le domaine {domain}: {error}",
"domain_deleted": "Le domaine a été supprimé",
"domain_deletion_failed": "Impossible de supprimer le domaine",
"domain_deletion_failed": "Impossible de supprimer le domaine {domain}: {error}",
"domain_dyndns_already_subscribed": "Vous avez déjà souscris à un domaine DynDNS",
"domain_dyndns_invalid": "Domaine incorrect pour un usage avec DynDNS",
"domain_dyndns_root_unknown": "Domaine DynDNS principal inconnu",
@ -88,15 +88,15 @@
"domain_zone_not_found": "Fichier de zone DNS introuvable pour le domaine {:s}",
"done": "Terminé",
"downloading": "Téléchargement en cours …",
"dyndns_cron_installed": "La tâche cron pour le domaine DynDNS a été installée",
"dyndns_cron_installed": "La tâche cron pour le domaine DynDNS a été créée",
"dyndns_cron_remove_failed": "Impossible de supprimer la tâche cron DynDNS parce que: {error}",
"dyndns_cron_removed": "La tâche cron pour le domaine DynDNS a été enlevée",
"dyndns_cron_removed": "La tâche cron pour le domaine DynDNS enlevée",
"dyndns_ip_update_failed": "Impossible de mettre à jour ladresse IP sur le domaine DynDNS",
"dyndns_ip_updated": "Votre adresse IP a été mise à jour pour le domaine DynDNS",
"dyndns_key_generating": "La clé DNS est en cours de génération, cela peut prendre un certain temps …",
"dyndns_ip_updated": "Mise à jour de votre IP pour le domaine DynDNS",
"dyndns_key_generating": "Génération de la clé DNS ... , cela peut prendre un certain temps.",
"dyndns_key_not_found": "Clé DNS introuvable pour le domaine",
"dyndns_no_domain_registered": "Aucun domaine na été enregistré avec DynDNS",
"dyndns_registered": "Le domaine DynDNS a été enregistré",
"dyndns_no_domain_registered": "Aucun domaine enregistré avec DynDNS",
"dyndns_registered": "Domaine DynDNS enregistré",
"dyndns_registration_failed": "Impossible denregistrer le domaine DynDNS : {error:s}",
"dyndns_unavailable": "Le domaine {domain:s} est indisponible.",
"executing_command": "Exécution de la commande '{command:s}' …",
@ -104,8 +104,8 @@
"extracting": "Extraction en cours …",
"field_invalid": "Champ incorrect : '{:s}'",
"firewall_reload_failed": "Impossible de recharger le pare-feu",
"firewall_reloaded": "Le pare-feu a été rechargé",
"firewall_rules_cmd_failed": "Certaines règles du pare-feu nont pas pu être appliquées. Pour plus dinformations, consultez le journal.",
"firewall_reloaded": "Pare-feu rechargé",
"firewall_rules_cmd_failed": "Certaines règles du pare-feu nont pas pu être appliquées. Plus d'info dans le journal de log.",
"format_datetime_short": "%d/%m/%Y %H:%M",
"hook_argument_missing": "Argument manquant : '{:s}'",
"hook_choice_invalid": "Choix incorrect : '{:s}'",
@ -114,16 +114,16 @@
"hook_list_by_invalid": "Propriété invalide pour lister les actions par celle-ci",
"hook_name_unknown": "Nom de l'action '{name:s}' inconnu",
"installation_complete": "Installation terminée",
"installation_failed": "Échec de linstallation",
"installation_failed": "Quelque chose s'est mal passé lors de l'installation",
"ip6tables_unavailable": "Vous ne pouvez pas jouer avec ip6tables ici. Vous êtes soit dans un conteneur, soit votre noyau ne le prend pas en charge",
"iptables_unavailable": "Vous ne pouvez pas jouer avec iptables ici. Vous êtes soit dans un conteneur, soit votre noyau ne le prend pas en charge",
"ldap_initialized": "Lannuaire LDAP a été initialisé",
"ldap_initialized": "Lannuaire LDAP initialisé",
"license_undefined": "indéfinie",
"mail_alias_remove_failed": "Impossible de supprimer lalias de courriel '{mail:s}'",
"mail_domain_unknown": "Le domaine '{domain:s}' pour l'adresse de courriel est inconnu",
"mail_forward_remove_failed": "Impossible de supprimer le courriel de transfert '{mail:s}'",
"maindomain_change_failed": "Impossible de modifier le domaine principal",
"maindomain_changed": "Le domaine principal a été modifié",
"maindomain_changed": "Le domaine principal modifié",
"monitor_disabled": "La supervision du serveur a été désactivé",
"monitor_enabled": "La supervision du serveur a été activé",
"monitor_glances_con_failed": "Impossible de se connecter au serveur Glances",
@ -173,7 +173,7 @@
"restore_already_installed_app": "Une application est déjà installée avec lidentifiant '{app:s}'",
"restore_app_failed": "Impossible de restaurer lapplication '{app:s}'",
"restore_cleaning_failed": "Impossible de nettoyer le dossier temporaire de restauration",
"restore_complete": "Restauration terminée",
"restore_complete": "Restauré",
"restore_confirm_yunohost_installed": "Voulez-vous vraiment restaurer un système déjà installé ? [{answers:s}]",
"restore_failed": "Impossible de restaurer le système",
"restore_hook_unavailable": "Le script de restauration '{part:s}' nest pas disponible sur votre système, et ne l'est pas non plus dans larchive",
@ -182,7 +182,7 @@
"restore_running_hooks": "Exécution des scripts de restauration …",
"service_add_configuration": "Ajout du fichier de configuration {file:s}",
"service_add_failed": "Impossible dajouter le service '{service:s}'",
"service_added": "Le service '{service:s}' a été ajouté",
"service_added": "Le service '{service:s}' ajouté",
"service_already_started": "Le service '{service:s}' est déjà démarré",
"service_already_stopped": "Le service '{service:s}' est déjà arrêté",
"service_cmd_exec_failed": "Impossible dexécuter la commande '{command:s}'",
@ -218,9 +218,9 @@
"service_unknown": "Le service '{service:s}' est inconnu",
"services_configured": "La configuration a été générée avec succès",
"show_diff": "Voici les différences :\n{diff:s}",
"ssowat_conf_generated": "La configuration de SSOwat a été générée",
"ssowat_conf_updated": "La configuration de SSOwat a été mise à jour",
"system_upgraded": "Le système a été mis à jour",
"ssowat_conf_generated": "La configuration de SSOwat générée",
"ssowat_conf_updated": "La configuration de SSOwat mise à jour",
"system_upgraded": "Système mis à jour",
"system_username_exists": "Ce nom dutilisateur existe déjà dans les utilisateurs système",
"unbackup_app": "Lapplication '{app:s}' ne sera pas sauvegardée",
"unexpected_error": "Une erreur inattendue est survenue : {error}",
@ -232,21 +232,21 @@
"upgrade_complete": "Mise à jour terminée",
"upgrading_packages": "Mise à jour des paquets en cours …",
"upnp_dev_not_found": "Aucun périphérique compatible UPnP na été trouvé",
"upnp_disabled": "UPnP a été désactivé",
"upnp_enabled": "UPnP a été activé",
"upnp_disabled": "UPnP désactivé",
"upnp_enabled": "UPnP activé",
"upnp_port_open_failed": "Impossible douvrir les ports UPnP",
"user_created": "Lutilisateur a été créé",
"user_creation_failed": "Impossible de créer lutilisateur",
"user_deleted": "Lutilisateur a été supprimé",
"user_deletion_failed": "Impossible de supprimer lutilisateur",
"user_created": "Lutilisateur créé",
"user_creation_failed": "Impossible de créer lutilisateur {user}: {error}",
"user_deleted": "Lutilisateur supprimé",
"user_deletion_failed": "Impossible de supprimer lutilisateur {user}: {error}",
"user_home_creation_failed": "Impossible de créer le dossier personnel de lutilisateur",
"user_info_failed": "Impossible de récupérer les informations de lutilisateur",
"user_unknown": "L'utilisateur {user:s} est inconnu",
"user_update_failed": "Impossible de modifier lutilisateur",
"user_update_failed": "Impossible de mettre à jour l'utilisateur {utilisateur}: {erreur}",
"user_updated": "Lutilisateur a été modifié",
"yunohost_already_installed": "YunoHost est déjà installé",
"yunohost_ca_creation_failed": "Impossible de créer lautorité de certification",
"yunohost_configured": "YunoHost a été configuré",
"yunohost_configured": "YunoHost maintenant configuré",
"yunohost_installing": "L'installation de YunoHost est en cours …",
"yunohost_not_installed": "YunoHost nest pas ou pas correctement installé. Veuillez exécuter 'yunohost tools postinstall'",
"certmanager_attempt_to_replace_valid_cert": "Vous êtes en train de vouloir remplacer un certificat correct et valide pour le domaine {domain:s} ! (Utilisez --force pour contourner cela)",
@ -259,17 +259,17 @@
"certmanager_error_no_A_record": "Aucun enregistrement DNS 'A' na été trouvé pour {domain:s}. Vous devez faire pointer votre nom de domaine vers votre machine pour être en mesure dinstaller un certificat Lets Encrypt ! (Si vous savez ce que vous faites, utilisez --no-checks pour désactiver ces contrôles)",
"certmanager_domain_dns_ip_differs_from_public_ip": "Lenregistrement DNS 'A' du domaine {domain:s} est différent de ladresse IP de ce serveur. Si vous avez récemment modifié votre enregistrement 'A', veuillez attendre sa propagation (quelques vérificateur de propagation DNS sont disponibles en ligne). (Si vous savez ce que vous faites, utilisez --no-checks pour désactiver ces contrôles)",
"certmanager_cannot_read_cert": "Quelque chose sest mal passé lors de la tentative douverture du certificat actuel pour le domaine {domain:s} (fichier : {file:s}), la cause est : {reason:s}",
"certmanager_cert_install_success_selfsigned": "Installation avec succès dun certificat auto-signé pour le domaine {domain:s} !",
"certmanager_cert_install_success": "Installation avec succès dun certificat Lets Encrypt pour le domaine {domain:s} !",
"certmanager_cert_renew_success": "Renouvellement avec succès dun certificat Lets Encrypt pour le domaine {domain:s} !",
"certmanager_cert_install_success_selfsigned": "Le certificat auto-signé est maintenant installé pour le domaine « {domain:s} »",
"certmanager_cert_install_success": "Le certificat Lets Encrypt est maintenant installé pour le domaine « {domain:s} »",
"certmanager_cert_renew_success": "Certificat Let's Encrypt renouvelé pour le domaine '{domain: s}'",
"certmanager_old_letsencrypt_app_detected": "\nYunoHost a détecté que lapplication « letsencrypt » est installé, ce qui est en conflit avec les nouvelles fonctionnalités de gestion intégrée de certificats dans YunoHost. Si vous souhaitez utiliser ces nouvelles fonctionnalités intégrées, veuillez lancer les commandes suivantes pour migrer votre installation :\n\n yunohost app remove letsencrypt\n yunohost domain cert-install\n\nN.B. : cela tentera de réinstaller les certificats de tous les domaines avec un certificat Let's Encrypt ou ceux auto-signés",
"certmanager_cert_signing_failed": "La signature du nouveau certificat a échoué",
"certmanager_cert_signing_failed": "Impossible de signer le nouveau certificat",
"certmanager_no_cert_file": "Impossible de lire le fichier du certificat pour le domaine {domain:s} (fichier : {file:s})",
"certmanager_conflicting_nginx_file": "Impossible de préparer le domaine pour le défi ACME : le fichier de configuration Nginx {filepath:s} est en conflit et doit être préalablement retiré",
"certmanager_conflicting_nginx_file": "Impossible de préparer le domaine pour le défi ACME : le fichier de configuration NGINX {filepath:s} est en conflit et doit être préalablement retiré",
"certmanager_hit_rate_limit": "Trop de certificats ont déjà été émis récemment pour ce même ensemble de domaines {domain:s}. Veuillez réessayer plus tard. Lisez https://letsencrypt.org/docs/rate-limits/ pour obtenir plus de détails sur les ratios et limitations",
"ldap_init_failed_to_create_admin": "Linitialisation de l'annuaire LDAP na pas réussi à créer lutilisateur admin",
"ssowat_persistent_conf_read_error": "Erreur lors de la lecture de la configuration persistante de SSOwat : {error:s}. Modifiez le fichier /etc/ssowat/conf.json.persistent pour réparer la syntaxe JSON",
"ssowat_persistent_conf_write_error": "Erreur lors de la sauvegarde de la configuration persistante de SSOwat : {error:s}. Modifiez le fichier /etc/ssowat/conf.json.persistent pour réparer la syntaxe JSON",
"ssowat_persistent_conf_read_error": "Impossible de lire la configuration persistante de SSOwat : {error:s}. Modifiez le fichier /etc/ssowat/conf.json.persistent pour réparer la syntaxe JSON",
"ssowat_persistent_conf_write_error": "Impossible de sauvegarder de la configuration persistante de SSOwat : {error:s}. Modifiez le fichier /etc/ssowat/conf.json.persistent pour réparer la syntaxe JSON",
"domain_cannot_remove_main": "Impossible de supprimer le domaine principal. Définissez d'abord un nouveau domaine principal",
"certmanager_self_ca_conf_file_not_found": "Le fichier de configuration pour lautorité du certificat auto-signé est introuvable (fichier : {file:s})",
"certmanager_unable_to_parse_self_CA_name": "Impossible danalyser le nom de lautorité du certificat auto-signé (fichier : {file:s})",
@ -280,14 +280,14 @@
"certmanager_domain_not_resolved_locally": "Le domaine {domain:s} ne peut être résolu depuis votre serveur YunoHost. Cela peut se produire si vous avez récemment modifié votre enregistrement DNS. Si c'est le cas, merci dattendre quelques heures quil se propage. Si le problème persiste, envisager dajouter {domain:s} au fichier /etc/hosts. (Si vous savez ce que vous faites, utilisez --no-checks pour désactiver ces vérifications.)",
"certmanager_http_check_timeout": "Expiration du délai lorsque le serveur a essayé de se contacter lui-même via HTTP en utilisant l'adresse IP public {ip:s} du domaine {domain:s}. Vous rencontrez peut-être un problème dhairpinning ou alors le pare-feu/routeur en amont de votre serveur est mal configuré.",
"certmanager_couldnt_fetch_intermediate_cert": "Expiration du délai lors de la tentative de récupération du certificat intermédiaire depuis Lets Encrypt. Linstallation ou le renouvellement du certificat a été annulé. Veuillez réessayer plus tard.",
"appslist_retrieve_bad_format": "Le fichier récupéré pour la liste dapplications {appslist:s} nest pas valide",
"domain_hostname_failed": "Échec de lutilisation dun nouveau nom dhôte. Cela pourrait causer des soucis plus tard (mais ce nest pas sûr… peut-être que ça nen causera pas).",
"yunohost_ca_creation_success": "Lautorité de certification locale a été créée.",
"appslist_name_already_tracked": "Il y a déjà une liste dapplications enregistrée avec le nom {name:s}.",
"appslist_retrieve_bad_format": "Impossible de lire la liste des applications extraites {appslist: s}",
"domain_hostname_failed": "Échec de lutilisation dun nouveau nom dhôte. Cela pourrait causer des soucis plus tard (peut-être que ça nen causera pas).",
"yunohost_ca_creation_success": "Lautorité de certification locale créée.",
"appslist_name_already_tracked": "Il y a déjà une liste dapplications enregistrée avec le nom {name:s} existe déjà.",
"appslist_url_already_tracked": "Il y a déjà une liste dapplications enregistrée avec lURL {url:s}.",
"appslist_migrating": "Migration de la liste dapplications {appslist:s} …",
"appslist_could_not_migrate": "Impossible de migrer la liste {appslist:s} ! Impossible dexploiter lURL. Lancienne tâche programmée a été conservée dans {bkp_file:s}.",
"appslist_corrupted_json": "Impossible de charger la liste dapplications. Il semble que {filename:s} soit corrompu.",
"appslist_corrupted_json": "Impossible de charger la liste dapplications. Il semble que {filename:s} soit endommager.",
"app_already_installed_cant_change_url": "Cette application est déjà installée. LURL ne peut pas être changé simplement par cette fonction. Regardez si cela est disponible avec `app changeurl`.",
"app_change_no_change_url_script": "Lapplication {app_name:s} ne prend pas encore en charge le changement dURL, vous pourriez avoir besoin de la mettre à jour.",
"app_change_url_failed_nginx_reload": "Le redémarrage de Nginx a échoué. Voici la sortie de 'nginx -t' :\n{nginx_errors:s}",
@ -297,13 +297,13 @@
"app_location_unavailable": "Cette URL nest pas disponible ou est en conflit avec une application existante :\n{apps:s}",
"app_already_up_to_date": "{app:s} est déjà à jour",
"invalid_url_format": "Format dURL non valide",
"global_settings_bad_choice_for_enum": "Valeur du paramètre {setting:s} incorrecte. Reçu : {received_type:s}, mais les valeurs possibles sont : {expected_type:s}",
"global_settings_bad_choice_for_enum": "Valeur du paramètre {setting:s} incorrecte. Reçu : {choice:s}, mais les valeurs possibles sont : {available_choices:s}",
"global_settings_bad_type_for_setting": "Le type du paramètre {setting:s} est incorrect. Reçu {received_type:s} alors que {expected_type:s} était attendu",
"global_settings_cant_open_settings": "Échec de louverture du ficher de configurations car : {reason:s}",
"global_settings_cant_serialize_setings": "Échec de sérialisation des données de configurations, cause : {reason:s}",
"global_settings_cant_write_settings": "Échec décriture du fichier de configurations car : {reason:s}",
"global_settings_key_doesnt_exists": "La clef '{settings_key:s}' nexiste pas dans les configurations générales, vous pouvez voir toutes les clefs disponibles en saisissant 'yunohost settings list'",
"global_settings_reset_success": "Super ! Vos configurations précédentes ont été sauvegardées dans {path:s}",
"global_settings_reset_success": "Vos configurations précédentes ont été sauvegardées dans {path:s}",
"global_settings_setting_example_bool": "Exemple doption booléenne",
"global_settings_setting_example_int": "Exemple doption de type entier",
"global_settings_setting_example_string": "Exemple doption de type chaîne",
@ -312,8 +312,8 @@
"global_settings_unknown_setting_from_settings_file": "Clé inconnue dans les paramètres : '{setting_key:s}', rejet de cette clé et sauvegarde de celle-ci dans /etc/yunohost/unkown_settings.json",
"service_conf_new_managed_file": "Le fichier de configuration « {conf} » est désormais géré par le service {service}.",
"service_conf_file_kept_back": "Le fichier de configuration '{conf}' devait être supprimé par le service {service} mais a été conservé.",
"backup_abstract_method": "Cette méthode de sauvegarde na pas encore été implémentée",
"backup_applying_method_tar": "Création de larchive tar de la sauvegarde …",
"backup_abstract_method": "Cette méthode de sauvegarde reste à implémenter",
"backup_applying_method_tar": "Création de larchive TAR de la sauvegarde…",
"backup_applying_method_copy": "Copie de tous les fichiers à sauvegarder …",
"backup_applying_method_borg": "Envoi de tous les fichiers à sauvegarder dans le répertoire borg-backup…",
"backup_applying_method_custom": "Appel de la méthode de sauvegarde personnalisée '{method:s}' …",
@ -324,20 +324,20 @@
"backup_borg_not_implemented": "La méthode de sauvegarde Borg nest pas encore implémentée",
"backup_cant_mount_uncompress_archive": "Impossible de monter en lecture seule le dossier de larchive décompressée",
"backup_copying_to_organize_the_archive": "Copie de {size:s} Mo pour organiser larchive",
"backup_csv_creation_failed": "Impossible de créer le fichier CSV nécessaire aux opérations futures de restauration",
"backup_csv_creation_failed": "Impossible de créer le fichier CSV nécessaire à la restauration",
"backup_csv_addition_failed": "Impossible dajouter des fichiers à sauvegarder dans le fichier CSV",
"backup_custom_need_mount_error": "Échec de la méthode de sauvegarde personnalisée à létape 'need_mount'",
"backup_custom_backup_error": "Échec de la méthode de sauvegarde personnalisée à létape 'backup'",
"backup_custom_mount_error": "Échec de la méthode de sauvegarde personnalisée à létape 'mount'",
"backup_no_uncompress_archive_dir": "Le dossier de larchive décompressée nexiste pas",
"backup_method_tar_finished": "Larchive tar de la sauvegarde a été créée",
"backup_no_uncompress_archive_dir": "Ce dossier darchive décompressée nexiste pas",
"backup_method_tar_finished": "Larchive TAR de la sauvegarde a été créée",
"backup_method_copy_finished": "La copie de la sauvegarde est terminée",
"backup_method_borg_finished": "La sauvegarde dans Borg est terminée",
"backup_method_custom_finished": "La méthode de sauvegarde personnalisée '{method:s}' est terminée",
"backup_system_part_failed": "Impossible de sauvegarder la partie '{part:s}' du système",
"backup_unable_to_organize_files": "Impossible dorganiser les fichiers dans larchive avec la méthode rapide",
"backup_unable_to_organize_files": "Impossible dutiliser la méthode rapide pour organiser les fichiers dans larchive",
"backup_with_no_backup_script_for_app": "Lapplication {app:s} na pas de script de sauvegarde. Ignorer.",
"backup_with_no_restore_script_for_app": "Lapplication {app:s} na pas de script de restauration, vous ne pourrez pas restaurer automatiquement la sauvegarde de cette application.",
"backup_with_no_restore_script_for_app": "Lapplication « {app:s} » na pas de script de restauration, vous ne pourrez pas restaurer automatiquement la sauvegarde de cette application.",
"global_settings_cant_serialize_settings": "Échec de la sérialisation des données de paramétrage car : {reason:s}",
"restore_removing_tmp_dir_failed": "Impossible de sauvegarder un ancien dossier temporaire",
"restore_extracting": "Extraction des fichiers nécessaires depuis larchive …",
@ -350,35 +350,35 @@
"domain_dyndns_dynette_is_unreachable": "Impossible de contacter la dynette YunoHost. Soit YunoHost nest pas correctement connecté à internet, soit le serveur de dynette est en panne. Erreur : {error}",
"migrations_backward": "Migration en arrière.",
"migrations_bad_value_for_target": "Nombre invalide pour le paramètre target, les numéros de migration sont 0 ou {}",
"migrations_cant_reach_migration_file": "Impossible daccéder aux fichiers de migrations avec le chemin %s",
"migrations_cant_reach_migration_file": "Impossible d'accéder aux fichiers de migration sur le chemin% s",
"migrations_current_target": "La cible de migration est {}",
"migrations_error_failed_to_load_migration": "ERREUR : échec du chargement de migration {number} {name}",
"migrations_forward": "Migration en avant",
"migrations_loading_migration": "Chargement de la migration {number} {name} …",
"migrations_migration_has_failed": "La migration {number} {name} a échoué avec lexception {exception} : annulation",
"migrations_loading_migration": "Chargement de la migration {id} …",
"migrations_migration_has_failed": "La migration {id} a échoué avec lexception {exception} : annulation",
"migrations_no_migrations_to_run": "Aucune migration à lancer",
"migrations_show_currently_running_migration": "Application de la migration {number} {name} …",
"migrations_show_last_migration": "La dernière migration appliquée est {}",
"migrations_skip_migration": "Ignorer et passer la migration {number} {name}…",
"migrations_skip_migration": "Ignorer et passer la migration {id}…",
"server_shutdown": "Le serveur va éteindre",
"server_shutdown_confirm": "Le serveur va être éteint immédiatement, le voulez-vous vraiment ? [{answers:s}]",
"server_reboot": "Le serveur va redémarrer",
"server_reboot_confirm": "Le serveur va redémarrer immédiatement, le voulez-vous vraiment ? [{answers:s}]",
"app_upgrade_some_app_failed": "Impossible de mettre à jour certaines applications",
"app_upgrade_some_app_failed": "Certaines applications nont pas été mises à jour",
"ask_path": "Chemin",
"dyndns_could_not_check_provide": "Impossible de vérifier si {provider:s} peut fournir {domain:s}.",
"dyndns_domain_not_provided": "Le fournisseur DynDNS {provider:s} ne peut pas fournir le domaine {domain:s}.",
"app_make_default_location_already_used": "Impossible de configurer lapplication '{app}' par défaut pour le domaine {domain} car il est déjà utilisé par l'application '{other_app}'",
"app_upgrade_app_name": "Mise à jour de lapplication {app} …",
"backup_output_symlink_dir_broken": "Vous avez un lien symbolique cassé à la place de votre dossier darchives « {path:s} ». Vous pourriez avoir une configuration personnalisée pour sauvegarder vos données sur un autre système de fichiers, dans ce cas vous avez probablement oublié de monter ou de connecter votre disque dur ou votre clé USB.",
"migrate_tsig_end": "La migration à hmac-sha512 est terminée",
"migrate_tsig_end": "La migration à HMAC-SHA-512 est terminée",
"migrate_tsig_failed": "La migration du domaine DynDNS {domain} à hmac-sha512 a échoué. Annulation des modifications. Erreur : {error_code} - {error}",
"migrate_tsig_start": "Lalgorithme de génération des clefs nest pas suffisamment sécurisé pour la signature TSIG du domaine '{domain}', lancement de la migration vers hmac-sha512 qui est plus sécurisé",
"migrate_tsig_wait": "Attendre 3 minutes pour que le serveur DynDNS prenne en compte la nouvelle clef …",
"migrate_tsig_start": "Lalgorithme de génération des clefs nest pas suffisamment sécurisé pour la signature TSIG du domaine '{domain}', lancement de la migration vers HMAC-SHA-512 qui est plus sécurisé",
"migrate_tsig_wait": "Attendre trois minutes pour que le serveur DynDNS prenne en compte la nouvelle clef …",
"migrate_tsig_wait_2": "2 minutes …",
"migrate_tsig_wait_3": "1 minute …",
"migrate_tsig_wait_4": "30 secondes …",
"migrate_tsig_not_needed": "Il ne semble pas que vous utilisez un domaine DynDNS, donc aucune migration nest nécessaire !",
"migrate_tsig_not_needed": "Il ne semble pas que vous utilisez un domaine DynDNS, donc aucune migration nest nécessaire.",
"app_checkurl_is_deprecated": "Packagers /!\\ 'app checkurl' est obsolète ! Utilisez 'app register-url' en remplacement !",
"migration_description_0001_change_cert_group_to_sslcert": "Changement des permissions de groupe des certificats de « metronome » à « ssl-cert »",
"migration_description_0002_migrate_to_tsig_sha256": "Amélioration de la sécurité de DynDNS TSIG en utilisant SHA512 au lieu de MD5",
@ -393,37 +393,37 @@
"migration_0003_not_jessie": "La distribution Debian actuelle nest pas Jessie !",
"migration_0003_system_not_fully_up_to_date": "Votre système nest pas complètement à jour. Veuillez mener une mise à jour classique avant de lancer à migration à Stretch.",
"migration_0003_still_on_jessie_after_main_upgrade": "Quelque chose sest mal passé pendant la mise à niveau principale : le système est toujours sur Debian Jessie !? Pour investiguer sur le problème, veuillez regarder les journaux {log}:s …",
"migration_0003_general_warning": "Veuillez noter que cette migration est une opération délicate. Si léquipe YunoHost a fait de son mieux pour la relire et la tester, la migration pourrait tout de même casser des parties de votre système ou de vos applications.\n\nEn conséquence, nous vous recommandons :\n - de lancer une sauvegarde de vos données ou applications critiques. Plus dinformations sur https://yunohost.org/backup ;\n - dêtre patient après avoir lancé la migration : selon votre connexion internet et matériel, cela pourrait prendre jusquà quelques heures pour que tout soit à niveau.\n\nEn outre, le port SMTP utilisé par les clients de messagerie externes comme (Thunderbird ou K9-Mail) a été changé de 465 (SSL/TLS) à 587 (STARTTLS). Lancien port 465 sera automatiquement fermé et le nouveau port 587 sera ouvert dans le pare-feu. Vous et vos utilisateurs *devront* adapter la configuration de vos clients de messagerie en conséquence !",
"migration_0003_general_warning": "Veuillez noter que cette migration est une opération délicate. Si léquipe YunoHost a fait de son mieux pour la relire et la tester, la migration pourrait tout de même casser des parties de votre système ou de vos applications.\n\nEn conséquence, nous vous recommandons :\n - de lancer une sauvegarde de vos données ou applications critiques. Plus dinformations sur https://yunohost.org/backup ;\n - dêtre patient après avoir lancé la migration : selon votre connexion internet et matériel, cela pourrait prendre jusquà quelques heures pour que tout soit à niveau.\n\nEn outre, le port SMTP utilisé par les clients de messagerie externes comme (Thunderbird ou K9-Mail) a été changé de 465 (SSL/TLS) à 587 (STARTTLS). Lancien port 465 sera automatiquement fermé et le nouveau port 587 sera ouvert dans le pare-feu. Vous et vos utilisateurs *devront* adapter la configuration de vos clients de messagerie en conséquence.",
"migration_0003_problematic_apps_warning": "Veuillez noter que des applications possiblement problématiques ont été détectées. Il semble quelles naient pas été installées depuis une liste dapplication ou quelles ne soit pas marquées comme « fonctionnelles ». En conséquence, nous ne pouvons pas garantir quelles fonctionneront après la mise à niveau : {problematic_apps}",
"migration_0003_modified_files": "Veuillez noter que les fichiers suivants ont été détectés comme modifiés manuellement et pourraient être écrasés à la fin de la mise à niveau : {manually_modified_files}",
"migrations_list_conflict_pending_done": "Vous ne pouvez pas utiliser --previous et --done simultanément.",
"migrations_to_be_ran_manually": "La migration {number} {name} doit être lancée manuellement. Veuillez aller dans Outils > Migrations dans linterface admin, ou lancer `yunohost tools migrations migrate`.",
"migrations_need_to_accept_disclaimer": "Pour lancer la migration {number} {name}, vous devez accepter cette clause de non-responsabilité :\n---\n{disclaimer}\n---\nSi vous acceptez de lancer la migration, veuillez relancer la commande avec loption --accept-disclaimer.",
"service_description_avahi-daemon": "permet datteindre votre serveur via yunohost.local sur votre réseau local",
"service_description_dnsmasq": "gère la résolution des noms de domaine (DNS)",
"service_description_dovecot": "permet aux clients de messagerie daccéder/récupérer les courriels (via IMAP et POP3)",
"service_description_fail2ban": "protège contre les attaques brute-force et autres types dattaques venant dInternet",
"service_description_glances": "surveille les informations système de votre serveur",
"service_description_metronome": "gère les comptes de messagerie instantanée XMPP",
"service_description_mysql": "stocke les données des applications (bases de données SQL)",
"service_description_nginx": "sert ou permet laccès à tous les sites web hébergés sur votre serveur",
"service_description_nslcd": "gère la connexion en ligne de commande des utilisateurs YunoHost",
"migrations_to_be_ran_manually": "La migration {id} doit être lancée manuellement. Veuillez aller dans Outils > Migrations dans linterface admin, ou lancer `yunohost tools migrations migrate`.",
"migrations_need_to_accept_disclaimer": "Pour lancer la migration {id}, vous devez accepter cette clause de non-responsabilité :\n---\n{disclaimer}\n---\nSi vous acceptez de lancer la migration, veuillez relancer la commande avec loption --accept-disclaimer.",
"service_description_avahi-daemon": "Vous permet datteindre votre serveur en utilisant «yunohost.local» sur votre réseau local",
"service_description_dnsmasq": "Gère la résolution des noms de domaine (DNS)",
"service_description_dovecot": "Permet aux clients de messagerie daccéder/récupérer les courriels (via IMAP et POP3)",
"service_description_fail2ban": "Protège contre les attaques brute-force et autres types dattaques venant dInternet",
"service_description_glances": "Surveille les info système de votre serveur",
"service_description_metronome": "Gère les comptes de messagerie instantanée XMPP",
"service_description_mysql": "Stocke les données des applications (bases de données SQL)",
"service_description_nginx": "Sert ou permet laccès à tous les sites web hébergés sur votre serveur",
"service_description_nslcd": "Gère la connexion en ligne de commande des utilisateurs YunoHost",
"service_description_php5-fpm": "exécute des applications écrites en PHP avec nginx",
"service_description_postfix": "utilisé pour envoyer et recevoir des courriels",
"service_description_redis-server": "une base de données spécialisée utilisée pour laccès rapide aux données, les files dattentes et la communication entre les programmes",
"service_description_rmilter": "vérifie divers paramètres dans les courriels",
"service_description_rspamd": "filtre le pourriel, et dautres fonctionnalités liées au courriel",
"service_description_slapd": "stocke les utilisateurs, domaines et leurs informations liées",
"service_description_ssh": "vous permet de vous connecter à distance à votre serveur via un terminal (protocole SSH)",
"service_description_yunohost-api": "permet les interactions entre linterface web de YunoHost et le système",
"service_description_yunohost-firewall": "gère l'ouverture et la fermeture des ports de connexion aux services",
"service_description_postfix": "Utilisé pour envoyer et recevoir des courriels",
"service_description_redis-server": "Une base de données spécialisée utilisée pour laccès rapide aux données, les files dattentes et la communication entre les programmes",
"service_description_rmilter": "Vérifie divers paramètres dans les courriels",
"service_description_rspamd": "Filtre le pourriel, et dautres fonctionnalités liées au courriel",
"service_description_slapd": "Stocke les utilisateurs, domaines et leurs informations liées",
"service_description_ssh": "Vous permet de vous connecter à distance à votre serveur via un terminal (protocole SSH)",
"service_description_yunohost-api": "Permet les interactions entre linterface web de YunoHost et le système",
"service_description_yunohost-firewall": "Gère l'ouverture et la fermeture des ports de connexion aux services",
"experimental_feature": "Attention : cette fonctionnalité est expérimentale et ne doit pas être considérée comme stable, vous ne devriez pas lutiliser à moins que vous ne sachiez ce que vous faites.",
"log_corrupted_md_file": "Le fichier yaml de métadonnées associé aux logs est corrompu : '{md_file}'\nErreur : {error}",
"log_corrupted_md_file": "Le fichier YAML de métadonnées associé aux logs est corrompu : '{md_file}'\nErreur : {error}",
"log_category_404": "Le journal de la catégorie '{category}' nexiste pas",
"log_link_to_log": "Journal historisé complet de cette opération : '<a href=\"#/tools/logs/{name}\" style=\"text-decoration:underline\"> {desc} </a>'",
"log_help_to_get_log": "Pour voir le journal historisé de cette opération '{desc}', utilisez la commande 'yunohost log display {name}'",
"log_link_to_failed_log": "Lopération '{desc}' a échouée ! Pour avoir de laide, merci <a href=\"#/tools/logs/{name}\"> de fournir le journal historisé complet de lopération en cliquant ici</a>",
"backup_php5_to_php7_migration_may_fail": "Impossible de convertir votre archive pour prendre en charge php7, vos applications php pourraient ne pas être restaurées (reason: {error:s})",
"log_link_to_failed_log": "Lopération '{desc}' a échouée ! Pour avoir de laide, merci <a href=\"#/tools/logs/{name}\"> cliqué ici</a> pour avoir de l'aide",
"backup_php5_to_php7_migration_may_fail": "Impossible de convertir votre archive pour prendre en charge PHP 7, vous pourriez ne plus pouvoir restaurer vos applications PHP (cause : {error:s})",
"log_help_to_get_failed_log": "Lopération '{desc}' a échouée ! Pour avoir de laide, merci de partager le journal historisé de cette opération en utilisant la commande 'yunohost log display {name} --share'",
"log_does_exists": "Il nexiste pas de journal historisé de lopération ayant pour nom '{log}', utiliser 'yunohost log list pour voir tous les fichiers de journaux historisés disponibles'",
"log_operation_unit_unclosed_properly": "Lopération ne sest pas terminée correctement",
@ -462,13 +462,13 @@
"log_tools_shutdown": "Éteindre votre serveur",
"log_tools_reboot": "Redémarrer votre serveur",
"mail_unavailable": "Cette adresse de courriel est réservée et doit être automatiquement attribuée au tout premier utilisateur",
"migration_description_0004_php5_to_php7_pools": "Reconfiguration des groupes PHP pour utiliser PHP 7 au lieu de PHP 5",
"migration_description_0004_php5_to_php7_pools": "Reconfigurez les groupes PHP pour utiliser PHP 7 au lieu de PHP 5",
"migration_description_0005_postgresql_9p4_to_9p6": "Migration des bases de données de PostgreSQL 9.4 vers PostgreSQL 9.6",
"migration_0005_postgresql_94_not_installed": "PostgreSQL na pas été installé sur votre système. Rien à faire !",
"migration_0005_postgresql_96_not_installed": "PostgreSQL 9.4 a été trouvé et installé, mais pas PostgreSQL 9.6 !? Quelque chose détrange a dû arriver à votre système… :(",
"migration_0005_not_enough_space": "Il ny a pas assez despace libre de disponible sur {path} pour lancer maintenant la migration :(.",
"migration_0005_not_enough_space": "Laissez suffisamment d'espace disponible dans {chemin} pour exécuter la migration.",
"recommend_to_add_first_user": "La post-installation est terminée mais YunoHost a besoin dau moins un utilisateur pour fonctionner correctement. Vous devez en ajouter un en utilisant la commande 'yunohost user create $nomdutilisateur' ou bien via linterface dadministration web.",
"service_description_php7.0-fpm": "exécute des applications écrites en PHP avec Nginx",
"service_description_php7.0-fpm": "Exécute des applications écrites en PHP avec NGINX",
"users_available": "Liste des utilisateurs disponibles :",
"good_practices_about_admin_password": "Vous êtes maintenant sur le point de définir un nouveau mot de passe dadministration. Le mot de passe doit comporter au moins 8 caractères bien quil soit recommandé dutiliser un mot de passe plus long (cest-à-dire une phrase secrète) et/ou dutiliser différents types de caractères (majuscules, minuscules, chiffres et caractères spéciaux).",
"good_practices_about_user_password": "Vous êtes maintenant sur le point de définir un nouveau mot de passe utilisateur. Le mot de passe doit comporter au moins 8 caractères - bien quil soit recommandé dutiliser un mot de passe plus long (cest-à-dire une phrase secrète) et/ou dutiliser différents types de caractères tels que : majuscules, minuscules, chiffres et caractères spéciaux.",
@ -481,8 +481,8 @@
"password_too_simple_3": "Le mot de passe doit comporter au moins 8 caractères et contenir des chiffres, des majuscules, des minuscules et des caractères spéciaux",
"password_too_simple_4": "Le mot de passe doit comporter au moins 12 caractères et contenir des chiffres, des majuscules, des minuscules et des caractères spéciaux",
"root_password_desynchronized": "Le mot de passe administrateur a été changé, mais YunoHost na pas pu le propager au mot de passe root !",
"aborting": "Opération annulée.",
"app_not_upgraded": "Les applications suivantes n'ont pas été mises à jour : {apps}",
"aborting": "Annulation.",
"app_not_upgraded": "Lapplication {failed_app} na pas été mise à jour et par conséquence les applications suivantes nont pas été mises à jour : {apps}",
"app_start_install": "Installation de l'application {app} …",
"app_start_remove": "Suppression de l'application {app} …",
"app_start_backup": "Collecte des fichiers devant être sauvegardés pour {app} …",
@ -493,8 +493,8 @@
"backup_actually_backuping": "Création d'une archive de sauvegarde à partir des fichiers collectés …",
"backup_mount_archive_for_restore": "Préparation de l'archive pour restauration …",
"confirm_app_install_warning": "Avertissement : cette application peut fonctionner mais n'est pas bien intégrée dans YunoHost. Certaines fonctionnalités telles que l'authentification unique et la sauvegarde/restauration peuvent ne pas être disponibles. L'installer quand même ? [{answers:s}] ",
"confirm_app_install_danger": "AVERTISSEMENT ! Cette application est encore expérimentale (explicitement, elle ne fonctionne pas) et risque de casser votre système ! Vous ne devriez probablement PAS l'installer sans savoir ce que vous faites. Êtes-vous prêt à prendre ce risque ? [{answers:s}] ",
"confirm_app_install_thirdparty": "AVERTISSEMENT ! L'installation d'applications tierces peut compromettre l'intégrité et la sécurité de votre système. Vous ne devriez probablement PAS l'installer si vous ne savez pas ce que vous faites. Êtes-vous prêt à prendre ce risque ? [{answers:s}] ",
"confirm_app_install_danger": "DANGER! Cette application est connue pour être encore expérimentale (si elle ne fonctionne pas explicitement)! Vous ne devriez probablement PAS l'installer à moins de savoir ce que vous faites. AUCUN SUPPORT ne sera fourni si cette application ne fonctionne pas ou casse votre système ... Si vous êtes prêt à prendre ce risque de toute façon, tapez '{answers: s}'",
"confirm_app_install_thirdparty": "DANGER! Cette application ne fait pas partie du catalogue d'applications de Yunohost. L'installation d'applications tierces peut compromettre l'intégrité et la sécurité de votre système. Vous ne devriez probablement PAS l'installer à moins de savoir ce que vous faites. AUCUN SUPPORT ne sera fourni si cette application ne fonctionne pas ou casse votre système ... Si vous êtes prêt à prendre ce risque de toute façon, tapez '{answers:s}'",
"dpkg_is_broken": "Vous ne pouvez pas faire ça maintenant car dpkg/apt (le gestionnaire de paquets du système) semble avoir laissé des choses non configurées. Vous pouvez essayer de résoudre ce problème en vous connectant via SSH et en exécutant `sudo dpkg --configure -a'.",
"dyndns_could_not_check_available": "Impossible de vérifier si {domain:s} est disponible chez {provider:s}.",
"file_does_not_exist": "Le fichier dont le chemin est {path:s} n'existe pas.",
@ -507,9 +507,9 @@
"migration_0007_cancelled": "YunoHost n'a pas réussi à améliorer la façon dont est gérée votre configuration SSH.",
"migration_0007_cannot_restart": "SSH ne peut pas être redémarré après avoir essayé d'annuler la migration numéro 6.",
"migration_0008_general_disclaimer": "Pour améliorer la sécurité de votre serveur, il est recommandé de laisser YunoHost gérer la configuration SSH. Votre configuration SSH actuelle diffère de la configuration recommandée. Si vous laissez YunoHost la reconfigurer, la façon dont vous vous connectez à votre serveur via SSH changera comme suit :",
"migration_0008_port": " - vous devrez vous connecter en utilisant le port 22 au lieu de votre actuel port SSH personnalisé. N'hésitez pas à le reconfigurer ;",
"migration_0008_root": " - vous ne pourrez pas vous connecter en tant que root via SSH. Au lieu de cela, vous devrez utiliser l'utilisateur admin ;",
"migration_0008_dsa": " - la clé DSA sera désactivée. Par conséquent, il se peut que vous ayez besoin d'invalider un avertissement effrayant de votre client SSH afin de revérifier l'empreinte de votre serveur ;",
"migration_0008_port": "- Vous devrez vous connecter en utilisant le port 22 au lieu de votre actuel port SSH personnalisé. N'hésitez pas à le reconfigurer ;",
"migration_0008_root": "- Vous ne pourrez pas vous connecter en tant que root via SSH. Au lieu de cela, vous devrez utiliser l'utilisateur admin ;",
"migration_0008_dsa": "- La clé DSA sera désactivée. Par conséquent, il se peut que vous ayez besoin d'invalider un avertissement effrayant de votre client SSH afin de revérifier l'empreinte de votre serveur ;",
"migration_0008_warning": "Si vous comprenez ces avertissements et que vous acceptez de laisser YunoHost remplacer votre configuration actuelle, exécutez la migration. Sinon, vous pouvez également passer la migration, bien que cela ne soit pas recommandé.",
"migration_0008_no_warning": "Aucun risque majeur n'a été identifié concernant l'écrasement de votre configuration SSH - mais nous ne pouvons pas en être absolument sûrs ;) ! Si vous acceptez de laisser YunoHost remplacer votre configuration actuelle, exécutez la migration. Sinon, vous pouvez également passer la migration, bien que cela ne soit pas recommandé.",
"migrations_success": "Migration {number} {name} réussie !",
@ -524,7 +524,7 @@
"service_reloaded_or_restarted": "Le service '{service:s}' a été rechargé ou redémarré",
"this_action_broke_dpkg": "Cette action a laissé des paquets non configurés par dpkg/apt (les gestionnaires de paquets système). Vous pouvez essayer de résoudre ce problème en vous connectant via SSH et en exécutant `sudo dpkg --configure -a`.",
"app_action_cannot_be_ran_because_required_services_down": "Cette application requiert certains services qui sont actuellement arrêtés. Avant de continuer, vous devriez essayer de redémarrer les services suivants (et éventuellement rechercher pourquoi ils sont arrêtés) : {services}",
"admin_password_too_long": "Choisissez un mot de passe plus court que 127 caractères",
"admin_password_too_long": "Veuillez choisir un mot de passe de moins de 127 caractères",
"log_regen_conf": "Régénérer les configurations du système '{}'",
"migration_0009_not_needed": "Cette migration semble avoir déjà été jouée ? On l'ignore.",
"regenconf_file_backed_up": "Le fichier de configuration '{conf}' a été sauvegardé sous '{backup}'",
@ -549,36 +549,36 @@
"regenconf_failed": "Impossible de régénérer la configuration pour la ou les catégorie(s) : '{categories}'",
"regenconf_pending_applying": "Applique la configuration en attente pour la catégorie '{category}' …",
"service_regen_conf_is_deprecated": "'yunohost service regen-conf' est obsolète ! Veuillez plutôt utiliser 'yunohost tools regen-conf' à la place.",
"tools_upgrade_at_least_one": "Veuillez spécifier --apps OU --system",
"tools_upgrade_at_least_one": "Veuillez spécifier '--apps' OU '--system'",
"tools_upgrade_cant_both": "Impossible de mettre à niveau le système et les applications en même temps",
"tools_upgrade_cant_hold_critical_packages": "Impossibilité de maintenir les paquets critiques...",
"tools_upgrade_regular_packages": "Mise à jour des paquets du système (non liés a YunoHost) ...",
"tools_upgrade_cant_hold_critical_packages": "Impossibilité de maintenir les paquets critiques",
"tools_upgrade_regular_packages": "Mise à jour des paquets du système (non liés a YunoHost) ",
"tools_upgrade_regular_packages_failed": "Impossible de mettre à jour les paquets suivants : {packages_list}",
"tools_upgrade_special_packages": "Mise à jour des paquets 'spécifiques' (liés a YunoHost) ...",
"tools_upgrade_special_packages": "Mise à jour des paquets 'spécifiques' (liés a YunoHost) ",
"tools_upgrade_special_packages_completed": "La mise à jour des paquets de YunoHost est finie!\nPressez [Entrée] pour revenir à la ligne de commande",
"updating_app_lists": "Récupération des mises à jour des applications disponibles…",
"dpkg_lock_not_available": "Cette commande ne peut être lancée maintenant car il semblerai qu'un autre programme utilise déjà le verrou dpkg du gestionnaire de paquets du système",
"tools_upgrade_cant_unhold_critical_packages": "Impossible de dé-marquer les paquets critiques ...",
"dpkg_lock_not_available": "Cette commande ne peut être exécutée actuellement car un autre programme semble utiliser le verrou de dpkg (gestionnaire de paquets)",
"tools_upgrade_cant_unhold_critical_packages": "Impossible de dé-marquer les paquets critiques ",
"tools_upgrade_special_packages_explanation": "Cette opération prendra fin mais la mise à jour spécifique continuera en arrière-plan. Veuillez ne pas lancer d'autre action sur votre serveur dans les 10 prochaines minutes (en fonction de la vitesse de votre matériel). Une fois que c'est fait, vous devrez peut-être vous reconnecter sur le panel d'administration web. Le journal de la mise à jour sera disponible dans Outils > Log (dans le panel d'administration web) ou dans la liste des journaux YunoHost (en ligne de commande).",
"update_apt_cache_failed": "Impossible de mettre à jour le cache APT (gestionnaire de paquets Debian). Voici un extrait du fichier sources.list qui pourrait vous aider à identifier les lignes problématiques :\n{sourceslist}",
"update_apt_cache_warning": "Des erreurs se sont produites lors de la mise à jour du cache APT (gestionnaire de paquets Debian). Voici un extrait des lignes du fichier sources.list qui pourrait vous aider à identifier les lignes problématiques :\n{sourceslist}",
"apps_permission_not_found": "Aucune permission trouvée pour les applications installées",
"apps_permission_restoration_failed": "L'autorisation '{permission:s}' pour la restauration de l'application {app:s} a échoué",
"backup_permission": "Autorisation de sauvegarde pour l'application {app:s}",
"backup_permission": "Permission de sauvegarde pour l'application {app:s}",
"edit_group_not_allowed": "Vous n'êtes pas autorisé à modifier le groupe {group:s}",
"error_when_removing_sftpuser_group": "Erreur en essayant de supprimer le groupe sftpusers",
"group_created": "Le groupe '{group}' a créé avec succès",
"group_deleted": "Le groupe '{group}' a été supprimé",
"error_when_removing_sftpuser_group": "Vous ne pouvez pas supprimer le groupe sftpusers",
"group_created": "Le groupe '{group}' a été créé",
"group_deleted": "Suppression du groupe '{group}'",
"group_deletion_not_allowed": "Le groupe {group:s} ne peut pas être supprimé manuellement.",
"group_info_failed": "L'information sur le groupe a échoué",
"group_unknown": "Le groupe {group:s} est inconnu",
"group_updated": "Le groupe '{group}' a été mis à jour",
"group_update_failed": "La mise à jour du groupe '{group}' a échoué",
"group_update_failed": "La mise à jour du groupe '{group}' a échoué : {error}",
"group_already_allowed": "Le groupe '{group:s}' a déjà la permission '{permission:s}' activée pour l'application '{app:s}'",
"group_already_disallowed": "Le groupe '{group:s}' a déjà la permission '{permission:s}' désactivée pour l'application '{app:s}'",
"group_name_already_exist": "Le groupe {name:s} existe déjà",
"group_creation_failed": "Échec de la création du groupe '{group}'",
"group_deletion_failed": "Échec de la suppression du groupe '{group}'",
"group_creation_failed": "Échec de la création du groupe '{group}': {error}",
"group_deletion_failed": "Échec de la suppression du groupe '{group}': {error}",
"edit_permission_with_group_all_users_not_allowed": "Vous n'êtes pas autorisé à modifier les permissions pour le groupe 'all_users', utilisez 'yunohost user permission clear APP' ou 'yunohost user permission add APP -u USER' à la place.",
"log_permission_add": "Ajouter l'autorisation '{}' pour l'application '{}'",
"log_permission_remove": "Supprimer l'autorisation '{}'",
@ -588,5 +588,70 @@
"log_user_group_update": "Mettre à jour '{}' pour le groupe",
"log_user_permission_add": "Mettre à jour l'autorisation pour '{}'",
"log_user_permission_remove": "Mettre à jour l'autorisation pour '{}'",
"mailbox_disabled": "La boîte aux lettres est désactivée pour l'utilisateur {user:s}"
"mailbox_disabled": "La boîte aux lettres est désactivée pour l'utilisateur {user:s}",
"app_action_broke_system": "Cette action semble avoir cassé des services important : {services}",
"apps_already_up_to_date": "Toutes les applications sont déjà à jour",
"app_upgrade_stopped": "La mise à jour de toutes les applications a été arrêtée afin déviter déventuels dommages dus à léchec de la mise à jour de lapplication précédente",
"migration_0011_create_group": "Créer un groupe pour chaque utilisateur…",
"migration_0011_done": "Migration réussie. Vous êtes maintenant en mesure de gérer des groupes d'utilisateurs.",
"migrations_must_provide_explicit_targets": "Vous devez fournir des cibles explicites lorsque vous utilisez '--skip' ou '--force-rerun'",
"migrations_no_such_migration": "Il n'y a pas de migration appelée {id}",
"migrations_pending_cant_rerun": "Ces migrations étant toujours en attente, vous ne pouvez pas les exécuter à nouveau: {ids}",
"migration_description_0012_postgresql_password_to_md5_authentication": "Forcer l'authentification PostgreSQL à utiliser MD5 pour les connexions locales",
"migrations_exclusive_options": "'auto', '--skip' et '--force-rerun' sont des options mutuellement exclusives.",
"migrations_not_pending_cant_skip": "Ces migrations ne sont pas en attente et ne peuvent donc pas être ignorées: {ids}",
"migration_0011_can_not_backup_before_migration": "La sauvegarde du système avant la migration a échoué. La migration a échoué. Erreur: {error: s}",
"migration_0011_migrate_permission": "Migration des autorisations des paramètres des applications vers LDAP…",
"migration_0011_migration_failed_trying_to_rollback": "La migration a échoué… essayait de restauration du système.",
"migration_0011_rollback_success": "Système restauré.",
"migration_0011_update_LDAP_database": "Mise à jour de la base de données LDAP…",
"system_groupname_exists": "Le nom de groupe existe déjà dans le groupe du systèmes",
"tools_update_failed_to_app_fetchlist": "Impossible de mettre à jour les applications de YunoHost car: {error}",
"user_already_in_group": "L'utilisateur '{user:}' est déjà dans le groupe '{group: s}'",
"user_not_in_group": "L'utilisateur '{user: s}' ne fait pas partie du groupe {group: s}",
"migration_0011_backup_before_migration": "Création d'une sauvegarde des paramètres de la base de données LDAP et des applications avant la migration.",
"permission_not_found": "Autorisation '{permission:s}' introuvable",
"permission_name_not_valid": "Choisissez un nom d'autorisation autorisé pour '{permission: s}'",
"permission_update_failed": "Impossible de mettre à jour la permission '{permission}': {error}",
"permission_generated": "Base de données des autorisations mise à jour",
"permission_updated": "Permission '{permission:s}' mise à jour",
"permission_update_nothing_to_do": "Aucune autorisation pour mettre à jour",
"remove_main_permission_not_allowed": "Supprimer l'autorisation principale n'est pas autorisé",
"dyndns_provider_unreachable": "Impossible datteindre le fournisseur Dyndns {provider}: votre YunoHost nest pas correctement connecté à Internet ou le serveur Dynette est en panne.",
"migration_0011_update_LDAP_schema": "Mise à jour du schéma LDAP…",
"migrations_already_ran": "Ces migrations sont déjà effectuées: {ids}",
"migrations_dependencies_not_satisfied": "Impossible d'exécuter la migration {id} car vous devez d'abord exécuter ces migrations: {dependencies_id}",
"migrations_failed_to_load_migration": "Impossible de charger la migration {id}: {error}",
"migrations_running_forward": "Exécution de la migration {id}…",
"migrations_success_forward": "Migration {id} terminée",
"need_define_permission_before": "Redéfinissez l'autorisation à l'aide de 'yunohost user permission add -u USER' avant de supprimer un groupe autorisé",
"operation_interrupted": "L'opération a été interrompue manuellement",
"permission_already_clear": "L'autorisation '{permission: s}' est déjà vide pour l'application {app: s}",
"permission_already_exist": "L'autorisation '{permission}' existe déjà",
"permission_created": "Permission '{permission:s}' créée",
"permission_creation_failed": "Impossible de créer l'autorisation '{permission}': {erreur}",
"permission_deleted": "Permission '{permission:s}' supprimée",
"permission_deletion_failed": "Impossible de supprimer la permission '{permission}': {error}",
"remove_user_of_group_not_allowed": "Vous n'êtes pas autorisé à supprimer l'utilisateur '{utilisateur: s}' dans le groupe '{groupe: s}'",
"migration_description_0011_setup_group_permission": "Configurer le groupe d'utilisateurs et configurer les autorisations pour les applications et les services",
"migration_0011_LDAP_config_dirty": "Il semble que vous ayez personnalisé votre configuration LDAP. Pour cette migration, la configuration LDAP doit être mise à jour.\nVous devez enregistrer votre configuration actuelle, réintialiser la configuration d'origine en exécutant 'yunohost tools regen-conf -f', puis réessayer la migration",
"migration_0011_LDAP_update_failed": "Impossible de mettre à jour LDAP. Erreur: {error: s}",
"group_already_exist": "Le groupe {group} existe déjà",
"group_already_exist_on_system": "Le groupe {group} existe déjà dans les groupes système",
"group_cannot_be_edited": "Le groupe {group} ne peut pas être édité manuellement.",
"group_cannot_be_deleted": "Le groupe {group} ne peut pas être supprimé manuellement.",
"group_user_already_in_group": "L'utilisateur {user} est déjà dans le groupe {group}",
"group_user_not_in_group": "L'utilisateur {user} n'est pas dans le groupe {group}",
"log_permission_create": "Créer permission '{}'",
"log_permission_delete": "supprimer permission '{}'",
"log_permission_urls": "Mettre à jour les URL liées à la permission '{}'",
"log_user_group_create": "Créer '{}' groupe",
"log_user_permission_update": "Mise à jour des accès pour la permission '{}'",
"log_user_permission_reset": "Réinitialiser la permission '{}'",
"migration_0011_failed_to_remove_stale_object": "Impossible de supprimer un objet obsolète {dn}: {error}",
"permission_already_allowed": "Le groupe '{group}' a déjà l'autorisation '{permission}' activée '",
"permission_already_disallowed": "Le groupe '{group}' a déjà l'autorisation '{permission}' désactivé '",
"permission_cannot_remove_main": "Supprimer une autorisation principale n'est pas autorisé",
"user_already_exists": "L'utilisateur {user} existe déjà",
"app_full_domain_unavailable": "Désolé, cette application nécessite l'installation d'un domaine complet, mais d'autres applications sont déjà installées sur le domaine '{domain}'. Une solution possible consiste à ajouter et à utiliser un sous-domaine dédié à cette application."
}

View file

@ -1 +1,169 @@
{}
{
"aborting": "Avbryter…",
"admin_password": "Administrasjonspassord",
"admin_password_change_failed": "Kan ikke endre passord",
"admin_password_changed": "Administrasjonspassord endret",
"admin_password_too_long": "Velg et passord kortere enn 127 tegn",
"app_already_installed": "{app:s} er allerede installert",
"app_already_up_to_date": "{app:s} er allerede oppdatert",
"app_argument_invalid": "Velg en gydlig verdi for argumentet '{name:s}': {error:s}",
"app_argument_required": "Argumentet '{name:s}' er påkrevd",
"diagnosis_no_apps": "Inget program installert",
"app_id_invalid": "Ugyldig program-ID",
"dyndns_cron_remove_failed": "Kunne ikke fjerne cron-jobb for DynDNS: {error}",
"dyndns_key_not_found": "Fant ikke DNS-nøkkel for domenet",
"app_not_correctly_installed": "{app:s} ser ikke ut til å ha blitt installert på riktig måte",
"dyndns_provider_unreachable": "Kunne ikke nå DynDNS-tilbyder {provider}: Enten har du ikke satt opp din YunoHost rett, dynette-tjeneren er nede, eller du mangler nett.",
"app_not_properly_removed": "{app:s} har ikke blitt fjernet på riktig måte",
"app_package_need_update": "Programmet {app}-pakken må oppdateres for å holde følge med YunoHost sine endringer",
"app_removed": "{app:s} fjernet",
"app_requirements_checking": "Sjekker påkrevde pakker for {app:s}…",
"app_requirements_failed": "Noen krav er ikke oppfylt for {app:s}: {error}",
"app_start_install": "Installerer programmet '{app}'…",
"action_invalid": "Ugyldig handling '{action:s}'",
"app_start_restore": "Gjenoppretter programmet '{app}'…",
"backup_created": "Sikkerhetskopi opprettet",
"backup_archive_name_exists": "En sikkerhetskopi med dette navnet finnes allerede.",
"backup_archive_name_unknown": "Ukjent lokalt sikkerhetskopiarkiv ved navn '{name:s}'",
"already_up_to_date": "Ingenting å gjøre. Alt er oppdatert.",
"backup_method_copy_finished": "Sikkerhetskopi fullført",
"backup_method_tar_finished": "TAR-sikkerhetskopiarkiv opprettet",
"app_action_cannot_be_ran_because_required_services_down": "Dette programmet krever noen tjenester som ikke kjører. Før du fortsetter, du bør prøve å starte følgende tjenester på ny (og antagelig undersøke hvorfor de er nede): {services}",
"app_already_installed_cant_change_url": "Dette programmet er allerede installert. Nettadressen kan ikke endres kun med denne funksjonen. Ta en titt på `app changeurl` hvis den er tilgjengelig.",
"diagnosis_monitor_disk_error": "Kunne ikke holde oppsyn med disker: {error}",
"diagnosis_monitor_system_error": "Kunne ikke holde oppsyn med systemet: {error}",
"domain_exists": "Domenet finnes allerede",
"app_change_url_failed_nginx_reload": "Kunne ikke gjeninnlaste NGINX. Her har du utdataen for 'nginx -t'\n{nginx_errors:s}",
"domains_available": "Tilgjengelige domener:",
"done": "Ferdig",
"downloading": "Laster ned…",
"dyndns_could_not_check_provide": "Kunne ikke sjekke om {provider:s} kan tilby {domain:s}.",
"dyndns_could_not_check_available": "Kunne ikke sjekke om {domain:s} er tilgjengelig på {provider:s}.",
"log_app_removeaccess": "Fjern tilgang til '{}'",
"license_undefined": "udefinert",
"mail_domain_unknown": "Ukjent e-postadresse for domenet '{domain:s}'",
"migrate_tsig_wait_2": "2 min…",
"log_remove_on_failed_restore": "Fjern '{}' etter mislykket gjenoppretting fra sikkerhetskopiarkiv",
"log_letsencrypt_cert_install": "Installer et Let's Encrypt-sertifikat på '{}'-domenet",
"log_permission_update": "Oppdater tilgang '{}' for programmet '{}'",
"log_letsencrypt_cert_renew": "Forny '{}'-Let's Encrypt-sertifikat",
"log_user_update": "Oppdater brukerinfo for '{}'",
"mail_alias_remove_failed": "Kunne ikke fjerne e-postaliaset '{mail:s}'",
"app_action_broke_system": "Denne handlingen ser ut til å ha knekt disse viktige tjenestene: {services}",
"app_argument_choice_invalid": "Bruk én av disse valgene '{choices:s}' for argumentet '{name:s}'",
"app_extraction_failed": "Kunne ikke pakke ut installasjonsfilene",
"app_incompatible": "Programmet {app} er ikke kompatibelt med din YunoHost-versjon",
"app_install_files_invalid": "Disse filene kan ikke installeres",
"app_location_already_used": "Programmet '{app}' er allerede installert i ({path})",
"ask_path": "Sti",
"backup_abstract_method": "Denne sikkerhetskopimetoden er ikke implementert enda",
"backup_actually_backuping": "Oppretter sikkerhetskopiarkiv fra innsamlede filer…",
"backup_app_failed": "Kunne ikke sikkerhetskopiere programmet '{app:s}'",
"backup_applying_method_tar": "Lager TAR-sikkerhetskopiarkiv…",
"backup_archive_app_not_found": "Fant ikke programmet '{app:s}' i sikkerhetskopiarkivet",
"backup_archive_open_failed": "Kunne ikke åpne sikkerhetskopiarkivet",
"app_start_remove": "Fjerner programmet '{app}'…",
"app_start_backup": "Samler inn filer for sikkerhetskopiering for {app}…",
"backup_applying_method_copy": "Kopier alle filer til sikkerhetskopi…",
"backup_borg_not_implemented": "Borg-sikkerhetskopimetoden er ikke implementert enda",
"backup_creation_failed": "Kunne ikke opprette sikkerhetskopiarkiv",
"backup_couldnt_bind": "Kunne ikke binde {src:s} til {dest:s}.",
"backup_csv_addition_failed": "Kunne ikke legge til filer for sikkerhetskopi inn i CSV-filen",
"backup_deleted": "Sikkerhetskopi slettet",
"backup_no_uncompress_archive_dir": "Det finnes ingen slik utpakket arkivmappe",
"backup_delete_error": "Kunne ikke slette '{path:s}'",
"certmanager_domain_unknown": "Ukjent domene '{domain:s}'",
"certmanager_cert_signing_failed": "Kunne ikke signere det nye sertifikatet",
"diagnosis_debian_version_error": "Kunne ikke hente Debian-versjon: {error}",
"diagnosis_kernel_version_error": "Kunne ikke hente kjerneversjon: {error}",
"error_when_removing_sftpuser_group": "Kunne ikke fjerne sftpusers-gruppen",
"executing_command": "Kjører kommendoen '{command:s}'…",
"executing_script": "Kjører skriptet '{script:s}'…",
"extracting": "Pakker ut…",
"edit_group_not_allowed": "Du tillates ikke å redigere gruppen {group:s}",
"log_domain_add": "Legg til '{}'-domenet i systemoppsett",
"log_domain_remove": "Fjern '{}'-domenet fra systemoppsett",
"log_dyndns_subscribe": "Abonner på YunoHost-underdomenet '{}'",
"log_dyndns_update": "Oppdater IP-adressen tilknyttet ditt YunoHost-underdomene '{}'",
"migrate_tsig_wait_3": "1 min…",
"migrate_tsig_wait_4": "30 sekunder…",
"apps_permission_restoration_failed": "Innvilg tilgangen '{permission:s}' for å gjenopprette {app:}",
"apps_permission_not_found": "Fant ingen tilgang for de installerte programmene",
"backup_invalid_archive": "Dette er ikke et sikkerhetskopiarkiv",
"backup_nothings_done": "Ingenting å lagre",
"backup_method_borg_finished": "Sikkerhetskopi inn i Borg fullført",
"field_invalid": "Ugyldig felt '{:s}'",
"firewall_reloaded": "Brannmur gjeninnlastet",
"log_app_removelist": "Fjern en programliste",
"log_app_change_url": "Endre nettadresse for '{}'-programmet",
"log_app_install": "Installer '{}'-programmet",
"log_app_remove": "Fjern '{}'-programmet",
"log_app_upgrade": "Oppgrader '{}'-programmet",
"log_app_makedefault": "Gjør '{}' til forvalgt program",
"log_available_on_yunopaste": "Denne loggen er nå tilgjengelig via {url}",
"log_tools_maindomain": "Gjør '{}' til hoveddomene",
"log_tools_shutdown": "Slå av tjeneren din",
"log_tools_reboot": "Utfør omstart av tjeneren din",
"apps_already_up_to_date": "Alle programmer allerede oppdatert",
"backup_mount_archive_for_restore": "Forbereder arkiv for gjenopprettelse…",
"backup_copying_to_organize_the_archive": "Kopierer {size:s} MB for å organisere arkivet",
"domain_cannot_remove_main": "Kan ikke fjerne hoveddomene. Sett et først",
"domain_cert_gen_failed": "Kunne ikke opprette sertifikat",
"domain_created": "Domene opprettet",
"domain_creation_failed": "Kunne ikke opprette domene",
"domain_dyndns_root_unknown": "Ukjent DynDNS-rotdomene",
"domain_unknown": "Ukjent domene",
"dyndns_cron_installed": "Opprettet cron-jobb for DynDNS",
"dyndns_cron_removed": "Fjernet cron-jobb for DynDNS",
"dyndns_ip_update_failed": "Kunne ikke oppdatere IP-adresse til DynDNS",
"dyndns_ip_updated": "Oppdaterte din IP på DynDNS",
"dyndns_key_generating": "Oppretter DNS-nøkkel… Dette kan ta en stund.",
"dyndns_no_domain_registered": "Inget domene registrert med DynDNS",
"dyndns_registered": "DynDNS-domene registrert",
"global_settings_setting_security_password_admin_strength": "Admin-passordets styrke",
"dyndns_registration_failed": "Kunne ikke registrere DynDNS-domene: {error:s}",
"global_settings_setting_security_password_user_strength": "Brukerpassordets styrke",
"log_app_fetchlist": "Legg til en programliste",
"log_backup_restore_app": "Gjenopprett '{}' fra sikkerhetskopiarkiv",
"log_remove_on_failed_install": "Fjern '{}' etter mislykket installasjon",
"log_permission_add": "Legg til '{}'-tilgangen for programmet '{}'",
"log_permission_remove": "Fjern tilgangen '{}'",
"log_selfsigned_cert_install": "Installer selvsignert sertifikat på '{}'-domenet",
"log_user_delete": "Slett '{}' bruker",
"log_user_group_add": "Legg til '{}' gruppe",
"log_user_group_delete": "Slett '{}' gruppe",
"log_user_group_update": "Oppdater '{}' gruppe",
"log_user_permission_add": "Oppdater '{}' tilgang",
"log_user_permission_remove": "Oppdater '{}' tilgang",
"ldap_init_failed_to_create_admin": "LDAP-igangsettelse kunne ikke opprette admin-bruker",
"ldap_initialized": "LDAP-igangsatt",
"maindomain_changed": "Hoveddomenet er nå endret",
"migration_description_0003_migrate_to_stretch": "Oppgrader systemet til Debian Stretch og YunoHost 3.0",
"app_unknown": "Ukjent program",
"app_upgrade_app_name": "Oppgraderer {app}…",
"app_upgrade_failed": "Kunne ikke oppgradere {app:s}",
"app_upgrade_some_app_failed": "Noen programmer kunne ikke oppgraderes",
"app_upgraded": "{app:s} oppgradert",
"ask_email": "E-postadresse",
"ask_firstname": "Fornavn",
"ask_lastname": "Etternavn",
"ask_list_to_remove": "Liste å fjerne",
"ask_main_domain": "Hoveddomene",
"ask_new_admin_password": "Nytt administrasjonspassord",
"app_upgrade_several_apps": "Følgende programmer vil oppgraderes: {apps}",
"appslist_removed": "{appslist:s}-programliste fjernet",
"appslist_url_already_tracked": "Dette er allerede en registrert programliste med nettadressen {url:s}.",
"ask_current_admin_password": "Nåværende administrasjonspassord",
"appslist_unknown": "Programlisten {appslist:s} er ukjent.",
"ask_new_domain": "Nytt domene",
"ask_new_path": "Ny sti",
"ask_password": "Passord",
"domain_deleted": "Domene slettet",
"domain_deletion_failed": "Kunne ikke slette domene",
"domain_dyndns_already_subscribed": "Du har allerede abonnement på et DynDNS-domene",
"log_category_404": "Loggkategorien '{category}' finnes ikke",
"log_link_to_log": "Full logg for denne operasjonen: '<a href=\"#/tools/logs/{name}\" style=\"text-decoration:underline\">{desc}</a>'",
"log_help_to_get_log": "For å vise loggen for operasjonen '{desc}', bruk kommandoen 'yunohost log display {name}'",
"log_app_clearaccess": "Fjern all tilgang til '{}'",
"log_user_create": "Legg til '{}' bruker"
}

View file

@ -406,10 +406,10 @@ def app_map(app=None, raw=False, user=None):
"""
from yunohost.permission import user_permission_list
from yunohost.utils.ldap import _get_ldap_interface
apps = []
result = {}
permissions = user_permission_list(full=True)["permissions"]
if app is not None:
if not _is_installed(app):
@ -429,12 +429,8 @@ def app_map(app=None, raw=False, user=None):
continue
if 'no_sso' in app_settings: # I don't think we need to check for the value here
continue
if user is not None:
ldap = _get_ldap_interface()
if not ldap.search(base='ou=permission,dc=yunohost,dc=org',
filter='(&(objectclass=permissionYnh)(cn=main.%s)(inheritPermission=uid=%s,ou=users,dc=yunohost,dc=org))' % (app_id, user),
attrs=['cn']):
continue
if user and user not in permissions[app_id + ".main"]["corresponding_users"]:
continue
domain = app_settings['domain']
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.domain import _normalize_domain_path, _get_conflicting_apps
from yunohost.permission import permission_update
from yunohost.permission import permission_urls
installed = _is_installed(app)
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, '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
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
"""
from yunohost.utils.ldap import _get_ldap_interface
from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback
from yunohost.log import OperationLogger
from yunohost.permission import permission_add, permission_update, permission_remove, permission_sync_to_user
ldap = _get_ldap_interface()
from yunohost.permission import user_permission_list, permission_create, permission_urls, permission_delete, permission_sync_to_user
# Fetch or extract sources
if not os.path.exists(INSTALL_TMP):
@ -789,20 +783,41 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu
if confirm is None or force or msettings.get('interface') == 'api':
return
answer = msignals.prompt(m18n.n('confirm_app_install_' + confirm,
answers='Y/N'))
if answer.upper() != "Y":
raise YunohostError("aborting")
if confirm in ["danger", "thirdparty"]:
answer = msignals.prompt(m18n.n('confirm_app_install_' + confirm,
answers='Yes, I understand'),
color="red")
if answer != "Yes, I understand":
raise YunohostError("aborting")
else:
answer = msignals.prompt(m18n.n('confirm_app_install_' + confirm,
answers='Y/N'),
color="yellow")
if answer.upper() != "Y":
raise YunohostError("aborting")
raw_app_list = app_list(raw=True)
if app in raw_app_list or ('@' in app) or ('http://' in app) or ('https://' in app):
# If we got an app name directly (e.g. just "wordpress"), we gonna test this name
if app in raw_app_list:
state = raw_app_list[app].get("state", "notworking")
level = raw_app_list[app].get("level", None)
app_name_to_test = app
# If we got an url like "https://github.com/foo/bar_ynh, we want to
# extract "bar" and test if we know this app
elif ('http://' in app) or ('https://' in app):
app_name_to_test = app.strip("/").split("/")[-1].replace("_ynh","")
if app_name_to_test in raw_app_list:
state = raw_app_list[app_name_to_test].get("state", "notworking")
level = raw_app_list[app_name_to_test].get("level", None)
confirm = "danger"
if state in ["working", "validated"]:
if isinstance(level, int) and level >= 3:
if isinstance(level, int) and level >= 5:
confirm = None
elif isinstance(level, int) and level > 0:
confirm = "warning"
@ -847,6 +862,9 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu
args_list = [ value[0] for value in args_odict.values() ]
args_list.append(app_instance_name)
# Validate domain / path availability for webapps
_validate_and_normalize_webpath(manifest, args_odict, extracted_app_folder)
# Prepare env. var. to pass to script
env_dict = _make_environment_dict(args_odict)
env_dict["YNH_APP_ID"] = app_id
@ -905,7 +923,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)
# Note that sync_perm is disabled to avoid triggering a whole bunch of code and messages
# can't be sure that we don't have one case when it's needed
permission_add(app=app_instance_name, permission="main", sync_perm=False)
permission_create(app_instance_name+".main", sync_perm=False)
# Execute the app install script
install_failed = True
@ -977,11 +995,9 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu
logger.exception(m18n.n('unexpected_error', error=u"\n" + traceback.format_exc()))
# Remove all permission in LDAP
result = ldap.search(base='ou=permission,dc=yunohost,dc=org',
filter='(&(objectclass=permissionYnh)(cn=*.%s))' % app_instance_name, attrs=['cn'])
permission_list = [p['cn'][0] for p in result]
for l in permission_list:
permission_remove(app_instance_name, l.split('.')[0], force=True)
for permission_name in user_permission_list()["permissions"].keys():
if permission_name.startswith(app_instance_name+"."):
permission_delete(permission_name, force=True)
if remove_retcode != 0:
msg = m18n.n('app_not_properly_removed',
@ -1025,8 +1041,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu
domain = app_settings.get('domain', None)
path = app_settings.get('path', None)
if domain and path:
permission_update(app_instance_name, permission="main", add_url=[domain+path], sync_perm=False)
permission_urls(app_instance_name+".main", add=[domain+path], sync_perm=False)
permission_sync_to_user()
logger.success(m18n.n('installation_complete'))
@ -1043,9 +1058,8 @@ def app_remove(operation_logger, app):
app -- App(s) to delete
"""
from yunohost.utils.ldap import _get_ldap_interface
from yunohost.hook import hook_exec, hook_remove, hook_callback
from yunohost.permission import permission_remove, permission_sync_to_user
from yunohost.permission import user_permission_list, permission_delete, permission_sync_to_user
if not _is_installed(app):
raise YunohostError('app_not_installed', app=app, all_apps=_get_all_installed_apps_id())
@ -1106,19 +1120,15 @@ def app_remove(operation_logger, app):
hook_remove(app)
# Remove all permission in LDAP
ldap = _get_ldap_interface()
result = ldap.search(base='ou=permission,dc=yunohost,dc=org',
filter='(&(objectclass=permissionYnh)(cn=*.%s))' % app, attrs=['cn'])
permission_list = [p['cn'][0] for p in result]
for l in permission_list:
permission_remove(app, l.split('.')[0], force=True, sync_perm=False)
for permission_name in user_permission_list()["permissions"].keys():
if permission_name.startswith(app+"."):
permission_delete(permission_name, force=True, sync_perm=False)
permission_sync_to_user()
_assert_system_is_sane_for_app(manifest, "post")
@is_unit_operation(['permission','app'])
def app_addaccess(operation_logger, apps, users=[]):
def app_addaccess(apps, users=[]):
"""
Grant access right to users (everyone by default)
@ -1129,15 +1139,15 @@ def app_addaccess(operation_logger, apps, users=[]):
"""
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': result}
return {'allowed_users': output}
@is_unit_operation(['permission','app'])
def app_removeaccess(operation_logger, apps, users=[]):
def app_removeaccess(apps, users=[]):
"""
Revoke access right to users (everyone by default)
@ -1148,15 +1158,15 @@ def app_removeaccess(operation_logger, apps, users=[]):
"""
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': result}
return {'allowed_users': output}
@is_unit_operation(['permission','app'])
def app_clearaccess(operation_logger, apps):
def app_clearaccess(apps):
"""
Reset access rights for the app
@ -1164,13 +1174,15 @@ def app_clearaccess(operation_logger, 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):
"""
@ -1492,12 +1504,10 @@ def app_ssowatconf():
skipped_regex.append("^[^/]*/%.well%-known/acme%-challenge/.*$")
skipped_regex.append("^[^/]*/%.well%-known/autoconfig/mail/config%-v1%.1%.xml.*$")
permission = {}
for a in user_permission_list()['permissions'].values():
for p in a.values():
if 'URL' in p:
for u in p['URL']:
permission[u] = p['allowed_users']
permissions_per_url = {}
for permission_name, permission_infos in user_permission_list(full=True)['permissions'].items():
for url in permission_infos["urls"]:
permissions_per_url[url] = permission_infos['corresponding_users']
conf_dict = {
'portal_domain': main_domain,
@ -1519,7 +1529,7 @@ def app_ssowatconf():
'redirected_regex': redirected_regex,
'users': {username: app_map(user=username)
for username in user_list()['users'].keys()},
'permission': permission,
'permissions': permissions_per_url,
}
with open('/etc/ssowat/conf.json', 'w+') as f:
@ -2584,8 +2594,7 @@ def _parse_args_for_action(action, args={}):
def _parse_args_in_yunohost_format(args, action_args):
"""Parse arguments store in either manifest.json or actions.json
"""
from yunohost.domain import (domain_list, _get_maindomain,
_get_conflicting_apps, _normalize_domain_path)
from yunohost.domain import domain_list, _get_maindomain
from yunohost.user import user_info, user_list
args_dict = OrderedDict()
@ -2703,13 +2712,18 @@ def _parse_args_in_yunohost_format(args, action_args):
assert_password_is_strong_enough('user', arg_value)
args_dict[arg_name] = (arg_value, arg_type)
# END loop over action_args...
return args_dict
def _validate_and_normalize_webpath(manifest, args_dict, app_folder):
from yunohost.domain import _get_conflicting_apps, _normalize_domain_path
# If there's only one "domain" and "path", validate that domain/path
# is an available url and normalize the path.
domain_args = [ (name, value[0]) for name, value in args_dict.items() if value[1] == "domain" ]
path_args = [ (name, value[0]) for name, value in args_dict.items() if value[1] == "path" ]
domain_args = [(name, value[0]) for name, value in args_dict.items() if value[1] == "domain"]
path_args = [(name, value[0]) for name, value in args_dict.items() if value[1] == "path"]
if len(domain_args) == 1 and len(path_args) == 1:
@ -2735,7 +2749,25 @@ def _parse_args_in_yunohost_format(args, action_args):
# standard path format to deal with no matter what the user inputted)
args_dict[path_args[0][0]] = (path, "path")
return args_dict
# This is likely to be a full-domain app...
elif len(domain_args) == 1 and len(path_args) == 0:
# Confirm that this is a full-domain app This should cover most cases
# ... though anyway the proper solution is to implement some mechanism
# in the manifest for app to declare that they require a full domain
# (among other thing) so that we can dynamically check/display this
# requirement on the webadmin form and not miserably fail at submit time
# Full-domain apps typically declare something like path_url="/" or path=/
# and use ynh_webpath_register or yunohost_app_checkurl inside the install script
install_script_content = open(os.path.join(app_folder, 'scripts/install')).read()
if re.search(r"\npath(_url)?=[\"']?/[\"']?\n", install_script_content) \
and re.search(r"(ynh_webpath_register|yunohost app checkurl)", install_script_content):
domain = domain_args[0][1]
if _get_conflicting_apps(domain, "/"):
raise YunohostError('app_full_domain_unavailable', domain=domain)
def _make_environment_dict(args_dict, prefix="APP_ARG_"):

View file

@ -40,7 +40,7 @@ from moulinette import msignals, m18n
from yunohost.utils.error import YunohostError
from moulinette.utils import filesystem
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 (
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
execution failed
"""
from yunohost.permission import user_permission_list
app_setting_path = os.path.join('/etc/yunohost/apps/', app)
# Prepare environment
@ -704,8 +706,9 @@ class BackupManager():
# backup permissions
logger.debug(m18n.n('backup_permission', app=app))
ldap_url = "ldap:///dc=yunohost,dc=org???(&(objectClass=permissionYnh)(cn=*.%s))" % app
os.system("slapcat -b dc=yunohost,dc=org -H '%s' -l '%s/permission.ldif'" % (ldap_url, settings_dir))
permissions = user_permission_list(full=True)["permissions"]
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:
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"])
permission_sync_to_user(force=False)
permission_sync_to_user()
if os.path.ismount(self.work_dir):
ret = subprocess.call(["umount", self.work_dir])
@ -1131,6 +1134,8 @@ class RestoreManager():
self._restore_system()
self._restore_apps()
except Exception as e:
raise YunohostError("The following critical error happened during restoration: %s" % e)
finally:
self.clean()
@ -1183,18 +1188,12 @@ class RestoreManager():
if system_targets == []:
return
from yunohost.utils.ldap import _get_ldap_interface
ldap = _get_ldap_interface()
from yunohost.user import user_group_list
from yunohost.permission import permission_create, permission_delete, user_permission_update, user_permission_list
# Backup old permission for apps
# We need to do that because in case of an app is installed we can't remove the permission for this app
old_apps_permission = []
try:
old_apps_permission = ldap.search('ou=permission,dc=yunohost,dc=org',
'(&(objectClass=permissionYnh)(!(cn=main.mail))(!(cn=main.metronome))(!(cn=main.sftp)))',
['cn', 'objectClass', 'groupPermission', 'URL', 'gidNumber'])
except:
logger.info(m18n.n('apps_permission_not_found'))
old_apps_permission = user_permission_list(ignore_system_perms=True, full=True)["permissions"]
# Start register change on system
operation_logger = OperationLogger('backup_restore_system')
@ -1232,12 +1231,11 @@ class RestoreManager():
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
result = ldap.search('ou=groups,dc=yunohost,dc=org',
'(&(objectclass=groupOfNamesYnh)(cn=all_users))',
['cn'])
if not result:
if not "all_users" in user_group_list()["groups"].keys():
from yunohost.tools import _get_migration_by_name
setup_group_permission = _get_migration_by_name("setup_group_permission")
# Update LDAP schema restart slapd
@ -1245,25 +1243,16 @@ class RestoreManager():
regen_conf(names=['slapd'], force=True)
setup_group_permission.migrate_LDAP_db()
# Remove all permission for all app which sill in the LDAP
for per in ldap.search('ou=permission,dc=yunohost,dc=org',
'(&(objectClass=permissionYnh)(!(cn=main.mail))(!(cn=main.metronome))(!(cn=main.sftp)))',
['cn']):
if not ldap.remove('cn=%s,ou=permission' % per['cn'][0]):
raise YunohostError('permission_deletion_failed',
permission=per['cn'][0].split('.')[0],
app=per['cn'][0].split('.')[1])
# Remove all permission for all app which is still in the LDAP
for permission_name in user_permission_list(ignore_system_perms=True)["permissions"].keys():
permission_delete(permission_name, force=True)
# Restore permission for the app which is installed
for per in old_apps_permission:
try:
permission_name, app_name = per['cn'][0].split('.')
except:
logger.warning(m18n.n('permission_name_not_valid', permission=per['cn'][0]))
for permission_name, permission_infos in old_apps_permission.items():
app_name = permission_name.split(".")[0]
if _is_installed(app_name):
if not ldap.add('cn=%s,ou=permission' % per['cn'][0], per):
raise YunohostError('apps_permission_restoration_failed', permission=permission_name, app=app_name)
permission_create(permission_name, urls=permission_infos["urls"], sync_perm=False)
user_permission_update(permission_name, remove="all_users", add=permission_infos["allowed"])
def _restore_apps(self):
"""Restore all apps targeted"""
@ -1271,7 +1260,6 @@ class RestoreManager():
apps_targets = self.targets.list("apps", exclude=["Skipped"])
for app in apps_targets:
print(app)
self._restore_app(app)
def _restore_app(self, app_instance_name):
@ -1301,11 +1289,8 @@ class RestoreManager():
name already exists
restore_app_failed -- Raised if the restore bash script failed
"""
from moulinette.utils.filesystem import read_ldif
from yunohost.user import user_group_list
from yunohost.permission import permission_remove
from yunohost.utils.ldap import _get_ldap_interface
ldap = _get_ldap_interface()
from yunohost.permission import permission_create, permission_delete, user_permission_list, user_permission_update
def copytree(src, dst, symlinks=False, ignore=None):
for item in os.listdir(src):
@ -1370,22 +1355,27 @@ class RestoreManager():
restore_script = os.path.join(tmp_folder_for_app_restore, 'restore')
# Restore permissions
if os.path.isfile(app_settings_in_archive + '/permission.ldif'):
filtred_entries = ['entryUUID', 'creatorsName', 'createTimestamp', 'entryCSN', 'structuralObjectClass',
'modifiersName', 'modifyTimestamp', 'inheritPermission', 'memberUid']
entries = read_ldif('%s/permission.ldif' % app_settings_in_archive, filtred_entries)
group_list = user_group_list(['cn'])['groups']
for dn, entry in entries:
# Remove the group which has been removed
for group in entry['groupPermission']:
group_name = group.split(',')[0].split('=')[1]
if group_name not in group_list:
entry['groupPermission'].remove(group)
if not ldap.add('cn=%s,ou=permission' % entry['cn'][0], entry):
raise YunohostError('apps_permission_restoration_failed',
permission=entry['cn'][0].split('.')[0],
app=entry['cn'][0].split('.')[1])
if os.path.isfile('%s/permissions.yml' % app_settings_new_path):
permissions = read_yaml('%s/permissions.yml' % app_settings_new_path)
existing_groups = user_group_list()['groups']
for permission_name, permission_infos in permissions.items():
permission_create(permission_name, urls=permission_infos.get("urls", []))
if "allowed" not in permission_infos:
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))
else:
should_be_allowed = [g for g in permission_infos["allowed"] if g in existing_groups]
current_allowed = user_permission_list()["permissions"][permission_name]["allowed"]
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:
# Otherwise, we need to migrate the legacy permissions of this
# app (included in its settings.yml)
from yunohost.tools import _get_migration_by_name
setup_group_permission = _get_migration_by_name("setup_group_permission")
setup_group_permission.migrate_app_permission(app=app_instance_name)
@ -1424,7 +1414,6 @@ class RestoreManager():
operation_logger.start()
# Execute remove script
# TODO: call app_remove instead
if hook_exec(remove_script, args=[app_instance_name],
env=env_dict_remove)[0] != 0:
msg = m18n.n('app_not_properly_removed', app=app_instance_name)
@ -1436,12 +1425,10 @@ class RestoreManager():
# Cleaning app directory
shutil.rmtree(app_settings_new_path, ignore_errors=True)
# Remove all permission in LDAP
result = ldap.search(base='ou=permission,dc=yunohost,dc=org',
filter='(&(objectclass=permissionYnh)(cn=*.%s))' % app_instance_name, attrs=['cn'])
permission_list = [p['cn'][0] for p in result]
for l in permission_list:
permission_remove(app_instance_name, l.split('.')[0], force=True)
# Remove all permission in LDAP for this app
for permission_name in user_permission_list()["permissions"].keys():
if permission_name.startswith(app_instance_name+"."):
permission_delete(permission_name, force=True)
# TODO Cleaning app hooks
else:

View file

@ -1,17 +1,16 @@
import yaml
import time
import os
from moulinette import m18n
from yunohost.utils.error import YunohostError
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import read_yaml
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.regenconf import regen_conf
from yunohost.permission import permission_add, permission_sync_to_user
from yunohost.user import user_permission_add
from yunohost.permission import permission_create, user_permission_update, permission_sync_to_user
logger = getActionLogger('yunohost.migration')
@ -19,6 +18,7 @@ logger = getActionLogger('yunohost.migration')
# Tools used also for restoration
###################################################
class MyMigration(Migration):
"""
Update the LDAP DB to be able to store the permission
@ -28,6 +28,28 @@ class MyMigration(Migration):
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):
logger.info(m18n.n("migration_0011_update_LDAP_database"))
@ -35,15 +57,13 @@ class MyMigration(Migration):
from yunohost.utils.ldap import _get_ldap_interface
ldap = _get_ldap_interface()
try:
ldap.remove('cn=sftpusers,ou=groups')
except:
logger.warn(m18n.n("error_when_removing_sftpuser_group"))
with open('/usr/share/yunohost/yunohost-config/moulinette/ldap_scheme.yml') as f:
ldap_map = yaml.load(f)
ldap_map = read_yaml('/usr/share/yunohost/yunohost-config/moulinette/ldap_scheme.yml')
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']
ldap.add('ou=permission', attr_dict)
@ -65,10 +85,8 @@ class MyMigration(Migration):
username = user_info['uid'][0]
ldap.update('uid=%s,ou=users' % username,
{'objectClass': ['mailAccount', 'inetOrgPerson', 'posixAccount', 'userPermissionYnh']})
user_group_add(username, gid=user_info['uidNumber'][0], sync_perm=False)
user_group_update(groupname=username, add_user=username, force=True, sync_perm=False)
user_group_update(groupname='all_users', add_user=username, force=True, sync_perm=False)
user_group_create(username, gid=user_info['uidNumber'][0], primary_group=True, sync_perm=False)
user_group_update(groupname='all_users', add=username, force=True, sync_perm=False)
def migrate_app_permission(self, app=None):
logger.info(m18n.n("migration_0011_migrate_permission"))
@ -85,14 +103,18 @@ class MyMigration(Migration):
domain = app_setting(app, 'domain')
urls = [domain + path] if domain and path else None
permission_add(app, permission='main', urls=urls, default_allow=True, sync_perm=False)
permission_create(app+".main", urls=urls, sync_perm=False)
if permission:
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)
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
ldap_regen_conf_status = regen_conf(names=['slapd'], dry_run=True)
# By this we check if the have been customized

View file

@ -112,8 +112,10 @@ def domain_add(operation_logger, domain, dyndns=False):
'virtualdomain': domain,
}
if not ldap.add('virtualdomain=%s,ou=domains' % domain, attr_dict):
raise YunohostError('domain_creation_failed')
try:
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
if os.path.exists('/etc/yunohost/installed'):
@ -167,10 +169,12 @@ def domain_remove(operation_logger, domain, force=False):
operation_logger.start()
ldap = _get_ldap_interface()
if ldap.remove('virtualdomain=' + domain + ',ou=domains') or force:
os.system('rm -rf /etc/yunohost/certs/%s' % domain)
else:
raise YunohostError('domain_deletion_failed')
try:
ldap.remove('virtualdomain=' + domain + ',ou=domains')
except Exception as e:
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'])
app_ssowatconf()

View file

@ -212,7 +212,7 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None,
from yunohost.tools import _get_migration_by_name
migration = _get_migration_by_name("migrate_to_tsig_sha256")
try:
migration.migrate(dyn_host, domain, key)
migration.run(dyn_host, domain, key)
except Exception as e:
logger.error(m18n.n('migrations_migration_has_failed',
exception=e,

View file

@ -44,7 +44,7 @@ CATEGORIES = ['operation', 'history', 'package', 'system', 'access', 'service',
'app']
METADATA_FILE_EXT = '.yml'
LOG_FILE_EXT = '.log'
RELATED_CATEGORIES = ['app', 'domain', 'service', 'user']
RELATED_CATEGORIES = ['app', 'domain', 'group', 'service', 'user']
logger = getActionLogger('yunohost.log')
@ -213,7 +213,7 @@ def log_display(path, number=None, share=False):
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):
"""
Configure quickly a unit operation

View file

@ -24,6 +24,7 @@
Manage permissions
"""
import copy
import grp
import random
@ -35,309 +36,247 @@ from yunohost.log import is_unit_operation
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
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
List permissions and corresponding accesses
"""
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()
permissions_infos = ldap.search('ou=permission,dc=yunohost,dc=org',
'(objectclass=permissionYnh)',
["cn", 'groupPermission', 'inheritPermission', 'URL'])
permission_attrs = [
'cn',
'groupPermission',
'inheritPermission',
'URL',
]
# Normally app is alway defined but it should be possible to set it
if app and not isinstance(app, list):
app = [app]
if permission and not isinstance(permission, list):
permission = [permission]
if not isinstance(username, list):
username = [username]
if not isinstance(group, list):
group = [group]
# Parse / organize information to be outputed
permissions = {}
for infos in permissions_infos:
result = ldap.search('ou=permission,dc=yunohost,dc=org',
'(objectclass=permissionYnh)', permission_attrs)
name = infos['cn'][0]
for res in result:
try:
permission_name, app_name = res['cn'][0].split('.')
except:
logger.warning(m18n.n('permission_name_not_valid', permission=res['cn'][0]))
group_name = []
if 'groupPermission' in res:
for g in res['groupPermission']:
group_name.append(g.split("=")[1].split(",")[0])
user_name = []
if 'inheritPermission' in res:
for u in res['inheritPermission']:
user_name.append(u.split("=")[1].split(",")[0])
# Don't show the result if the user defined a specific permission, user or group
if app and app_name not in app:
continue
if permission and permission_name not in permission:
continue
if username[0] and not set(username) & set(user_name):
continue
if group[0] and not set(group) & set(group_name):
if ignore_system_perms and name.split(".")[0] in SYSTEM_PERMS:
continue
if app_name not in permissions:
permissions[app_name] = {}
permissions[name] = {}
permissions[name]["allowed"] = [_ldap_path_extract(p, "cn") for p in infos.get('groupPermission', [])]
permissions[app_name][permission_name] = {'allowed_users': [], 'allowed_groups': []}
for g in group_name:
permissions[app_name][permission_name]['allowed_groups'].append(g)
for u in user_name:
permissions[app_name][permission_name]['allowed_users'].append(u)
if 'URL' in res:
permissions[app_name][permission_name]['URL'] = []
for u in res['URL']:
permissions[app_name][permission_name]['URL'].append(u)
if full:
permissions[name]["corresponding_users"] = [_ldap_path_extract(p, "uid") for p in infos.get('inheritPermission', [])]
permissions[name]["urls"] = infos.get("URL", [])
if short:
permissions = permissions.keys()
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
Keyword argument:
app -- an application OR sftp, xmpp (metronome), mail
permission -- name of the permission ("main" by default)
add_username -- Username to allow
add_group -- Groupname to allow
del_username -- Username to disallow
del_group -- Groupname to disallow
permission -- Name of the permission (e.g. mail or or wordpress or wordpress.editors)
add -- List of groups or usernames to add to this permission
remove -- List of groups or usernames to remove from to this permission
"""
from yunohost.hook import hook_callback
from yunohost.user import user_group_list
from yunohost.utils.ldap import _get_ldap_interface
ldap = _get_ldap_interface()
if permission:
if not isinstance(permission, list):
permission = [permission]
else:
permission = ["main"]
# By default, manipulate main permission
if "." not in permission:
permission = permission + ".main"
if add_group:
if not isinstance(add_group, list):
add_group = [add_group]
else:
add_group = []
# Fetch currently allowed groups for this permission
if add_username:
if not isinstance(add_username, list):
add_username = [add_username]
else:
add_username = []
existing_permission = user_permission_list(full=True)["permissions"].get(permission, None)
if existing_permission is None:
raise YunohostError('permission_not_found', permission=permission)
if del_group:
if not isinstance(del_group, list):
del_group = [del_group]
else:
del_group = []
current_allowed_groups = existing_permission["allowed"]
all_existing_groups = user_group_list()['groups'].keys()
operation_logger.related_to.append(('app', permission.split(".")[0]))
if del_username:
if not isinstance(del_username, list):
del_username = [del_username]
else:
del_username = []
# Compute new allowed group list (and make sure what we're doing make sense)
# Validate that the group exist
for g in add_group:
if g not in user_group_list(['cn'])['groups']:
raise YunohostError('group_unknown', group=g)
for u in add_username:
if u not in user_list(['uid'])['users']:
raise YunohostError('user_unknown', user=u)
for g in del_group:
if g not in user_group_list(['cn'])['groups']:
raise YunohostError('group_unknown', group=g)
for u in del_username:
if u not in user_list(['uid'])['users']:
raise YunohostError('user_unknown', user=u)
new_allowed_groups = copy.copy(current_allowed_groups)
# Merge user and group (note that we consider all user as a group)
add_group.extend(add_username)
del_group.extend(del_username)
if add:
groups_to_add = [add] if not isinstance(add, list) else add
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:
raise YunohostError('edit_permission_with_group_all_users_not_allowed')
new_allowed_groups += groups_to_add
# Populate permission informations
permission_attrs = [
'cn',
'groupPermission',
]
result = ldap.search('ou=permission,dc=yunohost,dc=org',
'(objectclass=permissionYnh)', permission_attrs)
result = {p['cn'][0]: p for p in result}
if remove:
groups_to_remove = [remove] if not isinstance(remove, list) else remove
for group in groups_to_remove:
if group not in all_existing_groups:
raise YunohostError('group_unknown', group=group)
if group not in current_allowed_groups:
logger.warning(m18n.n('permission_already_disallowed', permission=permission, group=group))
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:
for per in permission:
permission_name = per + '.' + a
if permission_name not in result:
raise YunohostError('permission_not_found', permission=per, app=a)
new_per_dict[permission_name] = set()
if 'groupPermission' in result[permission_name]:
new_per_dict[permission_name] = set(result[permission_name]['groupPermission'])
# If we end up with something like allowed groups is ["all_users", "volunteers"]
# we shall warn the users that they should probably choose between one or the other,
# because the current situation is probably not what they expect / is temporary ?
for g in del_group:
if 'cn=all_users,ou=groups,dc=yunohost,dc=org' in new_per_dict[permission_name]:
raise YunohostError('need_define_permission_before')
group_name = 'cn=' + g + ',ou=groups,dc=yunohost,dc=org'
if group_name not in new_per_dict[permission_name]:
logger.warning(m18n.n('group_already_disallowed', permission=per, app=a, group=g))
else:
new_per_dict[permission_name].remove(group_name)
if len(new_allowed_groups) > 1 and "all_users" in new_allowed_groups:
# FIXME : i18n
# FIXME : write a better explanation ?
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 'cn=all_users,ou=groups,dc=yunohost,dc=org' in new_per_dict[permission_name]:
new_per_dict[permission_name].remove('cn=all_users,ou=groups,dc=yunohost,dc=org')
for g in add_group:
group_name = 'cn=' + g + ',ou=groups,dc=yunohost,dc=org'
if group_name in new_per_dict[permission_name]:
logger.warning(m18n.n('group_already_allowed', permission=per, app=a, group=g))
else:
new_per_dict[permission_name].add(group_name)
# Don't update LDAP if we update exactly the same values
if set(new_allowed_groups) == set(current_allowed_groups):
# FIXME : i18n
logger.warning("The permission was not updated all addition/removal requests already match the current state.")
return
# Commit the new allowed group list
operation_logger.start()
for per, val in new_per_dict.items():
# Don't update LDAP if we update exactly the same values
if val == set(result[per]['groupPermission'] if 'groupPermission' in result[per] else []):
continue
if ldap.update('cn=%s,ou=permission' % per, {'groupPermission': val}):
p = per.split('.')
logger.debug(m18n.n('permission_updated', permission=p[0], app=p[1]))
else:
raise YunohostError('permission_update_failed')
try:
ldap.update('cn=%s,ou=permission' % permission,
{'groupPermission': ['cn=' + g + ',ou=groups,dc=yunohost,dc=org' for g in new_allowed_groups]})
except Exception as e:
raise YunohostError('permission_update_failed', permission=permission, error=e)
logger.debug(m18n.n('permission_updated', permission=permission))
# Trigger permission sync if asked
if sync_perm:
permission_sync_to_user()
for a in app:
allowed_users = set()
disallowed_users = set()
group_list = user_group_list(['member'])['groups']
new_permission = user_permission_list(full=True)["permissions"][permission]
for g in add_group:
if 'members' in group_list[g]:
allowed_users.union(group_list[g]['members'])
for g in del_group:
if 'members' in group_list[g]:
disallowed_users.union(group_list[g]['members'])
# Trigger app callbacks
allowed_users = ','.join(allowed_users)
disallowed_users = ','.join(disallowed_users)
if add_group:
hook_callback('post_app_addaccess', args=[app, allowed_users])
if del_group:
hook_callback('post_app_removeaccess', args=[app, disallowed_users])
app = permission.split(".")[0]
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:
app -- an application OR sftp, xmpp (metronome), mail
permission -- name of the permission ("main" by default)
username -- Username to get informations (all by default)
group -- Groupname to get informations (all by default)
permission -- Name of the permission (e.g. mail or nextcloud or wordpress.editors)
"""
from yunohost.hook import hook_callback
from yunohost.utils.ldap import _get_ldap_interface
ldap = _get_ldap_interface()
if permission:
if not isinstance(permission, list):
permission = [permission]
else:
permission = ["main"]
# By default, manipulate main permission
if "." not in permission:
permission = 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']}
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
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}
logger.debug(m18n.n('permission_updated', permission=permission))
for a in app:
for per in permission:
permission_name = per + '.' + a
if permission_name not in result:
raise YunohostError('permission_not_found', permission=per, app=a)
if 'groupPermission' in result[permission_name] and 'cn=all_users,ou=groups,dc=yunohost,dc=org' in result[permission_name]['groupPermission']:
logger.warning(m18n.n('permission_already_clear', permission=per, app=a))
continue
if ldap.update('cn=%s,ou=permission' % permission_name, default_permission):
logger.debug(m18n.n('permission_updated', permission=per, app=a))
else:
raise YunohostError('permission_update_failed')
if sync_perm:
permission_sync_to_user()
permission_sync_to_user()
new_permission = user_permission_list(full=True)["permissions"][permission]
for a in app:
permission_name = 'main.' + a
result = ldap.search('ou=permission,dc=yunohost,dc=org',
filter='cn=' + permission_name, attrs=['inheritPermission'])
if result:
allowed_users = result[0]['inheritPermission']
new_user_list = ','.join(allowed_users)
hook_callback('post_app_removeaccess', args=[app, new_user_list])
# Trigger app callbacks
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'])
def permission_add(operation_logger, app, permission, urls=None, default_allow=True, sync_perm=True):
@is_unit_operation()
def permission_create(operation_logger, permission, urls=None, sync_perm=True):
"""
Create a new permission for a specific application
Keyword argument:
app -- an application OR sftp, xmpp (metronome), mail
permission -- name of the permission ("main" by default)
permission -- Name of the permission (e.g. mail or nextcloud or wordpress.editors)
urls -- list of urls to specify for the permission
"""
from yunohost.domain import _normalize_domain_path
from yunohost.utils.ldap import _get_ldap_interface
ldap = _get_ldap_interface()
# By default, manipulate main permission
if "." not in permission:
permission = permission + ".main"
# Validate uniqueness of permission in LDAP
permission_name = str(permission + '.' + app) # str(...) Fix encoding issue
conflict = ldap.get_conflict({
'cn': permission_name
}, base_dn='ou=permission,dc=yunohost,dc=org')
if conflict:
raise YunohostError('permission_already_exist', permission=permission, app=app)
if ldap.get_conflict({'cn': permission},
base_dn='ou=permission,dc=yunohost,dc=org'):
raise YunohostError('permission_already_exist', permission=permission)
# Get random GID
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 = {
'objectClass': ['top', 'permissionYnh', 'posixGroup'],
'cn': permission_name,
'cn': str(permission),
'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:
attr_dict['URL'] = []
for url in urls:
domain = url[:url.index('/')]
path = url[url.index('/'):]
domain, path = _normalize_domain_path(domain, path)
attr_dict['URL'].append(domain + path)
attr_dict['URL'] = [_normalize_url(url) for url in urls]
operation_logger.related_to.append(('app', permission.split(".")[0]))
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:
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
user<->group link and the group<->permission link
Keyword argument:
force -- Force to recreate all attributes. Used generally with the
backup which uses "slapadd" which doesnt' use the memberOf overlay.
Note that by removing all value and adding a new time, we force the
overlay to update all attributes
"""
# Note that a LDAP operation with the same value that is in LDAP crash SLAP.
# So we need to check before each ldap operation that we really change something in LDAP
import os
from yunohost.app import app_ssowatconf
from yunohost.user import user_group_list
from yunohost.utils.ldap import _get_ldap_interface
ldap = _get_ldap_interface()
permission_attrs = [
'cn',
'member',
]
group_info = ldap.search('ou=groups,dc=yunohost,dc=org',
'(objectclass=groupOfNamesYnh)', permission_attrs)
group_info = {g['cn'][0]: g for g in group_info}
groups = user_group_list(full=True)["groups"]
permissions = user_permission_list(full=True)["permissions"]
for per in ldap.search('ou=permission,dc=yunohost,dc=org',
'(objectclass=permissionYnh)',
['cn', 'inheritPermission', 'groupPermission', 'memberUid']):
for permission_name, permission_infos in permissions.items():
if 'groupPermission' not in per:
per['groupPermission'] = []
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)
# These are the users currently allowed because there's an 'inheritPermission' object corresponding to it
currently_allowed_users = set(permission_infos["corresponding_users"])
if 'inheritPermission' not in per:
per['inheritPermission'] = []
if 'memberUid' not in per:
per['memberUid'] = []
# These are the users that should be allowed because they are member of a group that is allowed for this permission ...
should_be_allowed_users = set([user for group in permission_infos["allowed"] for user in groups[group]["members"]])
uid_val = [v.split("=")[1].split(",")[0] for v in user_permission]
if user_permission == set(per['inheritPermission']) and set(uid_val) == set(per['memberUid']) and not force:
# 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
if currently_allowed_users == should_be_allowed_users:
# We're all good, this permission is already correctly synchronized !
continue
inheritPermission = {'inheritPermission': user_permission, 'memberUid': uid_val}
if force:
if per['groupPermission']:
if not ldap.update('cn=%s,ou=permission' % per['cn'][0], {'groupPermission': []}):
raise YunohostError('permission_update_failed_clear')
if not ldap.update('cn=%s,ou=permission' % per['cn'][0], {'groupPermission': per['groupPermission']}):
raise YunohostError('permission_update_failed_populate')
if per['inheritPermission']:
if not ldap.update('cn=%s,ou=permission' % per['cn'][0], {'inheritPermission': []}):
raise YunohostError('permission_update_failed_clear')
if user_permission:
if not ldap.update('cn=%s,ou=permission' % per['cn'][0], inheritPermission):
raise YunohostError('permission_update_failed')
else:
if not ldap.update('cn=%s,ou=permission' % per['cn'][0], inheritPermission):
raise YunohostError('permission_update_failed')
logger.debug(m18n.n('permission_generated'))
new_inherited_perms = {'inheritPermission': ["uid=%s,ou=users,dc=yunohost,dc=org" % u for u in should_be_allowed_users],
'memberUid': should_be_allowed_users}
# Commit the change with the new inherited stuff
try:
ldap.update('cn=%s,ou=permission' % permission_name, new_inherited_perms)
except Exception as e:
raise YunohostError('permission_update_failed', permission=permission_name, error=e)
logger.debug("The permission database has been resynchronized")
app_ssowatconf()
# Reload unscd, otherwise the group ain't propagated to the LDAP database
os.system('nscd --invalidate=passwd')
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

View file

@ -70,7 +70,7 @@ def regen_conf(operation_logger, names=[], with_diff=False, force=False, dry_run
or not os.path.exists(REGEN_CONF_FILE)):
from yunohost.tools import _get_migration_by_name
migration = _get_migration_by_name("decouple_regenconf_from_services")
migration.migrate()
migration.run()
result = {}

View file

@ -36,16 +36,22 @@ def clean():
if _is_installed("legacy_app"):
app_remove("legacy_app")
if _is_installed("full_domain_app"):
app_remove("full_domain_app")
to_remove = []
to_remove += glob.glob("/etc/nginx/conf.d/*.d/*legacy*")
to_remove += glob.glob("/etc/nginx/conf.d/*.d/*full_domain*")
to_remove += glob.glob("/etc/nginx/conf.d/*.d/*break_yo_system*")
for filepath in to_remove:
os.remove(filepath)
to_remove = []
to_remove += glob.glob("/etc/yunohost/apps/*legacy_app*")
to_remove += glob.glob("/etc/yunohost/apps/*full_domain_app*")
to_remove += glob.glob("/etc/yunohost/apps/*break_yo_system*")
to_remove += glob.glob("/var/www/*legacy*")
to_remove += glob.glob("/var/www/*full_domain*")
for folderpath in to_remove:
shutil.rmtree(folderpath, ignore_errors=True)
@ -120,6 +126,13 @@ def install_legacy_app(domain, path):
force=True)
def install_full_domain_app(domain):
app_install("./tests/apps/full_domain_app_ynh",
args="domain=%s" % domain,
force=True)
def install_break_yo_system(domain, breakwhat):
app_install("./tests/apps/break_yo_system_ynh",
@ -272,6 +285,22 @@ def test_legacy_app_failed_remove(secondary_domain):
assert app_is_not_installed(secondary_domain, "legacy")
def test_full_domain_app(secondary_domain):
install_full_domain_app(secondary_domain)
assert app_is_exposed_on_http(secondary_domain, "/", "This is a dummy app")
def test_full_domain_app_with_conflicts(secondary_domain):
install_legacy_app(secondary_domain, "/legacy")
# TODO : once #808 is merged, add test that the message raised is 'app_full_domain_unavailable'
with pytest.raises(YunohostError):
install_full_domain_app(secondary_domain)
def test_systemfuckedup_during_app_install(secondary_domain):
with pytest.raises(YunohostError):

View file

@ -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.domain import _get_maindomain
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
# Get main domain
@ -38,10 +38,10 @@ def setup_function(function):
add_archive_wordpress_from_2p4()
assert len(backup_list()["archives"]) == 1
if "with_backup_legacy_app_installed" in markers:
assert not app_is_installed("backup_legacy_app")
install_app("backup_legacy_app_ynh", "/yolo")
assert app_is_installed("backup_legacy_app")
if "with_legacy_app_installed" in markers:
assert not app_is_installed("legacy_app")
install_app("legacy_app_ynh", "/yolo")
assert app_is_installed("legacy_app")
if "with_backup_recommended_app_installed" in markers:
assert not app_is_installed("backup_recommended_app")
@ -59,6 +59,13 @@ def setup_function(function):
add_archive_system_from_2p4()
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):
@ -73,6 +80,9 @@ def teardown_function(function):
if "clean_opt_dir" in markers:
shutil.rmtree("/opt/test_backup_output_directory")
if "alice" in user_list()["users"]:
user_delete("alice")
@pytest.fixture(autouse=True)
def check_LDAP_db_integrity_call():
@ -92,6 +102,9 @@ def check_permission_for_apps_call():
def app_is_installed(app):
if app == "permissions_app":
return _is_installed(app)
# These are files we know should be installed by the app
app_files = []
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)
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")
return True
@ -155,14 +168,9 @@ def delete_all_backups():
def uninstall_test_apps_if_needed():
if _is_installed("backup_legacy_app"):
app_remove("backup_legacy_app")
if _is_installed("backup_recommended_app"):
app_remove("backup_recommended_app")
if _is_installed("wordpress"):
app_remove("wordpress")
for app in ["legacy_app", "backup_recommended_app", "wordpress", "permissions_app"]:
if _is_installed(app):
app_remove(app)
def install_app(app, path, additionnal_args=""):
@ -497,10 +505,10 @@ def test_restore_app_already_installed(mocker):
assert _is_installed("wordpress")
@pytest.mark.with_backup_legacy_app_installed
@pytest.mark.with_legacy_app_installed
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
@ -514,6 +522,35 @@ def test_backup_and_restore_with_ynh_restore():
_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):
@ -531,7 +568,7 @@ def _test_backup_and_restore_app(app):
# Uninstall the app
app_remove(app)
assert not app_is_installed(app)
assert app not in user_permission_list()['permissions']
assert app+".main" not in user_permission_list()['permissions']
# Restore the app
backup_restore(system=None, name=archives[0],
@ -541,8 +578,7 @@ def _test_backup_and_restore_app(app):
# Check permission
per_list = user_permission_list()['permissions']
assert app in per_list
assert "main" in per_list[app]
assert app+".main" in per_list
#
# Some edge cases #

View file

@ -1,9 +1,11 @@
import pytest
from moulinette.core import MoulinetteError
from yunohost.app import app_install, app_remove, app_change_url, app_list
from yunohost.user import user_list, user_create, user_permission_list, user_delete, user_group_list, user_group_delete, user_permission_add, user_permission_remove, user_permission_clear
from yunohost.permission import permission_add, permission_update, permission_remove
from yunohost.app import app_install, app_remove, app_change_url, app_list, app_map
from yunohost.user import user_list, user_info, user_create, user_delete, user_update, \
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.utils.error import YunohostError
@ -18,20 +20,18 @@ def clean_user_groups_permission():
if g != "all_users":
user_group_delete(g)
for a, per in user_permission_list()['permissions'].items():
if a in ['wiki', 'blog', 'site']:
for p in per:
permission_remove(a, p, force=True, sync_perm=False)
for p in user_permission_list()['permissions']:
if any(p.startswith(name) for name in ["wiki", "blog", "site", "permissions_app"]):
permission_delete(p, force=True, sync_perm=False)
def setup_function(function):
clean_user_groups_permission()
user_create("alice", "Alice", "White", "alice@" + maindomain, "test123Ynh")
user_create("bob", "Bob", "Snow", "bob@" + maindomain, "test123Ynh")
permission_add("wiki", "main", [maindomain + "/wiki"], sync_perm=False)
permission_add("blog", "main", sync_perm=False)
user_permission_add(["blog"], "main", group="alice")
permission_create("wiki.main", urls=[maindomain + "/wiki"], sync_perm=False)
permission_create("blog.main", sync_perm=False)
user_permission_update("blog.main", remove="all_users", add="alice")
def teardown_function(function):
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.
# 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()
user_search = ldap.search('ou=users,dc=yunohost,dc=org',
@ -76,161 +76,155 @@ def check_LDAP_db_integrity():
for user in user_search:
user_dn = 'uid=' + user['uid'][0] + ',ou=users,dc=yunohost,dc=org'
group_list = [m.split("=")[1].split(",")[0] for m in user['memberOf']]
permission_list = []
if 'permission' in user:
permission_list = [m.split("=")[1].split(",")[0] for m in user['permission']]
group_list = [_ldap_path_extract(m, "cn") for m in user['memberOf']]
permission_list = [_ldap_path_extract(m, "cn") for m in user.get('permission', [])]
# This user's DN sould be found in all groups it is a member of
for group in group_list:
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:
assert user_dn in permission_map[permission]['inheritPermission']
for permission in permission_search:
permission_dn = 'cn=' + permission['cn'][0] + ',ou=permission,dc=yunohost,dc=org'
user_list = []
group_list = []
if 'inheritPermission' in permission:
user_list = [m.split("=")[1].split(",")[0] for m in permission['inheritPermission']]
assert set(user_list) == set(permission['memberUid'])
if 'groupPermission' in permission:
group_list = [m.split("=")[1].split(",")[0] for m in permission['groupPermission']]
# 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:
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:
assert permission_dn in group_map[group]['permission']
if 'member' in group_map[group]:
user_list_in_group = [m.split("=")[1].split(",")[0] for m in group_map[group]['member']]
assert set(user_list_in_group) <= set(user_list)
# The list of user in the group should be a subset of all users related to the current permission
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:
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:
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:
assert group_dn in permission_map[permission]['groupPermission']
if 'inheritPermission' in permission_map:
allowed_user_list = [m.split("=")[1].split(",")[0] for m in permission_map[permission]['inheritPermission']]
assert set(user_list) <= set(allowed_user_list)
# And the list of user of this group (user_list) should be a subset of all allowed users for this perm...
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():
# We check that the for each installed apps we have at last the "main" permission
# and we don't have any permission linked to no apps. The only exception who is not liked to an app
# is mail, metronome, and sftp
# is mail, xmpp, and sftp
from yunohost.utils.ldap import _get_ldap_interface
ldap = _get_ldap_interface()
permission_search = ldap.search('ou=permission,dc=yunohost,dc=org',
'(objectclass=permissionYnh)',
['cn', 'groupPermission', 'inheritPermission', 'memberUid'])
app_perms = user_permission_list(ignore_system_perms=True)["permissions"].keys()
# Keep only the prefix so that
# ["foo.main", "foo.pwet", "bar.main"]
# 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']}
permission_list_set = {permission['cn'][0].split(".")[1] for permission in permission_search}
extra_service_permission = set(['mail', 'metronome'])
if 'sftp' in permission_list_set:
extra_service_permission.add('sftp')
assert installed_apps == permission_list_set - extra_service_permission
assert installed_apps == app_perms_prefix
#
# List functions
#
def test_list_permission():
res = user_permission_list()['permissions']
def test_permission_list():
res = user_permission_list(full=True)['permissions']
assert "wiki" in res
assert "main" in res['wiki']
assert "blog" in res
assert "main" in res['blog']
assert "mail" in res
assert "main" in res['mail']
assert "metronome" in res
assert "main" in res['metronome']
assert ["all_users"] == res['wiki']['main']['allowed_groups']
assert ["alice"] == res['blog']['main']['allowed_groups']
assert set(["alice", "bob"]) == set(res['wiki']['main']['allowed_users'])
assert ["alice"] == res['blog']['main']['allowed_users']
assert [maindomain + "/wiki"] == res['wiki']['main']['URL']
assert "wiki.main" in res
assert "blog.main" in res
assert "mail.main" in res
assert "xmpp.main" in res
assert res['wiki.main']['allowed'] == ["all_users"]
assert res['blog.main']['allowed'] == ["alice"]
assert set(res['wiki.main']['corresponding_users']) == set(["alice", "bob"])
assert res['blog.main']['corresponding_users'] == ["alice"]
assert res['wiki.main']['urls'] == [maindomain + "/wiki"]
#
# Create - Remove functions
#
def test_add_permission_1():
permission_add("site", "test")
def test_permission_create_main():
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']
assert "site" in res
assert "test" in res['site']
assert "all_users" in res['site']['test']['allowed_groups']
assert set(["alice", "bob"]) == set(res['site']['test']['allowed_users'])
def test_add_permission_2():
permission_add("site", "main", default_allow=False)
res = user_permission_list()['permissions']
assert "site" in res
assert "main" in res['site']
assert [] == res['site']['main']['allowed_groups']
assert [] == res['site']['main']['allowed_users']
def test_remove_permission():
permission_remove("wiki", "main", force=True)
res = user_permission_list()['permissions']
assert "wiki" not in res
assert "wiki.main" not in res
#
# Error on create - remove function
#
def test_add_bad_permission():
# Create permission with same name
def test_permission_create_already_existing():
with pytest.raises(YunohostError):
permission_add("wiki", "main")
permission_create("wiki.main")
def test_remove_bad_permission():
# Remove not existant permission
with pytest.raises(MoulinetteError):
permission_remove("non_exit", "main", force=True)
def test_permission_delete_doesnt_existing():
with pytest.raises(YunohostError):
permission_delete("doesnt.exist", force=True)
res = user_permission_list()['permissions']
assert "wiki" in res
assert "main" in res['wiki']
assert "blog" in res
assert "main" in res['blog']
assert "mail" in res
assert "main" in res ['mail']
assert "metronome" in res
assert "main" in res['metronome']
assert "wiki.main" in res
assert "blog.main" in res
assert "mail.main" in res
assert "xmpp.main" in res
def test_remove_main_permission():
def test_permission_delete_main_without_force():
with pytest.raises(YunohostError):
permission_remove("blog", "main")
permission_delete("blog.main")
res = user_permission_list()['permissions']
assert "mail" in res
assert "main" in res['mail']
assert "blog.main" in res
#
# Update functions
@ -238,182 +232,154 @@ def test_remove_main_permission():
# user side functions
def test_allow_first_group():
# Remove permission to all_users and define per users
user_permission_add(["wiki"], "main", group="alice")
def test_permission_add_group():
user_permission_update("wiki.main", add="alice")
res = user_permission_list()['permissions']
assert ['alice'] == res['wiki']['main']['allowed_users']
assert ['alice'] == res['wiki']['main']['allowed_groups']
res = user_permission_list(full=True)['permissions']
assert set(res['wiki.main']['allowed']) == set(["all_users", "alice"])
assert set(res['wiki.main']['corresponding_users']) == set(["alice", "bob"])
def test_allow_other_group():
# Allow new user in a permission
user_permission_add(["blog"], "main", group="bob")
def test_permission_remove_group():
user_permission_update("blog.main", remove="alice")
res = user_permission_list()['permissions']
assert set(["alice", "bob"]) == set(res['blog']['main']['allowed_users'])
assert set(["alice", "bob"]) == set(res['blog']['main']['allowed_groups'])
res = user_permission_list(full=True)['permissions']
assert res['blog.main']['allowed'] == []
assert res['blog.main']['corresponding_users'] == []
def test_disallow_group_1():
# Disallow a user in a permission
user_permission_remove(["blog"], "main", group="alice")
def test_permission_add_and_remove_group():
user_permission_update("wiki.main", add="alice", remove="all_users")
res = user_permission_list()['permissions']
assert [] == res['blog']['main']['allowed_users']
assert [] == res['blog']['main']['allowed_groups']
res = user_permission_list(full=True)['permissions']
assert res['wiki.main']['allowed'] == ["alice"]
assert res['wiki.main']['corresponding_users'] == ["alice"]
def test_allow_group_1():
# Allow a user when he is already allowed
user_permission_add(["blog"], "main", group="alice")
def test_permission_add_group_already_allowed():
user_permission_update("blog.main", add="alice")
res = user_permission_list()['permissions']
assert ["alice"] == res['blog']['main']['allowed_users']
assert ["alice"] == res['blog']['main']['allowed_groups']
res = user_permission_list(full=True)['permissions']
assert res['blog.main']['allowed'] == ["alice"]
assert res['blog.main']['corresponding_users'] == ["alice"]
def test_disallow_group_1():
# Disallow a user when he is already disallowed
user_permission_remove(["blog"], "main", group="bob")
def test_permission_remove_group_already_not_allowed():
user_permission_update("blog.main", remove="bob")
res = user_permission_list()['permissions']
assert ["alice"] == res['blog']['main']['allowed_users']
assert ["alice"] == res['blog']['main']['allowed_groups']
res = user_permission_list(full=True)['permissions']
assert res['blog.main']['allowed'] == ["alice"]
assert res['blog.main']['corresponding_users'] == ["alice"]
def test_reset_permission():
def test_permission_reset():
# Reset permission
user_permission_clear(["blog"], "main")
user_permission_reset("blog.main")
res = user_permission_list()['permissions']
assert set(["alice", "bob"]) == set(res['blog']['main']['allowed_users'])
assert ["all_users"] == res['blog']['main']['allowed_groups']
# internal functions
def test_add_url_1():
# Add URL in permission which hasn't any URL defined
permission_update("blog", "main", add_url=[maindomain + "/testA"])
res = user_permission_list()['permissions']
assert [maindomain + "/testA"] == res['blog']['main']['URL']
def test_add_url_2():
# Add a second URL in a permission
permission_update("wiki", "main", add_url=[maindomain + "/testA"])
res = user_permission_list()['permissions']
assert set([maindomain + "/testA", maindomain + "/wiki"]) == set(res['wiki']['main']['URL'])
def test_remove_url_1():
permission_update("wiki", "main", remove_url=[maindomain + "/wiki"])
res = user_permission_list()['permissions']
assert 'URL' not in res['wiki']['main']
def test_add_url_3():
# Add a url already added
permission_update("wiki", "main", add_url=[maindomain + "/wiki"])
res = user_permission_list()['permissions']
assert [maindomain + "/wiki"] == res['wiki']['main']['URL']
def test_remove_url_2():
# Remove a url not added (with a permission which contain some URL)
permission_update("wiki", "main", remove_url=[maindomain + "/not_exist"])
res = user_permission_list()['permissions']
assert [maindomain + "/wiki"] == res['wiki']['main']['URL']
def test_remove_url_2():
# Remove a url not added (with a permission which contain no URL)
permission_update("blog", "main", remove_url=[maindomain + "/not_exist"])
res = user_permission_list()['permissions']
assert 'URL' not in res['blog']['main']
res = user_permission_list(full=True)['permissions']
assert res['blog.main']['allowed'] == ["all_users"]
assert set(res['blog.main']['corresponding_users']) == set(["alice", "bob"])
#
# Error on update function
#
def test_disallow_bad_group_1():
# Disallow a group when the group all_users is allowed
def test_permission_add_group_that_doesnt_exist():
with pytest.raises(YunohostError):
user_permission_remove("wiki", "main", group="alice")
user_permission_update("blog.main", add="doesnt_exist")
res = user_permission_list()['permissions']
assert ["all_users"] == res['wiki']['main']['allowed_groups']
assert set(["alice", "bob"]) == set(res['wiki']['main']['allowed_users'])
res = user_permission_list(full=True)['permissions']
assert res['blog.main']['allowed'] == ["alice"]
assert res['blog.main']['corresponding_users'] == ["alice"]
def test_allow_bad_user():
# Allow a non existant group
def test_permission_update_permission_that_doesnt_exist():
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():
# Disallow a non existant group
with pytest.raises(YunohostError):
user_permission_remove(["blog"], "main", group="not_exist")
# Permission url management
res = user_permission_list()['permissions']
assert ["alice"] == res['blog']['main']['allowed_groups']
assert ["alice"] == res['blog']['main']['allowed_users']
def test_permission_add_url():
permission_urls("blog.main", add=[maindomain + "/testA"])
def test_allow_bad_permission_1():
# Allow a user to a non existant permission
with pytest.raises(YunohostError):
user_permission_add(["wiki"], "not_exit", group="alice")
res = user_permission_list(full=True)['permissions']
assert res["blog.main"]["urls"] == [maindomain + "/testA"]
def test_allow_bad_permission_2():
# Allow a user to a non existant permission
with pytest.raises(YunohostError):
user_permission_add(["not_exit"], "main", group="alice")
def test_permission_add_second_url():
permission_urls("wiki.main", add=[maindomain + "/testA"])
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
#
def test_install_app():
def test_permission_app_install():
app_install("./tests/apps/permissions_app_ynh",
args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True)
res = user_permission_list()['permissions']
assert "permissions_app" in res
assert "main" in res['permissions_app']
assert [maindomain + "/urlpermissionapp"] == res['permissions_app']['main']['URL']
assert [maindomain + "/urlpermissionapp/admin"] == res['permissions_app']['admin']['URL']
assert [maindomain + "/urlpermissionapp/dev"] == res['permissions_app']['dev']['URL']
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 ["all_users"] == res['permissions_app']['main']['allowed_groups']
assert set(["alice", "bob"]) == set(res['permissions_app']['main']['allowed_users'])
assert res['permissions_app.main']['allowed'] == ["all_users"]
assert set(res['permissions_app.main']['corresponding_users']) == set(["alice", "bob"])
assert ["alice"] == res['permissions_app']['admin']['allowed_groups']
assert ["alice"] == res['permissions_app']['admin']['allowed_users']
assert res['permissions_app.admin']['allowed'] == ["alice"]
assert res['permissions_app.admin']['corresponding_users'] == ["alice"]
assert ["all_users"] == res['permissions_app']['dev']['allowed_groups']
assert set(["alice", "bob"]) == set(res['permissions_app']['dev']['allowed_users'])
assert res['permissions_app.dev']['allowed'] == []
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",
args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True)
app_remove("permissions_app")
res = user_permission_list()['permissions']
assert "permissions_app" not in res
# Check all permissions for this app got deleted
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",
args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True)
res = user_permission_list()['permissions']
assert [maindomain + "/urlpermissionapp"] == res['permissions_app']['main']['URL']
assert [maindomain + "/urlpermissionapp/admin"] == res['permissions_app']['admin']['URL']
assert [maindomain + "/urlpermissionapp/dev"] == res['permissions_app']['dev']['URL']
res = user_permission_list(full=True)['permissions']
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"]
app_change_url("permissions_app", maindomain, "/newchangeurl")
res = user_permission_list()['permissions']
assert [maindomain + "/newchangeurl"] == res['permissions_app']['main']['URL']
assert [maindomain + "/newchangeurl/admin"] == res['permissions_app']['admin']['URL']
assert [maindomain + "/newchangeurl/dev"] == res['permissions_app']['dev']['URL']
res = user_permission_list(full=True)['permissions']
assert res['permissions_app.main']['urls'] == [maindomain + "/newchangeurl"]
assert res['permissions_app.admin']['urls'] == [maindomain + "/newchangeurl/admin"]
assert res['permissions_app.dev']['urls'] == [maindomain + "/newchangeurl/dev"]

View file

@ -1,7 +1,7 @@
import pytest
from moulinette.core import MoulinetteError
from yunohost.user import user_list, user_info, user_group_list, user_create, user_delete, user_update, user_group_add, user_group_delete, user_group_update, user_group_info
from yunohost.user import user_list, user_info, user_create, user_delete, user_update, \
user_group_list, user_group_create, user_group_delete, user_group_update, user_group_info
from yunohost.domain import _get_maindomain
from yunohost.utils.error import YunohostError
from yunohost.tests.test_permission import check_LDAP_db_integrity
@ -24,10 +24,10 @@ def setup_function(function):
user_create("bob", "Bob", "Snow", "bob@" + maindomain, "test123Ynh")
user_create("jack", "Jack", "Black", "jack@" + maindomain, "test123Ynh")
user_group_add("dev")
user_group_add("apps")
user_group_update("dev", add_user=["alice"])
user_group_update("apps", add_user=["bob"])
user_group_create("dev")
user_group_create("apps")
user_group_update("dev", add=["alice"])
user_group_update("apps", add=["bob"])
def teardown_function(function):
clean_user_groups()
@ -82,12 +82,13 @@ def test_del_user():
assert "alice" not in group_res
assert "alice" not in group_res['all_users']['members']
def test_add_group():
user_group_add("adminsys")
def test_create_group():
user_group_create("adminsys")
group_res = user_group_list()['groups']
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():
user_group_delete("dev")
@ -99,112 +100,106 @@ def test_del_group():
# Error on create / remove function
#
def test_add_bad_user_1():
# Check email already exist
with pytest.raises(MoulinetteError):
def test_create_user_with_mail_address_already_taken():
with pytest.raises(YunohostError):
user_create("alice2", "Alice", "White", "alice@" + maindomain, "test123Ynh")
def test_add_bad_user_2():
# Check to short password
with pytest.raises(MoulinetteError):
def test_create_user_with_password_too_simple():
with pytest.raises(YunohostError):
user_create("other", "Alice", "White", "other@" + maindomain, "12")
def test_add_bad_user_3():
# Check user already exist
with pytest.raises(MoulinetteError):
def test_create_user_already_exists():
with pytest.raises(YunohostError):
user_create("alice", "Alice", "White", "other@" + maindomain, "test123Ynh")
def test_del_bad_user_1():
# Check user not found
with pytest.raises(MoulinetteError):
user_delete("not_exit")
def test_update_user_with_mail_address_already_taken():
with pytest.raises(YunohostError):
user_update("bob", add_mailalias="alice@" + maindomain)
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"
with pytest.raises(YunohostError):
user_group_add("all_users")
user_group_create("all_users")
def test_add_bad_group_2():
# Check groups already exist (for standard groups)
with pytest.raises(MoulinetteError):
user_group_add("dev")
def test_create_group_already_exists():
# Check groups already exist (regular groups)
with pytest.raises(YunohostError):
user_group_create("dev")
def test_del_bad_group_1():
# Check not allowed to remove this groups
def test_del_group_all_users():
with pytest.raises(YunohostError):
user_group_delete("all_users")
def test_del_bad_group_2():
# Check groups not found
with pytest.raises(MoulinetteError):
user_group_delete("not_exit")
def test_del_group_that_does_not_exist():
with pytest.raises(YunohostError):
user_group_delete("doesnt_exist")
#
# Update function
#
def test_update_user_1():
def test_update_user():
user_update("alice", firstname="NewName", lastname="NewLast")
info = user_info("alice")
assert "NewName" == info['firstname']
assert "NewLast" == info['lastname']
assert info['firstname'] == "NewName"
assert info['lastname'] == "NewLast"
def test_update_group_1():
user_group_update("dev", add_user=["bob"])
def test_update_group_add_user():
user_group_update("dev", add=["bob"])
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():
# Try to add a user in a group when the user is already in
user_group_update("apps", add_user=["bob"])
def test_update_group_add_user_already_in():
user_group_update("apps", add=["bob"])
group_res = user_group_list()['groups']
assert ["bob"] == group_res['apps']['members']
assert group_res['apps']['members'] == ["bob"]
def test_update_group_3():
# Try to remove a user in a group
user_group_update("apps", remove_user=["bob"])
def test_update_group_remove_user():
user_group_update("apps", remove=["bob"])
group_res = user_group_list()['groups']
assert "members" not in group_res['apps']
assert group_res['apps']['members'] == []
def test_update_group_4():
# Try to remove a user in a group when it is not already in
user_group_update("apps", remove_user=["jack"])
def test_update_group_remove_user_not_already_in():
user_group_update("apps", remove=["jack"])
group_res = user_group_list()['groups']
assert ["bob"] == group_res['apps']['members']
assert group_res['apps']['members'] == ["bob"]
#
# Error on update functions
#
def test_bad_update_user_1():
# Check user not found
def test_update_user_that_doesnt_exist():
with pytest.raises(YunohostError):
user_update("not_exit", firstname="NewName", lastname="NewLast")
user_update("doesnt_exist", firstname="NewName", lastname="NewLast")
def bad_update_group_1():
def test_update_group_that_doesnt_exist():
# Check groups not found
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():
# Check remove user in groups "all_users" not allowed
def test_update_group_all_users_manually():
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():
# Check remove user in it own group not allowed
assert "alice" in user_group_list()["groups"]["all_users"]["members"]
def test_update_group_primary_manually():
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
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"]

View file

@ -32,6 +32,7 @@ import crypt
import random
import string
import subprocess
import copy
from moulinette import m18n
from yunohost.utils.error import YunohostError
@ -126,12 +127,18 @@ def user_create(operation_logger, username, firstname, lastname, mail, password,
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
ldap.validate_uniqueness({
'uid': username,
'mail': mail,
'cn': username
})
try:
ldap.validate_uniqueness({
'uid': 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
all_existing_usernames = {x.pw_name for x in pwd.getpwall()}
@ -192,7 +199,7 @@ def user_create(operation_logger, username, firstname, lastname, mail, password,
with open('/etc/ssowat/conf.json.persistent') as json_conf:
ssowat_conf = json.loads(str(json_conf.read()))
except ValueError as e:
raise YunohostError('ssowat_persistent_conf_read_error', error=e.strerror)
raise YunohostError('ssowat_persistent_conf_read_error', error=str(e))
except IOError:
ssowat_conf = {}
@ -202,35 +209,36 @@ def user_create(operation_logger, username, firstname, lastname, mail, password,
with open('/etc/ssowat/conf.json.persistent', 'w+') as f:
json.dump(ssowat_conf, f, sort_keys=True, indent=4)
except IOError as e:
raise YunohostError('ssowat_persistent_conf_write_error', error=e.strerror)
raise YunohostError('ssowat_persistent_conf_write_error', error=str(e))
if ldap.add('uid=%s,ou=users' % username, attr_dict):
# Invalidate passwd to take user creation into account
subprocess.call(['nscd', '-i', 'passwd'])
try:
ldap.add('uid=%s,ou=users' % username, attr_dict)
except Exception as e:
raise YunohostError('user_creation_failed', user=username, error=e)
try:
# Attempt to create user home folder
subprocess.check_call(
['su', '-', username, '-c', "''"])
except subprocess.CalledProcessError:
if not os.path.isdir('/home/{0}'.format(username)):
logger.warning(m18n.n('user_home_creation_failed'),
exc_info=1)
# Invalidate passwd to take user creation into account
subprocess.call(['nscd', '-i', 'passwd'])
# Create group for user and add to group 'all_users'
user_group_add(groupname=username, gid=uid, sync_perm=False)
user_group_update(groupname=username, add_user=username, force=True, sync_perm=False)
user_group_update(groupname='all_users', add_user=username, force=True, sync_perm=True)
try:
# Attempt to create user home folder
subprocess.check_call(
['su', '-', username, '-c', "''"])
except subprocess.CalledProcessError:
if not os.path.isdir('/home/{0}'.format(username)):
logger.warning(m18n.n('user_home_creation_failed'),
exc_info=1)
# TODO: Send a welcome mail to user
logger.success(m18n.n('user_created'))
# Create group for user and add to group 'all_users'
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',
args=[username, mail, password, firstname, lastname])
# TODO: Send a welcome mail to user
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')])
@ -245,32 +253,35 @@ def user_delete(operation_logger, username, purge=False):
"""
from yunohost.hook import hook_callback
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()
ldap = _get_ldap_interface()
if ldap.remove('uid=%s,ou=users' % username):
# Invalidate passwd to take user deletion into account
subprocess.call(['nscd', '-i', 'passwd'])
if purge:
subprocess.call(['rm', '-rf', '/home/{0}'.format(username)])
subprocess.call(['rm', '-rf', '/var/mail/{0}'.format(username)])
else:
raise YunohostError('user_deletion_failed')
user_group_update("all_users", remove=username, force=True, sync_perm=False)
for group, infos in user_group_list()["groups"].items():
if group == "all_users":
continue
# If the user is in this group (and it's not the primary group),
# remove the member from the group
if username != group and username in infos["members"]:
user_group_update(group, remove=username, sync_perm=False)
user_group_delete(username, force=True, sync_perm=True)
group_list = ldap.search('ou=groups,dc=yunohost,dc=org',
'(&(objectclass=groupOfNamesYnh)(memberUid=%s))'
% username, ['cn'])
for group in group_list:
user_list = ldap.search('ou=groups,dc=yunohost,dc=org',
'cn=' + group['cn'][0],
['memberUid'])[0]
user_list['memberUid'].remove(username)
if not ldap.update('cn=%s,ou=groups' % group['cn'][0], user_list):
raise YunohostError('group_update_failed')
ldap = _get_ldap_interface()
try:
ldap.remove('uid=%s,ou=users' % username)
except Exception as e:
raise YunohostError('user_deletion_failed', user=username, error=e)
# Invalidate passwd to take user deletion into account
subprocess.call(['nscd', '-i', 'passwd'])
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])
@ -338,7 +349,10 @@ def user_update(operation_logger, username, firstname=None, lastname=None, mail=
'webmaster@' + 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:
raise YunohostError('mail_domain_unknown', domain=mail[mail.find('@') + 1:])
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):
add_mailalias = [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:
raise YunohostError('mail_domain_unknown', domain=mail[mail.find('@') + 1:])
user['mail'].append(mail)
@ -391,12 +408,14 @@ def user_update(operation_logger, username, firstname=None, lastname=None, mail=
operation_logger.start()
if ldap.update('uid=%s,ou=users' % username, new_attr_dict):
logger.success(m18n.n('user_updated'))
app_ssowatconf()
return user_info(username)
else:
raise YunohostError('user_update_failed')
try:
ldap.update('uid=%s,ou=users' % username, new_attr_dict)
except Exception as e:
raise YunohostError('user_update_failed', user=username, error=e)
logger.success(m18n.n('user_updated'))
app_ssowatconf()
return user_info(username)
def user_info(username):
@ -453,7 +472,7 @@ def user_info(username):
if service_status("dovecot")["status"] != "running":
logger.warning(m18n.n('mailbox_used_space_dovecot_down'))
elif not user_permission_list(app="mail", permission="main", username=username)['permissions']:
elif username not in user_permission_list(full=True)["permissions"]["mail.main"]["corresponding_users"]:
logger.warning(m18n.n('mailbox_disabled', user=username))
else:
cmd = 'doveadm -f flow quota get -u %s' % user['uid'][0]
@ -480,81 +499,59 @@ def user_info(username):
'use': storage_use
}
if result:
return result_dict
else:
raise YunohostError('user_info_failed')
return result_dict
#
# Group subcategory
#
def user_group_list(fields=None):
def user_group_list(short=False, full=False, include_primary_groups=True):
"""
List users
Keyword argument:
filter -- LDAP filter used to search
offset -- Starting number for user fetching
limit -- Maximum number of user fetched
fields -- fields to fetch
short -- Only list the name of the groups without any additional info
full -- List all the info available for each groups
include_primary_groups -- Include groups corresponding to users (which should always only contains this user)
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()
group_attr = {
'cn': 'groupname',
'member': 'members',
'permission': 'permission'
}
attrs = ['cn']
groups_infos = ldap.search('ou=groups,dc=yunohost,dc=org',
'(objectclass=groupOfNamesYnh)',
["cn", "member", "permission"])
# Parse / organize information to be outputed
users = user_list()["users"]
groups = {}
for infos in groups_infos:
if fields:
keys = group_attr.keys()
for attr in fields:
if attr in keys:
attrs.append(attr)
else:
raise YunohostError('field_invalid', attr)
else:
attrs = ['cn', 'member']
name = infos["cn"][0]
result = ldap.search('ou=groups,dc=yunohost,dc=org',
'(objectclass=groupOfNamesYnh)',
attrs)
for group in result:
# The group "admins" should be hidden for the user
if group_attr['cn'] == "admins":
if not include_primary_groups and name in users:
continue
entry = {}
for attr, values in group.items():
if values:
if attr == "member":
entry[group_attr[attr]] = []
for v in values:
entry[group_attr[attr]].append(v.split("=")[1].split(",")[0])
elif attr == "permission":
entry[group_attr[attr]] = {}
for v in values:
permission = v.split("=")[1].split(",")[0].split(".")[1]
pType = v.split("=")[1].split(",")[0].split(".")[0]
if permission in entry[group_attr[attr]]:
entry[group_attr[attr]][permission].append(pType)
else:
entry[group_attr[attr]][permission] = [pType]
else:
entry[group_attr[attr]] = values[0]
groupname = entry[group_attr['cn']]
groups[groupname] = entry
groups[name] = {}
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}
@is_unit_operation([('groupname', 'user')])
def user_group_add(operation_logger, groupname, gid=None, sync_perm=True):
@is_unit_operation([('groupname', 'group')])
def user_group_create(operation_logger, groupname, gid=None, primary_group=False, sync_perm=True):
"""
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.utils.ldap import _get_ldap_interface
operation_logger.start()
ldap = _get_ldap_interface()
# Validate uniqueness of groupname in LDAP
@ -574,12 +569,12 @@ def user_group_add(operation_logger, groupname, gid=None, sync_perm=True):
'cn': groupname
}, base_dn='ou=groups,dc=yunohost,dc=org')
if conflict:
raise YunohostError('group_name_already_exist', name=groupname)
raise YunohostError('group_already_exist', group=groupname)
# Validate uniqueness of groupname in system group
all_existing_groupnames = {x.gr_name for x in grp.getgrall()}
if groupname in all_existing_groupnames:
raise YunohostError('system_groupname_exists')
raise YunohostError('group_already_exist_on_system', group=groupname)
if not gid:
# Get random GID
@ -596,16 +591,30 @@ def user_group_add(operation_logger, groupname, gid=None, sync_perm=True):
'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))
if sync_perm:
permission_sync_to_user()
return {'name': groupname}
else:
logger.debug(m18n.n('group_created', group=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):
"""
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.utils.ldap import _get_ldap_interface
forbidden_groups = ["all_users", "admins"] + user_list(fields=['uid'])['users'].keys()
if not force and groupname in forbidden_groups:
raise YunohostError('group_deletion_not_allowed', group=groupname)
existing_groups = user_group_list()['groups'].keys()
if groupname not in existing_groups:
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()
ldap = _get_ldap_interface()
if not ldap.remove('cn=%s,ou=groups' % groupname):
raise YunohostError('group_deletion_failed', group=groupname)
try:
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:
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
Keyword argument:
groupname -- Groupname to update
add_user -- User to add in group
remove_user -- User to remove in group
add -- User(s) to add in group
remove -- User(s) to remove in group
"""
from yunohost.permission import permission_sync_to_user
from yunohost.utils.ldap import _get_ldap_interface
if (groupname == 'all_users' or groupname == 'admins') and not force:
raise YunohostError('edit_group_not_allowed', group=groupname)
# Refuse to edit a primary group of a user (e.g. group 'sam' related to user 'sam')
# 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
attrs_to_fetch = ['member']
result = ldap.search(base='ou=groups,dc=yunohost,dc=org',
filter='cn=' + groupname, attrs=attrs_to_fetch)
if not result:
raise YunohostError('group_unknown', group=groupname)
group = result[0]
if add:
users_to_add = [add] if not isinstance(add, list) else add
new_group_list = {'member': set(), 'memberUid': set()}
if 'member' in group:
new_group_list['member'] = set(group['member'])
else:
group['member'] = []
existing_users = user_list(fields=['uid'])['users'].keys()
if add_user:
if not isinstance(add_user, list):
add_user = [add_user]
for user in add_user:
for user in users_to_add:
if user not in existing_users:
raise YunohostError('user_unknown', user=user)
for user in add_user:
userDN = "uid=" + user + ",ou=users,dc=yunohost,dc=org"
if userDN in group['member']:
logger.warning(m18n.n('user_already_in_group', user=user, group=groupname))
new_group_list['member'].add(userDN)
if remove_user:
if not isinstance(remove_user, list):
remove_user = [remove_user]
for user in remove_user:
if user == groupname:
raise YunohostError('remove_user_of_group_not_allowed', user=user, group=groupname)
for user in remove_user:
userDN = "uid=" + user + ",ou=users,dc=yunohost,dc=org"
if 'member' in group and userDN in group['member']:
new_group_list['member'].remove(userDN)
if user in current_group:
logger.warning(m18n.n('group_user_already_in_group', user=user, group=groupname))
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)
# 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)
new_group += users_to_add
operation_logger.start()
if remove:
users_to_remove = [remove] if not isinstance(remove, list) else remove
if new_group_list['member'] != set(group['member']):
if not ldap.update('cn=%s,ou=groups' % groupname, new_group_list):
raise YunohostError('group_update_failed', group=groupname)
for user in users_to_remove:
if user not in current_group:
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:
permission_sync_to_user()
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()
group_attrs = [
'cn', 'member', 'permission'
]
result = ldap.search('ou=groups,dc=yunohost,dc=org', "cn=" + groupname, group_attrs)
# Fetch info for this group
result = ldap.search('ou=groups,dc=yunohost,dc=org',
"cn=" + groupname,
["cn", "member", "permission"])
if not result:
raise YunohostError('group_unknown', group=groupname)
group = result[0]
infos = result[0]
result_dict = {
'groupname': group['cn'][0],
'member': None
# Format data
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
#
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
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_add(operation_logger, app, permission="main", username=None, group=None, sync_perm=True):
def user_permission_update(permission, add=None, remove=None, sync_perm=True):
import yunohost.permission
return yunohost.permission.user_permission_update(operation_logger, app, permission=permission,
add_username=username, add_group=group,
del_username=None, del_group=None,
return yunohost.permission.user_permission_update(permission,
add=add, remove=remove,
sync_perm=sync_perm)
@is_unit_operation([('app', 'user')])
def user_permission_remove(operation_logger, app, permission="main", username=None, group=None, sync_perm=True):
def user_permission_reset(permission, sync_perm=True):
import yunohost.permission
return yunohost.permission.user_permission_update(operation_logger, app, permission=permission,
add_username=None, add_group=None,
del_username=username, del_group=group,
sync_perm=sync_perm)
@is_unit_operation([('app', 'user')])
def user_permission_clear(operation_logger, app, permission=None, sync_perm=True):
import yunohost.permission
return yunohost.permission.user_permission_clear(operation_logger, app, permission,
return yunohost.permission.user_permission_reset(permission,
sync_perm=sync_perm)

View file

@ -40,6 +40,20 @@ def _get_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
# when Python exits ...
# Otherwise there's a risk that some funky error appears at the very end