Release v19.24 to unstable

-----BEGIN PGP SIGNATURE-----
 
 iQJKBAABCgA0FiEEfWrbdQ+RCFWJSEvmd8DHXntlCAgFAl4KsAYWHGp2YWxsZXJv
 eUBtYWlsYm94Lm9yZwAKCRB3wMdee2UICCKJD/9/dh/L75e42MfAFFr07PrjLWQt
 zQQPvHLYZysuBoVEJ2Jbgz5vY74Q/YQ4cWGyP0TJ4if5WRDcv6sdonqRnPbpRsyY
 Y61JaZwQs5brWz9t6jSlomqo/AQwtjCQaFGYqcpnCxbk3MXdeSwDjpC9NP36lhZo
 vYnmYTMcPruqQDwnBYQETQMYQ5Eb2Q1SzE/RWemcwSmQg21ZYTv2h3/S1q8zhPHq
 5Ysl81RLsdNow7nJG6BXexjFtR8E50tRGAJPgg+v9IutIs1KvnjpnMdC3Juzbsi1
 T0ZYh0XnQRiMp6U7buPHmKUTzeAoUd2qmX3ZJd0kV2jfINuoGD3Xbwdl2w0OQJLY
 eXTvG+tjAOfa/UjKlufvZGcTvDFVkrTKl9saIUg0EkaeZWtgJt+4XfvKLsx7lF7e
 SosAmFgZYPYVN5caJKx/R///okbkihL04cB1y1Pe1bHBt5mLJM4a2V94Dpy9cPEk
 1pclp9lPe5fiNQAUuOsqYE/DzrjMO2+5wA7XQypD4nV6TwzDKNHN1e6eMJVI1jaf
 Qq5isdqMlshp9IWlhxg6FYs5XAFZejIjU9EP2QsTtcRT7Y0DcRt91TdiBwRwYUIy
 1v0BhsfbUbkKWCOvq1mL6YrIS2/A5GHsYI+1cEit8mf5vdZSVDjOMs6pwXtmSrD7
 KezjorZHWyPI8gQMBQ==
 =uGVf
 -----END PGP SIGNATURE-----
gpgsig -----BEGIN PGP SIGNATURE-----
 
 iQJKBAABCgA0FiEEfWrbdQ+RCFWJSEvmd8DHXntlCAgFAl4OmGkWHGp2YWxsZXJv
 eUBtYWlsYm94Lm9yZwAKCRB3wMdee2UICJkNEACcr8CPofqc+2FQywqBN6EeYX8s
 W3Z867JtL0VNHnIYYvlZmaxEyhQP9cSZ6chrezayHTABLzKTIhNfd+vJUJlXcjyo
 ZZ7qKjMN14ZK9DE8WpWjwh09cyD068yBZI53YhdVExQu3Gt+fBMFW1a1OXJdSfEd
 FH+DMqpc2zJnKR9MJ0CH4+3A8BphV/MxDzzUYnpE0qK1zSQtYnARRGwLx9Fbcpx8
 yIAojkARz+PZ1vhQi9vaYbzyZX5my0b+0cCexb8bgSZul89kLSomQNPGTcyd9wPP
 +BE18uFXOA1DeJuD5Mfkm9pEh3tWwWuR+t8Fq/QP9OILVISryo0Ys8h29QjRPsnb
 jdCNiQka6KRrmqZL/+XUHs9A1aGsDZUXneqd5Ith4TgdHsaxaZRmD0wWuuljJE1n
 JIVULCvXgxbzIC3gu+mSDZq0dGHRG2u2b/B31+vMjKfQFPxoPyL86O2e37kCuiL0
 4sNmd0R2OOM1w+sLlh2/E9JSviWwmcn7budKEpNSQFphrkwoz7NEbDhaeZ1CWRVh
 0TbyPPw6iCVA6peHt1pJh6qO9kh3hV6QZCKqcvMpsoNCucpjEK9ZniPyKQpcLOhm
 qdhFZz6NIWZY20EenPZmBv667MSkrdrX7p7JD02w/M52oLCji6aPSm+Q5kNNqOhm
 BR29egi7Yz3h+bJYnw==
 =QTqM
 -----END PGP SIGNATURE-----

Merge tag 'v19.24' into debian/buster-backports

Release v19.24 to unstable

Signed-off-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
James Valleroy 2020-01-02 20:26:59 -05:00
commit 75755b5df3
241 changed files with 13917 additions and 9359 deletions

View File

@ -22,7 +22,7 @@ run-unit-tests:
- cp -r . /home/tester/plinth
- chown -R tester:tester /home/tester/plinth
- su -c "cd ~/plinth; python3 -m flake8 --exclude actions/domainname-change,actions/dynamicdns,actions/hostname-change,actions/networks plinth actions/*" tester
- su -c "cd ~/plinth; py.test-3 --cov=plinth --cov-report=html:/home/tester/plinth/htmlcov --cov-report=term" tester
- su -c "cd ~/plinth;PYTHONPATH='.' py.test-3 --cov=plinth --cov-report=html:/home/tester/plinth/htmlcov --cov-report=term" tester
- cp -r /home/tester/plinth/htmlcov test-coverage-report
coverage: '/^TOTAL\s+.*\s+(\d+\.\d+%)$/'

View File

@ -38,6 +38,20 @@ ExecStart=bash -c "/usr/bin/deluge-web --base=deluge $(/usr/bin/deluge-web --ver
Restart=on-failure
User=debian-deluged
Group=debian-deluged
LockPersonality=yes
NoNewPrivileges=yes
PrivateDevices=yes
PrivateTmp=yes
PrivateUsers=yes
ProtectControlGroups=yes
ProtectKernelLogs=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
ProtectSystem=yes
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
RestrictRealtime=yes
StateDirectory=deluged
SystemCallArchitectures=native
[Install]
WantedBy=multi-user.target

