refactor(server): simplify templating and url derivation (#4547)

This refactors a few areas of the server templating and related functions.
pull/4585/head
James Elliott 2022-12-17 11:49:05 +11:00 committed by GitHub
parent 3de693623e
commit d13247ce43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 180 additions and 188 deletions

View File

@ -1,6 +1,13 @@
---
extends: default
locale: en_US.UTF-8
yaml-files:
- '*.yaml'
- '*.yml'
- '.yamllint'
ignore: |
docs/pnpm-lock.yaml
internal/configuration/test_resources/config_bad_quoting.yml

View File

@ -52,13 +52,7 @@ func OpenIDConnectAuthorization(ctx *middlewares.AutheliaCtx, rw http.ResponseWr
return
}
if issuer, err = ctx.IssuerURL(); err != nil {
ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' could not be processed: error occurred determining issuer: %+v", requester.GetID(), clientID, err)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrIssuerCouldNotDerive)
return
}
issuer = ctx.RootURL()
userSession := ctx.GetSession()

View File

@ -130,12 +130,7 @@ func OpenIDConnectConsentPOST(ctx *middlewares.AutheliaCtx) {
query url.Values
)
if redirectURI, err = ctx.IssuerURL(); err != nil {
ctx.Logger.Errorf("Failed to parse the consent redirect URL: %+v", err)
ctx.SetJSONError(messageOperationFailed)
return
}
redirectURI = ctx.RootURL()
if query, err = url.ParseQuery(consent.Form); err != nil {
ctx.Logger.Errorf("Failed to parse the consent form values: %+v", err)

View File

@ -20,13 +20,7 @@ func OpenIDConnectConfigurationWellKnownGET(ctx *middlewares.AutheliaCtx) {
err error
)
if issuer, err = ctx.IssuerURL(); err != nil {
ctx.Logger.Errorf("Error occurred determining OpenID Connect issuer details: %+v", err)
ctx.ReplyStatusCode(fasthttp.StatusBadRequest)
return
}
issuer = ctx.RootURL()
wellKnown := ctx.Providers.OpenIDConnect.GetOpenIDConnectWellKnownConfiguration(issuer.String())
@ -52,13 +46,7 @@ func OAuthAuthorizationServerWellKnownGET(ctx *middlewares.AutheliaCtx) {
err error
)
if issuer, err = ctx.IssuerURL(); err != nil {
ctx.Logger.Errorf("Error occurred determining OpenID Connect issuer details: %+v", err)
ctx.ReplyStatusCode(fasthttp.StatusBadRequest)
return
}
issuer = ctx.RootURL()
wellKnown := ctx.Providers.OpenIDConnect.GetOAuth2WellKnownConfiguration(issuer.String())

View File

@ -144,11 +144,7 @@ func handleOIDCWorkflowResponseWithTargetURL(ctx *middlewares.AutheliaCtx, targe
return
}
if issuerURL, err = ctx.IssuerURL(); err != nil {
ctx.Error(fmt.Errorf("unable to get issuer for redirection: %w", err), messageAuthenticationFailed)
return
}
issuerURL = ctx.RootURL()
if targetURL.Host != issuerURL.Host {
ctx.Error(fmt.Errorf("unable to redirect to '%s': target host '%s' does not match expected issuer host '%s'", targetURL, targetURL.Host, issuerURL.Host), messageAuthenticationFailed)
@ -221,11 +217,7 @@ func handleOIDCWorkflowResponseWithID(ctx *middlewares.AutheliaCtx, id string) {
form url.Values
)
if targetURL, err = ctx.IssuerURL(); err != nil {
ctx.Error(fmt.Errorf("unable to get issuer for redirection: %w", err), messageAuthenticationFailed)
return
}
targetURL = ctx.RootURL()
if form, err = consent.GetForm(); err != nil {
ctx.Error(fmt.Errorf("unable to get authorization form values from consent session with challenge id '%s': %w", consent.ChallengeID, err), messageAuthenticationFailed)

View File

@ -5,7 +5,6 @@ import (
"fmt"
"net"
"net/url"
"path"
"strings"
"github.com/asaskevich/govalidator"
@ -81,7 +80,7 @@ func (ctx *AutheliaCtx) ReplyError(err error, message string) {
ctx.Logger.Error(marshalErr)
}
ctx.SetContentTypeBytes(contentTypeApplicationJSON)
ctx.SetContentTypeApplicationJSON()
ctx.SetBody(b)
ctx.Logger.Debug(err)
}
@ -90,7 +89,7 @@ func (ctx *AutheliaCtx) ReplyError(err error, message string) {
func (ctx *AutheliaCtx) ReplyStatusCode(statusCode int) {
ctx.Response.Reset()
ctx.SetStatusCode(statusCode)
ctx.SetContentTypeBytes(contentTypeTextPlain)
ctx.SetContentTypeTextPlain()
ctx.SetBodyString(fmt.Sprintf("%d %s", statusCode, fasthttp.StatusMessage(statusCode)))
}
@ -108,7 +107,7 @@ func (ctx *AutheliaCtx) ReplyJSON(data any, statusCode int) (err error) {
ctx.SetStatusCode(statusCode)
}
ctx.SetContentTypeBytes(contentTypeApplicationJSON)
ctx.SetContentTypeApplicationJSON()
ctx.SetBody(body)
return nil
@ -145,7 +144,7 @@ func (ctx *AutheliaCtx) XForwardedProto() (proto []byte) {
}
// XForwardedMethod return the content of the X-Forwarded-Method header.
func (ctx *AutheliaCtx) XForwardedMethod() (method []byte) {
func (ctx *AutheliaCtx) XForwardedMethod() []byte {
return ctx.RequestCtx.Request.Header.PeekBytes(headerXForwardedMethod)
}
@ -171,79 +170,61 @@ func (ctx *AutheliaCtx) XForwardedURI() (uri []byte) {
return uri
}
// XAutheliaURL return the content of the X-Authelia-URL header.
func (ctx *AutheliaCtx) XAutheliaURL() (autheliaURL []byte) {
// XOriginalURL returns the content of the X-Original-URL header.
func (ctx *AutheliaCtx) XOriginalURL() []byte {
return ctx.RequestCtx.Request.Header.PeekBytes(headerXOriginalURL)
}
// XOriginalMethod return the content of the X-Original-Method header.
func (ctx *AutheliaCtx) XOriginalMethod() []byte {
return ctx.RequestCtx.Request.Header.PeekBytes(headerXOriginalMethod)
}
// XAutheliaURL return the content of the X-Authelia-URL header which is used to communicate the location of the
// portal when using proxies like Envoy.
func (ctx *AutheliaCtx) XAutheliaURL() []byte {
return ctx.RequestCtx.Request.Header.PeekBytes(headerXAutheliaURL)
}
// QueryArgRedirect return the content of the rd query argument.
func (ctx *AutheliaCtx) QueryArgRedirect() (val []byte) {
return ctx.RequestCtx.QueryArgs().PeekBytes(queryArgRedirect)
func (ctx *AutheliaCtx) QueryArgRedirect() []byte {
return ctx.RequestCtx.QueryArgs().PeekBytes(qryArgRedirect)
}
// BasePath returns the base_url as per the path visited by the client.
func (ctx *AutheliaCtx) BasePath() (base string) {
func (ctx *AutheliaCtx) BasePath() string {
if baseURL := ctx.UserValueBytes(UserValueKeyBaseURL); baseURL != nil {
return baseURL.(string)
}
return base
return ""
}
// ExternalRootURL gets the X-Forwarded-Proto, X-Forwarded-Host headers and the BasePath and forms them into a URL.
func (ctx *AutheliaCtx) ExternalRootURL() (string, error) {
protocol := ctx.XForwardedProto()
if protocol == nil {
return "", errMissingXForwardedProto
// BasePathSlash is the same as BasePath but returns a final slash as well.
func (ctx *AutheliaCtx) BasePathSlash() string {
if baseURL := ctx.UserValueBytes(UserValueKeyBaseURL); baseURL != nil {
return baseURL.(string) + strSlash
}
host := ctx.XForwardedHost()
if host == nil {
return "", errMissingXForwardedHost
}
externalRootURL := fmt.Sprintf("%s://%s", protocol, host)
if base := ctx.BasePath(); base != "" {
externalBaseURL, err := url.ParseRequestURI(externalRootURL)
if err != nil {
return "", err
}
externalBaseURL.Path = path.Join(externalBaseURL.Path, base)
return externalBaseURL.String(), nil
}
return externalRootURL, nil
return strSlash
}
// IssuerURL returns the expected Issuer.
func (ctx *AutheliaCtx) IssuerURL() (issuerURL *url.URL, err error) {
issuerURL = &url.URL{
Scheme: "https",
// RootURL returns the Root URL.
func (ctx *AutheliaCtx) RootURL() (issuerURL *url.URL) {
return &url.URL{
Scheme: string(ctx.XForwardedProto()),
Host: string(ctx.XForwardedHost()),
Path: ctx.BasePath(),
}
if scheme := ctx.XForwardedProto(); scheme != nil {
issuerURL.Scheme = string(scheme)
}
if host := ctx.XForwardedHost(); len(host) != 0 {
issuerURL.Host = string(host)
} else {
return nil, errMissingXForwardedHost
}
if base := ctx.BasePath(); base != "" {
issuerURL.Path = path.Join(issuerURL.Path, base)
}
return issuerURL, nil
}
// XOriginalURL return the content of the X-Original-URL header.
func (ctx *AutheliaCtx) XOriginalURL() []byte {
return ctx.RequestCtx.Request.Header.PeekBytes(headerXOriginalURL)
// RootURLSlash is the same as RootURL but includes a final slash as well.
func (ctx *AutheliaCtx) RootURLSlash() (issuerURL *url.URL) {
return &url.URL{
Scheme: string(ctx.XForwardedProto()),
Host: string(ctx.XForwardedHost()),
Path: ctx.BasePathSlash(),
}
}
// GetSession return the user session. Any update will be saved in cache.
@ -264,7 +245,7 @@ func (ctx *AutheliaCtx) SaveSession(userSession session.UserSession) error {
// ReplyOK is a helper method to reply ok.
func (ctx *AutheliaCtx) ReplyOK() {
ctx.SetContentTypeBytes(contentTypeApplicationJSON)
ctx.SetContentTypeApplicationJSON()
ctx.SetBody(okMessageBytes)
}
@ -377,7 +358,7 @@ func (ctx *AutheliaCtx) SpecialRedirect(uri string, statusCode int) {
statusCode = fasthttp.StatusFound
}
ctx.SetContentTypeBytes(contentTypeTextHTML)
ctx.SetContentTypeTextHTML()
ctx.SetStatusCode(statusCode)
u := fasthttp.AcquireURI()
@ -400,3 +381,18 @@ func (ctx *AutheliaCtx) RecordAuthentication(success, regulated bool, method str
ctx.Providers.Metrics.RecordAuthentication(success, regulated, method)
}
// SetContentTypeTextPlain efficiently sets the Content-Type header to 'text/plain; charset=utf-8'.
func (ctx *AutheliaCtx) SetContentTypeTextPlain() {
ctx.SetContentTypeBytes(contentTypeTextPlain)
}
// SetContentTypeTextHTML efficiently sets the Content-Type header to 'text/html; charset=utf-8'.
func (ctx *AutheliaCtx) SetContentTypeTextHTML() {
ctx.SetContentTypeBytes(contentTypeTextHTML)
}
// SetContentTypeApplicationJSON efficiently sets the Content-Type header to 'application/json; charset=utf-8'.
func (ctx *AutheliaCtx) SetContentTypeApplicationJSON() {
ctx.SetContentTypeBytes(contentTypeApplicationJSON)
}

View File

@ -21,7 +21,6 @@ func TestIssuerURL(t *testing.T) {
name string
proto, host, base string
expected string
err string
}{
{
name: "Standard",
@ -36,7 +35,7 @@ func TestIssuerURL(t *testing.T) {
{
name: "NoHost",
proto: "https", host: "", base: "",
err: "Missing header X-Forwarded-Host",
expected: "https:",
},
}
@ -52,21 +51,14 @@ func TestIssuerURL(t *testing.T) {
mock.Ctx.SetUserValue("base_url", tc.base)
}
actual, err := mock.Ctx.IssuerURL()
actual := mock.Ctx.RootURL()
switch tc.err {
case "":
assert.NoError(t, err)
require.NotNil(t, actual)
require.NotNil(t, actual)
assert.Equal(t, tc.expected, actual.String())
assert.Equal(t, tc.proto, actual.Scheme)
assert.Equal(t, tc.host, actual.Host)
assert.Equal(t, tc.base, actual.Path)
default:
assert.EqualError(t, err, tc.err)
assert.Nil(t, actual)
}
assert.Equal(t, tc.expected, actual.String())
assert.Equal(t, tc.proto, actual.Scheme)
assert.Equal(t, tc.host, actual.Host)
assert.Equal(t, tc.base, actual.Path)
})
}
}

View File

@ -20,6 +20,7 @@ var (
headerXForwardedURI = []byte("X-Forwarded-URI")
headerXOriginalURL = []byte("X-Original-URL")
headerXOriginalMethod = []byte("X-Original-Method")
headerXForwardedMethod = []byte("X-Forwarded-Method")
headerVary = []byte(fasthttp.HeaderVary)
@ -67,13 +68,17 @@ var (
const (
strProtoHTTPS = "https"
strProtoHTTP = "http"
strSlash = "/"
queryArgRedirect = "rd"
queryArgToken = "token"
)
var (
protoHTTPS = []byte(strProtoHTTPS)
protoHTTP = []byte(strProtoHTTP)
queryArgRedirect = []byte("rd")
qryArgRedirect = []byte(queryArgRedirect)
// UserValueKeyBaseURL is the User Value key where we store the Base URL.
UserValueKeyBaseURL = []byte("base_url")

View File

@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"net/mail"
"path"
"time"
"github.com/golang-jwt/jwt/v4"
@ -51,7 +52,7 @@ func IdentityVerificationStart(args IdentityVerificationStartArgs, delayFunc Tim
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
ss, err := token.SignedString([]byte(ctx.Configuration.JWTSecret))
signedToken, err := token.SignedString([]byte(ctx.Configuration.JWTSecret))
if err != nil {
ctx.Error(err, messageOperationFailed)
return
@ -62,23 +63,23 @@ func IdentityVerificationStart(args IdentityVerificationStartArgs, delayFunc Tim
return
}
var (
uri string
)
if uri, err = ctx.ExternalRootURL(); err != nil {
ctx.Error(err, messageOperationFailed)
return
}
disableHTML := false
if ctx.Configuration.Notifier.SMTP != nil {
disableHTML = ctx.Configuration.Notifier.SMTP.DisableHTMLEmails
}
linkURL := ctx.RootURL()
query := linkURL.Query()
query.Set(queryArgToken, signedToken)
linkURL.Path = path.Join(linkURL.Path, args.TargetEndpoint)
linkURL.RawQuery = query.Encode()
values := templates.EmailIdentityVerificationValues{
Title: args.MailTitle,
LinkURL: fmt.Sprintf("%s%s?token=%s", uri, args.TargetEndpoint, ss),
LinkURL: linkURL.String(),
LinkText: args.MailButtonContent,
DisplayName: identity.DisplayName,
RemoteIP: ctx.RemoteIP().String(),

View File

@ -91,24 +91,6 @@ func TestShouldFailSendingAnEmail(t *testing.T) {
assert.Equal(t, "no notif", mock.Hook.LastEntry().Message)
}
func TestShouldFailWhenXForwardedHostHeaderIsMissing(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)
defer mock.Close()
mock.Ctx.Configuration.JWTSecret = testJWTSecret
mock.Ctx.Request.Header.Add("X-Forwarded-Proto", "http")
mock.StorageMock.EXPECT().
SaveIdentityVerification(mock.Ctx, gomock.Any()).
Return(nil)
args := newArgs(defaultRetriever)
middlewares.IdentityVerificationStart(args, nil)(mock.Ctx)
assert.Equal(t, 200, mock.Ctx.Response.StatusCode())
assert.Equal(t, "Missing header X-Forwarded-Host", mock.Hook.LastEntry().Message)
}
func TestShouldSucceedIdentityVerificationStartProcess(t *testing.T) {
mock := mocks.NewMockAutheliaCtx(t)

View File

@ -11,6 +11,9 @@ const (
fileOpenAPI = "openapi.yml"
fileIndexHTML = "index.html"
fileLogo = "logo.png"
extHTML = ".html"
extJSON = ".json"
)
var (
@ -47,6 +50,7 @@ var (
)
const (
environment = "ENVIRONMENT"
dev = "dev"
f = "false"
t = "true"

View File

@ -3,7 +3,6 @@ package server
import (
"net"
"os"
"strconv"
"strings"
"time"
@ -92,21 +91,11 @@ func handleNotFound(next fasthttp.RequestHandler) fasthttp.RequestHandler {
}
func handleRouter(config schema.Configuration, providers middlewares.Providers) fasthttp.RequestHandler {
rememberMe := strconv.FormatBool(config.Session.RememberMeDuration != schema.RememberMeDisabled)
resetPassword := strconv.FormatBool(!config.AuthenticationBackend.PasswordReset.Disable)
optsTemplatedFile := NewTemplatedFileOptions(&config)
resetPasswordCustomURL := config.AuthenticationBackend.PasswordReset.CustomURL.String()
duoSelfEnrollment := f
if !config.DuoAPI.Disable {
duoSelfEnrollment = strconv.FormatBool(config.DuoAPI.EnableSelfEnrollment)
}
https := config.Server.TLS.Key != "" && config.Server.TLS.Certificate != ""
serveIndexHandler := ServeTemplatedFile(assetsRoot, fileIndexHTML, config.Server.AssetPath, duoSelfEnrollment, rememberMe, resetPassword, resetPasswordCustomURL, config.Session.Name, config.Theme, https)
serveSwaggerHandler := ServeTemplatedFile(assetsSwagger, fileIndexHTML, config.Server.AssetPath, duoSelfEnrollment, rememberMe, resetPassword, resetPasswordCustomURL, config.Session.Name, config.Theme, https)
serveSwaggerAPIHandler := ServeTemplatedFile(assetsSwagger, fileOpenAPI, config.Server.AssetPath, duoSelfEnrollment, rememberMe, resetPassword, resetPasswordCustomURL, config.Session.Name, config.Theme, https)
serveIndexHandler := ServeTemplatedFile(assetsRoot, fileIndexHTML, optsTemplatedFile)
serveSwaggerHandler := ServeTemplatedFile(assetsSwagger, fileIndexHTML, optsTemplatedFile)
serveSwaggerAPIHandler := ServeTemplatedFile(assetsSwagger, fileOpenAPI, optsTemplatedFile)
handlerPublicHTML := newPublicHTMLEmbeddedHandler()
handlerLocales := newLocalesEmbeddedHandler()
@ -115,7 +104,7 @@ func handleRouter(config schema.Configuration, providers middlewares.Providers)
WithPreMiddlewares(middlewares.SecurityHeaders).Build()
policyCORSPublicGET := middlewares.NewCORSPolicyBuilder().
WithAllowedMethods("OPTIONS", "GET").
WithAllowedMethods(fasthttp.MethodOptions, fasthttp.MethodGet).
WithAllowedOrigins("*").
Build()

View File

@ -6,11 +6,13 @@ import (
"os"
"path"
"path/filepath"
"strconv"
"strings"
"text/template"
"github.com/valyala/fasthttp"
"github.com/authelia/authelia/v4/internal/configuration/schema"
"github.com/authelia/authelia/v4/internal/logging"
"github.com/authelia/authelia/v4/internal/middlewares"
"github.com/authelia/authelia/v4/internal/utils"
@ -19,7 +21,7 @@ import (
// ServeTemplatedFile serves a templated version of a specified file,
// this is utilised to pass information between the backend and frontend
// and generate a nonce to support a restrictive CSP while using material-ui.
func ServeTemplatedFile(publicDir, file, assetPath, duoSelfEnrollment, rememberMe, resetPassword, resetPasswordCustomURL, session, theme string, https bool) middlewares.RequestHandler {
func ServeTemplatedFile(publicDir, file string, opts *TemplatedFileOptions) middlewares.RequestHandler {
logger := logging.Logger()
a, err := assets.Open(path.Join(publicDir, file))
@ -37,55 +39,40 @@ func ServeTemplatedFile(publicDir, file, assetPath, duoSelfEnrollment, rememberM
logger.Fatalf("Unable to parse %s template: %s", file, err)
}
return func(ctx *middlewares.AutheliaCtx) {
base := ""
if baseURL := ctx.UserValueBytes(middlewares.UserValueKeyBaseURL); baseURL != nil {
base = baseURL.(string)
}
isDevEnvironment := os.Getenv(environment) == dev
return func(ctx *middlewares.AutheliaCtx) {
logoOverride := f
if assetPath != "" {
if _, err := os.Stat(filepath.Join(assetPath, fileLogo)); err == nil {
if opts.AssetPath != "" {
if _, err = os.Stat(filepath.Join(opts.AssetPath, fileLogo)); err == nil {
logoOverride = t
}
}
var scheme = schemeHTTPS
if !https {
proto := string(ctx.XForwardedProto())
switch proto {
case "":
break
case schemeHTTP, schemeHTTPS:
scheme = proto
}
}
baseURL := scheme + "://" + string(ctx.XForwardedHost()) + base + "/"
nonce := utils.RandomString(32, utils.CharSetAlphaNumeric, true)
switch extension := filepath.Ext(file); extension {
case ".html":
ctx.SetContentType("text/html; charset=utf-8")
case extHTML:
ctx.SetContentTypeTextHTML()
case extJSON:
ctx.SetContentTypeApplicationJSON()
default:
ctx.SetContentType("text/plain; charset=utf-8")
ctx.SetContentTypeTextPlain()
}
nonce := utils.RandomString(32, utils.CharSetAlphaNumeric, true)
switch {
case publicDir == assetsSwagger:
ctx.Response.Header.Add(fasthttp.HeaderContentSecurityPolicy, fmt.Sprintf(tmplCSPSwagger, nonce, nonce))
case ctx.Configuration.Server.Headers.CSPTemplate != "":
ctx.Response.Header.Add(fasthttp.HeaderContentSecurityPolicy, strings.ReplaceAll(ctx.Configuration.Server.Headers.CSPTemplate, placeholderCSPNonce, nonce))
case os.Getenv("ENVIRONMENT") == dev:
case isDevEnvironment:
ctx.Response.Header.Add(fasthttp.HeaderContentSecurityPolicy, fmt.Sprintf(tmplCSPDevelopment, nonce))
default:
ctx.Response.Header.Add(fasthttp.HeaderContentSecurityPolicy, fmt.Sprintf(tmplCSPDefault, nonce))
}
err := tmpl.Execute(ctx.Response.BodyWriter(), struct{ Base, BaseURL, CSPNonce, DuoSelfEnrollment, LogoOverride, RememberMe, ResetPassword, ResetPasswordCustomURL, Session, Theme string }{Base: base, BaseURL: baseURL, CSPNonce: nonce, DuoSelfEnrollment: duoSelfEnrollment, LogoOverride: logoOverride, RememberMe: rememberMe, ResetPassword: resetPassword, ResetPasswordCustomURL: resetPasswordCustomURL, Session: session, Theme: theme})
if err != nil {
if err = tmpl.Execute(ctx.Response.BodyWriter(), opts.CommonData(ctx.BasePath(), ctx.RootURLSlash().String(), nonce, logoOverride)); err != nil {
ctx.RequestCtx.Error("an error occurred", 503)
logger.Errorf("Unable to execute template: %v", err)
@ -128,3 +115,62 @@ func writeHealthCheckEnv(disabled bool, scheme, host, path string, port int) (er
return err
}
// NewTemplatedFileOptions returns a new *TemplatedFileOptions.
func NewTemplatedFileOptions(config *schema.Configuration) (opts *TemplatedFileOptions) {
opts = &TemplatedFileOptions{
AssetPath: config.Server.AssetPath,
DuoSelfEnrollment: f,
RememberMe: strconv.FormatBool(config.Session.RememberMeDuration != schema.RememberMeDisabled),
ResetPassword: strconv.FormatBool(!config.AuthenticationBackend.PasswordReset.Disable),
ResetPasswordCustomURL: config.AuthenticationBackend.PasswordReset.CustomURL.String(),
Theme: config.Theme,
}
if !config.DuoAPI.Disable {
opts.DuoSelfEnrollment = strconv.FormatBool(config.DuoAPI.EnableSelfEnrollment)
}
return opts
}
// TemplatedFileOptions is a struct which is used for many templated files.
type TemplatedFileOptions struct {
AssetPath string
DuoSelfEnrollment string
RememberMe string
ResetPassword string
ResetPasswordCustomURL string
Session string
Theme string
}
// CommonData returns a TemplatedFileCommonData with the dynamic options.
func (options *TemplatedFileOptions) CommonData(base, baseURL, nonce, logoOverride string) TemplatedFileCommonData {
return TemplatedFileCommonData{
Base: base,
BaseURL: baseURL,
CSPNonce: nonce,
LogoOverride: logoOverride,
DuoSelfEnrollment: options.DuoSelfEnrollment,
RememberMe: options.RememberMe,
ResetPassword: options.ResetPassword,
ResetPasswordCustomURL: options.ResetPasswordCustomURL,
Session: options.Session,
Theme: options.Theme,
}
}
// TemplatedFileCommonData is a struct which is used for many templated files.
type TemplatedFileCommonData struct {
Base string
BaseURL string
CSPNonce string
LogoOverride string
DuoSelfEnrollment string
RememberMe string
ResetPassword string
ResetPasswordCustomURL string
Session string
Theme string
}

View File

@ -8,6 +8,7 @@
:8085 {
log
reverse_proxy authelia-backend:9091 {
header_up X-Forwarded-Proto https
import tls-transport
}
}

View File

@ -10,7 +10,7 @@ import (
"testing"
"github.com/stretchr/testify/suite"
yaml "gopkg.in/yaml.v3"
"gopkg.in/yaml.v3"
"github.com/authelia/authelia/v4/internal/model"
"github.com/authelia/authelia/v4/internal/storage"