mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2025-10-28 12:45:47 +00:00
Compare commits
2 Commits
57b7b66813
...
feat/multi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df10eb65ce | ||
|
|
8bf5a6067e |
17
cmd/root.go
17
cmd/root.go
@@ -105,12 +105,8 @@ var rootCmd = &cobra.Command{
|
|||||||
|
|
||||||
// Create api config
|
// Create api config
|
||||||
apiConfig := types.APIConfig{
|
apiConfig := types.APIConfig{
|
||||||
Port: config.Port,
|
Port: config.Port,
|
||||||
Address: config.Address,
|
Address: config.Address,
|
||||||
Secret: config.Secret,
|
|
||||||
CookieSecure: config.CookieSecure,
|
|
||||||
SessionExpiry: config.SessionExpiry,
|
|
||||||
Domain: domain,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create docker service
|
// Create docker service
|
||||||
@@ -121,7 +117,14 @@ var rootCmd = &cobra.Command{
|
|||||||
HandleError(err, "Failed to initialize docker")
|
HandleError(err, "Failed to initialize docker")
|
||||||
|
|
||||||
// Create auth service
|
// Create auth service
|
||||||
auth := auth.NewAuth(docker, users, oauthWhitelist, config.SessionExpiry)
|
auth := auth.NewAuth(types.AuthConfig{
|
||||||
|
Domain: domain,
|
||||||
|
Secret: config.Secret,
|
||||||
|
SessionExpiry: config.SessionExpiry,
|
||||||
|
CookieSecure: config.CookieSecure,
|
||||||
|
Users: users,
|
||||||
|
OAuthWhitelist: oauthWhitelist,
|
||||||
|
}, docker)
|
||||||
|
|
||||||
// Create OAuth providers service
|
// Create OAuth providers service
|
||||||
providers := providers.NewProviders(oauthConfig)
|
providers := providers.NewProviders(oauthConfig)
|
||||||
|
|||||||
4
go.mod
4
go.mod
@@ -3,7 +3,6 @@ module tinyauth
|
|||||||
go 1.23.2
|
go 1.23.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gin-contrib/sessions v1.0.2
|
|
||||||
github.com/gin-gonic/gin v1.10.0
|
github.com/gin-gonic/gin v1.10.0
|
||||||
github.com/go-playground/validator/v10 v10.24.0
|
github.com/go-playground/validator/v10 v10.24.0
|
||||||
github.com/google/go-querystring v1.1.0
|
github.com/google/go-querystring v1.1.0
|
||||||
@@ -58,9 +57,8 @@ require (
|
|||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/goccy/go-json v0.10.4 // indirect
|
github.com/goccy/go-json v0.10.4 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/gorilla/context v1.1.2 // indirect
|
|
||||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||||
github.com/gorilla/sessions v1.2.2 // indirect
|
github.com/gorilla/sessions v1.2.2
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -65,8 +65,6 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
|
|||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||||
github.com/gin-contrib/sessions v1.0.2 h1:UaIjUvTH1cMeOdj3in6dl+Xb6It8RiKRF9Z1anbUyCA=
|
|
||||||
github.com/gin-contrib/sessions v1.0.2/go.mod h1:KxKxWqWP5LJVDCInulOl4WbLzK2KSPlLesfZ66wRvMs=
|
|
||||||
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
|
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
|
||||||
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
|
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
|
||||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||||
@@ -99,8 +97,6 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
|||||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
|
|
||||||
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
|
|
||||||
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||||
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||||
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
|
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
|
||||||
|
|||||||
@@ -11,23 +11,21 @@ import (
|
|||||||
"tinyauth/internal/handlers"
|
"tinyauth/internal/handlers"
|
||||||
"tinyauth/internal/types"
|
"tinyauth/internal/types"
|
||||||
|
|
||||||
"github.com/gin-contrib/sessions"
|
|
||||||
"github.com/gin-contrib/sessions/cookie"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewAPI(config types.APIConfig, handlers *handlers.Handlers) *API {
|
func NewAPI(config types.APIConfig, handlers *handlers.Handlers) *API {
|
||||||
return &API{
|
return &API{
|
||||||
Config: config,
|
|
||||||
Handlers: handlers,
|
Handlers: handlers,
|
||||||
|
Config: config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type API struct {
|
type API struct {
|
||||||
Config types.APIConfig
|
|
||||||
Router *gin.Engine
|
Router *gin.Engine
|
||||||
Handlers *handlers.Handlers
|
Handlers *handlers.Handlers
|
||||||
|
Config types.APIConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) Init() {
|
func (api *API) Init() {
|
||||||
@@ -51,21 +49,6 @@ func (api *API) Init() {
|
|||||||
log.Debug().Msg("Setting up file server")
|
log.Debug().Msg("Setting up file server")
|
||||||
fileServer := http.FileServer(http.FS(dist))
|
fileServer := http.FileServer(http.FS(dist))
|
||||||
|
|
||||||
// Setup cookie store
|
|
||||||
log.Debug().Msg("Setting up cookie store")
|
|
||||||
store := cookie.NewStore([]byte(api.Config.Secret))
|
|
||||||
|
|
||||||
// Use session middleware
|
|
||||||
store.Options(sessions.Options{
|
|
||||||
Domain: api.Config.Domain,
|
|
||||||
Path: "/",
|
|
||||||
HttpOnly: true,
|
|
||||||
Secure: api.Config.CookieSecure,
|
|
||||||
MaxAge: api.Config.SessionExpiry,
|
|
||||||
})
|
|
||||||
|
|
||||||
router.Use(sessions.Sessions("tinyauth", store))
|
|
||||||
|
|
||||||
// UI middleware
|
// UI middleware
|
||||||
router.Use(func(c *gin.Context) {
|
router.Use(func(c *gin.Context) {
|
||||||
// If not an API request, serve the UI
|
// If not an API request, serve the UI
|
||||||
|
|||||||
@@ -19,13 +19,16 @@ import (
|
|||||||
"github.com/magiconair/properties/assert"
|
"github.com/magiconair/properties/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// User
|
||||||
|
var User = types.User{
|
||||||
|
Username: "user",
|
||||||
|
Password: "$2a$10$AvGHLTYv3xiRJ0xV9xs3XeVIlkGTygI9nqIamFYB5Xu.5.0UWF7B6", // pass
|
||||||
|
}
|
||||||
|
|
||||||
// Simple API config for tests
|
// Simple API config for tests
|
||||||
var apiConfig = types.APIConfig{
|
var apiConfig = types.APIConfig{
|
||||||
Port: 8080,
|
Port: 8080,
|
||||||
Address: "0.0.0.0",
|
Address: "0.0.0.0",
|
||||||
Secret: "super-secret-api-thing-for-tests", // It is 32 chars long
|
|
||||||
CookieSecure: false,
|
|
||||||
SessionExpiry: 3600,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple handlers config for tests
|
// Simple handlers config for tests
|
||||||
@@ -38,15 +41,21 @@ var handlersConfig = types.HandlersConfig{
|
|||||||
GenericName: "Generic",
|
GenericName: "Generic",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Simple auth config for tests
|
||||||
|
var authConfig = types.AuthConfig{
|
||||||
|
Domain: "localhost",
|
||||||
|
Secret: "super-secret-api-thing-for-tests", // It is 32 chars long
|
||||||
|
CookieSecure: false,
|
||||||
|
SessionExpiry: 3600,
|
||||||
|
Users: types.Users{
|
||||||
|
User,
|
||||||
|
},
|
||||||
|
OAuthWhitelist: []string{},
|
||||||
|
}
|
||||||
|
|
||||||
// Cookie
|
// Cookie
|
||||||
var cookie string
|
var cookie string
|
||||||
|
|
||||||
// User
|
|
||||||
var user = types.User{
|
|
||||||
Username: "user",
|
|
||||||
Password: "$2a$10$AvGHLTYv3xiRJ0xV9xs3XeVIlkGTygI9nqIamFYB5Xu.5.0UWF7B6", // pass
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need all this to be able to test the API
|
// We need all this to be able to test the API
|
||||||
func getAPI(t *testing.T) *api.API {
|
func getAPI(t *testing.T) *api.API {
|
||||||
// Create docker service
|
// Create docker service
|
||||||
@@ -61,12 +70,7 @@ func getAPI(t *testing.T) *api.API {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create auth service
|
// Create auth service
|
||||||
auth := auth.NewAuth(docker, types.Users{
|
auth := auth.NewAuth(authConfig, docker)
|
||||||
{
|
|
||||||
Username: user.Username,
|
|
||||||
Password: user.Password,
|
|
||||||
},
|
|
||||||
}, nil, apiConfig.SessionExpiry)
|
|
||||||
|
|
||||||
// Create providers service
|
// Create providers service
|
||||||
providers := providers.NewProviders(types.OAuthConfig{})
|
providers := providers.NewProviders(types.OAuthConfig{})
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -8,31 +9,27 @@ import (
|
|||||||
"tinyauth/internal/docker"
|
"tinyauth/internal/docker"
|
||||||
"tinyauth/internal/types"
|
"tinyauth/internal/types"
|
||||||
|
|
||||||
"github.com/gin-contrib/sessions"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/gorilla/sessions"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewAuth(docker *docker.Docker, userList types.Users, oauthWhitelist []string, sessionExpiry int) *Auth {
|
func NewAuth(config types.AuthConfig, docker *docker.Docker) *Auth {
|
||||||
return &Auth{
|
return &Auth{
|
||||||
Docker: docker,
|
Docker: docker,
|
||||||
Users: userList,
|
Config: config,
|
||||||
OAuthWhitelist: oauthWhitelist,
|
|
||||||
SessionExpiry: sessionExpiry,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Auth struct {
|
type Auth struct {
|
||||||
Users types.Users
|
Docker *docker.Docker
|
||||||
Docker *docker.Docker
|
Config types.AuthConfig
|
||||||
OAuthWhitelist []string
|
|
||||||
SessionExpiry int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *Auth) GetUser(username string) *types.User {
|
func (auth *Auth) GetUser(username string) *types.User {
|
||||||
// Loop through users and return the user if the username matches
|
// Loop through users and return the user if the username matches
|
||||||
for _, user := range auth.Users {
|
for _, user := range auth.Config.Users {
|
||||||
if user.Username == username {
|
if user.Username == username {
|
||||||
return &user
|
return &user
|
||||||
}
|
}
|
||||||
@@ -47,12 +44,12 @@ func (auth *Auth) CheckPassword(user types.User, password string) bool {
|
|||||||
|
|
||||||
func (auth *Auth) EmailWhitelisted(emailSrc string) bool {
|
func (auth *Auth) EmailWhitelisted(emailSrc string) bool {
|
||||||
// If the whitelist is empty, allow all emails
|
// If the whitelist is empty, allow all emails
|
||||||
if len(auth.OAuthWhitelist) == 0 {
|
if len(auth.Config.OAuthWhitelist) == 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop through the whitelist and return true if the email matches
|
// Loop through the whitelist and return true if the email matches
|
||||||
for _, email := range auth.OAuthWhitelist {
|
for _, email := range auth.Config.OAuthWhitelist {
|
||||||
if email == emailSrc {
|
if email == emailSrc {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -62,11 +59,35 @@ func (auth *Auth) EmailWhitelisted(emailSrc string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *Auth) CreateSessionCookie(c *gin.Context, data *types.SessionCookie) {
|
func (auth *Auth) GetCookieStore() *sessions.CookieStore {
|
||||||
|
// Create a new cookie store
|
||||||
|
store := sessions.NewCookieStore([]byte(auth.Config.Secret))
|
||||||
|
|
||||||
|
// Configure the cookie store
|
||||||
|
store.Options = &sessions.Options{
|
||||||
|
Path: "/",
|
||||||
|
Domain: fmt.Sprintf(".%s", auth.Config.Domain),
|
||||||
|
Secure: auth.Config.CookieSecure,
|
||||||
|
MaxAge: auth.Config.SessionExpiry,
|
||||||
|
HttpOnly: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the cookie store
|
||||||
|
return store
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *Auth) CreateSessionCookie(c *gin.Context, data *types.SessionCookie) error {
|
||||||
log.Debug().Msg("Creating session cookie")
|
log.Debug().Msg("Creating session cookie")
|
||||||
|
|
||||||
|
// Get cookie store
|
||||||
|
store := auth.GetCookieStore()
|
||||||
|
|
||||||
// Get session
|
// Get session
|
||||||
sessions := sessions.Default(c)
|
sessions, err := store.Get(c.Request, "tinyauth")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
log.Debug().Msg("Setting session cookie")
|
log.Debug().Msg("Setting session cookie")
|
||||||
|
|
||||||
@@ -76,43 +97,73 @@ func (auth *Auth) CreateSessionCookie(c *gin.Context, data *types.SessionCookie)
|
|||||||
if data.TotpPending {
|
if data.TotpPending {
|
||||||
sessionExpiry = 3600
|
sessionExpiry = 3600
|
||||||
} else {
|
} else {
|
||||||
sessionExpiry = auth.SessionExpiry
|
sessionExpiry = auth.Config.SessionExpiry
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set data
|
// Set data
|
||||||
sessions.Set("username", data.Username)
|
sessions.Values["username"] = data.Username
|
||||||
sessions.Set("provider", data.Provider)
|
sessions.Values["provider"] = data.Provider
|
||||||
sessions.Set("expiry", time.Now().Add(time.Duration(sessionExpiry)*time.Second).Unix())
|
sessions.Values["expiry"] = time.Now().Add(time.Duration(sessionExpiry) * time.Second).Unix()
|
||||||
sessions.Set("totpPending", data.TotpPending)
|
sessions.Values["totpPending"] = data.TotpPending
|
||||||
|
|
||||||
// Save session
|
// Save session
|
||||||
sessions.Save()
|
err = sessions.Save(c.Request, c.Writer)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return nil
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *Auth) DeleteSessionCookie(c *gin.Context) {
|
func (auth *Auth) DeleteSessionCookie(c *gin.Context) error {
|
||||||
log.Debug().Msg("Deleting session cookie")
|
log.Debug().Msg("Deleting session cookie")
|
||||||
|
|
||||||
|
// Get cookie store
|
||||||
|
store := auth.GetCookieStore()
|
||||||
|
|
||||||
// Get session
|
// Get session
|
||||||
sessions := sessions.Default(c)
|
sessions, err := store.Get(c.Request, "tinyauth")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Clear session
|
// Clear session
|
||||||
sessions.Clear()
|
for key := range sessions.Values {
|
||||||
|
delete(sessions.Values, key)
|
||||||
|
}
|
||||||
|
|
||||||
// Save session
|
// Save session
|
||||||
sessions.Save()
|
err = sessions.Save(c.Request, c.Writer)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return nil
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *Auth) GetSessionCookie(c *gin.Context) types.SessionCookie {
|
func (auth *Auth) GetSessionCookie(c *gin.Context) (types.SessionCookie, error) {
|
||||||
log.Debug().Msg("Getting session cookie")
|
log.Debug().Msg("Getting session cookie")
|
||||||
|
|
||||||
|
// Get cookie store
|
||||||
|
store := auth.GetCookieStore()
|
||||||
|
|
||||||
// Get session
|
// Get session
|
||||||
sessions := sessions.Default(c)
|
sessions, err := store.Get(c.Request, "tinyauth")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return types.SessionCookie{}, err
|
||||||
|
}
|
||||||
|
|
||||||
// Get data
|
// Get data
|
||||||
cookieUsername := sessions.Get("username")
|
cookieUsername := sessions.Values["username"]
|
||||||
cookieProvider := sessions.Get("provider")
|
cookieProvider := sessions.Values["provider"]
|
||||||
cookieExpiry := sessions.Get("expiry")
|
cookieExpiry := sessions.Values["expiry"]
|
||||||
cookieTotpPending := sessions.Get("totpPending")
|
cookieTotpPending := sessions.Values["totpPending"]
|
||||||
|
|
||||||
// Convert interfaces to correct types
|
// Convert interfaces to correct types
|
||||||
username, usernameOk := cookieUsername.(string)
|
username, usernameOk := cookieUsername.(string)
|
||||||
@@ -123,7 +174,7 @@ func (auth *Auth) GetSessionCookie(c *gin.Context) types.SessionCookie {
|
|||||||
// Check if the cookie is invalid
|
// Check if the cookie is invalid
|
||||||
if !usernameOk || !providerOk || !expiryOk || !totpPendingOk {
|
if !usernameOk || !providerOk || !expiryOk || !totpPendingOk {
|
||||||
log.Warn().Msg("Session cookie invalid")
|
log.Warn().Msg("Session cookie invalid")
|
||||||
return types.SessionCookie{}
|
return types.SessionCookie{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the cookie has expired
|
// Check if the cookie has expired
|
||||||
@@ -134,7 +185,7 @@ func (auth *Auth) GetSessionCookie(c *gin.Context) types.SessionCookie {
|
|||||||
auth.DeleteSessionCookie(c)
|
auth.DeleteSessionCookie(c)
|
||||||
|
|
||||||
// Return empty cookie
|
// Return empty cookie
|
||||||
return types.SessionCookie{}
|
return types.SessionCookie{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug().Str("username", username).Str("provider", provider).Int64("expiry", expiry).Bool("totpPending", totpPending).Msg("Parsed cookie")
|
log.Debug().Str("username", username).Str("provider", provider).Int64("expiry", expiry).Bool("totpPending", totpPending).Msg("Parsed cookie")
|
||||||
@@ -144,12 +195,12 @@ func (auth *Auth) GetSessionCookie(c *gin.Context) types.SessionCookie {
|
|||||||
Username: username,
|
Username: username,
|
||||||
Provider: provider,
|
Provider: provider,
|
||||||
TotpPending: totpPending,
|
TotpPending: totpPending,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *Auth) UserAuthConfigured() bool {
|
func (auth *Auth) UserAuthConfigured() bool {
|
||||||
// If there are users, return true
|
// If there are users, return true
|
||||||
return len(auth.Users) > 0
|
return len(auth.Config.Users) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *Auth) ResourceAllowed(c *gin.Context, context types.UserContext) (bool, error) {
|
func (auth *Auth) ResourceAllowed(c *gin.Context, context types.UserContext) (bool, error) {
|
||||||
|
|||||||
@@ -19,20 +19,20 @@ import (
|
|||||||
|
|
||||||
func NewHandlers(config types.HandlersConfig, auth *auth.Auth, hooks *hooks.Hooks, providers *providers.Providers, docker *docker.Docker) *Handlers {
|
func NewHandlers(config types.HandlersConfig, auth *auth.Auth, hooks *hooks.Hooks, providers *providers.Providers, docker *docker.Docker) *Handlers {
|
||||||
return &Handlers{
|
return &Handlers{
|
||||||
Config: config,
|
|
||||||
Auth: auth,
|
Auth: auth,
|
||||||
Hooks: hooks,
|
Hooks: hooks,
|
||||||
Providers: providers,
|
Providers: providers,
|
||||||
Docker: docker,
|
Docker: docker,
|
||||||
|
Config: config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Handlers struct {
|
type Handlers struct {
|
||||||
Config types.HandlersConfig
|
|
||||||
Auth *auth.Auth
|
Auth *auth.Auth
|
||||||
Hooks *hooks.Hooks
|
Hooks *hooks.Hooks
|
||||||
Providers *providers.Providers
|
Providers *providers.Providers
|
||||||
Docker *docker.Docker
|
Docker *docker.Docker
|
||||||
|
Config types.HandlersConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handlers) AuthHandler(c *gin.Context) {
|
func (h *Handlers) AuthHandler(c *gin.Context) {
|
||||||
|
|||||||
@@ -23,7 +23,13 @@ type Hooks struct {
|
|||||||
|
|
||||||
func (hooks *Hooks) UseUserContext(c *gin.Context) types.UserContext {
|
func (hooks *Hooks) UseUserContext(c *gin.Context) types.UserContext {
|
||||||
// Get session cookie and basic auth
|
// Get session cookie and basic auth
|
||||||
cookie := hooks.Auth.GetSessionCookie(c)
|
cookie, err := hooks.Auth.GetSessionCookie(c)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Failed to get session cookie")
|
||||||
|
return types.UserContext{}
|
||||||
|
}
|
||||||
|
|
||||||
basic := hooks.Auth.GetBasicAuth(c)
|
basic := hooks.Auth.GetBasicAuth(c)
|
||||||
|
|
||||||
// Check if basic auth is set
|
// Check if basic auth is set
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ func NewOAuth(config oauth2.Config) *OAuth {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type OAuth struct {
|
type OAuth struct {
|
||||||
Config oauth2.Config
|
Verifier string
|
||||||
Context context.Context
|
Context context.Context
|
||||||
Token *oauth2.Token
|
Token *oauth2.Token
|
||||||
Verifier string
|
Config oauth2.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func (oauth *OAuth) Init() {
|
func (oauth *OAuth) Init() {
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ func NewProviders(config types.OAuthConfig) *Providers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Providers struct {
|
type Providers struct {
|
||||||
Config types.OAuthConfig
|
|
||||||
Github *oauth.OAuth
|
Github *oauth.OAuth
|
||||||
Google *oauth.OAuth
|
Google *oauth.OAuth
|
||||||
Tailscale *oauth.OAuth
|
Tailscale *oauth.OAuth
|
||||||
Generic *oauth.OAuth
|
Generic *oauth.OAuth
|
||||||
|
Config types.OAuthConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func (providers *Providers) Init() {
|
func (providers *Providers) Init() {
|
||||||
|
|||||||
49
internal/types/api.go
Normal file
49
internal/types/api.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
// LoginQuery is the query parameters for the login endpoint
|
||||||
|
type LoginQuery struct {
|
||||||
|
RedirectURI string `url:"redirect_uri"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginRequest is the request body for the login endpoint
|
||||||
|
type LoginRequest struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OAuthRequest is the request for the OAuth endpoint
|
||||||
|
type OAuthRequest struct {
|
||||||
|
Provider string `uri:"provider" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnauthorizedQuery is the query parameters for the unauthorized endpoint
|
||||||
|
type UnauthorizedQuery struct {
|
||||||
|
Username string `url:"username"`
|
||||||
|
Resource string `url:"resource"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TailscaleQuery is the query parameters for the tailscale endpoint
|
||||||
|
type TailscaleQuery struct {
|
||||||
|
Code int `url:"code"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proxy is the uri parameters for the proxy endpoint
|
||||||
|
type Proxy struct {
|
||||||
|
Proxy string `uri:"proxy" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// User Context response is the response for the user context endpoint
|
||||||
|
type UserContextResponse struct {
|
||||||
|
Status int `json:"status"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
IsLoggedIn bool `json:"isLoggedIn"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Provider string `json:"provider"`
|
||||||
|
Oauth bool `json:"oauth"`
|
||||||
|
TotpPending bool `json:"totpPending"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Totp request is the request for the totp endpoint
|
||||||
|
type TotpRequest struct {
|
||||||
|
Code string `json:"code"`
|
||||||
|
}
|
||||||
79
internal/types/config.go
Normal file
79
internal/types/config.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
// Config is the configuration for the tinyauth server
|
||||||
|
type Config struct {
|
||||||
|
Port int `mapstructure:"port" validate:"required"`
|
||||||
|
Address string `validate:"required,ip4_addr" mapstructure:"address"`
|
||||||
|
Secret string `validate:"required,len=32" mapstructure:"secret"`
|
||||||
|
SecretFile string `mapstructure:"secret-file"`
|
||||||
|
AppURL string `validate:"required,url" mapstructure:"app-url"`
|
||||||
|
Users string `mapstructure:"users"`
|
||||||
|
UsersFile string `mapstructure:"users-file"`
|
||||||
|
CookieSecure bool `mapstructure:"cookie-secure"`
|
||||||
|
GithubClientId string `mapstructure:"github-client-id"`
|
||||||
|
GithubClientSecret string `mapstructure:"github-client-secret"`
|
||||||
|
GithubClientSecretFile string `mapstructure:"github-client-secret-file"`
|
||||||
|
GoogleClientId string `mapstructure:"google-client-id"`
|
||||||
|
GoogleClientSecret string `mapstructure:"google-client-secret"`
|
||||||
|
GoogleClientSecretFile string `mapstructure:"google-client-secret-file"`
|
||||||
|
TailscaleClientId string `mapstructure:"tailscale-client-id"`
|
||||||
|
TailscaleClientSecret string `mapstructure:"tailscale-client-secret"`
|
||||||
|
TailscaleClientSecretFile string `mapstructure:"tailscale-client-secret-file"`
|
||||||
|
GenericClientId string `mapstructure:"generic-client-id"`
|
||||||
|
GenericClientSecret string `mapstructure:"generic-client-secret"`
|
||||||
|
GenericClientSecretFile string `mapstructure:"generic-client-secret-file"`
|
||||||
|
GenericScopes string `mapstructure:"generic-scopes"`
|
||||||
|
GenericAuthURL string `mapstructure:"generic-auth-url"`
|
||||||
|
GenericTokenURL string `mapstructure:"generic-token-url"`
|
||||||
|
GenericUserURL string `mapstructure:"generic-user-url"`
|
||||||
|
GenericName string `mapstructure:"generic-name"`
|
||||||
|
DisableContinue bool `mapstructure:"disable-continue"`
|
||||||
|
OAuthWhitelist string `mapstructure:"oauth-whitelist"`
|
||||||
|
SessionExpiry int `mapstructure:"session-expiry"`
|
||||||
|
LogLevel int8 `mapstructure:"log-level" validate:"min=-1,max=5"`
|
||||||
|
Title string `mapstructure:"app-title"`
|
||||||
|
EnvFile string `mapstructure:"env-file"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// APIConfig is the configuration for the API
|
||||||
|
type APIConfig struct {
|
||||||
|
Port int
|
||||||
|
Address string
|
||||||
|
}
|
||||||
|
|
||||||
|
// OAuthConfig is the configuration for the providers
|
||||||
|
type OAuthConfig struct {
|
||||||
|
GithubClientId string
|
||||||
|
GithubClientSecret string
|
||||||
|
GoogleClientId string
|
||||||
|
GoogleClientSecret string
|
||||||
|
TailscaleClientId string
|
||||||
|
TailscaleClientSecret string
|
||||||
|
GenericClientId string
|
||||||
|
GenericClientSecret string
|
||||||
|
GenericScopes []string
|
||||||
|
GenericAuthURL string
|
||||||
|
GenericTokenURL string
|
||||||
|
GenericUserURL string
|
||||||
|
AppURL string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server configuration
|
||||||
|
type HandlersConfig struct {
|
||||||
|
AppURL string
|
||||||
|
Domain string
|
||||||
|
CookieSecure bool
|
||||||
|
DisableContinue bool
|
||||||
|
GenericName string
|
||||||
|
Title string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auth configuration
|
||||||
|
type AuthConfig struct {
|
||||||
|
Domain string
|
||||||
|
Secret string
|
||||||
|
CookieSecure bool
|
||||||
|
SessionExpiry int
|
||||||
|
Users Users
|
||||||
|
OAuthWhitelist []string
|
||||||
|
}
|
||||||
@@ -2,17 +2,6 @@ package types
|
|||||||
|
|
||||||
import "tinyauth/internal/oauth"
|
import "tinyauth/internal/oauth"
|
||||||
|
|
||||||
// LoginQuery is the query parameters for the login endpoint
|
|
||||||
type LoginQuery struct {
|
|
||||||
RedirectURI string `url:"redirect_uri"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoginRequest is the request body for the login endpoint
|
|
||||||
type LoginRequest struct {
|
|
||||||
Username string `json:"username"`
|
|
||||||
Password string `json:"password"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// User is the struct for a user
|
// User is the struct for a user
|
||||||
type User struct {
|
type User struct {
|
||||||
Username string
|
Username string
|
||||||
@@ -23,82 +12,6 @@ type User struct {
|
|||||||
// Users is a list of users
|
// Users is a list of users
|
||||||
type Users []User
|
type Users []User
|
||||||
|
|
||||||
// Config is the configuration for the tinyauth server
|
|
||||||
type Config struct {
|
|
||||||
Port int `mapstructure:"port" validate:"required"`
|
|
||||||
Address string `validate:"required,ip4_addr" mapstructure:"address"`
|
|
||||||
Secret string `validate:"required,len=32" mapstructure:"secret"`
|
|
||||||
SecretFile string `mapstructure:"secret-file"`
|
|
||||||
AppURL string `validate:"required,url" mapstructure:"app-url"`
|
|
||||||
Users string `mapstructure:"users"`
|
|
||||||
UsersFile string `mapstructure:"users-file"`
|
|
||||||
CookieSecure bool `mapstructure:"cookie-secure"`
|
|
||||||
GithubClientId string `mapstructure:"github-client-id"`
|
|
||||||
GithubClientSecret string `mapstructure:"github-client-secret"`
|
|
||||||
GithubClientSecretFile string `mapstructure:"github-client-secret-file"`
|
|
||||||
GoogleClientId string `mapstructure:"google-client-id"`
|
|
||||||
GoogleClientSecret string `mapstructure:"google-client-secret"`
|
|
||||||
GoogleClientSecretFile string `mapstructure:"google-client-secret-file"`
|
|
||||||
TailscaleClientId string `mapstructure:"tailscale-client-id"`
|
|
||||||
TailscaleClientSecret string `mapstructure:"tailscale-client-secret"`
|
|
||||||
TailscaleClientSecretFile string `mapstructure:"tailscale-client-secret-file"`
|
|
||||||
GenericClientId string `mapstructure:"generic-client-id"`
|
|
||||||
GenericClientSecret string `mapstructure:"generic-client-secret"`
|
|
||||||
GenericClientSecretFile string `mapstructure:"generic-client-secret-file"`
|
|
||||||
GenericScopes string `mapstructure:"generic-scopes"`
|
|
||||||
GenericAuthURL string `mapstructure:"generic-auth-url"`
|
|
||||||
GenericTokenURL string `mapstructure:"generic-token-url"`
|
|
||||||
GenericUserURL string `mapstructure:"generic-user-url"`
|
|
||||||
GenericName string `mapstructure:"generic-name"`
|
|
||||||
DisableContinue bool `mapstructure:"disable-continue"`
|
|
||||||
OAuthWhitelist string `mapstructure:"oauth-whitelist"`
|
|
||||||
SessionExpiry int `mapstructure:"session-expiry"`
|
|
||||||
LogLevel int8 `mapstructure:"log-level" validate:"min=-1,max=5"`
|
|
||||||
Title string `mapstructure:"app-title"`
|
|
||||||
EnvFile string `mapstructure:"env-file"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// UserContext is the context for the user
|
|
||||||
type UserContext struct {
|
|
||||||
Username string
|
|
||||||
IsLoggedIn bool
|
|
||||||
OAuth bool
|
|
||||||
Provider string
|
|
||||||
TotpPending bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// APIConfig is the configuration for the API
|
|
||||||
type APIConfig struct {
|
|
||||||
Port int
|
|
||||||
Address string
|
|
||||||
Secret string
|
|
||||||
CookieSecure bool
|
|
||||||
SessionExpiry int
|
|
||||||
Domain string
|
|
||||||
}
|
|
||||||
|
|
||||||
// OAuthConfig is the configuration for the providers
|
|
||||||
type OAuthConfig struct {
|
|
||||||
GithubClientId string
|
|
||||||
GithubClientSecret string
|
|
||||||
GoogleClientId string
|
|
||||||
GoogleClientSecret string
|
|
||||||
TailscaleClientId string
|
|
||||||
TailscaleClientSecret string
|
|
||||||
GenericClientId string
|
|
||||||
GenericClientSecret string
|
|
||||||
GenericScopes []string
|
|
||||||
GenericAuthURL string
|
|
||||||
GenericTokenURL string
|
|
||||||
GenericUserURL string
|
|
||||||
AppURL string
|
|
||||||
}
|
|
||||||
|
|
||||||
// OAuthRequest is the request for the OAuth endpoint
|
|
||||||
type OAuthRequest struct {
|
|
||||||
Provider string `uri:"provider" binding:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// OAuthProviders is the struct for the OAuth providers
|
// OAuthProviders is the struct for the OAuth providers
|
||||||
type OAuthProviders struct {
|
type OAuthProviders struct {
|
||||||
Github *oauth.OAuth
|
Github *oauth.OAuth
|
||||||
@@ -106,12 +19,6 @@ type OAuthProviders struct {
|
|||||||
Microsoft *oauth.OAuth
|
Microsoft *oauth.OAuth
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnauthorizedQuery is the query parameters for the unauthorized endpoint
|
|
||||||
type UnauthorizedQuery struct {
|
|
||||||
Username string `url:"username"`
|
|
||||||
Resource string `url:"resource"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// SessionCookie is the cookie for the session (exculding the expiry)
|
// SessionCookie is the cookie for the session (exculding the expiry)
|
||||||
type SessionCookie struct {
|
type SessionCookie struct {
|
||||||
Username string
|
Username string
|
||||||
@@ -127,25 +34,13 @@ type TinyauthLabels struct {
|
|||||||
Headers map[string]string
|
Headers map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// TailscaleQuery is the query parameters for the tailscale endpoint
|
// UserContext is the context for the user
|
||||||
type TailscaleQuery struct {
|
type UserContext struct {
|
||||||
Code int `url:"code"`
|
Username string
|
||||||
}
|
IsLoggedIn bool
|
||||||
|
OAuth bool
|
||||||
// Proxy is the uri parameters for the proxy endpoint
|
Provider string
|
||||||
type Proxy struct {
|
TotpPending bool
|
||||||
Proxy string `uri:"proxy" binding:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// User Context response is the response for the user context endpoint
|
|
||||||
type UserContextResponse struct {
|
|
||||||
Status int `json:"status"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
IsLoggedIn bool `json:"isLoggedIn"`
|
|
||||||
Username string `json:"username"`
|
|
||||||
Provider string `json:"provider"`
|
|
||||||
Oauth bool `json:"oauth"`
|
|
||||||
TotpPending bool `json:"totpPending"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// App Context is the response for the app context endpoint
|
// App Context is the response for the app context endpoint
|
||||||
@@ -157,18 +52,3 @@ type AppContext struct {
|
|||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
GenericName string `json:"genericName"`
|
GenericName string `json:"genericName"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Totp request is the request for the totp endpoint
|
|
||||||
type TotpRequest struct {
|
|
||||||
Code string `json:"code"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Server configuration
|
|
||||||
type HandlersConfig struct {
|
|
||||||
AppURL string
|
|
||||||
Domain string
|
|
||||||
CookieSecure bool
|
|
||||||
DisableContinue bool
|
|
||||||
GenericName string
|
|
||||||
Title string
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user