Merge branch 'bookworm' into portal-api

This commit is contained in:
Alexandre Aubin 2023-09-27 18:57:02 +02:00 committed by GitHub
commit db30b3acb8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
70 changed files with 889 additions and 936 deletions

View file

@ -7,7 +7,6 @@ generate-helpers-doc:
image: "before-install" image: "before-install"
needs: [] needs: []
before_script: before_script:
- apt-get update -y && apt-get install git hub -y
- git config --global user.email "yunohost@yunohost.org" - git config --global user.email "yunohost@yunohost.org"
- git config --global user.name "$GITHUB_USER" - git config --global user.name "$GITHUB_USER"
script: script:

View file

@ -3,34 +3,33 @@
######################################## ########################################
# later we must fix lint and format-check jobs and remove "allow_failure" # later we must fix lint and format-check jobs and remove "allow_failure"
lint39: lint311:
stage: lint stage: lint
image: "before-install" image: "before-install"
needs: [] needs: []
allow_failure: true allow_failure: true
script: script:
- tox -e py39-lint - tox -e py311-lint
invalidcode39: invalidcode311:
stage: lint stage: lint
image: "before-install" image: "before-install"
needs: [] needs: []
script: script:
- tox -e py39-invalidcode - tox -e py311-invalidcode
mypy: mypy:
stage: lint stage: lint
image: "before-install" image: "before-install"
needs: [] needs: []
script: script:
- tox -e py39-mypy - tox -e py311-mypy
black: black:
stage: lint stage: lint
image: "before-install" image: "before-install"
needs: [] needs: []
before_script: before_script:
- apt-get update -y && apt-get install git hub -y
- git config --global user.email "yunohost@yunohost.org" - git config --global user.email "yunohost@yunohost.org"
- git config --global user.name "$GITHUB_USER" - git config --global user.name "$GITHUB_USER"
- hub clone --branch ${CI_COMMIT_REF_NAME} "https://$GITHUB_TOKEN:x-oauth-basic@github.com/YunoHost/yunohost.git" github_repo - hub clone --branch ${CI_COMMIT_REF_NAME} "https://$GITHUB_TOKEN:x-oauth-basic@github.com/YunoHost/yunohost.git" github_repo
@ -38,7 +37,7 @@ black:
script: script:
# create a local branch that will overwrite distant one # create a local branch that will overwrite distant one
- git checkout -b "ci-format-${CI_COMMIT_REF_NAME}" --no-track - git checkout -b "ci-format-${CI_COMMIT_REF_NAME}" --no-track
- tox -e py39-black-run - tox -e py311-black-run
- '[ $(git diff | wc -l) != 0 ] || exit 0' # stop if there is nothing to commit - '[ $(git diff | wc -l) != 0 ] || exit 0' # stop if there is nothing to commit
- git commit -am "[CI] Format code with Black" || true - git commit -am "[CI] Format code with Black" || true
- git push -f origin "ci-format-${CI_COMMIT_REF_NAME}":"ci-format-${CI_COMMIT_REF_NAME}" - git push -f origin "ci-format-${CI_COMMIT_REF_NAME}":"ci-format-${CI_COMMIT_REF_NAME}"

View file

@ -1,7 +1,6 @@
.install_debs: &install_debs .install_debs: &install_debs
- apt-get update -o Acquire::Retries=3 - apt-get update -o Acquire::Retries=3
- DEBIAN_FRONTEND=noninteractive SUDO_FORCE_REMOVE=yes apt --assume-yes -o Dpkg::Options::="--force-confold" --allow-downgrades install ${CI_PROJECT_DIR}/*.deb - DEBIAN_FRONTEND=noninteractive SUDO_FORCE_REMOVE=yes apt --assume-yes -o Dpkg::Options::="--force-confold" --allow-downgrades install ${CI_PROJECT_DIR}/*.deb
- pip3 install -U mock pip pytest pytest-cov pytest-mock pytest-sugar requests-mock tox ansi2html black jinja2 "packaging<22"
.test-stage: .test-stage:
stage: test stage: test

View file

@ -16,7 +16,6 @@ autofix-translated-strings:
image: "before-install" image: "before-install"
needs: [] needs: []
before_script: before_script:
- apt-get update -y && apt-get install git hub -y
- git config --global user.email "yunohost@yunohost.org" - git config --global user.email "yunohost@yunohost.org"
- git config --global user.name "$GITHUB_USER" - git config --global user.name "$GITHUB_USER"
- hub clone --branch ${CI_COMMIT_REF_NAME} "https://$GITHUB_TOKEN:x-oauth-basic@github.com/YunoHost/yunohost.git" github_repo - hub clone --branch ${CI_COMMIT_REF_NAME} "https://$GITHUB_TOKEN:x-oauth-basic@github.com/YunoHost/yunohost.git" github_repo

View file

@ -132,12 +132,8 @@ def main() -> bool:
) )
continue continue
# Only broadcast IPv4 because IPv6 is buggy ... because we ain't using python3-ifaddr >= 0.1.7 # Broadcast IPv4 and IPv6
# Buster only ships 0.1.6 ips: List[str] = interfaces[interface]["ipv4"] + interfaces[interface]["ipv6"]
# Bullseye ships 0.1.7
# To be re-enabled once we're on bullseye...
# ips: List[str] = interfaces[interface]["ipv4"] + interfaces[interface]["ipv6"]
ips: List[str] = interfaces[interface]["ipv4"]
# If at least one IP is listed # If at least one IP is listed
if not ips: if not ips:

View file

@ -13,9 +13,8 @@ protocols = imap sieve {% if pop3_enabled == "True" %}pop3{% endif %}
mail_plugins = $mail_plugins quota notify push_notification mail_plugins = $mail_plugins quota notify push_notification
############################################################################### ###############################################################################
# generated 2023-06-13, Mozilla Guideline v5.7, Dovecot 2.3.19, OpenSSL 3.0.9, intermediate configuration
# generated 2020-08-18, Mozilla Guideline v5.6, Dovecot 2.3.4, OpenSSL 1.1.1d, intermediate configuration # https://ssl-config.mozilla.org/#server=dovecot&version=2.3.19&config=intermediate&openssl=3.0.9&guideline=5.7
# https://ssl-config.mozilla.org/#server=dovecot&version=2.3.4&config=intermediate&openssl=1.1.1d&guideline=5.6
ssl = required ssl = required
@ -32,7 +31,7 @@ ssl_dh = </usr/share/yunohost/ffdhe2048.pem
# intermediate configuration # intermediate configuration
ssl_min_protocol = TLSv1.2 ssl_min_protocol = TLSv1.2
ssl_cipher_list = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 ssl_cipher_list = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305
ssl_prefer_server_ciphers = no ssl_prefer_server_ciphers = no
############################################################################### ###############################################################################

View file

@ -18,7 +18,7 @@
# See man 5 jail.conf for details. # See man 5 jail.conf for details.
# #
# [DEFAULT] # [DEFAULT]
# bantime = 3600 # bantime = 1h
# #
# [sshd] # [sshd]
# enabled = true # enabled = true
@ -44,10 +44,52 @@ before = paths-debian.conf
# MISCELLANEOUS OPTIONS # MISCELLANEOUS OPTIONS
# #
# "ignoreip" can be an IP address, a CIDR mask or a DNS host. Fail2ban will not # "bantime.increment" allows to use database for searching of previously banned ip's to increase a
# ban a host which matches an address in this list. Several addresses can be # default ban time using special formula, default it is banTime * 1, 2, 4, 8, 16, 32...
# defined using space (and/or comma) separator. #bantime.increment = true
ignoreip = 127.0.0.1/8
# "bantime.rndtime" is the max number of seconds using for mixing with random time
# to prevent "clever" botnets calculate exact time IP can be unbanned again:
#bantime.rndtime =
# "bantime.maxtime" is the max number of seconds using the ban time can reach (doesn't grow further)
#bantime.maxtime =
# "bantime.factor" is a coefficient to calculate exponent growing of the formula or common multiplier,
# default value of factor is 1 and with default value of formula, the ban time
# grows by 1, 2, 4, 8, 16 ...
#bantime.factor = 1
# "bantime.formula" used by default to calculate next value of ban time, default value below,
# the same ban time growing will be reached by multipliers 1, 2, 4, 8, 16, 32...
#bantime.formula = ban.Time * (1<<(ban.Count if ban.Count<20 else 20)) * banFactor
#
# more aggressive example of formula has the same values only for factor "2.0 / 2.885385" :
#bantime.formula = ban.Time * math.exp(float(ban.Count+1)*banFactor)/math.exp(1*banFactor)
# "bantime.multipliers" used to calculate next value of ban time instead of formula, corresponding
# previously ban count and given "bantime.factor" (for multipliers default is 1);
# following example grows ban time by 1, 2, 4, 8, 16 ... and if last ban count greater as multipliers count,
# always used last multiplier (64 in example), for factor '1' and original ban time 600 - 10.6 hours
#bantime.multipliers = 1 2 4 8 16 32 64
# following example can be used for small initial ban time (bantime=60) - it grows more aggressive at begin,
# for bantime=60 the multipliers are minutes and equal: 1 min, 5 min, 30 min, 1 hour, 5 hour, 12 hour, 1 day, 2 day
#bantime.multipliers = 1 5 30 60 300 720 1440 2880
# "bantime.overalljails" (if true) specifies the search of IP in the database will be executed
# cross over all jails, if false (default), only current jail of the ban IP will be searched
#bantime.overalljails = false
# --------------------
# "ignoreself" specifies whether the local resp. own IP addresses should be ignored
# (default is true). Fail2ban will not ban a host which matches such addresses.
#ignoreself = true
# "ignoreip" can be a list of IP addresses, CIDR masks or DNS hosts. Fail2ban
# will not ban a host which matches an address in this list. Several addresses
# can be defined using space (and/or comma) separator.
#ignoreip = 127.0.0.1/8 ::1
# External command that will take an tagged arguments to ignore, e.g. <ip>, # External command that will take an tagged arguments to ignore, e.g. <ip>,
# and return true if the IP is to be ignored. False otherwise. # and return true if the IP is to be ignored. False otherwise.
@ -56,15 +98,18 @@ ignoreip = 127.0.0.1/8
ignorecommand = ignorecommand =
# "bantime" is the number of seconds that a host is banned. # "bantime" is the number of seconds that a host is banned.
bantime = 600 bantime = 10m
# A host is banned if it has generated "maxretry" during the last "findtime" # A host is banned if it has generated "maxretry" during the last "findtime"
# seconds. # seconds.
findtime = 600 findtime = 10m
# "maxretry" is the number of failures before a host get banned. # "maxretry" is the number of failures before a host get banned.
maxretry = 10 maxretry = 10
# "maxmatches" is the number of matches stored in ticket (resolvable via tag <matches> in actions).
maxmatches = %(maxretry)s
# "backend" specifies the backend used to get files modification. # "backend" specifies the backend used to get files modification.
# Available options are "pyinotify", "gamin", "polling", "systemd" and "auto". # Available options are "pyinotify", "gamin", "polling", "systemd" and "auto".
# This option can be overridden in each jail as well. # This option can be overridden in each jail as well.
@ -113,10 +158,13 @@ logencoding = auto
enabled = false enabled = false
# "mode" defines the mode of the filter (see corresponding filter implementation for more info).
mode = normal
# "filter" defines the filter to use by the jail. # "filter" defines the filter to use by the jail.
# By default jails have names matching their filter name # By default jails have names matching their filter name
# #
filter = %(__name__)s filter = %(__name__)s[mode=%(mode)s]
# #
@ -140,7 +188,7 @@ mta = sendmail
# Default protocol # Default protocol
protocol = tcp protocol = tcp
# Specify chain where jumps would need to be added in iptables-* actions # Specify chain where jumps would need to be added in ban-actions expecting parameter chain
chain = INPUT chain = INPUT
# Ports to be banned # Ports to be banned
@ -161,51 +209,53 @@ banaction = iptables-multiport
banaction_allports = iptables-allports banaction_allports = iptables-allports
# The simplest action to take: ban only # The simplest action to take: ban only
action_ = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"] action_ = %(banaction)s[port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
# ban & send an e-mail with whois report to the destemail. # ban & send an e-mail with whois report to the destemail.
action_mw = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"] action_mw = %(action_)s
%(mta)s-whois[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s"] %(mta)s-whois[sender="%(sender)s", dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s"]
# ban & send an e-mail with whois report and relevant log lines # ban & send an e-mail with whois report and relevant log lines
# to the destemail. # to the destemail.
action_mwl = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"] action_mwl = %(action_)s
%(mta)s-whois-lines[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"] %(mta)s-whois-lines[sender="%(sender)s", dest="%(destemail)s", logpath="%(logpath)s", chain="%(chain)s"]
# See the IMPORTANT note in action.d/xarf-login-attack for when to use this action # See the IMPORTANT note in action.d/xarf-login-attack for when to use this action
# #
# ban & send a xarf e-mail to abuse contact of IP address and include relevant log lines # ban & send a xarf e-mail to abuse contact of IP address and include relevant log lines
# to the destemail. # to the destemail.
action_xarf = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"] action_xarf = %(action_)s
xarf-login-attack[service=%(__name__)s, sender="%(sender)s", logpath=%(logpath)s, port="%(port)s"] xarf-login-attack[service=%(__name__)s, sender="%(sender)s", logpath="%(logpath)s", port="%(port)s"]
# ban & send a notification to one or more of the 50+ services supported by Apprise.
# See https://github.com/caronc/apprise/wiki for details on what is supported.
#
# You may optionally over-ride the default configuration line (containing the Apprise URLs)
# by using 'apprise[config="/alternate/path/to/apprise.cfg"]' otherwise
# /etc/fail2ban/apprise.conf is sourced for your supported notification configuration.
# action = %(action_)s
# apprise
# ban IP on CloudFlare & send an e-mail with whois report and relevant log lines # ban IP on CloudFlare & send an e-mail with whois report and relevant log lines
# to the destemail. # to the destemail.
action_cf_mwl = cloudflare[cfuser="%(cfemail)s", cftoken="%(cfapikey)s"] action_cf_mwl = cloudflare[cfuser="%(cfemail)s", cftoken="%(cfapikey)s"]
%(mta)s-whois-lines[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"] %(mta)s-whois-lines[sender="%(sender)s", dest="%(destemail)s", logpath="%(logpath)s", chain="%(chain)s"]
# Report block via blocklist.de fail2ban reporting service API # Report block via blocklist.de fail2ban reporting service API
# #
# See the IMPORTANT note in action.d/blocklist_de.conf for when to # See the IMPORTANT note in action.d/blocklist_de.conf for when to use this action.
# use this action. Create a file jail.d/blocklist_de.local containing # Specify expected parameters in file action.d/blocklist_de.local or if the interpolation
# [Init] # `action_blocklist_de` used for the action, set value of `blocklist_de_apikey`
# blocklist_de_apikey = {api key from registration] # in your `jail.local` globally (section [DEFAULT]) or per specific jail section (resp. in
# corresponding jail.d/my-jail.local file).
# #
action_blocklist_de = blocklist_de[email="%(sender)s", service=%(filter)s, apikey="%(blocklist_de_apikey)s", agent="%(fail2ban_agent)s"] action_blocklist_de = blocklist_de[email="%(sender)s", service="%(__name__)s", apikey="%(blocklist_de_apikey)s", agent="%(fail2ban_agent)s"]
# Report ban via badips.com, and use as blacklist # Report ban via abuseipdb.com.
# #
# See BadIPsAction docstring in config/action.d/badips.py for # See action.d/abuseipdb.conf for usage example and details.
# documentation for this action.
# #
# NOTE: This action relies on banaction being present on start and therefore action_abuseipdb = abuseipdb
# should be last action defined for a jail.
#
action_badips = badips.py[category="%(__name__)s", banaction="%(banaction)s", agent="%(fail2ban_agent)s"]
#
# Report ban via badips.com (uses action.d/badips.conf for reporting only)
#
action_badips_report = badips[category="%(__name__)s", agent="%(fail2ban_agent)s"]
# Choose default action. To change, just override value of 'action' with the # Choose default action. To change, just override value of 'action' with the
# interpolation to the chosen action shortcut (e.g. action_mw, action_mwl, etc) in jail.local # interpolation to the chosen action shortcut (e.g. action_mw, action_mwl, etc) in jail.local
@ -223,15 +273,10 @@ action = %(action_)s
[sshd] [sshd]
port = ssh # To use more aggressive sshd modes set filter parameter "mode" in jail.local:
logpath = %(sshd_log)s # normal (default), ddos, extra or aggressive (combines all).
backend = %(sshd_backend)s # See "tests/files/logs/sshd" or "filter.d/sshd.conf" for usage example and details.
#mode = normal
[sshd-ddos]
# This jail corresponds to the standard configuration in Fail2ban.
# The mail-whois action send a notification e-mail with a whois request
# in the body.
port = ssh port = ssh
logpath = %(sshd_log)s logpath = %(sshd_log)s
backend = %(sshd_backend)s backend = %(sshd_backend)s
@ -265,7 +310,7 @@ logpath = %(apache_error_log)s
# for email addresses. The mail outputs are buffered. # for email addresses. The mail outputs are buffered.
port = http,https port = http,https
logpath = %(apache_access_log)s logpath = %(apache_access_log)s
bantime = 172800 bantime = 48h
maxretry = 1 maxretry = 1
@ -301,7 +346,7 @@ maxretry = 2
port = http,https port = http,https
logpath = %(apache_access_log)s logpath = %(apache_access_log)s
maxretry = 1 maxretry = 1
ignorecommand = %(ignorecommands_dir)s/apache-fakegooglebot <ip> ignorecommand = %(fail2ban_confpath)s/filter.d/ignorecommands/apache-fakegooglebot <ip>
[apache-modsecurity] [apache-modsecurity]
@ -321,12 +366,15 @@ maxretry = 1
[openhab-auth] [openhab-auth]
filter = openhab filter = openhab
action = iptables-allports[name=NoAuthFailures] banaction = %(banaction_allports)s
logpath = /opt/openhab/logs/request.log logpath = /opt/openhab/logs/request.log
# To use more aggressive http-auth modes set filter parameter "mode" in jail.local:
# normal (default), aggressive (combines all), auth or fallback
# See "tests/files/logs/nginx-http-auth" or "filter.d/nginx-http-auth.conf" for usage example and details.
[nginx-http-auth] [nginx-http-auth]
# mode = normal
port = http,https port = http,https
logpath = %(nginx_error_log)s logpath = %(nginx_error_log)s
@ -342,8 +390,10 @@ logpath = %(nginx_error_log)s
port = http,https port = http,https
logpath = %(nginx_error_log)s logpath = %(nginx_error_log)s
maxretry = 2
[nginx-bad-request]
port = http,https
logpath = %(nginx_access_log)s
# Ban attackers that try to use PHP's URL-fopen() functionality # Ban attackers that try to use PHP's URL-fopen() functionality
# through GET/POST variables. - Experimental, with more than a year # through GET/POST variables. - Experimental, with more than a year
@ -377,6 +427,8 @@ logpath = %(lighttpd_error_log)s
port = http,https port = http,https
logpath = %(roundcube_errors_log)s logpath = %(roundcube_errors_log)s
# Use following line in your jail.local if roundcube logs to journal.
#backend = %(syslog_backend)s
[openwebmail] [openwebmail]
@ -426,11 +478,13 @@ backend = %(syslog_backend)s
port = http,https port = http,https
logpath = /var/log/tomcat*/catalina.out logpath = /var/log/tomcat*/catalina.out
#logpath = /var/log/guacamole.log
[monit] [monit]
#Ban clients brute-forcing the monit gui login #Ban clients brute-forcing the monit gui login
port = 2812 port = 2812
logpath = /var/log/monit logpath = /var/log/monit
/var/log/monit.log
[webmin-auth] [webmin-auth]
@ -513,27 +567,29 @@ logpath = %(vsftpd_log)s
# ASSP SMTP Proxy Jail # ASSP SMTP Proxy Jail
[assp] [assp]
port = smtp,submission port = smtp,465,submission
logpath = /root/path/to/assp/logs/maillog.txt logpath = /root/path/to/assp/logs/maillog.txt
[courier-smtp] [courier-smtp]
port = smtp,submission port = smtp,465,submission
logpath = %(syslog_mail)s logpath = %(syslog_mail)s
backend = %(syslog_backend)s backend = %(syslog_backend)s
[postfix] [postfix]
# To use another modes set filter parameter "mode" in jail.local:
port = smtp,submission mode = more
logpath = %(postfix_log)s port = smtp,465,submission
backend = %(postfix_backend)s logpath = %(postfix_log)s
backend = %(postfix_backend)s
[postfix-rbl] [postfix-rbl]
port = smtp,submission filter = postfix[mode=rbl]
port = smtp,465,submission
logpath = %(postfix_log)s logpath = %(postfix_log)s
backend = %(postfix_backend)s backend = %(postfix_backend)s
maxretry = 1 maxretry = 1
@ -541,14 +597,17 @@ maxretry = 1
[sendmail-auth] [sendmail-auth]
port = submission,smtp port = submission,465,smtp
logpath = %(syslog_mail)s logpath = %(syslog_mail)s
backend = %(syslog_backend)s backend = %(syslog_backend)s
[sendmail-reject] [sendmail-reject]
# To use more aggressive modes set filter parameter "mode" in jail.local:
port = smtp,submission # normal (default), extra or aggressive
# See "tests/files/logs/sendmail-reject" or "filter.d/sendmail-reject.conf" for usage example and details.
#mode = normal
port = smtp,465,submission
logpath = %(syslog_mail)s logpath = %(syslog_mail)s
backend = %(syslog_backend)s backend = %(syslog_backend)s
@ -556,7 +615,7 @@ backend = %(syslog_backend)s
[qmail-rbl] [qmail-rbl]
filter = qmail filter = qmail
port = smtp,submission port = smtp,465,submission
logpath = /service/qmail/log/main/current logpath = /service/qmail/log/main/current
@ -564,14 +623,14 @@ logpath = /service/qmail/log/main/current
# but can be set by syslog_facility in the dovecot configuration. # but can be set by syslog_facility in the dovecot configuration.
[dovecot] [dovecot]
port = pop3,pop3s,imap,imaps,submission,sieve port = pop3,pop3s,imap,imaps,submission,465,sieve
logpath = %(dovecot_log)s logpath = %(dovecot_log)s
backend = %(dovecot_backend)s backend = %(dovecot_backend)s
[sieve] [sieve]
port = smtp,submission port = smtp,465,submission
logpath = %(dovecot_log)s logpath = %(dovecot_log)s
backend = %(dovecot_backend)s backend = %(dovecot_backend)s
@ -583,20 +642,21 @@ logpath = %(solidpop3d_log)s
[exim] [exim]
# see filter.d/exim.conf for further modes supported from filter:
port = smtp,submission #mode = normal
port = smtp,465,submission
logpath = %(exim_main_log)s logpath = %(exim_main_log)s
[exim-spam] [exim-spam]
port = smtp,submission port = smtp,465,submission
logpath = %(exim_main_log)s logpath = %(exim_main_log)s
[kerio] [kerio]
port = imap,smtp,imaps port = imap,smtp,imaps,465
logpath = /opt/kerio/mailserver/store/logs/security.log logpath = /opt/kerio/mailserver/store/logs/security.log
@ -607,14 +667,15 @@ logpath = /opt/kerio/mailserver/store/logs/security.log
[courier-auth] [courier-auth]
port = smtp,submission,imaps,pop3,pop3s port = smtp,465,submission,imap,imaps,pop3,pop3s
logpath = %(syslog_mail)s logpath = %(syslog_mail)s
backend = %(syslog_backend)s backend = %(syslog_backend)s
[postfix-sasl] [postfix-sasl]
port = smtp,submission,imap,imaps,pop3,pop3s filter = postfix[mode=auth]
port = smtp,465,submission,imap,imaps,pop3,pop3s
# You might consider monitoring /var/log/mail.warn instead if you are # You might consider monitoring /var/log/mail.warn instead if you are
# running postfix since it would provide the same log lines at the # running postfix since it would provide the same log lines at the
# "warn" level but overall at the smaller filesize. # "warn" level but overall at the smaller filesize.
@ -631,7 +692,7 @@ backend = %(syslog_backend)s
[squirrelmail] [squirrelmail]
port = smtp,submission,imap,imap2,imaps,pop3,pop3s,http,https,socks port = smtp,465,submission,imap,imap2,imaps,pop3,pop3s,http,https,socks
logpath = /var/lib/squirrelmail/prefs/squirrelmail_access_log logpath = /var/lib/squirrelmail/prefs/squirrelmail_access_log
@ -684,8 +745,8 @@ logpath = /var/log/named/security.log
[nsd] [nsd]
port = 53 port = 53
action = %(banaction)s[name=%(__name__)s-tcp, port="%(port)s", protocol="tcp", chain="%(chain)s", actname=%(banaction)s-tcp] action_ = %(default/action_)s[name=%(__name__)s-tcp, protocol="tcp"]
%(banaction)s[name=%(__name__)s-udp, port="%(port)s", protocol="udp", chain="%(chain)s", actname=%(banaction)s-udp] %(default/action_)s[name=%(__name__)s-udp, protocol="udp"]
logpath = /var/log/nsd.log logpath = /var/log/nsd.log
@ -696,9 +757,8 @@ logpath = /var/log/nsd.log
[asterisk] [asterisk]
port = 5060,5061 port = 5060,5061
action = %(banaction)s[name=%(__name__)s-tcp, port="%(port)s", protocol="tcp", chain="%(chain)s", actname=%(banaction)s-tcp] action_ = %(default/action_)s[name=%(__name__)s-tcp, protocol="tcp"]
%(banaction)s[name=%(__name__)s-udp, port="%(port)s", protocol="udp", chain="%(chain)s", actname=%(banaction)s-udp] %(default/action_)s[name=%(__name__)s-udp, protocol="udp"]
%(mta)s-whois[name=%(__name__)s, dest="%(destemail)s"]
logpath = /var/log/asterisk/messages logpath = /var/log/asterisk/messages
maxretry = 10 maxretry = 10
@ -706,16 +766,22 @@ maxretry = 10
[freeswitch] [freeswitch]
port = 5060,5061 port = 5060,5061
action = %(banaction)s[name=%(__name__)s-tcp, port="%(port)s", protocol="tcp", chain="%(chain)s", actname=%(banaction)s-tcp] action_ = %(default/action_)s[name=%(__name__)s-tcp, protocol="tcp"]
%(banaction)s[name=%(__name__)s-udp, port="%(port)s", protocol="udp", chain="%(chain)s", actname=%(banaction)s-udp] %(default/action_)s[name=%(__name__)s-udp, protocol="udp"]
%(mta)s-whois[name=%(__name__)s, dest="%(destemail)s"]
logpath = /var/log/freeswitch.log logpath = /var/log/freeswitch.log
maxretry = 10 maxretry = 10
# enable adminlog; it will log to a file inside znc's directory by default.
[znc-adminlog]
port = 6667
logpath = /var/lib/znc/moddata/adminlog/znc.log
# To log wrong MySQL access attempts add to /etc/my.cnf in [mysqld] or # To log wrong MySQL access attempts add to /etc/my.cnf in [mysqld] or
# equivalent section: # equivalent section:
# log-warning = 2 # log-warnings = 2
# #
# for syslog (daemon facility) # for syslog (daemon facility)
# [mysqld_safe] # [mysqld_safe]
@ -731,6 +797,14 @@ logpath = %(mysql_log)s
backend = %(mysql_backend)s backend = %(mysql_backend)s
[mssql-auth]
# Default configuration for Microsoft SQL Server for Linux
# See the 'mssql-conf' manpage how to change logpath or port
logpath = /var/opt/mssql/log/errorlog
port = 1433
filter = mssql-auth
# Log wrong MongoDB auth (for details see filter 'filter.d/mongodb-auth.conf') # Log wrong MongoDB auth (for details see filter 'filter.d/mongodb-auth.conf')
[mongodb-auth] [mongodb-auth]
# change port when running with "--shardsvr" or "--configsvr" runtime operation # change port when running with "--shardsvr" or "--configsvr" runtime operation
@ -749,8 +823,8 @@ logpath = /var/log/mongodb/mongodb.log
logpath = /var/log/fail2ban.log logpath = /var/log/fail2ban.log
banaction = %(banaction_allports)s banaction = %(banaction_allports)s
bantime = 604800 ; 1 week bantime = 1w
findtime = 86400 ; 1 day findtime = 1d
# Generic filter for PAM. Has to be used with action which bans all # Generic filter for PAM. Has to be used with action which bans all
@ -786,11 +860,31 @@ logpath = /var/log/ejabberd/ejabberd.log
[counter-strike] [counter-strike]
logpath = /opt/cstrike/logs/L[0-9]*.log logpath = /opt/cstrike/logs/L[0-9]*.log
# Firewall: http://www.cstrike-planet.com/faq/6
tcpport = 27030,27031,27032,27033,27034,27035,27036,27037,27038,27039 tcpport = 27030,27031,27032,27033,27034,27035,27036,27037,27038,27039
udpport = 1200,27000,27001,27002,27003,27004,27005,27006,27007,27008,27009,27010,27011,27012,27013,27014,27015 udpport = 1200,27000,27001,27002,27003,27004,27005,27006,27007,27008,27009,27010,27011,27012,27013,27014,27015
action = %(banaction)s[name=%(__name__)s-tcp, port="%(tcpport)s", protocol="tcp", chain="%(chain)s", actname=%(banaction)s-tcp] action_ = %(default/action_)s[name=%(__name__)s-tcp, port="%(tcpport)s", protocol="tcp"]
%(banaction)s[name=%(__name__)s-udp, port="%(udpport)s", protocol="udp", chain="%(chain)s", actname=%(banaction)s-udp] %(default/action_)s[name=%(__name__)s-udp, port="%(udpport)s", protocol="udp"]
[softethervpn]
port = 500,4500
protocol = udp
logpath = /usr/local/vpnserver/security_log/*/sec.log
[gitlab]
port = http,https
logpath = /var/log/gitlab/gitlab-rails/application.log
[grafana]
port = http,https
logpath = /var/log/grafana/grafana.log
[bitwarden]
port = http,https
logpath = /home/*/bwdata/logs/identity/Identity/log.txt
[centreon]
port = http,https
logpath = /var/log/centreon/login.log
# consider low maxretry and a long bantime # consider low maxretry and a long bantime
# nobody except your own Nagios server should ever probe nrpe # nobody except your own Nagios server should ever probe nrpe
@ -824,7 +918,9 @@ filter = apache-pass[knocking_url="%(knocking_url)s"]
logpath = %(apache_access_log)s logpath = %(apache_access_log)s
blocktype = RETURN blocktype = RETURN
returntype = DROP returntype = DROP
bantime = 3600 action = %(action_)s[blocktype=%(blocktype)s, returntype=%(returntype)s,
actionstart_on_demand=false, actionrepair_on_unban=true]
bantime = 1h
maxretry = 1 maxretry = 1
findtime = 1 findtime = 1
@ -832,8 +928,8 @@ findtime = 1
[murmur] [murmur]
# AKA mumble-server # AKA mumble-server
port = 64738 port = 64738
action = %(banaction)s[name=%(__name__)s-tcp, port="%(port)s", protocol=tcp, chain="%(chain)s", actname=%(banaction)s-tcp] action_ = %(default/action_)s[name=%(__name__)s-tcp, protocol="tcp"]
%(banaction)s[name=%(__name__)s-udp, port="%(port)s", protocol=udp, chain="%(chain)s", actname=%(banaction)s-udp] %(default/action_)s[name=%(__name__)s-udp, protocol="udp"]
logpath = /var/log/mumble-server/mumble-server.log logpath = /var/log/mumble-server/mumble-server.log
@ -851,5 +947,34 @@ logpath = /var/log/haproxy.log
[slapd] [slapd]
port = ldap,ldaps port = ldap,ldaps
filter = slapd
logpath = /var/log/slapd.log logpath = /var/log/slapd.log
[domino-smtp]
port = smtp,ssmtp
logpath = /home/domino01/data/IBM_TECHNICAL_SUPPORT/console.log
[phpmyadmin-syslog]
port = http,https
logpath = %(syslog_authpriv)s
backend = %(syslog_backend)s
[zoneminder]
# Zoneminder HTTP/HTTPS web interface auth
# Logs auth failures to apache2 error log
port = http,https
logpath = %(apache_error_log)s
[traefik-auth]
# to use 'traefik-auth' filter you have to configure your Traefik instance,
# see `filter.d/traefik-auth.conf` for details and service example.
port = http,https
logpath = /var/log/traefik/access.log
[scanlogd]
logpath = %(syslog_local0)s
banaction = %(banaction_allports)s
[monitorix]
port = 8080
logpath = /var/log/monitorix-httpd

