Files
lijiaoqiao/supply-api/internal/iam/service/iam_service.go
Your Name 89104bd0db feat(P1/P2): 完成TDD开发及P1/P2设计文档
## 设计文档
- multi_role_permission_design: 多角色权限设计 (CONDITIONAL GO)
- audit_log_enhancement_design: 审计日志增强 (CONDITIONAL GO)
- routing_strategy_template_design: 路由策略模板 (CONDITIONAL GO)
- sso_saml_technical_research: SSO/SAML调研 (CONDITIONAL GO)
- compliance_capability_package_design: 合规能力包设计 (CONDITIONAL GO)

## TDD开发成果
- IAM模块: supply-api/internal/iam/ (111个测试)
- 审计日志模块: supply-api/internal/audit/ (40+测试)
- 路由策略模块: gateway/internal/router/ (33+测试)
- 合规能力包: gateway/internal/compliance/ + scripts/ci/compliance/

## 规范文档
- parallel_agent_output_quality_standards: 并行Agent产出质量规范
- project_experience_summary: 项目经验总结 (v2)
- 2026-04-02-p1-p2-tdd-execution-plan: TDD执行计划

## 评审报告
- 5个CONDITIONAL GO设计文档评审报告
- fix_verification_report: 修复验证报告
- full_verification_report: 全面质量验证报告
- tdd_module_quality_verification: TDD模块质量验证
- tdd_execution_summary: TDD执行总结

依据: Superpowers执行框架 + TDD规范
2026-04-02 23:35:53 +08:00

292 lines
7.1 KiB
Go

