Move config struct to model, avoid dependancy to storage
This commit is contained in:
parent
34f1404adb
commit
ee2f033b0d
33 changed files with 377 additions and 228 deletions
|
@ -67,7 +67,7 @@ func main() {
|
|||
color.NoColor = true
|
||||
|
||||
// Load and parse options
|
||||
var opts *config.Options
|
||||
var opts *happydns.Options
|
||||
if opts, err = config.ConsolidateConfig(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -29,18 +29,17 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/config"
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
"git.happydns.org/happyDomain/internal/usecase"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
type BackupController struct {
|
||||
config *config.Options
|
||||
config *happydns.Options
|
||||
store storage.Storage
|
||||
}
|
||||
|
||||
func NewBackupController(cfg *config.Options, store storage.Storage) *BackupController {
|
||||
func NewBackupController(cfg *happydns.Options, store storage.Storage) *BackupController {
|
||||
return &BackupController{
|
||||
config: cfg,
|
||||
store: store,
|
||||
|
|
|
@ -26,17 +26,16 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/config"
|
||||
"git.happydns.org/happyDomain/internal/usecase/session"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
type SessionController struct {
|
||||
config *config.Options
|
||||
config *happydns.Options
|
||||
store session.SessionStorage
|
||||
}
|
||||
|
||||
func NewSessionController(cfg *config.Options, store session.SessionStorage) *SessionController {
|
||||
func NewSessionController(cfg *happydns.Options, store session.SessionStorage) *SessionController {
|
||||
return &SessionController{
|
||||
config: cfg,
|
||||
store: store,
|
||||
|
|
|
@ -25,11 +25,11 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/api-admin/controller"
|
||||
"git.happydns.org/happyDomain/internal/config"
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func declareBackupRoutes(cfg *config.Options, router *gin.RouterGroup, store storage.Storage) {
|
||||
func declareBackupRoutes(cfg *happydns.Options, router *gin.RouterGroup, store storage.Storage) {
|
||||
bc := controller.NewBackupController(cfg, store)
|
||||
|
||||
router.POST("/backup.json", bc.BackupJSON)
|
||||
|
|
|
@ -25,12 +25,11 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
|
||||
api "git.happydns.org/happyDomain/internal/api/route"
|
||||
"git.happydns.org/happyDomain/internal/config"
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func DeclareRoutes(cfg *config.Options, router *gin.Engine, s storage.Storage, dependancies happydns.UsecaseDependancies) {
|
||||
func DeclareRoutes(cfg *happydns.Options, router *gin.Engine, s storage.Storage, dependancies happydns.UsecaseDependancies) {
|
||||
apiRoutes := router.Group("/api")
|
||||
|
||||
declareBackupRoutes(cfg, apiRoutes, s)
|
||||
|
|
|
@ -25,11 +25,11 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/api-admin/controller"
|
||||
"git.happydns.org/happyDomain/internal/config"
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func declareSessionsRoutes(cfg *config.Options, router *gin.RouterGroup, store storage.Storage) {
|
||||
func declareSessionsRoutes(cfg *happydns.Options, router *gin.RouterGroup, store storage.Storage) {
|
||||
sc := controller.NewSessionController(cfg, store)
|
||||
|
||||
router.DELETE("/sessions", sc.DeleteSessions)
|
||||
|
|
|
@ -31,6 +31,7 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
|
@ -39,7 +40,6 @@ import (
|
|||
"golang.org/x/oauth2"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/api/middleware"
|
||||
"git.happydns.org/happyDomain/internal/config"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
|
@ -48,23 +48,39 @@ const (
|
|||
)
|
||||
|
||||
type OIDCProvider struct {
|
||||
config *config.Options
|
||||
config *happydns.Options
|
||||
authService happydns.AuthenticationUsecase
|
||||
oauth2config *oauth2.Config
|
||||
oidcVerifier *oidc.IDTokenVerifier
|
||||
}
|
||||
|
||||
func NewOIDCProvider(cfg *config.Options, authService happydns.AuthenticationUsecase) *OIDCProvider {
|
||||
func GetOIDCProvider(o *happydns.Options, ctx context.Context) (*oidc.Provider, error) {
|
||||
return oidc.NewProvider(ctx, strings.TrimSuffix(o.OIDCClients[0].ProviderURL.String(), "/.well-known/openid-configuration"))
|
||||
}
|
||||
|
||||
func GetOAuth2Config(o *happydns.Options, provider *oidc.Provider) *oauth2.Config {
|
||||
oauth2Config := oauth2.Config{
|
||||
ClientID: o.OIDCClients[0].ClientID,
|
||||
ClientSecret: o.OIDCClients[0].ClientSecret,
|
||||
RedirectURL: o.GetAuthURL().String(),
|
||||
Endpoint: provider.Endpoint(),
|
||||
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
||||
}
|
||||
|
||||
return &oauth2Config
|
||||
}
|
||||
|
||||
func NewOIDCProvider(cfg *happydns.Options, authService happydns.AuthenticationUsecase) *OIDCProvider {
|
||||
// Initialize OIDC
|
||||
provider, err := cfg.GetOIDCProvider(context.Background())
|
||||
provider, err := GetOIDCProvider(cfg, context.Background())
|
||||
if err != nil {
|
||||
log.Fatal("Unable to instantiate OIDC Provider:", err)
|
||||
}
|
||||
|
||||
oauth2Config := cfg.GetOAuth2Config(provider)
|
||||
oauth2Config := GetOAuth2Config(cfg, provider)
|
||||
|
||||
oidcVerifier := provider.Verifier(&oidc.Config{
|
||||
ClientID: config.OIDCClientID,
|
||||
ClientID: cfg.OIDCClients[0].ClientID,
|
||||
})
|
||||
|
||||
return &OIDCProvider{
|
||||
|
|
|
@ -29,22 +29,21 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/api/controller"
|
||||
"git.happydns.org/happyDomain/internal/config"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func DeclareAuthenticationRoutes(cfg *config.Options, baserouter, apirouter *gin.RouterGroup, dependancies happydns.UsecaseDependancies) *controller.LoginController {
|
||||
func DeclareAuthenticationRoutes(cfg *happydns.Options, baserouter, apirouter *gin.RouterGroup, dependancies happydns.UsecaseDependancies) *controller.LoginController {
|
||||
lc := controller.NewLoginController(dependancies.AuthenticationUsecase())
|
||||
|
||||
apirouter.POST("/auth", lc.Login)
|
||||
apirouter.POST("/auth/logout", lc.Logout)
|
||||
|
||||
if cfg.GetOIDCProviderURL() != "" {
|
||||
if len(cfg.OIDCClients) > 0 {
|
||||
oidcp := controller.NewOIDCProvider(cfg, dependancies.AuthenticationUsecase())
|
||||
|
||||
authRoutes := baserouter.Group("/auth")
|
||||
|
||||
providerurl, _ := url.Parse(cfg.GetOIDCProviderURL())
|
||||
providerurl, _ := url.Parse(cfg.OIDCClients[0].ProviderURL.String())
|
||||
authRoutes.GET("has_oidc", func(c *gin.Context) {
|
||||
parts := strings.Split(strings.TrimSuffix(providerurl.Host, "."), ".")
|
||||
if len(parts) > 2 {
|
||||
|
|
|
@ -25,7 +25,6 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/api/middleware"
|
||||
"git.happydns.org/happyDomain/internal/config"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
|
@ -49,7 +48,7 @@ import (
|
|||
// @name Authorization
|
||||
// @description Description for what is this security definition being used
|
||||
|
||||
func DeclareRoutes(cfg *config.Options, router *gin.Engine, dependancies happydns.UsecaseDependancies) {
|
||||
func DeclareRoutes(cfg *happydns.Options, router *gin.Engine, dependancies happydns.UsecaseDependancies) {
|
||||
// Declare routes
|
||||
baseRoutes := router.Group("")
|
||||
|
||||
|
|
|
@ -33,13 +33,13 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
|
||||
admin "git.happydns.org/happyDomain/internal/api-admin/route"
|
||||
"git.happydns.org/happyDomain/internal/config"
|
||||
"git.happydns.org/happyDomain/internal/usecase"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
type Admin struct {
|
||||
router *gin.Engine
|
||||
cfg *config.Options
|
||||
cfg *happydns.Options
|
||||
srv *http.Server
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
|
||||
api "git.happydns.org/happyDomain/internal/api/route"
|
||||
"git.happydns.org/happyDomain/internal/config"
|
||||
"git.happydns.org/happyDomain/internal/mailer"
|
||||
"git.happydns.org/happyDomain/internal/newsletter"
|
||||
"git.happydns.org/happyDomain/internal/session"
|
||||
|
@ -59,7 +58,7 @@ type Usecases struct {
|
|||
}
|
||||
|
||||
type App struct {
|
||||
cfg *config.Options
|
||||
cfg *happydns.Options
|
||||
mailer *mailer.Mailer
|
||||
newsletter happydns.NewsletterSubscriptor
|
||||
router *gin.Engine
|
||||
|
@ -125,7 +124,7 @@ func (a *App) ZoneUsecase() happydns.ZoneUsecase {
|
|||
return a.usecases.zone
|
||||
}
|
||||
|
||||
func NewApp(cfg *config.Options) *App {
|
||||
func NewApp(cfg *happydns.Options) *App {
|
||||
app := &App{
|
||||
cfg: cfg,
|
||||
}
|
||||
|
@ -140,7 +139,7 @@ func NewApp(cfg *config.Options) *App {
|
|||
return app
|
||||
}
|
||||
|
||||
func NewAppWithStorage(cfg *config.Options, store storage.Storage) *App {
|
||||
func NewAppWithStorage(cfg *happydns.Options, store storage.Storage) *App {
|
||||
app := &App{
|
||||
cfg: cfg,
|
||||
store: store,
|
||||
|
@ -191,9 +190,9 @@ func (app *App) initStorageEngine() {
|
|||
}
|
||||
|
||||
func (app *App) initNewsletter() {
|
||||
if app.cfg.ListmonkURL.URL != nil {
|
||||
if app.cfg.ListmonkURL.String() != "" {
|
||||
app.newsletter = &newsletter.ListmonkNewsletterSubscription{
|
||||
ListmonkURL: app.cfg.ListmonkURL.URL,
|
||||
ListmonkURL: &app.cfg.ListmonkURL,
|
||||
ListmonkId: app.cfg.ListmonkId,
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -34,7 +34,6 @@ import (
|
|||
"time"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/api/controller"
|
||||
"git.happydns.org/happyDomain/internal/config"
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
@ -45,7 +44,7 @@ const (
|
|||
)
|
||||
|
||||
type insightsCollector struct {
|
||||
cfg *config.Options
|
||||
cfg *happydns.Options
|
||||
store storage.Storage
|
||||
stop chan bool
|
||||
}
|
||||
|
@ -117,11 +116,12 @@ func (c *insightsCollector) collect() (*happydns.Insights, error) {
|
|||
data.Config.DisableEmbeddedLogin = c.cfg.DisableEmbeddedLogin
|
||||
data.Config.DisableProviders = c.cfg.DisableProviders
|
||||
data.Config.DisableRegistration = c.cfg.DisableRegistration
|
||||
data.Config.HasBaseURL = c.cfg.GetBasePath() != ""
|
||||
data.Config.HasBaseURL = c.cfg.BasePath != ""
|
||||
data.Config.HasDevProxy = c.cfg.DevProxy != ""
|
||||
data.Config.HasExternalAuth = c.cfg.ExternalAuth.String() != ""
|
||||
data.Config.HasListmonkURL = c.cfg.ListmonkURL.String() != ""
|
||||
data.Config.LocalBind = strings.HasPrefix(c.cfg.Bind, "127.0.0.1:") || strings.HasPrefix(c.cfg.Bind, "[::1]:")
|
||||
data.Config.NbOidcProviders = len(c.cfg.OIDCClients)
|
||||
data.Config.NoAuthActive = c.cfg.NoAuth
|
||||
data.Config.NoMail = c.cfg.NoMail
|
||||
data.Config.NonUnixAdminBind = strings.Contains(c.cfg.AdminBind, ":")
|
||||
|
|
|
@ -26,26 +26,27 @@ import (
|
|||
"fmt"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
// declareFlags registers flags for the structure Options.
|
||||
func (o *Options) declareFlags() {
|
||||
func declareFlags(o *happydns.Options) {
|
||||
flag.StringVar(&o.DevProxy, "dev", o.DevProxy, "Proxify traffic to this host for static assets")
|
||||
flag.StringVar(&o.AdminBind, "admin-bind", o.AdminBind, "Bind port/socket for administration interface")
|
||||
flag.StringVar(&o.Bind, "bind", ":8081", "Bind port/socket")
|
||||
flag.BoolVar(&o.DisableProviders, "disable-providers-edit", o.DisableProviders, "Disallow all actions on provider (add/edit/delete)")
|
||||
flag.BoolVar(&o.DisableRegistration, "disable-registration", o.DisableRegistration, "Forbids new account creation through public form/API (still allow registration from external services)")
|
||||
flag.BoolVar(&o.DisableEmbeddedLogin, "disable-embedded-login", o.DisableEmbeddedLogin, "Disables the internal user/password login in favor of external-auth or OIDC")
|
||||
flag.Var(&o.ExternalURL, "externalurl", "Begining of the URL, before the base, that should be used eg. in mails")
|
||||
flag.StringVar(&o.baseURL, "baseurl", o.baseURL, "URL prepended to each URL")
|
||||
flag.Var(&URL{&o.ExternalURL}, "externalurl", "Begining of the URL, before the base, that should be used eg. in mails")
|
||||
flag.StringVar(&o.BasePath, "baseurl", o.BasePath, "URL prepended to each URL")
|
||||
flag.StringVar(&o.DefaultNameServer, "default-ns", o.DefaultNameServer, "Adress to the default name server")
|
||||
flag.Var(&o.StorageEngine, "storage-engine", fmt.Sprintf("Select the storage engine between %v", storage.GetStorageEngines()))
|
||||
flag.StringVar(&o.StorageEngine, "storage-engine", o.StorageEngine, fmt.Sprintf("Select the storage engine between %v", storage.GetStorageEngines()))
|
||||
flag.BoolVar(&o.NoAuth, "no-auth", false, "Disable user access control, use default account")
|
||||
flag.Var(&o.JWTSecretKey, "jwt-secret-key", "Secret key used to verify JWT authentication tokens (a random secret is used if undefined)")
|
||||
flag.Var(&o.ExternalAuth, "external-auth", "Base URL to use for login and registration (use embedded forms if left empty)")
|
||||
flag.Var(&JWTSecretKey{&o.JWTSecretKey}, "jwt-secret-key", "Secret key used to verify JWT authentication tokens (a random secret is used if undefined)")
|
||||
flag.Var(&URL{&o.ExternalAuth}, "external-auth", "Base URL to use for login and registration (use embedded forms if left empty)")
|
||||
flag.BoolVar(&o.OptOutInsights, "opt-out-insights", false, "Disable the anonymous usage statistics report. If you care about this project and don't participate in discussions, don't opt-out.")
|
||||
|
||||
flag.Var(&o.ListmonkURL, "newsletter-server-url", "Base URL of the listmonk newsletter server")
|
||||
flag.Var(&URL{&o.ListmonkURL}, "newsletter-server-url", "Base URL of the listmonk newsletter server")
|
||||
flag.IntVar(&o.ListmonkId, "newsletter-id", 1, "Listmonk identifier of the list receiving the new user")
|
||||
|
||||
flag.BoolVar(&o.NoMail, "no-mail", o.NoMail, "Disable all automatic mails, skip email verification at registration")
|
||||
|
@ -60,11 +61,11 @@ func (o *Options) declareFlags() {
|
|||
}
|
||||
|
||||
// parseCLI parse the flags and treats extra args as configuration filename.
|
||||
func (o *Options) parseCLI() error {
|
||||
func parseCLI(o *happydns.Options) error {
|
||||
flag.Parse()
|
||||
|
||||
for _, conf := range flag.Args() {
|
||||
err := o.parseFile(conf)
|
||||
err := parseFile(o, conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -32,104 +32,30 @@ import (
|
|||
"path"
|
||||
"strings"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
// Options stores the configuration of the software.
|
||||
type Options struct {
|
||||
// AdminBind is the address:port or unix socket used to serve the admin
|
||||
// API.
|
||||
AdminBind string
|
||||
|
||||
// Bind is the address:port used to bind the main interface with API.
|
||||
Bind string
|
||||
|
||||
// BaseURL is the relative path where begins the root of the app.
|
||||
baseURL string
|
||||
|
||||
// DevProxy is the URL that override static assets.
|
||||
DevProxy string
|
||||
|
||||
// DefaultNameServer is the NS server suggested by default.
|
||||
DefaultNameServer string
|
||||
|
||||
// DisableProviders should disallow all actions on provider (add/edit/delete) through public API.
|
||||
DisableProviders bool
|
||||
|
||||
// DisableRegistration forbids all new registration using the public form/API.
|
||||
DisableRegistration bool
|
||||
|
||||
// DisableEmbeddedLogin disables the internal user/password login in favor of ExternalAuth or OIDC.
|
||||
DisableEmbeddedLogin bool
|
||||
|
||||
// ExternalAuth is the URL of the login form to use instead of the embedded one.
|
||||
ExternalAuth URL
|
||||
|
||||
// ExternalURL keeps the URL used in communications (such as email,
|
||||
// ...), when it needs to use complete URL, not only relative parts.
|
||||
ExternalURL URL
|
||||
|
||||
// JWTSecretKey stores the private key to sign and verify JWT tokens.
|
||||
JWTSecretKey JWTSecretKey
|
||||
|
||||
// JWTSigningMethod is the signing method to check token signature.
|
||||
JWTSigningMethod string
|
||||
|
||||
// NoAuth controls if there is user access control or not.
|
||||
NoAuth bool
|
||||
|
||||
// OptOutInsights disable the anonymous usage statistics report.
|
||||
OptOutInsights bool
|
||||
|
||||
// StorageEngine points to the storage engine used.
|
||||
StorageEngine storage.StorageEngine
|
||||
|
||||
ListmonkURL URL
|
||||
ListmonkId int
|
||||
|
||||
// MailFrom holds the content of the From field for all e-mails that
|
||||
// will be send.
|
||||
MailFrom mail.Address
|
||||
|
||||
NoMail bool
|
||||
MailSMTPHost string
|
||||
MailSMTPPort uint
|
||||
MailSMTPUsername string
|
||||
MailSMTPPassword string
|
||||
MailSMTPTLSSNoVerify bool
|
||||
}
|
||||
|
||||
// GetBaseURL returns the full url to the absolute ExternalURL, including BaseURL.
|
||||
func (o *Options) GetBaseURL() string {
|
||||
return fmt.Sprintf("%s%s", o.ExternalURL.URL.String(), o.baseURL)
|
||||
}
|
||||
|
||||
// GetBasePath returns the baseURL.
|
||||
func (o *Options) GetBasePath() string {
|
||||
return o.baseURL
|
||||
}
|
||||
|
||||
// ConsolidateConfig fills an Options struct by reading configuration from
|
||||
// config files, environment, then command line.
|
||||
//
|
||||
// Should be called only one time.
|
||||
func ConsolidateConfig() (opts *Options, err error) {
|
||||
func ConsolidateConfig() (opts *happydns.Options, err error) {
|
||||
u, _ := url.Parse("http://localhost:8081")
|
||||
|
||||
// Define defaults options
|
||||
opts = &Options{
|
||||
opts = &happydns.Options{
|
||||
AdminBind: "./happydomain.sock",
|
||||
baseURL: "/",
|
||||
BasePath: "/",
|
||||
Bind: ":8081",
|
||||
DefaultNameServer: "127.0.0.1:53",
|
||||
ExternalURL: URL{URL: u},
|
||||
ExternalURL: *u,
|
||||
JWTSigningMethod: "HS512",
|
||||
MailFrom: mail.Address{Name: "happyDomain", Address: "happydomain@localhost"},
|
||||
MailSMTPPort: 587,
|
||||
StorageEngine: storage.StorageEngine("leveldb"),
|
||||
StorageEngine: "leveldb",
|
||||
}
|
||||
|
||||
opts.declareFlags()
|
||||
declareFlags(opts)
|
||||
|
||||
// Establish a list of possible configuration file locations
|
||||
configLocations := []string{
|
||||
|
@ -146,7 +72,7 @@ func ConsolidateConfig() (opts *Options, err error) {
|
|||
for _, filename := range configLocations {
|
||||
if _, e := os.Stat(filename); !os.IsNotExist(e) {
|
||||
log.Printf("Loading configuration from %s\n", filename)
|
||||
err = opts.parseFile(filename)
|
||||
err = parseFile(opts, filename)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -155,22 +81,22 @@ func ConsolidateConfig() (opts *Options, err error) {
|
|||
}
|
||||
|
||||
// Then, overwrite that by what is present in the environment
|
||||
err = opts.parseEnvironmentVariables()
|
||||
err = parseEnvironmentVariables(opts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Finaly, command line takes precedence
|
||||
err = opts.parseCLI()
|
||||
err = parseCLI(opts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Sanitize options
|
||||
if opts.baseURL != "/" {
|
||||
opts.baseURL = path.Clean(opts.baseURL)
|
||||
if opts.BasePath != "/" {
|
||||
opts.BasePath = path.Clean(opts.BasePath)
|
||||
} else {
|
||||
opts.baseURL = ""
|
||||
opts.BasePath = ""
|
||||
}
|
||||
|
||||
if opts.NoMail && opts.MailSMTPHost != "" {
|
||||
|
@ -178,26 +104,26 @@ func ConsolidateConfig() (opts *Options, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
if opts.ExternalURL.URL.Host == "" || opts.ExternalURL.URL.Scheme == "" {
|
||||
u, err2 := url.Parse("http://" + opts.ExternalURL.URL.String())
|
||||
if opts.ExternalURL.Host == "" || opts.ExternalURL.Scheme == "" {
|
||||
u, err2 := url.Parse("http://" + opts.ExternalURL.String())
|
||||
if err2 == nil {
|
||||
opts.ExternalURL.URL = u
|
||||
opts.ExternalURL = *u
|
||||
} else {
|
||||
err = fmt.Errorf("You defined an external URL without a scheme. The expected value is eg. http://localhost:8081")
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(opts.ExternalURL.URL.Path) > 1 {
|
||||
if opts.baseURL != "" && opts.baseURL != opts.ExternalURL.URL.Path {
|
||||
if len(opts.ExternalURL.Path) > 1 {
|
||||
if opts.BasePath != "" && opts.BasePath != opts.ExternalURL.Path {
|
||||
err = fmt.Errorf("You defined both baseurl and a path to externalurl that are different. Define only one of those.")
|
||||
return
|
||||
}
|
||||
|
||||
opts.baseURL = path.Clean(opts.ExternalURL.URL.Path)
|
||||
opts.BasePath = path.Clean(opts.ExternalURL.Path)
|
||||
}
|
||||
opts.ExternalURL.URL.Path = ""
|
||||
opts.ExternalURL.URL.Fragment = ""
|
||||
opts.ExternalURL.URL.RawQuery = ""
|
||||
opts.ExternalURL.Path = ""
|
||||
opts.ExternalURL.Fragment = ""
|
||||
opts.ExternalURL.RawQuery = ""
|
||||
|
||||
if len(opts.JWTSecretKey) == 0 {
|
||||
opts.JWTSecretKey = make([]byte, 32)
|
||||
|
@ -207,12 +133,17 @@ func ConsolidateConfig() (opts *Options, err error) {
|
|||
}
|
||||
}
|
||||
|
||||
err = ExtendsConfigWithOIDC(opts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// parseLine treats a config line and place the read value in the variable
|
||||
// declared to the corresponding flag.
|
||||
func (o *Options) parseLine(line string) (err error) {
|
||||
func parseLine(o *happydns.Options, line string) (err error) {
|
||||
fields := strings.SplitN(line, "=", 2)
|
||||
orig_key := strings.TrimSpace(fields[0])
|
||||
value := strings.TrimSpace(fields[1])
|
||||
|
|
|
@ -19,18 +19,20 @@
|
|||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package config // import "git.happydns.org/happyDomain/config"
|
||||
package config // import "git.happydns.org/happyDomain/internal/config"
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func TestParseLine(t *testing.T) {
|
||||
cfg := Options{}
|
||||
cfg.declareFlags()
|
||||
cfg := &happydns.Options{}
|
||||
declareFlags(cfg)
|
||||
|
||||
err := cfg.parseLine("HAPPYDOMAIN_BIND=:8080")
|
||||
err := parseLine(cfg, "HAPPYDOMAIN_BIND=:8080")
|
||||
if err != nil {
|
||||
t.Fatalf(`parseLine("BIND=:8080") => %v`, err.Error())
|
||||
}
|
||||
|
@ -38,30 +40,30 @@ func TestParseLine(t *testing.T) {
|
|||
t.Fatalf(`parseLine("BIND=:8080") = %q, want ":8080"`, cfg.Bind)
|
||||
}
|
||||
|
||||
err = cfg.parseLine("BASEURL=/base")
|
||||
err = parseLine(cfg, "BASEURL=/base")
|
||||
if err != nil {
|
||||
t.Fatalf(`parseLine("BASEURL=/base") => %v`, err.Error())
|
||||
}
|
||||
if cfg.baseURL != "/base" {
|
||||
t.Fatalf(`parseLine("BASEURL=/base") = %q, want "/base"`, cfg.baseURL)
|
||||
if cfg.BasePath != "/base" {
|
||||
t.Fatalf(`parseLine("BASEURL=/base") = %q, want "/base"`, cfg.BasePath)
|
||||
}
|
||||
|
||||
cfg.parseLine("EXTERNALURL=https://happydomain.org")
|
||||
parseLine(cfg, "EXTERNALURL=https://happydomain.org")
|
||||
if cfg.ExternalURL.String() != "https://happydomain.org" {
|
||||
t.Fatalf(`parseLine("EXTERNAL_URL=https://happydomain.org") = %q, want "https://happydomain.org"`, cfg.ExternalURL)
|
||||
t.Fatalf(`parseLine("EXTERNAL_URL=https://happydomain.org") = %q, want "https://happydomain.org"`, cfg.ExternalURL.String())
|
||||
}
|
||||
|
||||
cfg.parseLine("DEFAULT-NS=42.42.42.42:5353")
|
||||
parseLine(cfg, "DEFAULT-NS=42.42.42.42:5353")
|
||||
if cfg.DefaultNameServer != "42.42.42.42:5353" {
|
||||
t.Fatalf(`parseLine("DEFAULT-NS=42.42.42.42:5353") = %q, want "42.42.42.42:5353"`, cfg.DefaultNameServer)
|
||||
}
|
||||
|
||||
cfg.parseLine("DEFAULT_NS=42.42.42.42:3535")
|
||||
parseLine(cfg, "DEFAULT_NS=42.42.42.42:3535")
|
||||
if cfg.DefaultNameServer != "42.42.42.42:3535" {
|
||||
t.Fatalf(`parseLine("DEFAULT_NS=42.42.42.42:3535") = %q, want "42.42.42.42:3535"`, cfg.DefaultNameServer)
|
||||
}
|
||||
|
||||
err = cfg.parseLine("NO_AUTH=true")
|
||||
err = parseLine(cfg, "NO_AUTH=true")
|
||||
if err != nil {
|
||||
t.Fatalf(`parseLine("NO_AUTH=true") => %v`, err.Error())
|
||||
}
|
||||
|
@ -73,8 +75,8 @@ func TestParseLine(t *testing.T) {
|
|||
func TestGetBaseURL(t *testing.T) {
|
||||
u, _ := url.Parse("http://localhost:8081")
|
||||
|
||||
cfg := Options{
|
||||
ExternalURL: URL{URL: u},
|
||||
cfg := &happydns.Options{
|
||||
ExternalURL: *u,
|
||||
}
|
||||
|
||||
builded_url := cfg.GetBaseURL()
|
||||
|
@ -82,7 +84,7 @@ func TestGetBaseURL(t *testing.T) {
|
|||
t.Fatalf(`GetBaseURL() = %q, want "http://localhost:8081"`, builded_url)
|
||||
}
|
||||
|
||||
cfg.baseURL = "/base"
|
||||
cfg.BasePath = "/base"
|
||||
|
||||
builded_url = cfg.GetBaseURL()
|
||||
if builded_url != "http://localhost:8081/base" {
|
||||
|
|
|
@ -27,10 +27,12 @@ import (
|
|||
"net/url"
|
||||
)
|
||||
|
||||
type JWTSecretKey []byte
|
||||
type JWTSecretKey struct {
|
||||
Secret *[]byte
|
||||
}
|
||||
|
||||
func (i *JWTSecretKey) String() string {
|
||||
return base64.StdEncoding.EncodeToString(*i)
|
||||
return base64.StdEncoding.EncodeToString(*i.Secret)
|
||||
}
|
||||
|
||||
func (i *JWTSecretKey) Set(value string) error {
|
||||
|
@ -39,7 +41,7 @@ func (i *JWTSecretKey) Set(value string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
*i = z
|
||||
*i.Secret = z
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -83,6 +85,6 @@ func (i *URL) Set(value string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
i.URL = u
|
||||
*i.URL = *u
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -25,14 +25,16 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
// parseEnvironmentVariables analyzes all the environment variables to find
|
||||
// each one starting by HAPPYDOMAIN_
|
||||
func (o *Options) parseEnvironmentVariables() (err error) {
|
||||
func parseEnvironmentVariables(o *happydns.Options) (err error) {
|
||||
for _, line := range os.Environ() {
|
||||
if strings.HasPrefix(line, "HAPPYDOMAIN_") || strings.HasPrefix(line, "HAPPYDNS_") {
|
||||
err := o.parseLine(line)
|
||||
err := parseLine(o, line)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error in environment (%q): %w", line, err)
|
||||
}
|
||||
|
|
|
@ -26,11 +26,13 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
// parseFile opens the file at the given filename path, then treat each line
|
||||
// not starting with '#' as a configuration statement.
|
||||
func (o *Options) parseFile(filename string) error {
|
||||
func parseFile(o *happydns.Options, filename string) error {
|
||||
fp, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -43,7 +45,7 @@ func (o *Options) parseFile(filename string) error {
|
|||
n += 1
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if len(line) > 0 && !strings.HasPrefix(line, "#") && strings.Index(line, "=") > 0 {
|
||||
err := o.parseLine(line)
|
||||
err := parseLine(o, line)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%v:%d: error in configuration: %w", filename, n, err)
|
||||
}
|
||||
|
|
|
@ -23,6 +23,10 @@
|
|||
|
||||
package config
|
||||
|
||||
func (o *Options) GetOIDCProviderURL() string {
|
||||
return ""
|
||||
import (
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func ExtendsConfigWithOIDC(o *happydns.Options) error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -24,50 +24,32 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"golang.org/x/oauth2"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
var (
|
||||
OIDCClientID string
|
||||
oidcClientID string
|
||||
oidcClientSecret string
|
||||
OIDCProviderURL string
|
||||
oidcProviderURL url.URL
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&OIDCClientID, "oidc-client-id", OIDCClientID, "ClientID for OIDC")
|
||||
flag.StringVar(&oidcClientID, "oidc-client-id", oidcClientID, "ClientID for OIDC")
|
||||
flag.StringVar(&oidcClientSecret, "oidc-client-secret", oidcClientSecret, "Secret for OIDC")
|
||||
flag.StringVar(&OIDCProviderURL, "oidc-provider-url", OIDCProviderURL, "Base URL of the OpenId Connect service")
|
||||
flag.Var(&URL{&oidcProviderURL}, "oidc-provider-url", "Base URL of the OpenId Connect service")
|
||||
}
|
||||
|
||||
func (o *Options) GetAuthURL() *url.URL {
|
||||
redirecturl := *o.ExternalURL.URL
|
||||
redirecturl.Path = path.Join(redirecturl.Path, o.baseURL, "auth", "callback")
|
||||
return &redirecturl
|
||||
}
|
||||
|
||||
func (o *Options) GetOIDCProvider(ctx context.Context) (*oidc.Provider, error) {
|
||||
return oidc.NewProvider(ctx, strings.TrimSuffix(OIDCProviderURL, "/.well-known/openid-configuration"))
|
||||
}
|
||||
|
||||
func (o *Options) GetOIDCProviderURL() string {
|
||||
return OIDCProviderURL
|
||||
}
|
||||
|
||||
func (o *Options) GetOAuth2Config(provider *oidc.Provider) *oauth2.Config {
|
||||
oauth2Config := oauth2.Config{
|
||||
ClientID: OIDCClientID,
|
||||
func ExtendsConfigWithOIDC(o *happydns.Options) error {
|
||||
if oidcProviderURL.String() != "" {
|
||||
o.OIDCClients = append(o.OIDCClients, happydns.OIDCSettings{
|
||||
ClientID: oidcClientID,
|
||||
ClientSecret: oidcClientSecret,
|
||||
RedirectURL: o.GetAuthURL().String(),
|
||||
Endpoint: provider.Endpoint(),
|
||||
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
||||
ProviderURL: oidcProviderURL,
|
||||
})
|
||||
}
|
||||
|
||||
return &oauth2Config
|
||||
return nil
|
||||
}
|
||||
|
|
82
internal/config/oidc_test.go
Normal file
82
internal/config/oidc_test.go
Normal file
|
@ -0,0 +1,82 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2025 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||
//
|
||||
// For AGPL licensing:
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build !nooidc
|
||||
|
||||
package config // import "git.happydns.org/happyDomain/internal/config"
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
func TestOIDCConfig(t *testing.T) {
|
||||
cfg := &happydns.Options{}
|
||||
|
||||
err := parseLine(cfg, "HAPPYDOMAIN_OIDC_CLIENT_ID=test-oidc-1")
|
||||
if err != nil {
|
||||
t.Fatalf(`parseLine("HAPPYDOMAIN_OIDC_CLIENT_ID=test-oidc-1") => %v`, err.Error())
|
||||
}
|
||||
if oidcClientID != "test-oidc-1" {
|
||||
t.Fatalf(`parseLine("HAPPYDOMAIN_OIDC_CLIENT_ID=test-oidc-1") = %q, want "test-oidc-1"`, oidcClientID)
|
||||
}
|
||||
|
||||
err = parseLine(cfg, "HAPPYDOMAIN_OIDC_CLIENT_SECRET=s3cret$")
|
||||
if err != nil {
|
||||
t.Fatalf(`parseLine("HAPPYDOMAIN_OIDC_CLIENT_SECRET=s3cret$") => %v`, err.Error())
|
||||
}
|
||||
if oidcClientSecret != "s3cret$" {
|
||||
t.Fatalf(`parseLine("HAPPYDOMAIN_OIDC_CLIENT_SECRET=s3cret$") = %q, want "s3cret$"`, oidcClientSecret)
|
||||
}
|
||||
|
||||
if oidcProviderURL.String() != "" {
|
||||
t.Fatalf(`before parseLine("HAPPYDOMAIN_OIDC_PROVIDER_URL") = %q, want ""`, oidcProviderURL.String())
|
||||
}
|
||||
|
||||
err = parseLine(cfg, "HAPPYDOMAIN_OIDC_PROVIDER_URL=https://localhost:12345/secret")
|
||||
if err != nil {
|
||||
t.Fatalf(`parseLine("HAPPYDOMAIN_OIDC_PROVIDER_URL=https://localhost:12345/secret") => %v`, err.Error())
|
||||
}
|
||||
if oidcProviderURL.String() != "https://localhost:12345/secret" {
|
||||
t.Fatalf(`parseLine("HAPPYDOMAIN_OIDC_PROVIDER_URL=https://localhost:12345/secret") = %q, want "https://localhost:12345/secret"`, cfg.Bind)
|
||||
}
|
||||
|
||||
// Test extended config
|
||||
err = ExtendsConfigWithOIDC(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf(`ExtendsConfigWithOIDC(cfg) => %v`, err.Error())
|
||||
}
|
||||
|
||||
if len(cfg.OIDCClients) != 1 {
|
||||
t.Fatalf(`len(cfg.OIDCClients) == %d, should be 1`, len(cfg.OIDCClients))
|
||||
}
|
||||
|
||||
if cfg.OIDCClients[0].ClientID != "test-oidc-1" {
|
||||
t.Fatalf(`cfg.OIDCClients[0].ClientID == %q, should be test-oidc-1`, cfg.OIDCClients[0].ClientID)
|
||||
}
|
||||
if cfg.OIDCClients[0].ClientSecret != "s3cret$" {
|
||||
t.Fatalf(`cfg.OIDCClients[0].ClientSecret == %q, should be test-oidc-1`, cfg.OIDCClients[0].ClientSecret)
|
||||
}
|
||||
if cfg.OIDCClients[0].ProviderURL.String() != "https://localhost:12345/secret" {
|
||||
t.Fatalf(`cfg.OIDCClients[0].ProviderURL == %q, should be https://localhost:12345/secret`, cfg.OIDCClients[0].ProviderURL.String())
|
||||
}
|
||||
}
|
|
@ -33,7 +33,6 @@ import (
|
|||
"github.com/gorilla/sessions"
|
||||
"github.com/mileusna/useragent"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/config"
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
@ -47,13 +46,13 @@ type SessionStore struct {
|
|||
storage storage.Storage
|
||||
}
|
||||
|
||||
func NewSessionStore(opts *config.Options, storage storage.Storage, keyPairs ...[]byte) *SessionStore {
|
||||
func NewSessionStore(opts *happydns.Options, storage storage.Storage, keyPairs ...[]byte) *SessionStore {
|
||||
store := &SessionStore{
|
||||
Codecs: securecookie.CodecsFromPairs(keyPairs...),
|
||||
options: &sessions.Options{
|
||||
Path: opts.GetBasePath() + "/",
|
||||
Path: opts.BasePath + "/",
|
||||
MaxAge: 86400 * 30,
|
||||
Secure: opts.DevProxy == "" && opts.ExternalURL.URL.Scheme != "http",
|
||||
Secure: opts.DevProxy == "" && opts.ExternalURL.Scheme != "http",
|
||||
HttpOnly: true,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
},
|
||||
|
|
|
@ -34,11 +34,10 @@ func (i *StorageEngine) String() string {
|
|||
}
|
||||
|
||||
func (i *StorageEngine) Set(value string) (err error) {
|
||||
se := StorageEngine(value)
|
||||
if _, ok := StorageEngines[se]; !ok {
|
||||
if _, ok := StorageEngines[value]; !ok {
|
||||
return fmt.Errorf("Unexistant storage engine: please select one between: %v", GetStorageEngines())
|
||||
}
|
||||
*i = se
|
||||
*i = StorageEngine(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -48,7 +47,7 @@ type StorageInstanciation func() (Storage, error)
|
|||
|
||||
// StorageEngines lists all Storage implementations declared, with a
|
||||
// way to instanciate automatically each.
|
||||
var StorageEngines = map[StorageEngine]StorageInstanciation{}
|
||||
var StorageEngines = map[string]StorageInstanciation{}
|
||||
|
||||
// GetStorageEngines returns all declared Storage implementation.
|
||||
func GetStorageEngines() (se []string) {
|
||||
|
|
|
@ -27,7 +27,6 @@ import (
|
|||
"net/mail"
|
||||
"strings"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/config"
|
||||
"git.happydns.org/happyDomain/internal/helpers"
|
||||
"git.happydns.org/happyDomain/internal/mailer"
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
|
@ -35,12 +34,12 @@ import (
|
|||
)
|
||||
|
||||
type authUserUsecase struct {
|
||||
config *config.Options
|
||||
config *happydns.Options
|
||||
mailer *mailer.Mailer
|
||||
store storage.AuthUserAndSessionStorage
|
||||
}
|
||||
|
||||
func NewAuthUserUsecase(cfg *config.Options, m *mailer.Mailer, store storage.AuthUserAndSessionStorage) happydns.AuthUserUsecase {
|
||||
func NewAuthUserUsecase(cfg *happydns.Options, m *mailer.Mailer, store storage.AuthUserAndSessionStorage) happydns.AuthUserUsecase {
|
||||
return &authUserUsecase{
|
||||
config: cfg,
|
||||
mailer: m,
|
||||
|
|
|
@ -25,18 +25,17 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/config"
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
type loginUsecase struct {
|
||||
config *config.Options
|
||||
config *happydns.Options
|
||||
store storage.AuthenticationStorage
|
||||
userService happydns.UserUsecase
|
||||
}
|
||||
|
||||
func NewAuthenticationUsecase(cfg *config.Options, store storage.AuthenticationStorage, userService happydns.UserUsecase) happydns.AuthenticationUsecase {
|
||||
func NewAuthenticationUsecase(cfg *happydns.Options, store storage.AuthenticationStorage, userService happydns.UserUsecase) happydns.AuthenticationUsecase {
|
||||
return &loginUsecase{
|
||||
config: cfg,
|
||||
store: store,
|
||||
|
|
|
@ -22,15 +22,14 @@
|
|||
package usecase
|
||||
|
||||
import (
|
||||
"git.happydns.org/happyDomain/internal/config"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
type formUsecase struct {
|
||||
config *config.Options
|
||||
config *happydns.Options
|
||||
}
|
||||
|
||||
func NewFormUsecase(cfg *config.Options) happydns.FormUsecase {
|
||||
func NewFormUsecase(cfg *happydns.Options) happydns.FormUsecase {
|
||||
return &formUsecase{
|
||||
config: cfg,
|
||||
}
|
||||
|
|
|
@ -24,19 +24,18 @@ package usecase
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/config"
|
||||
"git.happydns.org/happyDomain/internal/forms"
|
||||
"git.happydns.org/happyDomain/internal/usecase/provider"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
type providerSettingsUsecase struct {
|
||||
config *config.Options
|
||||
config *happydns.Options
|
||||
providerService happydns.ProviderUsecase
|
||||
store provider.ProviderStorage
|
||||
}
|
||||
|
||||
func NewProviderSettingsUsecase(cfg *config.Options, ps happydns.ProviderUsecase, store provider.ProviderStorage) happydns.ProviderSettingsUsecase {
|
||||
func NewProviderSettingsUsecase(cfg *happydns.Options, ps happydns.ProviderUsecase, store provider.ProviderStorage) happydns.ProviderSettingsUsecase {
|
||||
return &providerSettingsUsecase{
|
||||
config: cfg,
|
||||
providerService: ps,
|
||||
|
|
|
@ -25,7 +25,6 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/config"
|
||||
"git.happydns.org/happyDomain/internal/storage"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
"git.happydns.org/happyDomain/providers"
|
||||
|
@ -33,10 +32,10 @@ import (
|
|||
|
||||
type providerUsecase struct {
|
||||
happydns.ProviderUsecase
|
||||
config *config.Options
|
||||
config *happydns.Options
|
||||
}
|
||||
|
||||
func NewProviderUsecase(cfg *config.Options, store storage.ProviderAndDomainStorage) happydns.ProviderUsecase {
|
||||
func NewProviderUsecase(cfg *happydns.Options, store storage.ProviderAndDomainStorage) happydns.ProviderUsecase {
|
||||
return &providerUsecase{
|
||||
ProviderUsecase: NewAdminProviderUsecase(store),
|
||||
config: cfg,
|
||||
|
|
|
@ -30,7 +30,6 @@ import (
|
|||
|
||||
"github.com/miekg/dns"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/config"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
|
@ -39,10 +38,10 @@ var (
|
|||
)
|
||||
|
||||
type resolverUsecase struct {
|
||||
config *config.Options
|
||||
config *happydns.Options
|
||||
}
|
||||
|
||||
func NewResolverUsecase(cfg *config.Options) happydns.ResolverUsecase {
|
||||
func NewResolverUsecase(cfg *happydns.Options) happydns.ResolverUsecase {
|
||||
return &resolverUsecase{
|
||||
config: cfg,
|
||||
}
|
||||
|
|
106
model/config.go
Normal file
106
model/config.go
Normal file
|
@ -0,0 +1,106 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2025 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||
//
|
||||
// For AGPL licensing:
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package happydns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/mail"
|
||||
"net/url"
|
||||
"path"
|
||||
)
|
||||
|
||||
// Options stores the configuration of the software.
|
||||
type Options struct {
|
||||
// AdminBind is the address:port or unix socket used to serve the admin
|
||||
// API.
|
||||
AdminBind string
|
||||
|
||||
// Bind is the address:port used to bind the main interface with API.
|
||||
Bind string
|
||||
|
||||
// BasePath is the relative path where begins the root of the app.
|
||||
BasePath string
|
||||
|
||||
// DevProxy is the URL that override static assets.
|
||||
DevProxy string
|
||||
|
||||
// DefaultNameServer is the NS server suggested by default.
|
||||
DefaultNameServer string
|
||||
|
||||
// DisableProviders should disallow all actions on provider (add/edit/delete) through public API.
|
||||
DisableProviders bool
|
||||
|
||||
// DisableRegistration forbids all new registration using the public form/API.
|
||||
DisableRegistration bool
|
||||
|
||||
// DisableEmbeddedLogin disables the internal user/password login in favor of ExternalAuth or OIDC.
|
||||
DisableEmbeddedLogin bool
|
||||
|
||||
// ExternalAuth is the URL of the login form to use instead of the embedded one.
|
||||
ExternalAuth url.URL
|
||||
|
||||
// ExternalURL keeps the URL used in communications (such as email,
|
||||
// ...), when it needs to use complete URL, not only relative parts.
|
||||
ExternalURL url.URL
|
||||
|
||||
// JWTSecretKey stores the private key to sign and verify JWT tokens.
|
||||
JWTSecretKey []byte
|
||||
|
||||
// JWTSigningMethod is the signing method to check token signature.
|
||||
JWTSigningMethod string
|
||||
|
||||
// NoAuth controls if there is user access control or not.
|
||||
NoAuth bool
|
||||
|
||||
// OptOutInsights disable the anonymous usage statistics report.
|
||||
OptOutInsights bool
|
||||
|
||||
// StorageEngine points to the storage engine used.
|
||||
StorageEngine string
|
||||
|
||||
ListmonkURL url.URL
|
||||
ListmonkId int
|
||||
|
||||
// MailFrom holds the content of the From field for all e-mails that
|
||||
// will be send.
|
||||
MailFrom mail.Address
|
||||
|
||||
NoMail bool
|
||||
MailSMTPHost string
|
||||
MailSMTPPort uint
|
||||
MailSMTPUsername string
|
||||
MailSMTPPassword string
|
||||
MailSMTPTLSSNoVerify bool
|
||||
|
||||
OIDCClients []OIDCSettings
|
||||
}
|
||||
|
||||
// GetBaseURL returns the full url to the absolute ExternalURL, including BaseURL.
|
||||
func (o *Options) GetBaseURL() string {
|
||||
return fmt.Sprintf("%s%s", o.ExternalURL.String(), o.BasePath)
|
||||
}
|
||||
|
||||
func (o *Options) GetAuthURL() *url.URL {
|
||||
redirecturl := o.ExternalURL
|
||||
redirecturl.Path = path.Join(redirecturl.Path, o.BasePath, "auth", "callback")
|
||||
return &redirecturl
|
||||
}
|
|
@ -43,6 +43,7 @@ type Insights struct {
|
|||
HasExternalAuth bool `json:"hasExternalAuth,omitempty"`
|
||||
HasListmonkURL bool `json:"hasListmonkURL,omitempty"`
|
||||
LocalBind bool `json:"localBind,omitempty"`
|
||||
NbOidcProviders int `json:"nbOidcProviders,omitempty"`
|
||||
NoAuthActive bool `json:"noAuthActive,omitempty"`
|
||||
NoMail bool `json:"noMail,omitempty"`
|
||||
NonUnixAdminBind bool `json:"nonUnixAdminBind,omitempty"`
|
||||
|
|
32
model/oidc.go
Normal file
32
model/oidc.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2024 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||
//
|
||||
// For AGPL licensing:
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package happydns
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type OIDCSettings struct {
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
ProviderURL url.URL
|
||||
}
|
|
@ -35,7 +35,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/config"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -55,7 +55,7 @@ func init() {
|
|||
flag.StringVar(&MsgHeaderColor, "msg-header-color", MsgHeaderColor, "Background color of the banner added at the top of the app")
|
||||
}
|
||||
|
||||
func DeclareRoutes(cfg *config.Options, router *gin.Engine) {
|
||||
func DeclareRoutes(cfg *happydns.Options, router *gin.Engine) {
|
||||
if cfg.DisableProviders {
|
||||
CustomHeadHTML += `<script type="text/javascript">window.disable_providers = true;</script>`
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ func DeclareRoutes(cfg *config.Options, router *gin.Engine) {
|
|||
CustomHeadHTML += `<script type="text/javascript">window.disable_embedded_login = true;</script>`
|
||||
}
|
||||
|
||||
if config.OIDCProviderURL != "" {
|
||||
if len(cfg.OIDCClients) > 0 {
|
||||
CustomHeadHTML += `<script type="text/javascript">window.oidc_configured = true;</script>`
|
||||
}
|
||||
|
||||
|
@ -129,7 +129,7 @@ func DeclareRoutes(cfg *config.Options, router *gin.Engine) {
|
|||
})
|
||||
}
|
||||
|
||||
func serveOrReverse(forced_url string, cfg *config.Options) gin.HandlerFunc {
|
||||
func serveOrReverse(forced_url string, cfg *happydns.Options) gin.HandlerFunc {
|
||||
if cfg.DevProxy != "" {
|
||||
// Forward to the Vue dev proxy
|
||||
return func(c *gin.Context) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue