1
0
forked from baron/baron-sso

feat(org): enhance bulk import to support multi-level hierarchy, auto-provision users, and map matrix organizations (#500)

This commit is contained in:
2026-04-07 15:34:25 +09:00
parent b3a7f47cf7
commit 02255116f4
3 changed files with 260 additions and 126 deletions

View File

@@ -1,6 +1,7 @@
package service
import (
"baron-sso-backend/internal/domain"
"bytes"
"context"
"encoding/json"
@@ -34,6 +35,7 @@ type KratosAdminService interface {
UpdateIdentity(ctx context.Context, identityID string, traits map[string]interface{}, state string) (*KratosIdentity, error)
UpdateIdentityPassword(ctx context.Context, identityID, newPassword string) error
DeleteIdentity(ctx context.Context, identityID string) error
CreateUser(ctx context.Context, user *domain.BrokerUser, password string) (string, error)
}
type kratosAdminService struct {
@@ -239,6 +241,67 @@ func (s *kratosAdminService) UpdateIdentityPassword(ctx context.Context, identit
return nil
}
func (s *kratosAdminService) CreateUser(ctx context.Context, user *domain.BrokerUser, password string) (string, error) {
if user == nil {
return "", fmt.Errorf("kratos admin: user payload is nil")
}
traits := map[string]interface{}{
"email": user.Email,
"name": user.Name,
}
if user.PhoneNumber != "" {
traits["phone_number"] = user.PhoneNumber
}
for k, v := range user.Attributes {
if k == "id" || k == "email" {
continue
}
traits[k] = v
}
payload := map[string]interface{}{
"schema_id": "default",
"traits": traits,
"credentials": map[string]interface{}{
"password": map[string]interface{}{
"config": map[string]string{
"password": password,
},
},
},
"state": "active",
}
body, _ := json.Marshal(payload)
endpoint := strings.TrimRight(s.AdminURL, "/") + "/admin/identities"
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(body))
if err != nil {
return "", err
}
req.Header.Set("Content-Type", "application/json")
resp, err := s.httpClient().Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode >= 300 {
respBody, _ := io.ReadAll(io.LimitReader(resp.Body, 2048))
return "", fmt.Errorf("kratos admin create identity failed status=%d body=%s", resp.StatusCode, string(respBody))
}
var created struct {
ID string `json:"id"`
}
if err := json.NewDecoder(resp.Body).Decode(&created); err != nil {
return "", err
}
return created.ID, nil
}
func hashPasswordForKratosAdmin(password string) (string, error) {
hashed, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {