修复角色日志迁移与插件授权测试
This commit is contained in:
parent
46fd3a6bff
commit
c6071414df
@ -19,7 +19,7 @@ const (
|
||||
)
|
||||
|
||||
func GetPluginList(ctx *context.Context, w http.ResponseWriter, req *http.Request) {
|
||||
if err := authorizeCapability(ctx, access.CapabilityPluginRead, "", false); err != nil {
|
||||
if !authenticatedPluginUser(ctx) || authorizeCapability(ctx, access.CapabilityPluginRead, "", false) != nil {
|
||||
response.NewErrorResponse(response.NoAccessPrivileges, "\u65e0\u6743\u8bbf\u95ee", "").FPrint(w)
|
||||
return
|
||||
}
|
||||
@ -32,7 +32,7 @@ func GetPluginList(ctx *context.Context, w http.ResponseWriter, req *http.Reques
|
||||
}
|
||||
|
||||
func SettingsHtml(ctx *context.Context, w http.ResponseWriter, req *http.Request) {
|
||||
if err := authorizeCapability(ctx, access.CapabilityPluginReadGlobal, "", false); err != nil {
|
||||
if !authenticatedPluginUser(ctx) || authorizeCapability(ctx, access.CapabilityPluginReadGlobal, "", false) != nil {
|
||||
response.NewErrorResponse(response.NoAccessPrivileges, "\u65e0\u6743\u8bbf\u95ee", "").FPrint(w)
|
||||
return
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"git.pcux.cn/xuans/Smail/hooks"
|
||||
hooksframework "git.pcux.cn/xuans/Smail/hooks/framework"
|
||||
"git.pcux.cn/xuans/Smail/models"
|
||||
"git.pcux.cn/xuans/Smail/services/access"
|
||||
"git.pcux.cn/xuans/Smail/utils/context"
|
||||
)
|
||||
|
||||
@ -43,7 +44,7 @@ func TestSettingsHtmlRejectsPathTraversal(t *testing.T) {
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
request := httptest.NewRequest(http.MethodGet, "/api/plugin/settings/%2e%2e/index.html", nil)
|
||||
SettingsHtml(&context.Context{IsAdmin: true}, recorder, request)
|
||||
SettingsHtml(&context.Context{UserID: 1, Role: access.RoleSuperAdmin}, recorder, request)
|
||||
if strings.Contains(recorder.Body.String(), "ok") {
|
||||
t.Fatal("path traversal reached a plugin")
|
||||
}
|
||||
@ -56,13 +57,13 @@ func TestSettingsHtmlSeparatesStaticAndSaveRequests(t *testing.T) {
|
||||
hooks.HookList = map[string]hooksframework.EmailHook{"PluginTest": hook}
|
||||
|
||||
getRecorder := httptest.NewRecorder()
|
||||
SettingsHtml(&context.Context{IsAdmin: true}, getRecorder, httptest.NewRequest(http.MethodGet, "/api/plugin/settings/PluginTest/index.html", nil))
|
||||
SettingsHtml(&context.Context{UserID: 1, Role: access.RoleSuperAdmin}, getRecorder, httptest.NewRequest(http.MethodGet, "/api/plugin/settings/PluginTest/index.html", nil))
|
||||
if hook.resource != "PluginTest/index.html" || !strings.HasPrefix(getRecorder.Header().Get("Content-Type"), "text/html") {
|
||||
t.Fatalf("static plugin page was not served safely: resource=%s type=%s", hook.resource, getRecorder.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
postRecorder := httptest.NewRecorder()
|
||||
SettingsHtml(&context.Context{IsAdmin: true}, postRecorder, httptest.NewRequest(http.MethodPost, "/api/plugin/settings/PluginTest/save", strings.NewReader(`{"url":"https://model.example.com"}`)))
|
||||
SettingsHtml(&context.Context{UserID: 1, Role: access.RoleSuperAdmin}, postRecorder, httptest.NewRequest(http.MethodPost, "/api/plugin/settings/PluginTest/save", strings.NewReader(`{"url":"https://model.example.com"}`)))
|
||||
if postRecorder.Code != http.StatusGone {
|
||||
t.Fatalf("legacy plugin save status = %d, want %d", postRecorder.Code, http.StatusGone)
|
||||
}
|
||||
@ -78,7 +79,7 @@ func TestSettingsHtmlRejectsOversizedPluginResponse(t *testing.T) {
|
||||
hooks.HookList = map[string]hooksframework.EmailHook{"PluginTest": hook}
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
SettingsHtml(&context.Context{IsAdmin: true}, recorder, httptest.NewRequest(http.MethodGet, "/api/plugin/settings/PluginTest/index.html", nil))
|
||||
SettingsHtml(&context.Context{UserID: 1, Role: access.RoleSuperAdmin}, recorder, httptest.NewRequest(http.MethodGet, "/api/plugin/settings/PluginTest/index.html", nil))
|
||||
if strings.Contains(recorder.Body.String(), hook.response) {
|
||||
t.Fatal("oversized plugin response was returned")
|
||||
}
|
||||
@ -109,11 +110,17 @@ func TestPluginConfigurationRejectsNonAdminAccess(t *testing.T) {
|
||||
listRecorder := httptest.NewRecorder()
|
||||
GetPluginList(&context.Context{}, listRecorder, httptest.NewRequest(http.MethodGet, "/api/plugin/list", nil))
|
||||
if !strings.Contains(listRecorder.Body.String(), "\u65e0\u6743\u8bbf\u95ee") {
|
||||
t.Fatalf("non-admin plugin list was not rejected: body=%s", listRecorder.Body.String())
|
||||
t.Fatalf("unauthenticated plugin list was not rejected: body=%s", listRecorder.Body.String())
|
||||
}
|
||||
|
||||
userListRecorder := httptest.NewRecorder()
|
||||
GetPluginList(&context.Context{UserID: 2, Role: access.RoleUser}, userListRecorder, httptest.NewRequest(http.MethodGet, "/api/plugin/list", nil))
|
||||
if !strings.Contains(userListRecorder.Body.String(), "PluginTest") {
|
||||
t.Fatalf("authenticated user cannot read plugin list: body=%s", userListRecorder.Body.String())
|
||||
}
|
||||
|
||||
pageRecorder := httptest.NewRecorder()
|
||||
SettingsHtml(&context.Context{}, pageRecorder, httptest.NewRequest(http.MethodGet, "/api/plugin/settings/PluginTest/index.html", nil))
|
||||
SettingsHtml(&context.Context{UserID: 2, Role: access.RoleUser}, pageRecorder, httptest.NewRequest(http.MethodGet, "/api/plugin/settings/PluginTest/index.html", nil))
|
||||
if !strings.Contains(pageRecorder.Body.String(), "\u65e0\u6743\u8bbf\u95ee") {
|
||||
t.Fatalf("non-admin plugin page was not rejected: body=%s", pageRecorder.Body.String())
|
||||
}
|
||||
|
||||
@ -55,6 +55,13 @@ func TestDefaultMigrationsCreateCompleteSQLiteSchema(t *testing.T) {
|
||||
t.Fatalf("index %s was not created", index)
|
||||
}
|
||||
}
|
||||
exists, err = columnExists(context.Background(), db, DialectSQLite, "operation_log", "actor_role")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !exists {
|
||||
t.Fatal("column operation_log.actor_role was not created")
|
||||
}
|
||||
}
|
||||
|
||||
func sqliteIndexExists(t *testing.T, db *sql.DB, index string) bool {
|
||||
@ -161,6 +168,60 @@ func TestUserEmailLoginAddressMigrationRejectsUnexpectedDialect(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestOperationLogActorRoleMigrationBackfillsValidatedRoles(t *testing.T) {
|
||||
db := openSQLite(t)
|
||||
for _, statement := range logStatements()[DialectSQLite] {
|
||||
if _, err := db.Exec(statement); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
rows := []struct {
|
||||
id string
|
||||
payload string
|
||||
want any
|
||||
}{
|
||||
{id: "user", payload: `{"actorRole":"user"}`, want: "user"},
|
||||
{id: "admin", payload: `{"actorRole":" admin "}`, want: "admin"},
|
||||
{id: "super", payload: `{"actorRole":"super_admin"}`, want: "super_admin"},
|
||||
{id: "missing", payload: `{}`, want: nil},
|
||||
{id: "unknown", payload: `{"actorRole":"owner"}`, want: nil},
|
||||
{id: "invalid", payload: `{`, want: nil},
|
||||
}
|
||||
for _, row := range rows {
|
||||
if _, err := db.Exec(`INSERT INTO operation_log (id, created_at, kind, actor_id, action, section, result, payload) VALUES (?, 1, 'operation', '1', 'test', '', 'success', ?)`, row.id, row.payload); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if err := applyOperationLogActorRoleSchema(context.Background(), db, DialectSQLite); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := applyOperationLogActorRoleSchema(context.Background(), db, DialectSQLite); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, row := range rows {
|
||||
var got sql.NullString
|
||||
if err := db.QueryRow(`SELECT actor_role FROM operation_log WHERE id = ?`, row.id).Scan(&got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if row.want == nil {
|
||||
if got.Valid {
|
||||
t.Fatalf("invalid historical role %s was backfilled as %q", row.id, got.String)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if !got.Valid || got.String != row.want {
|
||||
t.Fatalf("historical role %s = %q, want %q", row.id, got.String, row.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOperationLogActorRoleMigrationRejectsUnexpectedDialect(t *testing.T) {
|
||||
err := applyOperationLogActorRoleSchema(context.Background(), nil, Dialect("unsupported"))
|
||||
if err == nil || !strings.Contains(err.Error(), "unsupported") {
|
||||
t.Fatalf("unexpected dialect error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecurityEmailMySQLSchemaDoesNotCreateDuplicateIndex(t *testing.T) {
|
||||
statements := securityEmailStatements()[DialectMySQL]
|
||||
for _, statement := range statements {
|
||||
@ -221,6 +282,7 @@ func TestDefaultMigrationChecksumsAreImmutable(t *testing.T) {
|
||||
15: "59f0ff9acfed3619c17f078d21a1362ddd5f1feacb9a2d77626f69040d25de65",
|
||||
16: "00cc275529e522d93b41e608692fb6e2271f1aa9300ebe020fcaed4d68d0ff76",
|
||||
17: "1dad66acb5e7fecf6c39f4a78526b994748d90045220f446a2e80c1b5fd881e7",
|
||||
18: "17622df3406657f199d78a3a1aa1f3a8693baf85aa7707910cc6d6cb44c13ee4",
|
||||
}
|
||||
migrations := DefaultMigrations()
|
||||
if len(migrations) != len(expected) {
|
||||
|
||||
@ -123,16 +123,6 @@ func (s *SpamBlock) SettingsHtml(ctx *context.Context, url string, requestData s
|
||||
return "没有访问权限"
|
||||
}
|
||||
return "没有访问权限"
|
||||
|
||||
cfg, err := decodeSpamBlockConfig([]byte(requestData))
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
s.applyConfig(cfg)
|
||||
|
||||
return "保存成功"
|
||||
|
||||
}
|
||||
|
||||
func canManageSpamBlockSettings(ctx *context.Context) bool {
|
||||
|
||||
@ -179,15 +179,15 @@ func TestDecodeSpamBlockConfigUsesUnifiedValidation(t *testing.T) {
|
||||
func TestSettingsHtmlAppliesValidatedConfiguration(t *testing.T) {
|
||||
s := &SpamBlock{}
|
||||
result := s.SettingsHtml(&context.Context{IsAdmin: true}, "SpamBlock/save", `{"url":"https://model.example.com","timeout":"3000","threshold":"25"}`)
|
||||
if result != "保存成功" {
|
||||
if result != "旧版插件配置保存接口已停用,请使用新版插件配置接口" {
|
||||
t.Fatalf("save result = %q", result)
|
||||
}
|
||||
s.mu.RLock()
|
||||
config := s.cfg
|
||||
client := s.hc
|
||||
s.mu.RUnlock()
|
||||
if config.ApiURL != "https://model.example.com" || config.ApiTimeout != 3000 || config.Threshold != 25 || client == nil {
|
||||
t.Fatalf("unexpected applied configuration: %+v", config)
|
||||
if config != (SpamBlockConfig{}) || client != nil {
|
||||
t.Fatalf("legacy save changed configuration: %+v", config)
|
||||
}
|
||||
}
|
||||
|
||||
@ -225,9 +225,9 @@ func TestBuildSpamBlockModelTextHandlesNilAndLimitsSource(t *testing.T) {
|
||||
|
||||
func TestSettingsHtmlRejectsNonAdminSave(t *testing.T) {
|
||||
s := &SpamBlock{}
|
||||
result := s.SettingsHtml(&context.Context{}, "SpamBlock/save", `{"url":"https://model.example.com/predict"}`)
|
||||
if result != "没有访问权限" {
|
||||
t.Fatalf("non-admin save result = %q, want access denial", result)
|
||||
result := s.SettingsHtml(&context.Context{}, "SpamBlock/index.html", "")
|
||||
if !strings.Contains(result, "请联系管理员") {
|
||||
t.Fatalf("non-admin page result = %q, want access denial", result)
|
||||
}
|
||||
if s.cfg.ApiURL != "" {
|
||||
t.Fatal("non-admin save changed the configuration")
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
|
||||
"git.pcux.cn/xuans/Smail/config"
|
||||
"git.pcux.cn/xuans/Smail/dto/parsemail"
|
||||
"git.pcux.cn/xuans/Smail/services/access"
|
||||
"git.pcux.cn/xuans/Smail/utils/context"
|
||||
)
|
||||
|
||||
@ -50,17 +51,35 @@ func TestAuthorizedSubmissionEmailLimitsAdminToLocalDomains(t *testing.T) {
|
||||
oldConfig := config.Instance
|
||||
t.Cleanup(func() { config.Instance = oldConfig })
|
||||
config.Instance = &config.Config{Domains: []string{"example.com"}}
|
||||
ctx := &context.Context{UserID: 1, UserAccount: "admin", IsAdmin: true}
|
||||
ctx := &context.Context{UserID: 1, UserAccount: "admin", Role: access.RoleAdmin}
|
||||
|
||||
local := &parsemail.Email{
|
||||
From: &parsemail.User{EmailAddress: "alias@example.com"},
|
||||
Sender: &parsemail.User{EmailAddress: "alias@example.com"},
|
||||
From: &parsemail.User{EmailAddress: "admin@example.com"},
|
||||
Sender: &parsemail.User{EmailAddress: "admin@example.com"},
|
||||
}
|
||||
if !authorizedSubmissionEmail(local, "alias@example.com", ctx) {
|
||||
t.Fatal("admin local alias was rejected")
|
||||
if !authorizedSubmissionEmail(local, "admin@example.com", ctx) {
|
||||
t.Fatal("admin own sender was rejected")
|
||||
}
|
||||
local.From.EmailAddress = "alias@example.com"
|
||||
local.Sender.EmailAddress = "alias@example.com"
|
||||
if authorizedSubmissionEmail(local, "alias@example.com", ctx) {
|
||||
t.Fatal("admin local alias was accepted")
|
||||
}
|
||||
local.From.EmailAddress = "alias@external.example"
|
||||
if authorizedSubmissionEmail(local, "alias@example.com", ctx) {
|
||||
local.Sender.EmailAddress = "alias@external.example"
|
||||
if authorizedSubmissionEmail(local, "alias@external.example", ctx) {
|
||||
t.Fatal("admin external sender domain was accepted")
|
||||
}
|
||||
|
||||
superAdmin := &context.Context{UserID: 2, UserAccount: "root", Role: access.RoleSuperAdmin}
|
||||
local.From.EmailAddress = "alias@example.com"
|
||||
local.Sender.EmailAddress = "alias@example.com"
|
||||
if !authorizedSubmissionEmail(local, "alias@example.com", superAdmin) {
|
||||
t.Fatal("super admin local alias was rejected")
|
||||
}
|
||||
local.From.EmailAddress = "alias@external.example"
|
||||
local.Sender.EmailAddress = "alias@external.example"
|
||||
if authorizedSubmissionEmail(local, "alias@external.example", superAdmin) {
|
||||
t.Fatal("super admin external sender domain was accepted")
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,10 @@ import "testing"
|
||||
|
||||
func TestLogMigrationChecksum(t *testing.T) {
|
||||
items := DefaultMigrations()
|
||||
if got := items[len(items)-1].Checksum(); got != "1dad66acb5e7fecf6c39f4a78526b994748d90045220f446a2e80c1b5fd881e7" {
|
||||
if got := items[16].Checksum(); got != "1dad66acb5e7fecf6c39f4a78526b994748d90045220f446a2e80c1b5fd881e7" {
|
||||
t.Fatalf("unexpected log migration checksum: %s", got)
|
||||
}
|
||||
if got := items[17].Checksum(); got != "17622df3406657f199d78a3a1aa1f3a8693baf85aa7707910cc6d6cb44c13ee4" {
|
||||
t.Fatalf("unexpected operation log role migration checksum: %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,10 +9,7 @@ import (
|
||||
|
||||
func TestLogScopeFiltersBeforePagination(t *testing.T) {
|
||||
s := testStore(t)
|
||||
if _, err := s.db.Exec(`CREATE TABLE "user" (id INTEGER PRIMARY KEY, role TEXT, is_admin INTEGER)`); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := s.db.Exec(`INSERT INTO "user" VALUES (1,'super_admin',1),(2,'admin',1),(3,'admin',1),(4,'user',0),(5,NULL,0),(6,'legacy',1),(7,'user',1)`); err != nil {
|
||||
if _, err := s.db.Exec(`INSERT INTO "user" (id, account, name, password, role, is_admin) VALUES (1,'root','Root','hash','super_admin',1),(2,'manager','Manager','hash','admin',1),(3,'other-manager','Other','hash','admin',1),(4,'alice','Alice','hash','user',0),(5,'legacy','Legacy','hash','legacy',0),(6,'legacy-manager','Legacy admin','hash','legacy',1),(7,'user-admin-flag','User admin flag','hash','user',1)`); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
@ -73,7 +73,7 @@ func TestLogStoreServerDatabaseIntegration(t *testing.T) {
|
||||
t.Fatal("dedicated test database is unavailable")
|
||||
}
|
||||
migrations := migration.DefaultMigrations()
|
||||
for _, statement := range migrations[len(migrations)-1].StatementsFor(test.dialect) {
|
||||
for _, statement := range migrations[len(migrations)-2].StatementsFor(test.dialect) {
|
||||
if strings.HasPrefix(statement, "CREATE TABLE IF NOT EXISTS ") {
|
||||
statement = strings.Replace(statement, "CREATE TABLE IF NOT EXISTS ", "CREATE TEMPORARY TABLE ", 1)
|
||||
}
|
||||
@ -81,6 +81,10 @@ func TestLogStoreServerDatabaseIntegration(t *testing.T) {
|
||||
t.Fatal("temporary log schema creation failed")
|
||||
}
|
||||
}
|
||||
actorRoleDefinition := "VARCHAR(20) NULL"
|
||||
if _, err := database.ExecContext(ctx, "ALTER TABLE operation_log ADD COLUMN actor_role "+actorRoleDefinition); err != nil {
|
||||
t.Fatal("temporary operation log role column creation failed")
|
||||
}
|
||||
store, err := New(database, test.dialect)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@ -24,11 +24,9 @@ func testStore(t *testing.T) *Store {
|
||||
}
|
||||
database.SetMaxOpenConns(1)
|
||||
t.Cleanup(func() { _ = database.Close() })
|
||||
items := migration.DefaultMigrations()
|
||||
for _, statement := range items[len(items)-1].StatementsFor(migration.DialectSQLite) {
|
||||
if _, err := database.Exec(statement); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runner := migration.Runner{DB: database, Dialect: migration.DialectSQLite, Migrations: migration.DefaultMigrations()}
|
||||
if _, err := runner.Run(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
store, err := New(database, migration.DialectSQLite)
|
||||
if err != nil {
|
||||
|
||||
@ -28,11 +28,9 @@ func TestLogsAPIRejectsUnscopedAccessAndValidatesQueries(t *testing.T) {
|
||||
defer database.Close()
|
||||
// 与正式 SQLite 连接池一致,避免测试自行引入多写连接锁冲突。
|
||||
database.SetMaxOpenConns(1)
|
||||
items := migration.DefaultMigrations()
|
||||
for _, statement := range items[len(items)-1].StatementsFor(migration.DialectSQLite) {
|
||||
if _, err := database.Exec(statement); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runner := migration.Runner{DB: database, Dialect: migration.DialectSQLite, Migrations: migration.DefaultMigrations()}
|
||||
if _, err := runner.Run(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
store, err := logstore.New(database, migration.DialectSQLite)
|
||||
if err != nil {
|
||||
|
||||
@ -43,10 +43,9 @@ func TestOperationLogUsesCompletedIdentityWithoutSensitivePayload(t *testing.T)
|
||||
defer database.Close()
|
||||
database.SetMaxOpenConns(1)
|
||||
migrations := migration.DefaultMigrations()
|
||||
for _, statement := range migrations[len(migrations)-1].StatementsFor(migration.DialectSQLite) {
|
||||
if _, err := database.Exec(statement); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runner := migration.Runner{DB: database, Dialect: migration.DialectSQLite, Migrations: migrations}
|
||||
if _, err := runner.Run(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
store, err := logstore.New(database, migration.DialectSQLite)
|
||||
if err != nil {
|
||||
@ -85,10 +84,9 @@ func TestOperationResponseDistinguishesPendingHTTPFailureAndUnknown(t *testing.T
|
||||
// 与生产 SQLite 连接池一致,避免后台清理和写入争抢无等待的独立连接。
|
||||
database.SetMaxOpenConns(1)
|
||||
migrations := migration.DefaultMigrations()
|
||||
for _, statement := range migrations[len(migrations)-1].StatementsFor(migration.DialectSQLite) {
|
||||
if _, err := database.Exec(statement); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runner := migration.Runner{DB: database, Dialect: migration.DialectSQLite, Migrations: migrations}
|
||||
if _, err := runner.Run(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
store, err := logstore.New(database, migration.DialectSQLite)
|
||||
if err != nil {
|
||||
|
||||
@ -97,16 +97,23 @@ func TestAccountRoleScopeAndOwnProfile(t *testing.T) {
|
||||
}
|
||||
for _, ctx := range []*appcontext.Context{admin, ordinary} {
|
||||
ctx.IsAdmin = true
|
||||
if got := call(ctx, GetPluginList, `{}`); got.ErrorNo != response.NoAccessPrivileges {
|
||||
got := call(ctx, GetPluginList, `{}`)
|
||||
if got.ErrorNo != 0 {
|
||||
t.Fatal("已登录角色无法读取可用插件列表")
|
||||
}
|
||||
pageRecorder := httptest.NewRecorder()
|
||||
SettingsHtml(ctx, pageRecorder, httptest.NewRequest(http.MethodGet, "/api/plugin/settings/missing/index.html", nil))
|
||||
if ctx == ordinary && !strings.Contains(pageRecorder.Body.String(), "无权访问") {
|
||||
t.Fatal("旧管理员标志绕过插件页面授权")
|
||||
}
|
||||
ctx.IsAdmin = false
|
||||
if ctx == admin && strings.Contains(pageRecorder.Body.String(), "无权访问") {
|
||||
t.Fatal("管理员角色未获得插件页面授权")
|
||||
}
|
||||
}
|
||||
migrations := migration.DefaultMigrations()
|
||||
for _, statement := range migrations[len(migrations)-1].StatementsFor(migration.DialectSQLite) {
|
||||
if _, err := engine.Exec(statement); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runner := migration.Runner{DB: engine.DB().DB, Dialect: migration.DialectSQLite, Migrations: migrations}
|
||||
if _, err := runner.Run(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
store, err := logstore.New(engine.DB().DB, migration.DialectSQLite)
|
||||
if err != nil {
|
||||
@ -115,7 +122,7 @@ func TestAccountRoleScopeAndOwnProfile(t *testing.T) {
|
||||
runtime := logstore.Start(store)
|
||||
defer runtime.Stop()
|
||||
for _, user := range users {
|
||||
if err := store.Write(context.Background(), logstore.Entry{Kind: "operation", ActorID: itoa(user.ID), CreatedAt: time.Now(), Action: "权限验证", Result: "success", Payload: []byte(`{}`)}); err != nil {
|
||||
if err := store.Write(context.Background(), logstore.Entry{Kind: "operation", ActorID: itoa(user.ID), ActorRole: string(user.EffectiveRole()), CreatedAt: time.Now(), Action: "权限验证", Result: "success", Payload: []byte(`{}`)}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,11 +26,9 @@ func TestInboundMailLogBelongsToEachActualRecipient(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
migrations := migration.DefaultMigrations()
|
||||
for _, statement := range migrations[len(migrations)-1].StatementsFor(migration.DialectSQLite) {
|
||||
if _, err := engine.Exec(statement); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runner := migration.Runner{DB: engine.DB().DB, Dialect: migration.DialectSQLite, Migrations: migration.DefaultMigrations()}
|
||||
if _, err := runner.Run(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
store, err := logstore.New(engine.DB().DB, migration.DialectSQLite)
|
||||
if err != nil {
|
||||
@ -67,11 +65,9 @@ func (failedMailReader) Read([]byte) (int, error) { return 0, errors.New("privat
|
||||
func TestSMTPDataLogDistinguishesDiscardedMailAndReadFailure(t *testing.T) {
|
||||
engine, _ := newAuditTestEngine(t)
|
||||
config.Instance.SpamFilterLevel = 3
|
||||
migrations := migration.DefaultMigrations()
|
||||
for _, statement := range migrations[len(migrations)-1].StatementsFor(migration.DialectSQLite) {
|
||||
if _, err := engine.Exec(statement); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runner := migration.Runner{DB: engine.DB().DB, Dialect: migration.DialectSQLite, Migrations: migration.DefaultMigrations()}
|
||||
if _, err := runner.Run(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
store, err := logstore.New(engine.DB().DB, migration.DialectSQLite)
|
||||
if err != nil {
|
||||
|
||||
@ -21,11 +21,9 @@ func TestRelayMailLogRecordsResultWithoutChallengeSecrets(t *testing.T) {
|
||||
}
|
||||
defer database.Close()
|
||||
database.SetMaxOpenConns(1)
|
||||
migrations := migration.DefaultMigrations()
|
||||
for _, statement := range migrations[len(migrations)-1].StatementsFor(migration.DialectSQLite) {
|
||||
if _, err := database.Exec(statement); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runner := migration.Runner{DB: database, Dialect: migration.DialectSQLite, Migrations: migration.DefaultMigrations()}
|
||||
if _, err := runner.Run(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
store, err := logstore.New(database, migration.DialectSQLite)
|
||||
if err != nil {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user