feat(mail): 重构邮箱权限体系并细化前台能力控制

- 新增邮箱前台权限与默认普通用户权限迁移,拆分读信、发信、草稿、定时、整理、标签、附件、联系人等能力。
- 收紧后端路由与处理逻辑,按权限限制邮箱前台、个人中心和定时发送相关接口。
- 前端根据权限动态隐藏或禁用邮件、个人中心中的对应功能入口与操作。
- 补充权限迁移与访问控制测试,覆盖普通用户邮箱前台、发信和定时发送的权限校验。
This commit is contained in:
LanQin_
2026-06-22 15:54:15 +08:00
parent 5571dcc511
commit 0b982a5ff4
8 changed files with 507 additions and 153 deletions
+116 -2
View File
@@ -13,6 +13,23 @@ import (
)
const (
PermissionMailAccess = "mail.access"
PermissionMailRead = "mail.messages.read"
PermissionMailSend = "mail.messages.send"
PermissionMailDrafts = "mail.messages.drafts"
PermissionMailSchedule = "mail.messages.schedule"
PermissionMailOrganize = "mail.messages.organize"
PermissionMailLabels = "mail.labels.manage"
PermissionMailAttachments = "mail.attachments.download"
PermissionMailContacts = "mail.contacts.manage"
PermissionMailSignatures = "mail.signatures.manage"
PermissionMailRules = "mail.rules.manage"
PermissionMailBlocked = "mail.blocked_senders.manage"
PermissionMailStats = "mail.stats.view"
PermissionMailboxApply = "mail.mailboxes.apply"
PermissionAdminOverview = "admin.overview.view"
PermissionUsersView = "admin.users.view"
@@ -65,6 +82,8 @@ const (
PermissionSystemSettings = PermissionSettingsUpdate
)
const regularUserPermissionMigrationKey = "permission_groups.regular_mail_permissions_v1"
const (
PermissionGroupSuperAdmin = "pg_super_admin"
PermissionGroupRegular = "pg_regular_user"
@@ -117,6 +136,13 @@ type PermissionGroup struct {
}
var legacyPermissionExpansions = map[string][]string{
"mail": regularUserDefaultPermissions(),
"mail.messages": {PermissionMailAccess, PermissionMailRead, PermissionMailSend, PermissionMailDrafts, PermissionMailSchedule, PermissionMailOrganize, PermissionMailAttachments},
"mail.labels": {PermissionMailAccess, PermissionMailRead, PermissionMailLabels},
"mail.contacts": {PermissionMailContacts},
"mail.signatures": {PermissionMailSignatures},
"mail.rules": {PermissionMailRules, PermissionMailBlocked},
"mail.mailboxes": {PermissionMailboxApply},
"admin.overview": {PermissionAdminOverview},
"admin.users": {PermissionUsersView, PermissionUsersCreate, PermissionUsersUpdate, PermissionUsersDelete, PermissionUsersResetPassword, PermissionGroupsView},
"admin.permission_groups": {PermissionGroupsView, PermissionGroupsCreate, PermissionGroupsUpdate, PermissionGroupsDelete},
@@ -129,6 +155,21 @@ var legacyPermissionExpansions = map[string][]string{
}
var permissionCatalogItems = []PermissionInfo{
{Key: PermissionMailAccess, Label: "访问邮箱前台", Description: "进入邮箱前台并查看本人邮箱列表。", Category: "邮箱前台"},
{Key: PermissionMailRead, Label: "查看本人邮件", Description: "查看本人文件夹、邮件列表、星标邮件和邮件正文。", Category: "邮箱前台"},
{Key: PermissionMailSend, Label: "发送邮件", Description: "使用本人邮箱发送、回复和转发邮件。", Category: "邮箱前台"},
{Key: PermissionMailDrafts, Label: "管理草稿", Description: "保存、编辑和删除本人草稿。", Category: "邮箱前台"},
{Key: PermissionMailSchedule, Label: "定时发送", Description: "创建、查看和取消本人定时发送任务。", Category: "邮箱前台"},
{Key: PermissionMailOrganize, Label: "整理邮件", Description: "标记已读、星标、移动、归档、删除和清理本人邮件。", Category: "邮箱前台"},
{Key: PermissionMailLabels, Label: "管理邮件标签", Description: "创建标签,并为本人邮件添加或移除标签。", Category: "邮箱前台"},
{Key: PermissionMailAttachments, Label: "下载附件", Description: "下载本人邮件中的附件。", Category: "邮箱前台"},
{Key: PermissionMailContacts, Label: "管理联系人", Description: "查看、新增和删除本人的联系人。", Category: "个人中心"},
{Key: PermissionMailSignatures, Label: "管理签名", Description: "查看、新增、修改和删除本人的邮件签名。", Category: "个人中心"},
{Key: PermissionMailRules, Label: "管理收件规则", Description: "查看、新增和删除本人的收件规则。", Category: "个人中心"},
{Key: PermissionMailBlocked, Label: "管理拦截名单", Description: "查看、新增和删除本人的发件人拦截规则。", Category: "个人中心"},
{Key: PermissionMailStats, Label: "查看邮箱统计", Description: "查看本人邮箱统计和清理概览。", Category: "个人中心"},
{Key: PermissionMailboxApply, Label: "自助申请邮箱", Description: "在开放申请时为本人申请邮箱账号。", Category: "个人中心"},
{Key: PermissionAdminOverview, Label: "查看概览", Description: "查看后台统计和首次配置检查。", Category: "概览"},
{Key: PermissionUsersView, Label: "查看用户", Description: "查看用户列表、状态和绑定邮箱。", Category: "用户"},
@@ -186,6 +227,16 @@ func allPermissionKeys() []string {
return out
}
func adminPermissionKeys() []string {
out := make([]string, 0, len(permissionCatalogItems))
for _, item := range permissionCatalogItems {
if strings.HasPrefix(item.Key, "admin.") {
out = append(out, item.Key)
}
}
return out
}
func permissionSet() map[string]bool {
out := map[string]bool{}
for _, item := range permissionCatalogItems {
@@ -269,12 +320,31 @@ func defaultPermissionGroups() []PermissionGroup {
ID: PermissionGroupRegular,
Name: "普通用户",
Description: "仅可使用自己的邮箱功能,不包含后台权限。",
Permissions: []string{},
Permissions: regularUserDefaultPermissions(),
System: true,
},
}
}
func regularUserDefaultPermissions() []string {
return []string{
PermissionMailAccess,
PermissionMailRead,
PermissionMailSend,
PermissionMailDrafts,
PermissionMailSchedule,
PermissionMailOrganize,
PermissionMailLabels,
PermissionMailAttachments,
PermissionMailContacts,
PermissionMailSignatures,
PermissionMailRules,
PermissionMailBlocked,
PermissionMailStats,
PermissionMailboxApply,
}
}
func fixedPermissionGroupIDs() map[string]bool {
out := map[string]bool{}
for _, group := range defaultPermissionGroups() {
@@ -331,9 +401,53 @@ func (a *App) ensureDefaultPermissionGroups(ctx context.Context) error {
if err := a.cleanupLegacyDefaultPermissionGroups(ctx); err != nil {
return err
}
if err := a.ensureRegularUserMailPermissions(ctx); err != nil {
return err
}
return nil
}
func (a *App) ensureRegularUserMailPermissions(ctx context.Context) error {
var migrated string
err := a.db.QueryRowContext(ctx, `SELECT value FROM system_settings WHERE key=?`, regularUserPermissionMigrationKey).Scan(&migrated)
if err == nil && migrated == "1" {
return nil
}
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return err
}
var raw string
if err := a.db.QueryRowContext(ctx, `SELECT permissions_json FROM permission_groups WHERE id=?`, PermissionGroupRegular).Scan(&raw); err != nil {
return err
}
seen := map[string]bool{}
for _, permission := range decodeStoredPermissions(raw) {
seen[permission] = true
}
changed := false
for _, permission := range regularUserDefaultPermissions() {
if !seen[permission] {
seen[permission] = true
changed = true
}
}
now := a.now().UTC().Format(time.RFC3339Nano)
if changed {
permissions := make([]string, 0, len(seen))
for _, permission := range allPermissionKeys() {
if seen[permission] {
permissions = append(permissions, permission)
}
}
if _, err := a.db.ExecContext(ctx, `UPDATE permission_groups SET permissions_json=?, updated_at=? WHERE id=?`, encodePermissions(permissions), now, PermissionGroupRegular); err != nil {
return err
}
}
_, err = a.db.ExecContext(ctx, `INSERT INTO system_settings(key,value,updated_at) VALUES(?,?,?)
ON CONFLICT(key) DO UPDATE SET value=excluded.value, updated_at=excluded.updated_at`, regularUserPermissionMigrationKey, "1", now)
return err
}
func (a *App) cleanupLegacyDefaultPermissionGroups(ctx context.Context) error {
for _, groupID := range legacyDefaultPermissionGroupIDs {
var userCount int
@@ -543,7 +657,7 @@ func userHasAnyPermission(user *User, permissions ...string) bool {
}
func userHasAdminAccess(user *User) bool {
return userHasAnyPermission(user, allPermissionKeys()...)
return userHasAnyPermission(user, adminPermissionKeys()...)
}
func (a *App) requireAdminAccess(next http.Handler) http.Handler {