feat(auth): Add option to disable registration and tenant creation

This commit is contained in:
Björn Benouarets
2026-01-27 16:34:04 +01:00
parent 9d7adb740c
commit 4e49896319
9 changed files with 126 additions and 49 deletions

View File

@@ -22,8 +22,10 @@ type Config struct {
RedisPort string
RedisPassword string
JwtSecret string
ENV string
UNPROTECTED_ENDPOINTS []string
Environment string
UnprotectedEndpoints []string
AllowRegistration bool
AllowTenantCreation bool
}
var CONFIG *Config
@@ -33,11 +35,11 @@ func generateSecret() string {
}
func NewConfig() *Config {
ENV := utils.GetEnv("ENV", "development")
UNPROTECTED_ENDPOINTS := strings.Split(utils.GetEnv("UNPROTECTED_ENDPOINTS", ""), ",")
Environment := utils.GetEnv("ENV", "development")
UnprotectedEndpoints := strings.Split(utils.GetEnv("UNPROTECTED_ENDPOINTS", ""), ",")
if ENV == "development" {
UNPROTECTED_ENDPOINTS = append(UNPROTECTED_ENDPOINTS, "/api_keys")
if Environment == "development" {
UnprotectedEndpoints = append(UnprotectedEndpoints, "/api_keys")
}
c := &Config{
@@ -56,8 +58,10 @@ func NewConfig() *Config {
RedisHost: utils.GetEnv("REDIS_HOST", "localhost"),
RedisPort: utils.GetEnv("REDIS_PORT", "6379"),
RedisPassword: utils.GetEnv("REDIS_PASSWORD", ""),
ENV: ENV,
UNPROTECTED_ENDPOINTS: UNPROTECTED_ENDPOINTS,
AllowRegistration: utils.GetEnvBool("ALLOW_REGISTRATION", false),
AllowTenantCreation: utils.GetEnvBool("ALLOW_TENANT_CREATION", false),
Environment: Environment,
UnprotectedEndpoints: UnprotectedEndpoints,
}
CONFIG = c
return c

View File

@@ -1,6 +1,7 @@
package controllers
import (
"git.secnex.io/secnex/auth-api/config"
"git.secnex.io/secnex/auth-api/services"
"git.secnex.io/secnex/auth-api/utils"
"github.com/go-playground/validator/v10"
@@ -16,6 +17,11 @@ type RegisterRequest struct {
}
func RegisterController(c *fiber.Ctx) error {
if !config.CONFIG.AllowRegistration {
return utils.NewErrorResponse(fiber.StatusForbidden, &fiber.Map{
"message": "Registration is not allowed",
}).Send(c)
}
var request RegisterRequest
if err := c.BodyParser(&request); err != nil {
return utils.NewErrorResponse(fiber.StatusBadRequest, &fiber.Map{

View File

@@ -8,6 +8,7 @@ require (
github.com/gofiber/fiber/v2 v2.52.10
github.com/golang-jwt/jwt/v5 v5.3.0
github.com/google/uuid v1.6.0
github.com/valkey-io/valkey-go v1.0.70
golang.org/x/crypto v0.46.0
gorm.io/driver/postgres v1.6.0
gorm.io/gorm v1.31.1
@@ -30,7 +31,6 @@ require (
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/valkey-io/valkey-go v1.0.70 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.51.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect

View File

@@ -19,6 +19,8 @@ github.com/gofiber/fiber/v2 v2.52.10 h1:jRHROi2BuNti6NYXmZ6gbNSfT3zj/8c0xy94GOU5
github.com/gofiber/fiber/v2 v2.52.10/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
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/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
@@ -44,6 +46,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/onsi/gomega v1.38.3 h1:eTX+W6dobAYfFeGC2PV6RwXRu/MyT+cQguijutvkpSM=
github.com/onsi/gomega v1.38.3/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
@@ -61,8 +65,12 @@ github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1S
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View File

@@ -68,7 +68,7 @@ func main() {
app.Post("/logout", controllers.LogoutController)
app.Post("/session/info", controllers.SessionInfoController)
if config.ENV == "development" {
if config.Environment == "development" {
app.Get("/api_keys", controllers.CreateApiKeyController)
}

View File

@@ -14,7 +14,7 @@ import (
func AuthMiddleware() fiber.Handler {
return func(c *fiber.Ctx) error {
if slices.Contains(config.CONFIG.UNPROTECTED_ENDPOINTS, c.Path()) {
if slices.Contains(config.CONFIG.UnprotectedEndpoints, c.Path()) {
masterlog.Debug("Unprotected endpoint", map[string]interface{}{"path": c.Path()})
return c.Next()
}

39
app/models/user.go Normal file
View File

@@ -0,0 +1,39 @@
package models
import (
"time"
"git.secnex.io/secnex/auth-api/utils"
"github.com/google/uuid"
"gorm.io/gorm"
)
type User struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
FirstName string `gorm:"not null" json:"first_name"`
LastName string `gorm:"not null" json:"last_name"`
Username string `gorm:"not null;unique" json:"username"`
Password string `gorm:"not null" json:"password"`
Email string `gorm:"not null;unique" json:"email"`
Verified bool `gorm:"not null;default:false" json:"verified"`
TenantID *uuid.UUID `gorm:"type:uuid" json:"tenant_id"`
ExternalID *uuid.UUID `gorm:"type:uuid" json:"external_id"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at"`
Tenant *Tenant `gorm:"foreignKey:TenantID" json:"-"`
}
func (User) TableName() string {
return "users"
}
func (user *User) BeforeCreate(tx *gorm.DB) (err error) {
passwordHash, err := utils.Hash(user.Password)
if err != nil {
return err
}
user.Password = passwordHash
return nil
}

View File

@@ -1,38 +0,0 @@
package models
import (
"time"
"git.secnex.io/secnex/auth-api/utils"
"github.com/google/uuid"
"gorm.io/gorm"
)
type User struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
FirstName string `gorm:"not null" json:"first_name"`
LastName string `gorm:"not null" json:"last_name"`
Username string `gorm:"not null;unique" json:"username"`
Password string `gorm:"not null" json:"password"`
Email string `gorm:"not null;unique" json:"email"`
Verified bool `gorm:"not null;default:false" json:"verified"`
TenantID *uuid.UUID `gorm:"type:uuid" json:"tenant_id"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at"`
Tenant *Tenant `gorm:"foreignKey:TenantID" json:"tenant"`
}
func (User) TableName() string {
return "users"
}
func (user *User) BeforeCreate(tx *gorm.DB) (err error) {
passwordHash, err := utils.Hash(user.Password)
if err != nil {
return err
}
user.Password = passwordHash
return nil
}

View File

@@ -0,0 +1,58 @@
networks:
secnex:
driver: bridge
services:
db:
container_name: secnex-db
image: postgres:latest
restart: unless-stopped
ports:
- 5432:5432
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=secnex
volumes:
- secnex-db-data:/var/lib/postgresql
networks:
- secnex
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
valkey:
container_name: secnex-valkey
image: valkey/valkey:latest
restart: unless-stopped
ports:
- 6379:6379
networks:
- secnex
login-api:
container_name: secnex-login-api
image: git.secnex.io/secnex.platform/login-api:latest
restart: unless-stopped
ports:
- 3001:3000
environment:
- ENV=development
- DEBUG=true
- DATABASE_HOST=db
- DATABASE_PORT=5432
- DATABASE_USER=postgres
- DATABASE_PASSWORD=postgres
- DATABASE_NAME=secnex
- REDIS_HOST=valkey
- REDIS_PORT=6379
networks:
- secnex
depends_on:
- db
- valkey
volumes:
secnex-db-data: