Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ajout de l'authentification à deux facteurs #63

Closed
wants to merge 6 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ HOST_PROTO=http
HOST_URL=localhost # use 0.0.0.0 for Docker
ALLOWED_HOSTS=localhost, 0.0.0.0
HOST_PORT=8000
SITE_NAME=Nom du site

USE_DOCKER=0 # Set 1 to use Docker

@@ -19,3 +20,5 @@ S3_KEY_ID=
S3_KEY_SECRET=
S3_BUCKET_NAME=
S3_BUCKET_REGION=eu-west-3

WAGTAIL_2FA_REQUIRED=False
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -33,7 +33,7 @@ RUN poetry install

COPY --chown=app:app . .

RUN poetry run python manage.py collectstatic --no-input
RUN poetry run python manage.py collectstatic --no-input --ignore=*.sass

USER app

11 changes: 10 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -18,6 +18,15 @@ web-prompt:
test-unit:
$(EXEC_CMD) poetry run python manage.py test --settings config.settings_test

.PHONY: collectstatic
collectstatic:
$(EXEC_CMD) poetry run python manage.py collectstatic --noinput --ignore=*.sass

.PHONY: sass
sass:
$(EXEC_CMD) poetry run python manage.py compilescss
make collectstatic

.PHONY: quality
quality:
$(EXEC_CMD) poetry run black --check --exclude=venv .
@@ -35,7 +44,7 @@ init:
$(EXEC_CMD) poetry install
$(EXEC_CMD) poetry run pre-commit install
$(EXEC_CMD) poetry run python manage.py migrate
$(EXEC_CMD) poetry run python manage.py collectstatic --noinput
make collectstatic
$(EXEC_CMD) poetry run python manage.py create_sample_pages

.PHONY: runserver
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -87,3 +87,11 @@ Vous pouvez également générer un rapport sur la couverture de tests :
```sh
coverage run manage.py test --settings config.settings_test
```

## Configurer l’authentification à deux facteurs (2FA)
Pour activer l’authentification à deux facteurs sur le site, il faut passer la variable `WAGTAIL_2FA_REQUIRED` à True et remplir la variable `SITE_NAME` si elle ne l’est pas déjà.

```
SITE_NAME=Nom du site
WAGTAIL_2FA_REQUIRED=True
```
14 changes: 11 additions & 3 deletions config/settings.py
Original file line number Diff line number Diff line change
@@ -64,6 +64,9 @@
"dsfr",
"sass_processor",
"content_manager",
"wagtail_2fa",
"django_otp",
"django_otp.plugins.otp_totp",
]

if DEBUG:
@@ -77,6 +80,7 @@
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"wagtail_2fa.middleware.VerifyUserMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"wagtail.contrib.redirects.middleware.RedirectMiddleware",
@@ -158,9 +162,9 @@
# https://whitenoise.evans.io/en/latest/

STATICFILES_FINDERS = [
"django.contrib.staticfiles.finders.FileSystemFinder",
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
"sass_processor.finders.CssFinder",
"django.contrib.staticfiles.finders.FileSystemFinder",
]

# S3 uploads & MEDIA CONFIGURATION
@@ -179,7 +183,8 @@
MEDIA_URL = "medias/"

# Django Sass
SASS_PROCESSOR_ROOT = os.path.join(BASE_DIR, "static")
SASS_PROCESSOR_ROOT = os.path.join(BASE_DIR, "static/css")
SASS_PROCESSOR_AUTO_INCLUDE = False

STATIC_URL = "static/"
STATIC_ROOT = "staticfiles"
@@ -194,7 +199,7 @@
# Wagtail settings
# https://docs.wagtail.org/en/stable/reference/settings.html

WAGTAIL_SITE_NAME = "Gestionnaire de contenu avec le Système de Design de l'État"
WAGTAIL_SITE_NAME = os.getenv("SITE_NAME", "Gestionnaire de contenu avec le Système de Design de lÉtat")

# Base URL to use when referring to full URLs within the Wagtail admin backend -
# e.g. in notification emails. Don't include '/admin' or a trailing slash
@@ -227,3 +232,6 @@
CSRF_TRUSTED_ORIGINS = []
for host in ALLOWED_HOSTS:
CSRF_TRUSTED_ORIGINS.append("https://" + host)

# 2FA, see https://wagtail-2fa.readthedocs.io/en/stable/
WAGTAIL_2FA_REQUIRED = os.getenv("WAGTAIL_2FA_REQUIRED", False)
36 changes: 19 additions & 17 deletions content_manager/blocks.py
Original file line number Diff line number Diff line change
@@ -60,7 +60,7 @@ class AlertBlock(blocks.StructBlock):
class BadgeBlock(blocks.StructBlock):
text = blocks.CharBlock(label="Texte du badge", required=False)
color = blocks.ChoiceBlock(label="Couleur de badge", choices=badge_level_choices, required=False)
hide_icon = blocks.BooleanBlock(label="Masquer l'icon du badge", required=False)
hide_icon = blocks.BooleanBlock(label="Masquer l’icône du badge", required=False)


