mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2025-10-28 20:55:42 +00:00
Compare commits
2 Commits
57b7b66813
...
feat/analy
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
902574e501 | ||
|
|
aac19d4d5a |
@@ -94,6 +94,7 @@ func init() {
|
|||||||
{"resources-dir", "/data/resources", "Path to a directory containing custom resources (e.g. background image)."},
|
{"resources-dir", "/data/resources", "Path to a directory containing custom resources (e.g. background image)."},
|
||||||
{"database-path", "/data/tinyauth.db", "Path to the Sqlite database file."},
|
{"database-path", "/data/tinyauth.db", "Path to the Sqlite database file."},
|
||||||
{"trusted-proxies", "", "Comma separated list of trusted proxies (IP addresses or CIDRs) for correct client IP detection."},
|
{"trusted-proxies", "", "Comma separated list of trusted proxies (IP addresses or CIDRs) for correct client IP detection."},
|
||||||
|
{"disable-analytics", false, "Disable anonymous version collection."},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, opt := range configOptions {
|
for _, opt := range configOptions {
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
package bootstrap
|
package bootstrap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
"tinyauth/internal/config"
|
"tinyauth/internal/config"
|
||||||
"tinyauth/internal/controller"
|
"tinyauth/internal/controller"
|
||||||
"tinyauth/internal/middleware"
|
"tinyauth/internal/middleware"
|
||||||
@@ -29,40 +33,43 @@ type Service interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type BootstrapApp struct {
|
type BootstrapApp struct {
|
||||||
Config config.Config
|
config config.Config
|
||||||
|
uuid string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBootstrapApp(config config.Config) *BootstrapApp {
|
func NewBootstrapApp(config config.Config) *BootstrapApp {
|
||||||
return &BootstrapApp{
|
return &BootstrapApp{
|
||||||
Config: config,
|
config: config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *BootstrapApp) Setup() error {
|
func (app *BootstrapApp) Setup() error {
|
||||||
// Parse users
|
// Parse users
|
||||||
users, err := utils.GetUsers(app.Config.Users, app.Config.UsersFile)
|
users, err := utils.GetUsers(app.config.Users, app.config.UsersFile)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get OAuth configs
|
// Get OAuth configs
|
||||||
oauthProviders, err := utils.GetOAuthProvidersConfig(os.Environ(), os.Args, app.Config.AppURL)
|
oauthProviders, err := utils.GetOAuthProvidersConfig(os.Environ(), os.Args, app.config.AppURL)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get cookie domain
|
// Get cookie domain
|
||||||
cookieDomain, err := utils.GetCookieDomain(app.Config.AppURL)
|
cookieDomain, err := utils.GetCookieDomain(app.config.AppURL)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cookie names
|
// Cookie names
|
||||||
appUrl, _ := url.Parse(app.Config.AppURL) // Already validated
|
appUrl, _ := url.Parse(app.config.AppURL) // Already validated
|
||||||
cookieId := utils.GenerateIdentifier(appUrl.Hostname())
|
uuid := utils.GenerateUUID(appUrl.Hostname())
|
||||||
|
app.uuid = uuid
|
||||||
|
cookieId := strings.Split(uuid, "-")[0]
|
||||||
sessionCookieName := fmt.Sprintf("%s-%s", config.SessionCookieName, cookieId)
|
sessionCookieName := fmt.Sprintf("%s-%s", config.SessionCookieName, cookieId)
|
||||||
csrfCookieName := fmt.Sprintf("%s-%s", config.CSRFCookieName, cookieId)
|
csrfCookieName := fmt.Sprintf("%s-%s", config.CSRFCookieName, cookieId)
|
||||||
redirectCookieName := fmt.Sprintf("%s-%s", config.RedirectCookieName, cookieId)
|
redirectCookieName := fmt.Sprintf("%s-%s", config.RedirectCookieName, cookieId)
|
||||||
@@ -70,26 +77,26 @@ func (app *BootstrapApp) Setup() error {
|
|||||||
// Create configs
|
// Create configs
|
||||||
authConfig := service.AuthServiceConfig{
|
authConfig := service.AuthServiceConfig{
|
||||||
Users: users,
|
Users: users,
|
||||||
OauthWhitelist: app.Config.OAuthWhitelist,
|
OauthWhitelist: app.config.OAuthWhitelist,
|
||||||
SessionExpiry: app.Config.SessionExpiry,
|
SessionExpiry: app.config.SessionExpiry,
|
||||||
SecureCookie: app.Config.SecureCookie,
|
SecureCookie: app.config.SecureCookie,
|
||||||
CookieDomain: cookieDomain,
|
CookieDomain: cookieDomain,
|
||||||
LoginTimeout: app.Config.LoginTimeout,
|
LoginTimeout: app.config.LoginTimeout,
|
||||||
LoginMaxRetries: app.Config.LoginMaxRetries,
|
LoginMaxRetries: app.config.LoginMaxRetries,
|
||||||
SessionCookieName: sessionCookieName,
|
SessionCookieName: sessionCookieName,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup services
|
// Setup services
|
||||||
var ldapService *service.LdapService
|
var ldapService *service.LdapService
|
||||||
|
|
||||||
if app.Config.LdapAddress != "" {
|
if app.config.LdapAddress != "" {
|
||||||
ldapConfig := service.LdapServiceConfig{
|
ldapConfig := service.LdapServiceConfig{
|
||||||
Address: app.Config.LdapAddress,
|
Address: app.config.LdapAddress,
|
||||||
BindDN: app.Config.LdapBindDN,
|
BindDN: app.config.LdapBindDN,
|
||||||
BindPassword: app.Config.LdapBindPassword,
|
BindPassword: app.config.LdapBindPassword,
|
||||||
BaseDN: app.Config.LdapBaseDN,
|
BaseDN: app.config.LdapBaseDN,
|
||||||
Insecure: app.Config.LdapInsecure,
|
Insecure: app.config.LdapInsecure,
|
||||||
SearchFilter: app.Config.LdapSearchFilter,
|
SearchFilter: app.config.LdapSearchFilter,
|
||||||
}
|
}
|
||||||
|
|
||||||
ldapService = service.NewLdapService(ldapConfig)
|
ldapService = service.NewLdapService(ldapConfig)
|
||||||
@@ -104,7 +111,7 @@ func (app *BootstrapApp) Setup() error {
|
|||||||
|
|
||||||
// Bootstrap database
|
// Bootstrap database
|
||||||
databaseService := service.NewDatabaseService(service.DatabaseServiceConfig{
|
databaseService := service.NewDatabaseService(service.DatabaseServiceConfig{
|
||||||
DatabasePath: app.Config.DatabasePath,
|
DatabasePath: app.config.DatabasePath,
|
||||||
})
|
})
|
||||||
|
|
||||||
log.Debug().Str("service", fmt.Sprintf("%T", databaseService)).Msg("Initializing service")
|
log.Debug().Str("service", fmt.Sprintf("%T", databaseService)).Msg("Initializing service")
|
||||||
@@ -183,8 +190,8 @@ func (app *BootstrapApp) Setup() error {
|
|||||||
// Create engine
|
// Create engine
|
||||||
engine := gin.New()
|
engine := gin.New()
|
||||||
|
|
||||||
if len(app.Config.TrustedProxies) > 0 {
|
if len(app.config.TrustedProxies) > 0 {
|
||||||
engine.SetTrustedProxies(strings.Split(app.Config.TrustedProxies, ","))
|
engine.SetTrustedProxies(strings.Split(app.config.TrustedProxies, ","))
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Version != "development" {
|
if config.Version != "development" {
|
||||||
@@ -219,24 +226,24 @@ func (app *BootstrapApp) Setup() error {
|
|||||||
// Create controllers
|
// Create controllers
|
||||||
contextController := controller.NewContextController(controller.ContextControllerConfig{
|
contextController := controller.NewContextController(controller.ContextControllerConfig{
|
||||||
Providers: configuredProviders,
|
Providers: configuredProviders,
|
||||||
Title: app.Config.Title,
|
Title: app.config.Title,
|
||||||
AppURL: app.Config.AppURL,
|
AppURL: app.config.AppURL,
|
||||||
CookieDomain: cookieDomain,
|
CookieDomain: cookieDomain,
|
||||||
ForgotPasswordMessage: app.Config.ForgotPasswordMessage,
|
ForgotPasswordMessage: app.config.ForgotPasswordMessage,
|
||||||
BackgroundImage: app.Config.BackgroundImage,
|
BackgroundImage: app.config.BackgroundImage,
|
||||||
OAuthAutoRedirect: app.Config.OAuthAutoRedirect,
|
OAuthAutoRedirect: app.config.OAuthAutoRedirect,
|
||||||
}, apiRouter)
|
}, apiRouter)
|
||||||
|
|
||||||
oauthController := controller.NewOAuthController(controller.OAuthControllerConfig{
|
oauthController := controller.NewOAuthController(controller.OAuthControllerConfig{
|
||||||
AppURL: app.Config.AppURL,
|
AppURL: app.config.AppURL,
|
||||||
SecureCookie: app.Config.SecureCookie,
|
SecureCookie: app.config.SecureCookie,
|
||||||
CSRFCookieName: csrfCookieName,
|
CSRFCookieName: csrfCookieName,
|
||||||
RedirectCookieName: redirectCookieName,
|
RedirectCookieName: redirectCookieName,
|
||||||
CookieDomain: cookieDomain,
|
CookieDomain: cookieDomain,
|
||||||
}, apiRouter, authService, oauthBrokerService)
|
}, apiRouter, authService, oauthBrokerService)
|
||||||
|
|
||||||
proxyController := controller.NewProxyController(controller.ProxyControllerConfig{
|
proxyController := controller.NewProxyController(controller.ProxyControllerConfig{
|
||||||
AppURL: app.Config.AppURL,
|
AppURL: app.config.AppURL,
|
||||||
}, apiRouter, dockerService, authService)
|
}, apiRouter, dockerService, authService)
|
||||||
|
|
||||||
userController := controller.NewUserController(controller.UserControllerConfig{
|
userController := controller.NewUserController(controller.UserControllerConfig{
|
||||||
@@ -244,7 +251,7 @@ func (app *BootstrapApp) Setup() error {
|
|||||||
}, apiRouter, authService)
|
}, apiRouter, authService)
|
||||||
|
|
||||||
resourcesController := controller.NewResourcesController(controller.ResourcesControllerConfig{
|
resourcesController := controller.NewResourcesController(controller.ResourcesControllerConfig{
|
||||||
ResourcesDir: app.Config.ResourcesDir,
|
ResourcesDir: app.config.ResourcesDir,
|
||||||
}, mainRouter)
|
}, mainRouter)
|
||||||
|
|
||||||
healthController := controller.NewHealthController(apiRouter)
|
healthController := controller.NewHealthController(apiRouter)
|
||||||
@@ -264,8 +271,14 @@ func (app *BootstrapApp) Setup() error {
|
|||||||
ctrl.SetupRoutes()
|
ctrl.SetupRoutes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If analytics are not disabled, start heartbeat
|
||||||
|
if !app.config.DisableAnalytics {
|
||||||
|
log.Debug().Msg("Starting heartbeat routine")
|
||||||
|
go app.heartbeat()
|
||||||
|
}
|
||||||
|
|
||||||
// Start server
|
// Start server
|
||||||
address := fmt.Sprintf("%s:%d", app.Config.Address, app.Config.Port)
|
address := fmt.Sprintf("%s:%d", app.config.Address, app.config.Port)
|
||||||
log.Info().Msgf("Starting server on %s", address)
|
log.Info().Msgf("Starting server on %s", address)
|
||||||
if err := engine.Run(address); err != nil {
|
if err := engine.Run(address); err != nil {
|
||||||
log.Fatal().Err(err).Msg("Failed to start server")
|
log.Fatal().Err(err).Msg("Failed to start server")
|
||||||
@@ -273,3 +286,55 @@ func (app *BootstrapApp) Setup() error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (app *BootstrapApp) heartbeat() {
|
||||||
|
ticker := time.NewTicker(time.Duration(12) * time.Hour)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
type heartbeat struct {
|
||||||
|
UUID string `json:"uuid"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var body heartbeat
|
||||||
|
|
||||||
|
body.UUID = app.uuid
|
||||||
|
body.Version = config.Version
|
||||||
|
|
||||||
|
bodyJson, err := json.Marshal(body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Failed to marshal heartbeat body")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
|
||||||
|
heartbeatURL := config.ApiServer + "/v1/instances/heartbeat"
|
||||||
|
|
||||||
|
for ; true; <-ticker.C {
|
||||||
|
log.Debug().Msg("Sending heartbeat")
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodPost, heartbeatURL, bytes.NewReader(bodyJson))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Failed to create heartbeat request")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
|
||||||
|
res, err := client.Do(req)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Failed to send heartbeat")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Body.Close()
|
||||||
|
|
||||||
|
if res.StatusCode != 200 {
|
||||||
|
log.Debug().Str("status", res.Status).Msg("Heartbeat returned non-200 status")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ type Config struct {
|
|||||||
ResourcesDir string `mapstructure:"resources-dir"`
|
ResourcesDir string `mapstructure:"resources-dir"`
|
||||||
DatabasePath string `mapstructure:"database-path" validate:"required"`
|
DatabasePath string `mapstructure:"database-path" validate:"required"`
|
||||||
TrustedProxies string `mapstructure:"trusted-proxies"`
|
TrustedProxies string `mapstructure:"trusted-proxies"`
|
||||||
|
DisableAnalytics bool `mapstructure:"disable-analytics"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// OAuth/OIDC config
|
// OAuth/OIDC config
|
||||||
@@ -169,3 +170,7 @@ type AppPath struct {
|
|||||||
type Providers struct {
|
type Providers struct {
|
||||||
Providers map[string]OAuthServiceConfig
|
Providers map[string]OAuthServiceConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// API server
|
||||||
|
|
||||||
|
var ApiServer = "https://api.tinyauth.app"
|
||||||
|
|||||||
@@ -101,8 +101,7 @@ func CheckFilter(filter string, str string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateIdentifier(str string) string {
|
func GenerateUUID(str string) string {
|
||||||
uuid := uuid.NewSHA1(uuid.NameSpaceURL, []byte(str))
|
uuid := uuid.NewSHA1(uuid.NameSpaceURL, []byte(str))
|
||||||
uuidString := uuid.String()
|
return uuid.String()
|
||||||
return strings.Split(uuidString, "-")[0]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,16 +136,13 @@ func TestCheckFilter(t *testing.T) {
|
|||||||
assert.Equal(t, false, utils.CheckFilter("apple, banana, cherry", "grape"))
|
assert.Equal(t, false, utils.CheckFilter("apple, banana, cherry", "grape"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateIdentifier(t *testing.T) {
|
func TestGenerateUUID(t *testing.T) {
|
||||||
// Consistent output for same input
|
// Consistent output for same input
|
||||||
id1 := utils.GenerateIdentifier("teststring")
|
id1 := utils.GenerateUUID("teststring")
|
||||||
id2 := utils.GenerateIdentifier("teststring")
|
id2 := utils.GenerateUUID("teststring")
|
||||||
assert.Equal(t, id1, id2)
|
assert.Equal(t, id1, id2)
|
||||||
|
|
||||||
// Different output for different input
|
// Different output for different input
|
||||||
id3 := utils.GenerateIdentifier("differentstring")
|
id3 := utils.GenerateUUID("differentstring")
|
||||||
assert.Assert(t, id1 != id3)
|
assert.Assert(t, id1 != id3)
|
||||||
|
|
||||||
// Check length (should be 8 characters from first segment of UUID)
|
|
||||||
assert.Equal(t, 8, len(id1))
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user