orchard/internal/controller/api_service_accounts.go

173 lines
5.4 KiB
Go

package controller
import (
"errors"
"net/http"
"time"
storepkg "github.com/cirruslabs/orchard/internal/controller/store"
"github.com/cirruslabs/orchard/internal/responder"
"github.com/cirruslabs/orchard/internal/simplename"
v1 "github.com/cirruslabs/orchard/pkg/resource/v1"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
func (controller *Controller) createServiceAccount(ctx *gin.Context) responder.Responder {
if responder := controller.authorize(ctx, v1.ServiceAccountRoleAdminWrite); responder != nil {
return responder
}
var serviceAccount v1.ServiceAccount
if err := ctx.ShouldBindJSON(&serviceAccount); err != nil {
return responder.JSON(http.StatusBadRequest, NewErrorResponse("invalid JSON was provided"))
}
// Validate service account name
if serviceAccount.Name == "" {
return responder.JSON(http.StatusPreconditionFailed,
NewErrorResponse("service account name is empty"))
} else if err := simplename.Validate(serviceAccount.Name); err != nil {
return responder.JSON(http.StatusPreconditionFailed,
NewErrorResponse("service account %v", err))
}
// Validate roles
for _, role := range serviceAccount.Roles {
_, err := v1.NewServiceAccountRole(string(role))
if err != nil {
return responder.JSON(http.StatusPreconditionFailed,
NewErrorResponse("unsupported role \"%s\"", role))
}
}
if serviceAccount.Token == "" {
serviceAccount.Token = uuid.New().String()
}
serviceAccount.CreatedAt = time.Now()
return controller.storeUpdate(func(txn storepkg.Transaction) responder.Responder {
// Does the Service Account resource with this name already exists?
_, err := txn.GetServiceAccount(serviceAccount.Name)
if err != nil && !errors.Is(err, storepkg.ErrNotFound) {
controller.logger.Errorf("failed to check if the service account exists in the DB: %v", err)
return responder.Code(http.StatusInternalServerError)
}
if err == nil {
return responder.JSON(http.StatusConflict,
NewErrorResponse("service account with this name already exists"))
}
if err := txn.SetServiceAccount(&serviceAccount); err != nil {
controller.logger.Errorf("failed to create the service account in the DB: %v", err)
return responder.Code(http.StatusInternalServerError)
}
return responder.JSON(http.StatusOK, &serviceAccount)
})
}
func (controller *Controller) updateServiceAccount(ctx *gin.Context) responder.Responder {
if responder := controller.authorize(ctx, v1.ServiceAccountRoleAdminWrite); responder != nil {
return responder
}
var userServiceAccount v1.ServiceAccount
if err := ctx.ShouldBindJSON(&userServiceAccount); err != nil {
return responder.JSON(http.StatusBadRequest, NewErrorResponse("invalid JSON was provided"))
}
// Validate service account name
if userServiceAccount.Name == "" {
return responder.JSON(http.StatusPreconditionFailed,
NewErrorResponse("service account name is empty"))
} else if err := simplename.Validate(userServiceAccount.Name); err != nil {
return responder.JSON(http.StatusPreconditionFailed,
NewErrorResponse("service account %v", err))
}
// Validate roles
for _, role := range userServiceAccount.Roles {
_, err := v1.NewServiceAccountRole(string(role))
if err != nil {
return responder.JSON(http.StatusPreconditionFailed,
NewErrorResponse("unsupported role \"%s\"", role))
}
}
if userServiceAccount.Token == "" {
return responder.JSON(http.StatusPreconditionFailed, NewErrorResponse("service account token is empty"))
}
return controller.storeUpdate(func(txn storepkg.Transaction) responder.Responder {
dbServiceAccount, err := txn.GetServiceAccount(userServiceAccount.Name)
if err != nil {
return responder.Error(err)
}
dbServiceAccount.Token = userServiceAccount.Token
dbServiceAccount.Roles = userServiceAccount.Roles
if err := txn.SetServiceAccount(dbServiceAccount); err != nil {
controller.logger.Errorf("failed to update service account in the DB: %v", err)
return responder.Code(http.StatusInternalServerError)
}
return responder.JSON(http.StatusOK, &dbServiceAccount)
})
}
func (controller *Controller) getServiceAccount(ctx *gin.Context) responder.Responder {
if responder := controller.authorize(ctx, v1.ServiceAccountRoleAdminRead); responder != nil {
return responder
}
name := ctx.Param("name")
return controller.storeView(func(txn storepkg.Transaction) responder.Responder {
serviceAccount, err := txn.GetServiceAccount(name)
if err != nil {
return responder.Error(err)
}
return responder.JSON(http.StatusOK, &serviceAccount)
})
}
func (controller *Controller) listServiceAccounts(ctx *gin.Context) responder.Responder {
if responder := controller.authorize(ctx, v1.ServiceAccountRoleAdminRead); responder != nil {
return responder
}
return controller.storeView(func(txn storepkg.Transaction) responder.Responder {
serviceAccounts, err := txn.ListServiceAccounts()
if err != nil {
return responder.Error(err)
}
return responder.JSON(http.StatusOK, &serviceAccounts)
})
}
func (controller *Controller) deleteServiceAccount(ctx *gin.Context) responder.Responder {
if responder := controller.authorize(ctx, v1.ServiceAccountRoleAdminWrite); responder != nil {
return responder
}
name := ctx.Param("name")
return controller.storeUpdate(func(txn storepkg.Transaction) responder.Responder {
if err := txn.DeleteServiceAccount(name); err != nil {
return responder.Error(err)
}
return responder.Code(http.StatusOK)
})
}