mirror of https://github.com/cloudreve/Cloudreve
229 lines
7.2 KiB
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,
|
|
}
|
|
}
|