summaryrefslogtreecommitdiff
path: root/experimental/embed
diff options
context:
space:
mode:
Diffstat (limited to 'experimental/embed')
-rw-r--r--experimental/embed/config.go71
-rw-r--r--experimental/embed/config_test.go195
-rw-r--r--experimental/embed/context.go44
-rw-r--r--experimental/embed/context_test.go43
-rw-r--r--experimental/embed/doc.go13
-rw-r--r--experimental/embed/embed.go7
-rw-r--r--experimental/embed/embed_test.go19
-rw-r--r--experimental/embed/provider/authentication.go22
-rw-r--r--experimental/embed/provider/general.go113
-rw-r--r--experimental/embed/provider/notification.go24
-rw-r--r--experimental/embed/provider/storage.go29
-rw-r--r--experimental/embed/types.go24
-rw-r--r--experimental/embed/types_test.go15
13 files changed, 619 insertions, 0 deletions
diff --git a/experimental/embed/config.go b/experimental/embed/config.go
new file mode 100644
index 000000000..81d21463b
--- /dev/null
+++ b/experimental/embed/config.go
@@ -0,0 +1,71 @@
+package embed
+
+import (
+ "fmt"
+
+ "github.com/authelia/authelia/v4/internal/configuration"
+ "github.com/authelia/authelia/v4/internal/configuration/schema"
+ "github.com/authelia/authelia/v4/internal/configuration/validator"
+)
+
+// NewConfiguration builds a new configuration given a list of paths and filters. The filters can either be nil or
+// generated using NewNamedConfigFileFilters. This function essentially operates the same as Authelia does normally in
+// configuration steps.
+func NewConfiguration(paths []string, filters []configuration.BytesFilter) (keys []string, config *schema.Configuration, val *schema.StructValidator, err error) {
+ sources := configuration.NewDefaultSourcesWithDefaults(
+ paths,
+ filters,
+ configuration.DefaultEnvPrefix,
+ configuration.DefaultEnvDelimiter,
+ []configuration.Source{configuration.NewMapSource(configuration.Defaults())})
+
+ val = schema.NewStructValidator()
+
+ var definitions *schema.Definitions
+
+ if definitions, err = configuration.LoadDefinitions(val, sources...); err != nil {
+ return nil, nil, nil, err
+ }
+
+ config = &schema.Configuration{}
+
+ if keys, err = configuration.LoadAdvanced(
+ val,
+ "",
+ config,
+ definitions,
+ sources...); err != nil {
+ return nil, nil, nil, err
+ }
+
+ return keys, config, val, nil
+}
+
+// ValidateConfigurationAndKeys performs all configuration validation steps. The provided *schema.StructValidator should
+// at minimum be checked for errors before continuing.
+func ValidateConfigurationAndKeys(config *schema.Configuration, keys []string, val *schema.StructValidator) {
+ ValidateConfigurationKeys(keys, val)
+ ValidateConfiguration(config, val)
+}
+
+// ValidateConfigurationKeys just the keys validation steps. The provided *schema.StructValidator should
+// at minimum be checked for errors before continuing. This should be used prior to using ValidateConfiguration.
+func ValidateConfigurationKeys(keys []string, val *schema.StructValidator) {
+ validator.ValidateKeys(keys, configuration.GetMultiKeyMappedDeprecationKeys(), configuration.DefaultEnvPrefix, val)
+}
+
+// ValidateConfiguration just the configuration validation steps. The provided *schema.StructValidator should
+// at minimum be checked for errors before continuing. This should be used after using ValidateConfigurationKeys.
+func ValidateConfiguration(config *schema.Configuration, val *schema.StructValidator) {
+ validator.ValidateConfiguration(config, val)
+}
+
+// NewNamedConfigFileFilters allows configuring a set of file filters. The officially supported filter has the name
+// 'template'. The only other one at this stage is 'expand-env' which is deprecated.
+func NewNamedConfigFileFilters(names ...string) (filters []configuration.BytesFilter, err error) {
+ if filters, err = configuration.NewFileFilters(names); err != nil {
+ return nil, fmt.Errorf("error occurred loading filters: %w", err)
+ }
+
+ return filters, nil
+}
diff --git a/experimental/embed/config_test.go b/experimental/embed/config_test.go
new file mode 100644
index 000000000..eb0ce6e2f
--- /dev/null
+++ b/experimental/embed/config_test.go
@@ -0,0 +1,195 @@
+package embed
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ "github.com/authelia/authelia/v4/internal/configuration"
+)
+
+func TestNewConfiguration(t *testing.T) {
+ testCases := []struct {
+ name string
+ paths []string
+ filters []configuration.BytesFilter
+ keys []string
+ warnings []string
+ errors []string
+ err string
+ }{
+ {
+ name: "ShouldHandleWebAuthn",
+ paths: []string{"../../internal/configuration/test_resources/config.webauthn.yml"},
+ keys: []string{
+ "server.endpoints.rate_limits.reset_password_finish.enable",
+ "server.endpoints.rate_limits.reset_password_start.enable",
+ "server.endpoints.rate_limits.second_factor_duo.enable",
+ "server.endpoints.rate_limits.second_factor_totp.enable",
+ "server.endpoints.rate_limits.session_elevation_finish.enable",
+ "server.endpoints.rate_limits.session_elevation_start.enable",
+ "webauthn.selection_criteria.attachment",
+ "webauthn.selection_criteria.discoverability",
+ "webauthn.selection_criteria.user_verification",
+ },
+ warnings: nil,
+ errors: []string{
+ "identity_validation: reset_password: option 'jwt_secret' is required when the reset password functionality isn't disabled",
+ "authentication_backend: you must ensure either the 'file' or 'ldap' authentication backend is configured",
+ "access_control: 'default_policy' option 'deny' is invalid: when no rules are specified it must be 'two_factor' or 'one_factor'",
+ "session: option 'cookies' is required",
+ "storage: option 'encryption_key' is required",
+ "storage: configuration for a 'local', 'mysql' or 'postgres' database must be provided",
+ "notifier: you must ensure either the 'smtp' or 'filesystem' notifier is configured",
+ },
+ },
+ {
+ name: "ShouldHandleConfigWithDefinitions",
+ paths: []string{"../../internal/configuration/test_resources/config_with_definitions.yml"},
+ keys: []string{
+ "access_control.default_policy",
+ "access_control.networks",
+ "access_control.networks[].name",
+ "access_control.networks[].networks",
+ "access_control.rules",
+ "access_control.rules[].domain",
+ "access_control.rules[].networks",
+ "access_control.rules[].policy",
+ "access_control.rules[].resources",
+ "access_control.rules[].subject",
+ "authentication_backend.ldap.additional_groups_dn",
+ "authentication_backend.ldap.additional_users_dn",
+ "authentication_backend.ldap.address",
+ "authentication_backend.ldap.attributes.group_name",
+ "authentication_backend.ldap.attributes.mail",
+ "authentication_backend.ldap.attributes.username",
+ "authentication_backend.ldap.base_dn",
+ "authentication_backend.ldap.groups_filter",
+ "authentication_backend.ldap.tls.private_key",
+ "authentication_backend.ldap.user",
+ "authentication_backend.ldap.users_filter",
+ "authentication_backend.refresh_interval",
+ "definitions.network.lan",
+ "definitions.user_attributes.example.expression",
+ "duo_api.hostname",
+ "duo_api.integration_key",
+ "log.level",
+ "notifier.smtp.address",
+ "notifier.smtp.disable_require_tls",
+ "notifier.smtp.sender",
+ "notifier.smtp.username",
+ "regulation.ban_time",
+ "regulation.find_time",
+ "regulation.max_retries",
+ "server.address",
+ "server.endpoints.authz.auth-request.authn_strategies",
+ "server.endpoints.authz.auth-request.authn_strategies[].name",
+ "server.endpoints.authz.auth-request.implementation",
+ "server.endpoints.authz.ext-authz.authn_strategies",
+ "server.endpoints.authz.ext-authz.authn_strategies[].name",
+ "server.endpoints.authz.ext-authz.implementation",
+ "server.endpoints.authz.forward-auth.authn_strategies",
+ "server.endpoints.authz.forward-auth.authn_strategies[].name",
+ "server.endpoints.authz.forward-auth.implementation",
+ "server.endpoints.authz.legacy.implementation",
+ "server.endpoints.rate_limits.reset_password_finish.enable",
+ "server.endpoints.rate_limits.reset_password_start.enable",
+ "server.endpoints.rate_limits.second_factor_duo.enable",
+ "server.endpoints.rate_limits.second_factor_totp.enable",
+ "server.endpoints.rate_limits.session_elevation_finish.enable",
+ "server.endpoints.rate_limits.session_elevation_start.enable",
+ "session.cookies",
+ "session.cookies[].authelia_url",
+ "session.cookies[].default_redirection_url",
+ "session.cookies[].domain",
+ "session.expiration",
+ "session.inactivity",
+ "session.name",
+ "session.redis.high_availability.sentinel_name",
+ "session.redis.host",
+ "session.redis.port",
+ "storage.mysql.address",
+ "storage.mysql.database",
+ "storage.mysql.username",
+ "totp.issuer",
+ "webauthn.selection_criteria.discoverability",
+ "webauthn.selection_criteria.user_verification",
+ },
+ warnings: nil,
+ errors: []string{
+ "duo_api: option 'secret_key' is required when duo is enabled but it's absent",
+ "identity_validation: reset_password: option 'jwt_secret' is required when the reset password functionality isn't disabled",
+ "authentication_backend: ldap: option 'password' is required",
+ "session: option 'secret' is required when using the 'redis' provider",
+ "storage: option 'encryption_key' is required",
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ t.Run("Individual", func(t *testing.T) {
+ keys, config, val, err := NewConfiguration(tc.paths, tc.filters)
+
+ assert.Equal(t, tc.keys, keys)
+
+ if tc.err != "" {
+ assert.EqualError(t, err, tc.err)
+ assert.Nil(t, config)
+ } else {
+ assert.NoError(t, err)
+ assert.NotNil(t, config)
+ ValidateConfigurationKeys(keys, val)
+ ValidateConfiguration(config, val)
+ }
+
+ require.Len(t, val.Warnings(), len(tc.warnings))
+ require.Len(t, val.Errors(), len(tc.errors))
+
+ for i, err := range val.Warnings() {
+ assert.EqualError(t, err, tc.warnings[i])
+ }
+
+ for i, err := range val.Errors() {
+ assert.EqualError(t, err, tc.errors[i])
+ }
+ })
+ t.Run("Combined", func(t *testing.T) {
+ keys, config, val, err := NewConfiguration(tc.paths, tc.filters)
+
+ assert.Equal(t, tc.keys, keys)
+
+ if tc.err != "" {
+ assert.EqualError(t, err, tc.err)
+ assert.Nil(t, config)
+ } else {
+ assert.NoError(t, err)
+ assert.NotNil(t, config)
+ ValidateConfigurationAndKeys(config, keys, val)
+ }
+
+ require.Len(t, val.Warnings(), len(tc.warnings))
+ require.Len(t, val.Errors(), len(tc.errors))
+
+ for i, err := range val.Warnings() {
+ assert.EqualError(t, err, tc.warnings[i])
+ }
+
+ for i, err := range val.Errors() {
+ assert.EqualError(t, err, tc.errors[i])
+ }
+ })
+ })
+ }
+}
+
+func TestNewNamedConfigFileFilters(t *testing.T) {
+ filters, err := NewNamedConfigFileFilters("abc")
+ assert.Nil(t, filters)
+ assert.EqualError(t, err, "error occurred loading filters: invalid filter named 'abc'")
+
+ filters, err = NewNamedConfigFileFilters("template")
+ assert.NotNil(t, filters)
+ assert.NoError(t, err)
+}
diff --git a/experimental/embed/context.go b/experimental/embed/context.go
new file mode 100644
index 000000000..84cf483aa
--- /dev/null
+++ b/experimental/embed/context.go
@@ -0,0 +1,44 @@
+package embed
+
+import (
+ "context"
+
+ "github.com/sirupsen/logrus"
+
+ "github.com/authelia/authelia/v4/internal/configuration/schema"
+ "github.com/authelia/authelia/v4/internal/middlewares"
+)
+
+// Context is an interface used in various areas of Authelia to simplify access to important elements like the
+// configuration, providers, and logger.
+type Context interface {
+ GetLogger() *logrus.Entry
+ GetProviders() middlewares.Providers
+ GetConfiguration() *schema.Configuration
+
+ context.Context
+}
+
+type ctxEmbed struct {
+ Configuration *Configuration
+ Providers Providers
+ Logger *logrus.Entry
+
+ context.Context
+}
+
+func (c *ctxEmbed) GetConfiguration() *schema.Configuration {
+ return c.Configuration.ToInternal()
+}
+
+func (c *ctxEmbed) GetProviders() middlewares.Providers {
+ return c.Providers.ToInternal()
+}
+
+func (c *ctxEmbed) GetLogger() *logrus.Entry {
+ return c.Logger
+}
+
+var (
+ _ middlewares.Context = (*ctxEmbed)(nil)
+)
diff --git a/experimental/embed/context_test.go b/experimental/embed/context_test.go
new file mode 100644
index 000000000..cb5a2486e
--- /dev/null
+++ b/experimental/embed/context_test.go
@@ -0,0 +1,43 @@
+package embed
+
+import (
+ "testing"
+
+ "github.com/sirupsen/logrus"
+ "github.com/stretchr/testify/assert"
+
+ "github.com/authelia/authelia/v4/internal/logging"
+)
+
+func TestContext(t *testing.T) {
+ ctx := &ctxEmbed{}
+
+ assert.Nil(t, ctx.GetConfiguration())
+ assert.Nil(t, ctx.GetLogger())
+
+ providers := ctx.GetProviders()
+
+ assert.Nil(t, providers.StorageProvider)
+ assert.Nil(t, providers.Notifier)
+ assert.Nil(t, providers.UserProvider)
+ assert.Nil(t, providers.SessionProvider)
+ assert.Nil(t, providers.MetaDataService)
+ assert.Nil(t, providers.Metrics)
+ assert.Nil(t, providers.Templates)
+ assert.Nil(t, providers.Random)
+ assert.Nil(t, providers.OpenIDConnect)
+ assert.Nil(t, providers.UserAttributeResolver)
+ assert.Nil(t, providers.Authorizer)
+ assert.Nil(t, providers.NTP)
+ assert.Nil(t, providers.TOTP)
+}
+
+func TestContextWithValues(t *testing.T) {
+ ctx := &ctxEmbed{
+ Configuration: &Configuration{},
+ Logger: logrus.NewEntry(logging.Logger()),
+ }
+
+ assert.NotNil(t, ctx.GetConfiguration())
+ assert.NotNil(t, ctx.GetLogger())
+}
diff --git a/experimental/embed/doc.go b/experimental/embed/doc.go
new file mode 100644
index 000000000..9e9d4d09b
--- /dev/null
+++ b/experimental/embed/doc.go
@@ -0,0 +1,13 @@
+// Package embed provides tooling useful to embed Authelia into an external go process. This package is considered
+// experimental and as such is not supported by the standard versioning policy. It's strongly recommended that care is
+// taken when integrating with this package and appropriate tests are conducted when upgrading.
+//
+// This package and all subpackages are intended to facilitate differing levels of embedability within Authelia. It's
+// likely this package and subpackages will break often.
+//
+// The following considerations should be made in using this package:
+// - It's likely that many methods within this package can panic if not properly utilized.
+// - The package is likely at this stage to be changed abruptly from version to version in a breaking way.
+// - The package will likely have breaking changes at any minor version bump well into the future (breaking changes to
+// this package as a result of changing internal packages will not be a consideration that will slow development).
+package embed
diff --git a/experimental/embed/embed.go b/experimental/embed/embed.go
new file mode 100644
index 000000000..0a2d91168
--- /dev/null
+++ b/experimental/embed/embed.go
@@ -0,0 +1,7 @@
+package embed
+
+func ProvidersStartupCheck(ctx Context, log bool) (err error) {
+ providers := ctx.GetProviders()
+
+ return providers.StartupChecks(ctx, log)
+}
diff --git a/experimental/embed/embed_test.go b/experimental/embed/embed_test.go
new file mode 100644
index 000000000..c20e2cb77
--- /dev/null
+++ b/experimental/embed/embed_test.go
@@ -0,0 +1,19 @@
+package embed
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestShouldPanicNilCtx(t *testing.T) {
+ assert.Panics(t, func() {
+ _ = ProvidersStartupCheck(nil, false)
+ })
+
+ ctx := &ctxEmbed{}
+
+ assert.Panics(t, func() {
+ _ = ProvidersStartupCheck(ctx, false)
+ })
+}
diff --git a/experimental/embed/provider/authentication.go b/experimental/embed/provider/authentication.go
new file mode 100644
index 000000000..3903e6c1c
--- /dev/null
+++ b/experimental/embed/provider/authentication.go
@@ -0,0 +1,22 @@
+package provider
+
+import (
+ "crypto/x509"
+
+ "github.com/authelia/authelia/v4/internal/authentication"
+ "github.com/authelia/authelia/v4/internal/configuration/schema"
+)
+
+// NewAuthenticationFile directly instantiates a new authentication.UserProvider using a *authentication.FileUserProvider.
+//
+// Warning: This method may panic if the provided configuration isn't validated.
+func NewAuthenticationFile(config *schema.Configuration) authentication.UserProvider {
+ return authentication.NewFileUserProvider(config.AuthenticationBackend.File)
+}
+
+// NewAuthenticationLDAP directly instantiates a new authentication.UserProvider using a *authentication.LDAPUserProvider.
+//
+// Warning: This method may panic if the provided configuration isn't validated.
+func NewAuthenticationLDAP(config *schema.Configuration, caCertPool *x509.CertPool) authentication.UserProvider {
+ return authentication.NewLDAPUserProvider(config.AuthenticationBackend, caCertPool)
+}
diff --git a/experimental/embed/provider/general.go b/experimental/embed/provider/general.go
new file mode 100644
index 000000000..95e5d04a7
--- /dev/null
+++ b/experimental/embed/provider/general.go
@@ -0,0 +1,113 @@
+package provider
+
+import (
+ "crypto/x509"
+
+ "github.com/authelia/authelia/v4/internal/authorization"
+ "github.com/authelia/authelia/v4/internal/clock"
+ "github.com/authelia/authelia/v4/internal/configuration/schema"
+ "github.com/authelia/authelia/v4/internal/expression"
+ "github.com/authelia/authelia/v4/internal/metrics"
+ "github.com/authelia/authelia/v4/internal/middlewares"
+ "github.com/authelia/authelia/v4/internal/ntp"
+ "github.com/authelia/authelia/v4/internal/oidc"
+ "github.com/authelia/authelia/v4/internal/random"
+ "github.com/authelia/authelia/v4/internal/regulation"
+ "github.com/authelia/authelia/v4/internal/session"
+ "github.com/authelia/authelia/v4/internal/storage"
+ "github.com/authelia/authelia/v4/internal/templates"
+ "github.com/authelia/authelia/v4/internal/totp"
+ "github.com/authelia/authelia/v4/internal/webauthn"
+)
+
+// New returns a completely new set of providers using the internal API. It is expected you'll check the errs return
+// value for any errors, and handle any warnings in a graceful way. If errors are returned the providers should not be
+// utilized to run anything.
+func New(config *schema.Configuration, caCertPool *x509.CertPool) (providers middlewares.Providers, warns []error, errs []error) {
+ return middlewares.NewProviders(config, caCertPool)
+}
+
+// NewClock creates a new clock provider.
+func NewClock() clock.Provider {
+ return clock.New()
+}
+
+// NewAuthorizer creates a new *authorization.Authorizer.
+//
+// Warning: This method may panic if the provided configuration isn't validated.
+func NewAuthorizer(config *schema.Configuration) *authorization.Authorizer {
+ return authorization.NewAuthorizer(config)
+}
+
+// NewSession creates a new *session.Provider given a valid configuration.
+//
+// Warning: This method may panic if the provided configuration isn't validated.
+func NewSession(config *schema.Configuration, caCertPool *x509.CertPool) *session.Provider {
+ return session.NewProvider(config.Session, caCertPool)
+}
+
+// NewRegulator creates a new *regulation.Regulator given a valid configuration.
+//
+// Warning: This method may panic if the provided configuration isn't validated.
+func NewRegulator(config *schema.Configuration, storage storage.RegulatorProvider, clock clock.Provider) *regulation.Regulator {
+ return regulation.NewRegulator(config.Regulation, storage, clock)
+}
+
+// NewMetrics creates a new metrics.Provider.
+func NewMetrics() metrics.Provider {
+ return metrics.NewPrometheus()
+}
+
+// NewNTP creates a new *ntp.Provider given a valid configuration.
+//
+// Warning: This method may panic if the provided configuration isn't validated.
+func NewNTP(config *schema.Configuration) *ntp.Provider {
+ return ntp.NewProvider(&config.NTP)
+}
+
+// NewOpenIDConnect creates a new *oidc.OpenIDConnectProvider given a valid configuration.
+//
+// Warning: This method may panic if the provided configuration isn't validated.
+func NewOpenIDConnect(config *schema.Configuration, storage storage.Provider, templates *templates.Provider) *oidc.OpenIDConnectProvider {
+ return oidc.NewOpenIDConnectProvider(config, storage, templates)
+}
+
+// NewTemplates creates a new *templates.Provider given a valid configuration.
+//
+// Warning: This method may panic if the provided configuration isn't validated.
+func NewTemplates(config *schema.Configuration) (provider *templates.Provider, err error) {
+ return templates.New(templates.Config{EmailTemplatesPath: config.Notifier.TemplatePath})
+}
+
+// NewTOTP creates a new totp.Provider given a valid configuration.
+//
+// Warning: This method may panic if the provided configuration isn't validated.
+func NewTOTP(config *schema.Configuration) totp.Provider {
+ return totp.NewTimeBasedProvider(config.TOTP)
+}
+
+// NewPasswordPolicy creates a new middlewares.PasswordPolicyProvider given a valid configuration.
+//
+// Warning: This method may panic if the provided configuration isn't validated.
+func NewPasswordPolicy(config *schema.Configuration) middlewares.PasswordPolicyProvider {
+ return middlewares.NewPasswordPolicyProvider(config.PasswordPolicy)
+}
+
+// NewRandom creates a new random.Provider given a valid configuration. This uses the rand/crypto package.
+func NewRandom() random.Provider {
+ return &random.Cryptographical{}
+}
+
+// NewUserAttributeResolver creates a new expression.UserAttributeResolver given a valid configuration.
+//
+// Warning: This method may panic if the provided configuration isn't validated.
+func NewUserAttributeResolver(config *schema.Configuration) expression.UserAttributeResolver {
+ return expression.NewUserAttributes(config)
+}
+
+// NewMetaDataService creates a new webauthn.MetaDataProvider given a valid configuration.
+//
+// Warning: This method may panic if the provided configuration isn't validated.
+func NewMetaDataService(config *schema.Configuration, store storage.CachedDataProvider) (provider webauthn.MetaDataProvider, err error) {
+ return webauthn.NewMetaDataProvider(config, store)
+}
diff --git a/experimental/embed/provider/notification.go b/experimental/embed/provider/notification.go
new file mode 100644
index 000000000..a566cd20d
--- /dev/null
+++ b/experimental/embed/provider/notification.go
@@ -0,0 +1,24 @@
+package provider
+
+import (
+ "crypto/x509"
+
+ "github.com/authelia/authelia/v4/internal/configuration/schema"
+ "github.com/authelia/authelia/v4/internal/notification"
+)
+
+// NewNotificationSMTP creates a new notification.Notifier using the *notification.SMTPNotifier given a valid
+// configuration.
+//
+// Warning: This method may panic if the provided configuration isn't validated.
+func NewNotificationSMTP(config *schema.Configuration, caCertPool *x509.CertPool) notification.Notifier {
+ return notification.NewSMTPNotifier(config.Notifier.SMTP, caCertPool)
+}
+
+// NewNotificationFile creates a new notification.Notifier using the *notification.FileNotifier given a valid
+// configuration.
+//
+// Warning: This method may panic if the provided configuration isn't validated.
+func NewNotificationFile(config *schema.Configuration, caCertPool *x509.CertPool) notification.Notifier {
+ return notification.NewSMTPNotifier(config.Notifier.SMTP, caCertPool)
+}
diff --git a/experimental/embed/provider/storage.go b/experimental/embed/provider/storage.go
new file mode 100644
index 000000000..42c40aefd
--- /dev/null
+++ b/experimental/embed/provider/storage.go
@@ -0,0 +1,29 @@
+package provider
+
+import (
+ "crypto/x509"
+
+ "github.com/authelia/authelia/v4/internal/configuration/schema"
+ "github.com/authelia/authelia/v4/internal/storage"
+)
+
+// NewStoragePostgreSQL creates a new storage.Provider using the *storage.PostgreSQLProvider given a valid configuration.
+//
+// Warning: This method may panic if the provided configuration isn't validated.
+func NewStoragePostgreSQL(config *schema.Configuration, caCertPool *x509.CertPool) storage.Provider {
+ return storage.NewPostgreSQLProvider(config, caCertPool)
+}
+
+// NewStorageMySQL creates a new storage.Provider using the *storage.MySQLProvider given a valid configuration.
+//
+// Warning: This method may panic if the provided configuration isn't validated.
+func NewStorageMySQL(config *schema.Configuration, caCertPool *x509.CertPool) storage.Provider {
+ return storage.NewMySQLProvider(config, caCertPool)
+}
+
+// NewStorageSQLite creates a new storage.Provider using the *storage.SQLiteProvider given a valid configuration.
+//
+// Warning: This method may panic if the provided configuration isn't validated.
+func NewStorageSQLite(config *schema.Configuration) storage.Provider {
+ return storage.NewSQLiteProvider(config)
+}
diff --git a/experimental/embed/types.go b/experimental/embed/types.go
new file mode 100644
index 000000000..bd048206e
--- /dev/null
+++ b/experimental/embed/types.go
@@ -0,0 +1,24 @@
+package embed
+
+import (
+ "github.com/authelia/authelia/v4/internal/configuration/schema"
+ "github.com/authelia/authelia/v4/internal/middlewares"
+)
+
+// Configuration is a type alias for the internal schema.Configuration type. It allows manually configuring Authelia
+// and transitioning to the internal implementation.
+type Configuration schema.Configuration
+
+// ToInternal converts this Configuration struct into a *schema.Configuration struct using a type cast.
+func (c *Configuration) ToInternal() *schema.Configuration {
+ return (*schema.Configuration)(c)
+}
+
+// Providers is a type alias for the internal middlewares.Providers type. It allows manually performing setup of the
+// various Authelia providers and transitioning to the internal implementation.
+type Providers middlewares.Providers
+
+// ToInternal converts this Providers struct into a middlewares.Providers struct using a type cast.
+func (p Providers) ToInternal() middlewares.Providers {
+ return middlewares.Providers(p)
+}
diff --git a/experimental/embed/types_test.go b/experimental/embed/types_test.go
new file mode 100644
index 000000000..3a93ce183
--- /dev/null
+++ b/experimental/embed/types_test.go
@@ -0,0 +1,15 @@
+package embed
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestTypeBasics(t *testing.T) {
+ c := &Configuration{}
+ assert.NotNil(t, c.ToInternal())
+
+ p := &Providers{}
+ assert.NotNil(t, p.ToInternal())
+}