mirror of
https://github.com/tinyauthapp/tinyauth.git
synced 2026-08-12 03:53:32 +08:00
122 lines
3.3 KiB
Go
122 lines
3.3 KiB
Go
package service
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
|
|
"github.com/tinyauthapp/tinyauth/internal/model"
|
|
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
|
"github.com/tinyauthapp/tinyauth/pkg/validators"
|
|
"go.uber.org/dig"
|
|
)
|
|
|
|
type LabelProvider interface {
|
|
Lookup(locator func(name string, app *model.App) bool) error
|
|
}
|
|
|
|
type AccessControlsService struct {
|
|
log *logger.Logger
|
|
config *model.Config
|
|
labelProvider LabelProvider
|
|
}
|
|
|
|
type AccessControlServiceInput struct {
|
|
dig.In
|
|
|
|
Log *logger.Logger
|
|
Config *model.Config
|
|
LabelProvider LabelProvider `optional:"true"`
|
|
}
|
|
|
|
func NewAccessControlsService(i AccessControlServiceInput) *AccessControlsService {
|
|
|
|
return &AccessControlsService{
|
|
log: i.Log,
|
|
config: i.Config,
|
|
labelProvider: i.LabelProvider,
|
|
}
|
|
}
|
|
|
|
func (service *AccessControlsService) getACLs(domain string, lookup func(locator func(name string, app *model.App) bool) error) (*model.App, error) {
|
|
v := validators.NewDomainValidator(validators.DomainValidatorOptions{})
|
|
|
|
var domainMatch *model.App
|
|
var nameMatch *model.App
|
|
var nameMatchedApps []string
|
|
|
|
locatorFunc := func(name string, app *model.App) bool {
|
|
if app.Config.Domain != "" {
|
|
err := v.Validate(app.Config.Domain, domain)
|
|
if err == nil {
|
|
service.log.App.Debug().Str("name", name).Msg("Found matching container by domain")
|
|
domainMatch = app
|
|
return true
|
|
} else if !errors.Is(err, validators.ErrHostnameMismatch) {
|
|
service.log.App.Debug().Str("name", name).Err(err).Msg("Domain validation failed")
|
|
}
|
|
}
|
|
if strings.HasPrefix(strings.ToLower(domain), strings.ToLower(name+".")) {
|
|
service.log.App.Debug().Str("name", name).Msg("Found matching container by app name")
|
|
nameMatch = app
|
|
nameMatchedApps = append(nameMatchedApps, name)
|
|
}
|
|
return false
|
|
}
|
|
|
|
err := lookup(locatorFunc)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if domainMatch != nil {
|
|
service.log.App.Debug().Str("domain", domain).Msg("Found matching app by domain")
|
|
return domainMatch, nil
|
|
}
|
|
|
|
if nameMatch == nil {
|
|
service.log.App.Debug().Str("domain", domain).Msg("No match found for domain, skipping")
|
|
return nil, nil
|
|
}
|
|
|
|
if len(nameMatchedApps) > 1 {
|
|
service.log.App.Warn().Str("domain", domain).Strs("apps", nameMatchedApps).Msg("Multiple apps matched domain by name, app names must be unique, using last match")
|
|
}
|
|
|
|
service.log.App.Debug().Str("domain", domain).Msg("Found matching app by app name")
|
|
return nameMatch, nil
|
|
}
|
|
|
|
func (service *AccessControlsService) lookupStaticACLs(domain string) (*model.App, error) {
|
|
return service.getACLs(domain, func(locator func(name string, app *model.App) bool) error {
|
|
for app, config := range service.config.Apps {
|
|
if ok := locator(app, &config); ok {
|
|
return nil
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func (service *AccessControlsService) GetAccessControls(domain string) (*model.App, error) {
|
|
// First check in the static config
|
|
app, err := service.lookupStaticACLs(domain)
|
|
|
|
// Will never return an error here, but we need to check it
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if app != nil {
|
|
service.log.App.Debug().Msg("Using static ACLs for app")
|
|
return app, nil
|
|
}
|
|
|
|
// If we have a label provider configured, try to get ACLs from it
|
|
if service.labelProvider != nil {
|
|
return service.getACLs(domain, service.labelProvider.Lookup)
|
|
}
|
|
|
|
// No labels
|
|
return nil, nil
|
|
}
|