package access import ( "path/filepath" "testing" "time" ) func TestStorePersistsMembersAndTokens(t *testing.T) { path := filepath.Join(t.TempDir(), "project-access.json") store, err := New(path) if err != nil { t.Fatal(err) } member, err := store.AddMember("ssl", "user-1", RoleDeveloper) if err != nil { t.Fatal(err) } if member.Role != RoleDeveloper || !store.HasAccessControl("ssl") { t.Fatalf("项目成员保存错误:%+v", member) } expiresAt := time.Now().UTC().Add(time.Hour) token, secret, err := store.CreateToken("ssl", "客户端令牌", RoleViewer, &expiresAt) if err != nil { t.Fatal(err) } if secret == "" || token.SecretHash != "" || token.Prefix == "" { t.Fatalf("项目令牌返回错误:%+v,明文长度:%d", token, len(secret)) } matched, ok := store.AuthenticateToken("ssl", secret) if !ok || matched.ID != token.ID { t.Fatalf("项目令牌认证失败:%+v,%v", matched, ok) } if _, ok := store.AuthenticateToken("smail", secret); ok { t.Fatal("项目令牌不应访问其他项目") } reloaded, err := New(path) if err != nil { t.Fatal(err) } if len(reloaded.ListMembers("ssl")) != 1 || len(reloaded.ListTokens("ssl")) != 1 { t.Fatal("项目权限数据重新加载失败") } if _, ok := reloaded.AuthenticateToken("ssl", secret); !ok { t.Fatal("重新加载后项目令牌认证失败") } } func TestStoreRevokesTokenAndUpdatesMember(t *testing.T) { store, err := New(filepath.Join(t.TempDir(), "access.json")) if err != nil { t.Fatal(err) } if _, err := store.AddMember("ssl", "user-1", RoleViewer); err != nil { t.Fatal(err) } if _, err := store.UpdateMember("ssl", "user-1", RoleAdmin); err != nil { t.Fatal(err) } member, ok := store.GetMember("ssl", "user-1") if !ok || member.Role != RoleAdmin { t.Fatalf("项目成员更新错误:%+v,%v", member, ok) } _, secret, err := store.CreateToken("ssl", "令牌", RoleDeveloper, nil) if err != nil { t.Fatal(err) } tokens := store.ListTokens("ssl") if len(tokens) != 1 { t.Fatal("项目令牌列表数量错误") } if _, err := store.RevokeToken("ssl", tokens[0].ID); err != nil { t.Fatal(err) } if _, ok := store.AuthenticateToken("ssl", secret); ok { t.Fatal("已吊销项目令牌仍可认证") } } func TestStoreKeepsLastProjectAdmin(t *testing.T) { store, err := New(filepath.Join(t.TempDir(), "access.json")) if err != nil { t.Fatal(err) } if _, err := store.AddMember("ssl", "admin-1", RoleAdmin); err != nil { t.Fatal(err) } if _, err := store.UpdateMember("ssl", "admin-1", RoleViewer); err == nil { t.Fatal("demoting the last project admin should fail") } if err := store.DeleteMember("ssl", "admin-1"); err == nil { t.Fatal("deleting the last project admin should fail") } if _, err := store.AddMember("ssl", "admin-2", RoleAdmin); err != nil { t.Fatal(err) } if err := store.DeleteMember("ssl", "admin-2"); err != nil { t.Fatal(err) } if _, err := store.UpdateMember("ssl", "admin-1", RoleViewer); err == nil { t.Fatal("demoting the last project admin should fail") } }