mirror of
https://github.com/veops/oneterm.git
synced 2025-11-03 01:33:30 +08:00
feat(backend): implement GetAccountCredentials2 API with ACL-based permission check
This commit is contained in:
@@ -154,28 +154,69 @@ func (c *Controller) GetAccountCredentials(ctx *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Build query with authorization check
|
||||
db, err := accountService.BuildQueryWithAuthorization(ctx)
|
||||
account, err := accountService.GetAccountCredentials(ctx, accountId)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(http.StatusInternalServerError, &myErrors.ApiError{
|
||||
Code: myErrors.ErrInternal,
|
||||
Data: map[string]any{"err": err},
|
||||
if err.Error() == "account not found" {
|
||||
ctx.AbortWithError(http.StatusNotFound, &myErrors.ApiError{
|
||||
Data: map[string]any{"err": "Account not found"},
|
||||
})
|
||||
} else if err.Error() == "permission denied" {
|
||||
ctx.AbortWithError(http.StatusForbidden, &myErrors.ApiError{
|
||||
Code: myErrors.ErrNoPerm,
|
||||
Data: map[string]any{"perm": acl.READ},
|
||||
})
|
||||
} else {
|
||||
ctx.AbortWithError(http.StatusInternalServerError, &myErrors.ApiError{
|
||||
Code: myErrors.ErrInternal,
|
||||
Data: map[string]any{"err": err.Error()},
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, HttpResponse{
|
||||
Data: account,
|
||||
})
|
||||
}
|
||||
|
||||
// GetAccountCredentials2 godoc
|
||||
//
|
||||
// @Tags account
|
||||
// @Summary Get account credentials with authorization check only
|
||||
// @Param id path int true "Account ID"
|
||||
// @Success 200 {object} HttpResponse{data=model.Account}
|
||||
// @Router /account/{id}/credentials2 [get]
|
||||
func (c *Controller) GetAccountCredentials2(ctx *gin.Context) {
|
||||
// Get account ID from path parameter
|
||||
accountId := cast.ToInt(ctx.Param("id"))
|
||||
if accountId == 0 {
|
||||
ctx.AbortWithError(http.StatusBadRequest, &myErrors.ApiError{
|
||||
Code: myErrors.ErrInvalidArgument,
|
||||
Data: map[string]any{"err": "Invalid account ID"},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Query for the specific account with all fields (including sensitive ones)
|
||||
var account model.Account
|
||||
if err := db.Where("id = ?", accountId).First(&account).Error; err != nil {
|
||||
ctx.AbortWithError(http.StatusNotFound, &myErrors.ApiError{
|
||||
Data: map[string]any{"err": "Account not found or access denied"},
|
||||
})
|
||||
account, err := accountService.GetAccountCredentials(ctx, accountId)
|
||||
if err != nil {
|
||||
if err.Error() == "account not found" {
|
||||
ctx.AbortWithError(http.StatusNotFound, &myErrors.ApiError{
|
||||
Data: map[string]any{"err": "Account not found"},
|
||||
})
|
||||
} else if err.Error() == "permission denied" {
|
||||
ctx.AbortWithError(http.StatusForbidden, &myErrors.ApiError{
|
||||
Code: myErrors.ErrNoPerm,
|
||||
Data: map[string]any{"perm": acl.READ},
|
||||
})
|
||||
} else {
|
||||
ctx.AbortWithError(http.StatusInternalServerError, &myErrors.ApiError{
|
||||
Code: myErrors.ErrInternal,
|
||||
Data: map[string]any{"err": err.Error()},
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Decrypt sensitive data before returning
|
||||
accountService.DecryptSensitiveData([]*model.Account{&account})
|
||||
|
||||
ctx.JSON(http.StatusOK, HttpResponse{
|
||||
Data: account,
|
||||
})
|
||||
|
||||
@@ -232,6 +232,43 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/account/{id}/credentials2": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"account"
|
||||
],
|
||||
"summary": "Get account credentials with authorization check only",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Account ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/controller.HttpResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/model.Account"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/asset": {
|
||||
"get": {
|
||||
"tags": [
|
||||
|
||||
@@ -221,6 +221,43 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/account/{id}/credentials2": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"account"
|
||||
],
|
||||
"summary": "Get account credentials with authorization check only",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Account ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/controller.HttpResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/model.Account"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/asset": {
|
||||
"get": {
|
||||
"tags": [
|
||||
|
||||
@@ -1245,6 +1245,27 @@ paths:
|
||||
summary: Get account credentials with MFA verification
|
||||
tags:
|
||||
- account
|
||||
/account/{id}/credentials2:
|
||||
get:
|
||||
parameters:
|
||||
- description: Account ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/controller.HttpResponse'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/model.Account'
|
||||
type: object
|
||||
summary: Get account credentials with authorization check only
|
||||
tags:
|
||||
- account
|
||||
/asset:
|
||||
get:
|
||||
parameters:
|
||||
|
||||
@@ -2,11 +2,13 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/veops/oneterm/internal/acl"
|
||||
"github.com/veops/oneterm/internal/model"
|
||||
"github.com/veops/oneterm/internal/repository"
|
||||
"github.com/veops/oneterm/pkg/config"
|
||||
"github.com/veops/oneterm/pkg/utils"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"gorm.io/gorm"
|
||||
@@ -104,3 +106,40 @@ func (s *AccountService) BuildQueryWithAuthorization(ctx *gin.Context) (*gorm.DB
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// GetAccountCredentials gets account credentials with ACL permission check
|
||||
func (s *AccountService) GetAccountCredentials(ctx *gin.Context, accountId int) (*model.Account, error) {
|
||||
// Get current user info
|
||||
currentUser, err := acl.GetSessionFromCtx(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// First get the account to check if it exists
|
||||
var account model.Account
|
||||
baseRepo := repository.NewBaseRepository()
|
||||
if err := baseRepo.GetById(ctx, accountId, &account); err != nil {
|
||||
return nil, errors.New("account not found")
|
||||
}
|
||||
|
||||
if acl.IsAdmin(currentUser) {
|
||||
// Decrypt sensitive data before returning
|
||||
s.DecryptSensitiveData([]*model.Account{&account})
|
||||
return &account, nil
|
||||
}
|
||||
|
||||
// Check if user has read permission for this account resource
|
||||
hasPermission, err := acl.HasPermission(ctx, currentUser.GetRid(), config.RESOURCE_ACCOUNT, account.ResourceId, acl.READ)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !hasPermission {
|
||||
return nil, errors.New("permission denied")
|
||||
}
|
||||
|
||||
// Decrypt sensitive data before returning
|
||||
s.DecryptSensitiveData([]*model.Account{&account})
|
||||
|
||||
return &account, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user