mirror of
https://github.com/h44z/wg-portal.git
synced 2025-09-15 07:11:15 +00:00
chore: use interfaces for all other services
This commit is contained in:
@@ -14,25 +14,78 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
evbus "github.com/vardius/message-bus"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/h44z/wg-portal/internal/app"
|
||||
"github.com/h44z/wg-portal/internal/config"
|
||||
"github.com/h44z/wg-portal/internal/domain"
|
||||
)
|
||||
|
||||
// region dependencies
|
||||
|
||||
type UserManager interface {
|
||||
// GetUser returns a user by its identifier.
|
||||
GetUser(context.Context, domain.UserIdentifier) (*domain.User, error)
|
||||
// RegisterUser creates a new user in the database.
|
||||
RegisterUser(ctx context.Context, user *domain.User) error
|
||||
// UpdateUser updates an existing user in the database.
|
||||
UpdateUser(ctx context.Context, user *domain.User) (*domain.User, error)
|
||||
}
|
||||
|
||||
type EventBus interface {
|
||||
// Publish sends a message to the message bus.
|
||||
Publish(topic string, args ...any)
|
||||
}
|
||||
|
||||
// endregion dependencies
|
||||
|
||||
type AuthenticatorType string
|
||||
|
||||
const (
|
||||
AuthenticatorTypeOAuth AuthenticatorType = "oauth"
|
||||
AuthenticatorTypeOidc AuthenticatorType = "oidc"
|
||||
)
|
||||
|
||||
// AuthenticatorOauth is the interface for all OAuth authenticators.
|
||||
type AuthenticatorOauth interface {
|
||||
// GetName returns the name of the authenticator.
|
||||
GetName() string
|
||||
// GetType returns the type of the authenticator. It can be either AuthenticatorTypeOAuth or AuthenticatorTypeOidc.
|
||||
GetType() AuthenticatorType
|
||||
// AuthCodeURL returns the URL for the authentication flow.
|
||||
AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string
|
||||
// Exchange exchanges the OAuth code for an access token.
|
||||
Exchange(ctx context.Context, code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error)
|
||||
// GetUserInfo fetches the user information from the OAuth or OIDC provider.
|
||||
GetUserInfo(ctx context.Context, token *oauth2.Token, nonce string) (map[string]any, error)
|
||||
// ParseUserInfo parses the raw user information into a domain.AuthenticatorUserInfo struct.
|
||||
ParseUserInfo(raw map[string]any) (*domain.AuthenticatorUserInfo, error)
|
||||
// RegistrationEnabled returns whether registration is enabled for the OAuth authenticator.
|
||||
RegistrationEnabled() bool
|
||||
}
|
||||
|
||||
// AuthenticatorLdap is the interface for all LDAP authenticators.
|
||||
type AuthenticatorLdap interface {
|
||||
// GetName returns the name of the authenticator.
|
||||
GetName() string
|
||||
// PlaintextAuthentication performs a plaintext authentication against the LDAP server.
|
||||
PlaintextAuthentication(userId domain.UserIdentifier, plainPassword string) error
|
||||
// GetUserInfo fetches the user information from the LDAP server.
|
||||
GetUserInfo(ctx context.Context, username domain.UserIdentifier) (map[string]any, error)
|
||||
// ParseUserInfo parses the raw user information into a domain.AuthenticatorUserInfo struct.
|
||||
ParseUserInfo(raw map[string]any) (*domain.AuthenticatorUserInfo, error)
|
||||
// RegistrationEnabled returns whether registration is enabled for the LDAP authenticator.
|
||||
RegistrationEnabled() bool
|
||||
}
|
||||
|
||||
// Authenticator is the main entry point for all authentication related tasks.
|
||||
// This includes password authentication and external authentication providers (OIDC, OAuth, LDAP).
|
||||
type Authenticator struct {
|
||||
cfg *config.Auth
|
||||
bus evbus.MessageBus
|
||||
bus EventBus
|
||||
|
||||
oauthAuthenticators map[string]domain.OauthAuthenticator
|
||||
ldapAuthenticators map[string]domain.LdapAuthenticator
|
||||
oauthAuthenticators map[string]AuthenticatorOauth
|
||||
ldapAuthenticators map[string]AuthenticatorLdap
|
||||
|
||||
// URL prefix for the callback endpoints, this is a combination of the external URL and the API prefix
|
||||
callbackUrlPrefix string
|
||||
@@ -40,7 +93,8 @@ type Authenticator struct {
|
||||
users UserManager
|
||||
}
|
||||
|
||||
func NewAuthenticator(cfg *config.Auth, extUrl string, bus evbus.MessageBus, users UserManager) (
|
||||
// NewAuthenticator creates a new Authenticator instance.
|
||||
func NewAuthenticator(cfg *config.Auth, extUrl string, bus EventBus, users UserManager) (
|
||||
*Authenticator,
|
||||
error,
|
||||
) {
|
||||
@@ -68,8 +122,8 @@ func (a *Authenticator) setupExternalAuthProviders(ctx context.Context) error {
|
||||
return fmt.Errorf("failed to parse external url: %w", err)
|
||||
}
|
||||
|
||||
a.oauthAuthenticators = make(map[string]domain.OauthAuthenticator, len(a.cfg.OpenIDConnect)+len(a.cfg.OAuth))
|
||||
a.ldapAuthenticators = make(map[string]domain.LdapAuthenticator, len(a.cfg.Ldap))
|
||||
a.oauthAuthenticators = make(map[string]AuthenticatorOauth, len(a.cfg.OpenIDConnect)+len(a.cfg.OAuth))
|
||||
a.ldapAuthenticators = make(map[string]AuthenticatorLdap, len(a.cfg.Ldap))
|
||||
|
||||
for i := range a.cfg.OpenIDConnect { // OIDC
|
||||
providerCfg := &a.cfg.OpenIDConnect[i]
|
||||
@@ -123,6 +177,7 @@ func (a *Authenticator) setupExternalAuthProviders(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetExternalLoginProviders returns a list of all available external login providers.
|
||||
func (a *Authenticator) GetExternalLoginProviders(_ context.Context) []domain.LoginProviderInfo {
|
||||
authProviders := make([]domain.LoginProviderInfo, 0, len(a.cfg.OAuth)+len(a.cfg.OpenIDConnect))
|
||||
|
||||
@@ -157,6 +212,7 @@ func (a *Authenticator) GetExternalLoginProviders(_ context.Context) []domain.Lo
|
||||
return authProviders
|
||||
}
|
||||
|
||||
// IsUserValid checks if a user is valid and not locked or disabled.
|
||||
func (a *Authenticator) IsUserValid(ctx context.Context, id domain.UserIdentifier) bool {
|
||||
ctx = domain.SetUserInfo(ctx, domain.SystemAdminContextUserInfo()) // switch to admin user context
|
||||
user, err := a.users.GetUser(ctx, id)
|
||||
@@ -177,6 +233,8 @@ func (a *Authenticator) IsUserValid(ctx context.Context, id domain.UserIdentifie
|
||||
|
||||
// region password authentication
|
||||
|
||||
// PlainLogin performs a password authentication for a user. The username and password are trimmed before usage.
|
||||
// If the login is successful, the user is returned, otherwise an error.
|
||||
func (a *Authenticator) PlainLogin(ctx context.Context, username, password string) (*domain.User, error) {
|
||||
// Validate form input
|
||||
username = strings.TrimSpace(username)
|
||||
@@ -204,7 +262,7 @@ func (a *Authenticator) passwordAuthentication(
|
||||
domain.SystemAdminContextUserInfo()) // switch to admin user context to check if user exists
|
||||
|
||||
var ldapUserInfo *domain.AuthenticatorUserInfo
|
||||
var ldapProvider domain.LdapAuthenticator
|
||||
var ldapProvider AuthenticatorLdap
|
||||
|
||||
var userInDatabase = false
|
||||
var userSource domain.UserSource
|
||||
@@ -280,6 +338,7 @@ func (a *Authenticator) passwordAuthentication(
|
||||
|
||||
// region oauth authentication
|
||||
|
||||
// OauthLoginStep1 starts the oauth authentication flow by returning the authentication URL, state and nonce.
|
||||
func (a *Authenticator) OauthLoginStep1(_ context.Context, providerId string) (
|
||||
authCodeUrl, state, nonce string,
|
||||
err error,
|
||||
@@ -296,9 +355,9 @@ func (a *Authenticator) OauthLoginStep1(_ context.Context, providerId string) (
|
||||
}
|
||||
|
||||
switch oauthProvider.GetType() {
|
||||
case domain.AuthenticatorTypeOAuth:
|
||||
case AuthenticatorTypeOAuth:
|
||||
authCodeUrl = oauthProvider.AuthCodeURL(state)
|
||||
case domain.AuthenticatorTypeOidc:
|
||||
case AuthenticatorTypeOidc:
|
||||
nonce, err = a.randString(16)
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("failed to generate nonce: %w", err)
|
||||
@@ -318,6 +377,8 @@ func (a *Authenticator) randString(nByte int) (string, error) {
|
||||
return base64.RawURLEncoding.EncodeToString(b), nil
|
||||
}
|
||||
|
||||
// OauthLoginStep2 finishes the oauth authentication flow by exchanging the code for an access token and
|
||||
// fetching the user information.
|
||||
func (a *Authenticator) OauthLoginStep2(ctx context.Context, providerId, nonce, code string) (*domain.User, error) {
|
||||
oauthProvider, ok := a.oauthAuthenticators[providerId]
|
||||
if !ok {
|
||||
|
Reference in New Issue
Block a user