class BadgesListBlock(blocks.StreamBlock):
@@ -87,24 +87,26 @@ class CardBlock(blocks.StructBlock):
label="ou Document",
help_text=(
"Sélectionnez un document pour rendre la carte cliquable vers "
"celui ci (si le champ `Lien` n'est pas renseigné)."
"celui ci (si le champ « Lien » n’est pas renseigné)."
),
required=False,
)


class HeroBlock(blocks.StructBlock):
bg_image = ImageChooserBlock(label="Image d'arrière plan")
bg_image = ImageChooserBlock(label="Image darrière plan")
bg_color = blocks.RegexBlock(
label="Couleur d'arrière plan au format hexa (Ex: #f5f5fe)",
label="Couleur darrière plan au format hexa (Ex: #f5f5fe)",
regex=r"^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$",
error_messages={"invalid": "La couleur n'est pas correcte, le format doit être #fff ou #f5f5fe"},
error_messages={"invalid": "La couleur nest pas correcte, le format doit être #fff ou #f5f5fe"},
required=False,
)
title = blocks.CharBlock(label="Titre")
text = blocks.CharBlock(label="Texte", required=False)
cta_label = blocks.CharBlock(label="Texte du bouton", required=False)
cta_link = blocks.URLBlock(label="Lien du bouton", required=False)
large = blocks.BooleanBlock(label="Large", required=False)
darken = blocks.BooleanBlock(label="Assombrir", required=False)


class IframeBlock(blocks.StructBlock):
@@ -122,7 +124,7 @@ class IframeBlock(blocks.StructBlock):
class ImageAndTextBlock(blocks.StructBlock):
image = ImageChooserBlock(label="Illustration (à gauche)")
image_ratio = blocks.ChoiceBlock(
label="Largeur de l'image",
label="Largeur de limage",
choices=[
("3", "3/12"),
("5", "5/12"),
@@ -141,16 +143,16 @@ class ImageAndTextBlock(blocks.StructBlock):
class ImageBlock(blocks.StructBlock):
title = blocks.CharBlock(label="Titre", required=False)
image = ImageChooserBlock(label="Illustration")
alt = blocks.CharBlock(label="Texte alternatif (description textuelle de l'image)", required=False)
alt = blocks.CharBlock(label="Texte alternatif (description textuelle de limage)", required=False)
caption = blocks.CharBlock(label="Légende", required=False)
url = blocks.URLBlock(label="Lien", required=False)


class QuoteBlock(blocks.StructBlock):
image = ImageChooserBlock(label="Illustration (à gauche)", required=False)
quote = blocks.CharBlock(label="Citation")
author_name = blocks.CharBlock(label="Nom de l'auteur")
author_title = blocks.CharBlock(label="Titre de l'auteur")
author_name = blocks.CharBlock(label="Nom de lauteur")
author_title = blocks.CharBlock(label="Titre de lauteur")


class SeparatorBlock(blocks.StructBlock):
@@ -159,7 +161,7 @@ class SeparatorBlock(blocks.StructBlock):


class StepBlock(blocks.StructBlock):
title = blocks.CharBlock(label="Titre de l'étape")
title = blocks.CharBlock(label="Titre de létape")
detail = blocks.TextBlock(label="Détail")


@@ -169,15 +171,15 @@ class StepsListBlock(blocks.StreamBlock):

class StepperBlock(blocks.StructBlock):
title = blocks.CharBlock(label="Titre")
total = blocks.IntegerBlock(label="Nombre d'étape")
total = blocks.IntegerBlock(label="Nombre d’étapes")
current = blocks.IntegerBlock(label="Étape en cours")
steps = StepsListBlock(label="Les étapes")


class TextAndCTA(blocks.StructBlock):
text = blocks.RichTextBlock(label="Texte avec mise en forme", required=False)
cta_label = blocks.CharBlock(
label="Titre de l'appel à l'action",
label="Titre de lappel à laction",
help_text="Le lien apparait comme un bouton sous le bloc de texte",
required=False,
)
@@ -194,7 +196,7 @@ class VideoBlock(blocks.StructBlock):
caption = blocks.CharBlock(label="Légende")
url = blocks.URLBlock(
label="Lien de la vidéo",
help_text="URL au format 'embed' (Ex. : https://www.youtube.com/embed/gLzXOViPX-0)",
help_text="URL au format « embed » (Ex. : https://www.youtube.com/embed/gLzXOViPX-0)",
)


@@ -205,16 +207,16 @@ class MultiColumnsBlock(blocks.StreamBlock):
video = VideoBlock(label="Vidéo")
card = CardBlock(label="Carte")
quote = QuoteBlock(label="Citation")
text_cta = TextAndCTA(label="Texte et appel à l'action")
text_cta = TextAndCTA(label="Texte et appel à laction")
iframe = IframeBlock(label="Cadre intégré")


class MultiColumnsWithTitleBlock(blocks.StructBlock):
bg_image = ImageChooserBlock(label="Image d'arrière plan", required=False)
bg_image = ImageChooserBlock(label="Image darrière plan", required=False)
bg_color = blocks.RegexBlock(
label="Couleur d'arrière plan au format hexa (Ex: #f5f5fe)",
label="Couleur darrière plan au format hexa (Ex: #f5f5fe)",
regex=r"^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$",
error_messages={"invalid": "La couleur n'est pas correcte, le format doit être #fff ou #f5f5fe"},
error_messages={"invalid": "La couleur nest pas correcte, le format doit être #fff ou #f5f5fe"},
required=False,
)
title = blocks.CharBlock(label="Titre", required=False)
456 changes: 456 additions & 0 deletions content_manager/migrations/0010_alter_contentpage_body.py

Large diffs are not rendered by default.

12 changes: 7 additions & 5 deletions content_manager/templates/content_manager/blocks/hero.html
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
{% load static wagtailimages_tags %}
{% image block.value.bg_image original as bg_img %}
<div class="hero"
style="background: url({{ bg_img.url }}) {{ block.value.bg_color }} no-repeat center">
<div class="cmsfr-hero{% if block.value.large %} cmsfr-hero-large{% endif %}{% if block.value.darken %} cmsfr-hero-dark{% endif %}"
style="background-image: {% if block.value.darken %}linear-gradient(rgba(0,0,0,0.7), rgba(0,0,0,0.7)), {% endif %}url({{ bg_img.url }});
background-color: {{ block.value.bg_color }};
">
<div class="fr-container fr-py-14w">
<div class="fr-grid-row fr-grid-row--gutters">
<div class="fr-col fr-col-12 fr-col-md-6">
<h1>{{ block.value.title }}</h1>
<div class="fr-col fr-col-12{% if not block.value.large %}fr-col-md-6{% endif %}">
<h1{% if block.value.large%} class="fr-display--sm"{% endif %}>{{ block.value.title }}</h1>
{% if block.value.text %}<p>{{ block.value.text }}</p>{% endif %}
{% if block.value.cta_link and block.value.cta_label %}
<a class="fr-btn fr-btn--primary fr-btn--lg"
<a class="fr-btn {% if block.value.darken %}fr-btn--secondary fr-background-alt--blue-france fr-btn--lg{% else %}fr-btn--primary{% endif %} fr-btn--lg"
href="{{ block.value.cta_link }}">{{ block.value.cta_label }}</a>
{% endif %}
</div>
169 changes: 168 additions & 1 deletion poetry.lock
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@ wagtailmenus = "^3.1.9"
boto3 = "^1.29.1"
django-storages = "^1.14.2"
wagtail-modeladmin = "^1.0.0"
wagtail-2fa = "^1.6.6"


[tool.poetry.group.dev.dependencies]
@@ -37,6 +38,8 @@ pre-commit = "^3.5.0"
djlint = "^1.34.0"
django-extensions = "^3.2.3"
ipython = "^8.18.1"
libsass = "^0.22.0"
django-compressor = "^4.4"

[build-system]
requires = ["poetry-core"]
14 changes: 13 additions & 1 deletion static/css/style.css
Original file line number Diff line number Diff line change
@@ -1,2 +1,14 @@
/* Prefix the classes defined here with cmsfr- */
.cmsfr-hero {
background-size: cover;
background-position: center;
background-repeat: no-repeat; }

/*# sourceMappingURL=style.css.map */
.cmsfr-hero-large {
display: flex;
justify-content: center;
align-items: center;
text-align: center; }

.cmsfr-hero-dark h1, .cmsfr-hero-dark p {
color: white; }
16 changes: 16 additions & 0 deletions static/css/style.sass
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* Prefix the classes defined here with cmsfr- */

.cmsfr-hero
background-size: cover
background-position: center
background-repeat: no-repeat

.cmsfr-hero-large
display: flex
justify-content: center
align-items: center
text-align: center

.cmsfr-hero-dark
& h1, p
color: white
9 changes: 6 additions & 3 deletions templates/base.html
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
{% load static dsfr_tags wagtailuserbar wagtailsettings_tags %}
{% load static dsfr_tags wagtailuserbar wagtailsettings_tags sass_tags %}
{% get_settings %}
<!doctype html>
<html lang="fr" data-fr-scheme="system" {% if settings.content_manager.CmsDsfrConfig.mourning %}data-fr-mourning{% endif %}>

<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
{% block title %}<title>{% if title %}{{ title }} — {% endif %}{{ settings.content_manager.CmsDsfrConfig.site_title }}</title>{% endblock title %}
{% dsfr_favicon %}

{% dsfr_css %}
{% dsfr_favicon %}
<link href="{% static 'dsfr/dist/utility/utility.min.css' %}" rel="stylesheet" type="text/css" />
<link href="{% sass_src 'css/style.sass' %}" rel="stylesheet" type="text/css" />

{% block extra_css %}{% endblock extra_css %}

{% block title %}<title>{% if title %}{{ title }} — {% endif %}{{ settings.content_manager.CmsDsfrConfig.site_title }}</title>{% endblock title %}
{% block tracking %}
{% if settings.content_manager.AnalyticsSettings.head_scripts %}
{{settings.content_manager.AnalyticsSettings.head_scripts|safe}}