mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-21 07:55:00 +00:00
Test: Functional tests passed Signed-off-by: nbenedek <contact@nbenedek.me> [sunil: Update docstrings, make uninstall fail-safe] Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
182 lines
5.2 KiB
Python
182 lines
5.2 KiB
Python
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
"""Configure sharing."""
|
|
|
|
import os
|
|
import pathlib
|
|
import re
|
|
|
|
import augeas
|
|
|
|
from plinth import action_utils
|
|
from plinth.actions import privileged
|
|
|
|
APACHE_CONFIGURATION = '/etc/apache2/conf-available/sharing-freedombox.conf'
|
|
|
|
|
|
@privileged
|
|
def setup():
|
|
"""Create an empty apache configuration file."""
|
|
path = pathlib.Path(APACHE_CONFIGURATION)
|
|
if not path.exists():
|
|
path.touch()
|
|
|
|
|
|
def load_augeas():
|
|
"""Initialize augeas for this app's configuration file."""
|
|
aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD +
|
|
augeas.Augeas.NO_MODL_AUTOLOAD)
|
|
aug.set('/augeas/load/Httpd/lens', 'Httpd.lns')
|
|
aug.set('/augeas/load/Httpd/incl[last() + 1]', APACHE_CONFIGURATION)
|
|
aug.load()
|
|
|
|
aug.defvar('conf', '/files' + APACHE_CONFIGURATION)
|
|
|
|
return aug
|
|
|
|
|
|
@privileged
|
|
def add(name: str, path: str, groups: list[str], is_public: bool):
|
|
"""Add a share to Apache configuration."""
|
|
path = '"' + path.replace('"', r'\"') + '"'
|
|
url = '/share/' + name
|
|
|
|
if not os.path.exists(APACHE_CONFIGURATION):
|
|
pathlib.Path(APACHE_CONFIGURATION).touch()
|
|
|
|
aug = load_augeas()
|
|
shares = _list(aug)
|
|
if any([share for share in shares if share['name'] == name]):
|
|
raise Exception('Share already present')
|
|
|
|
aug.set('$conf/directive[last() + 1]', 'Alias')
|
|
aug.set('$conf/directive[last()]/arg[1]', url)
|
|
aug.set('$conf/directive[last()]/arg[2]', path)
|
|
|
|
aug.set('$conf/Location[last() + 1]/arg', url)
|
|
|
|
aug.set('$conf/Location[last()]/directive[last() + 1]', 'Include')
|
|
aug.set('$conf/Location[last()]/directive[last()]/arg',
|
|
'includes/freedombox-sharing.conf')
|
|
|
|
if not is_public:
|
|
aug.set('$conf/Location[last()]/directive[last() + 1]', 'Include')
|
|
aug.set('$conf/Location[last()]/directive[last()]/arg',
|
|
'includes/freedombox-single-sign-on.conf')
|
|
|
|
aug.set('$conf/Location[last()]/IfModule/arg', 'mod_auth_pubtkt.c')
|
|
aug.set('$conf/Location[last()]/IfModule/directive[1]', 'TKTAuthToken')
|
|
for group_name in groups:
|
|
aug.set(
|
|
'$conf/Location[last()]/IfModule/directive[1]/arg[last() + 1]',
|
|
group_name)
|
|
else:
|
|
aug.set('$conf/Location[last()]/directive[last() + 1]', 'Require')
|
|
aug.set('$conf/Location[last()]/directive[last()]/arg[1]', 'all')
|
|
aug.set('$conf/Location[last()]/directive[last()]/arg[2]', 'granted')
|
|
|
|
aug.save()
|
|
|
|
with action_utils.WebserverChange() as webserver_change:
|
|
webserver_change.enable('sharing-freedombox')
|
|
|
|
|
|
@privileged
|
|
def remove(name: str):
|
|
"""Remove a share from Apache configuration."""
|
|
url_to_remove = '/share/' + name
|
|
|
|
aug = load_augeas()
|
|
|
|
for directive in aug.match('$conf/directive'):
|
|
if aug.get(directive) != 'Alias':
|
|
continue
|
|
|
|
url = aug.get(directive + '/arg[1]')
|
|
if url == url_to_remove:
|
|
aug.remove(directive)
|
|
|
|
for location in aug.match('$conf/Location'):
|
|
url = aug.get(location + '/arg')
|
|
if url == url_to_remove:
|
|
aug.remove(location)
|
|
|
|
aug.save()
|
|
|
|
with action_utils.WebserverChange() as webserver_change:
|
|
webserver_change.enable('sharing-freedombox')
|
|
|
|
|
|
def _get_name_from_url(url):
|
|
"""Return the name of the share given the URL for it."""
|
|
matches = re.match(r'/share/([a-z0-9\-]*)', url)
|
|
if not matches:
|
|
raise ValueError
|
|
|
|
return matches[1]
|
|
|
|
|
|
def _list(aug=None):
|
|
"""List all Apache configuration shares."""
|
|
if not aug:
|
|
aug = load_augeas()
|
|
|
|
shares = []
|
|
|
|
for match in aug.match('$conf/directive'):
|
|
if aug.get(match) != 'Alias':
|
|
continue
|
|
|
|
url = aug.get(match + '/arg[1]')
|
|
path = aug.get(match + '/arg[2]')
|
|
|
|
path = path.removesuffix('"').removeprefix('"')
|
|
path = path.replace(r'\"', '"')
|
|
try:
|
|
name = _get_name_from_url(url)
|
|
shares.append({
|
|
'name': name,
|
|
'path': path,
|
|
'url': '/share/' + name
|
|
})
|
|
except ValueError:
|
|
continue
|
|
|
|
for location in aug.match('$conf/Location'):
|
|
url = aug.get(location + '/arg')
|
|
|
|
try:
|
|
name = _get_name_from_url(url)
|
|
except ValueError:
|
|
continue
|
|
|
|
groups = []
|
|
for group in aug.match(location + '//directive["TKTAuthToken"]/arg'):
|
|
groups.append(aug.get(group))
|
|
|
|
def _is_public():
|
|
"""Must contain the line 'Require all granted'."""
|
|
require = location + '//directive["Require"]'
|
|
return bool(aug.match(require)) and aug.get(
|
|
require +
|
|
'/arg[1]') == 'all' and aug.get(require +
|
|
'/arg[2]') == 'granted'
|
|
|
|
for share in shares:
|
|
if share['name'] == name:
|
|
share['groups'] = groups
|
|
share['is_public'] = _is_public()
|
|
|
|
return shares
|
|
|
|
|
|
@privileged
|
|
def list_shares() -> list[dict[str, object]]:
|
|
"""List all Apache configuration shares and print as JSON."""
|
|
return _list()
|
|
|
|
|
|
@privileged
|
|
def uninstall():
|
|
"""Remove apache config when app is uninstalled."""
|
|
pathlib.Path(APACHE_CONFIGURATION).unlink(missing_ok=True)
|