140 lines
4.5 KiB
Go
140 lines
4.5 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/easyai/easyai-ai-gateway/apps/api/internal/store"
|
|
)
|
|
|
|
func (s *Server) listAccessRules(w http.ResponseWriter, r *http.Request) {
|
|
items, err := s.store.ListAccessRules(r.Context())
|
|
if err != nil {
|
|
s.logger.Error("list access rules failed", "error", err)
|
|
writeError(w, http.StatusInternalServerError, "list access rules failed")
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]any{"items": items})
|
|
}
|
|
|
|
func (s *Server) createAccessRule(w http.ResponseWriter, r *http.Request) {
|
|
var input store.AccessRuleInput
|
|
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid json body")
|
|
return
|
|
}
|
|
if !validAccessRuleInput(input) {
|
|
writeError(w, http.StatusBadRequest, "subject, resource and effect are required")
|
|
return
|
|
}
|
|
item, err := s.store.CreateAccessRule(r.Context(), input)
|
|
if err != nil {
|
|
if store.IsUniqueViolation(err) {
|
|
writeError(w, http.StatusConflict, "access rule already exists")
|
|
return
|
|
}
|
|
s.logger.Error("create access rule failed", "error", err)
|
|
writeError(w, http.StatusInternalServerError, "create access rule failed")
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusCreated, item)
|
|
}
|
|
|
|
func (s *Server) batchAccessRules(w http.ResponseWriter, r *http.Request) {
|
|
var input store.AccessRuleBatchInput
|
|
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid json body")
|
|
return
|
|
}
|
|
if !validAccessRuleBatchInput(input) {
|
|
writeError(w, http.StatusBadRequest, "subject, effect and resources are required")
|
|
return
|
|
}
|
|
items, err := s.store.BatchAccessRules(r.Context(), input)
|
|
if err != nil {
|
|
s.logger.Error("batch access rules failed", "error", err)
|
|
writeError(w, http.StatusInternalServerError, "batch access rules failed")
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]any{"items": items})
|
|
}
|
|
|
|
func (s *Server) updateAccessRule(w http.ResponseWriter, r *http.Request) {
|
|
var input store.AccessRuleInput
|
|
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid json body")
|
|
return
|
|
}
|
|
if !validAccessRuleInput(input) {
|
|
writeError(w, http.StatusBadRequest, "subject, resource and effect are required")
|
|
return
|
|
}
|
|
item, err := s.store.UpdateAccessRule(r.Context(), r.PathValue("ruleID"), input)
|
|
if err != nil {
|
|
if store.IsNotFound(err) {
|
|
writeError(w, http.StatusNotFound, "access rule not found")
|
|
return
|
|
}
|
|
if store.IsUniqueViolation(err) {
|
|
writeError(w, http.StatusConflict, "access rule already exists")
|
|
return
|
|
}
|
|
s.logger.Error("update access rule failed", "error", err)
|
|
writeError(w, http.StatusInternalServerError, "update access rule failed")
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, item)
|
|
}
|
|
|
|
func (s *Server) deleteAccessRule(w http.ResponseWriter, r *http.Request) {
|
|
if err := s.store.DeleteAccessRule(r.Context(), r.PathValue("ruleID")); err != nil {
|
|
if store.IsNotFound(err) {
|
|
writeError(w, http.StatusNotFound, "access rule not found")
|
|
return
|
|
}
|
|
s.logger.Error("delete access rule failed", "error", err)
|
|
writeError(w, http.StatusInternalServerError, "delete access rule failed")
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusNoContent)
|
|
}
|
|
|
|
func validAccessRuleInput(input store.AccessRuleInput) bool {
|
|
return validOneOf(input.SubjectType, "user_group", "tenant", "user", "api_key") &&
|
|
strings.TrimSpace(input.SubjectID) != "" &&
|
|
validOneOf(input.ResourceType, "platform", "platform_model", "base_model") &&
|
|
strings.TrimSpace(input.ResourceID) != "" &&
|
|
validOneOf(input.Effect, "allow", "deny") &&
|
|
(input.Status == "" || validOneOf(input.Status, "active", "disabled"))
|
|
}
|
|
|
|
func validAccessRuleBatchInput(input store.AccessRuleBatchInput) bool {
|
|
if !validOneOf(input.SubjectType, "user_group", "tenant", "user", "api_key") ||
|
|
strings.TrimSpace(input.SubjectID) == "" ||
|
|
!validOneOf(input.Effect, "allow", "deny") {
|
|
return false
|
|
}
|
|
if len(input.UpsertResources) == 0 && len(input.DeleteResources) == 0 {
|
|
return false
|
|
}
|
|
for _, resource := range append(input.UpsertResources, input.DeleteResources...) {
|
|
if !validOneOf(resource.ResourceType, "platform", "platform_model", "base_model") ||
|
|
strings.TrimSpace(resource.ResourceID) == "" ||
|
|
(resource.Status != "" && !validOneOf(resource.Status, "active", "disabled")) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func validOneOf(value string, allowed ...string) bool {
|
|
value = strings.TrimSpace(value)
|
|
for _, item := range allowed {
|
|
if value == item {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|