View File

@ -94,6 +94,24 @@ After=network.target
User=infinoted
Group=infinoted
ExecStart=/usr/bin/infinoted
ConfigurationDirectory=infinoted
ConfigurationDirectoryMode=0750
LockPersonality=yes
NoNewPrivileges=yes
PrivateDevices=yes
PrivateMounts=yes
PrivateTmp=yes
PrivateUsers=yes
ProtectControlGroups=yes
ProtectHome=yes
ProtectKernelLogs=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
ProtectSystem=full
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
RestrictRealtime=yes
StateDirectory=infinoted
SystemCallArchitectures=native
[Install]
WantedBy=multi-user.target
@ -130,11 +148,10 @@ def subcommand_setup(_):
with open(CONF_PATH, 'w') as file_handle:
file_handle.write(CONF)
if not os.path.isfile(SYSTEMD_SERVICE_PATH):
with open(SYSTEMD_SERVICE_PATH, 'w') as file_handle:
file_handle.write(SYSTEMD_SERVICE)
with open(SYSTEMD_SERVICE_PATH, 'w') as file_handle:
file_handle.write(SYSTEMD_SERVICE)
subprocess.check_call(['systemctl', 'daemon-reload'])
subprocess.check_call(['systemctl', 'daemon-reload'])
# Create infinoted group if needed.
try:

View File

@ -25,7 +25,7 @@ import subprocess
import sys
import tempfile
from plinth.utils import generate_password, grep
from plinth.utils import generate_password
MAINTENANCE_SCRIPTS_DIR = "/usr/share/mediawiki/maintenance"
CONF_FILE = '/etc/mediawiki/FreedomBoxSettings.php'
@ -87,12 +87,30 @@ def subcommand_setup(_):
def include_custom_config():
"""Include FreedomBox specific configuration in LocalSettings.php
"""
if not grep(r'FreedomBoxSettings', LOCAL_SETTINGS_CONF):
with open(LOCAL_SETTINGS_CONF, 'a') as conf_file:
conf_file.write(
'include dirname(__FILE__)."/FreedomBoxSettings.php";\n')
"""Include FreedomBox specific configuration in LocalSettings.php."""
with open(LOCAL_SETTINGS_CONF, 'r') as conf_file:
lines = conf_file.readlines()
static_settings_index = None
settings_index = None
for line_number, line in enumerate(lines):
if 'FreedomBoxSettings.php' in line:
settings_index = line_number
if 'FreedomBoxStaticSettings.php' in line:
static_settings_index = line_number
if settings_index is None:
settings_index = len(lines)
lines.append('include dirname(__FILE__)."/FreedomBoxSettings.php";\n')
if static_settings_index is None:
lines.insert(
settings_index,
'include dirname(__FILE__)."/FreedomBoxStaticSettings.php";\n')
with open(LOCAL_SETTINGS_CONF, 'w') as conf_file:
conf_file.writelines(lines)
def subcommand_change_password(arguments):
@ -110,7 +128,7 @@ def subcommand_change_password(arguments):
def subcommand_update(_):
"""Run update.php maintenance script when version upgrades happen."""
update_script = os.path.join(MAINTENANCE_SCRIPTS_DIR, 'update.php')
subprocess.check_call(['php', update_script])
subprocess.check_call(['php', update_script, '--quick'])
def subcommand_public_registrations(arguments):

View File

@ -51,11 +51,11 @@ CONF = r'''
logging = file
panic action = /usr/share/samba/panic-action %d
server role = standalone server
obey pam restrictions = yes
unix password sync = yes
passwd program = /usr/bin/passwd %u
passwd chat = *Enter\snew\s*\spassword:* %n\n *Retype\snew\s*\spassword:* %n\n *password\supdated\ssuccessfully* .
pam password change = yes
#obey pam restrictions = yes
#unix password sync = yes
#passwd program = /usr/bin/passwd %u
#passwd chat = *Enter\snew\s*\spassword:* %n\n *Retype\snew\s*\spassword:* %n\n *password\supdated\ssuccessfully* .
#pam password change = yes
map to guest = bad user
# connection inactivity timeout in minutes
deadtime = 5
@ -73,9 +73,13 @@ def parse_arguments():
subparsers.add_parser('get-shares', help='Get configured samba shares')
subparsers.add_parser('get-users', help='Get users from Samba database')
subparser = subparsers.add_parser('add-share', help='Add new samba share')
subparser.add_argument('--mount-point', help='Path of the mount point',
required=True)
subparser.add_argument('--share-type', help='Type of the share',
required=True, choices=['open', 'group', 'home'])
subparser.add_argument('--windows-filesystem', required=False,
default=False, action='store_true',
help='Path is Windows filesystem')
@ -84,6 +88,8 @@ def parse_arguments():
'delete-share', help='Delete a samba share configuration')
subparser.add_argument('--mount-point', help='Path of the mount point',
required=True)
subparser.add_argument('--share-type', help='Type of the share',
required=True, choices=['open', 'group', 'home'])
subparsers.add_parser('dump-shares',
help='Dump share configuration to file')
@ -104,20 +110,38 @@ def _conf_command(parameters, **kwargs):
subprocess.check_call(['net', 'conf'] + parameters, **kwargs)
def _create_share(mount_point, windows_filesystem=False):
"""Create a samba share."""
def _create_share(mount_point, share_type, windows_filesystem=False):
"""Create samba public, group and private shares."""
if share_type == 'open':
subdir = 'open_share'
elif share_type == 'group':
subdir = 'group_share'
elif share_type == 'home':
subdir = 'homes'
shares_path = _get_shares_path(mount_point)
open_share_path = os.path.join(mount_point, shares_path, 'open_share')
os.makedirs(open_share_path, exist_ok=True)
share_path = os.path.join(mount_point, shares_path, subdir)
os.makedirs(share_path, exist_ok=True)
_make_mounts_readable_by_others(mount_point)
# FAT and NTFS partitions don't support setting permissions
if not windows_filesystem:
_set_open_share_permissions(open_share_path)
if share_type in ['open', 'group']:
_set_share_permissions(share_path)
else:
shutil.chown(share_path, group='users')
os.chmod(share_path, 0o0775)
share_name = _create_share_name(mount_point)
_define_open_share(share_name, open_share_path, windows_filesystem)
if share_type == 'open':
_define_open_share(share_name, share_path, windows_filesystem)
elif share_type == 'group':
_define_group_share(share_name + '_group', share_path,
windows_filesystem)
elif share_type == 'home':
_define_homes_share(share_name + '_home', share_path)
def _create_share_name(mount_point):
@ -141,17 +165,40 @@ def _define_open_share(name, path, windows_filesystem=False):
_conf_command(['setparm', name, 'inherit permissions', 'yes'])
def _define_group_share(name, path, windows_filesystem=False):
"""Define a group samba share."""
try:
_conf_command(['delshare', name], stderr=subprocess.DEVNULL)
except subprocess.CalledProcessError:
pass
_conf_command(['addshare', name, path, 'writeable=y', 'guest_ok=n'])
_conf_command(['setparm', name, 'valid users', '@freedombox-share @admin'])
if not windows_filesystem:
_conf_command(['setparm', name, 'force group', 'freedombox-share'])
_conf_command(['setparm', name, 'inherit permissions', 'yes'])
def _define_homes_share(name, path):
"""Define a samba share for private homes."""
try:
_conf_command(['delshare', name], stderr=subprocess.DEVNULL)
except subprocess.CalledProcessError:
pass
userpath = os.path.join(path, '%u')
_conf_command(['addshare', name, userpath, 'writeable=y', 'guest_ok=n'])
_conf_command(['setparm', name, 'valid users', '@freedombox-share @admin'])
_conf_command(
['setparm', name, 'preexec', 'mkdir -p -m 755 {}'.format(userpath)])
def _get_mount_point(path):
"""Get the mount point where the share is."""
subpath = 'FreedomBox/shares/'
if '/var/lib/freedombox/shares/' in path:
try:
# test whether var directory is a mount point
_validate_mount_point(path.split('lib/freedombox/shares/')[0])
except RuntimeError:
subpath = 'var/lib/freedombox/shares/'
else:
if os.path.ismount(path.split('lib/freedombox/shares/')[0]):
subpath = 'lib/freedombox/shares/'
else:
subpath = 'var/lib/freedombox/shares/'
return path.split(subpath)[0]
@ -160,13 +207,20 @@ def _get_shares():
"""Get shares."""
shares = []
output = subprocess.check_output(['net', 'conf', 'list'])
config = configparser.ConfigParser()
config = configparser.RawConfigParser()
config.read_string(output.decode())
for name in config.sections():
path = config[name]['path']
mount_point = _get_mount_point(path)
share_type = 'open'
if name.endswith('_group'):
share_type = 'group'
elif name.endswith('_home'):
share_type = 'home'
share_path = config[name]['path']
mount_point = _get_mount_point(share_path)
mount_point = os.path.normpath(mount_point)
shares.append(dict(name=name, mount_point=mount_point, path=path))
shares.append(
dict(name=name, mount_point=mount_point, path=share_path,
share_type=share_type))
return shares
@ -221,19 +275,31 @@ def _use_config_file(conf_file):
aug.save()
def _validate_mount_point(path):
"""Validate that given path string is a mount point."""
if path != '/':
parent_path = os.path.dirname(path)
if os.stat(path).st_dev == os.stat(parent_path).st_dev:
raise RuntimeError('Path "{0}" is not a mount point.'.format(path))
def _set_share_permissions(directory):
"""Set file and directory permissions for a share."""
shutil.chown(directory, group='freedombox-share')
os.chmod(directory, 0o2775)
for root, dirs, files in os.walk(directory):
for subdir in dirs:
subdir_path = os.path.join(root, subdir)
shutil.chown(subdir_path, group='freedombox-share')
os.chmod(subdir_path, 0o2775)
for file in files:
file_path = os.path.join(root, file)
shutil.chown(file_path, group='freedombox-share')
os.chmod(file_path, 0o0664)
subprocess.check_call(['setfacl', '-Rm', 'g::rwX', directory])
subprocess.check_call(['setfacl', '-Rdm', 'g::rwX', directory])
def subcommand_add_share(arguments):
"""Create a samba share."""
mount_point = os.path.normpath(arguments.mount_point)
_validate_mount_point(mount_point)
_create_share(mount_point, arguments.windows_filesystem)
if not os.path.ismount(mount_point):
raise RuntimeError(
'Path "{0}" is not a mount point.'.format(mount_point))
_create_share(mount_point, arguments.share_type,
arguments.windows_filesystem)
def subcommand_delete_share(arguments):
@ -241,13 +307,10 @@ def subcommand_delete_share(arguments):
mount_point = os.path.normpath(arguments.mount_point)
shares = _get_shares()
for share in shares:
if share['mount_point'] == mount_point:
if share['mount_point'] == mount_point and share[
'share_type'] == arguments.share_type:
_close_share(share['name'])
_conf_command(['delshare', share['name']])
break
else:
raise RuntimeError(
'Mount point "{0}" is not shared.'.format(mount_point))
def subcommand_get_shares(_):
@ -255,11 +318,20 @@ def subcommand_get_shares(_):
print(json.dumps(_get_shares()))
def subcommand_get_users(_):
"""Get users from Samba database."""
output = subprocess.check_output(['pdbedit', '-L']).decode()
samba_users = [line.split(':')[0] for line in output.split()]
print(json.dumps({'users': samba_users}))
def subcommand_setup(_):
"""Configure samba, use custom samba config file."""
with open(CONF_PATH, 'w') as file_handle:
file_handle.write(CONF)
_use_config_file(CONF_PATH)
os.makedirs('/var/lib/freedombox', exist_ok=True)
os.chmod('/var/lib/freedombox', 0o0755)
if action_utils.service_is_running('smbd'):
action_utils.service_restart('smbd')

View File

@ -26,7 +26,7 @@ import re
import subprocess
import sys
from plinth import action_utils
from plinth.modules.apache.components import check_url
AUTO_CONF_FILE = '/etc/apt/apt.conf.d/20auto-upgrades'
LOG_FILE = '/var/log/unattended-upgrades/unattended-upgrades.log'
@ -129,8 +129,7 @@ def _is_release_file_available(protocol):
if protocol == 'tor+http':
wrapper = 'torsocks'
result = action_utils.check_url(BUSTER_BACKPORTS_RELEASE_FILE_URL,
wrapper=wrapper)
result = check_url(BUSTER_BACKPORTS_RELEASE_FILE_URL, wrapper=wrapper)
return result == 'passed'

View File

@ -27,7 +27,6 @@ import subprocess
import sys
import augeas
from plinth import action_utils
ACCESS_CONF = '/etc/security/access.conf'
@ -80,6 +79,12 @@ def parse_arguments():
subparser.add_argument('username', help='LDAP user to add to group')
subparser.add_argument('groupname', help='LDAP group to add the user to')
subparser = subparsers.add_parser('set-user-status',
help='Set user as active or inactive')
subparser.add_argument('username', help='User to change status')
subparser.add_argument('status', choices=['active', 'inactive'],
help='New status of the user')
subparser = subparsers.add_parser(
'remove-user-from-group',
help='Remove an LDAP user from an LDAP group')
@ -210,8 +215,8 @@ def configure_ldapscripts():
# modify a copy of the config file
shutil.copy('/etc/ldapscripts/ldapscripts.conf', LDAPSCRIPTS_CONF)
aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD +
augeas.Augeas.NO_MODL_AUTOLOAD)
aug = augeas.Augeas(
flags=augeas.Augeas.NO_LOAD + augeas.Augeas.NO_MODL_AUTOLOAD)
aug.set('/augeas/load/Shellvars/lens', 'Shellvars.lns')
aug.set('/augeas/load/Shellvars/incl[last() + 1]', LDAPSCRIPTS_CONF)
aug.load()
@ -227,6 +232,30 @@ def configure_ldapscripts():
aug.save()
def get_samba_users():
"""Get users from the Samba user database."""
# 'pdbedit -L' is better for listing users but is installed only with samba
stdout = subprocess.check_output(
['tdbdump', '/var/lib/samba/private/passdb.tdb']).decode()
return re.findall(r'USER_(.*)\\0', stdout)
def delete_samba_user(username):
"""Delete a Samba user."""
if username in get_samba_users():
subprocess.check_call(['smbpasswd', '-x', username])
disconnect_samba_user(username)
def disconnect_samba_user(username):
"""Disconnect a Samba user."""
try:
subprocess.check_call(['pkill', '-U', username, 'smbd'])
except subprocess.CalledProcessError as error:
if error.returncode != 1:
raise
def read_password():
"""Read the password from stdin."""
return ''.join(sys.stdin)
@ -235,8 +264,10 @@ def read_password():
def subcommand_create_user(arguments):
"""Create an LDAP user, set password and flush cache."""
_run(['ldapadduser', arguments.username, 'users'])
set_user_password(arguments.username, read_password())
password = read_password()
set_user_password(arguments.username, password)
flush_cache()
set_samba_user(arguments.username, password)
def subcommand_remove_user(arguments):
@ -244,10 +275,13 @@ def subcommand_remove_user(arguments):
username = arguments.username
groups = get_user_groups(username)
delete_samba_user(username)
for group in groups:
remove_user_from_group(username, group)
_run(['ldapdeleteuser', username])
flush_cache()
@ -257,6 +291,8 @@ def subcommand_rename_user(arguments):
new_username = arguments.newusername
groups = get_user_groups(old_username)
delete_samba_user(old_username)
for group in groups:
remove_user_from_group(old_username, group)
@ -275,9 +311,25 @@ def set_user_password(username, password):
_run(['ldapsetpasswd', username, password])
def set_samba_user(username, password):
"""Insert a user to the Samba database.
If a user already exists, update password.
"""
proc = subprocess.Popen(['smbpasswd', '-a', '-s', username],
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
_, stderr = proc.communicate(input='{0}\n{0}\n'.format(password).encode(),
timeout=10)
if proc.returncode != 0:
raise RuntimeError('Unable to add Samba user: ', stderr)
def subcommand_set_user_password(arguments):
"""Set a user's password."""
set_user_password(arguments.username, read_password())
password = read_password()
set_user_password(arguments.username, password)
set_samba_user(arguments.username, password)
def get_user_groups(username):
@ -352,6 +404,8 @@ def subcommand_remove_user_from_group(arguments):
"""Remove an LDAP user from an LDAP group."""
remove_user_from_group(arguments.username, arguments.groupname)
flush_cache()
if arguments.groupname == 'freedombox-share':
disconnect_samba_user(arguments.username)
def subcommand_get_group_users(arguments):
@ -369,6 +423,22 @@ def subcommand_get_group_users(arguments):
print(user)
def subcommand_set_user_status(arguments):
"""Set the status of the user."""
username = arguments.username
status = arguments.status
if status == 'active':
flag = '-e'
else:
flag = '-d'
if username in get_samba_users():
subprocess.check_call(['smbpasswd', flag, username])
if status == 'inactive':
disconnect_samba_user(username)
def flush_cache():
"""Flush nscd and apache2 cache."""
_run(['nscd', '--invalidate=passwd'])

View File

@ -71,6 +71,13 @@ def fixture_needs_root():
pytest.skip('Needs to be root')
@pytest.fixture(name='needs_not_root', scope='session')
def fixture_needs_not_root():
"""Skip test if running in root mode."""
if os.geteuid() == 0:
pytest.skip('Needs not to be root')
@pytest.fixture(name='needs_sudo')
def fixture_needs_sudo():
"""Skip test if sudo command is not available."""

70
debian/changelog vendored
View File

@ -1,3 +1,73 @@
plinth (19.24) unstable; urgency=medium
[ Thomas Vincent ]
* Translated using Weblate (French)
* Translated using Weblate (French)
[ Veiko Aasa ]
* app: Fix javascript doesn't run on first visit
* samba: private shares
* storage: Tests for the directory validation action
* users: Add tests for the Samba user database
[ James Valleroy ]
* samba: Fix spelling in description
* debian: Update French debconf translation (Closes: #947386)
- Thanks to Jean-Pierre Giraud for the patch.
* firewall: Support upgrading firewalld to 0.8
* mldonkey: Add ProtectKernelLogs
* deluge: Use systemd sandboxing features
* infinoted: Use systemd sandboxing features
* storage: Add systemd sandboxing features to udiskie service
* upgrades: Add systemd sandboxing features to repository setup service
* security: List whether each app is sandboxed
* locale: Update translation strings
* debian: Update Dutch debconf translation (Closes: #947136)
- Thanks to Frans Spiesschaert for the patch.
* doc: Fetch latest manual
[ Michael Breidenbach ]
* Translated using Weblate (German)
* Translated using Weblate (Swedish)
[ Nektarios Katakis ]
* Translated using Weblate (Greek)
[ Doma Gergő ]
* Translated using Weblate (Hungarian)
[ Allan Nordhøy ]
* Translated using Weblate (Norwegian Bokmål)
[ Kunal Mehta ]
* mediawiki: Pass --quick when running update.php
[ Sunil Mohan Adapa ]
* help: Refactor to move app into __init__.py for consistency
* app: Introduce API to return a list of all apps
* app: Introduce API to run diagnostics on an app
* apache: Implement diagnostic test for web server component
* daemon: Implement diagnostic test for daemon component
* daemon: Implement diagnostic test to check if a daemon is running
* firewall: Implement new diagnostic tests to check port status
* diagnostics: Use new component based API for all diagnostic tests
* cosmetic: Yapf and isort fixes
* daemon: Move diagnosing port listening into daemon module
* daemon: Move diagnosing using netcat to daemon module
* apache: Move diagnostics for checking URLs into apache module
* app: Implement API to check if app/component has diagnostics
* views: Don't require sending diagnostics module name separately
* minidlna: Fix showing clients information
* mediawiki: Fix problem with session cache failing logins
[ Ralf Barkow ]
* Translated using Weblate (German)
[ erlendnagel ]
* Translated using Weblate (Dutch)
-- James Valleroy <jvalleroy@mailbox.org> Mon, 30 Dec 2019 21:17:58 -0500
plinth (19.23~bpo10+1) buster-backports; urgency=medium
* Rebuild for buster-backports.

12
debian/po/fr.po vendored
View File

@ -1,14 +1,14 @@
# Translation of plinth debconf templates to French
# Copyright (C) 2018 FreedomBox packaging team <freedombox-pkg-team@lists.alioth.debian.org>
# This file is distributed under the same license as the plinth package.
# Jean-Pierre Giraud <jean-pierregiraud@neuf.fr>, 2018.
#
# Jean-Pierre Giraud <jean-pierregiraud@neuf.fr>, 2018, 2019.
msgid ""
msgstr ""
"Project-Id-Version: plinth\n"
"Report-Msgid-Bugs-To: plinth@packages.debian.org\n"
"POT-Creation-Date: 2019-11-18 18:11-0500\n"
"PO-Revision-Date: 2018-07-30 23:19+0100\n"
"PO-Revision-Date: 2019-12-16 10:23+0100\n"
"Last-Translator: Jean-Pierre Giraud <jean-pierregiraud@neuf.fr>\n"
"Language-Team: French <debian-l10n-french@lists.debian.org>\n"
"Language: fr_FR\n"
@ -27,7 +27,6 @@ msgstr "Phrase secrète du premier assistant de FreedomBox - ${secret}"
#. Type: note
#. Description
#: ../templates:1001
#, fuzzy
#| msgid ""
#| "Please save this string. You will be asked to enter this in the first "
#| "screen after you launch the FreedomBox interface. In case you lose it, "
@ -37,6 +36,7 @@ msgid ""
"first screen after you launch the FreedomBox web interface. In case you lose "
"it, you can retrieve it by running the following command:"
msgstr ""
"Veuillez retenir cette chaîne. Le premier écran après le chargement de "
"l'interface de FreedomBox vous la demandera. Si vous l'avez oubliée, vous "
"pourrez la retrouver dans le fichier /var/lib/plinth/firstboot-wizard-secret."
"Veuillez noter cette phrase secrète. Le premier écran après le chargement de "
"l'interface web de FreedomBox vous la demandera. Si vous l'avez oubliée, vous "
"pourrez la récupérer en exécutant la commande suivante :"

19
debian/po/nl.po vendored
View File

@ -6,10 +6,10 @@
#
msgid ""
msgstr ""
"Project-Id-Version: plinth_0.34.0\n"
"Project-Id-Version: plinth_19.21\n"
"Report-Msgid-Bugs-To: plinth@packages.debian.org\n"
"POT-Creation-Date: 2019-11-18 18:11-0500\n"
"PO-Revision-Date: 2018-07-31 16:32+0200\n"
"PO-Revision-Date: 2019-12-02 21:04+0100\n"
"Last-Translator: Frans Spiesschaert <Frans.Spiesschaert@yucom.be>\n"
"Language-Team: Debian Dutch l10n Team <debian-l10n-dutch@lists.debian.org>\n"
"Language: nl\n"
@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Gtranslator 2.91.7\n"
"X-Generator: Poedit 2.2.1\n"
#. Type: note
#. Description
@ -28,17 +28,12 @@ msgstr "Geheime code voor de initiële wizard van FreedomBox - ${secret}"
#. Type: note
#. Description
#: ../templates:1001
#, fuzzy
#| msgid ""
#| "Please save this string. You will be asked to enter this in the first "
#| "screen after you launch the FreedomBox interface. In case you lose it, "
#| "you can find it in the file /var/lib/plinth/firstboot-wizard-secret."
msgid ""
"Please note down the above secret. You will be asked to enter this in the "
"first screen after you launch the FreedomBox web interface. In case you lose "
"it, you can retrieve it by running the following command:"
msgstr ""
"Bewaar deze tekenreeks. Er zal u gevraagd worden om ze in te voeren in het "
"openingsscherm bij de eerste opstart van de FreedomBox-interface. In geval u "
"ze kwijtraakt kunt u ze terugvinden in het bestand /var/lib/plinth/firstboot-"
"wizard-secret."
"Noteer bovenstaande geheime code. Er zal u gevraagd worden om ze in het "
"openingsscherm in te voeren wanneer u de FreedomBox-interface opstart. In "
"geval u ze kwijtraakt, kunt u ze terughalen door het volgende commando uit "
"te voeren:"

View File

@ -14,21 +14,6 @@ are loaded. The ``init()`` call order guarantees that other applications that
this application depends on will be initialized before this application is
initialized.
<app-module>.diagnose()
^^^^^^^^^^^^^^^^^^^^^^^
Optional. Called when the user invokes system-wide diagnostics by visiting
**System -> Diagnositcs**. This method must return an array of diagnostic
results. Each diagnostic result must be a two-tuple with first element as a
string that is shown to the user as name of the test and second element is the
result of the test. It must be one of ``passed``, ``failed``, ``error``. Example
return value:
.. code-block:: python3
[('Check http://localhost/app is reachable', 'passed'),
('Check configuration is sane', 'passed')]
<app-module>.depends
^^^^^^^^^^^^^^^^^^^^

View File

@ -26,14 +26,17 @@ our app's class.
def __init__(self):
...
daemon = Daemon('daemon-transmission', managed_services[0])
daemon = Daemon('daemon-transmission', managed_services[0],
listen_ports=[(9091, 'tcp4')])
self.add(daemon)
The first argument to instantiate the :class:`~plinth.daemon.Daemon` class is a
unique ID. The second is the name of the `systemd
<https://www.freedesktop.org/wiki/Software/systemd/>`_ unit file which manages
the daemon.
the daemon. The final argument is the list of ports that this daemon listens on.
This information is used to check if the daemon is listening on the expected
ports when the user requests diagnostic tests on the app.
Managing web server configuration
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -57,14 +60,18 @@ app. Let us do that in our app's class.
def __init__(self):
...
webserver = Webserver('webserver-transmission', 'transmission-plinth')
webserver = Webserver('webserver-transmission', 'transmission-plinth'
urls=['https://{host}/transmission'])
self.add(webserver)
The first argument to instantiate the
:class:`~plinth.modules.apache.components.Webserver` class is a unique ID. The
second is the name of the Apache2 web server configuration snippet that contains
the directives to proxy Transmission web interface via Apache2. We then need to
create the configuration file itself in ``tranmission-freedombox.conf``.
create the configuration file itself in ``tranmission-freedombox.conf``. The
final argument is the list of URLs that the app exposes to the users of the app.
This information is used to check if the URLs are accessible as expected when
the user requests diagnostic tests on the app.
.. code-block:: apache

View File

@ -102,57 +102,38 @@ daemons should be stopped during the backup process. In ``__init__.py``, add:
Creating diagnostics
^^^^^^^^^^^^^^^^^^^^
When the app does not work as expected, the user should known what is happening
with the app. The FreedomBox framework provides an API for running and showing
diagnostics results. The app has to implement a method for actually running the
diagnostics and return the results as a list. FreedomBox then takes care of
When the app does not work as expected, the user should know what is happening
with the app. FreedomBox framework provides an API for running and showing
diagnostics results. Most of the common diagnostic tests are implemented by the
framework as part of the components used by an app. FreedomBox takes care of
calling the diagnostics method and displaying the list in a formatted manner.
To implement the diagnostics, a method called ``diagnose()`` has to be available
as ``<app-module>.diagnose()``. It must return a list in which each item is the
result of a test performed. The item itself is a two-tuple containing the
display name of the test followed by the result as ``passed``, ``failed`` or
``error``.
To implement additional diagnostic tests on top of those provided by the
framework, the method :meth:`plinth.app.App.diagnose` has to be overridden or in
a component that belongs to the app, the method
:meth:`plinth.app.Component.diagnose` has to be overridden. The methods must
return a list in which each item is the result of a test performed. The item
itself is a two-tuple containing the display name of the test followed by the
result as ``passed``, ``failed`` or ``error``.
.. code-block:: python3
def diagnose():
"""Run diagnostics and return the results."""
results = []
results.extend(action_utils.diagnose_url_on_all(
'https://{host}/transmission', extra_options=['--no-check-certificate']))
return results
Now that we have implemented diagnostics, we also need to show a diagnostics
button in the App's page. Adding an attribute to the
:class:`~plinth.views.AppView` will take care of this.
.. code-block:: python3
class TransmissionView(views.AppView):
class TransmissionAppView(views.AppView):
...
diagnostics_module_name = 'transmission'
def diagnose():
"""Run diagnostics and return the results."""
results = super().diagnose()
There are several helpers available to implement some of the common diagnostic
tests. For our application we wish to implement a test to check whether the
``/transmission`` URL is accessible. Since this is a commonly performed test,
there is a helper method available and we have used it in the above code. The
``{host}`` tag replaced with various IP addresses, hostnames and domain names by
the helper to produce different kinds of URLs and they are all tested. Results
for all tests are returned which we then pass on to the framework.
results.append(['Example test', 'passed'])
return results
The user can trigger the diagnostics test by going to **System -> Diagnostics**
page. This runs diagnostics for all the applications. Users can also run
diagnostics specifically for this app from the app's page. A diagnostics button
is shown by the `app.html` template automatically when
``diagnostics_module_name`` attribute is set in the app's ``AppView`` derived
from :obj:`plinth.views.AppView`.
.. code-block:: django
{% include "diagnostics_button.html" with module="ttrss" enabled=True %}
diagnostics specifically for this app from the app's page. A diagnostics menu
item is shown by the :class:`plinth.views.AppView` and `app.html` template
automatically when ``diagnose()`` method is overridden in the app or a
component.
Logging
^^^^^^^

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -4,6 +4,18 @@
<articleinfo>
<title>FreedomBox/Manual</title>
<revhistory>
<revision>
<revnumber>80</revnumber>
<date>2019-12-25 18:43:42</date>
<authorinitials>Drahtseil</authorinitials>
<revremark>added MiniDLNA</revremark>
</revision>
<revision>
<revnumber>79</revnumber>
<date>2019-12-20 19:58:30</date>
<authorinitials>Drahtseil</authorinitials>
<revremark>Placed Simple Gitweb in the correct place</revremark>
</revision>
<revision>
<revnumber>78</revnumber>
<date>2019-12-15 19:26:31</date>
@ -1811,6 +1823,15 @@ echo 'select name from users' | sqlite3 /var/lib/matrix-synapse/homeserver.db ]
</listitem>
</itemizedlist>
</listitem>
<listitem>
<para>If you wish to create a community in Matrix Synapse, a Matrix user with server admin privileges is needed. Run the following commands: </para>
<itemizedlist>
<listitem override="none">
<screen><![CDATA[sudo apt install sqlite3
echo "UPDATE users SET admin=1 WHERE name='@username:domainname'" | sudo sqlite3 /var/lib/matrix-synapse/homeserver.db ]]></screen>
</listitem>
</itemizedlist>
</listitem>
</orderedlist>
</section>
</section>
@ -2351,6 +2372,31 @@ echo 'select name from users' | sqlite3 /var/lib/matrix-synapse/homeserver.db ]
</section>
</section>
</section>
<section>
<title>MiniDLNA</title>
<para>MiniDLNA is a media server with the aim to be compliant with DLNA/UPnP clients. </para>
<section>
<title>What is UPnP/DLNA?</title>
<para>Universal plug &amp; play is a set of networking protocols that allow devices within a network such as PCs, TVs, printers etc. to seamlessly discover each other and establish communication for data sharing. It is zero configuration protocol and requires only a media server and a media player that are compliant with the protocol. </para>
<para>DLNA is derived from UPnP as a form of standardizing media interoperability. It forms a standard/certification which many consumer electronics conform to. </para>
</section>
<section>
<title>Setting up MiniDLNA on your FreedomBox.</title>
<para>To install/enable the media server you need to navigate at MiniDLNA page and enable it. The application is intended to be available in the internal (home) network and therefore it requires a network interface configured for internal traffic. </para>
<para>After installation a web page becomes available on <ulink url="https://&lt;your-freedombox&gt;/_minidlna"/>. It includes information for how many files the server is detecting, how many connections exist etc. This is very useful if plugging external disks with media to check if the new media files are detected properly. If that is not happening, disabling and enabling the server will fix it. </para>
</section>
<section>
<title>File systems for external drives.</title>
<para>If using an external drive that is used also from a Windows system the preferred filesystem should be NTFS. NTFS will keep Linux file permissions and UTF8 encoding for file names. This is useful if file names are in your language. </para>
</section>
<section>
<title>External links</title>
<para>
<ulink url="http://minidlna.sourceforge.net/"/>
<ulink url="https://en.wikipedia.org/wiki/Digital_Living_Network_Alliance"/>
</para>
</section>
</section>
<section>
<title>News Feed Reader (Tiny Tiny RSS)</title>
<para>Tiny Tiny RSS is a news feed (RSS/Atom) reader and aggregator, designed to allow reading news from any location, while feeling as close to a real desktop application as possible. </para>
@ -2506,6 +2552,34 @@ echo 'select name from users' | sqlite3 /var/lib/matrix-synapse/homeserver.db ]
</para>
</section>
</section>
<section>
<title>Simple Git Hosting (GitWeb)</title>
<para>Git is a distributed version-control system for tracking changes in source code during software development. GitWeb provides a web interface to Git repositories. You can browse history and content of source code, use search to find relevant commits and code. You can also clone repositories and upload code changes with a command-line Git client or with multiple available graphical clients. And you can share your code with people around the world. </para>
<para>To learn more on how to use Git visit <ulink url="https://git-scm.com/docs/gittutorial">Git tutorial</ulink>. </para>
<para><emphasis role="strong">Available since version:</emphasis> 19.19 </para>
<section>
<title>Managing the repositories</title>
<para>After installation of GitWeb, a new repository can be created. It can be marked as <emphasis>private</emphasis> to limit access. </para>
</section>
<section>
<title>Access</title>
<para>GitWeb can be accessed after installation e.g. by the web client through <ulink url="https://&lt;my"/> freedombox name&gt;/gitweb </para>
</section>
<section>
<title>HTTP basic auth</title>
<para>GitWeb on FreedomBox currently supports HTTP remotes only. To avoid having to enter the password each time you pull/push to the repository, you can edit your remote to include the credentials. </para>
<para>
<emphasis>Example:</emphasis>
<ulink url="https://username@password:my.freedombox.rocks/gitweb/myrepo"/>
</para>
<para>Your username and password will be encrypted. Someone monitoring the network traffic will notice the domain name only.</para>
<para><emphasis role="strong">Note:</emphasis> If using this method, your password will be stored in plain text in the local repository's .git/config file. </para>
</section>
<section>
<title>Mirroring</title>
<para>Though your repositories are primarily hosted on your own FreedomBox, you can configure a repository on another Git hosting system like <ulink url="https://wiki.debian.org/FreedomBox/Manual/GitLab#">GitLab</ulink> as a mirror. </para>
</section>
</section>
<section>
<title>SIP Server (repro)</title>
<caution>
@ -3119,34 +3193,6 @@ echo "newpassword" | su mumble-server -s /bin/sh -c "/usr/sbin/murmurd -ini /etc
</itemizedlist>
</section>
</section>
<section>
<title>Simple Git Hosting (GitWeb)</title>
<para>Git is a distributed version-control system for tracking changes in source code during software development. GitWeb provides a web interface to Git repositories. You can browse history and content of source code, use search to find relevant commits and code. You can also clone repositories and upload code changes with a command-line Git client or with multiple available graphical clients. And you can share your code with people around the world. </para>
<para>To learn more on how to use Git visit <ulink url="https://git-scm.com/docs/gittutorial">Git tutorial</ulink>. </para>
<para><emphasis role="strong">Available since version:</emphasis> 19.19 </para>
<section>
<title>Managing the repositories</title>
<para>After installation of GitWeb, a new repository can be created. It can be marked as <emphasis>private</emphasis> to limit access. </para>
</section>
<section>
<title>Access</title>
<para>GitWeb can be accessed after installation e.g. by the web client through <ulink url="https://&lt;my"/> freedombox name&gt;/gitweb </para>
</section>
<section>
<title>HTTP basic auth</title>
<para>GitWeb on FreedomBox currently supports HTTP remotes only. To avoid having to enter the password each time you pull/push to the repository, you can edit your remote to include the credentials. </para>
<para>
<emphasis>Example:</emphasis>
<ulink url="https://username@password:my.freedombox.rocks/gitweb/myrepo"/>
</para>
<para>Your username and password will be encrypted. Someone monitoring the network traffic will notice the domain name only.</para>
<para><emphasis role="strong">Note:</emphasis> If using this method, your password will be stored in plain text in the local repository's .git/config file. </para>
</section>
<section>
<title>Mirroring</title>
<para>Though your repositories are primarily hosted on your own FreedomBox, you can configure a repository on another Git hosting system like <ulink url="https://wiki.debian.org/FreedomBox/Manual/GitLab#">GitLab</ulink> as a mirror. </para>
</section>
</section>
</section>
<section>
<title>System</title>
@ -9990,6 +10036,50 @@ wget https://www.thinkpenguin.com/files/ath9k_firmware_free-version/htc_7010.fw]
<section>
<title>Release Notes</title>
<para>The following are the release notes for each FreedomBox version. </para>
<section>
<title>FreedomBox 19.24 (2019-12-30)</title>
<itemizedlist>
<listitem>
<para>app: Fix JavaScript doesn't run on first visit </para>
</listitem>
<listitem>
<para>samba: Add private shares </para>
</listitem>
<listitem>
<para>firewall: Support upgrading firewalld to 0.8 </para>
</listitem>
<listitem>
<para>deluge: Add systemd sandboxing features </para>
</listitem>
<listitem>
<para>infinoted: Add systemd sandboxing features </para>
</listitem>
<listitem>
<para>storage: Add systemd sandboxing features to udiskie service </para>
</listitem>
<listitem>
<para>upgrades: Add systemd sandboxing features to repository setup service </para>
</listitem>
<listitem>
<para>security: List whether each app is sandboxed </para>
</listitem>
<listitem>
<para>mediawiki: Avoid delay in update script </para>
</listitem>
<listitem>
<para>diagnostics: Use new component based API for all diagnostic tests </para>
</listitem>
<listitem>
<para>minidlna: Fix showing clients information </para>
</listitem>
<listitem>
<para>mediawiki: Fix problem with session cache failing logins </para>
</listitem>
<listitem>
<para>locale: Update translations for French, German, Swedish, Greek, Hungarian, Norwegian Bokmål, and Dutch </para>
</listitem>
</itemizedlist>
</section>
<section>
<title>FreedomBox 19.23 (2019-12-16)</title>
<itemizedlist>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More