View file

@ -3,16 +3,16 @@ ssl_session_cache shared:SSL:50m; # about 200000 sessions
ssl_session_tickets off; ssl_session_tickets off;
{% if compatibility == "modern" %} {% if compatibility == "modern" %}
# generated 2020-08-14, Mozilla Guideline v5.6, nginx 1.14.2, OpenSSL 1.1.1d, modern configuration # generated 2023-06-13, Mozilla Guideline v5.7, nginx 1.22.1, OpenSSL 3.0.9, modern configuration
# https://ssl-config.mozilla.org/#server=nginx&version=1.14.2&config=modern&openssl=1.1.1d&guideline=5.6 # https://ssl-config.mozilla.org/#server=nginx&version=1.22.1&config=modern&openssl=3.0.9&guideline=5.7
ssl_protocols TLSv1.3; ssl_protocols TLSv1.3;
ssl_prefer_server_ciphers off; ssl_prefer_server_ciphers off;
{% else %} {% else %}
# Ciphers with intermediate compatibility # Ciphers with intermediate compatibility
# generated 2020-08-14, Mozilla Guideline v5.6, nginx 1.14.2, OpenSSL 1.1.1d, intermediate configuration # generated 2023-06-13, Mozilla Guideline v5.7, nginx 1.22.1, OpenSSL 3.0.9, intermediate configuration
# https://ssl-config.mozilla.org/#server=nginx&version=1.14.2&config=intermediate&openssl=1.1.1d&guideline=5.6 # https://ssl-config.mozilla.org/#server=nginx&version=1.22.1&config=intermediate&openssl=3.0.9&guideline=5.7
ssl_protocols TLSv1.2 TLSv1.3; ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off; ssl_prefer_server_ciphers off;
# Pre-defined FFDHE group (RFC 7919) # Pre-defined FFDHE group (RFC 7919)

View file

@ -30,8 +30,8 @@ smtpd_tls_chain_files =
tls_server_sni_maps = hash:/etc/postfix/sni tls_server_sni_maps = hash:/etc/postfix/sni
{% if compatibility == "intermediate" %} {% if compatibility == "intermediate" %}
# generated 2020-08-18, Mozilla Guideline v5.6, Postfix 3.4.14, OpenSSL 1.1.1d, intermediate configuration # generated 2023-06-13, Mozilla Guideline v5.7, Postfix 3.7.5, OpenSSL 3.0.9, intermediate configuration
# https://ssl-config.mozilla.org/#server=postfix&version=3.4.14&config=intermediate&openssl=1.1.1d&guideline=5.6 # https://ssl-config.mozilla.org/#server=postfix&version=3.7.5&config=intermediate&openssl=3.0.9&guideline=5.7
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
@ -41,10 +41,10 @@ smtpd_tls_mandatory_ciphers = medium
# not actually 1024 bits, this applies to all DHE >= 1024 bits # not actually 1024 bits, this applies to all DHE >= 1024 bits
smtpd_tls_dh1024_param_file = /usr/share/yunohost/ffdhe2048.pem smtpd_tls_dh1024_param_file = /usr/share/yunohost/ffdhe2048.pem
tls_medium_cipherlist = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 tls_medium_cipherlist = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305
{% else %} {% else %}
# generated 2020-08-18, Mozilla Guideline v5.6, Postfix 3.4.14, OpenSSL 1.1.1d, modern configuration # generated 2023-06-13, Mozilla Guideline v5.7, Postfix 3.7.5, OpenSSL 3.0.9, modern configuration
# https://ssl-config.mozilla.org/#server=postfix&version=3.4.14&config=modern&openssl=1.1.1d&guideline=5.6 # https://ssl-config.mozilla.org/#server=postfix&version=3.7.5&config=modern&openssl=3.0.9&guideline=5.7
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, !TLSv1.2 smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, !TLSv1.2
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, !TLSv1.2 smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, !TLSv1.2

44
debian/changelog vendored
View file

@ -1,3 +1,47 @@
yunohost (12.0.0) unstable; urgency=low
- Tmp changelog to prepare Bookworm
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 04 May 2023 20:30:19 +0200
yunohost (11.2.4) stable; urgency=low
- doc: Improve --help for 'yunohost app install' ([#1702](https://github.com/yunohost/yunohost/pull/1702))
- helpers: add new --group option for ynh_add_fpm_config to customize the Group parameter (65d25710)
- apps: allow to use jinja {% if foobar %} blocks in their notifications/doc pages (57699289)
- apps: BACKUP_CORE_ONLY was not set for pre-upgrade safety backups, resulting in unecessarily large pre-upgrade backups (07daa687)
- apps: Use the existing db_name setting for database provising to ease v1->v2 transition with specific db_name ([#1704](https://github.com/yunohost/yunohost/pull/1704))
- configpanels/forms: more edge cases with some questions not implementing some methods/attributes (b0fe49ae)
- diagnosis: reverse DNS check should be case-insensitive #2235 ([#1705](https://github.com/yunohost/yunohost/pull/1705))
- i18n: Translations updated for Galician, Indonesian, Polish, Spanish, Turkish
Thanks to all contributors <3 ! (Grzegorz Cichocki, José M, Kuba Bazan, ljf (zamentur), massyas, Neko Nekowazarashi, selfhoster1312, Suleyman Harmandar, taco, Tagada)
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 31 Aug 2023 17:30:21 +0200
yunohost (11.2.3) stable; urgency=low
- apps: fix another case of no attribute 'value' due to config panels/questions refactoring (4fda8ed49)
-- Alexandre Aubin <alex.aubin@mailoo.org> Sat, 22 Jul 2023 16:48:22 +0200
yunohost (11.2.2) stable; urgency=low
- domains: Gandi's `api_protocol` field should be a `select` type ([#1693](https://github.com/yunohost/yunohost/pull/1693))
- configpanel: fix .value call for readonly-type options (e1ceb084)
- i18n: Translations updated for French, Galician
Thanks to all contributors <3 ! (axolotle, José M, ppr, tituspijean)
-- Alexandre Aubin <alex.aubin@mailoo.org> Wed, 19 Jul 2023 02:35:28 +0200
yunohost (11.2.1) stable; urgency=low
- doc: fix resource doc generation .. not sure why this line that removed legit indent was there (ced222ea)
- apps: hotfix for funky issue, apps getting named 'undefined' (781f924e)
-- Alexandre Aubin <alex.aubin@mailoo.org> Mon, 17 Jul 2023 21:13:54 +0200
yunohost (11.2) stable; urgency=low yunohost (11.2) stable; urgency=low
- dyndns: add support for recovery passwords ([#1475](https://github.com/YunoHost/yunohost/pull/1475)) - dyndns: add support for recovery passwords ([#1475](https://github.com/YunoHost/yunohost/pull/1475))

25
debian/control vendored
View file

@ -2,7 +2,7 @@ Source: yunohost
Section: utils Section: utils
Priority: extra Priority: extra
Maintainer: YunoHost Contributors <contrib@yunohost.org> Maintainer: YunoHost Contributors <contrib@yunohost.org>
Build-Depends: debhelper (>=9), debhelper-compat (= 13), dh-python, python3-all (>= 3.7), python3-yaml, python3-jinja2 Build-Depends: debhelper (>=9), debhelper-compat (= 13), dh-python, python3-all (>= 3.11), python3-yaml, python3-jinja2
Standards-Version: 3.9.6 Standards-Version: 3.9.6
Homepage: https://yunohost.org/ Homepage: https://yunohost.org/
@ -14,10 +14,10 @@ Depends: ${python3:Depends}, ${misc:Depends}
, python3-psutil, python3-requests, python3-dnspython, python3-openssl , python3-psutil, python3-requests, python3-dnspython, python3-openssl
, python3-miniupnpc, python3-dbus, python3-jinja2 , python3-miniupnpc, python3-dbus, python3-jinja2
, python3-toml, python3-packaging, python3-publicsuffix2 , python3-toml, python3-packaging, python3-publicsuffix2
, python3-ldap, python3-zeroconf (>= 0.36), python3-lexicon, , python3-ldap, python3-zeroconf (>= 0.47), python3-lexicon,
, python3-cryptography, python3-jwt , python3-cryptography, python3-jwt
, python-is-python3 , python-is-python3
, nginx, nginx-extras (>=1.18) , nginx, nginx-extras (>=1.22)
, apt, apt-transport-https, apt-utils, dirmngr , apt, apt-transport-https, apt-utils, dirmngr
, openssh-server, iptables, fail2ban, bind9-dnsutils , openssh-server, iptables, fail2ban, bind9-dnsutils
, openssl, ca-certificates, netcat-openbsd, iproute2 , openssl, ca-certificates, netcat-openbsd, iproute2
@ -33,23 +33,18 @@ Depends: ${python3:Depends}, ${misc:Depends}
Recommends: yunohost-admin Recommends: yunohost-admin
, ntp, inetutils-ping | iputils-ping , ntp, inetutils-ping | iputils-ping
, bash-completion, rsyslog , bash-completion, rsyslog
, php7.4-common, php7.4-fpm, php7.4-ldap, php7.4-intl
, mariadb-server, php7.4-mysql
, php7.4-gd, php7.4-curl, php-php-gettext
, python3-pip
, unattended-upgrades , unattended-upgrades
, libdbd-ldap-perl, libnet-dns-perl , libdbd-ldap-perl, libnet-dns-perl
, metronome (>=3.14.0)
Conflicts: iptables-persistent Conflicts: iptables-persistent
, apache2 , apache2
, bind9 , bind9
, nginx-extras (>= 1.19) , nginx-extras (>= 1.23)
, openssl (>= 1.1.1o-0) , openssl (>= 3.1)
, slapd (>= 2.4.58) , slapd (>= 2.6)
, dovecot-core (>= 1:2.3.14) , dovecot-core (>= 1:2.4)
, redis-server (>= 5:6.1) , redis-server (>= 5:7.1)
, fail2ban (>= 0.11.3) , fail2ban (>= 1.1)
, iptables (>= 1.8.8) , iptables (>= 1.8.10)
Description: manageable and configured self-hosting server Description: manageable and configured self-hosting server
YunoHost aims to make self-hosting accessible to everyone. It configures YunoHost aims to make self-hosting accessible to everyone. It configures
an email, Web and IM server alongside a LDAP base. It also provides an email, Web and IM server alongside a LDAP base. It also provides

View file

@ -62,8 +62,6 @@ for c in ResourceClasses:
for resource_id, doc in sorted(ResourceDocString.items()): for resource_id, doc in sorted(ResourceDocString.items()):
doc = doc.replace("\n ", "\n")
print("----------------") print("----------------")
print("") print("")
print(f"## {resource_id.replace('_', ' ').title()}") print(f"## {resource_id.replace('_', ' ').title()}")

View file

@ -210,6 +210,9 @@ ynh_mysql_setup_db() {
# If $db_pwd is not provided, use new_db_pwd instead for db_pwd # If $db_pwd is not provided, use new_db_pwd instead for db_pwd
db_pwd="${db_pwd:-$new_db_pwd}" db_pwd="${db_pwd:-$new_db_pwd}"
# Dirty patch for super-legacy apps
dpkg --list | grep -q "^ii mariadb-server" || { ynh_print_warn "Packager: you called ynh_mysql_setup_db without declaring a dependency to mariadb-server. Please add it to your apt dependencies !"; ynh_apt install mariadb-server; }
ynh_mysql_create_db "$db_name" "$db_user" "$db_pwd" ynh_mysql_create_db "$db_name" "$db_user" "$db_pwd"
ynh_app_setting_set --app=$app --key=mysqlpwd --value=$db_pwd ynh_app_setting_set --app=$app --key=mysqlpwd --value=$db_pwd
} }

View file

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
readonly YNH_DEFAULT_PHP_VERSION=7.4 readonly YNH_DEFAULT_PHP_VERSION=8.2
# Declare the actual PHP version to use. # Declare the actual PHP version to use.
# A packager willing to use another version of PHP can override the variable into its _common.sh. # A packager willing to use another version of PHP can override the variable into its _common.sh.
YNH_PHP_VERSION=${YNH_PHP_VERSION:-$YNH_DEFAULT_PHP_VERSION} YNH_PHP_VERSION=${YNH_PHP_VERSION:-$YNH_DEFAULT_PHP_VERSION}
@ -70,16 +70,15 @@ YNH_PHP_VERSION=${YNH_PHP_VERSION:-$YNH_DEFAULT_PHP_VERSION}
ynh_add_fpm_config() { ynh_add_fpm_config() {
local _globalphpversion=${phpversion-:} local _globalphpversion=${phpversion-:}
# Declare an array to define the options of this helper. # Declare an array to define the options of this helper.
local legacy_args=vufpd local legacy_args=vufg
local -A args_array=([v]=phpversion= [u]=usage= [f]=footprint= [p]=package= [d]=dedicated_service) local -A args_array=([v]=phpversion= [u]=usage= [f]=footprint= [g]=group=)
local group
local phpversion local phpversion
local usage local usage
local footprint local footprint
local package
local dedicated_service
# Manage arguments with getopts # Manage arguments with getopts
ynh_handle_getopts_args "$@" ynh_handle_getopts_args "$@"
package=${package:-} group=${group:-}
# The default behaviour is to use the template. # The default behaviour is to use the template.
local autogenconf=false local autogenconf=false
@ -105,8 +104,6 @@ ynh_add_fpm_config() {
fi fi
fi fi
# Do not use a dedicated service by default
dedicated_service=${dedicated_service:-0}
# Set the default PHP-FPM version by default # Set the default PHP-FPM version by default
if dpkg --compare-versions ${YNH_APP_PACKAGING_FORMAT:-0} lt 2; then if dpkg --compare-versions ${YNH_APP_PACKAGING_FORMAT:-0} lt 2; then
@ -130,45 +127,16 @@ ynh_add_fpm_config() {
fi fi
fi fi
# Legacy args (packager should just list their php dependency as regular apt dependencies... local fpm_service="php${phpversion}-fpm"
if [ -n "$package" ]; then local fpm_config_dir="/etc/php/$phpversion/fpm"
# Install the additionnal packages from the default repository
ynh_print_warn --message "Argument --package of ynh_add_fpm_config is deprecated and to be removed in the future"
ynh_install_app_dependencies "$package"
fi
if [ $dedicated_service -eq 1 ]; then
ynh_print_warn --message "Argument --dedicated_service of ynh_add_fpm_config is deprecated and to be removed in the future"
local fpm_service="${app}-phpfpm"
local fpm_config_dir="/etc/php/$phpversion/dedicated-fpm"
else
local fpm_service="php${phpversion}-fpm"
local fpm_config_dir="/etc/php/$phpversion/fpm"
fi
# Create the directory for FPM pools # Create the directory for FPM pools
mkdir --parents "$fpm_config_dir/pool.d" mkdir --parents "$fpm_config_dir/pool.d"
ynh_app_setting_set --app=$app --key=fpm_config_dir --value="$fpm_config_dir" ynh_app_setting_set --app=$app --key=fpm_config_dir --value="$fpm_config_dir"
ynh_app_setting_set --app=$app --key=fpm_service --value="$fpm_service" ynh_app_setting_set --app=$app --key=fpm_service --value="$fpm_service"
ynh_app_setting_set --app=$app --key=fpm_dedicated_service --value="$dedicated_service"
ynh_app_setting_set --app=$app --key=phpversion --value=$phpversion ynh_app_setting_set --app=$app --key=phpversion --value=$phpversion
# Migrate from mutual PHP service to dedicated one.
if [ $dedicated_service -eq 1 ]; then
local old_fpm_config_dir="/etc/php/$phpversion/fpm"
# If a config file exist in the common pool, move it.
if [ -e "$old_fpm_config_dir/pool.d/$app.conf" ]; then
ynh_print_info --message="Migrate to a dedicated php-fpm service for $app."
# Create a backup of the old file before migration
ynh_backup_if_checksum_is_different --file="$old_fpm_config_dir/pool.d/$app.conf"
# Remove the old PHP config file
ynh_secure_remove --file="$old_fpm_config_dir/pool.d/$app.conf"
# Reload PHP to release the socket and allow the dedicated service to use it
ynh_systemd_action --service_name=php${phpversion}-fpm --action=reload
fi
fi
if [ $autogenconf == "false" ]; then if [ $autogenconf == "false" ]; then
# Usage 1, use the template in conf/php-fpm.conf # Usage 1, use the template in conf/php-fpm.conf
local phpfpm_path="$YNH_APP_BASEDIR/conf/php-fpm.conf" local phpfpm_path="$YNH_APP_BASEDIR/conf/php-fpm.conf"
@ -180,12 +148,13 @@ ynh_add_fpm_config() {
# Define the values to use for the configuration of PHP. # Define the values to use for the configuration of PHP.
ynh_get_scalable_phpfpm --usage=$usage --footprint=$footprint ynh_get_scalable_phpfpm --usage=$usage --footprint=$footprint
local phpfpm_group=$([[ -n "$group" ]] && echo "$group" || echo "$app")
local phpfpm_path="$YNH_APP_BASEDIR/conf/php-fpm.conf" local phpfpm_path="$YNH_APP_BASEDIR/conf/php-fpm.conf"
echo " echo "
[__APP__] [__APP__]
user = __APP__ user = __APP__
group = __APP__ group = __PHPFPM_GROUP__
chdir = __INSTALL_DIR__ chdir = __INSTALL_DIR__
@ -221,56 +190,13 @@ pm.process_idle_timeout = 10s
local finalphpconf="$fpm_config_dir/pool.d/$app.conf" local finalphpconf="$fpm_config_dir/pool.d/$app.conf"
ynh_add_config --template="$phpfpm_path" --destination="$finalphpconf" ynh_add_config --template="$phpfpm_path" --destination="$finalphpconf"
if [ -e "$YNH_APP_BASEDIR/conf/php-fpm.ini" ]; then # Validate that the new php conf doesn't break php-fpm entirely
ynh_print_warn --message="Packagers ! Please do not use a separate php ini file, merge your directives in the pool file instead." if ! php-fpm${phpversion} --test 2>/dev/null; then
ynh_add_config --template="php-fpm.ini" --destination="$fpm_config_dir/conf.d/20-$app.ini" php-fpm${phpversion} --test || true
fi ynh_secure_remove --file="$finalphpconf"
ynh_die --message="The new configuration broke php-fpm?"
if [ $dedicated_service -eq 1 ]; then
# Create a dedicated php-fpm.conf for the service
local globalphpconf=$fpm_config_dir/php-fpm-$app.conf
echo "[global]
pid = /run/php/php__PHPVERSION__-fpm-__APP__.pid
error_log = /var/log/php/fpm-php.__APP__.log
syslog.ident = php-fpm-__APP__
include = __FINALPHPCONF__
" >$YNH_APP_BASEDIR/conf/php-fpm-$app.conf
ynh_add_config --template="php-fpm-$app.conf" --destination="$globalphpconf"
# Create a config for a dedicated PHP-FPM service for the app
echo "[Unit]
Description=PHP __PHPVERSION__ FastCGI Process Manager for __APP__
After=network.target
[Service]
Type=notify
PIDFile=/run/php/php__PHPVERSION__-fpm-__APP__.pid
ExecStart=/usr/sbin/php-fpm__PHPVERSION__ --nodaemonize --fpm-config __GLOBALPHPCONF__
ExecReload=/bin/kill -USR2 \$MAINPID
[Install]
WantedBy=multi-user.target
" >$YNH_APP_BASEDIR/conf/$fpm_service
# Create this dedicated PHP-FPM service
ynh_add_systemd_config --service=$fpm_service --template=$fpm_service
# Integrate the service in YunoHost admin panel
yunohost service add $fpm_service --log /var/log/php/fpm-php.$app.log --description "Php-fpm dedicated to $app"
# Configure log rotate
ynh_use_logrotate --logfile=/var/log/php
# Restart the service, as this service is either stopped or only for this app
ynh_systemd_action --service_name=$fpm_service --action=restart
else
# Validate that the new php conf doesn't break php-fpm entirely
if ! php-fpm${phpversion} --test 2>/dev/null; then
php-fpm${phpversion} --test || true
ynh_secure_remove --file="$finalphpconf"
ynh_die --message="The new configuration broke php-fpm?"
fi
ynh_systemd_action --service_name=$fpm_service --action=reload
fi fi
ynh_systemd_action --service_name=$fpm_service --action=reload
} }
# Remove the dedicated PHP-FPM config # Remove the dedicated PHP-FPM config
@ -281,8 +207,6 @@ WantedBy=multi-user.target
ynh_remove_fpm_config() { ynh_remove_fpm_config() {
local fpm_config_dir=$(ynh_app_setting_get --app=$app --key=fpm_config_dir) local fpm_config_dir=$(ynh_app_setting_get --app=$app --key=fpm_config_dir)
local fpm_service=$(ynh_app_setting_get --app=$app --key=fpm_service) local fpm_service=$(ynh_app_setting_get --app=$app --key=fpm_service)
local dedicated_service=$(ynh_app_setting_get --app=$app --key=fpm_dedicated_service)
dedicated_service=${dedicated_service:-0}
# Get the version of PHP used by this app # Get the version of PHP used by this app
local phpversion=$(ynh_app_setting_get --app=$app --key=phpversion) local phpversion=$(ynh_app_setting_get --app=$app --key=phpversion)
@ -296,69 +220,7 @@ ynh_remove_fpm_config() {
fi fi
ynh_secure_remove --file="$fpm_config_dir/pool.d/$app.conf" ynh_secure_remove --file="$fpm_config_dir/pool.d/$app.conf"
if [ -e $fpm_config_dir/conf.d/20-$app.ini ]; then ynh_systemd_action --service_name=$fpm_service --action=reload
ynh_secure_remove --file="$fpm_config_dir/conf.d/20-$app.ini"
fi
if [ $dedicated_service -eq 1 ]; then
# Remove the dedicated service PHP-FPM service for the app
ynh_remove_systemd_config --service=$fpm_service
# Remove the global PHP-FPM conf
ynh_secure_remove --file="$fpm_config_dir/php-fpm-$app.conf"
# Remove the service from the list of services known by YunoHost
yunohost service remove $fpm_service
elif ynh_package_is_installed --package="php${phpversion}-fpm"; then
ynh_systemd_action --service_name=$fpm_service --action=reload
fi
# If the PHP version used is not the default version for YunoHost
# The second part with YNH_APP_PURGE is an ugly hack to guess that we're inside the remove script
# (we don't actually care about its value, we just check its not empty hence it exists)
if [ "$phpversion" != "$YNH_DEFAULT_PHP_VERSION" ] && [ -n "${YNH_APP_PURGE:-}" ] && dpkg --compare-versions ${YNH_APP_PACKAGING_FORMAT:-0} lt 2; then
# Remove app dependencies ... but ideally should happen via an explicit call from packager
ynh_remove_app_dependencies
fi
}
# Install another version of PHP.
#
# [internal]
#
# Legacy, to be remove on bullseye
#
# usage: ynh_install_php --phpversion=phpversion [--package=packages]
# | arg: -v, --phpversion= - Version of PHP to install.
# | arg: -p, --package= - Additionnal PHP packages to install
#
# Requires YunoHost version 3.8.1 or higher.
ynh_install_php() {
# Declare an array to define the options of this helper.
local legacy_args=vp
local -A args_array=([v]=phpversion= [p]=package=)
local phpversion
local package
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
package=${package:-}
if [ "$phpversion" == "$YNH_DEFAULT_PHP_VERSION" ]; then
ynh_die --message="Do not use ynh_install_php to install php$YNH_DEFAULT_PHP_VERSION"
fi
ynh_install_app_dependencies "$package"
}
# Remove the specific version of PHP used by the app.
#
# [internal]
#
# Legacy, to be remove on bullseye
#
# usage: ynh_remove_php
#
# Requires YunoHost version 3.8.1 or higher.
ynh_remove_php () {
ynh_remove_app_dependencies
} }
# Define the values to configure PHP-FPM # Define the values to configure PHP-FPM

View file

@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
PSQL_ROOT_PWD_FILE=/etc/yunohost/psql PSQL_ROOT_PWD_FILE=/etc/yunohost/psql
PSQL_VERSION=13 PSQL_VERSION=15
# Open a connection as a user # Open a connection as a user
# #

View file

@ -18,11 +18,7 @@ ynh_app_setting_get() {
ynh_handle_getopts_args "$@" ynh_handle_getopts_args "$@"
app="${app:-$_globalapp}" app="${app:-$_globalapp}"
if [[ $key =~ (unprotected|protected|skipped)_ ]]; then ynh_app_setting "get" "$app" "$key"
yunohost app setting $app $key
else
ynh_app_setting "get" "$app" "$key"
fi
} }
# Set an application setting # Set an application setting
@ -45,11 +41,7 @@ ynh_app_setting_set() {
ynh_handle_getopts_args "$@" ynh_handle_getopts_args "$@"
app="${app:-$_globalapp}" app="${app:-$_globalapp}"
if [[ $key =~ (unprotected|protected|skipped)_ ]]; then ynh_app_setting "set" "$app" "$key" "$value"
yunohost app setting $app $key -v $value
else
ynh_app_setting "set" "$app" "$key" "$value"
fi
} }
# Delete an application setting # Delete an application setting
@ -70,11 +62,7 @@ ynh_app_setting_delete() {
ynh_handle_getopts_args "$@" ynh_handle_getopts_args "$@"
app="${app:-$_globalapp}" app="${app:-$_globalapp}"
if [[ "$key" =~ (unprotected|skipped|protected)_ ]]; then ynh_app_setting "delete" "$app" "$key"
yunohost app setting $app $key -d
else
ynh_app_setting "delete" "$app" "$key"
fi
} }
# Small "hard-coded" interface to avoid calling "yunohost app" directly each # Small "hard-coded" interface to avoid calling "yunohost app" directly each

View file

@ -75,7 +75,7 @@ fi
# usage: ynh_setup_source --dest_dir=dest_dir [--source_id=source_id] [--keep="file1 file2"] [--full_replace] # usage: ynh_setup_source --dest_dir=dest_dir [--source_id=source_id] [--keep="file1 file2"] [--full_replace]
# | arg: -d, --dest_dir= - Directory where to setup sources # | arg: -d, --dest_dir= - Directory where to setup sources
# | arg: -s, --source_id= - Name of the source, defaults to `main` (when the sources resource exists in manifest.toml) or (legacy) `app` otherwise # | arg: -s, --source_id= - Name of the source, defaults to `main` (when the sources resource exists in manifest.toml) or (legacy) `app` otherwise
# | arg: -k, --keep= - Space-separated list of files/folders that will be backup/restored in $dest_dir, such as a config file you don't want to overwrite. For example 'conf.json secrets.json logs/' # | arg: -k, --keep= - Space-separated list of files/folders that will be backup/restored in $dest_dir, such as a config file you don't want to overwrite. For example 'conf.json secrets.json logs' (no trailing `/` for folders)
# | arg: -r, --full_replace= - Remove previous sources before installing new sources # | arg: -r, --full_replace= - Remove previous sources before installing new sources
# #
# #### New 'sources' resources # #### New 'sources' resources
@ -244,9 +244,11 @@ ynh_setup_source() {
if [ "$src_format" = "docker" ]; then if [ "$src_format" = "docker" ]; then
src_platform="${src_platform:-"linux/$YNH_ARCH"}" src_platform="${src_platform:-"linux/$YNH_ARCH"}"
elif test -e "$local_src"; then
cp $local_src $src_filename
else else
if test -e "$local_src"; then
cp $local_src $src_filename
fi
[ -n "$src_url" ] || ynh_die "Couldn't parse SOURCE_URL from $src_file_path ?" [ -n "$src_url" ] || ynh_die "Couldn't parse SOURCE_URL from $src_file_path ?"
# If the file was prefetched but somehow doesn't match the sum, rm and redownload it # If the file was prefetched but somehow doesn't match the sum, rm and redownload it

View file

@ -9,5 +9,5 @@ source /usr/share/yunohost/helpers
# Backup destination # Backup destination
backup_dir="${1}/data/xmpp" backup_dir="${1}/data/xmpp"
ynh_backup /var/lib/metronome "${backup_dir}/var_lib_metronome" [[ ! -d /var/lib/metronome ]] || ynh_backup /var/lib/metronome "${backup_dir}/var_lib_metronome" --not_mandatory
ynh_backup /var/xmpp-upload/ "${backup_dir}/var_xmpp-upload" [[ ! -d /var/xmpp-upload ]] || ynh_backup /var/xmpp-upload/ "${backup_dir}/var_xmpp-upload" --not_mandatory

View file

@ -11,7 +11,7 @@ do_pre_regen() {
# Add sury # Add sury
mkdir -p ${pending_dir}/etc/apt/sources.list.d/ mkdir -p ${pending_dir}/etc/apt/sources.list.d/
echo "deb https://packages.sury.org/php/ $(lsb_release --codename --short) main" > "${pending_dir}/etc/apt/sources.list.d/extra_php_version.list" echo "deb [signed-by=/etc/apt/trusted.gpg.d/extra_php_version.gpg] https://packages.sury.org/php/ $(lsb_release --codename --short) main" > "${pending_dir}/etc/apt/sources.list.d/extra_php_version.list"
# Ban some packages from sury # Ban some packages from sury
echo " echo "
@ -23,19 +23,33 @@ Pin-Priority: 500" >>"${pending_dir}/etc/apt/preferences.d/extra_php_version"
for package in $packages_to_refuse_from_sury; do for package in $packages_to_refuse_from_sury; do
echo " echo "
Package: $package Package: $package
Pin: origin \"packages.sury.org\" Pin: origin \"packages.sury.org\"
Pin-Priority: -1" >>"${pending_dir}/etc/apt/preferences.d/extra_php_version" Pin-Priority: -1" >>"${pending_dir}/etc/apt/preferences.d/extra_php_version"
done done
# Add yarn
echo "deb [signed-by=/etc/apt/trusted.gpg.d/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > "${pending_dir}/etc/apt/sources.list.d/yarn.list"
# Ban everything from Yarn except Yarn
echo "
Package: *
Pin: origin \"dl.yarnpkg.com\"
Pin-Priority: -1
Package: yarn
Pin: origin \"dl.yarnpkg.com\"
Pin-Priority: 500" >>"${pending_dir}/etc/apt/preferences.d/yarn"
# Ban apache2, bind9
echo " echo "
# PLEASE READ THIS WARNING AND DON'T EDIT THIS FILE # PLEASE READ THIS WARNING AND DON'T EDIT THIS FILE
# You are probably reading this file because you tried to install apache2 or # You are probably reading this file because you tried to install apache2 or
# bind9. These 2 packages conflict with YunoHost. # bind9. These 2 packages conflict with YunoHost.
# Installing apache2 will break nginx and break the entire YunoHost ecosystem # Installing apache2 will break nginx and break the entire YunoHost ecosystem
# on your server, therefore don't remove those lines! # on your server, therefore don't remove those lines!
# You have been warned. # You have been warned.
@ -69,6 +83,12 @@ do_post_regen() {
wget --timeout 900 --quiet "https://packages.sury.org/php/apt.gpg" --output-document=- | gpg --dearmor >"/etc/apt/trusted.gpg.d/extra_php_version.gpg" wget --timeout 900 --quiet "https://packages.sury.org/php/apt.gpg" --output-document=- | gpg --dearmor >"/etc/apt/trusted.gpg.d/extra_php_version.gpg"
fi fi
# Similar to Sury
if [[ ! -s /etc/apt/trusted.gpg.d/yarn.gpg ]]
then
wget --timeout 900 --quiet "https://dl.yarnpkg.com/debian/pubkey.gpg" --output-document=- | gpg --dearmor >"/etc/apt/trusted.gpg.d/yarn.gpg"
fi
# Make sure php7.4 is the default version when using php in cli # Make sure php7.4 is the default version when using php in cli
if test -e /usr/bin/php$YNH_DEFAULT_PHP_VERSION if test -e /usr/bin/php$YNH_DEFAULT_PHP_VERSION
then then

View file

@ -1,4 +1,11 @@
backup_dir="$1/data/xmpp" backup_dir="$1/data/xmpp"
cp -a $backup_dir/var_lib_metronome/. /var/lib/metronome if [[ -e $backup_dir/var_lib_metronome/ ]]
cp -a $backup_dir/var_xmpp-upload/. /var/xmpp-upload then
cp -a $backup_dir/var_lib_metronome/. /var/lib/metronome
fi
if [[ -e $backup_dir/var_xmpp-upload ]]
then
cp -a $backup_dir/var_xmpp-upload/. /var/xmpp-upload
fi

View file

@ -588,7 +588,20 @@
"migration_0024_rebuild_python_venv_disclaimer_rebuild": "Rebuilding the virtualenv will be attempted for the following apps (NB: the operation may take some time!): {rebuild_apps}", "migration_0024_rebuild_python_venv_disclaimer_rebuild": "Rebuilding the virtualenv will be attempted for the following apps (NB: the operation may take some time!): {rebuild_apps}",
"migration_0024_rebuild_python_venv_failed": "Failed to rebuild the Python virtualenv for {app}. The app may not work as long as this is not resolved. You should fix the situation by forcing the upgrade of this app using `yunohost app upgrade --force {app}`.", "migration_0024_rebuild_python_venv_failed": "Failed to rebuild the Python virtualenv for {app}. The app may not work as long as this is not resolved. You should fix the situation by forcing the upgrade of this app using `yunohost app upgrade --force {app}`.",
"migration_0024_rebuild_python_venv_in_progress": "Now attempting to rebuild the Python virtualenv for `{app}`", "migration_0024_rebuild_python_venv_in_progress": "Now attempting to rebuild the Python virtualenv for `{app}`",
"migration_description_0021_migrate_to_bullseye": "Upgrade the system to Debian Bullseye and YunoHost 11.x", "migration_0027_cleaning_up": "Cleaning up cache and packages not useful anymore...",
"migration_0027_hgjghjghjgeneral_warning": "Please note that this migration is a delicate operation. The YunoHost team did its best to review and test it, but the migration might still break parts of the system or its apps.\n\nTherefore, it is recommended to:\n - Perform a backup of any critical data or app. More info on https://yunohost.org/backup;\n - Be patient after launching the migration: Depending on your Internet connection and hardware, it might take up to a few hours for everything to upgrade.",
"migration_0027_main_upgrade": "Starting main upgrade...",
"migration_0027_modified_files": "Please note that the following files were found to be manually modified and might be overwritten following the upgrade: {manually_modified_files}",
"migration_0027_not_buster2": "The current Debian distribution is not Bullseye! If you already ran the Bullseye->Bookworm migration, then this error is symptomatic of the fact that the migration procedure was not 100% succesful (otherwise YunoHost would have flagged it as completed). It is recommended to investigate what happened with the support team, who will need the **full** log of the `migration, which can be found in Tools > Logs in the webadmin.",
"migration_0027_not_enough_free_space": "Free space is pretty low in /var/! You should have at least 1GB free to run this migration.",
"migration_0027_patch_yunohost_conflicts": "Applying patch to workaround conflict issue...",
"migration_0027_patching_sources_list": "Patching the sources.lists...",
"migration_0027_problematic_apps_warning": "Please note that the following possibly problematic installed apps were detected. It looks like those were not installed from the YunoHost app catalog, or are not flagged as 'working'. Consequently, it cannot be guaranteed that they will still work after the upgrade: {problematic_apps}",
"migration_0027_start": "Starting migration to Bookworm",
"migration_0027_still_on_buster_after_main_upgrade": "Something went wrong during the main upgrade, the system appears to still be on Debian Bullseye",
"migration_0027_system_not_fully_up_to_date": "Your system is not fully up-to-date. Please perform a regular upgrade before running the migration to Bookworm.",
"migration_0027_yunohost_upgrade": "Starting YunoHost core upgrade...",
"migration_description_0021_migrate_to_bullseye": "Upgrade the system to Debian Bookworm and YunoHost 12",
"migration_description_0022_php73_to_php74_pools": "Migrate php7.3-fpm 'pool' conf files to php7.4", "migration_description_0022_php73_to_php74_pools": "Migrate php7.3-fpm 'pool' conf files to php7.4",
"migration_description_0023_postgresql_11_to_13": "Migrate databases from PostgreSQL 11 to 13", "migration_description_0023_postgresql_11_to_13": "Migrate databases from PostgreSQL 11 to 13",
"migration_description_0024_rebuild_python_venv": "Repair Python app after bullseye migration", "migration_description_0024_rebuild_python_venv": "Repair Python app after bullseye migration",

View file

@ -522,8 +522,8 @@
"diagnosis_mail_outgoing_port_25_blocked_relay_vpn": "Algunos proveedores de internet no le permitirán desbloquear el puerto 25 porque no les importa la Neutralidad de la Red.<br> - Algunos proporcionan una alternativa usando <a href='https://yunohost.org/#/email_configure_relay'>un relay como servidor de correo</a> lo que implica que el relay podrá espiar tu tráfico de correo.<br>- Una alternativa buena para la privacidad es utilizar una VPN *con una IP pública dedicada* para evitar estas limitaciones. Mira en <a href='https://yunohost.org/#/vpn_advantage'>https://yunohost.org/#/vpn_advantage</a><br>- Otra alternativa es cambiar de proveedor de internet a <a href='https://yunohost.org/#/isp'>uno más amable con la Neutralidad de la Red</a>", "diagnosis_mail_outgoing_port_25_blocked_relay_vpn": "Algunos proveedores de internet no le permitirán desbloquear el puerto 25 porque no les importa la Neutralidad de la Red.<br> - Algunos proporcionan una alternativa usando <a href='https://yunohost.org/#/email_configure_relay'>un relay como servidor de correo</a> lo que implica que el relay podrá espiar tu tráfico de correo.<br>- Una alternativa buena para la privacidad es utilizar una VPN *con una IP pública dedicada* para evitar estas limitaciones. Mira en <a href='https://yunohost.org/#/vpn_advantage'>https://yunohost.org/#/vpn_advantage</a><br>- Otra alternativa es cambiar de proveedor de internet a <a href='https://yunohost.org/#/isp'>uno más amable con la Neutralidad de la Red</a>",
"diagnosis_backports_in_sources_list": "Parece que apt (el gestor de paquetes) está configurado para usar el repositorio backports. A menos que realmente sepas lo que estás haciendo, desaconsejamos absolutamente instalar paquetes desde backports, ya que pueden provocar comportamientos intestables o conflictos en el sistema.", "diagnosis_backports_in_sources_list": "Parece que apt (el gestor de paquetes) está configurado para usar el repositorio backports. A menos que realmente sepas lo que estás haciendo, desaconsejamos absolutamente instalar paquetes desde backports, ya que pueden provocar comportamientos intestables o conflictos en el sistema.",
"diagnosis_basesystem_hardware_model": "El modelo de servidor es {model}", "diagnosis_basesystem_hardware_model": "El modelo de servidor es {model}",
"additional_urls_already_removed": "La URL adicional '{url}' ya se ha eliminado para el permiso «{permission}»", "additional_urls_already_removed": "URL adicional '{url}' ya eliminada en la URL adicional para permiso «{permission}»",
"additional_urls_already_added": "La URL adicional '{url}' ya se ha añadido para el permiso «{permission}»", "additional_urls_already_added": "URL adicional '{url}' ya añadida en la URL adicional para permiso «{permission}»",
"config_apply_failed": "Falló la aplicación de la nueva configuración: {error}", "config_apply_failed": "Falló la aplicación de la nueva configuración: {error}",
"app_restore_script_failed": "Ha ocurrido un error dentro del script de restauración de aplicaciones", "app_restore_script_failed": "Ha ocurrido un error dentro del script de restauración de aplicaciones",
"app_config_unable_to_apply": "No se pudieron aplicar los valores del panel configuración.", "app_config_unable_to_apply": "No se pudieron aplicar los valores del panel configuración.",
@ -747,5 +747,10 @@
"global_settings_setting_smtp_relay_host": "Host de retransmisión SMTP", "global_settings_setting_smtp_relay_host": "Host de retransmisión SMTP",
"migration_0024_rebuild_python_venv_disclaimer_rebuild": "Se intentará reconstruir el virtualenv para las siguientes apps (NB: ¡la operación puede llevar algún tiempo!): {rebuild_apps}", "migration_0024_rebuild_python_venv_disclaimer_rebuild": "Se intentará reconstruir el virtualenv para las siguientes apps (NB: ¡la operación puede llevar algún tiempo!): {rebuild_apps}",
"migration_description_0025_global_settings_to_configpanel": "Migración de la nomenclatura de ajustes globales heredada a la nomenclatura nueva y moderna", "migration_description_0025_global_settings_to_configpanel": "Migración de la nomenclatura de ajustes globales heredada a la nomenclatura nueva y moderna",
"registrar_infos": "Información sobre el registrador" "registrar_infos": "Información sobre el registrador",
} "app_failed_to_download_asset": "Error al descargar el recurso '{source_id}' ({url}) para {app}: {out}",
"app_corrupt_source": "YunoHost ha podido descargar el recurso '{source_id}' ({url}) para {app}, pero no coincide con la suma de comprobación esperada. Esto puede significar que ocurrió un fallo de red en tu servidor, o que el recurso ha sido modificado por el responsable de la aplicación (¿o un actor malicioso?) y los responsables de empaquetar esta aplicación para YunoHost necesitan investigar y actualizar el manifesto de la aplicación para reflejar estos cambios. \n Suma de control sha256 esperada: {expected_sha256}\n Suma de control sha256 descargada: {computed_sha256}\n Tamaño del archivo descargado: {size}",
"app_change_url_failed": "No es possible cambiar la URL para {app}: {error}",
"app_change_url_require_full_domain": "{app} no se puede mover a esta nueva URL porque requiere un dominio completo (es decir, con una ruta = /)",
"app_change_url_script_failed": "Se ha producido un error en el script de modificación de la url"
}

View file

@ -527,7 +527,7 @@
"pattern_email_forward": "L'adresse électronique doit être valide, le symbole '+' étant accepté (par exemple : johndoe+yunohost@exemple.com)", "pattern_email_forward": "L'adresse électronique doit être valide, le symbole '+' étant accepté (par exemple : johndoe+yunohost@exemple.com)",
"global_settings_setting_smtp_relay_password": "Mot de passe du relais SMTP", "global_settings_setting_smtp_relay_password": "Mot de passe du relais SMTP",
"diagnosis_package_installed_from_sury": "Des paquets du système devraient être rétrogradé de version", "diagnosis_package_installed_from_sury": "Des paquets du système devraient être rétrogradé de version",
"additional_urls_already_added": "URL supplémentaire '{url}' déjà ajoutée pour la permission '{permission}'", "additional_urls_already_added": "L'URL supplémentaire '{url}' a déjà été ajoutée pour la permission '{permission}'",
"unknown_main_domain_path": "Domaine ou chemin inconnu pour '{app}'. Vous devez spécifier un domaine et un chemin pour pouvoir spécifier une URL pour l'autorisation.", "unknown_main_domain_path": "Domaine ou chemin inconnu pour '{app}'. Vous devez spécifier un domaine et un chemin pour pouvoir spécifier une URL pour l'autorisation.",
"show_tile_cant_be_enabled_for_regex": "Vous ne pouvez pas activer 'show_tile' pour le moment, cela car l'URL de l'autorisation '{permission}' est une expression régulière", "show_tile_cant_be_enabled_for_regex": "Vous ne pouvez pas activer 'show_tile' pour le moment, cela car l'URL de l'autorisation '{permission}' est une expression régulière",
"show_tile_cant_be_enabled_for_url_not_defined": "Vous ne pouvez pas activer 'show_tile' pour le moment, car vous devez d'abord définir une URL pour l'autorisation '{permission}'", "show_tile_cant_be_enabled_for_url_not_defined": "Vous ne pouvez pas activer 'show_tile' pour le moment, car vous devez d'abord définir une URL pour l'autorisation '{permission}'",
@ -536,7 +536,7 @@
"permission_protected": "L'autorisation {permission} est protégée. Vous ne pouvez pas ajouter ou supprimer le groupe visiteurs à/de cette autorisation.", "permission_protected": "L'autorisation {permission} est protégée. Vous ne pouvez pas ajouter ou supprimer le groupe visiteurs à/de cette autorisation.",
"invalid_regex": "Regex non valide : '{regex}'", "invalid_regex": "Regex non valide : '{regex}'",
"app_label_deprecated": "Cette commande est obsolète ! Veuillez utiliser la nouvelle commande 'yunohost user permission update' pour gérer l'étiquette de l'application.", "app_label_deprecated": "Cette commande est obsolète ! Veuillez utiliser la nouvelle commande 'yunohost user permission update' pour gérer l'étiquette de l'application.",
"additional_urls_already_removed": "URL supplémentaire '{url}' déjà supprimées pour la permission '{permission}'", "additional_urls_already_removed": "L'URL supplémentaire '{url}' a déjà été supprimée pour la permission '{permission}'",
"invalid_number": "Doit être un nombre", "invalid_number": "Doit être un nombre",
"diagnosis_basesystem_hardware_model": "Le modèle/architecture du serveur est {model}", "diagnosis_basesystem_hardware_model": "Le modèle/architecture du serveur est {model}",
"diagnosis_backports_in_sources_list": "Il semble que le gestionnaire de paquet APT soit configuré pour utiliser le dépôt des rétro-portages (backports). A moins que vous ne sachiez vraiment ce que vous faites, nous vous déconseillons fortement d'installer des paquets provenant du dépôt 'backports', car cela risque de créer des instabilités ou des conflits sur votre système.", "diagnosis_backports_in_sources_list": "Il semble que le gestionnaire de paquet APT soit configuré pour utiliser le dépôt des rétro-portages (backports). A moins que vous ne sachiez vraiment ce que vous faites, nous vous déconseillons fortement d'installer des paquets provenant du dépôt 'backports', car cela risque de créer des instabilités ou des conflits sur votre système.",
@ -766,5 +766,21 @@
"group_mailalias_add": "L'alias de courrier électronique '{mail}' sera ajouté au groupe '{group}'", "group_mailalias_add": "L'alias de courrier électronique '{mail}' sera ajouté au groupe '{group}'",
"group_user_add": "L'utilisateur '{user}' sera ajouté au groupe '{group}'", "group_user_add": "L'utilisateur '{user}' sera ajouté au groupe '{group}'",
"group_user_remove": "L'utilisateur '{user}' sera retiré du groupe '{group}'", "group_user_remove": "L'utilisateur '{user}' sera retiré du groupe '{group}'",
"group_mailalias_remove": "L'alias de courrier électronique '{mail}' sera supprimé du groupe '{group}'" "group_mailalias_remove": "L'alias de courrier électronique '{mail}' sera supprimé du groupe '{group}'",
"ask_dyndns_recovery_password_explain": "Veuillez choisir un mot de passe de récupération pour votre domaine DynDNS, au cas où vous devriez le réinitialiser plus tard.",
"ask_dyndns_recovery_password": "Mot de passe de récupération DynDNS",
"ask_dyndns_recovery_password_explain_during_unsubscribe": "Veuillez saisir le mot de passe de récupération pour ce domaine DynDNS.",
"dyndns_no_recovery_password": "Aucun mot de passe de récupération n'a été spécifié ! Si vous perdez le contrôle de ce domaine, vous devrez contacter un administrateur de l'équipe YunoHost !",
"dyndns_subscribed": "Domaine DynDNS souscrit/enregistré",
"dyndns_subscribe_failed": "Impossible de souscrire/de s'enregistrer au domaine DynDNS : {erreur}",
"dyndns_unsubscribe_failed": "Impossible de se désinscrire du domaine DynDNS : {erreur}",
"dyndns_unsubscribed": "Désinscription du domaine DynDNS",
"dyndns_unsubscribe_denied": "Échec de la désinscription du domaine : informations d'identification non valides",
"dyndns_unsubscribe_already_unsubscribed": "Le domaine est déjà désabonné/retiré",
"dyndns_set_recovery_password_denied": "Échec de la définition du mot de passe de récupération : mot de passe non valide",
"dyndns_set_recovery_password_unknown_domain": "Échec de la définition du mot de passe de récupération : le domaine n'est pas enregistré",
"dyndns_set_recovery_password_invalid_password": "Échec de la définition du mot de passe de récupération : le mot de passe n'est pas assez fort/solide",
"dyndns_set_recovery_password_failed": "Échec de la définition du mot de passe de récupération : {erreur}",
"dyndns_set_recovery_password_success": "Mot de passe de récupération défini/configuré !",
"log_dyndns_unsubscribe": "Se désabonner d'un sous-domaine YunoHost '{}'"
} }

View file

@ -612,7 +612,7 @@
"domain_config_auth_consumer_key": "Chave consumidora", "domain_config_auth_consumer_key": "Chave consumidora",
"log_domain_dns_push": "Enviar rexistros DNS para o dominio '{}'", "log_domain_dns_push": "Enviar rexistros DNS para o dominio '{}'",
"other_available_options": "... e outras {n} opcións dispoñibles non mostradas", "other_available_options": "... e outras {n} opcións dispoñibles non mostradas",
"domain_dns_registrar_yunohost": "Este dominio un dos de nohost.me / nohost.st / ynh.fr e a configuración DNS xestionaa directamente YunoHost se máis requisitos. (mira o comando 'yunohost dyndns update')", "domain_dns_registrar_yunohost": "Este dominio é un dos de nohost.me / nohost.st / ynh.fr e a configuración DNS xestionaa directamente YunoHost sen máis requisitos. (mira o comando 'yunohost dyndns update')",
"domain_dns_registrar_supported": "YunoHost detectou automáticamente que este dominio está xestionado pola rexistradora **{registrar}**. Se queres, YunoHost pode configurar automáticamente as súas zonas DNS, se proporcionas as credenciais de acceso á API. Podes ver a documentación sobre como obter as credenciais da API nesta páxina: https://yunohost.org/registrar_api_{registrar}. (Tamén podes configurar manualmente os rexistros DNS seguindo a documentación en https://yunohost.org/dns )", "domain_dns_registrar_supported": "YunoHost detectou automáticamente que este dominio está xestionado pola rexistradora **{registrar}**. Se queres, YunoHost pode configurar automáticamente as súas zonas DNS, se proporcionas as credenciais de acceso á API. Podes ver a documentación sobre como obter as credenciais da API nesta páxina: https://yunohost.org/registrar_api_{registrar}. (Tamén podes configurar manualmente os rexistros DNS seguindo a documentación en https://yunohost.org/dns )",
"domain_dns_push_partial_failure": "Actualización parcial dos rexistros DNS: informouse dalgúns avisos/erros.", "domain_dns_push_partial_failure": "Actualización parcial dos rexistros DNS: informouse dalgúns avisos/erros.",
"domain_config_auth_token": "Token de autenticación", "domain_config_auth_token": "Token de autenticación",
@ -654,7 +654,7 @@
"global_settings_setting_admin_strength": "Fortaleza do contrasinal de Admin", "global_settings_setting_admin_strength": "Fortaleza do contrasinal de Admin",
"global_settings_setting_user_strength": "Fortaleza do contrasinal da usuaria", "global_settings_setting_user_strength": "Fortaleza do contrasinal da usuaria",
"global_settings_setting_postfix_compatibility_help": "Compromiso entre compatibilidade e seguridade para o servidor Postfix. Aféctalle ao cifrado (e outros aspectos da seguridade)", "global_settings_setting_postfix_compatibility_help": "Compromiso entre compatibilidade e seguridade para o servidor Postfix. Aféctalle ao cifrado (e outros aspectos da seguridade)",
"global_settings_setting_ssh_compatibility_help": "Compromiso entre compatibilidade e seguridade para o servidor SSH. Aféctalle ao cifrado (e outros aspectos da seguridade)", "global_settings_setting_ssh_compatibility_help": "Compromiso entre compatibilidade e seguridade para o servidor SSH. Aféctalle ao cifrado (e outros aspectos da seguridade). Le https://infosec.mozilla.org/guidelines/openssh for more info.",
"global_settings_setting_ssh_password_authentication_help": "Permitir autenticación con contrasinal para SSH", "global_settings_setting_ssh_password_authentication_help": "Permitir autenticación con contrasinal para SSH",
"global_settings_setting_ssh_port": "Porto SSH", "global_settings_setting_ssh_port": "Porto SSH",
"global_settings_setting_webadmin_allowlist_help": "Enderezos IP con permiso para acceder á webadmin. Separados por vírgulas.", "global_settings_setting_webadmin_allowlist_help": "Enderezos IP con permiso para acceder á webadmin. Separados por vírgulas.",
@ -766,5 +766,21 @@
"group_mailalias_add": "Vaise engadir o alias de correo '{mail}' ao grupo '{group}'", "group_mailalias_add": "Vaise engadir o alias de correo '{mail}' ao grupo '{group}'",
"group_mailalias_remove": "Vaise quitar o alias de email '{mail}' do grupo '{group}'", "group_mailalias_remove": "Vaise quitar o alias de email '{mail}' do grupo '{group}'",
"group_user_add": "Vaise engadir a '{user}' ao grupo '{grupo}'", "group_user_add": "Vaise engadir a '{user}' ao grupo '{grupo}'",
"group_user_remove": "Vaise quitar a '{user}' do grupo '{grupo}'" "group_user_remove": "Vaise quitar a '{user}' do grupo '{grupo}'",
"ask_dyndns_recovery_password_explain": "Elixe un contrasinal de recuperación para o teu dominio DynDNS, por se precisas restablecelo no futuro.",
"ask_dyndns_recovery_password": "Contrasinal de recuperación DynDNS",
"ask_dyndns_recovery_password_explain_during_unsubscribe": "Escribe o contrasinal de recuperación para este dominio DynDNS.",
"dyndns_no_recovery_password": "Non se estableceu un contrasinal de recuperación! Se perdes o control sobre dominio precisarás contactar coa administración do equipo YunoHost!",
"dyndns_subscribed": "Tes unha subscrición a un dominio DynDNS",
"dyndns_subscribe_failed": "Non te subscribiches ao dominio DynDNS: {error}",
"dyndns_unsubscribe_failed": "Non se retirou a subscrición ao dominio DynDNS: {error}",
"dyndns_unsubscribed": "Retirada a subscrición ao dominio DynDNS",
"dyndns_unsubscribe_denied": "Fallo ao intentar retirar subscrición: credenciais incorrectas",
"dyndns_unsubscribe_already_unsubscribed": "Non tes unha subscrición ao dominio",
"dyndns_set_recovery_password_denied": "Fallou o establecemento do contrasinal de recuperación: chave non válida",
"dyndns_set_recovery_password_unknown_domain": "Fallo ao establecer o contrasinal de recuperación: dominio non rexistrado",
"dyndns_set_recovery_password_invalid_password": "Fallo ao establecer contrasinal de recuperación: o contrasinal non é suficientemente forte",
"dyndns_set_recovery_password_failed": "Fallo ao establecer o contrasinal de recuperación: {error}",
"dyndns_set_recovery_password_success": "Estableceuse o contrasinal de recuperación!",
"log_dyndns_unsubscribe": "Retirar subscrición para o subdominio YunoHost '{}'"
} }

View file

@ -391,5 +391,54 @@
"log_letsencrypt_cert_renew": "Memperbarui sertifikat Let's Encrypt '{}'", "log_letsencrypt_cert_renew": "Memperbarui sertifikat Let's Encrypt '{}'",
"log_selfsigned_cert_install": "Memasang sertifikat ditandai sendiri pada domain '{}'", "log_selfsigned_cert_install": "Memasang sertifikat ditandai sendiri pada domain '{}'",
"log_user_permission_reset": "Mengatur ulang izin '{}'", "log_user_permission_reset": "Mengatur ulang izin '{}'",
"domain_config_xmpp": "Pesan Langsung (XMPP)" "domain_config_xmpp": "Pesan Langsung (XMPP)",
"diagnosis_http_connection_error": "Masalah jaringan: tidak dapat terhubung dengan domain yang diminta, sangat mungkin terputus.",
"dyndns_ip_updated": "IP Anda diperbarui di DynDNS",
"ask_dyndns_recovery_password_explain": "Pilih kata sandi pemulihan untuk domain DynDNS Anda.",
"ask_dyndns_recovery_password": "Kata sandi pemulihan DynDNS",
"backup_output_directory_not_empty": "Anda harus memilih direktori yang kosong",
"service_reload_or_restart_failed": "Tidak dapat memuat atau memulai ulang layanan '{service}'\n\nLog layanan baru-baru ini:{logs}",
"service_reload_failed": "Tidak dapat memuat ulang layanan '{service}'\n\nLog layanan baru-baru ini:{logs}",
"service_start_failed": "Tidak dapat memulai layanan '{service}'\n\nLog layanan baru-baru ini: {logs}",
"diagnosis_apps_deprecated_practices": "Versi aplikasi yang dipasang ini masih menggunakan praktik pengemasan yang lama. Anda lebih baik untuk memperbarui aplikasi tersebut.",
"diagnosis_dns_bad_conf": "Beberapa rekaman DNS untuk domain {domain} ada yang tidak ada atau salah (kategori {category})",
"diagnosis_dns_good_conf": "Rekaman DNS untuk domain {domain} sudah diatur dengan benar (kategori {category})",
"dyndns_unavailable": "Domain '{domain}' tidak tersedia.",
"dyndns_set_recovery_password_denied": "Tidak dapat menyetel kata sandi pemulihan: tidak valid",
"dyndns_set_recovery_password_unknown_domain": "Tidak dapat menyetel kata sandi pemulihan: domain belum terdaftar",
"dyndns_set_recovery_password_invalid_password": "Tidak dapat menyetel kata sandi pemulihan: kata sandi tidak cukup kuat",
"dyndns_set_recovery_password_failed": "Tidak dapat menyetel kata sandi pemulihan: {error}",
"dyndns_set_recovery_password_success": "Kata sandi pemulihan berhasil disetel!",
"file_does_not_exist": "Berkas {path} tidak ada.",
"firewall_reload_failed": "Tidak dapat memuat ulang tembok api",
"firewall_reloaded": "Tembok api dimuat ulang",
"migration_description_0023_postgresql_11_to_13": "Migrasi basis data dari PostgreSQL 11 ke 13",
"service_enabled": "Layanan '{service}' akan secara mandiri dimulai saat pemulaian.",
"service_reloaded_or_restarted": "Layanan {service} dimuat atau dimulai ulang",
"service_stopped": "Layanan '{service}' diberhentikan",
"service_unknown": "Layanan yang tidak diketahui: '{service}'",
"updating_apt_cache": "Mengambil pembaruan yang tersedia untuk paket sistem...",
"group_mailalias_remove": "Alias surel '{mail}' akan dihapus dari kelompok '{group}'",
"migration_description_0021_migrate_to_bullseye": "Peningkatan sistem ke Debian Bullseye dan YunoHost 11.x",
"migration_description_0024_rebuild_python_venv": "Memperbaiki aplikasi Python setelah migrasi Bullseye",
"service_disable_failed": "Tidak dapat membuat layanan '{service}' dimulai saat pemulaian.\n\nLog layanan baru-baru ini:{logs}",
"service_disabled": "Layanan '{service}' tidak akan dimulai kembali saat pemulaian.",
"tools_upgrade_failed": "Tidak dapat memperbarui paket: {packages_list}",
"global_settings_setting_nginx_redirect_to_https": "Paksa HTTPS",
"backup_archive_system_part_not_available": "Segmen '{part}' tidak tersedia di cadangan ini",
"backup_output_directory_forbidden": "Pilih direktori yang berbeda. Cadangan tidak dapat dibuat di /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var, atau subfolder dari /home/yunohost.backup/archives",
"ask_dyndns_recovery_password_explain_during_unsubscribe": "Masukkan kata sandi pemulihan untuk domain DynDNS ini.",
"backup_output_symlink_dir_broken": "Direktori arsip Anda '{path}' rusak penautannya. Mungkin Anda lupa untuk menambatkan ulang atau memasukkan kembali penyimpanan tujuan penautan direktori arsip tersebut.",
"diagnosis_apps_not_in_app_catalog": "Aplikasi ini tidak ada di katalog aplikasi YunoHost. Jika aplikasi ini ada di sana sebelumnya dan dihapus, Anda disarankan untuk melepas aplikasi ini dikarenakan ini tidak akan menerima pembaruan dan mungkin bisa menghancurkan integritas dan keamanan sistem Anda.",
"dyndns_ip_update_failed": "Tidak dapat memperbarui IP Anda di DynDNS",
"service_restarted": "Layanan {service} dimulai ulang",
"service_started": "Layanan '{service}' dimulai",
"service_stop_failed": "Tidak dapat menghentikan layanan '{service}'\n\nLog layanan baru-baru ini: {logs}",
"apps_catalog_failed_to_download": "Tidak dapat mengunduh katalog aplikasi {apps_catalog}: {error}",
"backup_archive_corrupted": "Sepertinya arsip cadangan '{archive}' rusak: {error}",
"diagnosis_found_errors": "{errors} masalah signifikan ditemukan terkait dengan {category}!",
"restore_system_part_failed": "Tidak dapat memulihkan segmen '{part}'",
"service_enable_failed": "Tidak dapat membuat layanan '{service}' dimulai mandiri saat pemulaian.\n\nLog layanan baru-baru ini:{logs}",
"service_not_reloading_because_conf_broken": "Tidak memuat atau memulai ulang layanan '{name}' karena konfigurasinya rusak: {errors}",
"service_reloaded": "Layanan {service} dimuat ulang"
} }

View file

@ -103,7 +103,7 @@
"backup_applying_method_custom": "Wywołuję niestandardową metodę tworzenia kopii zapasowych '{method}'...", "backup_applying_method_custom": "Wywołuję niestandardową metodę tworzenia kopii zapasowych '{method}'...",
"app_remove_after_failed_install": "Usuwanie aplikacji po niepowodzeniu instalacji...", "app_remove_after_failed_install": "Usuwanie aplikacji po niepowodzeniu instalacji...",
"app_upgrade_script_failed": "Wystąpił błąd w skrypcie aktualizacji aplikacji", "app_upgrade_script_failed": "Wystąpił błąd w skrypcie aktualizacji aplikacji",
"apps_catalog_init_success": "Zainicjowano system katalogu aplikacji!", "apps_catalog_init_success": "System katalogu aplikacji został zainicjowany!",
"apps_catalog_obsolete_cache": "Pamięć podręczna katalogu aplikacji jest pusta lub przestarzała.", "apps_catalog_obsolete_cache": "Pamięć podręczna katalogu aplikacji jest pusta lub przestarzała.",
"app_extraction_failed": "Nie można wyodrębnić plików instalacyjnych", "app_extraction_failed": "Nie można wyodrębnić plików instalacyjnych",
"app_packaging_format_not_supported": "Ta aplikacja nie może zostać zainstalowana, ponieważ jej format opakowania nie jest obsługiwany przez twoją wersję YunoHost. Prawdopodobnie powinieneś rozważyć aktualizację swojego systemu.", "app_packaging_format_not_supported": "Ta aplikacja nie może zostać zainstalowana, ponieważ jej format opakowania nie jest obsługiwany przez twoją wersję YunoHost. Prawdopodobnie powinieneś rozważyć aktualizację swojego systemu.",
@ -183,7 +183,7 @@
"app_failed_to_download_asset": "Nie udało się pobrać zasobu '{source_id}' ({url}) dla {app}: {out}", "app_failed_to_download_asset": "Nie udało się pobrać zasobu '{source_id}' ({url}) dla {app}: {out}",
"backup_with_no_backup_script_for_app": "Aplikacja '{app}' nie posiada skryptu kopii zapasowej. Ignorowanie.", "backup_with_no_backup_script_for_app": "Aplikacja '{app}' nie posiada skryptu kopii zapasowej. Ignorowanie.",
"backup_with_no_restore_script_for_app": "Aplikacja {app} nie posiada skryptu przywracania, co oznacza, że nie będzie można automatycznie przywrócić kopii zapasowej tej aplikacji.", "backup_with_no_restore_script_for_app": "Aplikacja {app} nie posiada skryptu przywracania, co oznacza, że nie będzie można automatycznie przywrócić kopii zapasowej tej aplikacji.",
"certmanager_acme_not_configured_for_domain": "Wyzwanie ACME nie może zostać uruchomione dla domeny {domain}, ponieważ jej konfiguracja nginx nie zawiera odpowiedniego fragmentu kodu... Upewnij się, że konfiguracja nginx jest aktualna, używając polecenia yunohost tools regen-conf nginx --dry-run --with-diff.", "certmanager_acme_not_configured_for_domain": "Wyzwanie ACME nie może być teraz uruchomione dla {domain}, ponieważ jego konfiguracja nginx nie zawiera odpowiedniego fragmentu kodu… Upewnij się, że twoja konfiguracja nginx jest aktualna, używając `yunohost tools regen-conf nginx --dry-run --with-diff`.",
"certmanager_domain_dns_ip_differs_from_public_ip": "Rekordy DNS dla domeny '{domain}' różnią się od adresu IP tego serwera. Sprawdź kategorię 'Rekordy DNS' (podstawowe) w diagnozie, aby uzyskać więcej informacji. Jeśli niedawno dokonałeś zmiany rekordu A, poczekaj, aż zostanie on zaktualizowany (można skorzystać z narzędzi online do sprawdzania propagacji DNS). (Jeśli wiesz, co robisz, użyj opcji '--no-checks', aby wyłączyć te sprawdzania.)", "certmanager_domain_dns_ip_differs_from_public_ip": "Rekordy DNS dla domeny '{domain}' różnią się od adresu IP tego serwera. Sprawdź kategorię 'Rekordy DNS' (podstawowe) w diagnozie, aby uzyskać więcej informacji. Jeśli niedawno dokonałeś zmiany rekordu A, poczekaj, aż zostanie on zaktualizowany (można skorzystać z narzędzi online do sprawdzania propagacji DNS). (Jeśli wiesz, co robisz, użyj opcji '--no-checks', aby wyłączyć te sprawdzania.)",
"confirm_app_install_danger": "UWAGA! Ta aplikacja jest wciąż w fazie eksperymentalnej (jeśli nie działa jawnie)! Prawdopodobnie NIE powinieneś jej instalować, chyba że wiesz, co robisz. NIE ZOSTANIE udzielone wsparcie, jeśli ta aplikacja nie będzie działać poprawnie lub spowoduje uszkodzenie systemu... Jeśli mimo to jesteś gotów podjąć to ryzyko, wpisz '{answers}", "confirm_app_install_danger": "UWAGA! Ta aplikacja jest wciąż w fazie eksperymentalnej (jeśli nie działa jawnie)! Prawdopodobnie NIE powinieneś jej instalować, chyba że wiesz, co robisz. NIE ZOSTANIE udzielone wsparcie, jeśli ta aplikacja nie będzie działać poprawnie lub spowoduje uszkodzenie systemu... Jeśli mimo to jesteś gotów podjąć to ryzyko, wpisz '{answers}",
"confirm_app_install_thirdparty": "UWAGA! Ta aplikacja nie jest częścią katalogu aplikacji YunoHost. Instalowanie aplikacji innych firm może naruszyć integralność i bezpieczeństwo systemu. Prawdopodobnie NIE powinieneś jej instalować, chyba że wiesz, co robisz. NIE ZOSTANIE udzielone wsparcie, jeśli ta aplikacja nie będzie działać poprawnie lub spowoduje uszkodzenie systemu... Jeśli mimo to jesteś gotów podjąć to ryzyko, wpisz '{answers}'", "confirm_app_install_thirdparty": "UWAGA! Ta aplikacja nie jest częścią katalogu aplikacji YunoHost. Instalowanie aplikacji innych firm może naruszyć integralność i bezpieczeństwo systemu. Prawdopodobnie NIE powinieneś jej instalować, chyba że wiesz, co robisz. NIE ZOSTANIE udzielone wsparcie, jeśli ta aplikacja nie będzie działać poprawnie lub spowoduje uszkodzenie systemu... Jeśli mimo to jesteś gotów podjąć to ryzyko, wpisz '{answers}'",
@ -192,19 +192,19 @@
"config_no_panel": "Nie znaleziono panelu konfiguracji.", "config_no_panel": "Nie znaleziono panelu konfiguracji.",
"config_unknown_filter_key": "Klucz filtru '{filter_key}' jest niepoprawny.", "config_unknown_filter_key": "Klucz filtru '{filter_key}' jest niepoprawny.",
"config_validate_email": "Proszę podać poprawny adres e-mail", "config_validate_email": "Proszę podać poprawny adres e-mail",
"backup_hook_unknown": "Nieznany jest hook kopii zapasowej '{hook}'.", "backup_hook_unknown": "Nieznany jest hook kopii zapasowej '{hook}'",
"backup_no_uncompress_archive_dir": "Nie istnieje taki katalog nieskompresowanego archiwum.", "backup_no_uncompress_archive_dir": "Nie istnieje taki katalog nieskompresowanego archiwum",
"backup_output_symlink_dir_broken": "Twój katalog archiwum '{path}' to uszkodzony dowiązanie symboliczne. Być może zapomniałeś o ponownym zamontowaniu lub podłączeniu nośnika przechowującego, do którego on wskazuje.", "backup_output_symlink_dir_broken": "Twój katalog archiwum {path} to uszkodzony symlink. Być może zapomniałeś o ponownym zamontowaniu lub podłączeniu nośnika przechowującego, do którego on wskazuje.",
"backup_system_part_failed": "Nie można wykonać kopii zapasowej części systemu '{part}'", "backup_system_part_failed": "Nie udało się wykonać kopii zapasowej części systemu {part}",
"config_validate_color": "Powinien być poprawnym szesnastkowym kodem koloru RGB.", "config_validate_color": "Powinien być poprawnym szesnastkowym kodem koloru RGB.",
"config_validate_date": "Data powinna być poprawna w formacie RRRR-MM-DD", "config_validate_date": "Data powinna być poprawna w formacie RRRR-MM-DD",
"config_validate_time": "Podaj poprawny czas w formacie GG:MM", "config_validate_time": "Podaj poprawny czas w formacie GG:MM",
"certmanager_domain_not_diagnosed_yet": "Nie ma jeszcze wyników diagnozy dla domeny {domain}. Proszę ponownie uruchomić diagnozę dla kategorii 'Rekordy DNS' i 'Strona internetowa' w sekcji diagnozy, aby sprawdzić, czy domena jest gotowa do użycia Let's Encrypt. (Jeśli wiesz, co robisz, użyj opcji '--no-checks', aby wyłączyć te sprawdzania.)", "certmanager_domain_not_diagnosed_yet": "Nie ma jeszcze wyników diagnozy dla domeny {domain}. Proszę ponownie uruchomić diagnozę dla kategorii 'Rekordy DNS' i 'Strona internetowa' w sekcji diagnozy, aby sprawdzić, czy domena jest gotowa do użycia Let's Encrypt. (Jeśli wiesz, co robisz, użyj opcji '--no-checks', aby wyłączyć te sprawdzania.)",
"certmanager_cannot_read_cert": "Wystąpił problem podczas próby otwarcia bieżącego certyfikatu dla domeny {domain} (plik: {file}), przyczyna: {reason}", "certmanager_cannot_read_cert": "Wystąpił problem podczas próby otwarcia bieżącego certyfikatu dla domeny {domain} (plik: {file}), przyczyna: {reason}",
"certmanager_no_cert_file": "Nie można odczytać pliku certyfikatu dla domeny {domain} (plik: {file}).", "certmanager_no_cert_file": "Nie można odczytać pliku certyfikatu dla domeny {domain} (plik: {file})",
"certmanager_self_ca_conf_file_not_found": "Nie można znaleźć pliku konfiguracyjnego dla autorytetu samopodpisującego (plik: {file})", "certmanager_self_ca_conf_file_not_found": "Nie można znaleźć pliku konfiguracyjnego dla samodzielnie podpisanego upoważnienia do (file: {file})",
"backup_running_hooks": "Uruchamianie hooków kopii zapasowej...", "backup_running_hooks": "Uruchamianie kopii zapasowej hooków...",
"backup_permission": "Uprawnienia kopii zapasowej dla aplikacji {app}", "backup_permission": "Uprawnienia do tworzenia kopii zapasowej dla aplikacji {app}",
"certmanager_domain_cert_not_selfsigned": "Certyfikat dla domeny {domain} nie jest samopodpisany. Czy na pewno chcesz go zastąpić? (Użyj opcji '--force', aby to zrobić.)", "certmanager_domain_cert_not_selfsigned": "Certyfikat dla domeny {domain} nie jest samopodpisany. Czy na pewno chcesz go zastąpić? (Użyj opcji '--force', aby to zrobić.)",
"config_action_disabled": "Nie można uruchomić akcji '{action}', ponieważ jest ona wyłączona. Upewnij się, że spełnione są jej ograniczenia. Pomoc: {help}", "config_action_disabled": "Nie można uruchomić akcji '{action}', ponieważ jest ona wyłączona. Upewnij się, że spełnione są jej ograniczenia. Pomoc: {help}",
"config_action_failed": "Nie udało się uruchomić akcji '{action}': {error}", "config_action_failed": "Nie udało się uruchomić akcji '{action}': {error}",
@ -214,7 +214,7 @@
"confirm_app_insufficient_ram": "UWAGA! Ta aplikacja wymaga {required} pamięci RAM do zainstalowania/aktualizacji, a obecnie dostępne jest tylko {current}. Nawet jeśli aplikacja mogłaby działać, proces instalacji/aktualizacji wymaga dużej ilości pamięci RAM, więc serwer może się zawiesić i niepowodzenie może być katastrofalne. Jeśli mimo to jesteś gotów podjąć to ryzyko, wpisz '{answers}'", "confirm_app_insufficient_ram": "UWAGA! Ta aplikacja wymaga {required} pamięci RAM do zainstalowania/aktualizacji, a obecnie dostępne jest tylko {current}. Nawet jeśli aplikacja mogłaby działać, proces instalacji/aktualizacji wymaga dużej ilości pamięci RAM, więc serwer może się zawiesić i niepowodzenie może być katastrofalne. Jeśli mimo to jesteś gotów podjąć to ryzyko, wpisz '{answers}'",
"app_not_upgraded_broken_system": "Aplikacja '{failed_app}' nie powiodła się w procesie aktualizacji i spowodowała uszkodzenie systemu. W rezultacie anulowane zostały aktualizacje następujących aplikacji: {apps}", "app_not_upgraded_broken_system": "Aplikacja '{failed_app}' nie powiodła się w procesie aktualizacji i spowodowała uszkodzenie systemu. W rezultacie anulowane zostały aktualizacje następujących aplikacji: {apps}",
"app_not_upgraded_broken_system_continue": "Aplikacja '{failed_app}' nie powiodła się w procesie aktualizacji i spowodowała uszkodzenie systemu (parametr --continue-on-failure jest ignorowany). W rezultacie anulowane zostały aktualizacje następujących aplikacji: {apps}", "app_not_upgraded_broken_system_continue": "Aplikacja '{failed_app}' nie powiodła się w procesie aktualizacji i spowodowała uszkodzenie systemu (parametr --continue-on-failure jest ignorowany). W rezultacie anulowane zostały aktualizacje następujących aplikacji: {apps}",
"certmanager_domain_http_not_working": "Domena {domain} wydaje się niedostępna przez HTTP. Sprawdź kategorię 'Strona internetowa' diagnostyki, aby uzyskać więcej informacji. (Jeśli wiesz, co robisz, użyj opcji '--no-checks', aby wyłączyć te sprawdzania.)", "certmanager_domain_http_not_working": "Domena {domain} nie wydaje się być dostępna przez HTTP. Sprawdź kategorię 'Strona internetowa' diagnostyki, aby uzyskać więcej informacji. (Jeśli wiesz, co robisz, użyj opcji '--no-checks', aby wyłączyć te sprawdzania.)",
"migration_0021_system_not_fully_up_to_date": "Twój system nie jest w pełni zaktualizowany! Proszę, wykonaj zwykłą aktualizację oprogramowania zanim rozpoczniesz migrację na system Bullseye.", "migration_0021_system_not_fully_up_to_date": "Twój system nie jest w pełni zaktualizowany! Proszę, wykonaj zwykłą aktualizację oprogramowania zanim rozpoczniesz migrację na system Bullseye.",
"global_settings_setting_smtp_relay_port": "Port przekaźnika SMTP", "global_settings_setting_smtp_relay_port": "Port przekaźnika SMTP",
"domain_config_cert_renew": "Odnów certyfikat Let's Encrypt", "domain_config_cert_renew": "Odnów certyfikat Let's Encrypt",
@ -274,5 +274,12 @@
"global_settings_setting_smtp_allow_ipv6_help": "Zezwól na wykorzystywanie IPv7 do odbierania i wysyłania maili", "global_settings_setting_smtp_allow_ipv6_help": "Zezwól na wykorzystywanie IPv7 do odbierania i wysyłania maili",
"global_settings_setting_ssh_password_authentication": "Logowanie hasłem", "global_settings_setting_ssh_password_authentication": "Logowanie hasłem",
"diagnosis_backports_in_sources_list": "Wygląda na to że apt (menedżer pakietów) został skonfigurowany tak, aby wykorzystywać repozytorium backported. Nie zalecamy wykorzystywania repozytorium backported, ponieważ może powodować problemy ze stabilnością i/lub konflikty z konfiguracją. No chyba, że wiesz co robisz.", "diagnosis_backports_in_sources_list": "Wygląda na to że apt (menedżer pakietów) został skonfigurowany tak, aby wykorzystywać repozytorium backported. Nie zalecamy wykorzystywania repozytorium backported, ponieważ może powodować problemy ze stabilnością i/lub konflikty z konfiguracją. No chyba, że wiesz co robisz.",
"domain_config_xmpp_help": "Uwaga: niektóre funkcje XMPP będą wymagały aktualizacji rekordów DNS i odnowienia certyfikatu Lets Encrypt w celu ich włączenia" "domain_config_xmpp_help": "Uwaga: niektóre funkcje XMPP będą wymagały aktualizacji rekordów DNS i odnowienia certyfikatu Lets Encrypt w celu ich włączenia",
"ask_dyndns_recovery_password_explain": "Proszę wybrać hasło odzyskiwania dla swojej domeny DynDNS, na wypadek gdybyś musiał go później zresetować.",
"ask_dyndns_recovery_password_explain_during_unsubscribe": "Proszę wprowadzić hasło odzyskiwania dla tej domeny DynDNS.",
"certmanager_unable_to_parse_self_CA_name": "Nie można spasować nazwy organu samopodpisywanego (pliku: {file})",
"app_corrupt_source": "YunoHost był w stanie pobrać zasób {source_id} ({url}) dla {app}, ale zasób nie pasuje do oczekiwanego sumy kontrolnej. Może to oznaczać, że na twoim serwerze wystąpiła tymczasowa awaria sieci, LUB zasób został jakoś zmieniony przez dostawcę usługi (lub złośliwego aktora?) i pakowacze YunoHost muszą zbadać sprawę i zaktualizować manifest aplikacji, aby odzwierciedlić tę zmianę. \nOczekiwana suma kontrolna sha256: {expected_sha256} \nPobrana suma kontrolna sha256: {computed_sha256} \nRozmiar pobranego pliku: {size}”",
"ask_dyndns_recovery_password": "Hasło odzyskiwania DynDNS",
"certmanager_hit_rate_limit": "Zbyt wiele certyfikatów zostało ostatnio wydanych dla tej dokładnej grupy domen {domain}. Spróbuj ponownie później. Zobacz https://letsencrypt.org/docs/rate-limits/ aby uzyskać więcej informacji",
"apps_failed_to_upgrade_line": "\n * {app_id} (aby zobaczyć odpowiedni dziennik, wykonaj yunohost log show {operation_logger_name})"
} }

