mirror of
https://github.com/EZ-Api/ez-api.git
synced 2026-01-13 17:47:51 +00:00
feat(api): add admin endpoint to issue keys for masters
Add `POST /admin/masters/{id}/keys` allowing admins to issue child keys
on behalf of a master. Introduce an `issued_by` field in the Key model
to audit whether a key was issued by the master or an admin.
Refactor master service to use typed errors for consistent HTTP status
mapping and ensure validation logic (active status, group check) is
shared.
This commit is contained in:
@@ -13,6 +13,13 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrMasterNotFound = errors.New("master not found")
|
||||
ErrMasterNotActive = errors.New("master is not active")
|
||||
ErrChildKeyLimitReached = errors.New("child key limit reached")
|
||||
ErrChildKeyGroupForbidden = errors.New("cannot issue key for a different group")
|
||||
)
|
||||
|
||||
type MasterService struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
@@ -92,15 +99,37 @@ verified:
|
||||
}
|
||||
|
||||
func (s *MasterService) IssueChildKey(masterID uint, group string, scopes string) (*model.Key, string, error) {
|
||||
return s.issueChildKey(masterID, group, scopes, "master")
|
||||
}
|
||||
|
||||
func (s *MasterService) IssueChildKeyAsAdmin(masterID uint, group string, scopes string) (*model.Key, string, error) {
|
||||
return s.issueChildKey(masterID, group, scopes, "admin")
|
||||
}
|
||||
|
||||
func (s *MasterService) issueChildKey(masterID uint, group string, scopes string, issuedBy string) (*model.Key, string, error) {
|
||||
var master model.Master
|
||||
if err := s.db.First(&master, masterID).Error; err != nil {
|
||||
return nil, "", fmt.Errorf("master not found: %w", err)
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, "", fmt.Errorf("%w: %d", ErrMasterNotFound, masterID)
|
||||
}
|
||||
return nil, "", fmt.Errorf("load master: %w", err)
|
||||
}
|
||||
if master.Status != "active" {
|
||||
return nil, "", fmt.Errorf("%w", ErrMasterNotActive)
|
||||
}
|
||||
|
||||
group = strings.TrimSpace(group)
|
||||
if group == "" {
|
||||
group = master.Group
|
||||
}
|
||||
if group != master.Group {
|
||||
return nil, "", fmt.Errorf("%w", ErrChildKeyGroupForbidden)
|
||||
}
|
||||
|
||||
var count int64
|
||||
s.db.Model(&model.Key{}).Where("master_id = ?", masterID).Count(&count)
|
||||
if count >= int64(master.MaxChildKeys) {
|
||||
return nil, "", fmt.Errorf("child key limit reached for master %d", masterID)
|
||||
return nil, "", fmt.Errorf("%w for master %d", ErrChildKeyLimitReached, masterID)
|
||||
}
|
||||
|
||||
rawChildKey, err := generateRandomKey(32)
|
||||
@@ -123,6 +152,10 @@ func (s *MasterService) IssueChildKey(masterID uint, group string, scopes string
|
||||
Scopes: scopes,
|
||||
IssuedAtEpoch: master.Epoch,
|
||||
Status: "active",
|
||||
IssuedBy: strings.TrimSpace(issuedBy),
|
||||
}
|
||||
if key.IssuedBy == "" {
|
||||
key.IssuedBy = "master"
|
||||
}
|
||||
|
||||
if err := s.db.Create(key).Error; err != nil {
|
||||
|
||||
Reference in New Issue
Block a user