Skip to content

Commit 5186643

Browse files
FEATURE (secutiry): Add read-only user creation before Postgresus backups
1 parent 244a56d commit 5186643

File tree

15 files changed

+1168
-11
lines changed

15 files changed

+1168
-11
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ postgresus-data/
44
pgdata/
55
docker-compose.yml
66
node_modules/
7-
.idea
7+
.idea
8+
/articles

backend/internal/features/databases/controller.go

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ func (c *DatabaseController) RegisterRoutes(router *gin.RouterGroup) {
2626
router.POST("/databases/test-connection-direct", c.TestDatabaseConnectionDirect)
2727
router.POST("/databases/:id/copy", c.CopyDatabase)
2828
router.GET("/databases/notifier/:id/is-using", c.IsNotifierUsing)
29-
29+
router.POST("/databases/is-readonly", c.IsUserReadOnly)
30+
router.POST("/databases/create-readonly-user", c.CreateReadOnlyUser)
3031
}
3132

3233
// CreateDatabase
@@ -330,3 +331,76 @@ func (c *DatabaseController) CopyDatabase(ctx *gin.Context) {
330331

331332
ctx.JSON(http.StatusCreated, copiedDatabase)
332333
}
334+
335+
// IsUserReadOnly
336+
// @Summary Check if database user is read-only
337+
// @Description Check if current database credentials have only read (SELECT) privileges
338+
// @Tags databases
339+
// @Accept json
340+
// @Produce json
341+
// @Security BearerAuth
342+
// @Param request body Database true "Database configuration to check"
343+
// @Success 200 {object} IsReadOnlyResponse
344+
// @Failure 400 {object} map[string]string
345+
// @Failure 401 {object} map[string]string
346+
// @Failure 403 {object} map[string]string
347+
// @Router /databases/is-readonly [post]
348+
func (c *DatabaseController) IsUserReadOnly(ctx *gin.Context) {
349+
user, ok := users_middleware.GetUserFromContext(ctx)
350+
if !ok {
351+
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
352+
return
353+
}
354+
355+
var request Database
356+
if err := ctx.ShouldBindJSON(&request); err != nil {
357+
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
358+
return
359+
}
360+
361+
isReadOnly, err := c.databaseService.IsUserReadOnly(user, &request)
362+
if err != nil {
363+
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
364+
return
365+
}
366+
367+
ctx.JSON(http.StatusOK, IsReadOnlyResponse{IsReadOnly: isReadOnly})
368+
}
369+
370+
// CreateReadOnlyUser
371+
// @Summary Create read-only database user
372+
// @Description Create a new PostgreSQL user with read-only privileges for backup operations
373+
// @Tags databases
374+
// @Accept json
375+
// @Produce json
376+
// @Security BearerAuth
377+
// @Param request body Database true "Database configuration to create user for"
378+
// @Success 200 {object} CreateReadOnlyUserResponse
379+
// @Failure 400 {object} map[string]string
380+
// @Failure 401 {object} map[string]string
381+
// @Failure 403 {object} map[string]string
382+
// @Router /databases/create-readonly-user [post]
383+
func (c *DatabaseController) CreateReadOnlyUser(ctx *gin.Context) {
384+
user, ok := users_middleware.GetUserFromContext(ctx)
385+
if !ok {
386+
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
387+
return
388+
}
389+
390+
var request Database
391+
if err := ctx.ShouldBindJSON(&request); err != nil {
392+
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
393+
return
394+
}
395+
396+
username, password, err := c.databaseService.CreateReadOnlyUser(user, &request)
397+
if err != nil {
398+
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
399+
return
400+
}
401+
402+
ctx.JSON(http.StatusOK, CreateReadOnlyUserResponse{
403+
Username: username,
404+
Password: password,
405+
})
406+
}

0 commit comments

Comments
 (0)