mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-21 07:55:00 +00:00
privacy: Add new system app for popularity-contest
- Keep the description about app generic - Remove enable/disable option - Create a booleanfield to turn on/off popcon - Don't re-enable popcon during an update Tests: - When enabling/disabling the option, the `"PARTICIPATE"` value in `/etc/popularity-contest.conf` is changed to yes/no as expected. For reference see `/var/lib/dpkg/info/popularity-contest.templates` - When popcon option is enabled, running sudo sh -x /etc/cron.daily/popularity-context shows that execution was successful and data was submitted. Remove files /var/log/popularity-contest* and /var/lib/popularity-contest/lastsub if necessary. Gpg is used and encrypted data is what was submitted. - When popcon option is disabled, running sudo sh -x /etc/cron.daily/popularity-context shows that execution stopped because the option is disabled. Signed-off-by: nbenedek <contact@nbenedek.me> [sunil: Add a notification to tell users about privacy app] [sunil: Correct the URL to /sys] [sunil: Minor code styling changes and updates to description, icon] [sunil: Ensure that popcon works with encryption] [sunil: Write configuration to a separate file] [sunil: Use Shellvars lens instead of Php lns] [sunil: Add functional tests] [sunil: Backup/restore the configuration file] Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
This commit is contained in:
parent
5dd2751514
commit
7e2ebcb743
79
plinth/modules/privacy/__init__.py
Normal file
79
plinth/modules/privacy/__init__.py
Normal file
@ -0,0 +1,79 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""FreedomBox app to the Privacy app."""
|
||||
|
||||
import augeas
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import gettext_noop
|
||||
|
||||
from plinth import app as app_module
|
||||
from plinth import menu
|
||||
from plinth.modules.backups.components import BackupRestore
|
||||
from plinth.package import Packages
|
||||
|
||||
from . import manifest, privileged
|
||||
|
||||
_description = [_('Manage system-wide privacy settings.')]
|
||||
|
||||
|
||||
class PrivacyApp(app_module.App):
|
||||
"""FreedomBox app for Privacy."""
|
||||
|
||||
app_id = 'privacy'
|
||||
|
||||
_version = 1
|
||||
|
||||
can_be_disabled = False
|
||||
|
||||
def __init__(self):
|
||||
"""Create components for the app."""
|
||||
super().__init__()
|
||||
|
||||
info = app_module.Info(app_id=self.app_id, version=self._version,
|
||||
is_essential=True, name=_('Privacy'),
|
||||
icon='fa-eye-slash', description=_description,
|
||||
manual_page=None)
|
||||
self.add(info)
|
||||
|
||||
menu_item = menu.Menu('menu-privacy', info.name,
|
||||
info.short_description, info.icon,
|
||||
'privacy:index', parent_url_name='system')
|
||||
self.add(menu_item)
|
||||
|
||||
packages = Packages('packages-privacy', ['popularity-contest', 'gpg'])
|
||||
self.add(packages)
|
||||
|
||||
backup_restore = BackupRestore('backup-restore-privacy',
|
||||
**manifest.backup)
|
||||
self.add(backup_restore)
|
||||
|
||||
def setup(self, old_version):
|
||||
"""Install and configure the app."""
|
||||
super().setup(old_version)
|
||||
privileged.setup()
|
||||
if old_version == 0:
|
||||
privileged.set_configuration(enable_popcon=True)
|
||||
_show_privacy_notification()
|
||||
|
||||
|
||||
def _show_privacy_notification():
|
||||
"""Show a notification asking user to review privacy settings."""
|
||||
from plinth.notification import Notification
|
||||
message = gettext_noop(
|
||||
'Please update privacy settings to match your preferences.')
|
||||
data = {
|
||||
'app_name': 'translate:' + gettext_noop('Privacy'),
|
||||
'app_icon': 'fa-eye-slash'
|
||||
}
|
||||
title = gettext_noop('Review privacy setting')
|
||||
actions_ = [{
|
||||
'type': 'link',
|
||||
'class': 'primary',
|
||||
'text': gettext_noop('Go to {app_name}'),
|
||||
'url': 'privacy:index'
|
||||
}, {
|
||||
'type': 'dismiss'
|
||||
}]
|
||||
Notification.update_or_create(id='privacy-review', app_id='privacy',
|
||||
severity='info', title=title,
|
||||
message=message, actions=actions_, data=data,
|
||||
group='admin')
|
||||
@ -0,0 +1 @@
|
||||
plinth.modules.privacy
|
||||
24
plinth/modules/privacy/forms.py
Normal file
24
plinth/modules/privacy/forms.py
Normal file
@ -0,0 +1,24 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""FreedomBox privacy app."""
|
||||
|
||||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from plinth import cfg
|
||||
from plinth.utils import format_lazy
|
||||
|
||||
|
||||
class PrivacyForm(forms.Form):
|
||||
"""Privacy configuration form."""
|
||||
|
||||
enable_popcon = forms.BooleanField(
|
||||
label=_('Periodically submit a list of apps used (suggested)'),
|
||||
required=False, help_text=format_lazy(
|
||||
_('Help Debian/{box_name} developers by participating in the '
|
||||
'Popularity Contest package survey program. When enabled, a '
|
||||
'list of apps used on this system will be anonymously submitted '
|
||||
'to Debian every week. Statistics for the data collected are '
|
||||
'publicly available at <a href="https://popcon.debian.org/" '
|
||||
'target="_blank">popcon.debian.org</a>. Submission happens over '
|
||||
'the Tor network for additional anonymity if Tor app is enabled.'
|
||||
), box_name=_(cfg.box_name)))
|
||||
6
plinth/modules/privacy/manifest.py
Normal file
6
plinth/modules/privacy/manifest.py
Normal file
@ -0,0 +1,6 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""Application manifest for privacy app."""
|
||||
|
||||
from . import privileged
|
||||
|
||||
backup = {'config': {'files': [str(privileged.CONFIG_FILE)]}}
|
||||
51
plinth/modules/privacy/privileged.py
Normal file
51
plinth/modules/privacy/privileged.py
Normal file
@ -0,0 +1,51 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""Configure Privacy App."""
|
||||
|
||||
import pathlib
|
||||
from typing import Optional
|
||||
|
||||
import augeas
|
||||
|
||||
from plinth.actions import privileged
|
||||
|
||||
CONFIG_FILE = pathlib.Path('/etc/popularity-contest.d/freedombox.conf')
|
||||
|
||||
|
||||
@privileged
|
||||
def setup():
|
||||
"""Create initial popcon configuration."""
|
||||
CONFIG_FILE.parent.mkdir(exist_ok=True)
|
||||
CONFIG_FILE.touch()
|
||||
|
||||
aug = _load_augeas()
|
||||
aug.set('ENCRYPT', 'yes')
|
||||
aug.save()
|
||||
|
||||
|
||||
@privileged
|
||||
def set_configuration(enable_popcon: Optional[bool] = None):
|
||||
"""Update popcon configuration."""
|
||||
aug = _load_augeas()
|
||||
if enable_popcon:
|
||||
aug.set('PARTICIPATE', 'yes')
|
||||
else:
|
||||
aug.set('PARTICIPATE', 'no')
|
||||
|
||||
aug.save()
|
||||
|
||||
|
||||
def get_configuration() -> dict[str, bool]:
|
||||
"""Return if popcon participation is enabled."""
|
||||
aug = _load_augeas()
|
||||
value = aug.get('PARTICIPATE')
|
||||
return {'enable_popcon': (value == 'yes')}
|
||||
|
||||
|
||||
def _load_augeas():
|
||||
"""Initialize Augeas."""
|
||||
aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD +
|
||||
augeas.Augeas.NO_MODL_AUTOLOAD)
|
||||
aug.transform('Shellvars', str(CONFIG_FILE))
|
||||
aug.set('/augeas/context', '/files' + str(CONFIG_FILE))
|
||||
aug.load()
|
||||
return aug
|
||||
0
plinth/modules/privacy/tests/__init__.py
Normal file
0
plinth/modules/privacy/tests/__init__.py
Normal file
59
plinth/modules/privacy/tests/test_functional.py
Normal file
59
plinth/modules/privacy/tests/test_functional.py
Normal file
@ -0,0 +1,59 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""Functional, browser based tests for privacy app."""
|
||||
|
||||
import pytest
|
||||
|
||||
from plinth.tests import functional
|
||||
|
||||
pytestmark = [pytest.mark.system, pytest.mark.privacy]
|
||||
|
||||
|
||||
class TestPrivacyApp(functional.BaseAppTests):
|
||||
"""Tests for privacy app."""
|
||||
|
||||
app_name = 'privacy'
|
||||
has_service = False
|
||||
has_web = False
|
||||
disable_after_tests = False
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def fixture_background(self, session_browser):
|
||||
"""Login, install, and enable the app."""
|
||||
functional.login(session_browser)
|
||||
functional.nav_to_module(session_browser, self.app_name)
|
||||
yield
|
||||
|
||||
def test_enable_disable(self, session_browser):
|
||||
"""Skip test for enabling and disabling the app."""
|
||||
pytest.skip('Can not be disabled')
|
||||
|
||||
@pytest.mark.backups
|
||||
def test_enable_disable_popcon(self, session_browser):
|
||||
"""Test that popcon can be enable/disabled."""
|
||||
functional.change_checkbox_status(session_browser, self.app_name,
|
||||
'id_enable_popcon', 'disabled')
|
||||
functional.change_checkbox_status(session_browser, self.app_name,
|
||||
'id_enable_popcon', 'enabled')
|
||||
assert session_browser.find_by_id('id_enable_popcon').checked
|
||||
functional.change_checkbox_status(session_browser, self.app_name,
|
||||
'id_enable_popcon', 'disabled')
|
||||
assert not session_browser.find_by_id('id_enable_popcon').checked
|
||||
|
||||
@pytest.mark.backups
|
||||
def test_backup_restore(self, session_browser):
|
||||
"""Test that backup and restore operations work on the app."""
|
||||
functional.change_checkbox_status(session_browser, self.app_name,
|
||||
'id_enable_popcon', 'disabled')
|
||||
functional.backup_create(session_browser, self.app_name,
|
||||
'test_' + self.app_name)
|
||||
functional.nav_to_module(session_browser, self.app_name)
|
||||
functional.change_checkbox_status(session_browser, self.app_name,
|
||||
'id_enable_popcon', 'enabled')
|
||||
functional.backup_restore(session_browser, self.app_name,
|
||||
'test_' + self.app_name)
|
||||
functional.nav_to_module(session_browser, self.app_name)
|
||||
assert not session_browser.find_by_id('id_enable_popcon').checked
|
||||
|
||||
def test_uninstall(self, session_browser):
|
||||
"""Skip test for uninstall."""
|
||||
pytest.skip('Essential app')
|
||||
10
plinth/modules/privacy/urls.py
Normal file
10
plinth/modules/privacy/urls.py
Normal file
@ -0,0 +1,10 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""URLs for the Privacy module."""
|
||||
|
||||
from django.urls import re_path
|
||||
|
||||
from .views import PrivacyAppView
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^sys/privacy/$', PrivacyAppView.as_view(), name='index'),
|
||||
]
|
||||
38
plinth/modules/privacy/views.py
Normal file
38
plinth/modules/privacy/views.py
Normal file
@ -0,0 +1,38 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""Views for privacy app."""
|
||||
|
||||
from django.contrib import messages
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from plinth.modules.privacy.forms import PrivacyForm
|
||||
from plinth.views import AppView
|
||||
|
||||
from . import privileged
|
||||
|
||||
|
||||
class PrivacyAppView(AppView):
|
||||
"""Serve configuration page."""
|
||||
|
||||
app_id = 'privacy'
|
||||
form_class = PrivacyForm
|
||||
|
||||
def get_initial(self):
|
||||
"""Return the values to fill in the form."""
|
||||
initial = super().get_initial()
|
||||
initial.update(privileged.get_configuration())
|
||||
return initial
|
||||
|
||||
def form_valid(self, form):
|
||||
"""Change the configurations of Minetest service."""
|
||||
new_config = form.cleaned_data
|
||||
old_config = form.initial
|
||||
|
||||
changes = {}
|
||||
if old_config['enable_popcon'] != new_config['enable_popcon']:
|
||||
changes['enable_popcon'] = new_config['enable_popcon']
|
||||
|
||||
if changes:
|
||||
privileged.set_configuration(**changes)
|
||||
messages.success(self.request, _('Configuration updated'))
|
||||
|
||||
return super().form_valid(form)
|
||||
@ -50,8 +50,8 @@ _site_url = {
|
||||
_sys_modules = [
|
||||
'avahi', 'backups', 'bind', 'cockpit', 'config', 'datetime', 'diagnostics',
|
||||
'dynamicdns', 'firewall', 'letsencrypt', 'names', 'networks', 'pagekite',
|
||||
'performance', 'power', 'security', 'snapshot', 'ssh', 'storage',
|
||||
'upgrades', 'users'
|
||||
'performance', 'power', 'privacy', 'security', 'snapshot', 'ssh',
|
||||
'storage', 'upgrades', 'users'
|
||||
]
|
||||
|
||||
|
||||
|
||||
@ -46,6 +46,7 @@ markers = [
|
||||
"openvpn",
|
||||
"pagekite",
|
||||
"performance",
|
||||
"privacy",
|
||||
"privoxy",
|
||||
"quassel",
|
||||
"radicale",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user