diff --git a/docker-compose.yml b/docker-compose.yml
index 33d12b7..051b4d3 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -21,22 +21,30 @@ services:
image: quay.io/keycloak/keycloak:26.0
container_name: ecosplay-auth-keycloak
restart: unless-stopped
- command: start-dev
+ command: ["start", "--import-realm"]
environment:
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: keycloak
- KC_HOSTNAME: localhost
+ KC_HOSTNAME: https://auth.e-cosplay.fr
KC_HTTP_ENABLED: "true"
+ KC_PROXY_HEADERS: xforwarded
KC_HEALTH_ENABLED: "true"
KC_METRICS_ENABLED: "true"
- KEYCLOAK_ADMIN: admin
- KEYCLOAK_ADMIN_PASSWORD: admin
+ KC_BOOTSTRAP_ADMIN_USERNAME: admin
+ KC_BOOTSTRAP_ADMIN_PASSWORD: admin
ports:
- - "9450:8080"
+ - "127.0.0.1:9450:8080"
volumes:
- ./themes/ecosplay:/opt/keycloak/themes/ecosplay:ro
+ - ./realms:/opt/keycloak/data/import:ro
+ healthcheck:
+ test: ["CMD-SHELL", "exec 3<>/dev/tcp/localhost/9000 && printf 'GET /health/ready HTTP/1.0\\r\\nHost: localhost\\r\\n\\r\\n' >&3 && grep -q UP <&3"]
+ interval: 10s
+ timeout: 5s
+ retries: 30
+ start_period: 120s
depends_on:
postgres:
condition: service_healthy
@@ -48,7 +56,7 @@ services:
container_name: ecosplay-auth-init
depends_on:
keycloak:
- condition: service_started
+ condition: service_healthy
environment:
KC_SERVER: http://keycloak:8080
KC_ADMIN: admin
diff --git a/realms/ecosplay-realm.json b/realms/ecosplay-realm.json
new file mode 100644
index 0000000..08b3cdb
--- /dev/null
+++ b/realms/ecosplay-realm.json
@@ -0,0 +1,85 @@
+{
+ "realm": "ecosplay",
+ "displayName": "E-Cosplay",
+ "displayNameHtml": "E-Cosplay",
+ "enabled": true,
+
+ "loginTheme": "ecosplay",
+ "accountTheme": "ecosplay",
+ "emailTheme": "ecosplay",
+ "adminTheme": "keycloak.v2",
+
+ "internationalizationEnabled": true,
+ "supportedLocales": ["fr"],
+ "defaultLocale": "fr",
+
+ "registrationAllowed": true,
+ "registrationEmailAsUsername": true,
+ "rememberMe": true,
+ "verifyEmail": true,
+ "loginWithEmailAllowed": true,
+ "duplicateEmailsAllowed": false,
+ "resetPasswordAllowed": true,
+ "editUsernameAllowed": false,
+
+ "bruteForceProtected": true,
+ "permanentLockout": false,
+ "maxFailureWaitSeconds": 900,
+ "minimumQuickLoginWaitSeconds": 60,
+ "waitIncrementSeconds": 60,
+ "quickLoginCheckMilliSeconds": 1000,
+ "maxDeltaTimeSeconds": 43200,
+ "failureFactor": 5,
+
+ "passwordPolicy": "length(10) and specialChars(1) and digits(1) and upperCase(1) and lowerCase(1) and notUsername(undefined) and notEmail(undefined)",
+
+ "accessTokenLifespan": 300,
+ "accessTokenLifespanForImplicitFlow": 900,
+ "ssoSessionIdleTimeout": 1800,
+ "ssoSessionMaxLifespan": 36000,
+ "offlineSessionIdleTimeout": 2592000,
+ "actionTokenGeneratedByUserLifespan": 900,
+
+ "smtpServer": {
+ "host": "email-smtp.eu-west-3.amazonaws.com",
+ "port": "587",
+ "from": "auth@e-cosplay.fr",
+ "fromDisplayName": "E-Cosplay",
+ "replyTo": "noreply@e-cosplay.fr",
+ "envelopeFrom": "auth@e-cosplay.fr",
+ "auth": "true",
+ "starttls": "true",
+ "ssl": "false",
+ "user": "AKIAWTT2T22CWBRBBDYN",
+ "password": "BBdgb6KxRQ8mNcpWFJsZCJxbSGNdgLhKFiITMErfBlQP"
+ },
+
+ "clients": [
+ {
+ "clientId": "ecosplay-web",
+ "name": "E-Cosplay Web",
+ "description": "Application web principale e-cosplay.fr",
+ "enabled": true,
+ "publicClient": false,
+ "secret": "change-me-in-admin-console",
+ "redirectUris": [
+ "https://www.e-cosplay.fr/*",
+ "https://e-cosplay.fr/*"
+ ],
+ "webOrigins": [
+ "https://www.e-cosplay.fr",
+ "https://e-cosplay.fr"
+ ],
+ "protocol": "openid-connect",
+ "standardFlowEnabled": true,
+ "implicitFlowEnabled": false,
+ "directAccessGrantsEnabled": false,
+ "serviceAccountsEnabled": false,
+ "frontchannelLogout": true,
+ "attributes": {
+ "post.logout.redirect.uris": "https://www.e-cosplay.fr/*##https://e-cosplay.fr/*",
+ "pkce.code.challenge.method": "S256"
+ }
+ }
+ ]
+}
diff --git a/themes/ecosplay/account/resources/css/account.css b/themes/ecosplay/account/resources/css/account.css
new file mode 100644
index 0000000..ebd7c7e
--- /dev/null
+++ b/themes/ecosplay/account/resources/css/account.css
@@ -0,0 +1,228 @@
+/* ============================================================
+ E-Cosplay neo-brutalist overlay for the v2 (PatternFly) account console
+ ============================================================ */
+
+:root {
+ --pf-v5-global--primary-color--100: #4f46e5;
+ --pf-v5-global--primary-color--200: #4338ca;
+ --pf-v5-global--primary-color--light-100: #6366f1;
+ --pf-v5-global--link--Color: #4f46e5;
+ --pf-v5-global--link--Color--hover: #111827;
+ --pf-v5-global--BackgroundColor--100: #fbfbfb;
+ --pf-v5-global--BackgroundColor--200: #ffffff;
+ --pf-v5-global--BorderColor--100: #111827;
+ --pf-v5-global--FontFamily--text: ui-sans-serif, system-ui, -apple-system,
+ "Segoe UI", Roboto, sans-serif;
+ --pf-v5-global--FontFamily--heading: ui-sans-serif, system-ui, -apple-system,
+ "Segoe UI", Roboto, sans-serif;
+ --pf-v5-global--FontFamily--monospace: ui-monospace, "SFMono-Regular",
+ Menlo, monospace;
+ --pf-v5-global--FontWeight--bold: 900;
+ --pf-v5-global--BorderRadius--sm: 0;
+ --pf-v5-global--BorderRadius--lg: 0;
+}
+
+html,
+body,
+.pf-v5-c-page,
+.pf-v5-c-page__main {
+ background-color: #fbfbfb !important;
+ font-style: italic;
+}
+
+/* Header band */
+.pf-v5-c-masthead {
+ background-color: #111827 !important;
+ border-bottom: 4px solid #4f46e5 !important;
+}
+
+.pf-v5-c-masthead__brand,
+.pf-v5-c-masthead__main {
+ color: #ffffff !important;
+ font-weight: 900 !important;
+ text-transform: uppercase !important;
+ letter-spacing: 0.15em !important;
+ font-style: italic !important;
+}
+
+/* Cards */
+.pf-v5-c-card {
+ background-color: #ffffff !important;
+ border: 4px solid #111827 !important;
+ border-radius: 0 !important;
+ box-shadow: 8px 8px 0 rgba(0, 0, 0, 1) !important;
+}
+
+.pf-v5-c-card__title,
+.pf-v5-c-card__title-text {
+ text-transform: uppercase !important;
+ font-style: italic !important;
+ font-weight: 900 !important;
+ letter-spacing: -0.02em !important;
+}
+
+/* Headings */
+h1,
+h2,
+h3,
+.pf-v5-c-title,
+.pf-v5-c-content h1,
+.pf-v5-c-content h2,
+.pf-v5-c-content h3 {
+ text-transform: uppercase !important;
+ font-style: italic !important;
+ font-weight: 900 !important;
+ letter-spacing: -0.025em !important;
+ color: #111827 !important;
+}
+
+/* Form labels */
+.pf-v5-c-form__label,
+.pf-v5-c-form__label-text {
+ text-transform: uppercase !important;
+ font-weight: 900 !important;
+ letter-spacing: 0.1em !important;
+ font-size: 11px !important;
+ color: #111827 !important;
+ font-style: normal !important;
+}
+
+/* Inputs */
+.pf-v5-c-form-control,
+.pf-v5-c-form-control input,
+input.pf-v5-c-form-control,
+.pf-v5-c-text-input-group__text-input {
+ border: 4px solid #111827 !important;
+ border-radius: 0 !important;
+ background-color: #ffffff !important;
+ color: #111827 !important;
+ font-weight: 700 !important;
+ font-style: normal !important;
+ padding: 12px 14px !important;
+ height: auto !important;
+}
+
+.pf-v5-c-form-control:focus,
+.pf-v5-c-form-control:focus-within {
+ background-color: #fef9c3 !important;
+ box-shadow: 6px 6px 0 #4f46e5 !important;
+ outline: none !important;
+}
+
+/* Buttons */
+.pf-v5-c-button {
+ border-radius: 0 !important;
+ border: 4px solid #111827 !important;
+ font-weight: 900 !important;
+ text-transform: uppercase !important;
+ letter-spacing: 0.12em !important;
+ font-style: italic !important;
+ padding: 14px 24px !important;
+ transition: all 0.15s ease !important;
+}
+
+.pf-v5-c-button.pf-m-primary {
+ background-color: #4f46e5 !important;
+ color: #ffffff !important;
+ box-shadow: 6px 6px 0 rgba(0, 0, 0, 1) !important;
+}
+
+.pf-v5-c-button.pf-m-primary:hover {
+ background-color: #4338ca !important;
+ box-shadow: none !important;
+ transform: translate(4px, 4px) !important;
+}
+
+.pf-v5-c-button.pf-m-secondary {
+ background-color: #ffffff !important;
+ color: #111827 !important;
+ box-shadow: 6px 6px 0 rgba(0, 0, 0, 1) !important;
+}
+
+.pf-v5-c-button.pf-m-secondary:hover {
+ background-color: #facc15 !important;
+ box-shadow: none !important;
+ transform: translate(4px, 4px) !important;
+}
+
+.pf-v5-c-button.pf-m-danger {
+ background-color: #dc2626 !important;
+ color: #ffffff !important;
+ box-shadow: 6px 6px 0 rgba(0, 0, 0, 1) !important;
+}
+
+.pf-v5-c-button.pf-m-link {
+ color: #4f46e5 !important;
+ border: 0 !important;
+ box-shadow: none !important;
+ text-decoration: underline !important;
+ text-underline-offset: 4px !important;
+ text-decoration-thickness: 3px !important;
+ padding: 4px 8px !important;
+}
+
+.pf-v5-c-button.pf-m-link:hover {
+ color: #111827 !important;
+ transform: none !important;
+}
+
+/* Nav */
+.pf-v5-c-nav__link {
+ font-weight: 900 !important;
+ text-transform: uppercase !important;
+ letter-spacing: 0.1em !important;
+ font-style: italic !important;
+ border-left: 4px solid transparent !important;
+ border-radius: 0 !important;
+}
+
+.pf-v5-c-nav__link.pf-m-current,
+.pf-v5-c-nav__link:hover {
+ border-left-color: #4f46e5 !important;
+ background-color: #fef9c3 !important;
+ color: #111827 !important;
+}
+
+/* Alerts */
+.pf-v5-c-alert {
+ border: 4px solid #111827 !important;
+ border-radius: 0 !important;
+ box-shadow: 6px 6px 0 rgba(0, 0, 0, 1) !important;
+ font-weight: 800 !important;
+}
+
+.pf-v5-c-alert.pf-m-success {
+ background-color: #bbf7d0 !important;
+}
+.pf-v5-c-alert.pf-m-warning {
+ background-color: #fde68a !important;
+}
+.pf-v5-c-alert.pf-m-danger {
+ background-color: #fecaca !important;
+}
+.pf-v5-c-alert.pf-m-info {
+ background-color: #c7d2fe !important;
+}
+
+/* Tables */
+.pf-v5-c-table {
+ border: 4px solid #111827 !important;
+}
+
+.pf-v5-c-table thead th {
+ background-color: #111827 !important;
+ color: #ffffff !important;
+ text-transform: uppercase !important;
+ font-weight: 900 !important;
+ letter-spacing: 0.1em !important;
+ font-style: italic !important;
+}
+
+/* Links */
+a {
+ color: #4f46e5 !important;
+}
+a:hover {
+ color: #111827 !important;
+ text-decoration-thickness: 3px !important;
+}
diff --git a/themes/ecosplay/account/theme.properties b/themes/ecosplay/account/theme.properties
new file mode 100644
index 0000000..1e0cd6e
--- /dev/null
+++ b/themes/ecosplay/account/theme.properties
@@ -0,0 +1,4 @@
+parent=keycloak.v2
+import=common/keycloak
+locales=fr,en
+styles=css/account.css
diff --git a/themes/ecosplay/email/html/template.ftl b/themes/ecosplay/email/html/template.ftl
new file mode 100644
index 0000000..72626bf
--- /dev/null
+++ b/themes/ecosplay/email/html/template.ftl
@@ -0,0 +1,52 @@
+<#macro emailLayout>
+
+
+
+
+
+ E-Cosplay
+
+
+
+
+
+
+
+
+
+ |
+ // Authentification // E-Cosplay
+ |
+
+
+
+
+ |
+ // Connexion sécurisée
+
+ <#nested>
+
+ |
+
+
+
+
+ |
+ Communauté Inclusive // Hauts-de-France
+ |
+
+
+
+
+
+ © E-Cosplay · www.e-cosplay.fr
+ Cet email vous a été envoyé suite à une action sur votre compte. Si vous n'êtes pas à l'origine de cette demande, ignorez ce message.
+ |
+
+
+ |
+
+
+
+
+#macro>
diff --git a/themes/ecosplay/email/text/template.ftl b/themes/ecosplay/email/text/template.ftl
new file mode 100644
index 0000000..eafbda2
--- /dev/null
+++ b/themes/ecosplay/email/text/template.ftl
@@ -0,0 +1,16 @@
+<#macro emailLayout>
+=========================================================
+ // E-COSPLAY // CONNEXION SECURISEE
+=========================================================
+
+<#nested>
+
+---------------------------------------------------------
+E-Cosplay - Communaute Inclusive - Hauts-de-France
+https://www.e-cosplay.fr
+
+Cet email vous a ete envoye suite a une action sur votre
+compte. Si vous n'etes pas a l'origine de cette demande,
+ignorez ce message.
+---------------------------------------------------------
+#macro>
diff --git a/themes/ecosplay/email/theme.properties b/themes/ecosplay/email/theme.properties
new file mode 100644
index 0000000..d66dbc3
--- /dev/null
+++ b/themes/ecosplay/email/theme.properties
@@ -0,0 +1,2 @@
+parent=keycloak
+locales=fr,en