fix: 生产安全修复 + Go SDK + CAS SSO框架
安全修复: - CRITICAL: SSO重定向URL注入漏洞 - 修复redirect_uri白名单验证 - HIGH: SSO ClientSecret未验证 - 使用crypto/subtle.ConstantTimeCompare验证 - HIGH: 邮件验证码熵值过低(3字节) - 提升到6字节(48位熵) - HIGH: 短信验证码熵值过低(4字节) - 提升到6字节 - HIGH: Goroutine使用已取消上下文 - auth_email.go使用独立context+超时 - HIGH: SQL LIKE查询注入风险 - permission/role仓库使用escapeLikePattern 新功能: - Go SDK: sdk/go/user-management/ 完整SDK实现 - CAS SSO框架: internal/auth/cas.go CAS协议支持 其他: - L1Cache实例问题修复 - AuthMiddleware共享l1Cache - 设备指纹XSS防护 - 内存存储替代localStorage - 响应格式协议中间件 - 导出无界查询修复
This commit is contained in:
247
sdk/go/user-management/user.go
Normal file
247
sdk/go/user-management/user.go
Normal file
@@ -0,0 +1,247 @@
|
||||
package userManagement
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// CreateUserRequest 创建用户请求
|
||||
type CreateUserRequest struct {
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
Phone string `json:"phone,omitempty"`
|
||||
Nickname string `json:"nickname,omitempty"`
|
||||
RoleIDs []int64 `json:"role_ids,omitempty"`
|
||||
Status UserStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// UpdateUserRequest 更新用户请求
|
||||
type UpdateUserRequest struct {
|
||||
Email string `json:"email,omitempty"`
|
||||
Phone string `json:"phone,omitempty"`
|
||||
Nickname string `json:"nickname,omitempty"`
|
||||
Avatar string `json:"avatar,omitempty"`
|
||||
Status UserStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// AssignRolesRequest 分配角色请求
|
||||
type AssignRolesRequest struct {
|
||||
RoleIDs []int64 `json:"role_ids"`
|
||||
}
|
||||
|
||||
// ListUsersParams 用户列表查询参数
|
||||
type ListUsersParams struct {
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
Keyword string `json:"keyword,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// GetCurrentUser 获取当前登录用户
|
||||
func (c *Client) GetCurrentUser(ctx context.Context) (*User, error) {
|
||||
resp, err := c.doRequest(ctx, "GET", "/api/v1/users/me", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result User
|
||||
if err := c.parseResponse(resp, &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// GetUser 获取用户详情
|
||||
func (c *Client) GetUser(ctx context.Context, id int64) (*User, error) {
|
||||
resp, err := c.doRequest(ctx, "GET", fmt.Sprintf("/api/v1/users/%d", id), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result User
|
||||
if err := c.parseResponse(resp, &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// ListUsers 获取用户列表
|
||||
func (c *Client) ListUsers(ctx context.Context, params *ListUsersParams) (*PaginatedResponse, error) {
|
||||
if params.Page <= 0 {
|
||||
params.Page = 1
|
||||
}
|
||||
if params.PageSize <= 0 {
|
||||
params.PageSize = 20
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/api/v1/users?page=%d&page_size=%d", params.Page, params.PageSize)
|
||||
if params.Keyword != "" {
|
||||
path += "&keyword=" + params.Keyword
|
||||
}
|
||||
if params.Status != "" {
|
||||
path += "&status=" + params.Status
|
||||
}
|
||||
|
||||
resp, err := c.doRequest(ctx, "GET", path, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result PaginatedResponse
|
||||
if err := c.parseResponse(resp, &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// CreateUser 创建用户
|
||||
func (c *Client) CreateUser(ctx context.Context, req *CreateUserRequest) (*User, error) {
|
||||
resp, err := c.doRequest(ctx, "POST", "/api/v1/users", req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result User
|
||||
if err := c.parseResponse(resp, &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// UpdateUser 更新用户
|
||||
func (c *Client) UpdateUser(ctx context.Context, id int64, req *UpdateUserRequest) (*User, error) {
|
||||
resp, err := c.doRequest(ctx, "PUT", fmt.Sprintf("/api/v1/users/%d", id), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result User
|
||||
if err := c.parseResponse(resp, &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// DeleteUser 删除用户
|
||||
func (c *Client) DeleteUser(ctx context.Context, id int64) error {
|
||||
resp, err := c.doRequest(ctx, "DELETE", fmt.Sprintf("/api/v1/users/%d", id), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.parseResponse(resp, nil)
|
||||
}
|
||||
|
||||
// AssignRoles 分配角色
|
||||
func (c *Client) AssignRoles(ctx context.Context, userID int64, req *AssignRolesRequest) error {
|
||||
resp, err := c.doRequest(ctx, "POST", fmt.Sprintf("/api/v1/users/%d/roles", userID), req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.parseResponse(resp, nil)
|
||||
}
|
||||
|
||||
// GetUserRoles 获取用户角色
|
||||
func (c *Client) GetUserRoles(ctx context.Context, userID int64) ([]*Role, error) {
|
||||
resp, err := c.doRequest(ctx, "GET", fmt.Sprintf("/api/v1/users/%d/roles", userID), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result []*Role
|
||||
if err := c.parseResponse(resp, &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// UpdatePassword 更新密码
|
||||
func (c *Client) UpdatePassword(ctx context.Context, oldPassword, newPassword string) error {
|
||||
req := map[string]string{
|
||||
"old_password": oldPassword,
|
||||
"new_password": newPassword,
|
||||
}
|
||||
resp, err := c.doRequest(ctx, "POST", "/api/v1/users/me/password", req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.parseResponse(resp, nil)
|
||||
}
|
||||
|
||||
// BindEmail 绑定邮箱
|
||||
func (c *Client) BindEmail(ctx context.Context, email string) error {
|
||||
req := map[string]string{"email": email}
|
||||
resp, err := c.doRequest(ctx, "POST", "/api/v1/users/me/email", req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.parseResponse(resp, nil)
|
||||
}
|
||||
|
||||
// BindPhone 绑定手机
|
||||
func (c *Client) BindPhone(ctx context.Context, phone, code string) error {
|
||||
req := map[string]string{
|
||||
"phone": phone,
|
||||
"code": code,
|
||||
}
|
||||
resp, err := c.doRequest(ctx, "POST", "/api/v1/users/me/phone", req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.parseResponse(resp, nil)
|
||||
}
|
||||
|
||||
// EnableTOTP 启用 TOTP
|
||||
func (c *Client) EnableTOTP(ctx context.Context) (*TOTPSetupResponse, error) {
|
||||
resp, err := c.doRequest(ctx, "POST", "/api/v1/users/me/2fa/totp/setup", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result TOTPSetupResponse
|
||||
if err := c.parseResponse(resp, &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// TOTPSetupResponse TOTP 设置响应
|
||||
type TOTPSetupResponse struct {
|
||||
Secret string `json:"secret"`
|
||||
QRCodeURL string `json:"qr_code_url"`
|
||||
RecoveryCodes []string `json:"recovery_codes,omitempty"`
|
||||
}
|
||||
|
||||
// VerifyTOTP 验证 TOTP
|
||||
func (c *Client) VerifyTOTP(ctx context.Context, code string) error {
|
||||
req := map[string]string{"code": code}
|
||||
resp, err := c.doRequest(ctx, "POST", "/api/v1/users/me/2fa/totp/verify", req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.parseResponse(resp, nil)
|
||||
}
|
||||
|
||||
// DisableTOTP 禁用 TOTP
|
||||
func (c *Client) DisableTOTP(ctx context.Context, code string) error {
|
||||
req := map[string]string{"code": code}
|
||||
resp, err := c.doRequest(ctx, "POST", "/api/v1/users/me/2fa/totp/disable", req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.parseResponse(resp, nil)
|
||||
}
|
||||
Reference in New Issue
Block a user