View file

@ -15,5 +15,10 @@
"additional_urls_already_added": "Ek URL '{url}' zaten '{permission}' izni için ek URL'ye eklendi", "additional_urls_already_added": "Ek URL '{url}' zaten '{permission}' izni için ek URL'ye eklendi",
"additional_urls_already_removed": "Ek URL '{url}', '{permission}' izni için ek URL'de zaten kaldırıldı", "additional_urls_already_removed": "Ek URL '{url}', '{permission}' izni için ek URL'de zaten kaldırıldı",
"app_action_cannot_be_ran_because_required_services_down": "Bu eylemi gerçekleştirmek için şu servisler çalışıyor olmalıdır: {services}. Devam etmek için onları yeniden başlatın (ve muhtemelen neden çalışmadığını araştırın).", "app_action_cannot_be_ran_because_required_services_down": "Bu eylemi gerçekleştirmek için şu servisler çalışıyor olmalıdır: {services}. Devam etmek için onları yeniden başlatın (ve muhtemelen neden çalışmadığını araştırın).",
"app_arch_not_supported": "Bu uygulama yalnızca {required} işlemci mimarisi üzerine kurulabilir ancak sunucunuzun işlemci mimarisi {current}." "app_arch_not_supported": "Bu uygulama yalnızca {required} işlemci mimarisi üzerine kurulabilir ancak sunucunuzun işlemci mimarisi {current}.",
} "app_argument_choice_invalid": "'{name}'' için geçerli bir değer giriniz '{value}' mevcut seçimlerin arasında değil ({choices})",
"app_change_url_failed": "{app}: {error} için url değiştirilemedi",
"app_argument_required": "'{name}' değeri gerekli",
"app_argument_invalid": "'{name}': {error} için geçerli bir değer giriniz",
"app_argument_password_no_default": "'{name}': çözümlenirken bir hata meydana geldi. Parola argümanı güvenlik nedeniyle varsayılan değer alamaz"
}

View file

