feat: add functionality to create and reveal secret access keys for users

Signed-off-by: Noste <83548733+Noooste@users.noreply.github.com>
This commit is contained in:
Noste
2025-12-20 12:24:53 +01:00
parent 983e6e5fa9
commit 4656d1bce9
13 changed files with 1033 additions and 138 deletions
+2 -15
View File
@@ -111,25 +111,12 @@ func (h *BucketHandler) CreateBucket(c fiber.Ctx) error {
)
}
// Check if bucket already exists
bucketInfo, err := h.adminService.GetBucketInfoByAlias(ctx, req.Name)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(
models.ErrorResponse(models.ErrCodeInternalError, "Failed to check bucket existence: "+err.Error()),
)
}
if bucketInfo != nil {
return c.Status(fiber.StatusConflict).JSON(
models.ErrorResponse(models.ErrCodeBucketExists, "Bucket already exists"),
)
}
// Create the bucket
createBucketReq := models.CreateBucketAdminRequest{
GlobalAlias: &req.Name,
}
if bucketInfo, err = h.adminService.CreateBucket(ctx, createBucketReq); err != nil {
if _, err := h.adminService.CreateBucket(ctx, createBucketReq); err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(
models.ErrorResponse(models.ErrCodeInternalError, "Failed to create bucket: "+err.Error()),
)
+35
View File
@@ -251,6 +251,41 @@ func (h *UserHandler) GetUser(c fiber.Ctx) error {
return c.JSON(models.SuccessResponse(userInfo))
}
// GetUserSecretKey retrieves the secret key for a specific user/access key
//
// @Summary Get user secret key
// @Description Retrieves the secret access key for a specific user/access key
// @Tags Users
// @Produce json
// @Param access_key path string true "Access key of the user to retrieve secret for"
// @Success 200 {object} models.APIResponse{data=map[string]string} "Secret key retrieved successfully"
// @Failure 400 {object} models.APIResponse{error=models.APIError} "Access key is required"
// @Failure 500 {object} models.APIResponse{error=models.APIError} "Failed to get secret key"
// @Router /api/v1/users/{access_key}/secret [get]
func (h *UserHandler) GetUserSecretKey(c fiber.Ctx) error {
ctx := c.Context()
accessKey := c.Params("access_key")
if accessKey == "" {
return c.Status(fiber.StatusBadRequest).JSON(
models.ErrorResponse(models.ErrCodeBadRequest, "Access key is required"),
)
}
// Get key information WITH secret key
keyInfo, err := h.adminService.GetKeyInfo(ctx, accessKey, true)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(
models.ErrorResponse(models.ErrCodeInternalError, "Failed to get secret key: "+err.Error()),
)
}
// Return only the secret key
return c.JSON(models.SuccessResponse(map[string]string{
"secretKey": *keyInfo.SecretAccessKey,
}))
}
// UpdateUserPermissions updates user permissions
//
// @Summary Update user permissions
+1 -1
View File
@@ -194,7 +194,7 @@ type ClusterHealth struct {
KnownNodes int `json:"knownNodes"`
ConnectedNodes int `json:"connectedNodes"`
StorageNodes int `json:"storageNodes"`
StorageNodesUp int `json:"storageNodesUp"`
StorageNodesUp int `json:"storageNodesOk"`
Partitions int `json:"partitions"`
PartitionsQuorum int `json:"partitionsQuorum"`
PartitionsAllOk int `json:"partitionsAllOk"`
+3 -2
View File
@@ -74,6 +74,7 @@ func SetupRoutes(
users.Get("/", userHandler.ListUsers) // List all users/keys
users.Post("/", userHandler.CreateUser) // Create new user/key
users.Get("/:access_key", userHandler.GetUser) // Get user info
users.Get("/:access_key/secret", userHandler.GetUserSecretKey) // Get user secret key
users.Delete("/:access_key", userHandler.DeleteUser) // Delete user/key
users.Patch("/:access_key", userHandler.UpdateUserPermissions) // Update user permissions
}
@@ -217,10 +218,10 @@ func SetupRoutes(
}
}
cfg.Server.FrontendPath = "./frontend/dist"
// Check if frontend path exists
if _, err := os.Stat(cfg.Server.FrontendPath); err == nil {
fmt.Println("Serving frontend from:", cfg.Server.FrontendPath)
// SPA fallback - serve index.html for all non-API routes
app.Use(func(c fiber.Ctx) error {
path := c.Path()
+1 -1
View File
@@ -210,7 +210,7 @@ func (s *GarageAdminService) GetBucketInfoByAlias(ctx context.Context, globalAli
}
var result models.GarageBucketInfo
if err := decodeResponse(resp, &result); err != nil {
if err = decodeResponse(resp, &result); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}