diff options
| author | James Elliott <james-d-elliott@users.noreply.github.com> | 2025-03-09 01:53:44 +1100 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-03-09 01:53:44 +1100 | 
| commit | 9241731a4dd5592b4a02b5352c903b4d06b6f4ab (patch) | |
| tree | 5184b98751912a261ff70fd8721b9cd4f1c98f1e /experimental | |
| parent | bbcb38ab9ff35e69d5d52a71ab56346749f5e8b1 (diff) | |
feat(embed): make authelia embedable (#8841)
This adds a highly experimental option for developers looking to embed Authelia within another go binary.
Closes #5803
Signed-off-by: James Elliott <james-d-elliott@users.noreply.github.com>
Diffstat (limited to 'experimental')
| -rw-r--r-- | experimental/embed/config.go | 71 | ||||
| -rw-r--r-- | experimental/embed/config_test.go | 195 | ||||
| -rw-r--r-- | experimental/embed/context.go | 44 | ||||
| -rw-r--r-- | experimental/embed/context_test.go | 43 | ||||
| -rw-r--r-- | experimental/embed/doc.go | 13 | ||||
| -rw-r--r-- | experimental/embed/embed.go | 7 | ||||
| -rw-r--r-- | experimental/embed/embed_test.go | 19 | ||||
| -rw-r--r-- | experimental/embed/provider/authentication.go | 22 | ||||
| -rw-r--r-- | experimental/embed/provider/general.go | 113 | ||||
| -rw-r--r-- | experimental/embed/provider/notification.go | 24 | ||||
| -rw-r--r-- | experimental/embed/provider/storage.go | 29 | ||||
| -rw-r--r-- | experimental/embed/types.go | 24 | ||||
| -rw-r--r-- | experimental/embed/types_test.go | 15 | 
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()) +}  | 
