Cloudreve/service/user/response.go

229 lines
7.2 KiB
Go

package user
import (
"fmt"
"time"
"github.com/cloudreve/Cloudreve/v4/ent"
"github.com/cloudreve/Cloudreve/v4/ent/user"
"github.com/cloudreve/Cloudreve/v4/inventory/types"
"github.com/cloudreve/Cloudreve/v4/pkg/auth"
"github.com/cloudreve/Cloudreve/v4/pkg/boolset"
"github.com/cloudreve/Cloudreve/v4/pkg/hashid"
"github.com/go-webauthn/webauthn/protocol"
"github.com/go-webauthn/webauthn/webauthn"
"github.com/samber/lo"
"github.com/ua-parser/uap-go/uaparser"
)
type PreparePasskeyLoginResponse struct {
Options *protocol.CredentialAssertion `json:"options"`
SessionID string `json:"session_id"`
}
type UserSettings struct {
VersionRetentionEnabled bool `json:"version_retention_enabled"`
VersionRetentionExt []string `json:"version_retention_ext,omitempty"`
VersionRetentionMax int `json:"version_retention_max,omitempty"`
Paswordless bool `json:"passwordless"`
TwoFAEnabled bool `json:"two_fa_enabled"`
Passkeys []Passkey `json:"passkeys,omitempty"`
DisableViewSync bool `json:"disable_view_sync"`
ShareLinksInProfile string `json:"share_links_in_profile"`
}
func BuildUserSettings(u *ent.User, passkeys []*ent.Passkey, parser *uaparser.Parser) *UserSettings {
return &UserSettings{
VersionRetentionEnabled: u.Settings.VersionRetention,
VersionRetentionExt: u.Settings.VersionRetentionExt,
VersionRetentionMax: u.Settings.VersionRetentionMax,
TwoFAEnabled: u.TwoFactorSecret != "",
Paswordless: u.Password == "",
Passkeys: lo.Map(passkeys, func(item *ent.Passkey, index int) Passkey {
return BuildPasskey(item)
}),
DisableViewSync: u.Settings.DisableViewSync,
ShareLinksInProfile: string(u.Settings.ShareLinksInProfile),
}
}
type Passkey struct {
ID string `json:"id"`
Name string `json:"name"`
UsedAt *time.Time `json:"used_at,omitempty"`
CreatedAt time.Time `json:"created_at"`
}
func BuildPasskey(passkey *ent.Passkey) Passkey {
return Passkey{
ID: passkey.CredentialID,
Name: passkey.Name,
UsedAt: passkey.UsedAt,
CreatedAt: passkey.CreatedAt,
}
}
// Node option for handling workflows.
type Node struct {
ID string `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
Capabilities *boolset.BooleanSet `json:"capabilities"`
}
// BuildNodes serialize a list of nodes.
func BuildNodes(nodes []*ent.Node, idEncoder hashid.Encoder) []*Node {
res := make([]*Node, 0, len(nodes))
for _, v := range nodes {
res = append(res, BuildNode(v, idEncoder))
}
return res
}
// BuildNode serialize a node.
func BuildNode(node *ent.Node, idEncoder hashid.Encoder) *Node {
return &Node{
ID: hashid.EncodeNodeID(idEncoder, node.ID),
Name: node.Name,
Type: string(node.Type),
Capabilities: node.Capabilities,
}
}
// BuiltinLoginResponse response for a successful login for builtin auth provider.
type BuiltinLoginResponse struct {
User User `json:"user"`
Token auth.Token `json:"token"`
}
// User 用户序列化器
type User struct {
ID string `json:"id"`
Email string `json:"email,omitempty"`
Nickname string `json:"nickname"`
Status user.Status `json:"status,omitempty"`
Avatar string `json:"avatar,omitempty"`
CreatedAt time.Time `json:"created_at"`
PreferredTheme string `json:"preferred_theme,omitempty"`
Anonymous bool `json:"anonymous,omitempty"`
Group *Group `json:"group,omitempty"`
Pined []types.PinedFile `json:"pined,omitempty"`
Language string `json:"language,omitempty"`
DisableViewSync bool `json:"disable_view_sync,omitempty"`
ShareLinksInProfile types.ShareLinksInProfileLevel `json:"share_links_in_profile,omitempty"`
}
type Group struct {
ID string `json:"id"`
Name string `json:"name"`
Permission *boolset.BooleanSet `json:"permission,omitempty"`
DirectLinkBatchSize int `json:"direct_link_batch_size,omitempty"`
TrashRetention int `json:"trash_retention,omitempty"`
}
type storage struct {
Used uint64 `json:"used"`
Free uint64 `json:"free"`
Total uint64 `json:"total"`
}
// WebAuthnCredentials 外部验证器凭证
type WebAuthnCredentials struct {
ID []byte `json:"id"`
FingerPrint string `json:"fingerprint"`
}
type PrepareLoginResponse struct {
WebAuthnEnabled bool `json:"webauthn_enabled"`
PasswordEnabled bool `json:"password_enabled"`
}
// BuildWebAuthnList 构建设置页面凭证列表
func BuildWebAuthnList(credentials []webauthn.Credential) []WebAuthnCredentials {
res := make([]WebAuthnCredentials, 0, len(credentials))
for _, v := range credentials {
credential := WebAuthnCredentials{
ID: v.ID,
FingerPrint: fmt.Sprintf("% X", v.Authenticator.AAGUID),
}
res = append(res, credential)
}
return res
}
// BuildUser 序列化用户
func BuildUser(user *ent.User, idEncoder hashid.Encoder) User {
return User{
ID: hashid.EncodeUserID(idEncoder, user.ID),
Email: user.Email,
Nickname: user.Nick,
Status: user.Status,
Avatar: user.Avatar,
CreatedAt: user.CreatedAt,
PreferredTheme: user.Settings.PreferredTheme,
Anonymous: user.ID == 0,
Group: BuildGroup(user.Edges.Group, idEncoder),
Pined: user.Settings.Pined,
Language: user.Settings.Language,
DisableViewSync: user.Settings.DisableViewSync,
ShareLinksInProfile: user.Settings.ShareLinksInProfile,
}
}
func BuildGroup(group *ent.Group, idEncoder hashid.Encoder) *Group {
if group == nil {
return nil
}
return &Group{
ID: hashid.EncodeGroupID(idEncoder, group.ID),
Name: group.Name,
Permission: group.Permissions,
DirectLinkBatchSize: group.Settings.SourceBatchSize,
TrashRetention: group.Settings.TrashRetention,
}
}
const sensitiveTag = "redacted"
const (
RedactLevelAnonymous = iota
RedactLevelUser
)
// BuildUserRedacted Serialize a user without sensitive information.
func BuildUserRedacted(u *ent.User, level int, idEncoder hashid.Encoder) User {
userRaw := BuildUser(u, idEncoder)
user := User{
ID: userRaw.ID,
Nickname: userRaw.Nickname,
Avatar: userRaw.Avatar,
CreatedAt: userRaw.CreatedAt,
ShareLinksInProfile: userRaw.ShareLinksInProfile,
}
if userRaw.Group != nil {
user.Group = RedactedGroup(userRaw.Group)
}
if level == RedactLevelUser {
user.Email = userRaw.Email
}
return user
}
// BuildGroupRedacted Serialize a group without sensitive information.
func RedactedGroup(g *Group) *Group {
if g == nil {
return nil
}
return &Group{
ID: g.ID,
Name: g.Name,
}
}