Skip to content

Commit

Permalink
move secrets to constance
Browse files Browse the repository at this point in the history
  • Loading branch information
saxix committed Oct 23, 2024
1 parent 35c5a47 commit fccbe5a
Show file tree
Hide file tree
Showing 15 changed files with 182 additions and 24 deletions.
37 changes: 28 additions & 9 deletions src/country_workspace/config/fragments/constance.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,36 @@
CONSTANCE_BACKEND = "constance.backends.database.DatabaseBackend"


# CONSTANCE_CONFIG_FIELDSETS = {
# "User settings": {
# "fields": ("NEW_USER_IS_STAFF", "NEW_USER_DEFAULT_GROUP"),
# "collapse": False,
# }
# }

CONSTANCE_ADDITIONAL_FIELDS = {
"email": [
"django.forms.EmailField",
{},
],
"group_select": [
"country_workspace.utils.constance.GroupSelect",
"country_workspace.utils.constance.GroupChoiceField",
{"initial": NEW_USER_DEFAULT_GROUP},
],
"read_only_text": [
"django.forms.fields.CharField",
{
"required": False,
"widget": "country_workspace.utils.constance.ObfuscatedInput",
},
],
"write_only_text": [
"django.forms.fields.CharField",
{
"required": False,
"widget": "country_workspace.utils.constance.WriteOnlyTextarea",
},
],
"write_only_input": [
"django.forms.fields.CharField",
{
"required": False,
"widget": "country_workspace.utils.constance.WriteOnlyInput",
},
],
}

CONSTANCE_CONFIG = {
Expand All @@ -28,5 +42,10 @@
"Group to assign to any new user",
"group_select",
),
"HOPE_API_URL": ("https://hope.unicef.org/api/rest/", "", str),
"HOPE_API_URL": ("https://hope.unicef.org/api/rest/", "HOPE API Server address", str),
"HOPE_API_TOKEN": ("", "HOPE API Access Token", "write_only_input"),
"AURORA_API_URL": ("https://register.unicef.org/api/", "Aurora API Server address", str),
"AURORA_API_TOKEN": ("", "Aurora API Access Token", "write_only_input"),
"KOBO_API_URL": ("", "Kobo API Server address", str),
"KOBO_API_TOKEN": ("", "Kobo API Access Token", "write_only_input"),
}
39 changes: 37 additions & 2 deletions src/country_workspace/utils/constance.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,47 @@
import logging
from typing import Any

from django.forms import ChoiceField
from django.forms import ChoiceField, HiddenInput, Textarea, TextInput
from django.template import Context, Template
from django.utils.safestring import mark_safe

from constance import config

logger = logging.getLogger(__name__)


class GroupSelect(ChoiceField):
class ObfuscatedInput(HiddenInput):

def render(self, name, value, attrs=None, renderer=None):
context = self.get_context(name, value, attrs)
context["value"] = str(value)
context["label"] = "Set" if value else "Not Set"

tpl = Template('<input type="hidden" name="{{ widget.name }}" value="{{ value }}">{{ label }}')
return mark_safe(tpl.render(Context(context))) # nosec B308 B703


class WriteOnlyWidget:
def format_value(self, value):
value = "***"
return super().format_value(value)

def value_from_datadict(self, data, files, name):
value = data.get(name)
if value == "***":
return getattr(config, name)
return value


class WriteOnlyTextarea(WriteOnlyWidget, Textarea):
pass


class WriteOnlyInput(WriteOnlyWidget, TextInput):
pass


class GroupChoiceField(ChoiceField):
def __init__(self, **kwargs: Any) -> None:
from django.contrib.auth.models import Group

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,7 @@
{% endif %}

{% endif %}
{% else %}
<h1>&nbsp;</h1>
{% endif %}
</h1>
1 change: 1 addition & 0 deletions src/country_workspace/workspaces/admin/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .batch import CountryBatchAdmin # noqa
from .household import CountryHouseholdAdmin # noqa
from .individual import CountryIndividualAdmin # noqa
from .job import CountryJobAdmin # noqa
from .program import CountryProgramAdmin # noqa
3 changes: 3 additions & 0 deletions src/country_workspace/workspaces/admin/batch.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import TYPE_CHECKING

from django.contrib.admin import register
from django.db.models import QuerySet
from django.http import HttpRequest
from django.urls import reverse
Expand All @@ -11,6 +12,7 @@
from ..filters import CWLinkedAutoCompleteFilter
from ..models import CountryBatch
from ..options import WorkspaceModelAdmin
from ..sites import workspace
from .hh_ind import SelectedProgramMixin

if TYPE_CHECKING:
Expand All @@ -26,6 +28,7 @@ def queryset(self, request: HttpRequest, queryset: QuerySet) -> QuerySet:
return queryset


@register(CountryBatch, site=workspace)
class CountryBatchAdmin(SelectedProgramMixin, WorkspaceModelAdmin):
list_display = ["name", "program", "country_office"]
search_fields = ("label",)
Expand Down
6 changes: 6 additions & 0 deletions src/country_workspace/workspaces/admin/household.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@
if TYPE_CHECKING:
from ..models import CountryProgram

from django.contrib.admin import register

from ..models import CountryHousehold
from ..sites import workspace


@register(CountryHousehold, site=workspace)
class CountryHouseholdAdmin(BeneficiaryBaseAdmin):
list_display = ["name", "batch"]
search_fields = ("name",)
Expand Down
6 changes: 4 additions & 2 deletions src/country_workspace/workspaces/admin/individual.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
from typing import Any, Optional

from django.contrib.admin import AdminSite
from django.contrib.admin import AdminSite, register
from django.db.models import Model
from django.http import HttpRequest