package service
import (
"context"
"errors"
"time"
)
// 错误定义
var (
ErrRoleNotFound = errors.New("role not found")
ErrDuplicateRoleCode = errors.New("role code already exists")
ErrDuplicateAssignment = errors.New("user already has this role")
ErrInvalidRequest = errors.New("invalid request")
)
// Role 角色(简化的服务层模型)
type Role struct {
Code string
Name string
Type string
Level int
Description string
IsActive bool
Version int
CreatedAt time.Time
UpdatedAt time.Time
}
// UserRole 用户角色(简化的服务层模型)
type UserRole struct {
UserID int64
RoleCode string
TenantID int64
IsActive bool
ExpiresAt *time.Time
}
// CreateRoleRequest 创建角色请求
type CreateRoleRequest struct {
Code string
Name string
Type string
Level int
Description string
Scopes []string
ParentCode string
}
// UpdateRoleRequest 更新角色请求
type UpdateRoleRequest struct {
Code string
Name string
Description string
Scopes []string
IsActive *bool
}
// AssignRoleRequest 分配角色请求
type AssignRoleRequest struct {
UserID int64
RoleCode string
TenantID int64
GrantedBy int64
ExpiresAt *time.Time
}
// IAMServiceInterface IAM服务接口
type IAMServiceInterface interface {
CreateRole(ctx context.Context, req *CreateRoleRequest) (*Role, error)
GetRole(ctx context.Context, roleCode string) (*Role, error)
UpdateRole(ctx context.Context, req *UpdateRoleRequest) (*Role, error)
DeleteRole(ctx context.Context, roleCode string) error
ListRoles(ctx context.Context, roleType string) ([]*Role, error)
AssignRole(ctx context.Context, req *AssignRoleRequest) (*UserRole, error)
RevokeRole(ctx context.Context, userID int64, roleCode string, tenantID int64) error
GetUserRoles(ctx context.Context, userID int64) ([]*UserRole, error)
CheckScope(ctx context.Context, userID int64, requiredScope string) (bool, error)
GetUserScopes(ctx context.Context, userID int64) ([]string, error)
}
// DefaultIAMService 默认IAM服务实现
type DefaultIAMService struct {
// 角色存储
roleStore map[string]*Role
// 用户角色存储: userID -> []*UserRole
userRoleStore map[int64][]*UserRole
// 角色Scope存储: roleCode -> []scopeCode
roleScopeStore map[string][]string
}
// NewDefaultIAMService 创建默认IAM服务
func NewDefaultIAMService() *DefaultIAMService {
return &DefaultIAMService{
roleStore: make(map[string]*Role),
userRoleStore: make(map[int64][]*UserRole),
roleScopeStore: make(map[string][]string),
}
}
// CreateRole 创建角色
func (s *DefaultIAMService) CreateRole(ctx context.Context, req *CreateRoleRequest) (*Role, error) {
// 检查是否重复
if _, exists := s.roleStore[req.Code]; exists {
return nil, ErrDuplicateRoleCode
}
// 验证角色类型
if req.Type != "platform" && req.Type != "supply" && req.Type != "consumer" {
return nil, ErrInvalidRequest
}
now := time.Now()
role := &Role{
Code: req.Code,
Name: req.Name,
Type: req.Type,
Level: req.Level,
Description: req.Description,
IsActive: true,
Version: 1,
CreatedAt: now,
UpdatedAt: now,
}
// 存储角色
s.roleStore[req.Code] = role
// 存储角色Scope关联
if len(req.Scopes) > 0 {
s.roleScopeStore[req.Code] = req.Scopes
}
return role, nil
}
// GetRole 获取角色
func (s *DefaultIAMService) GetRole(ctx context.Context, roleCode string) (*Role, error) {
role, exists := s.roleStore[roleCode]
if !exists {
return nil, ErrRoleNotFound
}
return role, nil
}
// UpdateRole 更新角色
func (s *DefaultIAMService) UpdateRole(ctx context.Context, req *UpdateRoleRequest) (*Role, error) {
role, exists := s.roleStore[req.Code]
if !exists {
return nil, ErrRoleNotFound
}
// 更新字段
if req.Name != "" {
role.Name = req.Name
}
if req.Description != "" {
role.Description = req.Description
}
if req.Scopes != nil {
s.roleScopeStore[req.Code] = req.Scopes
}
if req.IsActive != nil {
role.IsActive = *req.IsActive
}
// 递增版本
role.Version++
role.UpdatedAt = time.Now()
return role, nil
}
// DeleteRole 删除角色(软删除)
func (s *DefaultIAMService) DeleteRole(ctx context.Context, roleCode string) error {
role, exists := s.roleStore[roleCode]
if !exists {
return ErrRoleNotFound
}
role.IsActive = false
role.UpdatedAt = time.Now()
return nil
}
// ListRoles 列出角色
func (s *DefaultIAMService) ListRoles(ctx context.Context, roleType string) ([]*Role, error) {
var roles []*Role
for _, role := range s.roleStore {
if roleType == "" || role.Type == roleType {
roles = append(roles, role)
}
}
return roles, nil
}
// AssignRole 分配角色
func (s *DefaultIAMService) AssignRole(ctx context.Context, req *AssignRoleRequest) (*UserRole, error) {
// 检查角色是否存在
if _, exists := s.roleStore[req.RoleCode]; !exists {
return nil, ErrRoleNotFound
}
// 检查是否已分配
for _, ur := range s.userRoleStore[req.UserID] {
if ur.RoleCode == req.RoleCode && ur.TenantID == req.TenantID && ur.IsActive {
return nil, ErrDuplicateAssignment
}
}
userRole := &UserRole{
UserID: req.UserID,
RoleCode: req.RoleCode,
TenantID: req.TenantID,
IsActive: true,
ExpiresAt: req.ExpiresAt,
}
// 存储映射
s.userRoleStore[req.UserID] = append(s.userRoleStore[req.UserID], userRole)
return userRole, nil
}
// RevokeRole 撤销角色
func (s *DefaultIAMService) RevokeRole(ctx context.Context, userID int64, roleCode string, tenantID int64) error {
for _, ur := range s.userRoleStore[userID] {
if ur.RoleCode == roleCode && ur.TenantID == tenantID {
ur.IsActive = false
return nil
}
}
return ErrRoleNotFound
}
// GetUserRoles 获取用户角色
func (s *DefaultIAMService) GetUserRoles(ctx context.Context, userID int64) ([]*UserRole, error) {
var userRoles []*UserRole
for _, ur := range s.userRoleStore[userID] {
if ur.IsActive {
userRoles = append(userRoles, ur)
}
}
return userRoles, nil
}
// CheckScope 检查用户是否有指定Scope
func (s *DefaultIAMService) CheckScope(ctx context.Context, userID int64, requiredScope string) (bool, error) {
scopes, err := s.GetUserScopes(ctx, userID)
if err != nil {
return false, err
}
for _, scope := range scopes {
if scope == requiredScope || scope == "*" {
return true, nil
}
}
return false, nil
}
// GetUserScopes 获取用户所有Scope
func (s *DefaultIAMService) GetUserScopes(ctx context.Context, userID int64) ([]string, error) {
var allScopes []string
seen := make(map[string]bool)
for _, ur := range s.userRoleStore[userID] {
if ur.IsActive && (ur.ExpiresAt == nil || ur.ExpiresAt.After(time.Now())) {
if scopes, exists := s.roleScopeStore[ur.RoleCode]; exists {
for _, scope := range scopes {
if !seen[scope] {
seen[scope] = true
allScopes = append(allScopes, scope)
}
}
}
}
}
return allScopes, nil
}
// IsExpired 检查用户角色是否过期
func (ur *UserRole) IsExpired() bool {
if ur.ExpiresAt == nil {
return false
}
return time.Now().After(*ur.ExpiresAt)
}