mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-21 07:55:00 +00:00
Allow read access by URL by default. Tests: - Installing bepasty fresh show the default permissions as read. - Upgrading bepasty from older version when default permissions are none sets the default permissions to read. - Upgrading bepasty from older version when default permissions are not none retrains the permissions. Signed-off-by: Joseph Nuthalapati <njoseph@riseup.net> [sunil: Don't relocate setup() method] Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org> Tested-by: Sunil Mohan Adapa <sunil@medhas.org>
220 lines
6.6 KiB
Python
Executable File
220 lines
6.6 KiB
Python
Executable File
#!/usr/bin/python3
|
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
"""
|
|
Configuration helper for bepasty.
|
|
"""
|
|
|
|
import argparse
|
|
import collections
|
|
import grp
|
|
import json
|
|
import os
|
|
import pathlib
|
|
import pwd
|
|
import secrets
|
|
import shutil
|
|
import string
|
|
import subprocess
|
|
import sys
|
|
|
|
import augeas
|
|
|
|
from plinth import action_utils
|
|
from plinth.modules import bepasty
|
|
|
|
DATA_DIR = '/var/lib/bepasty'
|
|
|
|
PASSWORD_LENGTH = 20
|
|
|
|
CONF_FILE = pathlib.Path('/etc/bepasty-freedombox.conf')
|
|
|
|
|
|
def parse_arguments():
|
|
"""Return parsed command line arguments as dictionary."""
|
|
parser = argparse.ArgumentParser()
|
|
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
|
|
|
|
setup = subparsers.add_parser(
|
|
'setup', help='Perform post-installation operations for bepasty')
|
|
setup.add_argument('--domain-name', required=True,
|
|
help='The domain name that will be used by bepasty')
|
|
|
|
subparsers.add_parser('get-configuration', help='Get all configuration')
|
|
|
|
add_password = subparsers.add_parser(
|
|
'add-password', help='Generate a password with given permissions')
|
|
add_password.add_argument(
|
|
'--permissions', nargs='+',
|
|
help='Any number of permissions from the set: {}'.format(', '.join(
|
|
bepasty.PERMISSIONS.keys())))
|
|
add_password.add_argument(
|
|
'--comment', required=False,
|
|
help='A comment for the password and its permissions')
|
|
|
|
subparsers.add_parser('remove-password',
|
|
help='Remove a password and its permissions')
|
|
|
|
set_default = subparsers.add_parser('set-default',
|
|
help='Set default permissions')
|
|
set_default.add_argument(
|
|
'--permissions', nargs='*',
|
|
help='Any number of permissions from the set: {}'.format(', '.join(
|
|
bepasty.PERMISSIONS.keys())))
|
|
|
|
subparsers.required = True
|
|
return parser.parse_args()
|
|
|
|
|
|
def _augeas_load():
|
|
"""Initialize Augeas."""
|
|
aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD +
|
|
augeas.Augeas.NO_MODL_AUTOLOAD)
|
|
aug.set('/augeas/load/Simplevars/lens', 'Simplevars.lns')
|
|
aug.set('/augeas/load/Simplevars/incl[last() + 1]', str(CONF_FILE))
|
|
aug.load()
|
|
return aug
|
|
|
|
|
|
def _key_path(key):
|
|
"""Return the augeas path for the key."""
|
|
return '/files' + str(CONF_FILE) + '/' + key
|
|
|
|
|
|
def conf_file_read():
|
|
"""Read and return the configuration."""
|
|
aug = _augeas_load()
|
|
conf = collections.OrderedDict()
|
|
for path in aug.match(_key_path('*')):
|
|
key = path.rsplit('/', 1)[-1]
|
|
if key[0] != '#':
|
|
conf[key] = json.loads(aug.get(path))
|
|
|
|
return conf
|
|
|
|
|
|
def conf_file_write(conf):
|
|
"""Write configuration to the file."""
|
|
aug = _augeas_load()
|
|
for key, value in conf.items():
|
|
if not key.startswith('#'):
|
|
value = json.dumps(value)
|
|
|
|
aug.set(_key_path(key), value)
|
|
|
|
aug.save()
|
|
|
|
|
|
def subcommand_setup(arguments):
|
|
"""Post installation actions for bepasty."""
|
|
# Create bepasty group if needed.
|
|
try:
|
|
grp.getgrnam('bepasty')
|
|
except KeyError:
|
|
subprocess.run(['addgroup', '--system', 'bepasty'], check=True)
|
|
|
|
# Create bepasty user if needed.
|
|
try:
|
|
pwd.getpwnam('bepasty')
|
|
except KeyError:
|
|
subprocess.run([
|
|
'adduser', '--system', '--ingroup', 'bepasty', '--home',
|
|
'/var/lib/bepasty', '--gecos', 'bepasty file sharing', 'bepasty'
|
|
], check=True)
|
|
|
|
# Create data directory if needed.
|
|
if not os.path.exists(DATA_DIR):
|
|
os.makedirs(DATA_DIR, mode=0o750)
|
|
shutil.chown(DATA_DIR, user='bepasty', group='bepasty')
|
|
|
|
# Create configuration file if needed.
|
|
if not CONF_FILE.is_file():
|
|
passwords = [_generate_password() for _ in range(3)]
|
|
conf = {
|
|
'#comment':
|
|
'This file is managed by FreedomBox. Only a small subset of '
|
|
'the original configuration format is supported. Each line '
|
|
'should be in KEY = VALUE format. VALUE must be a JSON '
|
|
'encoded string.',
|
|
'SITENAME': arguments.domain_name,
|
|
'STORAGE_FILESYSTEM_DIRECTORY': '/var/lib/bepasty',
|
|
'SECRET_KEY': secrets.token_hex(64),
|
|
'PERMISSIONS': {
|
|
passwords[0]: 'admin,list,create,read,delete',
|
|
passwords[1]: 'list,create,read,delete',
|
|
passwords[2]: 'list,read',
|
|
},
|
|
'PERMISSION_COMMENTS': {
|
|
passwords[0]: 'admin',
|
|
passwords[1]: 'editor',
|
|
passwords[2]: 'viewer',
|
|
},
|
|
'DEFAULT_PERMISSIONS': 'read',
|
|
}
|
|
conf_file_write(conf)
|
|
CONF_FILE.chmod(0o640)
|
|
shutil.chown(CONF_FILE, user='bepasty', group='bepasty')
|
|
|
|
|
|
def subcommand_get_configuration(_):
|
|
"""Get default permissions, passwords, permissions and comments."""
|
|
conf = conf_file_read()
|
|
print(json.dumps(conf))
|
|
|
|
|
|
def subcommand_add_password(arguments):
|
|
"""Generate a password with given permissions."""
|
|
conf = conf_file_read()
|
|
permissions = _format_permissions(arguments.permissions)
|
|
password = _generate_password()
|
|
conf['PERMISSIONS'][password] = permissions
|
|
if arguments.comment:
|
|
conf['PERMISSION_COMMENTS'][password] = arguments.comment
|
|
|
|
conf_file_write(conf)
|
|
action_utils.service_try_restart('uwsgi')
|
|
|
|
|
|
def subcommand_remove_password(_arguments):
|
|
"""Remove a password and its permissions."""
|
|
conf = conf_file_read()
|
|
password = ''.join(sys.stdin)
|
|
if password in conf['PERMISSIONS']:
|
|
del conf['PERMISSIONS'][password]
|
|
|
|
if password in conf['PERMISSION_COMMENTS']:
|
|
del conf['PERMISSION_COMMENTS'][password]
|
|
conf_file_write(conf)
|
|
action_utils.service_try_restart('uwsgi')
|
|
|
|
|
|
def subcommand_set_default(arguments):
|
|
"""Set default permissions."""
|
|
conf = {'DEFAULT_PERMISSIONS': _format_permissions(arguments.permissions)}
|
|
conf_file_write(conf)
|
|
action_utils.service_try_restart('uwsgi')
|
|
|
|
|
|
def _format_permissions(permissions=None):
|
|
"""Format permissions as comma-separated."""
|
|
return ','.join(set(bepasty.PERMISSIONS.keys()).intersection(
|
|
permissions)) if permissions else ''
|
|
|
|
|
|
def _generate_password():
|
|
"""Generate a random password."""
|
|
alphabet = string.ascii_letters + string.digits
|
|
return ''.join(secrets.choice(alphabet) for _ in range(PASSWORD_LENGTH))
|
|
|
|
|
|
def main():
|
|
"""Parse arguments and perform all duties."""
|
|
arguments = parse_arguments()
|
|
|
|
subcommand = arguments.subcommand.replace('-', '_')
|
|
subcommand_method = globals()['subcommand_' + subcommand]
|
|
subcommand_method(arguments)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|