from ...state import state
from ..filters import HouseholdFilter, ProgramFilter
from ..models import CountryHousehold, CountryIndividual, CountryProgram
from ..sites import workspace
from .hh_ind import BeneficiaryBaseAdmin


@register(CountryIndividual, site=workspace)
class CountryIndividualAdmin(BeneficiaryBaseAdmin):
list_display = [
"name",
Expand All @@ -30,7 +32,7 @@ class CountryIndividualAdmin(BeneficiaryBaseAdmin):
change_form_template = "workspace/individual/change_form.html"
ordering = ("name",)

def __init__(self, model: Model, admin_site: AdminSite):
def __init__(self, model: Model, admin_site: "AdminSite"):
self._selected_household = None
super().__init__(model, admin_site)

Expand Down
21 changes: 21 additions & 0 deletions src/country_workspace/workspaces/admin/job.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from django.contrib.admin import register

from ..models import CountryJob
from ..options import WorkspaceModelAdmin
from ..sites import workspace


@register(CountryJob, site=workspace)
class CountryJobAdmin(WorkspaceModelAdmin):
list_display = (
"name",
"sector",
"status",
"active",
)

def has_add_permission(self, request):
return False


# workspace.register(CountryJob, CountryJobAdmin)
3 changes: 3 additions & 0 deletions src/country_workspace/workspaces/admin/program.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Any

from django import forms
from django.contrib.admin import register
from django.db.models import QuerySet
from django.db.transaction import atomic
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
Expand All @@ -21,6 +22,7 @@
from ...sync.office import sync_programs
from ..models import CountryProgram
from ..options import WorkspaceModelAdmin
from ..sites import workspace
from .forms import ImportFileForm


Expand Down Expand Up @@ -55,6 +57,7 @@ def clean_field_name(v):
return v.replace("_h_c", "").replace("_h_f", "").replace("_i_c", "").replace("_i_f", "").lower()


@register(CountryProgram, site=workspace)
class CountryProgramAdmin(WorkspaceModelAdmin):
list_display = (
"name",
Expand Down
15 changes: 5 additions & 10 deletions src/country_workspace/workspaces/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,8 @@
class Config(AppConfig):
name = __name__.rpartition(".")[0]

def ready(self) -> None:
from country_workspace.workspaces import models

from . import admin
from .sites import workspace

workspace.register(models.CountryBatch, admin.CountryBatchAdmin)
workspace.register(models.CountryHousehold, admin.CountryHouseholdAdmin)
workspace.register(models.CountryIndividual, admin.CountryIndividualAdmin)
workspace.register(models.CountryProgram, admin.CountryProgramAdmin)
# def ready(self) -> None:
# from country_workspace.workspaces import models
#
# from . import admin
# from .sites import workspace
7 changes: 6 additions & 1 deletion src/country_workspace/workspaces/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from hope_flex_fields.models import DataChecker

from country_workspace.models import Batch, Household, Individual, Office, Program
from country_workspace.models import AsyncJob, Batch, Household, Individual, Office, Program

__all__ = ["CountryProgram", "CountryHousehold", "CountryIndividual", "CountryBatch"]

Expand Down Expand Up @@ -45,5 +45,10 @@ class Meta:
class CountryChecker(DataChecker):
country_office = models.ForeignKey(Office, on_delete=models.CASCADE)


class CountryJob(AsyncJob):
class Meta:
proxy = True

# class Meta:
# app_label = "workspaces"
31 changes: 31 additions & 0 deletions tests/utils/test_utils_constance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from constance.test import override_config

from country_workspace.utils.constance import GroupChoiceField, ObfuscatedInput, WriteOnlyInput, WriteOnlyTextarea


def test_utils_groupchoicefield():
field = GroupChoiceField()
assert field


# LdapDNField


# ObfuscatedInput
def test_obfuscatedinput():
field = ObfuscatedInput()
assert field.render("name", "value") == '<input type="hidden" name="name" value="value">Set'


# WriteOnlyTextarea
def test_writeonlytextarea():
field = WriteOnlyTextarea()
assert field.render("name", "value") == '<textarea name="name" cols="40" rows="10">\n***</textarea>'


@override_config(HOPE_API_TOKEN="abc")
def test_writeonlyinput():
field = WriteOnlyInput()
assert field.render("name", "value")
assert field.value_from_datadict({"HOPE_API_TOKEN": "***"}, {}, "HOPE_API_TOKEN") == "abc"
assert field.value_from_datadict({"HOPE_API_TOKEN": "123"}, {}, "HOPE_API_TOKEN") == "123"
File renamed without changes.
File renamed without changes.
35 changes: 35 additions & 0 deletions tests/workspace/test_ws_autocmplete.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from django.urls import reverse

import pytest
from django_webtest import DjangoTestApp
from django_webtest.pytest_plugin import MixinWithInstanceVariables
from responses import RequestsMock
from testutils.utils import select_office


@pytest.fixture()
def program():
from testutils.factories import CountryProgramFactory

return CountryProgramFactory()


@pytest.fixture()
def app(django_app_factory: "MixinWithInstanceVariables", mocked_responses: "RequestsMock") -> "DjangoTestApp":
from testutils.factories.user import SuperUserFactory

django_app = django_app_factory(csrf_checks=False)
admin_user = SuperUserFactory(username="superuser")
django_app.set_user(admin_user)
django_app._user = admin_user
return django_app


def test_project_autocomplete(app: DjangoTestApp, program) -> None:
url = reverse("workspace:autocomplete")
with select_office(app, program.country_office):
res = app.get(url, expect_errors=True)
assert res.status_code == 403

res = app.get(f"{url}?app_label=country_workspace&model_name=batch&field_name=program")
assert res.status_code == 200

0 comments on commit fccbe5a

Please sign in to comment.