@ -70,26 +70,10 @@ user:
help: The full name of the user. For example 'Camille Dupont' help: The full name of the user. For example 'Camille Dupont'
extra: extra:
ask: ask_fullname ask: ask_fullname
required: False required: True
pattern: &pattern_fullname pattern: &pattern_fullname
- !!str ^([^\W_]{1,30}[ ,.'-]{0,3})+$ - !!str ^([^\W_]{1,30}[ ,.'-]{0,3})+$
- "pattern_fullname" - "pattern_fullname"
-f:
full: --firstname
help: Deprecated. Use --fullname instead.
extra:
required: False
pattern: &pattern_firstname
- !!str ^([^\W\d_]{1,30}[ ,.'-]{0,3})+$
- "pattern_firstname"
-l:
full: --lastname
help: Deprecated. Use --fullname instead.
extra:
required: False
pattern: &pattern_lastname
- !!str ^([^\W\d_]{1,30}[ ,.'-]{0,3})+$
- "pattern_lastname"
-p: -p:
full: --password full: --password
help: User password help: User password
@ -147,16 +131,6 @@ user:
help: The full name of the user. For example 'Camille Dupont' help: The full name of the user. For example 'Camille Dupont'
extra: extra:
pattern: *pattern_fullname pattern: *pattern_fullname
-f:
full: --firstname
help: Deprecated. Use --fullname instead.
extra:
pattern: *pattern_firstname
-l:
full: --lastname
help: Deprecated. Use --fullname instead.
extra:
pattern: *pattern_lastname
-m: -m:
full: --mail full: --mail
extra: extra:
@ -553,17 +527,6 @@ domain:
help: If removing a DynDNS domain, unsubscribe from the DynDNS service with a password help: If removing a DynDNS domain, unsubscribe from the DynDNS service with a password
extra: extra:
pattern: *pattern_password pattern: *pattern_password
### domain_dns_conf()
dns-conf:
deprecated: true
action_help: Generate sample DNS configuration for a domain
arguments:
domain:
help: Target domain
extra:
pattern: *pattern_domain
### domain_maindomain() ### domain_maindomain()
main-domain: main-domain:
@ -578,54 +541,6 @@ domain:
extra: extra:
pattern: *pattern_domain pattern: *pattern_domain
### certificate_status()
cert-status:
deprecated: true
action_help: List status of current certificates (all by default).
arguments:
domain_list:
help: Domains to check
nargs: "*"
--full:
help: Show more details
action: store_true
### certificate_install()
cert-install:
deprecated: true
action_help: Install Let's Encrypt certificates for given domains (all by default).
arguments:
domain_list:
help: Domains for which to install the certificates
nargs: "*"
--force:
help: Install even if current certificate is not self-signed
action: store_true
--no-checks:
help: Does not perform any check that your domain seems correctly configured (DNS, reachability) before attempting to install. (Not recommended)
action: store_true
--self-signed:
help: Install self-signed certificate instead of Let's Encrypt
action: store_true
### certificate_renew()
cert-renew:
deprecated: true
action_help: Renew the Let's Encrypt certificates for given domains (all by default).
arguments:
domain_list:
help: Domains for which to renew the certificates
nargs: "*"
--force:
help: Ignore the validity threshold (30 days)
action: store_true
--email:
help: Send an email to root with logs if some renewing fails
action: store_true
--no-checks:
help: Does not perform any check that your domain seems correctly configured (DNS, reachability) before attempting to renew. (Not recommended)
action: store_true
### domain_url_available() ### domain_url_available()
url-available: url-available:
hide_in_help: True hide_in_help: True
@ -934,14 +849,14 @@ app:
help: Custom name for the app help: Custom name for the app
-a: -a:
full: --args full: --args
help: Serialized arguments for app script (i.e. "domain=domain.tld&path=/path") help: Serialized arguments for app script (i.e. "domain=domain.tld&path=/path&init_main_permission=visitors")
-n: -n:
full: --no-remove-on-failure full: --no-remove-on-failure
help: Debug option to avoid removing the app on a failed installation help: Debug option to avoid removing the app on a failed installation
action: store_true action: store_true
-f: -f:
full: --force full: --force
help: Do not ask confirmation if the app is not safe to use (low quality, experimental or 3rd party) help: Do not ask confirmation if the app is not safe to use (low quality, experimental or 3rd party), or when the app displays a post-install notification
action: store_true action: store_true
### app_remove() ### app_remove()
@ -980,7 +895,7 @@ app:
action: store_true action: store_true
-c: -c:
full: --continue-on-failure full: --continue-on-failure
help: Continue to upgrade apps event if one or more upgrade failed help: Continue to upgrade apps even if one or more upgrade failed
action: store_true action: store_true
### app_change_url() ### app_change_url()

View file

@ -227,7 +227,7 @@
redact = true redact = true
[gandi.api_protocol] [gandi.api_protocol]
type = "string" type = "select"
choices.rpc = "RPC" choices.rpc = "RPC"
choices.rest = "REST" choices.rest = "REST"
default = "rest" default = "rest"

View file

@ -144,17 +144,11 @@ def init_logging(interface="cli", debug=False, quiet=False, logdir="/var/log/yun
"version": 1, "version": 1,
"disable_existing_loggers": True, "disable_existing_loggers": True,
"formatters": { "formatters": {
"console": { "tty-debug": {
"format": "%(relativeCreated)-5d %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s" "format": "%(relativeCreated)-4d %(level_with_color)s %(message)s"
}, },
"tty-debug": {"format": "%(relativeCreated)-4d %(fmessage)s"},
"precise": { "precise": {
"format": "%(asctime)-15s %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s" "format": "%(asctime)-15s %(levelname)-8s %(name)s.%(funcName)s - %(message)s"
},
},
"filters": {
"action": {
"()": "moulinette.utils.log.ActionFilter",
}, },
}, },
"handlers": { "handlers": {
@ -175,7 +169,6 @@ def init_logging(interface="cli", debug=False, quiet=False, logdir="/var/log/yun
"class": "logging.FileHandler", "class": "logging.FileHandler",
"formatter": "precise", "formatter": "precise",
"filename": logfile, "filename": logfile,
"filters": ["action"],
}, },
}, },
"loggers": { "loggers": {

View file

@ -28,9 +28,9 @@ import tempfile
import copy import copy
from typing import List, Tuple, Dict, Any, Iterator, Optional from typing import List, Tuple, Dict, Any, Iterator, Optional
from packaging import version from packaging import version
from logging import getLogger
from moulinette import Moulinette, m18n from moulinette import Moulinette, m18n
from moulinette.utils.log import getActionLogger
from moulinette.utils.process import run_commands, check_output from moulinette.utils.process import run_commands, check_output
from moulinette.utils.filesystem import ( from moulinette.utils.filesystem import (
read_file, read_file,
@ -71,7 +71,7 @@ from yunohost.app_catalog import ( # noqa
APPS_CATALOG_LOGOS, APPS_CATALOG_LOGOS,
) )
logger = getActionLogger("yunohost.app") logger = getLogger("yunohost.app")
APPS_SETTING_PATH = "/etc/yunohost/apps/" APPS_SETTING_PATH = "/etc/yunohost/apps/"
APP_TMP_WORKDIRS = "/var/cache/yunohost/app_tmp_work_dirs" APP_TMP_WORKDIRS = "/var/cache/yunohost/app_tmp_work_dirs"
@ -186,11 +186,17 @@ def app_info(app, full=False, upgradable=False):
ret["from_catalog"] = from_catalog ret["from_catalog"] = from_catalog
# Hydrate app notifications and doc # Hydrate app notifications and doc
rendered_doc = {}
for pagename, content_per_lang in ret["manifest"]["doc"].items(): for pagename, content_per_lang in ret["manifest"]["doc"].items():
for lang, content in content_per_lang.items(): for lang, content in content_per_lang.items():
ret["manifest"]["doc"][pagename][lang] = _hydrate_app_template( rendered_content = _hydrate_app_template(content, settings)
content, settings # Rendered content may be empty because of conditional blocks
) if not rendered_content:
continue
if pagename not in rendered_doc:
rendered_doc[pagename] = {}
rendered_doc[pagename][lang] = rendered_content
ret["manifest"]["doc"] = rendered_doc
# Filter dismissed notification # Filter dismissed notification
ret["manifest"]["notifications"] = { ret["manifest"]["notifications"] = {
@ -201,9 +207,16 @@ def app_info(app, full=False, upgradable=False):
# Hydrate notifications (also filter uneeded post_upgrade notification based on version) # Hydrate notifications (also filter uneeded post_upgrade notification based on version)
for step, notifications in ret["manifest"]["notifications"].items(): for step, notifications in ret["manifest"]["notifications"].items():
rendered_notifications = {}
for name, content_per_lang in notifications.items(): for name, content_per_lang in notifications.items():
for lang, content in content_per_lang.items(): for lang, content in content_per_lang.items():
notifications[name][lang] = _hydrate_app_template(content, settings) rendered_content = _hydrate_app_template(content, settings)
if not rendered_content:
continue
if name not in rendered_notifications:
rendered_notifications[name] = {}
rendered_notifications[name][lang] = rendered_content
ret["manifest"]["notifications"][step] = rendered_notifications
ret["is_webapp"] = "domain" in settings and "path" in settings ret["is_webapp"] = "domain" in settings and "path" in settings
@ -238,8 +251,8 @@ def _app_upgradable(app_infos):
# Determine upgradability # Determine upgradability
app_in_catalog = app_infos.get("from_catalog") app_in_catalog = app_infos.get("from_catalog")
installed_version = version.parse(app_infos.get("version", "0~ynh0")) installed_version = _parse_app_version(app_infos.get("version", "0~ynh0"))
version_in_catalog = version.parse( version_in_catalog = _parse_app_version(
app_infos.get("from_catalog", {}).get("manifest", {}).get("version", "0~ynh0") app_infos.get("from_catalog", {}).get("manifest", {}).get("version", "0~ynh0")
) )
@ -254,25 +267,7 @@ def _app_upgradable(app_infos):
): ):
return "bad_quality" return "bad_quality"
# If the app uses the standard version scheme, use it to determine if installed_version < version_in_catalog:
# upgradability
if "~ynh" in str(installed_version) and "~ynh" in str(version_in_catalog):
if installed_version < version_in_catalog:
return "yes"
else:
return "no"
# Legacy stuff for app with old / non-standard version numbers...
# In case there is neither update_time nor install_time, we assume the app can/has to be upgraded
if not app_infos["from_catalog"].get("lastUpdate") or not app_infos[
"from_catalog"
].get("git"):
return "url_required"
settings = app_infos["settings"]
local_update_time = settings.get("update_time", settings.get("install_time", 0))
if app_infos["from_catalog"]["lastUpdate"] > local_update_time:
return "yes" return "yes"
else: else:
return "no" return "no"
@ -617,9 +612,11 @@ def app_upgrade(
# Manage upgrade type and avoid any upgrade if there is nothing to do # Manage upgrade type and avoid any upgrade if there is nothing to do
upgrade_type = "UNKNOWN" upgrade_type = "UNKNOWN"
# Get current_version and new version # Get current_version and new version
app_new_version = version.parse(manifest.get("version", "?")) app_new_version_raw = manifest.get("version", "?")
app_current_version = version.parse(app_dict.get("version", "?")) app_current_version_raw = app_dict.get("version", "?")
if "~ynh" in str(app_current_version) and "~ynh" in str(app_new_version): app_new_version = _parse_app_version(app_new_version_raw)
app_current_version = _parse_app_version(app_current_version_raw)
if "~ynh" in str(app_current_version_raw) and "~ynh" in str(app_new_version_raw):
if app_current_version >= app_new_version and not force: if app_current_version >= app_new_version and not force:
# In case of upgrade from file or custom repository # In case of upgrade from file or custom repository
# No new version available # No new version available
@ -639,10 +636,10 @@ def app_upgrade(
upgrade_type = "UPGRADE_FORCED" upgrade_type = "UPGRADE_FORCED"
else: else:
app_current_version_upstream, app_current_version_pkg = str( app_current_version_upstream, app_current_version_pkg = str(
app_current_version app_current_version_raw
).split("~ynh") ).split("~ynh")
app_new_version_upstream, app_new_version_pkg = str( app_new_version_upstream, app_new_version_pkg = str(
app_new_version app_new_version_raw
).split("~ynh") ).split("~ynh")
if app_current_version_upstream == app_new_version_upstream: if app_current_version_upstream == app_new_version_upstream:
upgrade_type = "UPGRADE_PACKAGE" upgrade_type = "UPGRADE_PACKAGE"
@ -672,7 +669,7 @@ def app_upgrade(
settings = _get_app_settings(app_instance_name) settings = _get_app_settings(app_instance_name)
notifications = _filter_and_hydrate_notifications( notifications = _filter_and_hydrate_notifications(
manifest["notifications"]["PRE_UPGRADE"], manifest["notifications"]["PRE_UPGRADE"],
current_version=app_current_version, current_version=app_current_version_raw,
data=settings, data=settings,
) )
_display_notifications(notifications, force=force) _display_notifications(notifications, force=force)
@ -694,9 +691,17 @@ def app_upgrade(
safety_backup_name = f"{app_instance_name}-pre-upgrade2" safety_backup_name = f"{app_instance_name}-pre-upgrade2"
other_safety_backup_name = f"{app_instance_name}-pre-upgrade1" other_safety_backup_name = f"{app_instance_name}-pre-upgrade1"
backup_create( tweaked_backup_core_only = False
name=safety_backup_name, apps=[app_instance_name], system=None if "BACKUP_CORE_ONLY" not in os.environ:
) tweaked_backup_core_only = True
os.environ["BACKUP_CORE_ONLY"] = "1"
try:
backup_create(
name=safety_backup_name, apps=[app_instance_name], system=None
)
finally:
if tweaked_backup_core_only:
del os.environ["BACKUP_CORE_ONLY"]
if safety_backup_name in backup_list()["archives"]: if safety_backup_name in backup_list()["archives"]:
# if the backup suceeded, delete old safety backup to save space # if the backup suceeded, delete old safety backup to save space
@ -729,8 +734,8 @@ def app_upgrade(
env_dict_more = { env_dict_more = {
"YNH_APP_UPGRADE_TYPE": upgrade_type, "YNH_APP_UPGRADE_TYPE": upgrade_type,
"YNH_APP_MANIFEST_VERSION": str(app_new_version), "YNH_APP_MANIFEST_VERSION": str(app_new_version_raw),
"YNH_APP_CURRENT_VERSION": str(app_current_version), "YNH_APP_CURRENT_VERSION": str(app_current_version_raw),
} }
if manifest["packaging_format"] < 2: if manifest["packaging_format"] < 2:
@ -788,7 +793,7 @@ def app_upgrade(
and not no_safety_backup and not no_safety_backup
): ):
logger.warning( logger.warning(
"Upgrade failed ... attempting to restore the satefy backup (Yunohost first need to remove the app for this) ..." "Upgrade failed ... attempting to restore the safety backup (Yunohost first need to remove the app for this) ..."
) )
app_remove(app_instance_name, force_workdir=extracted_app_folder) app_remove(app_instance_name, force_workdir=extracted_app_folder)
@ -913,7 +918,7 @@ def app_upgrade(
settings = _get_app_settings(app_instance_name) settings = _get_app_settings(app_instance_name)
notifications = _filter_and_hydrate_notifications( notifications = _filter_and_hydrate_notifications(
manifest["notifications"]["POST_UPGRADE"], manifest["notifications"]["POST_UPGRADE"],
current_version=app_current_version, current_version=app_current_version_raw,
data=settings, data=settings,
) )
if Moulinette.interface.type == "cli": if Moulinette.interface.type == "cli":
@ -1098,7 +1103,7 @@ def app_install(
args = { args = {
question.id: question.value question.id: question.value
for question in questions for question in questions
if question.value is not None if not question.readonly and question.value is not None
} }
# Validate domain / path availability for webapps # Validate domain / path availability for webapps
@ -1157,6 +1162,10 @@ def app_install(
recursive=True, recursive=True,
) )
# Hotfix for bug in the webadmin while we fix the actual issue :D
if label == "undefined":
label = None
# Override manifest name by given label # Override manifest name by given label
# This info is also later picked-up by the 'permission' resource initialization # This info is also later picked-up by the 'permission' resource initialization
if label: if label:
@ -1512,119 +1521,6 @@ def app_setting(app, key, value=None, delete=False):
""" """
app_settings = _get_app_settings(app) or {} app_settings = _get_app_settings(app) or {}
#
# Legacy permission setting management
# (unprotected, protected, skipped_uri/regex)
#
is_legacy_permission_setting = any(
key.startswith(word + "_") for word in ["unprotected", "protected", "skipped"]
)
if is_legacy_permission_setting:
from yunohost.permission import (
user_permission_list,
user_permission_update,
permission_create,
permission_delete,
permission_url,
)
permissions = user_permission_list(full=True, apps=[app])["permissions"]
key_ = key.split("_")[0]
permission_name = f"{app}.legacy_{key_}_uris"
permission = permissions.get(permission_name)
# GET
if value is None and not delete:
return (
",".join(permission.get("uris", []) + permission["additional_urls"])
if permission
else None
)
# DELETE
if delete:
# If 'is_public' setting still exists, we interpret this as
# coming from a legacy app (because new apps shouldn't manage the
# is_public state themselves anymore...)
#
# In that case, we interpret the request for "deleting
# unprotected/skipped" setting as willing to make the app
# private
if (
"is_public" in app_settings
and "visitors" in permissions[app + ".main"]["allowed"]
):
if key.startswith("unprotected_") or key.startswith("skipped_"):
user_permission_update(app + ".main", remove="visitors")
if permission:
permission_delete(permission_name)
# SET
else:
urls = value
# If the request is about the root of the app (/), ( = the vast majority of cases)
# we interpret this as a change for the main permission
# (i.e. allowing/disallowing visitors)
if urls == "/":
if key.startswith("unprotected_") or key.startswith("skipped_"):
permission_url(app + ".main", url="/", sync_perm=False)
user_permission_update(app + ".main", add="visitors")
else:
user_permission_update(app + ".main", remove="visitors")
else:
urls = urls.split(",")
if key.endswith("_regex"):
urls = ["re:" + url for url in urls]
if permission:
# In case of new regex, save the urls, to add a new time in the additional_urls
# In case of new urls, we do the same thing but inversed
if key.endswith("_regex"):
# List of urls to save
current_urls_or_regex = [
url
for url in permission["additional_urls"]
if not url.startswith("re:")
]
else:
# List of regex to save
current_urls_or_regex = [
url
for url in permission["additional_urls"]
if url.startswith("re:")
]
new_urls = urls + current_urls_or_regex
# We need to clear urls because in the old setting the new setting override the old one and dont just add some urls
permission_url(permission_name, clear_urls=True, sync_perm=False)
permission_url(permission_name, add_url=new_urls)
else:
from yunohost.utils.legacy import legacy_permission_label
# Let's create a "special" permission for the legacy settings
permission_create(
permission=permission_name,
# FIXME find a way to limit to only the user allowed to the main permission
allowed=["all_users"]
if key.startswith("protected_")
else ["all_users", "visitors"],
url=None,
additional_urls=urls,
auth_header=not key.startswith("skipped_"),
label=legacy_permission_label(app, key.split("_")[0]),
show_tile=False,
protected=True,
)
return
#
# Regular setting management
#
# GET # GET
if value is None and not delete: if value is None and not delete:
return app_settings.get(key, None) return app_settings.get(key, None)
@ -1639,8 +1535,6 @@ def app_setting(app, key, value=None, delete=False):
# SET # SET
else: else:
if key in ["redirected_urls", "redirected_regex"]:
value = yaml.safe_load(value)
app_settings[key] = value app_settings[key] = value
_set_app_settings(app, app_settings) _set_app_settings(app, app_settings)
@ -1989,20 +1883,6 @@ def _get_app_settings(app):
logger.error(m18n.n("app_not_correctly_installed", app=app)) logger.error(m18n.n("app_not_correctly_installed", app=app))
return {} return {}
# Stupid fix for legacy bullshit
# In the past, some setups did not have proper normalization for app domain/path
# Meaning some setups (as of January 2021) still have path=/foobar/ (with a trailing slash)
# resulting in stupid issue unless apps using ynh_app_normalize_path_stuff
# So we yolofix the settings if such an issue is found >_>
# A simple call to `yunohost app list` (which happens quite often) should be enough
# to migrate all app settings ... so this can probably be removed once we're past Bullseye...
if settings.get("path") != "/" and (
settings.get("path", "").endswith("/")
or not settings.get("path", "/").startswith("/")
):
settings["path"] = "/" + settings["path"].strip("/")
_set_app_settings(app, settings)
# Make the app id available as $app too # Make the app id available as $app too
settings["app"] = app settings["app"] = app
@ -2026,6 +1906,20 @@ def _set_app_settings(app, settings):
yaml.safe_dump(settings, f, default_flow_style=False) yaml.safe_dump(settings, f, default_flow_style=False)
def _parse_app_version(v):
if v == "?":
return (0,0)
try:
if "~" in v:
return (version.parse(v.split("~")[0]), int(v.split("~")[1].replace("ynh", "")))
else:
return (version.parse(v), 0)
except Exception as e:
raise YunohostError(f"Failed to parse app version '{v}' : {e}", raw_msg=True)
def _get_manifest_of_app(path): def _get_manifest_of_app(path):
"Get app manifest stored in json or in toml" "Get app manifest stored in json or in toml"
@ -2223,6 +2117,13 @@ def _parse_app_doc_and_notifications(path):
def _hydrate_app_template(template, data): def _hydrate_app_template(template, data):
# Apply jinja for stuff like {% if .. %} blocks,
# but only if there's indeed an if block (to try to reduce overhead or idk)
if "{%" in template:
from jinja2 import Template
template = Template(template).render(**data)
stuff_to_replace = set(re.findall(r"__[A-Z0-9]+?[A-Z0-9_]*?[A-Z0-9]*?__", template)) stuff_to_replace = set(re.findall(r"__[A-Z0-9]+?[A-Z0-9_]*?[A-Z0-9]*?__", template))
for stuff in stuff_to_replace: for stuff in stuff_to_replace:
@ -2231,7 +2132,7 @@ def _hydrate_app_template(template, data):
if varname in data: if varname in data:
template = template.replace(stuff, str(data[varname])) template = template.replace(stuff, str(data[varname]))
return template return template.strip()
def _convert_v1_manifest_to_v2(manifest): def _convert_v1_manifest_to_v2(manifest):
@ -2905,6 +2806,7 @@ def _make_environment_for_app_script(
app_id, app_instance_nb = _parse_app_instance_name(app) app_id, app_instance_nb = _parse_app_instance_name(app)
env_dict = { env_dict = {
"YNH_DEFAULT_PHP_VERSION": "8.2",
"YNH_APP_ID": app_id, "YNH_APP_ID": app_id,
"YNH_APP_INSTANCE_NAME": app, "YNH_APP_INSTANCE_NAME": app,
"YNH_APP_INSTANCE_NUMBER": str(app_instance_nb), "YNH_APP_INSTANCE_NUMBER": str(app_instance_nb),
@ -3043,10 +2945,10 @@ def _assert_system_is_sane_for_app(manifest, when):
services = manifest.get("services", []) services = manifest.get("services", [])
# Some apps use php-fpm, php5-fpm or php7.x-fpm which is now php7.4-fpm # Some apps use php-fpm, php5-fpm or php7.x-fpm which is now php8.2-fpm
def replace_alias(service): def replace_alias(service):
if service in ["php-fpm", "php5-fpm", "php7.0-fpm", "php7.3-fpm"]: if service in ["php-fpm", "php5-fpm", "php7.0-fpm", "php7.3-fpm", "php7.4-fpm"]:
return "php7.4-fpm" return "php8.2-fpm"
else: else:
return service return service
@ -3055,7 +2957,7 @@ def _assert_system_is_sane_for_app(manifest, when):
# We only check those, mostly to ignore "custom" services # We only check those, mostly to ignore "custom" services
# (added by apps) and because those are the most popular # (added by apps) and because those are the most popular
# services # services
service_filter = ["nginx", "php7.4-fpm", "mysql", "postfix"] service_filter = ["nginx", "php8.2-fpm", "mysql", "postfix"]
services = [str(s) for s in services if s in service_filter] services = [str(s) for s in services if s in service_filter]
if "nginx" not in services: if "nginx" not in services:
@ -3131,14 +3033,9 @@ def _notification_is_dismissed(name, settings):
def _filter_and_hydrate_notifications(notifications, current_version=None, data={}): def _filter_and_hydrate_notifications(notifications, current_version=None, data={}):
def is_version_more_recent_than_current_version(name, current_version): def is_version_more_recent_than_current_version(name, current_version):
current_version = str(current_version) current_version = str(current_version)
# Boring code to handle the fact that "0.1 < 9999~ynh1" is False return _parse_app_version(name) > _parse_app_version(current_version)
if "~" in name: out = {
return version.parse(name) > version.parse(current_version)
else:
return version.parse(name) > version.parse(current_version.split("~")[0])
return {
# Should we render the markdown maybe? idk # Should we render the markdown maybe? idk
name: _hydrate_app_template(_value_for_locale(content_per_lang), data) name: _hydrate_app_template(_value_for_locale(content_per_lang), data)
for name, content_per_lang in notifications.items() for name, content_per_lang in notifications.items()
@ -3147,6 +3044,9 @@ def _filter_and_hydrate_notifications(notifications, current_version=None, data=
or is_version_more_recent_than_current_version(name, current_version) or is_version_more_recent_than_current_version(name, current_version)
} }
# Filter out empty notifications (notifications may be empty because of if blocks)
return {name:content for name, content in out.items() if content and content.strip()}
def _display_notifications(notifications, force=False): def _display_notifications(notifications, force=False):
if not notifications: if not notifications:
@ -3223,7 +3123,7 @@ def regen_mail_app_user_config_for_dovecot_and_postfix(only=None):
if dovecot: if dovecot:
hashed_password = _hash_user_password(settings["mail_pwd"]) hashed_password = _hash_user_password(settings["mail_pwd"])
dovecot_passwd.append( dovecot_passwd.append(
f"{app}:{hashed_password}::::::allow_nets=127.0.0.1/24" f"{app}:{hashed_password}::::::allow_nets=::1,127.0.0.1/24,local"
) )
if postfix: if postfix:
mail_user = settings.get("mail_user", app) mail_user = settings.get("mail_user", app)

View file

@ -19,9 +19,9 @@
import os import os
import re import re
import hashlib import hashlib
from logging import getLogger
from moulinette import m18n from moulinette import m18n
from moulinette.utils.log import getActionLogger
from moulinette.utils.network import download_json from moulinette.utils.network import download_json
from moulinette.utils.filesystem import ( from moulinette.utils.filesystem import (
read_json, read_json,
@ -34,7 +34,7 @@ from moulinette.utils.filesystem import (
from yunohost.utils.i18n import _value_for_locale from yunohost.utils.i18n import _value_for_locale
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError
logger = getActionLogger("yunohost.app_catalog") logger = getLogger("yunohost.app_catalog")
APPS_CATALOG_CACHE = "/var/cache/yunohost/repo" APPS_CATALOG_CACHE = "/var/cache/yunohost/repo"
APPS_CATALOG_LOGOS = "/usr/share/yunohost/applogos" APPS_CATALOG_LOGOS = "/usr/share/yunohost/applogos"

View file

@ -30,10 +30,10 @@ from glob import glob
from collections import OrderedDict from collections import OrderedDict
from functools import reduce from functools import reduce
from packaging import version from packaging import version
from logging import getLogger
from moulinette import Moulinette, m18n from moulinette import Moulinette, m18n
from moulinette.utils.text import random_ascii from moulinette.utils.text import random_ascii
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import ( from moulinette.utils.filesystem import (
read_file, read_file,
mkdir, mkdir,
@ -84,7 +84,7 @@ APP_MARGIN_SPACE_SIZE = 100 # In MB
CONF_MARGIN_SPACE_SIZE = 10 # IN MB CONF_MARGIN_SPACE_SIZE = 10 # IN MB
POSTINSTALL_ESTIMATE_SPACE_SIZE = 5 # In MB POSTINSTALL_ESTIMATE_SPACE_SIZE = 5 # In MB
MB_ALLOWED_TO_ORGANIZE = 10 MB_ALLOWED_TO_ORGANIZE = 10
logger = getActionLogger("yunohost.backup") logger = getLogger("yunohost.backup")
class BackupRestoreTargetsManager: class BackupRestoreTargetsManager:
@ -1204,7 +1204,7 @@ class RestoreManager:
def _patch_legacy_php_versions_in_csv_file(self): def _patch_legacy_php_versions_in_csv_file(self):
""" """
Apply dirty patch to redirect php5 and php7.0 files to php7.4 Apply dirty patch to redirect php5 and php7.x files to php8.2
""" """
from yunohost.utils.legacy import LEGACY_PHP_VERSION_REPLACEMENTS from yunohost.utils.legacy import LEGACY_PHP_VERSION_REPLACEMENTS

View file

@ -21,11 +21,10 @@ import sys
import shutil import shutil
import subprocess import subprocess
from glob import glob from glob import glob
from logging import getLogger
from datetime import datetime from datetime import datetime
from moulinette import m18n from moulinette import m18n
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import read_file, chown, chmod from moulinette.utils.filesystem import read_file, chown, chmod
from moulinette.utils.process import check_output from moulinette.utils.process import check_output
@ -38,7 +37,7 @@ from yunohost.service import _run_service_command
from yunohost.regenconf import regen_conf from yunohost.regenconf import regen_conf
from yunohost.log import OperationLogger from yunohost.log import OperationLogger
logger = getActionLogger("yunohost.certmanager") logger = getLogger("yunohost.certmanager")
CERT_FOLDER = "/etc/yunohost/certs/" CERT_FOLDER = "/etc/yunohost/certs/"
TMP_FOLDER = "/var/www/.well-known/acme-challenge-private/" TMP_FOLDER = "/var/www/.well-known/acme-challenge-private/"

View file

@ -215,6 +215,11 @@ class MyDiagnoser(Diagnoser):
for part in current for part in current
if not part.startswith("ip4:") and not part.startswith("ip6:") if not part.startswith("ip4:") and not part.startswith("ip6:")
} }
if "v=DMARC1" in r["value"]:
for param in current:
key, value = param.split("=")
if key == "p":
return value in ["none", "quarantine", "reject"]
return expected == current return expected == current
elif r["type"] == "MX": elif r["type"] == "MX":
# For MX, we want to ignore the priority # For MX, we want to ignore the priority

View file

@ -43,7 +43,7 @@ class MyDiagnoser(Diagnoser):
dependencies: List[str] = ["ip"] dependencies: List[str] = ["ip"]
def run(self): def run(self):
self.ehlo_domain = _get_maindomain() self.ehlo_domain = _get_maindomain().lower()
self.mail_domains = domain_list()["domains"] self.mail_domains = domain_list()["domains"]
self.ipversions, self.ips = self.get_ips_checked() self.ipversions, self.ips = self.get_ips_checked()
@ -132,7 +132,7 @@ class MyDiagnoser(Diagnoser):
summary=summary, summary=summary,
details=[summary + "_details"], details=[summary + "_details"],
) )
elif r["helo"] != self.ehlo_domain: elif r["helo"].lower() != self.ehlo_domain:
yield dict( yield dict(
meta={"test": "mail_ehlo", "ipversion": ipversion}, meta={"test": "mail_ehlo", "ipversion": ipversion},
data={"wrong_ehlo": r["helo"], "right_ehlo": self.ehlo_domain}, data={"wrong_ehlo": r["helo"], "right_ehlo": self.ehlo_domain},
@ -185,7 +185,7 @@ class MyDiagnoser(Diagnoser):
rdns_domain = "" rdns_domain = ""
if len(value) > 0: if len(value) > 0:
rdns_domain = value[0][:-1] if value[0].endswith(".") else value[0] rdns_domain = value[0][:-1] if value[0].endswith(".") else value[0]
if rdns_domain != self.ehlo_domain: if rdns_domain.lower() != self.ehlo_domain:
details = [ details = [
"diagnosis_mail_fcrdns_different_from_ehlo_domain_details" "diagnosis_mail_fcrdns_different_from_ehlo_domain_details"
] + details ] + details
@ -194,7 +194,7 @@ class MyDiagnoser(Diagnoser):
data={ data={
"ip": ip, "ip": ip,
"ehlo_domain": self.ehlo_domain, "ehlo_domain": self.ehlo_domain,
"rdns_domain": rdns_domain, "rdns_domain": rdns_domain.lower(),
}, },
status="ERROR", status="ERROR",
summary="diagnosis_mail_fcrdns_different_from_ehlo_domain", summary="diagnosis_mail_fcrdns_different_from_ehlo_domain",

View file

@ -21,6 +21,7 @@ import os
import time import time
import glob import glob
from importlib import import_module from importlib import import_module
from logging import getLogger
from moulinette import m18n, Moulinette from moulinette import m18n, Moulinette
from moulinette.utils import log from moulinette.utils import log
@ -33,7 +34,7 @@ from moulinette.utils.filesystem import (
from yunohost.utils.error import YunohostError, YunohostValidationError from yunohost.utils.error import YunohostError, YunohostValidationError
logger = log.getActionLogger("yunohost.diagnosis") logger = getLogger("yunohost.diagnosis")
DIAGNOSIS_CACHE = "/var/cache/yunohost/diagnosis/" DIAGNOSIS_CACHE = "/var/cache/yunohost/diagnosis/"
DIAGNOSIS_CONFIG_FILE = "/etc/yunohost/diagnosis.yml" DIAGNOSIS_CONFIG_FILE = "/etc/yunohost/diagnosis.yml"

View file

@ -19,12 +19,11 @@
import os import os
import re import re
import time import time
from logging import getLogger
from difflib import SequenceMatcher from difflib import SequenceMatcher
from collections import OrderedDict from collections import OrderedDict
from moulinette import m18n, Moulinette from moulinette import m18n, Moulinette
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import read_file, write_to_file, read_toml, mkdir from moulinette.utils.filesystem import read_file, write_to_file, read_toml, mkdir
from yunohost.domain import ( from yunohost.domain import (
@ -42,7 +41,7 @@ from yunohost.settings import settings_get
from yunohost.log import is_unit_operation from yunohost.log import is_unit_operation
from yunohost.hook import hook_callback from yunohost.hook import hook_callback
logger = getActionLogger("yunohost.domain") logger = getLogger("yunohost.domain")
DOMAIN_REGISTRAR_LIST_PATH = "/usr/share/yunohost/registrar_list.toml" DOMAIN_REGISTRAR_LIST_PATH = "/usr/share/yunohost/registrar_list.toml"

View file

@ -20,10 +20,10 @@ import os
import time import time
from typing import List, Optional from typing import List, Optional
from collections import OrderedDict from collections import OrderedDict
from logging import getLogger
from moulinette import m18n, Moulinette from moulinette import m18n, Moulinette
from moulinette.core import MoulinetteError from moulinette.core import MoulinetteError
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import write_to_file, read_yaml, write_to_yaml, rm from moulinette.utils.filesystem import write_to_file, read_yaml, write_to_yaml, rm
from yunohost.app import ( from yunohost.app import (
@ -39,7 +39,7 @@ from yunohost.utils.error import YunohostError, YunohostValidationError
from yunohost.utils.dns import is_yunohost_dyndns_domain from yunohost.utils.dns import is_yunohost_dyndns_domain
from yunohost.log import is_unit_operation from yunohost.log import is_unit_operation
logger = getActionLogger("yunohost.domain") logger = getLogger("yunohost.domain")
DOMAIN_SETTINGS_DIR = "/etc/yunohost/domains" DOMAIN_SETTINGS_DIR = "/etc/yunohost/domains"
@ -175,11 +175,12 @@ def domain_info(domain):
from yunohost.app import app_info from yunohost.app import app_info
from yunohost.dns import _get_registar_settings from yunohost.dns import _get_registar_settings
from yunohost.certificate import certificate_status
_assert_domain_exists(domain) _assert_domain_exists(domain)
registrar, _ = _get_registar_settings(domain) registrar, _ = _get_registar_settings(domain)
certificate = domain_cert_status([domain], full=True)["certificates"][domain] certificate = certificate_status([domain], full=True)["certificates"][domain]
apps = [] apps = []
for app in _installed_apps(): for app in _installed_apps():
@ -865,10 +866,6 @@ def domain_cert_renew(domain_list, force=False, no_checks=False, email=False):
return certificate_renew(domain_list, force, no_checks, email) return certificate_renew(domain_list, force, no_checks, email)
def domain_dns_conf(domain):
return domain_dns_suggest(domain)
def domain_dns_suggest(domain): def domain_dns_suggest(domain):
from yunohost.dns import domain_dns_suggest from yunohost.dns import domain_dns_suggest

View file

@ -22,10 +22,10 @@ import glob
import base64 import base64
import subprocess import subprocess
import hashlib import hashlib
from logging import getLogger
from moulinette import Moulinette, m18n from moulinette import Moulinette, m18n
from moulinette.core import MoulinetteError from moulinette.core import MoulinetteError
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import write_to_file, rm, chown, chmod from moulinette.utils.filesystem import write_to_file, rm, chown, chmod
from moulinette.utils.network import download_json from moulinette.utils.network import download_json
@ -36,7 +36,7 @@ from yunohost.utils.dns import dig, is_yunohost_dyndns_domain
from yunohost.log import is_unit_operation from yunohost.log import is_unit_operation
from yunohost.regenconf import regen_conf from yunohost.regenconf import regen_conf
logger = getActionLogger("yunohost.dyndns") logger = getLogger("yunohost.dyndns")
DYNDNS_PROVIDER = "dyndns.yunohost.org" DYNDNS_PROVIDER = "dyndns.yunohost.org"
DYNDNS_DNS_AUTH = ["ns0.yunohost.org", "ns1.yunohost.org"] DYNDNS_DNS_AUTH = ["ns0.yunohost.org", "ns1.yunohost.org"]

View file

@ -19,16 +19,16 @@
import os import os
import yaml import yaml
import miniupnpc import miniupnpc
from logging import getLogger
from moulinette import m18n from moulinette import m18n
from yunohost.utils.error import YunohostError, YunohostValidationError from yunohost.utils.error import YunohostError, YunohostValidationError
from moulinette.utils import process from moulinette.utils import process
from moulinette.utils.log import getActionLogger
FIREWALL_FILE = "/etc/yunohost/firewall.yml" FIREWALL_FILE = "/etc/yunohost/firewall.yml"
UPNP_CRON_JOB = "/etc/cron.d/yunohost-firewall-upnp" UPNP_CRON_JOB = "/etc/cron.d/yunohost-firewall-upnp"
logger = getActionLogger("yunohost.firewall") logger = getLogger("yunohost.firewall")
def firewall_allow( def firewall_allow(
@ -402,7 +402,13 @@ def firewall_upnp(action="status", no_refresh=False):
# Discover UPnP device(s) # Discover UPnP device(s)
logger.debug("discovering UPnP devices...") logger.debug("discovering UPnP devices...")
nb_dev = upnpc.discover() try:
nb_dev = upnpc.discover()
except Exception:
logger.warning("Failed to find any UPnP device on the network")
nb_dev = -1
enabled = False
logger.debug("found %d UPnP device(s)", int(nb_dev)) logger.debug("found %d UPnP device(s)", int(nb_dev))
if nb_dev < 1: if nb_dev < 1:
logger.error(m18n.n("upnp_dev_not_found")) logger.error(m18n.n("upnp_dev_not_found"))

View file

@ -23,16 +23,16 @@ import tempfile
import mimetypes import mimetypes
from glob import iglob from glob import iglob
from importlib import import_module from importlib import import_module
from logging import getLogger
from moulinette import m18n, Moulinette from moulinette import m18n, Moulinette
from yunohost.utils.error import YunohostError, YunohostValidationError from yunohost.utils.error import YunohostError, YunohostValidationError
from moulinette.utils import log
from moulinette.utils.filesystem import read_yaml, cp from moulinette.utils.filesystem import read_yaml, cp
HOOK_FOLDER = "/usr/share/yunohost/hooks/" HOOK_FOLDER = "/usr/share/yunohost/hooks/"
CUSTOM_HOOK_FOLDER = "/etc/yunohost/hooks.d/" CUSTOM_HOOK_FOLDER = "/etc/yunohost/hooks.d/"
logger = log.getActionLogger("yunohost.hook") logger = getLogger("yunohost.hook")
def hook_add(app, file): def hook_add(app, file):
@ -359,6 +359,7 @@ def hook_exec(
r"Removing obsolete dictionary files", r"Removing obsolete dictionary files",
r"Creating new PostgreSQL cluster", r"Creating new PostgreSQL cluster",
r"/usr/lib/postgresql/13/bin/initdb", r"/usr/lib/postgresql/13/bin/initdb",
r"/usr/lib/postgresql/15/bin/initdb",
r"The files belonging to this database system will be owned by user", r"The files belonging to this database system will be owned by user",
r"This user must also own the server process.", r"This user must also own the server process.",
r"The database cluster will be initialized with locale", r"The database cluster will be initialized with locale",
@ -366,6 +367,7 @@ def hook_exec(
r"The default text search configuration will be set to", r"The default text search configuration will be set to",
r"Data page checksums are disabled.", r"Data page checksums are disabled.",
r"fixing permissions on existing directory /var/lib/postgresql/13/main ... ok", r"fixing permissions on existing directory /var/lib/postgresql/13/main ... ok",
r"fixing permissions on existing directory /var/lib/postgresql/15/main ... ok",
r"creating subdirectories \.\.\. ok", r"creating subdirectories \.\.\. ok",
r"selecting dynamic .* \.\.\. ", r"selecting dynamic .* \.\.\. ",
r"selecting default .* \.\.\. ", r"selecting default .* \.\.\. ",

View file

@ -32,10 +32,9 @@ from moulinette import m18n, Moulinette
from moulinette.core import MoulinetteError from moulinette.core import MoulinetteError
from yunohost.utils.error import YunohostError, YunohostValidationError from yunohost.utils.error import YunohostError, YunohostValidationError
from yunohost.utils.system import get_ynh_package_version from yunohost.utils.system import get_ynh_package_version
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import read_file, read_yaml from moulinette.utils.filesystem import read_file, read_yaml
logger = getActionLogger("yunohost.log") logger = getLogger("yunohost.log")
CATEGORIES_PATH = "/var/log/yunohost/categories/" CATEGORIES_PATH = "/var/log/yunohost/categories/"
OPERATIONS_PATH = "/var/log/yunohost/categories/operation/" OPERATIONS_PATH = "/var/log/yunohost/categories/operation/"

View file

@ -1,9 +1,9 @@
import glob import glob
import os import os
from logging import getLogger
from moulinette import m18n from moulinette import m18n
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError
from moulinette.utils.log import getActionLogger
from moulinette.utils.process import check_output, call_async_output from moulinette.utils.process import check_output, call_async_output
from moulinette.utils.filesystem import read_file, rm, write_to_file from moulinette.utils.filesystem import read_file, rm, write_to_file
@ -22,7 +22,7 @@ from yunohost.utils.system import (
) )
from yunohost.service import _get_services, _save_services from yunohost.service import _get_services, _save_services
logger = getActionLogger("yunohost.migration") logger = getLogger("yunohost.migration")
N_CURRENT_DEBIAN = 10 N_CURRENT_DEBIAN = 10
N_CURRENT_YUNOHOST = 4 N_CURRENT_YUNOHOST = 4

View file

@ -1,15 +1,14 @@
import os import os
import glob import glob
from shutil import copy2 from shutil import copy2
from logging import getLogger
from moulinette.utils.log import getActionLogger
from yunohost.app import _is_installed from yunohost.app import _is_installed
from yunohost.utils.legacy import _patch_legacy_php_versions_in_settings from yunohost.utils.legacy import _patch_legacy_php_versions_in_settings
from yunohost.tools import Migration from yunohost.tools import Migration
from yunohost.service import _run_service_command from yunohost.service import _run_service_command
logger = getActionLogger("yunohost.migration") logger = getLogger("yunohost.migration")
OLDPHP_POOLS = "/etc/php/7.3/fpm/pool.d" OLDPHP_POOLS = "/etc/php/7.3/fpm/pool.d"
NEWPHP_POOLS = "/etc/php/7.4/fpm/pool.d" NEWPHP_POOLS = "/etc/php/7.4/fpm/pool.d"

View file

@ -1,15 +1,15 @@
import subprocess import subprocess
import time import time
import os import os
from logging import getLogger
from moulinette import m18n from moulinette import m18n
from yunohost.utils.error import YunohostError, YunohostValidationError from yunohost.utils.error import YunohostError, YunohostValidationError
from moulinette.utils.log import getActionLogger
from yunohost.tools import Migration from yunohost.tools import Migration
from yunohost.utils.system import free_space_in_directory, space_used_by_directory from yunohost.utils.system import free_space_in_directory, space_used_by_directory
logger = getActionLogger("yunohost.migration") logger = getLogger("yunohost.migration")
class MyMigration(Migration): class MyMigration(Migration):

View file

@ -1,14 +1,14 @@
import os import os
from logging import getLogger
from moulinette import m18n from moulinette import m18n
from moulinette.utils.log import getActionLogger
from moulinette.utils.process import call_async_output from moulinette.utils.process import call_async_output
from yunohost.tools import Migration, tools_migrations_state from yunohost.tools import Migration, tools_migrations_state
from moulinette.utils.filesystem import rm from moulinette.utils.filesystem import rm
logger = getActionLogger("yunohost.migration") logger = getLogger("yunohost.migration")
VENV_REQUIREMENTS_SUFFIX = ".requirements_backup_for_bullseye_upgrade.txt" VENV_REQUIREMENTS_SUFFIX = ".requirements_backup_for_bullseye_upgrade.txt"

View file

@ -1,13 +1,13 @@
import os import os
from logging import getLogger
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import read_json, write_to_yaml from moulinette.utils.filesystem import read_json, write_to_yaml
from yunohost.tools import Migration from yunohost.tools import Migration
from yunohost.utils.legacy import translate_legacy_settings_to_configpanel_settings from yunohost.utils.legacy import translate_legacy_settings_to_configpanel_settings
logger = getActionLogger("yunohost.migration") logger = getLogger("yunohost.migration")
SETTINGS_PATH = "/etc/yunohost/settings.yml" SETTINGS_PATH = "/etc/yunohost/settings.yml"
OLD_SETTINGS_PATH = "/etc/yunohost/settings.json" OLD_SETTINGS_PATH = "/etc/yunohost/settings.json"

View file

@ -1,8 +1,8 @@
from moulinette.utils.log import getActionLogger from logging import getLogger
from yunohost.tools import Migration from yunohost.tools import Migration
logger = getActionLogger("yunohost.migration") logger = getLogger("yunohost.migration")
################################################### ###################################################
# Tools used also for restoration # Tools used also for restoration

View file

@ -20,13 +20,13 @@ import re
import copy import copy
import grp import grp
import random import random
from logging import getLogger
from moulinette import m18n from moulinette import m18n
from moulinette.utils.log import getActionLogger
from yunohost.utils.error import YunohostError, YunohostValidationError from yunohost.utils.error import YunohostError, YunohostValidationError
from yunohost.log import is_unit_operation from yunohost.log import is_unit_operation
logger = getActionLogger("yunohost.user") logger = getLogger("yunohost.user")
SYSTEM_PERMS = ["mail", "xmpp", "sftp", "ssh"] SYSTEM_PERMS = ["mail", "xmpp", "sftp", "ssh"]

View file

@ -20,12 +20,12 @@ import os
import yaml import yaml
import shutil import shutil
import hashlib import hashlib
from logging import getLogger
from difflib import unified_diff from difflib import unified_diff
from datetime import datetime from datetime import datetime
from moulinette import m18n from moulinette import m18n
from moulinette.utils import log, filesystem from moulinette.utils.filesystem import mkdir
from moulinette.utils.process import check_output from moulinette.utils.process import check_output
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError
@ -37,7 +37,7 @@ BACKUP_CONF_DIR = os.path.join(BASE_CONF_PATH, "backup")
PENDING_CONF_DIR = os.path.join(BASE_CONF_PATH, "pending") PENDING_CONF_DIR = os.path.join(BASE_CONF_PATH, "pending")
REGEN_CONF_FILE = "/etc/yunohost/regenconf.yml" REGEN_CONF_FILE = "/etc/yunohost/regenconf.yml"
logger = log.getActionLogger("yunohost.regenconf") logger = getLogger("yunohost.regenconf")
# FIXME : those ain't just services anymore ... what are we supposed to do with this ... # FIXME : those ain't just services anymore ... what are we supposed to do with this ...
@ -102,7 +102,7 @@ def regen_conf(
for name in names: for name in names:
shutil.rmtree(os.path.join(PENDING_CONF_DIR, name), ignore_errors=True) shutil.rmtree(os.path.join(PENDING_CONF_DIR, name), ignore_errors=True)
else: else:
filesystem.mkdir(PENDING_CONF_DIR, 0o755, True) mkdir(PENDING_CONF_DIR, 0o755, True)
# Execute hooks for pre-regen # Execute hooks for pre-regen
# element 2 and 3 with empty string is because of legacy... # element 2 and 3 with empty string is because of legacy...
@ -111,7 +111,7 @@ def regen_conf(
def _pre_call(name, priority, path, args): def _pre_call(name, priority, path, args):
# create the pending conf directory for the category # create the pending conf directory for the category
category_pending_path = os.path.join(PENDING_CONF_DIR, name) category_pending_path = os.path.join(PENDING_CONF_DIR, name)
filesystem.mkdir(category_pending_path, 0o755, True, uid="root") mkdir(category_pending_path, 0o755, True, uid="root")
# return the arguments to pass to the script # return the arguments to pass to the script
return pre_args + [ return pre_args + [
@ -622,7 +622,7 @@ def _process_regen_conf(system_conf, new_conf=None, save=True):
backup_dir = os.path.dirname(backup_path) backup_dir = os.path.dirname(backup_path)
if not os.path.isdir(backup_dir): if not os.path.isdir(backup_dir):
filesystem.mkdir(backup_dir, 0o755, True) mkdir(backup_dir, 0o755, True)
shutil.copy2(system_conf, backup_path) shutil.copy2(system_conf, backup_path)
logger.debug( logger.debug(
@ -637,7 +637,7 @@ def _process_regen_conf(system_conf, new_conf=None, save=True):
system_dir = os.path.dirname(system_conf) system_dir = os.path.dirname(system_conf)
if not os.path.isdir(system_dir): if not os.path.isdir(system_dir):
filesystem.mkdir(system_dir, 0o755, True) mkdir(system_dir, 0o755, True)
shutil.copyfile(new_conf, system_conf) shutil.copyfile(new_conf, system_conf)
logger.debug(m18n.n("regenconf_file_updated", conf=system_conf)) logger.debug(m18n.n("regenconf_file_updated", conf=system_conf))

View file

@ -21,14 +21,13 @@ import os
import time import time
import yaml import yaml
import subprocess import subprocess
from logging import getLogger
from glob import glob from glob import glob
from datetime import datetime from datetime import datetime
from moulinette import m18n from moulinette import m18n
from yunohost.utils.error import YunohostError, YunohostValidationError from yunohost.utils.error import YunohostError, YunohostValidationError
from moulinette.utils.process import check_output from moulinette.utils.process import check_output
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import ( from moulinette.utils.filesystem import (
read_file, read_file,
append_to_file, append_to_file,
@ -42,7 +41,7 @@ MOULINETTE_LOCK = "/var/run/moulinette_yunohost.lock"
SERVICES_CONF = "/etc/yunohost/services.yml" SERVICES_CONF = "/etc/yunohost/services.yml"
SERVICES_CONF_BASE = "/usr/share/yunohost/conf/yunohost/services.yml" SERVICES_CONF_BASE = "/usr/share/yunohost/conf/yunohost/services.yml"
logger = getActionLogger("yunohost.service") logger = getLogger("yunohost.service")
def service_add( def service_add(

View file

@ -18,18 +18,18 @@
# #
import os import os
import subprocess import subprocess
from logging import getLogger
from moulinette import m18n from moulinette import m18n
from yunohost.utils.error import YunohostError, YunohostValidationError from yunohost.utils.error import YunohostError, YunohostValidationError
from yunohost.utils.configpanel import ConfigPanel from yunohost.utils.configpanel import ConfigPanel
from yunohost.utils.form import BaseOption from yunohost.utils.form import BaseOption
from moulinette.utils.log import getActionLogger
from yunohost.regenconf import regen_conf from yunohost.regenconf import regen_conf
from yunohost.firewall import firewall_reload from yunohost.firewall import firewall_reload
from yunohost.log import is_unit_operation from yunohost.log import is_unit_operation
from yunohost.utils.legacy import translate_legacy_settings_to_configpanel_settings from yunohost.utils.legacy import translate_legacy_settings_to_configpanel_settings
logger = getActionLogger("yunohost.settings") logger = getLogger("yunohost.settings")
SETTINGS_PATH = "/etc/yunohost/settings.yml" SETTINGS_PATH = "/etc/yunohost/settings.yml"

View file

@ -1,5 +1,6 @@
import os import os
import pytest import pytest
from unittest.mock import Mock
import moulinette import moulinette
from moulinette import m18n, Moulinette from moulinette import m18n, Moulinette
@ -23,11 +24,15 @@ def get_test_apps_dir():
@contextmanager @contextmanager
def message(mocker, key, **kwargs): def message(key, **kwargs):
mocker.spy(m18n, "n") m = Mock(wraps=m18n.n)
old_m18n = m18n.n
m18n.n = m
yield yield
m18n.n.assert_any_call(key, **kwargs) try:
m.assert_any_call(key, **kwargs)
finally:
m18n.n = old_m18n
@contextmanager @contextmanager
def raiseYunohostError(mocker, key, **kwargs): def raiseYunohostError(mocker, key, **kwargs):

View file

@ -5,6 +5,8 @@ import requests_mock
import glob import glob
import shutil import shutil
from .conftest import message
from moulinette import m18n from moulinette import m18n
from moulinette.utils.filesystem import read_json, write_to_json, write_to_yaml from moulinette.utils.filesystem import read_json, write_to_json, write_to_yaml
@ -258,13 +260,12 @@ def test_apps_catalog_load_with_conflicts_between_lists(mocker):
assert "bar" in app_dict.keys() assert "bar" in app_dict.keys()
def test_apps_catalog_load_with_oudated_api_version(mocker): def test_apps_catalog_load_with_outdated_api_version():
# Initialize ... # Initialize ...
_initialize_apps_catalog_system() _initialize_apps_catalog_system()
# Update # Update
with requests_mock.Mocker() as m: with requests_mock.Mocker() as m:
mocker.spy(m18n, "n")
m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL, text=DUMMY_APP_CATALOG) m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL, text=DUMMY_APP_CATALOG)
_update_apps_catalog() _update_apps_catalog()
@ -282,10 +283,8 @@ def test_apps_catalog_load_with_oudated_api_version(mocker):
with requests_mock.Mocker() as m: with requests_mock.Mocker() as m:
# Mock the server response with a dummy apps catalog # Mock the server response with a dummy apps catalog
m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL, text=DUMMY_APP_CATALOG) m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL, text=DUMMY_APP_CATALOG)
with message("apps_catalog_update_success"):
mocker.spy(m18n, "n") app_dict = _load_apps_catalog()["apps"]
app_dict = _load_apps_catalog()["apps"]
m18n.n.assert_any_call("apps_catalog_update_success")
assert "foo" in app_dict.keys() assert "foo" in app_dict.keys()
assert "bar" in app_dict.keys() assert "bar" in app_dict.keys()

View file

@ -394,9 +394,9 @@ def test_legacy_app_install_private(secondary_domain):
assert app_is_not_installed(secondary_domain, "legacy_app") assert app_is_not_installed(secondary_domain, "legacy_app")
def test_legacy_app_install_unknown_domain(mocker): def test_legacy_app_install_unknown_domain():
with pytest.raises(YunohostError): with pytest.raises(YunohostError):
with message(mocker, "app_argument_invalid"): with message("app_argument_invalid"):
install_legacy_app("whatever.nope", "/legacy") install_legacy_app("whatever.nope", "/legacy")
assert app_is_not_installed("whatever.nope", "legacy_app") assert app_is_not_installed("whatever.nope", "legacy_app")
@ -423,12 +423,12 @@ def test_legacy_app_install_multiple_instances(secondary_domain):
assert app_is_not_installed(secondary_domain, "legacy_app__2") assert app_is_not_installed(secondary_domain, "legacy_app__2")
def test_legacy_app_install_path_unavailable(mocker, secondary_domain): def test_legacy_app_install_path_unavailable(secondary_domain):
# These will be removed in teardown # These will be removed in teardown
install_legacy_app(secondary_domain, "/legacy") install_legacy_app(secondary_domain, "/legacy")
with pytest.raises(YunohostError): with pytest.raises(YunohostError):
with message(mocker, "app_location_unavailable"): with message("app_location_unavailable"):
install_legacy_app(secondary_domain, "/") install_legacy_app(secondary_domain, "/")
assert app_is_installed(secondary_domain, "legacy_app") assert app_is_installed(secondary_domain, "legacy_app")
@ -444,19 +444,19 @@ def test_legacy_app_install_with_nginx_down(mocker, secondary_domain):
install_legacy_app(secondary_domain, "/legacy") install_legacy_app(secondary_domain, "/legacy")
def test_legacy_app_failed_install(mocker, secondary_domain): def test_legacy_app_failed_install(secondary_domain):
# This will conflict with the folder that the app # This will conflict with the folder that the app
# attempts to create, making the install fail # attempts to create, making the install fail
mkdir("/var/www/legacy_app/", 0o750) mkdir("/var/www/legacy_app/", 0o750)
with pytest.raises(YunohostError): with pytest.raises(YunohostError):
with message(mocker, "app_install_script_failed"): with message("app_install_script_failed"):
install_legacy_app(secondary_domain, "/legacy") install_legacy_app(secondary_domain, "/legacy")
assert app_is_not_installed(secondary_domain, "legacy_app") assert app_is_not_installed(secondary_domain, "legacy_app")
def test_legacy_app_failed_remove(mocker, secondary_domain): def test_legacy_app_failed_remove(secondary_domain):
install_legacy_app(secondary_domain, "/legacy") install_legacy_app(secondary_domain, "/legacy")
# The remove script runs with set -eu and attempt to remove this # The remove script runs with set -eu and attempt to remove this
@ -488,52 +488,52 @@ def test_full_domain_app_with_conflicts(mocker, secondary_domain):
install_full_domain_app(secondary_domain) install_full_domain_app(secondary_domain)
def test_systemfuckedup_during_app_install(mocker, secondary_domain): def test_systemfuckedup_during_app_install(secondary_domain):
with pytest.raises(YunohostError): with pytest.raises(YunohostError):
with message(mocker, "app_install_failed"): with message("app_install_failed"):
with message(mocker, "app_action_broke_system"): with message("app_action_broke_system"):
install_break_yo_system(secondary_domain, breakwhat="install") install_break_yo_system(secondary_domain, breakwhat="install")
assert app_is_not_installed(secondary_domain, "break_yo_system") assert app_is_not_installed(secondary_domain, "break_yo_system")
def test_systemfuckedup_during_app_remove(mocker, secondary_domain): def test_systemfuckedup_during_app_remove(secondary_domain):
install_break_yo_system(secondary_domain, breakwhat="remove") install_break_yo_system(secondary_domain, breakwhat="remove")
with pytest.raises(YunohostError): with pytest.raises(YunohostError):
with message(mocker, "app_action_broke_system"): with message("app_action_broke_system"):
with message(mocker, "app_removed"): with message("app_removed"):
app_remove("break_yo_system") app_remove("break_yo_system")
assert app_is_not_installed(secondary_domain, "break_yo_system") assert app_is_not_installed(secondary_domain, "break_yo_system")
def test_systemfuckedup_during_app_install_and_remove(mocker, secondary_domain): def test_systemfuckedup_during_app_install_and_remove(secondary_domain):
with pytest.raises(YunohostError): with pytest.raises(YunohostError):
with message(mocker, "app_install_failed"): with message("app_install_failed"):
with message(mocker, "app_action_broke_system"): with message("app_action_broke_system"):
install_break_yo_system(secondary_domain, breakwhat="everything") install_break_yo_system(secondary_domain, breakwhat="everything")
assert app_is_not_installed(secondary_domain, "break_yo_system") assert app_is_not_installed(secondary_domain, "break_yo_system")
def test_systemfuckedup_during_app_upgrade(mocker, secondary_domain): def test_systemfuckedup_during_app_upgrade(secondary_domain):
install_break_yo_system(secondary_domain, breakwhat="upgrade") install_break_yo_system(secondary_domain, breakwhat="upgrade")
with pytest.raises(YunohostError): with pytest.raises(YunohostError):
with message(mocker, "app_action_broke_system"): with message("app_action_broke_system"):
app_upgrade( app_upgrade(
"break_yo_system", "break_yo_system",
file=os.path.join(get_test_apps_dir(), "break_yo_system_ynh"), file=os.path.join(get_test_apps_dir(), "break_yo_system_ynh"),
) )
def test_failed_multiple_app_upgrade(mocker, secondary_domain): def test_failed_multiple_app_upgrade(secondary_domain):
install_legacy_app(secondary_domain, "/legacy") install_legacy_app(secondary_domain, "/legacy")
install_break_yo_system(secondary_domain, breakwhat="upgrade") install_break_yo_system(secondary_domain, breakwhat="upgrade")
with pytest.raises(YunohostError): with pytest.raises(YunohostError):
with message(mocker, "app_not_upgraded"): with message("app_not_upgraded"):
app_upgrade( app_upgrade(
["break_yo_system", "legacy_app"], ["break_yo_system", "legacy_app"],
file={ file={

View file

@ -217,10 +217,6 @@ def test_normalize_permission_path_with_bad_regex():
) )
# Full Regex # Full Regex
with pytest.raises(YunohostError):
_validate_and_sanitize_permission_url(
"re:" + maindomain + "/yolo?+/", maindomain + "/path", "test_permission"
)
with pytest.raises(YunohostError): with pytest.raises(YunohostError):
_validate_and_sanitize_permission_url( _validate_and_sanitize_permission_url(
"re:" + maindomain + "/yolo[1-9]**/", "re:" + maindomain + "/yolo[1-9]**/",

View file

@ -49,8 +49,8 @@ def setup_function(function):
for m in function.__dict__.get("pytestmark", []) for m in function.__dict__.get("pytestmark", [])
} }
if "with_wordpress_archive_from_4p2" in markers: if "with_wordpress_archive_from_11p2" in markers:
add_archive_wordpress_from_4p2() add_archive_wordpress_from_11p2()
assert len(backup_list()["archives"]) == 1 assert len(backup_list()["archives"]) == 1
if "with_legacy_app_installed" in markers: if "with_legacy_app_installed" in markers:
@ -72,8 +72,8 @@ def setup_function(function):
) )
assert app_is_installed("backup_recommended_app") assert app_is_installed("backup_recommended_app")
if "with_system_archive_from_4p2" in markers: if "with_system_archive_from_11p2" in markers:
add_archive_system_from_4p2() add_archive_system_from_11p2()
assert len(backup_list()["archives"]) == 1 assert len(backup_list()["archives"]) == 1
if "with_permission_app_installed" in markers: if "with_permission_app_installed" in markers:
@ -148,7 +148,7 @@ def app_is_installed(app):
def backup_test_dependencies_are_met(): def backup_test_dependencies_are_met():
# Dummy test apps (or backup archives) # Dummy test apps (or backup archives)
assert os.path.exists( assert os.path.exists(
os.path.join(get_test_apps_dir(), "backup_wordpress_from_4p2") os.path.join(get_test_apps_dir(), "backup_wordpress_from_11p2")
) )
assert os.path.exists(os.path.join(get_test_apps_dir(), "legacy_app_ynh")) assert os.path.exists(os.path.join(get_test_apps_dir(), "legacy_app_ynh"))
assert os.path.exists( assert os.path.exists(
@ -211,23 +211,23 @@ def install_app(app, path, additionnal_args=""):
) )
def add_archive_wordpress_from_4p2(): def add_archive_wordpress_from_11p2():
os.system("mkdir -p /home/yunohost.backup/archives") os.system("mkdir -p /home/yunohost.backup/archives")
os.system( os.system(
"cp " "cp "
+ os.path.join(get_test_apps_dir(), "backup_wordpress_from_4p2/backup.tar") + os.path.join(get_test_apps_dir(), "backup_wordpress_from_11p2/backup.tar")
+ " /home/yunohost.backup/archives/backup_wordpress_from_4p2.tar" + " /home/yunohost.backup/archives/backup_wordpress_from_11p2.tar"
) )
def add_archive_system_from_4p2(): def add_archive_system_from_11p2():
os.system("mkdir -p /home/yunohost.backup/archives") os.system("mkdir -p /home/yunohost.backup/archives")
os.system( os.system(
"cp " "cp "
+ os.path.join(get_test_apps_dir(), "backup_system_from_4p2/backup.tar") + os.path.join(get_test_apps_dir(), "backup_system_from_11p2/backup.tar")
+ " /home/yunohost.backup/archives/backup_system_from_4p2.tar" + " /home/yunohost.backup/archives/backup_system_from_11p2.tar"
) )
@ -236,10 +236,10 @@ def add_archive_system_from_4p2():
# #
def test_backup_only_ldap(mocker): def test_backup_only_ldap():
# Create the backup # Create the backup
name = random_ascii(8) name = random_ascii(8)
with message(mocker, "backup_created", name=name): with message("backup_created", name=name):
backup_create(name=name, system=["conf_ldap"], apps=None) backup_create(name=name, system=["conf_ldap"], apps=None)
archives = backup_list()["archives"] archives = backup_list()["archives"]
@ -253,7 +253,7 @@ def test_backup_only_ldap(mocker):
def test_backup_system_part_that_does_not_exists(mocker): def test_backup_system_part_that_does_not_exists(mocker):
# Create the backup # Create the backup
with message(mocker, "backup_hook_unknown", hook="doesnt_exist"): with message("backup_hook_unknown", hook="doesnt_exist"):
with raiseYunohostError(mocker, "backup_nothings_done"): with raiseYunohostError(mocker, "backup_nothings_done"):
backup_create(system=["doesnt_exist"], apps=None) backup_create(system=["doesnt_exist"], apps=None)
@ -263,10 +263,10 @@ def test_backup_system_part_that_does_not_exists(mocker):
# #
def test_backup_and_restore_all_sys(mocker): def test_backup_and_restore_all_sys():
name = random_ascii(8) name = random_ascii(8)
# Create the backup # Create the backup
with message(mocker, "backup_created", name=name): with message("backup_created", name=name):
backup_create(name=name, system=[], apps=None) backup_create(name=name, system=[], apps=None)
archives = backup_list()["archives"] archives = backup_list()["archives"]
@ -284,7 +284,7 @@ def test_backup_and_restore_all_sys(mocker):
assert not os.path.exists("/etc/ssowat/conf.json") assert not os.path.exists("/etc/ssowat/conf.json")
# Restore the backup # Restore the backup
with message(mocker, "restore_complete"): with message("restore_complete"):
backup_restore(name=archives[0], force=True, system=[], apps=None) backup_restore(name=archives[0], force=True, system=[], apps=None)
# Check ssowat conf is back # Check ssowat conf is back
@ -292,22 +292,22 @@ def test_backup_and_restore_all_sys(mocker):
# #
# System restore from 3.8 # # System restore from 11.2 #
# #
@pytest.mark.with_system_archive_from_4p2 @pytest.mark.with_system_archive_from_11p2
def test_restore_system_from_Ynh4p2(monkeypatch, mocker): def test_restore_system_from_Ynh11p2(monkeypatch):
name = random_ascii(8) name = random_ascii(8)
# Backup current system # Backup current system
with message(mocker, "backup_created", name=name): with message("backup_created", name=name):
backup_create(name=name, system=[], apps=None) backup_create(name=name, system=[], apps=None)
archives = backup_list()["archives"] archives = backup_list()["archives"]
assert len(archives) == 2 assert len(archives) == 2
# Restore system archive from 3.8 # Restore system archive from 11.2
try: try:
with message(mocker, "restore_complete"): with message("restore_complete"):
backup_restore( backup_restore(
name=backup_list()["archives"][1], system=[], apps=None, force=True name=backup_list()["archives"][1], system=[], apps=None, force=True
) )
@ -336,7 +336,7 @@ def test_backup_script_failure_handling(monkeypatch, mocker):
# with the expected error message key # with the expected error message key
monkeypatch.setattr("yunohost.backup.hook_exec", custom_hook_exec) monkeypatch.setattr("yunohost.backup.hook_exec", custom_hook_exec)
with message(mocker, "backup_app_failed", app="backup_recommended_app"): with message("backup_app_failed", app="backup_recommended_app"):
with raiseYunohostError(mocker, "backup_nothings_done"): with raiseYunohostError(mocker, "backup_nothings_done"):
backup_create(system=None, apps=["backup_recommended_app"]) backup_create(system=None, apps=["backup_recommended_app"])
@ -363,7 +363,7 @@ def test_backup_not_enough_free_space(monkeypatch, mocker):
def test_backup_app_not_installed(mocker): def test_backup_app_not_installed(mocker):
assert not _is_installed("wordpress") assert not _is_installed("wordpress")
with message(mocker, "unbackup_app", app="wordpress"): with message("unbackup_app", app="wordpress"):
with raiseYunohostError(mocker, "backup_nothings_done"): with raiseYunohostError(mocker, "backup_nothings_done"):
backup_create(system=None, apps=["wordpress"]) backup_create(system=None, apps=["wordpress"])
@ -375,14 +375,14 @@ def test_backup_app_with_no_backup_script(mocker):
assert not os.path.exists(backup_script) assert not os.path.exists(backup_script)
with message( with message(
mocker, "backup_with_no_backup_script_for_app", app="backup_recommended_app" "backup_with_no_backup_script_for_app", app="backup_recommended_app"
): ):
with raiseYunohostError(mocker, "backup_nothings_done"): with raiseYunohostError(mocker, "backup_nothings_done"):
backup_create(system=None, apps=["backup_recommended_app"]) backup_create(system=None, apps=["backup_recommended_app"])
@pytest.mark.with_backup_recommended_app_installed @pytest.mark.with_backup_recommended_app_installed
def test_backup_app_with_no_restore_script(mocker): def test_backup_app_with_no_restore_script():
restore_script = "/etc/yunohost/apps/backup_recommended_app/scripts/restore" restore_script = "/etc/yunohost/apps/backup_recommended_app/scripts/restore"
os.system("rm %s" % restore_script) os.system("rm %s" % restore_script)
assert not os.path.exists(restore_script) assert not os.path.exists(restore_script)
@ -391,16 +391,16 @@ def test_backup_app_with_no_restore_script(mocker):
# user... # user...
with message( with message(
mocker, "backup_with_no_restore_script_for_app", app="backup_recommended_app" "backup_with_no_restore_script_for_app", app="backup_recommended_app"
): ):
backup_create(system=None, apps=["backup_recommended_app"]) backup_create(system=None, apps=["backup_recommended_app"])
@pytest.mark.clean_opt_dir @pytest.mark.clean_opt_dir
def test_backup_with_different_output_directory(mocker): def test_backup_with_different_output_directory():
name = random_ascii(8) name = random_ascii(8)
# Create the backup # Create the backup
with message(mocker, "backup_created", name=name): with message("backup_created", name=name):
backup_create( backup_create(
system=["conf_ynh_settings"], system=["conf_ynh_settings"],
apps=None, apps=None,
@ -420,10 +420,10 @@ def test_backup_with_different_output_directory(mocker):
@pytest.mark.clean_opt_dir @pytest.mark.clean_opt_dir
def test_backup_using_copy_method(mocker): def test_backup_using_copy_method():
# Create the backup # Create the backup
name = random_ascii(8) name = random_ascii(8)
with message(mocker, "backup_created", name=name): with message("backup_created", name=name):
backup_create( backup_create(
system=["conf_ynh_settings"], system=["conf_ynh_settings"],
apps=None, apps=None,
@ -439,17 +439,16 @@ def test_backup_using_copy_method(mocker):
# App restore # # App restore #
# #
@pytest.mark.with_wordpress_archive_from_11p2
@pytest.mark.with_wordpress_archive_from_4p2
@pytest.mark.with_custom_domain("yolo.test") @pytest.mark.with_custom_domain("yolo.test")
def test_restore_app_wordpress_from_Ynh4p2(mocker): def test_restore_app_wordpress_from_Ynh11p2():
with message(mocker, "restore_complete"): with message("restore_complete"):
backup_restore( backup_restore(
system=None, name=backup_list()["archives"][0], apps=["wordpress"] system=None, name=backup_list()["archives"][0], apps=["wordpress"]
) )
@pytest.mark.with_wordpress_archive_from_4p2 @pytest.mark.with_wordpress_archive_from_11p2
@pytest.mark.with_custom_domain("yolo.test") @pytest.mark.with_custom_domain("yolo.test")
def test_restore_app_script_failure_handling(monkeypatch, mocker): def test_restore_app_script_failure_handling(monkeypatch, mocker):
def custom_hook_exec(name, *args, **kwargs): def custom_hook_exec(name, *args, **kwargs):
@ -461,7 +460,7 @@ def test_restore_app_script_failure_handling(monkeypatch, mocker):
assert not _is_installed("wordpress") assert not _is_installed("wordpress")
with message(mocker, "app_restore_script_failed"): with message("app_restore_script_failed"):
with raiseYunohostError(mocker, "restore_nothings_done"): with raiseYunohostError(mocker, "restore_nothings_done"):
backup_restore( backup_restore(
system=None, name=backup_list()["archives"][0], apps=["wordpress"] system=None, name=backup_list()["archives"][0], apps=["wordpress"]
@ -470,7 +469,7 @@ def test_restore_app_script_failure_handling(monkeypatch, mocker):
assert not _is_installed("wordpress") assert not _is_installed("wordpress")
@pytest.mark.with_wordpress_archive_from_4p2 @pytest.mark.with_wordpress_archive_from_11p2
def test_restore_app_not_enough_free_space(monkeypatch, mocker): def test_restore_app_not_enough_free_space(monkeypatch, mocker):
def custom_free_space_in_directory(dirpath): def custom_free_space_in_directory(dirpath):
return 0 return 0
@ -489,12 +488,12 @@ def test_restore_app_not_enough_free_space(monkeypatch, mocker):
assert not _is_installed("wordpress") assert not _is_installed("wordpress")
@pytest.mark.with_wordpress_archive_from_4p2 @pytest.mark.with_wordpress_archive_from_11p2
def test_restore_app_not_in_backup(mocker): def test_restore_app_not_in_backup(mocker):
assert not _is_installed("wordpress") assert not _is_installed("wordpress")
assert not _is_installed("yoloswag") assert not _is_installed("yoloswag")
with message(mocker, "backup_archive_app_not_found", app="yoloswag"): with message("backup_archive_app_not_found", app="yoloswag"):
with raiseYunohostError(mocker, "restore_nothings_done"): with raiseYunohostError(mocker, "restore_nothings_done"):
backup_restore( backup_restore(
system=None, name=backup_list()["archives"][0], apps=["yoloswag"] system=None, name=backup_list()["archives"][0], apps=["yoloswag"]
@ -504,12 +503,12 @@ def test_restore_app_not_in_backup(mocker):
assert not _is_installed("yoloswag") assert not _is_installed("yoloswag")
@pytest.mark.with_wordpress_archive_from_4p2 @pytest.mark.with_wordpress_archive_from_11p2
@pytest.mark.with_custom_domain("yolo.test") @pytest.mark.with_custom_domain("yolo.test")
def test_restore_app_already_installed(mocker): def test_restore_app_already_installed(mocker):
assert not _is_installed("wordpress") assert not _is_installed("wordpress")
with message(mocker, "restore_complete"): with message("restore_complete"):
backup_restore( backup_restore(
system=None, name=backup_list()["archives"][0], apps=["wordpress"] system=None, name=backup_list()["archives"][0], apps=["wordpress"]
) )
@ -525,22 +524,22 @@ def test_restore_app_already_installed(mocker):
@pytest.mark.with_legacy_app_installed @pytest.mark.with_legacy_app_installed
def test_backup_and_restore_legacy_app(mocker): def test_backup_and_restore_legacy_app():
_test_backup_and_restore_app(mocker, "legacy_app") _test_backup_and_restore_app("legacy_app")
@pytest.mark.with_backup_recommended_app_installed @pytest.mark.with_backup_recommended_app_installed
def test_backup_and_restore_recommended_app(mocker): def test_backup_and_restore_recommended_app():
_test_backup_and_restore_app(mocker, "backup_recommended_app") _test_backup_and_restore_app("backup_recommended_app")
@pytest.mark.with_backup_recommended_app_installed_with_ynh_restore @pytest.mark.with_backup_recommended_app_installed_with_ynh_restore
def test_backup_and_restore_with_ynh_restore(mocker): def test_backup_and_restore_with_ynh_restore():
_test_backup_and_restore_app(mocker, "backup_recommended_app") _test_backup_and_restore_app("backup_recommended_app")
@pytest.mark.with_permission_app_installed @pytest.mark.with_permission_app_installed
def test_backup_and_restore_permission_app(mocker): def test_backup_and_restore_permission_app():
res = user_permission_list(full=True)["permissions"] res = user_permission_list(full=True)["permissions"]
assert "permissions_app.main" in res assert "permissions_app.main" in res
assert "permissions_app.admin" in res assert "permissions_app.admin" in res
@ -554,7 +553,7 @@ def test_backup_and_restore_permission_app(mocker):
assert res["permissions_app.admin"]["allowed"] == ["alice"] assert res["permissions_app.admin"]["allowed"] == ["alice"]
assert res["permissions_app.dev"]["allowed"] == [] assert res["permissions_app.dev"]["allowed"] == []
_test_backup_and_restore_app(mocker, "permissions_app") _test_backup_and_restore_app("permissions_app")
res = user_permission_list(full=True)["permissions"] res = user_permission_list(full=True)["permissions"]
assert "permissions_app.main" in res assert "permissions_app.main" in res
@ -570,10 +569,10 @@ def test_backup_and_restore_permission_app(mocker):
assert res["permissions_app.dev"]["allowed"] == [] assert res["permissions_app.dev"]["allowed"] == []
def _test_backup_and_restore_app(mocker, app): def _test_backup_and_restore_app(app):
# Create a backup of this app # Create a backup of this app
name = random_ascii(8) name = random_ascii(8)
with message(mocker, "backup_created", name=name): with message("backup_created", name=name):
backup_create(name=name, system=None, apps=[app]) backup_create(name=name, system=None, apps=[app])
archives = backup_list()["archives"] archives = backup_list()["archives"]
@ -590,7 +589,7 @@ def _test_backup_and_restore_app(mocker, app):
assert app + ".main" not in user_permission_list()["permissions"] assert app + ".main" not in user_permission_list()["permissions"]
# Restore the app # Restore the app
with message(mocker, "restore_complete"): with message("restore_complete"):
backup_restore(system=None, name=archives[0], apps=[app]) backup_restore(system=None, name=archives[0], apps=[app])
assert app_is_installed(app) assert app_is_installed(app)
@ -616,34 +615,34 @@ def test_restore_archive_with_no_json(mocker):
backup_restore(name="badbackup", force=True) backup_restore(name="badbackup", force=True)
@pytest.mark.with_wordpress_archive_from_4p2 @pytest.mark.with_wordpress_archive_from_11p2
def test_restore_archive_with_bad_archive(mocker): def test_restore_archive_with_bad_archive(mocker):
# Break the archive # Break the archive
os.system( os.system(
"head -n 1000 /home/yunohost.backup/archives/backup_wordpress_from_4p2.tar > /home/yunohost.backup/archives/backup_wordpress_from_4p2_bad.tar" "head -n 1000 /home/yunohost.backup/archives/backup_wordpress_from_11p2.tar > /home/yunohost.backup/archives/backup_wordpress_from_11p2_bad.tar"
) )
assert "backup_wordpress_from_4p2_bad" in backup_list()["archives"] assert "backup_wordpress_from_11p2_bad" in backup_list()["archives"]
with raiseYunohostError(mocker, "backup_archive_corrupted"): with raiseYunohostError(mocker, "backup_archive_corrupted"):
backup_restore(name="backup_wordpress_from_4p2_bad", force=True) backup_restore(name="backup_wordpress_from_11p2_bad", force=True)
clean_tmp_backup_directory() clean_tmp_backup_directory()
def test_restore_archive_with_custom_hook(mocker): def test_restore_archive_with_custom_hook():
custom_restore_hook_folder = os.path.join(CUSTOM_HOOK_FOLDER, "restore") custom_restore_hook_folder = os.path.join(CUSTOM_HOOK_FOLDER, "restore")
os.system("touch %s/99-yolo" % custom_restore_hook_folder) os.system("touch %s/99-yolo" % custom_restore_hook_folder)
# Backup with custom hook system # Backup with custom hook system
name = random_ascii(8) name = random_ascii(8)
with message(mocker, "backup_created", name=name): with message("backup_created", name=name):
backup_create(name=name, system=[], apps=None) backup_create(name=name, system=[], apps=None)
archives = backup_list()["archives"] archives = backup_list()["archives"]
assert len(archives) == 1 assert len(archives) == 1
# Restore system with custom hook # Restore system with custom hook
with message(mocker, "restore_complete"): with message("restore_complete"):
backup_restore( backup_restore(
name=backup_list()["archives"][0], system=[], apps=None, force=True name=backup_list()["archives"][0], system=[], apps=None, force=True
) )
@ -651,7 +650,7 @@ def test_restore_archive_with_custom_hook(mocker):
os.system("rm %s/99-yolo" % custom_restore_hook_folder) os.system("rm %s/99-yolo" % custom_restore_hook_folder)
def test_backup_binds_are_readonly(mocker, monkeypatch): def test_backup_binds_are_readonly(monkeypatch):
def custom_mount_and_backup(self): def custom_mount_and_backup(self):
self._organize_files() self._organize_files()
@ -676,5 +675,5 @@ def test_backup_binds_are_readonly(mocker, monkeypatch):
# Create the backup # Create the backup
name = random_ascii(8) name = random_ascii(8)
with message(mocker, "backup_created", name=name): with message("backup_created", name=name):
backup_create(name=name, system=[]) backup_create(name=name, system=[])

View file

@ -39,7 +39,7 @@ def check_changeurl_app(path):
assert appmap[maindomain][path]["id"] == "change_url_app" assert appmap[maindomain][path]["id"] == "change_url_app"
r = requests.get( r = requests.get(
"https://127.0.0.1%s/" % path, headers={"domain": maindomain}, verify=False "https://127.0.0.1%s/" % path, headers={"Host": maindomain}, verify=False
) )
assert r.status_code == 200 assert r.status_code == 200

View file

@ -59,7 +59,7 @@ def test_authenticate_with_wrong_password():
assert expected_msg in str(exception) assert expected_msg in str(exception)
def test_authenticate_server_down(mocker): def test_authenticate_server_down():
os.system("systemctl stop slapd && sleep 5") os.system("systemctl stop slapd && sleep 5")
LDAPAuth().authenticate_credentials(credentials="alice:Yunohost") LDAPAuth().authenticate_credentials(credentials="alice:Yunohost")

View file

@ -435,8 +435,8 @@ def test_permission_list():
# #
def test_permission_create_main(mocker): def test_permission_create_main():
with message(mocker, "permission_created", permission="site.main"): with message("permission_created", permission="site.main"):
permission_create("site.main", allowed=["all_users"], protected=False) permission_create("site.main", allowed=["all_users"], protected=False)
res = user_permission_list(full=True)["permissions"] res = user_permission_list(full=True)["permissions"]
@ -446,8 +446,8 @@ def test_permission_create_main(mocker):
assert res["site.main"]["protected"] is False assert res["site.main"]["protected"] is False
def test_permission_create_extra(mocker): def test_permission_create_extra():
with message(mocker, "permission_created", permission="site.test"): with message("permission_created", permission="site.test"):
permission_create("site.test") permission_create("site.test")
res = user_permission_list(full=True)["permissions"] res = user_permission_list(full=True)["permissions"]
@ -466,8 +466,8 @@ def test_permission_create_with_specific_user():
assert res["site.test"]["allowed"] == ["alice"] assert res["site.test"]["allowed"] == ["alice"]
def test_permission_create_with_tile_management(mocker): def test_permission_create_with_tile_management():
with message(mocker, "permission_created", permission="site.main"): with message("permission_created", permission="site.main"):
_permission_create_with_dummy_app( _permission_create_with_dummy_app(
"site.main", "site.main",
allowed=["all_users"], allowed=["all_users"],
@ -483,8 +483,8 @@ def test_permission_create_with_tile_management(mocker):
assert res["site.main"]["show_tile"] is False assert res["site.main"]["show_tile"] is False
def test_permission_create_with_tile_management_with_main_default_value(mocker): def test_permission_create_with_tile_management_with_main_default_value():
with message(mocker, "permission_created", permission="site.main"): with message("permission_created", permission="site.main"):
_permission_create_with_dummy_app( _permission_create_with_dummy_app(
"site.main", "site.main",
allowed=["all_users"], allowed=["all_users"],
@ -500,8 +500,8 @@ def test_permission_create_with_tile_management_with_main_default_value(mocker):
assert res["site.main"]["show_tile"] is True assert res["site.main"]["show_tile"] is True
def test_permission_create_with_tile_management_with_not_main_default_value(mocker): def test_permission_create_with_tile_management_with_not_main_default_value():
with message(mocker, "permission_created", permission="wiki.api"): with message("permission_created", permission="wiki.api"):
_permission_create_with_dummy_app( _permission_create_with_dummy_app(
"wiki.api", "wiki.api",
allowed=["all_users"], allowed=["all_users"],
@ -517,8 +517,8 @@ def test_permission_create_with_tile_management_with_not_main_default_value(mock
assert res["wiki.api"]["show_tile"] is True assert res["wiki.api"]["show_tile"] is True
def test_permission_create_with_urls_management_without_url(mocker): def test_permission_create_with_urls_management_without_url():
with message(mocker, "permission_created", permission="wiki.api"): with message("permission_created", permission="wiki.api"):
_permission_create_with_dummy_app( _permission_create_with_dummy_app(
"wiki.api", allowed=["all_users"], domain=maindomain, path="/site" "wiki.api", allowed=["all_users"], domain=maindomain, path="/site"
) )
@ -530,8 +530,8 @@ def test_permission_create_with_urls_management_without_url(mocker):
assert res["wiki.api"]["auth_header"] is True assert res["wiki.api"]["auth_header"] is True
def test_permission_create_with_urls_management_simple_domain(mocker): def test_permission_create_with_urls_management_simple_domain():
with message(mocker, "permission_created", permission="site.main"): with message("permission_created", permission="site.main"):
_permission_create_with_dummy_app( _permission_create_with_dummy_app(
"site.main", "site.main",
allowed=["all_users"], allowed=["all_users"],
@ -553,8 +553,8 @@ def test_permission_create_with_urls_management_simple_domain(mocker):
@pytest.mark.other_domains(number=2) @pytest.mark.other_domains(number=2)
def test_permission_create_with_urls_management_multiple_domain(mocker): def test_permission_create_with_urls_management_multiple_domain():
with message(mocker, "permission_created", permission="site.main"): with message("permission_created", permission="site.main"):
_permission_create_with_dummy_app( _permission_create_with_dummy_app(
"site.main", "site.main",
allowed=["all_users"], allowed=["all_users"],
@ -575,14 +575,14 @@ def test_permission_create_with_urls_management_multiple_domain(mocker):
assert res["site.main"]["auth_header"] is True assert res["site.main"]["auth_header"] is True
def test_permission_delete(mocker): def test_permission_delete():
with message(mocker, "permission_deleted", permission="wiki.main"): with message("permission_deleted", permission="wiki.main"):
permission_delete("wiki.main", force=True) permission_delete("wiki.main", force=True)
res = user_permission_list()["permissions"] res = user_permission_list()["permissions"]
assert "wiki.main" not in res assert "wiki.main" not in res
with message(mocker, "permission_deleted", permission="blog.api"): with message("permission_deleted", permission="blog.api"):
permission_delete("blog.api", force=False) permission_delete("blog.api", force=False)
res = user_permission_list()["permissions"] res = user_permission_list()["permissions"]
@ -625,8 +625,8 @@ def test_permission_delete_main_without_force(mocker):
# user side functions # user side functions
def test_permission_add_group(mocker): def test_permission_add_group():
with message(mocker, "permission_updated", permission="wiki.main"): with message("permission_updated", permission="wiki.main"):
user_permission_update("wiki.main", add="alice") user_permission_update("wiki.main", add="alice")
res = user_permission_list(full=True)["permissions"] res = user_permission_list(full=True)["permissions"]
@ -634,8 +634,8 @@ def test_permission_add_group(mocker):
assert set(res["wiki.main"]["corresponding_users"]) == {"alice", "bob"} assert set(res["wiki.main"]["corresponding_users"]) == {"alice", "bob"}
def test_permission_remove_group(mocker): def test_permission_remove_group():
with message(mocker, "permission_updated", permission="blog.main"): with message("permission_updated", permission="blog.main"):
user_permission_update("blog.main", remove="alice") user_permission_update("blog.main", remove="alice")
res = user_permission_list(full=True)["permissions"] res = user_permission_list(full=True)["permissions"]
@ -643,8 +643,8 @@ def test_permission_remove_group(mocker):
assert res["blog.main"]["corresponding_users"] == [] assert res["blog.main"]["corresponding_users"] == []
def test_permission_add_and_remove_group(mocker): def test_permission_add_and_remove_group():
with message(mocker, "permission_updated", permission="wiki.main"): with message("permission_updated", permission="wiki.main"):
user_permission_update("wiki.main", add="alice", remove="all_users") user_permission_update("wiki.main", add="alice", remove="all_users")
res = user_permission_list(full=True)["permissions"] res = user_permission_list(full=True)["permissions"]
@ -652,9 +652,9 @@ def test_permission_add_and_remove_group(mocker):
assert res["wiki.main"]["corresponding_users"] == ["alice"] assert res["wiki.main"]["corresponding_users"] == ["alice"]
def test_permission_add_group_already_allowed(mocker): def test_permission_add_group_already_allowed():
with message( with message(
mocker, "permission_already_allowed", permission="blog.main", group="alice" "permission_already_allowed", permission="blog.main", group="alice"
): ):
user_permission_update("blog.main", add="alice") user_permission_update("blog.main", add="alice")
@ -663,9 +663,9 @@ def test_permission_add_group_already_allowed(mocker):
assert res["blog.main"]["corresponding_users"] == ["alice"] assert res["blog.main"]["corresponding_users"] == ["alice"]
def test_permission_remove_group_already_not_allowed(mocker): def test_permission_remove_group_already_not_allowed():
with message( with message(
mocker, "permission_already_disallowed", permission="blog.main", group="bob" "permission_already_disallowed", permission="blog.main", group="bob"
): ):
user_permission_update("blog.main", remove="bob") user_permission_update("blog.main", remove="bob")
@ -674,8 +674,8 @@ def test_permission_remove_group_already_not_allowed(mocker):
assert res["blog.main"]["corresponding_users"] == ["alice"] assert res["blog.main"]["corresponding_users"] == ["alice"]
def test_permission_reset(mocker): def test_permission_reset():
with message(mocker, "permission_updated", permission="blog.main"): with message("permission_updated", permission="blog.main"):
user_permission_reset("blog.main") user_permission_reset("blog.main")
res = user_permission_list(full=True)["permissions"] res = user_permission_list(full=True)["permissions"]
@ -693,42 +693,42 @@ def test_permission_reset_idempotency():
assert set(res["blog.main"]["corresponding_users"]) == {"alice", "bob"} assert set(res["blog.main"]["corresponding_users"]) == {"alice", "bob"}
def test_permission_change_label(mocker): def test_permission_change_label():
with message(mocker, "permission_updated", permission="wiki.main"): with message("permission_updated", permission="wiki.main"):
user_permission_update("wiki.main", label="New Wiki") user_permission_update("wiki.main", label="New Wiki")
res = user_permission_list(full=True)["permissions"] res = user_permission_list(full=True)["permissions"]
assert res["wiki.main"]["label"] == "New Wiki" assert res["wiki.main"]["label"] == "New Wiki"
def test_permission_change_label_with_same_value(mocker): def test_permission_change_label_with_same_value():
with message(mocker, "permission_updated", permission="wiki.main"): with message("permission_updated", permission="wiki.main"):
user_permission_update("wiki.main", label="Wiki") user_permission_update("wiki.main", label="Wiki")
res = user_permission_list(full=True)["permissions"] res = user_permission_list(full=True)["permissions"]
assert res["wiki.main"]["label"] == "Wiki" assert res["wiki.main"]["label"] == "Wiki"
def test_permission_switch_show_tile(mocker): def test_permission_switch_show_tile():
# Note that from the actionmap the value is passed as string, not as bool # Note that from the actionmap the value is passed as string, not as bool
# Try with lowercase # Try with lowercase
with message(mocker, "permission_updated", permission="wiki.main"): with message("permission_updated", permission="wiki.main"):
user_permission_update("wiki.main", show_tile="false") user_permission_update("wiki.main", show_tile="false")
res = user_permission_list(full=True)["permissions"] res = user_permission_list(full=True)["permissions"]
assert res["wiki.main"]["show_tile"] is False assert res["wiki.main"]["show_tile"] is False
# Try with uppercase # Try with uppercase
with message(mocker, "permission_updated", permission="wiki.main"): with message("permission_updated", permission="wiki.main"):
user_permission_update("wiki.main", show_tile="TRUE") user_permission_update("wiki.main", show_tile="TRUE")
res = user_permission_list(full=True)["permissions"] res = user_permission_list(full=True)["permissions"]
assert res["wiki.main"]["show_tile"] is True assert res["wiki.main"]["show_tile"] is True
def test_permission_switch_show_tile_with_same_value(mocker): def test_permission_switch_show_tile_with_same_value():
# Note that from the actionmap the value is passed as string, not as bool # Note that from the actionmap the value is passed as string, not as bool
with message(mocker, "permission_updated", permission="wiki.main"): with message("permission_updated", permission="wiki.main"):
user_permission_update("wiki.main", show_tile="True") user_permission_update("wiki.main", show_tile="True")
res = user_permission_list(full=True)["permissions"] res = user_permission_list(full=True)["permissions"]
@ -806,7 +806,7 @@ def test_permission_main_url_regex():
def test_permission_main_url_bad_regex(mocker): def test_permission_main_url_bad_regex(mocker):
with raiseYunohostError(mocker, "invalid_regex"): with raiseYunohostError(mocker, "invalid_regex"):
permission_url("blog.main", url="re:/[a-z]++reboy/.*") permission_url("blog.main", url="re:/[a-z]+++reboy/.*")
@pytest.mark.other_domains(number=1) @pytest.mark.other_domains(number=1)
@ -837,7 +837,7 @@ def test_permission_add_additional_regex():
def test_permission_add_additional_bad_regex(mocker): def test_permission_add_additional_bad_regex(mocker):
with raiseYunohostError(mocker, "invalid_regex"): with raiseYunohostError(mocker, "invalid_regex"):
permission_url("blog.main", add_url=["re:/[a-z]++reboy/.*"]) permission_url("blog.main", add_url=["re:/[a-z]+++reboy/.*"])
def test_permission_remove_additional_url(): def test_permission_remove_additional_url():
@ -1131,7 +1131,7 @@ def test_permission_app_propagation_on_ssowat():
def test_permission_legacy_app_propagation_on_ssowat(): def test_permission_legacy_app_propagation_on_ssowat():
app_install( app_install(
os.path.join(get_test_apps_dir(), "legacy_app_ynh"), os.path.join(get_test_apps_dir(), "legacy_app_ynh"),
args="domain=%s&domain_2=%s&path=%s&is_public=1" args="domain=%s&domain_2=%s&path=%s&is_public=0"
% (maindomain, other_domains[0], "/legacy"), % (maindomain, other_domains[0], "/legacy"),
force=True, force=True,
) )
@ -1139,12 +1139,12 @@ def test_permission_legacy_app_propagation_on_ssowat():
# App is configured as public by default using the legacy unprotected_uri mechanics # App is configured as public by default using the legacy unprotected_uri mechanics
# It should automatically be migrated during the install # It should automatically be migrated during the install
res = user_permission_list(full=True)["permissions"] res = user_permission_list(full=True)["permissions"]
assert "visitors" in res["legacy_app.main"]["allowed"] assert "visitors" not in res["legacy_app.main"]["allowed"]
assert "all_users" in res["legacy_app.main"]["allowed"] assert "all_users" in res["legacy_app.main"]["allowed"]
app_webroot = "https://%s/legacy" % maindomain app_webroot = "https://%s/legacy" % maindomain
assert can_access_webpage(app_webroot, logged_as=None) assert not can_access_webpage(app_webroot, logged_as=None)
assert can_access_webpage(app_webroot, logged_as="alice") assert can_access_webpage(app_webroot, logged_as="alice")
# Try to update the permission and check that permissions are still consistent # Try to update the permission and check that permissions are still consistent

View file

@ -87,7 +87,7 @@ def test_ssh_conf_unmanaged():
assert SSHD_CONFIG in _get_conf_hashes("ssh") assert SSHD_CONFIG in _get_conf_hashes("ssh")
def test_ssh_conf_unmanaged_and_manually_modified(mocker): def test_ssh_conf_unmanaged_and_manually_modified():
_force_clear_hashes([SSHD_CONFIG]) _force_clear_hashes([SSHD_CONFIG])
os.system("echo ' ' >> %s" % SSHD_CONFIG) os.system("echo ' ' >> %s" % SSHD_CONFIG)
@ -98,7 +98,7 @@ def test_ssh_conf_unmanaged_and_manually_modified(mocker):
assert SSHD_CONFIG in _get_conf_hashes("ssh") assert SSHD_CONFIG in _get_conf_hashes("ssh")
assert SSHD_CONFIG in manually_modified_files() assert SSHD_CONFIG in manually_modified_files()
with message(mocker, "regenconf_need_to_explicitly_specify_ssh"): with message("regenconf_need_to_explicitly_specify_ssh"):
regen_conf(force=True) regen_conf(force=True)
assert SSHD_CONFIG in _get_conf_hashes("ssh") assert SSHD_CONFIG in _get_conf_hashes("ssh")

View file

@ -91,8 +91,8 @@ def test_list_groups():
# #
def test_create_user(mocker): def test_create_user():
with message(mocker, "user_created"): with message("user_created"):
user_create("albert", maindomain, "test123Ynh", fullname="Albert Good") user_create("albert", maindomain, "test123Ynh", fullname="Albert Good")
group_res = user_group_list()["groups"] group_res = user_group_list()["groups"]
@ -102,8 +102,8 @@ def test_create_user(mocker):
assert "albert" in group_res["all_users"]["members"] assert "albert" in group_res["all_users"]["members"]
def test_del_user(mocker): def test_del_user():
with message(mocker, "user_deleted"): with message("user_deleted"):
user_delete("alice") user_delete("alice")
group_res = user_group_list()["groups"] group_res = user_group_list()["groups"]
@ -112,7 +112,7 @@ def test_del_user(mocker):
assert "alice" not in group_res["all_users"]["members"] assert "alice" not in group_res["all_users"]["members"]
def test_import_user(mocker): def test_import_user():
import csv import csv
from io import StringIO from io import StringIO
@ -157,7 +157,7 @@ def test_import_user(mocker):
} }
) )
csv_io.seek(0) csv_io.seek(0)
with message(mocker, "user_import_success"): with message("user_import_success"):
user_import(csv_io, update=True, delete=True) user_import(csv_io, update=True, delete=True)
group_res = user_group_list()["groups"] group_res = user_group_list()["groups"]
@ -171,7 +171,7 @@ def test_import_user(mocker):
assert "alice" not in group_res["dev"]["members"] assert "alice" not in group_res["dev"]["members"]
def test_export_user(mocker): def test_export_user():
result = user_export() result = user_export()
should_be = ( should_be = (
"username;firstname;lastname;password;mail;mail-alias;mail-forward;mailbox-quota;groups\r\n" "username;firstname;lastname;password;mail;mail-alias;mail-forward;mailbox-quota;groups\r\n"
@ -182,8 +182,8 @@ def test_export_user(mocker):
assert result == should_be assert result == should_be
def test_create_group(mocker): def test_create_group():
with message(mocker, "group_created", group="adminsys"): with message("group_created", group="adminsys"):
user_group_create("adminsys") user_group_create("adminsys")
group_res = user_group_list()["groups"] group_res = user_group_list()["groups"]
@ -192,8 +192,8 @@ def test_create_group(mocker):
assert group_res["adminsys"]["members"] == [] assert group_res["adminsys"]["members"] == []
def test_del_group(mocker): def test_del_group():
with message(mocker, "group_deleted", group="dev"): with message("group_deleted", group="dev"):
user_group_delete("dev") user_group_delete("dev")
group_res = user_group_list()["groups"] group_res = user_group_list()["groups"]
@ -262,46 +262,40 @@ def test_del_group_that_does_not_exist(mocker):
# #
def test_update_user(mocker): def test_update_user():
with message(mocker, "user_updated"): with message("user_updated"):
user_update("alice", firstname="NewName", lastname="NewLast")
info = user_info("alice")
assert info["fullname"] == "NewName NewLast"
with message(mocker, "user_updated"):
user_update("alice", fullname="New2Name New2Last") user_update("alice", fullname="New2Name New2Last")
info = user_info("alice") info = user_info("alice")
assert info["fullname"] == "New2Name New2Last" assert info["fullname"] == "New2Name New2Last"
def test_update_group_add_user(mocker): def test_update_group_add_user():
with message(mocker, "group_updated", group="dev"): with message("group_updated", group="dev"):
user_group_update("dev", add=["bob"]) user_group_update("dev", add=["bob"])
group_res = user_group_list()["groups"] group_res = user_group_list()["groups"]
assert set(group_res["dev"]["members"]) == {"alice", "bob"} assert set(group_res["dev"]["members"]) == {"alice", "bob"}
def test_update_group_add_user_already_in(mocker): def test_update_group_add_user_already_in():
with message(mocker, "group_user_already_in_group", user="bob", group="apps"): with message("group_user_already_in_group", user="bob", group="apps"):
user_group_update("apps", add=["bob"]) user_group_update("apps", add=["bob"])
group_res = user_group_list()["groups"] group_res = user_group_list()["groups"]
assert group_res["apps"]["members"] == ["bob"] assert group_res["apps"]["members"] == ["bob"]
def test_update_group_remove_user(mocker): def test_update_group_remove_user():
with message(mocker, "group_updated", group="apps"): with message("group_updated", group="apps"):
user_group_update("apps", remove=["bob"]) user_group_update("apps", remove=["bob"])
group_res = user_group_list()["groups"] group_res = user_group_list()["groups"]
assert group_res["apps"]["members"] == [] assert group_res["apps"]["members"] == []
def test_update_group_remove_user_not_already_in(mocker): def test_update_group_remove_user_not_already_in():
with message(mocker, "group_user_not_in_group", user="jack", group="apps"): with message("group_user_not_in_group", user="jack", group="apps"):
user_group_update("apps", remove=["jack"]) user_group_update("apps", remove=["jack"])
group_res = user_group_list()["groups"] group_res = user_group_list()["groups"]
@ -315,7 +309,7 @@ def test_update_group_remove_user_not_already_in(mocker):
def test_update_user_that_doesnt_exist(mocker): def test_update_user_that_doesnt_exist(mocker):
with raiseYunohostError(mocker, "user_unknown"): with raiseYunohostError(mocker, "user_unknown"):
user_update("doesnt_exist", firstname="NewName", lastname="NewLast") user_update("doesnt_exist", fullname="Foo Bar")
def test_update_group_that_doesnt_exist(mocker): def test_update_group_that_doesnt_exist(mocker):

View file

@ -24,9 +24,9 @@ import time
from importlib import import_module from importlib import import_module
from packaging import version from packaging import version
from typing import List from typing import List
from logging import getLogger
from moulinette import Moulinette, m18n from moulinette import Moulinette, m18n
from moulinette.utils.log import getActionLogger
from moulinette.utils.process import call_async_output from moulinette.utils.process import call_async_output
from moulinette.utils.filesystem import read_yaml, write_to_yaml, cp, mkdir, rm, chown from moulinette.utils.filesystem import read_yaml, write_to_yaml, cp, mkdir, rm, chown
@ -55,7 +55,7 @@ from yunohost.log import is_unit_operation, OperationLogger
MIGRATIONS_STATE_PATH = "/etc/yunohost/migrations.yaml" MIGRATIONS_STATE_PATH = "/etc/yunohost/migrations.yaml"
logger = getActionLogger("yunohost.tools") logger = getLogger("yunohost.tools")
def tools_versions(): def tools_versions():

View file

@ -25,9 +25,9 @@ import random
import string import string
import subprocess import subprocess
import copy import copy
from logging import getLogger
from moulinette import Moulinette, m18n from moulinette import Moulinette, m18n
from moulinette.utils.log import getActionLogger
from moulinette.utils.process import check_output from moulinette.utils.process import check_output
from yunohost.utils.error import YunohostError, YunohostValidationError from yunohost.utils.error import YunohostError, YunohostValidationError
@ -35,7 +35,7 @@ from yunohost.service import service_status
from yunohost.log import is_unit_operation from yunohost.log import is_unit_operation
from yunohost.utils.system import binary_to_human from yunohost.utils.system import binary_to_human
logger = getActionLogger("yunohost.user") logger = getLogger("yunohost.user")
FIELDS_FOR_IMPORT = { FIELDS_FOR_IMPORT = {
"username": r"^[a-z0-9_]+$", "username": r"^[a-z0-9_]+$",
@ -141,33 +141,20 @@ def user_create(
domain, domain,
password, password,
fullname=None, fullname=None,
firstname=None,
lastname=None,
mailbox_quota="0", mailbox_quota="0",
admin=False, admin=False,
from_import=False, from_import=False,
loginShell=None, loginShell=None,
): ):
if firstname or lastname:
logger.warning(
"Options --firstname / --lastname of 'yunohost user create' are deprecated. We recommend using --fullname instead."
)
if not fullname or not fullname.strip(): if not fullname or not fullname.strip():
if not firstname.strip(): raise YunohostValidationError(
raise YunohostValidationError( "You should specify the fullname of the user using option -F"
"You should specify the fullname of the user using option -F" )
) fullname = fullname.strip()
lastname = ( firstname = fullname.split()[0]
lastname or " " lastname = (
) # Stupid hack because LDAP requires the sn/lastname attr, but it accepts a single whitespace... " ".join(fullname.split()[1:]) or " "
fullname = f"{firstname} {lastname}".strip() ) # Stupid hack because LDAP requires the sn/lastname attr, but it accepts a single whitespace...
else:
fullname = fullname.strip()
firstname = fullname.split()[0]
lastname = (
" ".join(fullname.split()[1:]) or " "
) # Stupid hack because LDAP requires the sn/lastname attr, but it accepts a single whitespace...
from yunohost.domain import domain_list, _get_maindomain, _assert_domain_exists from yunohost.domain import domain_list, _get_maindomain, _assert_domain_exists
from yunohost.hook import hook_callback from yunohost.hook import hook_callback
@ -364,8 +351,6 @@ def user_delete(operation_logger, username, purge=False, from_import=False):
def user_update( def user_update(
operation_logger, operation_logger,
username, username,
firstname=None,
lastname=None,
mail=None, mail=None,
change_password=None, change_password=None,
add_mailforward=None, add_mailforward=None,
@ -377,17 +362,15 @@ def user_update(
fullname=None, fullname=None,
loginShell=None, loginShell=None,
): ):
if firstname or lastname:
logger.warning(
"Options --firstname / --lastname of 'yunohost user create' are deprecated. We recommend using --fullname instead."
)
if fullname and fullname.strip(): if fullname and fullname.strip():
fullname = fullname.strip() fullname = fullname.strip()
firstname = fullname.split()[0] firstname = fullname.split()[0]
lastname = ( lastname = (
" ".join(fullname.split()[1:]) or " " " ".join(fullname.split()[1:]) or " "
) # Stupid hack because LDAP requires the sn/lastname attr, but it accepts a single whitespace... ) # Stupid hack because LDAP requires the sn/lastname attr, but it accepts a single whitespace...
else:
firstname = None
lastname = None
from yunohost.domain import domain_list from yunohost.domain import domain_list
from yunohost.app import app_ssowatconf from yunohost.app import app_ssowatconf
@ -884,8 +867,7 @@ def user_import(operation_logger, csvfile, update=False, delete=False):
user_update( user_update(
new_infos["username"], new_infos["username"],
firstname=new_infos["firstname"], fullname=(new_infos["firstname"] + " " + new_infos["lastname"]).strip(),
lastname=new_infos["lastname"],
change_password=new_infos["password"], change_password=new_infos["password"],
mailbox_quota=new_infos["mailbox-quota"], mailbox_quota=new_infos["mailbox-quota"],
mail=new_infos["mail"], mail=new_infos["mail"],
@ -930,8 +912,7 @@ def user_import(operation_logger, csvfile, update=False, delete=False):
user["password"], user["password"],
user["mailbox-quota"], user["mailbox-quota"],
from_import=True, from_import=True,
firstname=user["firstname"], fullname=(user["firstname"] + " " + user["lastname"]).strip(),
lastname=user["lastname"],
) )
update(user) update(user)
result["created"] += 1 result["created"] += 1

View file

@ -22,11 +22,11 @@ import re
import urllib.parse import urllib.parse
from collections import OrderedDict from collections import OrderedDict
from typing import Union from typing import Union
from logging import getLogger
from moulinette import Moulinette, m18n from moulinette import Moulinette, m18n
from moulinette.interfaces.cli import colorize from moulinette.interfaces.cli import colorize
from moulinette.utils.filesystem import mkdir, read_toml, read_yaml, write_to_yaml from moulinette.utils.filesystem import mkdir, read_toml, read_yaml, write_to_yaml
from moulinette.utils.log import getActionLogger
from yunohost.utils.error import YunohostError, YunohostValidationError from yunohost.utils.error import YunohostError, YunohostValidationError
from yunohost.utils.form import ( from yunohost.utils.form import (
OPTIONS, OPTIONS,
@ -40,7 +40,7 @@ from yunohost.utils.form import (
) )
from yunohost.utils.i18n import _value_for_locale from yunohost.utils.i18n import _value_for_locale
logger = getActionLogger("yunohost.configpanel") logger = getLogger("yunohost.configpanel")
CONFIG_PANEL_VERSION_SUPPORTED = 1.0 CONFIG_PANEL_VERSION_SUPPORTED = 1.0
@ -160,11 +160,15 @@ class ConfigPanel:
result[key] = {"ask": ask} result[key] = {"ask": ask}
if "current_value" in option: if "current_value" in option:
question_class = OPTIONS[option.get("type", OptionType.string)] question_class = OPTIONS[option.get("type", OptionType.string)]
result[key]["value"] = question_class.humanize( if hasattr(question_class, "humanize"):
option["current_value"], option result[key]["value"] = question_class.humanize(
) option["current_value"], option
)
else:
result[key]["value"] = option["current_value"]
# FIXME: semantics, technically here this is not about a prompt... # FIXME: semantics, technically here this is not about a prompt...
if question_class.hide_user_input_in_prompt: if getattr(question_class, "hide_user_input_in_prompt", None):
result[key][ result[key][
"value" "value"
] = "**************" # Prevent displaying password in `config get` ] = "**************" # Prevent displaying password in `config get`
@ -610,7 +614,7 @@ class ConfigPanel:
{ {
question.id: question.value question.id: question.value
for question in questions for question in questions
if question.value is not None if not question.readonly and question.value is not None
} }
) )

View file

@ -25,16 +25,16 @@ import tempfile
import urllib.parse import urllib.parse
from enum import Enum from enum import Enum
from typing import Any, Callable, Dict, List, Literal, Mapping, Optional, Union from typing import Any, Callable, Dict, List, Literal, Mapping, Optional, Union
from logging import getLogger
from moulinette import Moulinette, m18n from moulinette import Moulinette, m18n
from moulinette.interfaces.cli import colorize from moulinette.interfaces.cli import colorize
from moulinette.utils.filesystem import read_file, write_to_file from moulinette.utils.filesystem import read_file, write_to_file
from moulinette.utils.log import getActionLogger
from yunohost.log import OperationLogger from yunohost.log import OperationLogger
from yunohost.utils.error import YunohostError, YunohostValidationError from yunohost.utils.error import YunohostError, YunohostValidationError
from yunohost.utils.i18n import _value_for_locale from yunohost.utils.i18n import _value_for_locale
logger = getActionLogger("yunohost.form") logger = getLogger("yunohost.form")
Context = dict[str, Any] Context = dict[str, Any]

View file

@ -19,8 +19,9 @@
import os import os
import re import re
import glob import glob
from logging import getLogger
from moulinette.core import MoulinetteError from moulinette.core import MoulinetteError
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import ( from moulinette.utils.filesystem import (
read_file, read_file,
write_to_file, write_to_file,
@ -32,7 +33,7 @@ from moulinette.utils.filesystem import (
from yunohost.utils.error import YunohostValidationError from yunohost.utils.error import YunohostValidationError
logger = getActionLogger("yunohost.legacy") logger = getLogger("yunohost.utils.legacy")
LEGACY_PERMISSION_LABEL = { LEGACY_PERMISSION_LABEL = {
("nextcloud", "skipped"): "api", # .well-known ("nextcloud", "skipped"): "api", # .well-known
@ -163,32 +164,45 @@ def translate_legacy_default_app_in_ssowant_conf_json_persistent():
LEGACY_PHP_VERSION_REPLACEMENTS = [ LEGACY_PHP_VERSION_REPLACEMENTS = [
("/etc/php5", "/etc/php/7.4"), ("/etc/php5", "/etc/php/8.2"),
("/etc/php/7.0", "/etc/php/7.4"), ("/etc/php/7.0", "/etc/php/8.2"),
("/etc/php/7.3", "/etc/php/7.4"), ("/etc/php/7.3", "/etc/php/8.2"),
("/var/run/php5-fpm", "/var/run/php/php7.4-fpm"), ("/etc/php/7.4", "/etc/php/8.2"),
("/var/run/php/php7.0-fpm", "/var/run/php/php7.4-fpm"), ("/var/run/php5-fpm", "/var/run/php/php8.2-fpm"),
("/var/run/php/php7.3-fpm", "/var/run/php/php7.4-fpm"), ("/var/run/php/php7.0-fpm", "/var/run/php/php8.2-fpm"),
("php5", "php7.4"), ("/var/run/php/php7.3-fpm", "/var/run/php/php8.2-fpm"),
("php7.0", "php7.4"), ("/var/run/php/php7.4-fpm", "/var/run/php/php8.2-fpm"),
("php7.3", "php7.4"), ("php5", "php8.2"),
('YNH_PHP_VERSION="7.3"', 'YNH_PHP_VERSION="7.4"'), ("php7.0", "php8.2"),
("php7.3", "php8.2"),
("php7.4", "php8.2"),
('YNH_PHP_VERSION="7.3"', 'YNH_PHP_VERSION="8.2"'),
('YNH_PHP_VERSION="7.4"', 'YNH_PHP_VERSION="8.2"'),
( (
'phpversion="${phpversion:-7.0}"', 'phpversion="${phpversion:-7.0}"',
'phpversion="${phpversion:-7.4}"', 'phpversion="${phpversion:-8.2}"',
), # Many helpers like the composer ones use 7.0 by default ... ), # Many helpers like the composer ones use 7.0 by default ...
( (
'phpversion="${phpversion:-7.3}"', 'phpversion="${phpversion:-7.3}"',
'phpversion="${phpversion:-8.2}"',
), # Many helpers like the composer ones use 7.0 by default ...
(
'phpversion="${phpversion:-7.4}"', 'phpversion="${phpversion:-7.4}"',
'phpversion="${phpversion:-8.2}"',
), # Many helpers like the composer ones use 7.0 by default ... ), # Many helpers like the composer ones use 7.0 by default ...
( (
'"$phpversion" == "7.0"', '"$phpversion" == "7.0"',
'$(bc <<< "$phpversion >= 7.4") -eq 1', '$(bc <<< "$phpversion >= 8.2") -eq 1',
), # patch ynh_install_php to refuse installing/removing php <= 7.3 ), # patch ynh_install_php to refuse installing/removing php <= 7.3
( (
'"$phpversion" == "7.3"', '"$phpversion" == "7.3"',
'$(bc <<< "$phpversion >= 7.4") -eq 1', '$(bc <<< "$phpversion >= 8.2") -eq 1',
), # patch ynh_install_php to refuse installing/removing php <= 7.3 ), # patch ynh_install_php to refuse installing/removing php <= 7.3
(
'"$phpversion" == "7.4"',
'$(bc <<< "$phpversion >= 8.2") -eq 1',
), # patch ynh_install_php to refuse installing/removing php <= 7.3
] ]
@ -217,15 +231,16 @@ def _patch_legacy_php_versions(app_folder):
def _patch_legacy_php_versions_in_settings(app_folder): def _patch_legacy_php_versions_in_settings(app_folder):
settings = read_yaml(os.path.join(app_folder, "settings.yml")) settings = read_yaml(os.path.join(app_folder, "settings.yml"))
if settings.get("fpm_config_dir") in ["/etc/php/7.0/fpm", "/etc/php/7.3/fpm"]: if settings.get("fpm_config_dir") in ["/etc/php/7.0/fpm", "/etc/php/7.3/fpm", "/etc/php/7.4/fpm"]:
settings["fpm_config_dir"] = "/etc/php/7.4/fpm" settings["fpm_config_dir"] = "/etc/php/8.2/fpm"
if settings.get("fpm_service") in ["php7.0-fpm", "php7.3-fpm"]: if settings.get("fpm_service") in ["php7.0-fpm", "php7.3-fpm", "php7.4-fpm"]:
settings["fpm_service"] = "php7.4-fpm" settings["fpm_service"] = "php8.2-fpm"
if settings.get("phpversion") in ["7.0", "7.3"]: if settings.get("phpversion") in ["7.0", "7.3", "7.4"]:
settings["phpversion"] = "7.4" settings["phpversion"] = "8.2"
# We delete these checksums otherwise the file will appear as manually modified # We delete these checksums otherwise the file will appear as manually modified
list_to_remove = [ list_to_remove = [
"checksum__etc_php_7.4_fpm_pool",
"checksum__etc_php_7.3_fpm_pool", "checksum__etc_php_7.3_fpm_pool",
"checksum__etc_php_7.0_fpm_pool", "checksum__etc_php_7.0_fpm_pool",
"checksum__etc_nginx_conf.d", "checksum__etc_nginx_conf.d",

View file

@ -23,11 +23,11 @@ import random
import tempfile import tempfile
import subprocess import subprocess
from typing import Dict, Any, List, Union from typing import Dict, Any, List, Union
from logging import getLogger
from moulinette import m18n from moulinette import m18n
from moulinette.utils.text import random_ascii from moulinette.utils.text import random_ascii
from moulinette.utils.process import check_output from moulinette.utils.process import check_output
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import mkdir, chown, chmod, write_to_file from moulinette.utils.filesystem import mkdir, chown, chmod, write_to_file
from moulinette.utils.filesystem import ( from moulinette.utils.filesystem import (
rm, rm,
@ -35,7 +35,7 @@ from moulinette.utils.filesystem import (
from yunohost.utils.system import system_arch from yunohost.utils.system import system_arch
from yunohost.utils.error import YunohostError, YunohostValidationError from yunohost.utils.error import YunohostError, YunohostValidationError
logger = getActionLogger("yunohost.app_resources") logger = getLogger("yunohost.utils.resources")
class AppResourceManager: class AppResourceManager:
@ -1337,8 +1337,8 @@ class DatabaseAppResource(AppResource):
def provision_or_update(self, context: Dict = {}): def provision_or_update(self, context: Dict = {}):
# This is equivalent to ynh_sanitize_dbid # This is equivalent to ynh_sanitize_dbid
db_name = self.app.replace("-", "_").replace(".", "_") db_user = self.app.replace("-", "_").replace(".", "_")
db_user = db_name db_name = self.get_setting("db_name") or db_user
self.set_setting("db_name", db_name) self.set_setting("db_name", db_name)
self.set_setting("db_user", db_user) self.set_setting("db_user", db_user)
@ -1372,8 +1372,8 @@ class DatabaseAppResource(AppResource):
) )
def deprovision(self, context: Dict = {}): def deprovision(self, context: Dict = {}):
db_name = self.app.replace("-", "_").replace(".", "_") db_user = self.app.replace("-", "_").replace(".", "_")
db_user = db_name db_name = self.get_setting("db_name") or db_user
if self.dbtype == "mysql": if self.dbtype == "mysql":
self._run_script( self._run_script(

18
tox.ini
View file

@ -1,15 +1,15 @@
[tox] [tox]
envlist = py39-{lint,invalidcode},py39-black-{run,check} envlist = py311-{lint,invalidcode},py311-black-{run,check}
[testenv] [testenv]
skip_install=True skip_install=True
deps = deps =
py39-{lint,invalidcode}: flake8 py311-{lint,invalidcode}: flake8
py39-black-{run,check}: black py311-black-{run,check}: black
py39-mypy: mypy >= 0.900 py311-mypy: mypy >= 0.900
commands = commands =
py39-lint: flake8 src doc maintenance tests --ignore E402,E501,E203,W503,E741 --exclude src/tests,src/vendor py311-lint: flake8 src doc maintenance tests --ignore E402,E501,E203,W503,E741 --exclude src/tests,src/vendor
py39-invalidcode: flake8 src bin maintenance --exclude src/tests,src/vendor --select F,E722,W605 py311-invalidcode: flake8 src bin maintenance --exclude src/tests,src/vendor --select F,E722,W605
py39-black-check: black --check --diff bin src doc maintenance tests py311-black-check: black --check --diff bin src doc maintenance tests
py39-black-run: black bin src doc maintenance tests py311-black-run: black bin src doc maintenance tests
py39-mypy: mypy --ignore-missing-import --install-types --non-interactive --follow-imports silent src/ --exclude (acme_tiny|migrations) py311-mypy: mypy --ignore-missing-import --install-types --non-interactive --follow-imports silent src/ --exclude (acme_tiny|migrations)