diff --git a/app/config/config.go b/app/config/config.go index 40b8eae..7a15442 100644 --- a/app/config/config.go +++ b/app/config/config.go @@ -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 diff --git a/app/controllers/register.go b/app/controllers/register.go index cf85f93..d6b6135 100644 --- a/app/controllers/register.go +++ b/app/controllers/register.go @@ -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{ diff --git a/app/go.mod b/app/go.mod index 1cb9957..bde7dde 100644 --- a/app/go.mod +++ b/app/go.mod @@ -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 diff --git a/app/go.sum b/app/go.sum index 3c7eaf4..733af9b 100644 --- a/app/go.sum +++ b/app/go.sum @@ -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= diff --git a/app/main.go b/app/main.go index 5635bc5..8f0bbbb 100644 --- a/app/main.go +++ b/app/main.go @@ -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) } diff --git a/app/middlewares/auth.go b/app/middlewares/auth.go index 43f7dfe..a6df15f 100644 --- a/app/middlewares/auth.go +++ b/app/middlewares/auth.go @@ -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() } diff --git a/app/models/user.go b/app/models/user.go new file mode 100644 index 0000000..ba2f3ee --- /dev/null +++ b/app/models/user.go @@ -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 +} diff --git a/app/models/users.go b/app/models/users.go deleted file mode 100644 index 1cd05f3..0000000 --- a/app/models/users.go +++ /dev/null @@ -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 -} diff --git a/docker-compose.yml b/docker-compose.yml index e69de29..eebdb71 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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: \ No newline at end of file