go-duck-cli 1.1.42 → 1.1.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -273,6 +273,8 @@ import (
273
273
  my_middleware "{{projectName}}/middleware"
274
274
  "gorm.io/gorm"
275
275
  "go.mongodb.org/mongo-driver/mongo"
276
+ "google.golang.org/grpc/codes"
277
+ "google.golang.org/grpc/status"
276
278
  "strings"
277
279
  "fmt"
278
280
  "sort"
@@ -297,13 +299,14 @@ func TenantServerInterceptor(conf *config.Config, db *gorm.DB) middleware.Middle
297
299
  rolesInterface, ok := ra["roles"].([]interface{})
298
300
  if !ok { return handler(ctx, req) }
299
301
 
302
+ superAdminRole := strings.ToLower(conf.GoDuck.Security.SuperAdminRole)
300
303
  // Normalize roles (lowercase)
301
304
  var lowerRoles []string
302
305
  isAdmin := false
303
306
  for _, r := range rolesInterface {
304
307
  roleStr := strings.ToLower(fmt.Sprintf("%v", r))
305
308
  lowerRoles = append(lowerRoles, roleStr)
306
- if roleStr == "admin" || roleStr == "role_admin" { isAdmin = true }
309
+ if roleStr == "admin" || roleStr == "role_admin" || (superAdminRole != "" && roleStr == superAdminRole) { isAdmin = true }
307
310
  }
308
311
 
309
312
  var requestedTenant string
@@ -312,18 +315,12 @@ func TenantServerInterceptor(conf *config.Config, db *gorm.DB) middleware.Middle
312
315
  }
313
316
 
314
317
  var mappings []models.TenantRole
315
- if requestedTenant != "" {
316
- if isAdmin {
317
- db.Raw("SELECT role_name, db_name, is_primary FROM tenant_roles WHERE tenant_id = ?", requestedTenant).Scan(&mappings)
318
- } else {
319
- db.Raw("SELECT role_name, db_name, is_primary FROM tenant_roles WHERE LOWER(role_name) IN ? AND tenant_id = ?", lowerRoles, requestedTenant).Scan(&mappings)
320
- }
318
+ if isAdmin {
319
+ mappings = []models.TenantRole{}
320
+ } else if requestedTenant != "" {
321
+ db.Raw("SELECT role_name, db_name, is_primary FROM tenant_roles WHERE LOWER(role_name) IN ? AND tenant_id = ?", lowerRoles, requestedTenant).Scan(&mappings)
321
322
  } else {
322
- if isAdmin {
323
- db.Raw("SELECT role_name, db_name, is_primary FROM tenant_roles").Scan(&mappings)
324
- } else {
325
- db.Raw("SELECT role_name, db_name, is_primary FROM tenant_roles WHERE LOWER(role_name) IN ?", lowerRoles).Scan(&mappings)
326
- }
323
+ db.Raw("SELECT role_name, db_name, is_primary FROM tenant_roles WHERE LOWER(role_name) IN ?", lowerRoles).Scan(&mappings)
327
324
  }
328
325
 
329
326
  siloConnections := make(map[string]*gorm.DB)
@@ -340,6 +337,9 @@ func TenantServerInterceptor(conf *config.Config, db *gorm.DB) middleware.Middle
340
337
  mappings = authorizedMappings
341
338
 
342
339
  if len(mappings) == 0 {
340
+ if !isAdmin {
341
+ return nil, status.Error(codes.PermissionDenied, "Access denied. No tenant database provisioned for your roles.")
342
+ }
343
343
  conn, _ := mgr.GetDB(fallbackDB)
344
344
  siloConnections["fallback"] = conn
345
345
  ctx = context.WithValue(ctx, "tenantDBConn", conn)
@@ -147,12 +147,13 @@ func TenantMiddleware(db *gorm.DB, cfg *config.Config) gin.HandlerFunc {
147
147
  return
148
148
  }
149
149
 
150
+ superAdminRole := strings.ToLower(cfg.GoDuck.Security.SuperAdminRole)
150
151
  var lowerRoles []string
151
152
  isAdmin := false
152
153
  for _, r := range roles {
153
154
  roleStr := strings.ToLower(fmt.Sprintf("%v", r))
154
155
  lowerRoles = append(lowerRoles, roleStr)
155
- if roleStr == "admin" || roleStr == "role_admin" {
156
+ if roleStr == "admin" || roleStr == "role_admin" || (superAdminRole != "" && roleStr == superAdminRole) {
156
157
  isAdmin = true
157
158
  }
158
159
  }
@@ -173,18 +174,12 @@ func TenantMiddleware(db *gorm.DB, cfg *config.Config) gin.HandlerFunc {
173
174
  }
174
175
  }
175
176
 
176
- if len(requestedTenants) > 0 {
177
- if isAdmin {
178
- db.Raw("SELECT role_name, db_name, is_primary FROM tenant_roles WHERE tenant_id IN ?", requestedTenants).Scan(&mappings)
179
- } else {
180
- db.Raw("SELECT role_name, db_name, is_primary FROM tenant_roles WHERE LOWER(role_name) IN ? AND tenant_id IN ?", lowerRoles, requestedTenants).Scan(&mappings)
181
- }
177
+ if isAdmin {
178
+ mappings = []models.TenantRole{}
179
+ } else if len(requestedTenants) > 0 {
180
+ db.Raw("SELECT role_name, db_name, is_primary FROM tenant_roles WHERE LOWER(role_name) IN ? AND tenant_id IN ?", lowerRoles, requestedTenants).Scan(&mappings)
182
181
  } else {
183
- if isAdmin {
184
- db.Raw("SELECT role_name, db_name, is_primary FROM tenant_roles").Scan(&mappings)
185
- } else {
186
- db.Raw("SELECT role_name, db_name, is_primary FROM tenant_roles WHERE LOWER(role_name) IN ?", lowerRoles).Scan(&mappings)
187
- }
182
+ db.Raw("SELECT role_name, db_name, is_primary FROM tenant_roles WHERE LOWER(role_name) IN ?", lowerRoles).Scan(&mappings)
188
183
  }
189
184
 
190
185
  // Filter out unauthorized mappings (e.g. admin_db for non-admins)
@@ -198,11 +193,16 @@ func TenantMiddleware(db *gorm.DB, cfg *config.Config) gin.HandlerFunc {
198
193
  mappings = authorizedMappings
199
194
 
200
195
  if len(mappings) == 0 {
196
+ if !isAdmin {
197
+ c.JSON(http.StatusForbidden, gin.H{"error": "Access denied. No tenant database provisioned for your roles."})
198
+ c.Abort()
199
+ return
200
+ }
201
201
  conn, err := mgr.GetDB(fallbackDB)
202
202
  if err != nil || conn == nil {
203
- c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to resolve tenant fallback database connection"})
204
- c.Abort()
205
- return
203
+ c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to resolve tenant fallback database connection"})
204
+ c.Abort()
205
+ return
206
206
  }
207
207
  siloConnections["fallback"] = conn
208
208
  c.Set("tenantDBConn", conn)
@@ -355,6 +355,7 @@ import (
355
355
  )
356
356
 
357
357
  type DatabaseRequest struct {
358
+ TenantID string \`json:"tenantId" form:"tenantId"\`
358
359
  RoleName string \`json:"roleName" form:"roleName" binding:"required"\`
359
360
  DBName string \`json:"dbName" form:"dbName" binding:"required"\`
360
361
  IsPrimary bool \`json:"isPrimary" form:"isPrimary"\`
@@ -390,15 +391,25 @@ func CreateDatabaseAndMigrate(masterDB *gorm.DB) gin.HandlerFunc {
390
391
  if count == 0 {
391
392
  isPrimary = true
392
393
  }
394
+
395
+ tenantID := req.TenantID
396
+ if tenantID == "" {
397
+ tenantID = uuid.New().String()
398
+ }
399
+
393
400
  if err := masterDB.Where("LOWER(role_name) = LOWER(?) AND db_name = ?", req.RoleName, req.DBName).First(&existing).Error; err == nil {
394
401
  if count <= 1 {
395
402
  isPrimary = true
396
403
  }
397
404
  existing.IsPrimary = isPrimary
405
+ if req.TenantID != "" {
406
+ existing.TenantID = req.TenantID
407
+ }
398
408
  masterDB.Save(&existing)
409
+ tenantID = existing.TenantID
399
410
  } else {
400
411
  masterDB.Create(&models.TenantRole{
401
- TenantID: uuid.New().String(),
412
+ TenantID: tenantID,
402
413
  RoleName: req.RoleName,
403
414
  DBName: req.DBName,
404
415
  IsPrimary: isPrimary,
@@ -414,7 +425,7 @@ func CreateDatabaseAndMigrate(masterDB *gorm.DB) gin.HandlerFunc {
414
425
  migrations.RunGoNativeMigrationsForTenant(tenantDB)
415
426
  }
416
427
 
417
- c.JSON(http.StatusOK, gin.H{"message": "Role silo assigned successfully", "role": req.RoleName, "primary": isPrimary})
428
+ c.JSON(http.StatusOK, gin.H{"message": "Role silo assigned successfully", "role": req.RoleName, "tenantId": tenantID, "primary": isPrimary})
418
429
  }
419
430
  }
420
431
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "go-duck-cli",
3
- "version": "1.1.42",
3
+ "version": "1.1.44",
4
4
  "description": "The Ultimate Evolutionary Go Microservice Scaffolder.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -230,6 +230,13 @@ func SetupRouter(appConfig *config.Config) *gin.Engine {
230
230
  api := r.Group("/api")
231
231
  api.Use(middleware.JWTMiddleware())
232
232
  api.Use(middleware.TenantMiddleware(masterDB, appConfig))
233
+ // Log request DB resolution for debugging
234
+ api.Use(func(c *gin.Context) {
235
+ if dbName, ok := c.Get("resolvedDB"); ok {
236
+ fmt.Printf("[LOG] %s %s using DB: %s\n", c.Request.Method, c.Request.URL.Path, dbName.(string))
237
+ }
238
+ c.Next()
239
+ })
233
240
  api.Use(middleware.AuditMiddleware(masterDB))
234
241
  api.Use(middleware.MeteringMiddleware(masterDB))
235
242
  {