feat(web): i18n asset overrides (#3040)
This allows overriding translation files in folders with lowercase RFC5646 / BCP47 Format language codes. This also fixes an issues where languages which don't expressly match the language code specified due to having a variant will also match the existing codes. Co-authored-by: Amir Zarrinkafsh <nightah@me.com>pull/3107/head^2
parent
ee9ce27ccf
commit
aac4c4772c
|
@ -34,15 +34,15 @@ logged for users.
|
|||
### 4.30.0
|
||||
The following changes occurred in 4.30.0:
|
||||
|
||||
|Previous Key |New Key |
|
||||
|:-----------:|:--------------------:|
|
||||
|host |server.host |
|
||||
|port |server.port |
|
||||
|tls_key |server.tls.key |
|
||||
|tls_cert |server.tls.certificate|
|
||||
|log_level |log.level |
|
||||
|log_file_path|log.file_path |
|
||||
|log_format |log.format |
|
||||
| Previous Key | New Key |
|
||||
|:-------------:|:----------------------:|
|
||||
| host | server.host |
|
||||
| port | server.port |
|
||||
| tls_key | server.tls.key |
|
||||
| tls_cert | server.tls.certificate |
|
||||
| log_level | log.level |
|
||||
| log_file_path | log.file_path |
|
||||
| log_format | log.format |
|
||||
|
||||
_**Please Note:** you can no longer define secrets for providers that you are not using. For example if you're using the
|
||||
[filesystem notifier](./notifier/filesystem.md) you must ensure that the `AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE`
|
||||
|
|
|
@ -108,13 +108,15 @@ Example:
|
|||
```console
|
||||
/config/assets/
|
||||
├── favicon.ico
|
||||
└── logo.png
|
||||
├── logo.png
|
||||
└── locales/<lang>[-[variant]]/<namespace>.json
|
||||
```
|
||||
|
||||
|Asset |File name|
|
||||
|:-----:|:---------------:|
|
||||
|Favicon|favicon.ico |
|
||||
|Logo |logo.png |
|
||||
| Asset | File name |
|
||||
|:-------:|:-------------:|
|
||||
| Favicon | favicon.ico |
|
||||
| Logo | logo.png |
|
||||
| locales | see [locales] |
|
||||
|
||||
### read_buffer_size
|
||||
<div markdown="1">
|
||||
|
@ -242,3 +244,43 @@ you're able to tune these individually depending on your needs.
|
|||
|
||||
If replacing the Logo for your Authelia portal it is recommended to upload a transparent PNG of your desired logo.
|
||||
Authelia will automatically resize the logo to an appropriate size to present in the frontend.
|
||||
|
||||
#### locales
|
||||
|
||||
The locales folder holds folders of internationalization locales. This folder can be utilized to override these locales.
|
||||
They are the names of locales that are returned by the `navigator.langauge` ECMAScript command. These are generally
|
||||
those in the [RFC5646 / BCP47 Format](https://datatracker.ietf.org/doc/html/rfc5646) except the name normalized to
|
||||
lowercase for consistency ease; for example the `en-US` locale should be in the directory `en-us`.
|
||||
|
||||
Each directory has json files which you can explore the format of in the
|
||||
[internal/server/locales](https://github.com/authelia/authelia/tree/master/internal/server/locales) directory on
|
||||
GitHub. The important part is the key names you wish to override. Each file represents a translation namespace. The list
|
||||
of current namespaces are below:
|
||||
|
||||
| Namespace | Purpose |
|
||||
|:---------:|:-------------------:|
|
||||
| portal | Portal translations |
|
||||
|
||||
A full example for the `en-US` locale for the portal namespace is `locales/en-us/portal.json`.
|
||||
|
||||
Languages in browsers are supported in two forms. In their language only form such as `en` for English, and in their
|
||||
variant form such as `en-AU` for English (Australian). If a user has the browser language `en-AU` we automatically load
|
||||
the `en` and `en-AU` languages, where any keys in the `en-AU` language take precedence over the `en` language, and the
|
||||
translations for the `en` language only applying when a translation from `en-AU` is not available.
|
||||
|
||||
List of supported languages and variants:
|
||||
|
||||
| Description | Language | Additional Variants | Location |
|
||||
|:-----------:|:--------:|:-------------------:|:----------------------:|
|
||||
| English | en | N/A | locales/en/portal.json |
|
||||
| Spanish | es | N/A | locales/es/portal.json |
|
||||
| German | de | N/A | locales/de/portal.json |
|
||||
|
||||
_**Important Note** Currently users can only override languages that already exist in this list either by overriding
|
||||
the language itself, or adding a variant form of that language. If you'd like support for another language feel free
|
||||
to make a PR. We also encourage people to make PR's for variants where the difference in the variants is important._
|
||||
|
||||
_**Important Note** Overriding these files will not guarantee any form of stability. Users who planning to utilize these
|
||||
overrides should either check for changes to the files in the
|
||||
[en](https://github.com/authelia/authelia/tree/master/internal/server/locales/en) translation prior to upgrading or PR
|
||||
their translation to ensure it is maintained._
|
|
@ -2,28 +2,27 @@ package middlewares
|
|||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
||||
// AssetOverrideMiddleware allows overriding and serving of specific embedded assets from disk.
|
||||
func AssetOverrideMiddleware(assetPath string, next fasthttp.RequestHandler) fasthttp.RequestHandler {
|
||||
func AssetOverrideMiddleware(root string, strip int, next fasthttp.RequestHandler) fasthttp.RequestHandler {
|
||||
return func(ctx *fasthttp.RequestCtx) {
|
||||
uri := string(ctx.RequestURI())
|
||||
file := uri[strings.LastIndex(uri, "/")+1:]
|
||||
|
||||
if assetPath != "" && utils.IsStringInSlice(file, validOverrideAssets) {
|
||||
_, err := os.Stat(assetPath + file)
|
||||
if err != nil {
|
||||
next(ctx)
|
||||
} else {
|
||||
fasthttp.FSHandler(assetPath, strings.Count(uri, "/")-1)(ctx)
|
||||
}
|
||||
} else {
|
||||
if root == "" {
|
||||
next(ctx)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
_, err := os.Stat(filepath.Join(root, string(fasthttp.NewPathSlashesStripper(strip)(ctx))))
|
||||
if err != nil {
|
||||
next(ctx)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
fasthttp.FSHandler(root, strip)(ctx)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,6 +57,5 @@ const (
|
|||
)
|
||||
|
||||
var protoHostSeparator = []byte("://")
|
||||
var validOverrideAssets = []string{"favicon.ico", "logo.png"}
|
||||
|
||||
var errPasswordPolicyNoMet = errors.New("the supplied password does not met the security policy")
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
"github.com/valyala/fasthttp/fasthttpadaptor"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/utils"
|
||||
)
|
||||
|
||||
//go:embed locales
|
||||
var locales embed.FS
|
||||
|
||||
//go:embed public_html
|
||||
var assets embed.FS
|
||||
|
||||
func newPublicHTMLEmbeddedHandler() fasthttp.RequestHandler {
|
||||
embeddedPath, _ := fs.Sub(assets, "public_html")
|
||||
|
||||
return fasthttpadaptor.NewFastHTTPHandler(http.FileServer(http.FS(embeddedPath)))
|
||||
}
|
||||
|
||||
func newLocalesEmbeddedHandler() (handler fasthttp.RequestHandler) {
|
||||
var languages []string
|
||||
|
||||
entries, err := locales.ReadDir("locales")
|
||||
if err == nil {
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() && len(entry.Name()) == 2 {
|
||||
languages = append(languages, entry.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return func(ctx *fasthttp.RequestCtx) {
|
||||
var (
|
||||
language, variant, locale, namespace string
|
||||
)
|
||||
|
||||
language = ctx.UserValue("language").(string)
|
||||
namespace = ctx.UserValue("namespace").(string)
|
||||
locale = language
|
||||
|
||||
if v := ctx.UserValue("variant"); v != nil {
|
||||
variant = v.(string)
|
||||
locale = fmt.Sprintf("%s-%s", language, locale)
|
||||
}
|
||||
|
||||
var data []byte
|
||||
|
||||
if data, err = locales.ReadFile(fmt.Sprintf("locales/%s/%s.json", locale, namespace)); err != nil {
|
||||
if variant != "" && utils.IsStringInSliceFold(language, languages) {
|
||||
data = []byte("{}")
|
||||
}
|
||||
|
||||
if len(data) == 0 {
|
||||
hfsHandleErr(ctx, err)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctx.SetContentType("application/json")
|
||||
ctx.SetBody(data)
|
||||
}
|
||||
}
|
||||
|
||||
func hfsHandleErr(ctx *fasthttp.RequestCtx, err error) {
|
||||
switch {
|
||||
case errors.Is(err, fs.ErrNotExist):
|
||||
writeStatus(ctx, fasthttp.StatusNotFound)
|
||||
case errors.Is(err, fs.ErrPermission):
|
||||
writeStatus(ctx, fasthttp.StatusForbidden)
|
||||
default:
|
||||
writeStatus(ctx, fasthttp.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func writeStatus(ctx *fasthttp.RequestCtx, status int) {
|
||||
ctx.SetStatusCode(status)
|
||||
ctx.SetBodyString(fmt.Sprintf("%d %s", status, fasthttp.StatusMessage(status)))
|
||||
}
|
|
@ -9,7 +9,7 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
rootFiles = []string{"favicon.ico", "manifest.json", "robots.txt"}
|
||||
rootFiles = []string{"manifest.json", "robots.txt"}
|
||||
swaggerFiles = []string{
|
||||
"favicon-16x16.png",
|
||||
"favicon-32x32.png",
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"An email has been sent to your address to complete the process": "Es wurde eine E-Mail an Ihre Adresse geschickt, um den Vorgang abzuschließen.",
|
||||
"Authenticated": "Authentifiziert",
|
||||
"Cancel": "Abbrechen",
|
||||
"Contact your administrator to register a device": "Wenden Sie sich an Ihren Administrator, um ein Gerät zu registrieren.",
|
||||
"Could not obtain user settings": "Benutzereinstellungen konnten nicht abgerufen werden",
|
||||
"Done": "Erledigt",
|
||||
"Enter new password": "Neues Passwort eingeben",
|
||||
"Enter one-time password": "Einmal-Passwort eingeben",
|
||||
"Failed to register device, the provided link is expired or has already been used": "Gerät konnte nicht registriert werden, der angegebene Link ist abgelaufen oder wurde bereits verwendet",
|
||||
"Hi": "Hallo",
|
||||
"Incorrect username or password": "Falscher Benutzername oder falsches Passwort.",
|
||||
"Loading": "Laden",
|
||||
"Logout": "Abmelden",
|
||||
"Lost your device?": "Haben Sie Ihr Gerät verloren?",
|
||||
"Methods": "Verfahren",
|
||||
"Need Google Authenticator?": "Benötigen Sie Google Authenticator?",
|
||||
"New password": "Neues Passwort",
|
||||
"No verification token provided": "Kein Verifizierungs-Token vorhanden",
|
||||
"OTP Secret copied to clipboard": "OTP Secret in die Zwischenablage kopiert.",
|
||||
"OTP URL copied to clipboard": "OTP-URL in die Zwischenablage kopiert.",
|
||||
"One-Time Password": "One-Time-Passwort",
|
||||
"Password has been reset": "Das Passwort wurde zurückgesetzt.",
|
||||
"Password": "Passwort",
|
||||
"Passwords do not match": "Die Passwörter stimmen nicht überein.",
|
||||
"Push Notification": "Push-Benachrichtigung",
|
||||
"Register device": "Gerät registrieren",
|
||||
"Register your first device by clicking on the link below": "Registrieren Sie Ihr erstes Gerät, indem Sie auf den unten stehenden Link klicken.",
|
||||
"Remember me": "Angemeldet bleiben",
|
||||
"Repeat new password": "Neues Passwort wiederholen",
|
||||
"Reset password": "Passwort zurücksetzen",
|
||||
"Reset password?": "Passwort zurücksetzen?",
|
||||
"Reset": "Zurücksetzen",
|
||||
"Scan QR Code": "QR-Code scannen",
|
||||
"Secret": "Geheimnis",
|
||||
"Security Key - WebAuthN": "Sicherheitsschlüssel - WebAuthN",
|
||||
"Select a Device": "Gerät auswählen",
|
||||
"Sign in": "Anmelden",
|
||||
"Sign out": "Abmelden",
|
||||
"The resource you're attempting to access requires two-factor authentication": "Die Ressource, auf die Sie zuzugreifen versuchen, erfordert eine Zwei-Faktor-Authentifizierung.",
|
||||
"There was a problem initiating the registration process": "Es gab ein Problem beim Starten des Registrierungsprozesses.",
|
||||
"There was an issue completing the process. The verification token might have expired": "Es gab ein Problem beim Abschluss des Prozesses. Das Verifizierungs-Token könnte abgelaufen sein.",
|
||||
"There was an issue initiating the password reset process": "Es gab ein Problem beim beim Starten des Passwortrücksetzprozesses.",
|
||||
"There was an issue resetting the password": "Es gab ein Problem beim Zurücksetzen des Passworts",
|
||||
"There was an issue signing out": "Es gab ein Problem bei der Abmeldung",
|
||||
"Time-based One-Time Password": "Zeitbasiertes One-Time-Passwort",
|
||||
"Username": "Benutzername",
|
||||
"You must open the link from the same device and browser that initiated the registration process": "Sie müssen den Link mit demselben Gerät und demselben Browser öffnen, mit dem Sie den Registrierungsprozess gestartet haben.",
|
||||
"You're being signed out and redirected": "Sie werden abgemeldet und umgeleitet",
|
||||
"Your supplied password does not meet the password policy requirements": "Ihr angegebenes Passwort entspricht nicht den Anforderungen der Passwortrichtlinie.",
|
||||
"Use OpenID to verify your identity": "Verwenden Sie OpenID, um Ihre Identität zu überprüfen",
|
||||
"Access your display name": "Zugriff auf Ihren Anzeigenamen",
|
||||
"Access your group membership": "Zugriff auf Ihre Gruppenmitgliedschaft",
|
||||
"Access your email addresses": "Zugriff auf Ihre E-Mail-Adressen",
|
||||
"Accept": "Annehmen",
|
||||
"Deny": "Ablehnen",
|
||||
"The above application is requesting the following permissions": "Die oben genannte Anwendung bittet um die folgenden Berechtigungen"
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
{
|
||||
"An email has been sent to your address to complete the process": "An email has been sent to your address to complete the process.",
|
||||
"Authenticated": "Authenticated",
|
||||
"Cancel": "Cancel",
|
||||
"Contact your administrator to register a device": "Contact your administrator to register a device.",
|
||||
"Could not obtain user settings": "Could not obtain user settings",
|
||||
"Done": "Done",
|
||||
"Enter new password": "Enter new password",
|
||||
"Enter one-time password": "Enter one-time password",
|
||||
"Failed to register device, the provided link is expired or has already been used": "Failed to register device, the provided link is expired or has already been used",
|
||||
"Hi": "Hi",
|
||||
"Incorrect username or password": "Incorrect username or password.",
|
||||
"Loading": "Loading",
|
||||
"Logout": "Logout",
|
||||
"Lost your device?": "Lost your device?",
|
||||
"Methods": "Methods",
|
||||
"Need Google Authenticator?": "Need Google Authenticator?",
|
||||
"New password": "New password",
|
||||
"No verification token provided": "No verification token provided",
|
||||
"OTP Secret copied to clipboard": "OTP Secret copied to clipboard.",
|
||||
"OTP URL copied to clipboard": "OTP URL copied to clipboard.",
|
||||
"One-Time Password": "One-Time Password",
|
||||
"Password has been reset": "Password has been reset.",
|
||||
"Password": "Password",
|
||||
"Passwords do not match": "Passwords do not match.",
|
||||
"Push Notification": "Push Notification",
|
||||
"Register device": "Register device",
|
||||
"Register your first device by clicking on the link below": "Register your first device by clicking on the link below.",
|
||||
"Remember me": "Remember me",
|
||||
"Repeat new password": "Repeat new password",
|
||||
"Reset password": "Reset password",
|
||||
"Reset password?": "Reset password?",
|
||||
"Reset": "Reset",
|
||||
"Scan QR Code": "Scan QR Code",
|
||||
"Secret": "Secret",
|
||||
"Security Key - WebAuthN": "Security Key - WebAuthN",
|
||||
"Select a Device": "Select a Device",
|
||||
"Sign in": "Sign in",
|
||||
"Sign out": "Sign out",
|
||||
"The resource you're attempting to access requires two-factor authentication": "The resource you're attempting to access requires two-factor authentication.",
|
||||
"There was a problem initiating the registration process": "There was a problem initiating the registration process",
|
||||
"There was an issue completing the process. The verification token might have expired": "There was an issue completing the process. The verification token might have expired.",
|
||||
"There was an issue initiating the password reset process": "There was an issue initiating the password reset process.",
|
||||
"There was an issue resetting the password": "There was an issue resetting the password",
|
||||
"There was an issue signing out": "There was an issue signing out",
|
||||
"Time-based One-Time Password": "Time-based One-Time Password",
|
||||
"Username": "Username",
|
||||
"You must open the link from the same device and browser that initiated the registration process": "You must open the link from the same device and browser that initiated the registration process",
|
||||
"You're being signed out and redirected": "You're being signed out and redirected",
|
||||
"Your supplied password does not meet the password policy requirements": "Your supplied password does not meet the password policy requirements.",
|
||||
"Use OpenID to verify your identity": "Use OpenID to verify your identity",
|
||||
"Access your display name": "Access your display name",
|
||||
"Access your group membership": "Access your group membership",
|
||||
"Access your email addresses": "Access your email addresses",
|
||||
"Accept": "Accept",
|
||||
"Deny": "Deny",
|
||||
"The above application is requesting the following permissions": "The above application is requesting the following permissions",
|
||||
"The password does not meet the password policy": "The password does not meet the password policy",
|
||||
"Must have at least one lowercase letter": "Must have at least one lowercase letter",
|
||||
"Must have at least one UPPERCASE letter": "Must have at least one UPPERCASE letter",
|
||||
"Must have at least one number": "Must have at least one number",
|
||||
"Must have at least one special character": "Must have at least one special character",
|
||||
"Must be at least {{len}} characters in length": "Must be at least {{len}} characters in length",
|
||||
"Must not be more than {{len}} characters in length": "Must not be more than {{len}} characters in length"
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
{
|
||||
"An email has been sent to your address to complete the process": "Un correo ha sido enviado a su cuenta para completar el proceso",
|
||||
"Authenticated": "Autenticado",
|
||||
"Cancel": "Cancelar",
|
||||
"Contact your administrator to register a device": "Contacte a su administrador para registrar un dispositivo.",
|
||||
"Could not obtain user settings": "Error al obtener configuración de usuario",
|
||||
"Done": "Hecho",
|
||||
"Enter new password": "Ingrese una nueva contraseña",
|
||||
"Enter one-time password": "Ingrese contraseña de un solo uso (OTP)",
|
||||
"Failed to register device, the provided link is expired or has already been used": "Error al registrar dispositivo, el link expiró o ya ha sido utilizado",
|
||||
"Hi": "Hola",
|
||||
"Incorrect username or password": "Usuario y/o contraseña incorrectos",
|
||||
"Loading": "Cargando",
|
||||
"Logout": "Cerrar Sesión",
|
||||
"Lost your device?": "Perdió su dispositivo?",
|
||||
"Methods": "Métodos",
|
||||
"Need Google Authenticator?": "Necesita Google Authenticator?",
|
||||
"New password": "Nueva contraseña",
|
||||
"No verification token provided": "No se ha recibido el token de verificación",
|
||||
"OTP Secret copied to clipboard": "La clave OTP ha sido copiada al portapapeles",
|
||||
"OTP URL copied to clipboard": "la URL OTP ha sido copiada al portapapeles.",
|
||||
"One-Time Password": "Contraseña de un solo uso (OTP)",
|
||||
"Password has been reset": "La contraseña ha sido restablecida.",
|
||||
"Password": "Contraseña",
|
||||
"Passwords do not match": "Las contraseñas no coinciden.",
|
||||
"Push Notification": "Notificaciones Push",
|
||||
"Register device": "Registrar Dispositivo",
|
||||
"Register your first device by clicking on the link below": "Registre su primer dispositivo, haciendo click en el siguiente link.",
|
||||
"Remember me": "Recordarme",
|
||||
"Repeat new password": "Repetir la contraseña",
|
||||
"Reset password": "Restablecer Contraseña",
|
||||
"Reset password?": "Olvidé mi contraseña",
|
||||
"Reset": "Restablecer",
|
||||
"Scan QR Code": "Escanear Código QR",
|
||||
"Secret": "Secreto",
|
||||
"Security Key - WebAuthN": "Llave de Seguridad - WebAuthN",
|
||||
"Select a Device": "Seleccionar Dispositivo",
|
||||
"Sign in": "Iniciar Sesión",
|
||||
"Sign out": "Cerrar Sesión",
|
||||
"The resource you're attempting to access requires two-factor authentication": "El recurso que intenta alcanzar requiere un segundo factor de autenticación (2FA).",
|
||||
"There was a problem initiating the registration process": "Ocurrió un problema al iniciar el proceso de registración",
|
||||
"There was an issue completing the process. The verification token might have expired": "Ocurrió un problema mientras se completaba el proceso. El token de verificación pudo haber expirado.",
|
||||
"There was an issue initiating the password reset process": "Ha ocurrido un error al iniciar el proceso de proceso de restauración de contraseña.",
|
||||
"There was an issue resetting the password": "Ocurrió un error al intentar restablecer la contraseña",
|
||||
"There was an issue signing out": "Ocurrió un error al intentar cerrar sesión",
|
||||
"Time-based One-Time Password": "Contraseña de uso único - OTP",
|
||||
"Username": "Usuario",
|
||||
"You must open the link from the same device and browser that initiated the registration process": "Debe abrir el link desde el mismo dispositivo y navegador desde el que inició el proceso de registración",
|
||||
"You're being signed out and redirected": "Cerrando Sesión y redirigiendo",
|
||||
"Your supplied password does not meet the password policy requirements": "La contraseña suministrada no cumple con los requerimientos de la política de contraseñas",
|
||||
"Use OpenID to verify your identity": "Utilizar OpenID para verificar su identidad",
|
||||
"Access your display name": "Acceso a su nombre",
|
||||
"Access your group membership": "Acceso a su(s) grupo(s)",
|
||||
"Access your email addresses": "Acceso a su dirección de correo",
|
||||
"Accept": "Aceptar",
|
||||
"Deny": "Denegar",
|
||||
"The above application is requesting the following permissions": "La aplicación solicita los siguientes permisos",
|
||||
"The password does not meet the password policy": "La contraseña no cumple con la política de contraseñas",
|
||||
"Must have at least one lowercase letter": "Debe contener al menos una letra minúscula",
|
||||
"Must have at least one UPPERCASE letter": "Debe contener al menos una letra MAYUSCULA",
|
||||
"Must have at least one number": "Debe contener al menos un número",
|
||||
"Must have at least one special character": "Debe contener al menos un caracter especial",
|
||||
"Must be at least {{len}} characters in length": "La longitud mínima es de {{len}} caracteres",
|
||||
"Must not be more than {{len}} characters in length": "La longitud máxima es de {{len}} caracteres"
|
||||
}
|
|
@ -1,10 +1,7 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
@ -13,7 +10,6 @@ import (
|
|||
"github.com/fasthttp/router"
|
||||
"github.com/valyala/fasthttp"
|
||||
"github.com/valyala/fasthttp/expvarhandler"
|
||||
"github.com/valyala/fasthttp/fasthttpadaptor"
|
||||
"github.com/valyala/fasthttp/pprofhandler"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||
|
@ -23,9 +19,6 @@ import (
|
|||
"github.com/authelia/authelia/v4/internal/middlewares"
|
||||
)
|
||||
|
||||
//go:embed public_html
|
||||
var assets embed.FS
|
||||
|
||||
func registerRoutes(configuration schema.Configuration, providers middlewares.Providers) fasthttp.RequestHandler {
|
||||
autheliaMiddleware := middlewares.AutheliaMiddleware(configuration, providers)
|
||||
rememberMe := strconv.FormatBool(configuration.Session.RememberMeDuration != schema.RememberMeDisabled)
|
||||
|
@ -36,8 +29,8 @@ func registerRoutes(configuration schema.Configuration, providers middlewares.Pr
|
|||
duoSelfEnrollment = strconv.FormatBool(configuration.DuoAPI.EnableSelfEnrollment)
|
||||
}
|
||||
|
||||
embeddedPath, _ := fs.Sub(assets, "public_html")
|
||||
embeddedFS := fasthttpadaptor.NewFastHTTPHandler(http.FileServer(http.FS(embeddedPath)))
|
||||
handlerPublicHTML := newPublicHTMLEmbeddedHandler()
|
||||
handlerLocales := newLocalesEmbeddedHandler()
|
||||
|
||||
https := configuration.Server.TLS.Key != "" && configuration.Server.TLS.Certificate != ""
|
||||
|
||||
|
@ -50,17 +43,22 @@ func registerRoutes(configuration schema.Configuration, providers middlewares.Pr
|
|||
r.OPTIONS("/", autheliaMiddleware(handleOPTIONS))
|
||||
|
||||
for _, f := range rootFiles {
|
||||
r.GET("/"+f, middlewares.AssetOverrideMiddleware(configuration.Server.AssetPath, embeddedFS))
|
||||
r.GET("/"+f, handlerPublicHTML)
|
||||
}
|
||||
|
||||
r.GET("/api/", autheliaMiddleware(serveSwaggerHandler))
|
||||
r.GET("/api/"+apiFile, autheliaMiddleware(serveSwaggerAPIHandler))
|
||||
|
||||
for _, file := range swaggerFiles {
|
||||
r.GET("/api/"+file, embeddedFS)
|
||||
r.GET("/api/"+file, handlerPublicHTML)
|
||||
}
|
||||
|
||||
r.GET("/static/{filepath:*}", middlewares.AssetOverrideMiddleware(configuration.Server.AssetPath, embeddedFS))
|
||||
r.GET("/favicon.ico", middlewares.AssetOverrideMiddleware(configuration.Server.AssetPath, 0, handlerPublicHTML))
|
||||
r.GET("/static/media/logo.png", middlewares.AssetOverrideMiddleware(configuration.Server.AssetPath, 2, handlerPublicHTML))
|
||||
r.GET("/static/{filepath:*}", handlerPublicHTML)
|
||||
|
||||
r.GET("/locales/{language:[a-z]{1,3}}-{variant:[a-z0-9-]+}/{namespace:[a-z]+}.json", middlewares.AssetOverrideMiddleware(configuration.Server.AssetPath, 0, handlerLocales))
|
||||
r.GET("/locales/{language:[a-z]{1,3}}/{namespace:[a-z]+}.json", middlewares.AssetOverrideMiddleware(configuration.Server.AssetPath, 0, handlerLocales))
|
||||
|
||||
r.GET("/api/health", autheliaMiddleware(handlers.HealthGet))
|
||||
r.GET("/api/state", autheliaMiddleware(handlers.StateGet))
|
||||
|
|
|
@ -43,7 +43,7 @@ func ServeTemplatedFile(publicDir, file, assetPath, duoSelfEnrollment, rememberM
|
|||
logoOverride := f
|
||||
|
||||
if assetPath != "" {
|
||||
if _, err := os.Stat(assetPath + logoFile); err == nil {
|
||||
if _, err := os.Stat(filepath.Join(assetPath, logoFile)); err == nil {
|
||||
logoOverride = t
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState, useEffect, Suspense } from "react";
|
||||
|
||||
import { config as faConfig } from "@fortawesome/fontawesome-svg-core";
|
||||
import { CssBaseline, ThemeProvider } from "@material-ui/core";
|
||||
|
@ -21,6 +21,7 @@ import { getBasePath } from "@utils/BasePath";
|
|||
import { getDuoSelfEnrollment, getRememberMe, getResetPassword, getTheme } from "@utils/Configuration";
|
||||
import RegisterOneTimePassword from "@views/DeviceRegistration/RegisterOneTimePassword";
|
||||
import RegisterWebauthn from "@views/DeviceRegistration/RegisterWebauthn";
|
||||
import BaseLoadingPage from "@views/LoadingPage/BaseLoadingPage";
|
||||
import ConsentView from "@views/LoginPortal/ConsentView/ConsentView";
|
||||
import LoginPortal from "@views/LoginPortal/LoginPortal";
|
||||
import SignOut from "@views/LoginPortal/SignOut/SignOut";
|
||||
|
@ -60,30 +61,32 @@ const App: React.FC = () => {
|
|||
}, []);
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<NotificationsContext.Provider value={{ notification, setNotification }}>
|
||||
<Router basename={getBasePath()}>
|
||||
<NotificationBar onClose={() => setNotification(null)} />
|
||||
<Routes>
|
||||
<Route path={ResetPasswordStep1Route} element={<ResetPasswordStep1 />} />
|
||||
<Route path={ResetPasswordStep2Route} element={<ResetPasswordStep2 />} />
|
||||
<Route path={RegisterWebauthnRoute} element={<RegisterWebauthn />} />
|
||||
<Route path={RegisterOneTimePasswordRoute} element={<RegisterOneTimePassword />} />
|
||||
<Route path={LogoutRoute} element={<SignOut />} />
|
||||
<Route path={ConsentRoute} element={<ConsentView />} />
|
||||
<Route
|
||||
path={`${IndexRoute}*`}
|
||||
element={
|
||||
<LoginPortal
|
||||
duoSelfEnrollment={getDuoSelfEnrollment()}
|
||||
rememberMe={getRememberMe()}
|
||||
resetPassword={getResetPassword()}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Routes>
|
||||
</Router>
|
||||
</NotificationsContext.Provider>
|
||||
<Suspense fallback={<BaseLoadingPage message={"Loading"} />}>
|
||||
<CssBaseline />
|
||||
<NotificationsContext.Provider value={{ notification, setNotification }}>
|
||||
<Router basename={getBasePath()}>
|
||||
<NotificationBar onClose={() => setNotification(null)} />
|
||||
<Routes>
|
||||
<Route path={ResetPasswordStep1Route} element={<ResetPasswordStep1 />} />
|
||||
<Route path={ResetPasswordStep2Route} element={<ResetPasswordStep2 />} />
|
||||
<Route path={RegisterWebauthnRoute} element={<RegisterWebauthn />} />
|
||||
<Route path={RegisterOneTimePasswordRoute} element={<RegisterOneTimePassword />} />
|
||||
<Route path={LogoutRoute} element={<SignOut />} />
|
||||
<Route path={ConsentRoute} element={<ConsentView />} />
|
||||
<Route
|
||||
path={`${IndexRoute}*`}
|
||||
element={
|
||||
<LoginPortal
|
||||
duoSelfEnrollment={getDuoSelfEnrollment()}
|
||||
rememberMe={getRememberMe()}
|
||||
resetPassword={getResetPassword()}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Routes>
|
||||
</Router>
|
||||
</NotificationsContext.Provider>
|
||||
</Suspense>
|
||||
</ThemeProvider>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,33 +1,28 @@
|
|||
import i18n from "i18next";
|
||||
import LanguageDetector from "i18next-browser-languagedetector";
|
||||
import XHR from "i18next-http-backend";
|
||||
import Backend from "i18next-http-backend";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
|
||||
import langDe from "@i18n/locales/de.json";
|
||||
import langEn from "@i18n/locales/en.json";
|
||||
import langEs from "@i18n/locales/es.json";
|
||||
|
||||
const resources = {
|
||||
en: langEn,
|
||||
es: langEs,
|
||||
de: langDe,
|
||||
};
|
||||
|
||||
const options = {
|
||||
order: ["querystring", "navigator"],
|
||||
lookupQuerystring: "lng",
|
||||
};
|
||||
|
||||
i18n.use(XHR)
|
||||
i18n.use(Backend)
|
||||
.use(LanguageDetector)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
detection: options,
|
||||
resources,
|
||||
ns: [""],
|
||||
defaultNS: "",
|
||||
fallbackLng: "en",
|
||||
detection: {
|
||||
order: ["querystring", "navigator"],
|
||||
lookupQuerystring: "lng",
|
||||
},
|
||||
backend: {
|
||||
loadPath: "/locales/{{lng}}/{{ns}}.json",
|
||||
},
|
||||
ns: ["portal"],
|
||||
defaultNS: "portal",
|
||||
fallbackLng: {
|
||||
default: ["en"],
|
||||
},
|
||||
load: "all",
|
||||
supportedLngs: ["en", "es", "de"],
|
||||
lowerCaseLng: true,
|
||||
nonExplicitSupportedLngs: true,
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
{
|
||||
"Portal": {
|
||||
"An email has been sent to your address to complete the process": "Es wurde eine E-Mail an Ihre Adresse geschickt, um den Vorgang abzuschließen.",
|
||||
"Authenticated": "Authentifiziert",
|
||||
"Cancel": "Abbrechen",
|
||||
"Contact your administrator to register a device": "Wenden Sie sich an Ihren Administrator, um ein Gerät zu registrieren.",
|
||||
"Could not obtain user settings": "Benutzereinstellungen konnten nicht abgerufen werden",
|
||||
"Done": "Erledigt",
|
||||
"Enter new password": "Neues Passwort eingeben",
|
||||
"Enter one-time password": "Einmal-Passwort eingeben",
|
||||
"Failed to register device, the provided link is expired or has already been used": "Gerät konnte nicht registriert werden, der angegebene Link ist abgelaufen oder wurde bereits verwendet",
|
||||
"Hi": "Hallo",
|
||||
"Incorrect username or password": "Falscher Benutzername oder falsches Passwort.",
|
||||
"Loading": "Laden",
|
||||
"Logout": "Abmelden",
|
||||
"Lost your device?": "Haben Sie Ihr Gerät verloren?",
|
||||
"Methods": "Verfahren",
|
||||
"Need Google Authenticator?": "Benötigen Sie Google Authenticator?",
|
||||
"New password": "Neues Passwort",
|
||||
"No verification token provided": "Kein Verifizierungs-Token vorhanden",
|
||||
"OTP Secret copied to clipboard": "OTP Secret in die Zwischenablage kopiert.",
|
||||
"OTP URL copied to clipboard": "OTP-URL in die Zwischenablage kopiert.",
|
||||
"One-Time Password": "One-Time-Passwort",
|
||||
"Password has been reset": "Das Passwort wurde zurückgesetzt.",
|
||||
"Password": "Passwort",
|
||||
"Passwords do not match": "Die Passwörter stimmen nicht überein.",
|
||||
"Push Notification": "Push-Benachrichtigung",
|
||||
"Register device": "Gerät registrieren",
|
||||
"Register your first device by clicking on the link below": "Registrieren Sie Ihr erstes Gerät, indem Sie auf den unten stehenden Link klicken.",
|
||||
"Remember me": "Angemeldet bleiben",
|
||||
"Repeat new password": "Neues Passwort wiederholen",
|
||||
"Reset password": "Passwort zurücksetzen",
|
||||
"Reset password?": "Passwort zurücksetzen?",
|
||||
"Reset": "Zurücksetzen",
|
||||
"Scan QR Code": "QR-Code scannen",
|
||||
"Secret": "Geheimnis",
|
||||
"Security Key - WebAuthN": "Sicherheitsschlüssel - WebAuthN",
|
||||
"Select a Device": "Gerät auswählen",
|
||||
"Sign in": "Anmelden",
|
||||
"Sign out": "Abmelden",
|
||||
"The resource you're attempting to access requires two-factor authentication": "Die Ressource, auf die Sie zuzugreifen versuchen, erfordert eine Zwei-Faktor-Authentifizierung.",
|
||||
"There was a problem initiating the registration process": "Es gab ein Problem beim Starten des Registrierungsprozesses.",
|
||||
"There was an issue completing the process. The verification token might have expired": "Es gab ein Problem beim Abschluss des Prozesses. Das Verifizierungs-Token könnte abgelaufen sein.",
|
||||
"There was an issue initiating the password reset process": "Es gab ein Problem beim beim Starten des Passwortrücksetzprozesses.",
|
||||
"There was an issue resetting the password": "Es gab ein Problem beim Zurücksetzen des Passworts",
|
||||
"There was an issue signing out": "Es gab ein Problem bei der Abmeldung",
|
||||
"Time-based One-Time Password": "Zeitbasiertes One-Time-Passwort",
|
||||
"Username": "Benutzername",
|
||||
"You must open the link from the same device and browser that initiated the registration process": "Sie müssen den Link mit demselben Gerät und demselben Browser öffnen, mit dem Sie den Registrierungsprozess gestartet haben.",
|
||||
"You're being signed out and redirected": "Sie werden abgemeldet und umgeleitet",
|
||||
"Your supplied password does not meet the password policy requirements": "Ihr angegebenes Passwort entspricht nicht den Anforderungen der Passwortrichtlinie.",
|
||||
"Use OpenID to verify your identity": "Verwenden Sie OpenID, um Ihre Identität zu überprüfen",
|
||||
"Access your display name": "Zugriff auf Ihren Anzeigenamen",
|
||||
"Access your group membership": "Zugriff auf Ihre Gruppenmitgliedschaft",
|
||||
"Access your email addresses": "Zugriff auf Ihre E-Mail-Adressen",
|
||||
"Accept": "Annehmen",
|
||||
"Deny": "Ablehnen",
|
||||
"The above application is requesting the following permissions": "Die oben genannte Anwendung bittet um die folgenden Berechtigungen"
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
{
|
||||
"Portal": {
|
||||
"An email has been sent to your address to complete the process": "An email has been sent to your address to complete the process.",
|
||||
"Authenticated": "Authenticated",
|
||||
"Cancel": "Cancel",
|
||||
"Contact your administrator to register a device": "Contact your administrator to register a device.",
|
||||
"Could not obtain user settings": "Could not obtain user settings",
|
||||
"Done": "Done",
|
||||
"Enter new password": "Enter new password",
|
||||
"Enter one-time password": "Enter one-time password",
|
||||
"Failed to register device, the provided link is expired or has already been used": "Failed to register device, the provided link is expired or has already been used",
|
||||
"Hi": "Hi",
|
||||
"Incorrect username or password": "Incorrect username or password.",
|
||||
"Loading": "Loading",
|
||||
"Logout": "Logout",
|
||||
"Lost your device?": "Lost your device?",
|
||||
"Methods": "Methods",
|
||||
"Need Google Authenticator?": "Need Google Authenticator?",
|
||||
"New password": "New password",
|
||||
"No verification token provided": "No verification token provided",
|
||||
"OTP Secret copied to clipboard": "OTP Secret copied to clipboard.",
|
||||
"OTP URL copied to clipboard": "OTP URL copied to clipboard.",
|
||||
"One-Time Password": "One-Time Password",
|
||||
"Password has been reset": "Password has been reset.",
|
||||
"Password": "Password",
|
||||
"Passwords do not match": "Passwords do not match.",
|
||||
"Push Notification": "Push Notification",
|
||||
"Register device": "Register device",
|
||||
"Register your first device by clicking on the link below": "Register your first device by clicking on the link below.",
|
||||
"Remember me": "Remember me",
|
||||
"Repeat new password": "Repeat new password",
|
||||
"Reset password": "Reset password",
|
||||
"Reset password?": "Reset password?",
|
||||
"Reset": "Reset",
|
||||
"Scan QR Code": "Scan QR Code",
|
||||
"Secret": "Secret",
|
||||
"Security Key - WebAuthN": "Security Key - WebAuthN",
|
||||
"Select a Device": "Select a Device",
|
||||
"Sign in": "Sign in",
|
||||
"Sign out": "Sign out",
|
||||
"The resource you're attempting to access requires two-factor authentication": "The resource you're attempting to access requires two-factor authentication.",
|
||||
"There was a problem initiating the registration process": "There was a problem initiating the registration process",
|
||||
"There was an issue completing the process. The verification token might have expired": "There was an issue completing the process. The verification token might have expired.",
|
||||
"There was an issue initiating the password reset process": "There was an issue initiating the password reset process.",
|
||||
"There was an issue resetting the password": "There was an issue resetting the password",
|
||||
"There was an issue signing out": "There was an issue signing out",
|
||||
"Time-based One-Time Password": "Time-based One-Time Password",
|
||||
"Username": "Username",
|
||||
"You must open the link from the same device and browser that initiated the registration process": "You must open the link from the same device and browser that initiated the registration process",
|
||||
"You're being signed out and redirected": "You're being signed out and redirected",
|
||||
"Your supplied password does not meet the password policy requirements": "Your supplied password does not meet the password policy requirements.",
|
||||
"Use OpenID to verify your identity": "Use OpenID to verify your identity",
|
||||
"Access your display name": "Access your display name",
|
||||
"Access your group membership": "Access your group membership",
|
||||
"Access your email addresses": "Access your email addresses",
|
||||
"Accept": "Accept",
|
||||
"Deny": "Deny",
|
||||
"The above application is requesting the following permissions": "The above application is requesting the following permissions",
|
||||
"The password does not meet the password policy": "The password does not meet the password policy",
|
||||
"Must have at least one lowercase letter": "Must have at least one lowercase letter",
|
||||
"Must have at least one UPPERCASE letter": "Must have at least one UPPERCASE letter",
|
||||
"Must have at least one number": "Must have at least one number",
|
||||
"Must have at least one special character": "Must have at least one special character",
|
||||
"Must be at least {{len}} characters in length": "Must be at least {{len}} characters in length",
|
||||
"Must not be more than {{len}} characters in length": "Must not be more than {{len}} characters in length"
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
{
|
||||
"Portal": {
|
||||
"An email has been sent to your address to complete the process": "Un correo ha sido enviado a su cuenta para completar el proceso",
|
||||
"Authenticated": "Autenticado",
|
||||
"Cancel": "Cancelar",
|
||||
"Contact your administrator to register a device": "Contacte a su administrador para registrar un dispositivo.",
|
||||
"Could not obtain user settings": "Error al obtener configuración de usuario",
|
||||
"Done": "Hecho",
|
||||
"Enter new password": "Ingrese una nueva contraseña",
|
||||
"Enter one-time password": "Ingrese contraseña de un solo uso (OTP)",
|
||||
"Failed to register device, the provided link is expired or has already been used": "Error al registrar dispositivo, el link expiró o ya ha sido utilizado",
|
||||
"Hi": "Hola",
|
||||
"Incorrect username or password": "Usuario y/o contraseña incorrectos",
|
||||
"Loading": "Cargando",
|
||||
"Logout": "Cerrar Sesión",
|
||||
"Lost your device?": "Perdió su dispositivo?",
|
||||
"Methods": "Métodos",
|
||||
"Need Google Authenticator?": "Necesita Google Authenticator?",
|
||||
"New password": "Nueva contraseña",
|
||||
"No verification token provided": "No se ha recibido el token de verificación",
|
||||
"OTP Secret copied to clipboard": "La clave OTP ha sido copiada al portapapeles",
|
||||
"OTP URL copied to clipboard": "la URL OTP ha sido copiada al portapapeles.",
|
||||
"One-Time Password": "Contraseña de un solo uso (OTP)",
|
||||
"Password has been reset": "La contraseña ha sido restablecida.",
|
||||
"Password": "Contraseña",
|
||||
"Passwords do not match": "Las contraseñas no coinciden.",
|
||||
"Push Notification": "Notificaciones Push",
|
||||
"Register device": "Registrar Dispositivo",
|
||||
"Register your first device by clicking on the link below": "Registre su primer dispositivo, haciendo click en el siguiente link.",
|
||||
"Remember me": "Recordarme",
|
||||
"Repeat new password": "Repetir la contraseña",
|
||||
"Reset password": "Restablecer Contraseña",
|
||||
"Reset password?": "Olvidé mi contraseña",
|
||||
"Reset": "Restablecer",
|
||||
"Scan QR Code": "Escanear Código QR",
|
||||
"Secret": "Secreto",
|
||||
"Security Key - WebAuthN": "Llave de Seguridad - WebAuthN",
|
||||
"Select a Device": "Seleccionar Dispositivo",
|
||||
"Sign in": "Iniciar Sesión",
|
||||
"Sign out": "Cerrar Sesión",
|
||||
"The resource you're attempting to access requires two-factor authentication": "El recurso que intenta alcanzar requiere un segundo factor de autenticación (2FA).",
|
||||
"There was a problem initiating the registration process": "Ocurrió un problema al iniciar el proceso de registración",
|
||||
"There was an issue completing the process. The verification token might have expired": "Ocurrió un problema mientras se completaba el proceso. El token de verificación pudo haber expirado.",
|
||||
"There was an issue initiating the password reset process": "Ha ocurrido un error al iniciar el proceso de proceso de restauración de contraseña.",
|
||||
"There was an issue resetting the password": "Ocurrió un error al intentar restablecer la contraseña",
|
||||
"There was an issue signing out": "Ocurrió un error al intentar cerrar sesión",
|
||||
"Time-based One-Time Password": "Contraseña de uso único - OTP",
|
||||
"Username": "Usuario",
|
||||
"You must open the link from the same device and browser that initiated the registration process": "Debe abrir el link desde el mismo dispositivo y navegador desde el que inició el proceso de registración",
|
||||
"You're being signed out and redirected": "Cerrando Sesión y redirigiendo",
|
||||
"Your supplied password does not meet the password policy requirements": "La contraseña suministrada no cumple con los requerimientos de la política de contraseñas",
|
||||
"Use OpenID to verify your identity": "Utilizar OpenID para verificar su identidad",
|
||||
"Access your display name": "Acceso a su nombre",
|
||||
"Access your group membership": "Acceso a su(s) grupo(s)",
|
||||
"Access your email addresses": "Acceso a su dirección de correo",
|
||||
"Accept": "Aceptar",
|
||||
"Deny": "Denegar",
|
||||
"The above application is requesting the following permissions": "La aplicación solicita los siguientes permisos",
|
||||
"The password does not meet the password policy": "La contraseña no cumple con la política de contraseñas",
|
||||
"Must have at least one lowercase letter": "Debe contener al menos una letra minúscula",
|
||||
"Must have at least one UPPERCASE letter": "Debe contener al menos una letra MAYUSCULA",
|
||||
"Must have at least one number": "Debe contener al menos un número",
|
||||
"Must have at least one special character": "Debe contener al menos un caracter especial",
|
||||
"Must be at least {{len}} characters in length": "La longitud mínima es de {{len}} caracteres",
|
||||
"Must not be more than {{len}} characters in length": "La longitud máxima es de {{len}} caracteres"
|
||||
}
|
||||
}
|
|
@ -27,7 +27,7 @@ const RegisterOneTimePassword = function () {
|
|||
const { createSuccessNotification, createErrorNotification } = useNotifications();
|
||||
const [hasErrored, setHasErrored] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { t: translate } = useTranslation("Portal");
|
||||
const { t: translate } = useTranslation();
|
||||
|
||||
// Get the token from the query param to give it back to the API when requesting
|
||||
// the secret for OTP.
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import React from "react";
|
||||
|
||||
import { Grid, Typography, useTheme } from "@material-ui/core";
|
||||
import ReactLoading from "react-loading";
|
||||
|
||||
export interface Props {
|
||||
message: string;
|
||||
}
|
||||
|
||||
const BaseLoadingPage = function (props: Props) {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Grid container alignItems="center" justifyContent="center" style={{ minHeight: "100vh" }}>
|
||||
<Grid item style={{ textAlign: "center", display: "inline-block" }}>
|
||||
<ReactLoading width={64} height={64} color={theme.custom.loadingBar} type="bars" />
|
||||
<Typography>{props.message}...</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default BaseLoadingPage;
|
|
@ -1,20 +1,12 @@
|
|||
import React from "react";
|
||||
|
||||
import { useTheme, Typography, Grid } from "@material-ui/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ReactLoading from "react-loading";
|
||||
|
||||
import BaseLoadingPage from "@views/LoadingPage/BaseLoadingPage";
|
||||
|
||||
const LoadingPage = function () {
|
||||
const theme = useTheme();
|
||||
const { t: translate } = useTranslation("Portal");
|
||||
return (
|
||||
<Grid container alignItems="center" justifyContent="center" style={{ minHeight: "100vh" }}>
|
||||
<Grid item style={{ textAlign: "center", display: "inline-block" }}>
|
||||
<ReactLoading width={64} height={64} color={theme.custom.loadingBar} type="bars" />
|
||||
<Typography>{translate("Loading")}...</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
const { t: translate } = useTranslation();
|
||||
return <BaseLoadingPage message={translate("Loading")} />;
|
||||
};
|
||||
|
||||
export default LoadingPage;
|
||||
|
|
|
@ -7,7 +7,7 @@ import SuccessIcon from "@components/SuccessIcon";
|
|||
|
||||
const Authenticated = function () {
|
||||
const classes = useStyles();
|
||||
const { t: translate } = useTranslation("Portal");
|
||||
const { t: translate } = useTranslation();
|
||||
return (
|
||||
<div id="authenticated-stage">
|
||||
<div className={classes.iconContainer}>
|
||||
|
|
|
@ -15,7 +15,7 @@ export interface Props {
|
|||
const AuthenticatedView = function (props: Props) {
|
||||
const style = useStyles();
|
||||
const navigate = useNavigate();
|
||||
const { t: translate } = useTranslation("Portal");
|
||||
const { t: translate } = useTranslation();
|
||||
|
||||
const handleLogoutClick = () => {
|
||||
navigate(SignOutRoute);
|
||||
|
|
|
@ -46,7 +46,7 @@ const ConsentView = function (props: Props) {
|
|||
const redirect = useRedirector();
|
||||
const { createErrorNotification, resetNotification } = useNotifications();
|
||||
const [resp, fetch, , err] = useRequestedScopes();
|
||||
const { t: translate } = useTranslation("Portal");
|
||||
const { t: translate } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
if (err) {
|
||||
|
|
|
@ -38,7 +38,7 @@ const FirstFactorForm = function (props: Props) {
|
|||
// TODO (PR: #806, Issue: #511) potentially refactor
|
||||
const usernameRef = useRef() as MutableRefObject<HTMLInputElement>;
|
||||
const passwordRef = useRef() as MutableRefObject<HTMLInputElement>;
|
||||
const { t: translate } = useTranslation("Portal");
|
||||
const { t: translate } = useTranslation();
|
||||
useEffect(() => {
|
||||
const timeout = setTimeout(() => usernameRef.current.focus(), 10);
|
||||
return () => clearTimeout(timeout);
|
||||
|
|
|
@ -28,7 +28,7 @@ export interface Props {
|
|||
|
||||
const DefaultMethodContainer = function (props: Props) {
|
||||
const style = useStyles();
|
||||
const { t: translate } = useTranslation("Portal");
|
||||
const { t: translate } = useTranslation();
|
||||
const registerMessage = props.registered
|
||||
? props.title === "Push Notification"
|
||||
? ""
|
||||
|
@ -97,7 +97,7 @@ interface NotRegisteredContainerProps {
|
|||
}
|
||||
|
||||
function NotRegisteredContainer(props: NotRegisteredContainerProps) {
|
||||
const { t: translate } = useTranslation("Portal");
|
||||
const { t: translate } = useTranslation();
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Fragment>
|
||||
|
|
|
@ -29,7 +29,7 @@ export interface Props {
|
|||
const MethodSelectionDialog = function (props: Props) {
|
||||
const style = useStyles();
|
||||
const theme = useTheme();
|
||||
const { t: translate } = useTranslation("Portal");
|
||||
const { t: translate } = useTranslation();
|
||||
|
||||
const pieChartIcon = (
|
||||
<TimerIcon width={24} height={24} period={15} color={theme.palette.primary.main} backgroundColor={"white"} />
|
||||
|
|
|
@ -33,7 +33,7 @@ const OneTimePasswordMethod = function (props: Props) {
|
|||
props.authenticationLevel === AuthenticationLevel.TwoFactor ? State.Success : State.Idle,
|
||||
);
|
||||
const redirectionURL = useRedirectionURL();
|
||||
const { t: translate } = useTranslation("Portal");
|
||||
const { t: translate } = useTranslation();
|
||||
|
||||
const { onSignInSuccess, onSignInError } = props;
|
||||
const onSignInErrorCallback = useRef(onSignInError).current;
|
||||
|
|
|
@ -41,7 +41,7 @@ const SecondFactorForm = function (props: Props) {
|
|||
const { createInfoNotification, createErrorNotification } = useNotifications();
|
||||
const [registrationInProgress, setRegistrationInProgress] = useState(false);
|
||||
const [webauthnSupported, setWebauthnSupported] = useState(false);
|
||||
const { t: translate } = useTranslation("Portal");
|
||||
const { t: translate } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
setWebauthnSupported(isWebauthnSupported());
|
||||
|
|
|
@ -22,7 +22,7 @@ const SignOut = function (props: Props) {
|
|||
const redirector = useRedirector();
|
||||
const [timedOut, setTimedOut] = useState(false);
|
||||
const [safeRedirect, setSafeRedirect] = useState(false);
|
||||
const { t: translate } = useTranslation("Portal");
|
||||
const { t: translate } = useTranslation();
|
||||
|
||||
const doSignOut = useCallback(async () => {
|
||||
try {
|
||||
|
|
|
@ -16,7 +16,7 @@ const ResetPasswordStep1 = function () {
|
|||
const [error, setError] = useState(false);
|
||||
const { createInfoNotification, createErrorNotification } = useNotifications();
|
||||
const navigate = useNavigate();
|
||||
const { t: translate } = useTranslation("Portal");
|
||||
const { t: translate } = useTranslation();
|
||||
|
||||
const doInitiateResetPasswordProcess = async () => {
|
||||
if (username === "") {
|
||||
|
|
|
@ -25,7 +25,7 @@ const ResetPasswordStep2 = function () {
|
|||
const [errorPassword1, setErrorPassword1] = useState(false);
|
||||
const [errorPassword2, setErrorPassword2] = useState(false);
|
||||
const { createSuccessNotification, createErrorNotification } = useNotifications();
|
||||
const { t: translate } = useTranslation("Portal");
|
||||
const { t: translate } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
|
|
Loading…
Reference in New Issue