misaki/server/server_test.go
q 4512570a49
All checks were successful
Build & push Docker image / build (push) Successful in 2m22s
new features and style changes
2026-11-07 12:03:59 -05:00

771 lines
23 KiB
Go

package server_test
import (
"fmt"
"log"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"strings"
"testing"
"misaki/db"
"misaki/server"
"golang.org/x/crypto/bcrypt"
)
func TestMain(m *testing.M) {
if err := os.Chdir(".."); err != nil {
log.Fatalf("chdir to project root: %v", err)
}
os.Exit(m.Run())
}
func newTestDB(t *testing.T) *db.DB {
t.Helper()
d, err := db.Open(filepath.Join(t.TempDir(), "test.db"))
if err != nil {
t.Fatalf("db.Open: %v", err)
}
t.Cleanup(func() { d.Close() })
return d
}
func newTestServer(t *testing.T) (http.Handler, *db.DB) {
t.Helper()
d := newTestDB(t)
srv := server.New(d, server.ServiceURLs{}, nil, server.SMTPConfig{}, server.Config{})
return srv, d
}
func makeApprovedUser(t *testing.T, d *db.DB, username, password string) int64 {
t.Helper()
h, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.MinCost)
if err != nil {
t.Fatalf("bcrypt: %v", err)
}
id, err := d.CreateUser(username, string(h), "test bio")
if err != nil {
t.Fatalf("CreateUser(%q): %v", username, err)
}
if err := d.SetUserStatus(id, "approved"); err != nil {
t.Fatalf("SetUserStatus: %v", err)
}
return id
}
func makeAdminUser(t *testing.T, d *db.DB, username, password string) int64 {
t.Helper()
id := makeApprovedUser(t, d, username, password)
if err := d.SetUserAdmin(id, true); err != nil {
t.Fatalf("SetUserAdmin: %v", err)
}
return id
}
func doLogin(t *testing.T, srv http.Handler, username, password string) *http.Cookie {
t.Helper()
rr := doPost(srv, "/login", url.Values{
"username": {username},
"password": {password},
}, nil)
for _, c := range rr.Result().Cookies() {
if c.Name == "session" {
return c
}
}
t.Fatalf("login failed: status %d, location %q", rr.Code, rr.Header().Get("Location"))
return nil
}
func doGet(srv http.Handler, path string, cookie *http.Cookie) *httptest.ResponseRecorder {
req := httptest.NewRequest(http.MethodGet, path, nil)
if cookie != nil {
req.AddCookie(cookie)
}
rr := httptest.NewRecorder()
srv.ServeHTTP(rr, req)
return rr
}
func doPost(srv http.Handler, path string, form url.Values, cookie *http.Cookie) *httptest.ResponseRecorder {
req := httptest.NewRequest(http.MethodPost, path, strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
if cookie != nil {
req.AddCookie(cookie)
}
rr := httptest.NewRecorder()
srv.ServeHTTP(rr, req)
return rr
}
func assertStatus(t *testing.T, rr *httptest.ResponseRecorder, want int) {
t.Helper()
if rr.Code != want {
t.Errorf("status: got %d, want %d", rr.Code, want)
}
}
func assertRedirectTo(t *testing.T, rr *httptest.ResponseRecorder, wantPrefix string) {
t.Helper()
assertStatus(t, rr, http.StatusSeeOther)
if loc := rr.Header().Get("Location"); !strings.HasPrefix(loc, wantPrefix) {
t.Errorf("Location: got %q, want prefix %q", loc, wantPrefix)
}
}
func TestPublicPages(t *testing.T) {
srv, _ := newTestServer(t)
for _, path := range []string{"/", "/apply", "/applied", "/login"} {
t.Run(path, func(t *testing.T) {
assertStatus(t, doGet(srv, path, nil), http.StatusOK)
})
}
}
func TestNotFound(t *testing.T) {
srv, _ := newTestServer(t)
assertStatus(t, doGet(srv, "/no-such-page", nil), http.StatusNotFound)
}
func TestApply_Validation(t *testing.T) {
srv, _ := newTestServer(t)
cases := []struct {
name string
form url.Values
}{
{
"missing password",
url.Values{"username": {"alice"}, "email": {"a@b.com"}, "agree": {"on"}},
},
{
"password mismatch",
url.Values{"username": {"alice"}, "password": {"abc"}, "password_confirm": {"xyz"}, "email": {"a@b.com"}, "agree": {"on"}},
},
{
"username too long",
url.Values{"username": {strings.Repeat("a", 22)}, "password": {"p"}, "password_confirm": {"p"}, "email": {"a@b.com"}, "agree": {"on"}},
},
{
"no agreement",
url.Values{"username": {"alice"}, "password": {"p"}, "password_confirm": {"p"}, "email": {"a@b.com"}},
},
{
"invalid email",
url.Values{"username": {"alice"}, "password": {"p"}, "password_confirm": {"p"}, "email": {"notanemail"}, "agree": {"on"}},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
assertRedirectTo(t, doPost(srv, "/apply", tc.form, nil), "/apply?err=")
})
}
}
func TestApply_DuplicateUsername(t *testing.T) {
srv, d := newTestServer(t)
makeApprovedUser(t, d, "alice", "password")
rr := doPost(srv, "/apply", url.Values{
"username": {"alice"}, "password": {"p"}, "password_confirm": {"p"},
"email": {"x@x.com"}, "agree": {"on"},
}, nil)
assertRedirectTo(t, rr, "/apply?err=")
}
func TestApply_Success(t *testing.T) {
srv, d := newTestServer(t)
rr := doPost(srv, "/apply", url.Values{
"username": {"newuser"},
"password": {"securepass"},
"password_confirm": {"securepass"},
"email": {"new@example.com"},
"bio": {"hello world"},
"agree": {"on"},
}, nil)
assertRedirectTo(t, rr, "/applied")
u, err := d.GetUserByUsername("newuser")
if err != nil {
t.Fatalf("user not in db after apply: %v", err)
}
if u.Status != "pending" {
t.Errorf("expected pending, got %q", u.Status)
}
if u.Note != "I love misaki!" {
t.Errorf("default note not set: got %q", u.Note)
}
}
func TestLogin_Success(t *testing.T) {
srv, d := newTestServer(t)
makeApprovedUser(t, d, "alice", "password")
rr := doPost(srv, "/login", url.Values{"username": {"alice"}, "password": {"password"}}, nil)
assertRedirectTo(t, rr, "/dashboard")
hasCookie := false
for _, c := range rr.Result().Cookies() {
if c.Name == "session" {
hasCookie = true
}
}
if !hasCookie {
t.Error("no session cookie set after successful login")
}
}
func TestLogin_WrongPassword(t *testing.T) {
srv, d := newTestServer(t)
makeApprovedUser(t, d, "alice", "password")
assertRedirectTo(t,
doPost(srv, "/login", url.Values{"username": {"alice"}, "password": {"wrong"}}, nil),
"/login?err=")
}
func TestLogin_PendingUser(t *testing.T) {
srv, d := newTestServer(t)
h, _ := bcrypt.GenerateFromPassword([]byte("pass"), bcrypt.MinCost)
d.CreateUser("pending", string(h), "")
assertRedirectTo(t,
doPost(srv, "/login", url.Values{"username": {"pending"}, "password": {"pass"}}, nil),
"/login?err=")
}
func TestLogin_RejectedUser(t *testing.T) {
srv, d := newTestServer(t)
id := makeApprovedUser(t, d, "alice", "password")
d.SetUserStatus(id, "rejected")
assertRedirectTo(t,
doPost(srv, "/login", url.Values{"username": {"alice"}, "password": {"password"}}, nil),
"/login?err=")
}
func TestLogout(t *testing.T) {
srv, d := newTestServer(t)
makeApprovedUser(t, d, "alice", "password")
cookie := doLogin(t, srv, "alice", "password")
rr := doPost(srv, "/logout", url.Values{}, cookie)
assertRedirectTo(t, rr, "/login")
cleared := false
for _, c := range rr.Result().Cookies() {
if c.Name == "session" && c.MaxAge < 0 {
cleared = true
}
}
if !cleared {
t.Error("session cookie not cleared after logout")
}
}
func TestDashboard_RequiresAuth(t *testing.T) {
srv, _ := newTestServer(t)
assertRedirectTo(t, doGet(srv, "/dashboard", nil), "/login")
}
func TestDashboard_Authenticated(t *testing.T) {
srv, d := newTestServer(t)
makeApprovedUser(t, d, "alice", "password")
cookie := doLogin(t, srv, "alice", "password")
assertStatus(t, doGet(srv, "/dashboard", cookie), http.StatusOK)
}
func TestHelp_RequiresAuth(t *testing.T) {
srv, _ := newTestServer(t)
assertRedirectTo(t, doGet(srv, "/help", nil), "/login")
}
func TestHelp_Authenticated(t *testing.T) {
srv, d := newTestServer(t)
makeApprovedUser(t, d, "alice", "password")
cookie := doLogin(t, srv, "alice", "password")
assertStatus(t, doGet(srv, "/help", cookie), http.StatusOK)
}
func TestUpdateNote(t *testing.T) {
srv, d := newTestServer(t)
id := makeApprovedUser(t, d, "alice", "password")
cookie := doLogin(t, srv, "alice", "password")
assertRedirectTo(t,
doPost(srv, "/dashboard/note", url.Values{"note": {"my new biography"}}, cookie),
"/dashboard")
u, _ := d.GetUserByID(id)
if u.Note != "my new biography" {
t.Errorf("note not persisted: got %q", u.Note)
}
}
func TestUpdateNote_TooLong(t *testing.T) {
srv, d := newTestServer(t)
makeApprovedUser(t, d, "alice", "password")
cookie := doLogin(t, srv, "alice", "password")
assertRedirectTo(t,
doPost(srv, "/dashboard/note", url.Values{"note": {strings.Repeat("x", 2001)}}, cookie),
"/dashboard?err=")
}
func TestUpdateNote_RequiresAuth(t *testing.T) {
srv, _ := newTestServer(t)
assertRedirectTo(t, doPost(srv, "/dashboard/note", url.Values{"note": {"hi"}}, nil), "/login")
}
func TestApplyService_Form(t *testing.T) {
srv, d := newTestServer(t)
d.SetServiceEnabled("mail", true)
makeApprovedUser(t, d, "alice", "password")
cookie := doLogin(t, srv, "alice", "password")
assertRedirectTo(t, doPost(srv, "/dashboard/apply", url.Values{
"service": {"mail"}, "handle": {"alice"}, "password": {"mailpass"}, "reason": {"need email"},
}, cookie), "/dashboard")
}
func TestApplyService_Request(t *testing.T) {
srv, d := newTestServer(t)
d.SetServiceEnabled("cerca", true)
makeApprovedUser(t, d, "alice", "password")
cookie := doLogin(t, srv, "alice", "password")
assertRedirectTo(t, doPost(srv, "/dashboard/apply", url.Values{
"service": {"cerca"},
}, cookie), "/dashboard")
}
func TestApplyService_AlreadyApplied(t *testing.T) {
srv, d := newTestServer(t)
d.SetServiceEnabled("mail", true)
id := makeApprovedUser(t, d, "alice", "password")
d.ApplyForService(id, "mail")
cookie := doLogin(t, srv, "alice", "password")
assertRedirectTo(t, doPost(srv, "/dashboard/apply", url.Values{
"service": {"mail"}, "handle": {"alice"}, "password": {"p"},
}, cookie), "/dashboard?err=")
}
func TestApplyService_InvalidService(t *testing.T) {
srv, d := newTestServer(t)
makeApprovedUser(t, d, "alice", "password")
cookie := doLogin(t, srv, "alice", "password")
assertRedirectTo(t, doPost(srv, "/dashboard/apply", url.Values{
"service": {"notreal"},
}, cookie), "/dashboard?err=")
}
func TestApplyService_LinkTypeRejected(t *testing.T) {
srv, d := newTestServer(t)
d.SetServiceEnabled("gancio", true)
makeApprovedUser(t, d, "alice", "password")
cookie := doLogin(t, srv, "alice", "password")
assertRedirectTo(t, doPost(srv, "/dashboard/apply", url.Values{
"service": {"gancio"},
}, cookie), "/dashboard?err=")
}
func TestApplyService_RequiresAuth(t *testing.T) {
srv, _ := newTestServer(t)
assertRedirectTo(t, doPost(srv, "/dashboard/apply", url.Values{"service": {"mail"}}, nil), "/login")
}
func TestRequestReset(t *testing.T) {
srv, d := newTestServer(t)
d.SetServiceEnabled("mail", true)
id := makeApprovedUser(t, d, "alice", "password")
d.ApplyForService(id, "mail")
d.SetUserServiceEnabled(id, "mail", true)
cookie := doLogin(t, srv, "alice", "password")
assertRedirectTo(t, doPost(srv, "/dashboard/request-reset", url.Values{
"service": {"mail"},
}, cookie), "/dashboard")
}
func TestRequestReset_RequiresAuth(t *testing.T) {
srv, _ := newTestServer(t)
assertRedirectTo(t, doPost(srv, "/dashboard/request-reset", url.Values{"service": {"mail"}}, nil), "/login")
}
func TestProsodyApplyService_Request(t *testing.T) {
srv, d := newTestServer(t)
d.SetServiceEnabled("prosody", true)
makeApprovedUser(t, d, "alice", "password")
cookie := doLogin(t, srv, "alice", "password")
assertRedirectTo(t, doPost(srv, "/dashboard/apply", url.Values{
"service": {"prosody"},
}, cookie), "/dashboard")
}
func TestAdminSetlink_Prosody(t *testing.T) {
srv, d := newTestServer(t)
makeAdminUser(t, d, "admin", "adminpass")
adminCookie := doLogin(t, srv, "admin", "adminpass")
d.SetServiceEnabled("prosody", true)
uid := makeApprovedUser(t, d, "alice", "pass")
d.ApplyForService(uid, "prosody")
link := "https://xmpp.example.com/register/xyz"
assertRedirectTo(t, doPost(srv, "/admin/user-service", url.Values{
"user_id": {fmt.Sprintf("%d", uid)}, "service": {"prosody"},
"action": {"setlink"}, "data": {link},
}, adminCookie), "/admin")
svcs, _ := d.ListUserServices(uid)
if len(svcs) != 1 || svcs[0].Data != link {
t.Errorf("prosody link not set: %+v", svcs)
}
}
func TestErgoApplyService_Request(t *testing.T) {
srv, d := newTestServer(t)
d.SetServiceEnabled("ergo", true)
makeApprovedUser(t, d, "alice", "password")
cookie := doLogin(t, srv, "alice", "password")
assertRedirectTo(t, doPost(srv, "/dashboard/apply", url.Values{
"service": {"ergo"},
}, cookie), "/dashboard")
}
func TestErgoApplyService_GrantedNoSetlink(t *testing.T) {
srv, d := newTestServer(t)
makeAdminUser(t, d, "admin", "adminpass")
adminCookie := doLogin(t, srv, "admin", "adminpass")
d.SetServiceEnabled("ergo", true)
uid := makeApprovedUser(t, d, "alice", "pass")
d.ApplyForService(uid, "ergo")
// Grant ergo — no data/link needed
assertRedirectTo(t, doPost(srv, "/admin/user-service", url.Values{
"user_id": {fmt.Sprintf("%d", uid)}, "service": {"ergo"},
"action": {"grant"},
}, adminCookie), "/admin")
svcs, _ := d.ListUserServices(uid)
if len(svcs) != 1 || !svcs[0].Granted {
t.Errorf("ergo not granted: %+v", svcs)
}
}
func TestCercaRequestReset(t *testing.T) {
srv, d := newTestServer(t)
d.SetServiceEnabled("cerca", true)
id := makeApprovedUser(t, d, "alice", "password")
d.ApplyForService(id, "cerca")
d.SetUserServiceEnabled(id, "cerca", true)
cookie := doLogin(t, srv, "alice", "password")
assertRedirectTo(t, doPost(srv, "/dashboard/request-reset", url.Values{
"service": {"cerca"},
}, cookie), "/dashboard")
}
func TestRequestNameChange(t *testing.T) {
srv, d := newTestServer(t)
d.SetServiceEnabled("cerca", true)
id := makeApprovedUser(t, d, "alice", "password")
d.ApplyForService(id, "cerca")
d.SetUserServiceEnabled(id, "cerca", true)
cookie := doLogin(t, srv, "alice", "password")
assertRedirectTo(t, doPost(srv, "/dashboard/request-namechange", url.Values{
"service": {"cerca"}, "new_name": {"alice_new"},
}, cookie), "/dashboard")
}
func TestRequestNameChange_EmptyName(t *testing.T) {
srv, d := newTestServer(t)
makeApprovedUser(t, d, "alice", "password")
cookie := doLogin(t, srv, "alice", "password")
assertRedirectTo(t, doPost(srv, "/dashboard/request-namechange", url.Values{
"service": {"cerca"}, "new_name": {""},
}, cookie), "/dashboard?err=")
}
func TestRequestNameChange_InvalidService(t *testing.T) {
srv, d := newTestServer(t)
makeApprovedUser(t, d, "alice", "password")
cookie := doLogin(t, srv, "alice", "password")
assertRedirectTo(t, doPost(srv, "/dashboard/request-namechange", url.Values{
"service": {"notreal"}, "new_name": {"alice_new"},
}, cookie), "/dashboard?err=")
}
func TestRequestNameChange_RequiresAuth(t *testing.T) {
srv, _ := newTestServer(t)
assertRedirectTo(t, doPost(srv, "/dashboard/request-namechange", url.Values{
"service": {"cerca"}, "new_name": {"alice_new"},
}, nil), "/login")
}
func TestAdmin_RequiresAuth(t *testing.T) {
srv, _ := newTestServer(t)
assertRedirectTo(t, doGet(srv, "/admin", nil), "/login")
}
func TestAdmin_RegularUserBlocked(t *testing.T) {
srv, d := newTestServer(t)
makeApprovedUser(t, d, "alice", "password")
cookie := doLogin(t, srv, "alice", "password")
assertRedirectTo(t, doGet(srv, "/admin", cookie), "/login")
}
func TestAdmin_AdminAccess(t *testing.T) {
srv, d := newTestServer(t)
makeAdminUser(t, d, "admin", "adminpass")
cookie := doLogin(t, srv, "admin", "adminpass")
assertStatus(t, doGet(srv, "/admin", cookie), http.StatusOK)
}
func TestAdminAction_RequiresAdmin(t *testing.T) {
srv, d := newTestServer(t)
makeApprovedUser(t, d, "alice", "password")
cookie := doLogin(t, srv, "alice", "password")
assertRedirectTo(t, doPost(srv, "/admin/action", url.Values{
"id": {"1"}, "action": {"approve"},
}, cookie), "/login")
}
func TestAdminApproveUser(t *testing.T) {
srv, d := newTestServer(t)
makeAdminUser(t, d, "admin", "adminpass")
adminCookie := doLogin(t, srv, "admin", "adminpass")
h, _ := bcrypt.GenerateFromPassword([]byte("p"), bcrypt.MinCost)
uid, _ := d.CreateUser("pending", string(h), "")
assertRedirectTo(t, doPost(srv, "/admin/action", url.Values{
"id": {fmt.Sprintf("%d", uid)}, "action": {"approve"},
}, adminCookie), "/admin")
u, _ := d.GetUserByID(uid)
if u.Status != "approved" {
t.Errorf("expected approved, got %q", u.Status)
}
}
func TestAdminRejectUser(t *testing.T) {
srv, d := newTestServer(t)
makeAdminUser(t, d, "admin", "adminpass")
adminCookie := doLogin(t, srv, "admin", "adminpass")
uid := makeApprovedUser(t, d, "alice", "pass")
assertRedirectTo(t, doPost(srv, "/admin/action", url.Values{
"id": {fmt.Sprintf("%d", uid)}, "action": {"reject"},
}, adminCookie), "/admin")
u, _ := d.GetUserByID(uid)
if u.Status != "rejected" {
t.Errorf("expected rejected, got %q", u.Status)
}
}
func TestAdminReinstateUser(t *testing.T) {
srv, d := newTestServer(t)
makeAdminUser(t, d, "admin", "adminpass")
adminCookie := doLogin(t, srv, "admin", "adminpass")
uid := makeApprovedUser(t, d, "alice", "pass")
d.SetUserStatus(uid, "rejected")
assertRedirectTo(t, doPost(srv, "/admin/action", url.Values{
"id": {fmt.Sprintf("%d", uid)}, "action": {"reinstate"},
}, adminCookie), "/admin")
u, _ := d.GetUserByID(uid)
if u.Status != "approved" {
t.Errorf("expected approved after reinstate, got %q", u.Status)
}
}
func TestAdminAction_InvalidAction(t *testing.T) {
srv, d := newTestServer(t)
makeAdminUser(t, d, "admin", "adminpass")
adminCookie := doLogin(t, srv, "admin", "adminpass")
rr := doPost(srv, "/admin/action", url.Values{
"id": {"1"}, "action": {"delete"},
}, adminCookie)
assertStatus(t, rr, http.StatusBadRequest)
}
func TestAdminToggleService(t *testing.T) {
srv, d := newTestServer(t)
makeAdminUser(t, d, "admin", "adminpass")
adminCookie := doLogin(t, srv, "admin", "adminpass")
assertRedirectTo(t, doPost(srv, "/admin/service", url.Values{
"service": {"mail"}, "enabled": {"1"},
}, adminCookie), "/admin")
svcs, _ := d.ListServices()
for _, s := range svcs {
if s.Name == "mail" && !s.Enabled {
t.Error("expected mail to be enabled")
}
}
doPost(srv, "/admin/service", url.Values{"service": {"mail"}, "enabled": {"0"}}, adminCookie)
svcs, _ = d.ListServices()
for _, s := range svcs {
if s.Name == "mail" && s.Enabled {
t.Error("expected mail to be disabled after toggle")
}
}
}
func TestAdminUserService_Grant(t *testing.T) {
srv, d := newTestServer(t)
makeAdminUser(t, d, "admin", "adminpass")
adminCookie := doLogin(t, srv, "admin", "adminpass")
d.SetServiceEnabled("mail", true)
uid := makeApprovedUser(t, d, "alice", "pass")
d.ApplyForService(uid, "mail")
assertRedirectTo(t, doPost(srv, "/admin/user-service", url.Values{
"user_id": {fmt.Sprintf("%d", uid)}, "service": {"mail"}, "action": {"grant"},
}, adminCookie), "/admin")
svcs, _ := d.ListUserServices(uid)
if len(svcs) != 1 || !svcs[0].Granted {
t.Errorf("expected granted: %+v", svcs)
}
}
func TestAdminUserService_Revoke(t *testing.T) {
srv, d := newTestServer(t)
makeAdminUser(t, d, "admin", "adminpass")
adminCookie := doLogin(t, srv, "admin", "adminpass")
d.SetServiceEnabled("mail", true)
uid := makeApprovedUser(t, d, "alice", "pass")
d.ApplyForService(uid, "mail")
d.SetUserServiceEnabled(uid, "mail", true)
assertRedirectTo(t, doPost(srv, "/admin/user-service", url.Values{
"user_id": {fmt.Sprintf("%d", uid)}, "service": {"mail"}, "action": {"revoke"},
}, adminCookie), "/admin")
svcs, _ := d.ListUserServices(uid)
if len(svcs) != 1 || svcs[0].Granted {
t.Errorf("expected revoked: %+v", svcs)
}
}
func TestAdminUserService_Setlink(t *testing.T) {
srv, d := newTestServer(t)
makeAdminUser(t, d, "admin", "adminpass")
adminCookie := doLogin(t, srv, "admin", "adminpass")
d.SetServiceEnabled("cerca", true)
uid := makeApprovedUser(t, d, "alice", "pass")
d.ApplyForService(uid, "cerca")
link := "https://forum.example.com/register/xyz"
assertRedirectTo(t, doPost(srv, "/admin/user-service", url.Values{
"user_id": {fmt.Sprintf("%d", uid)}, "service": {"cerca"},
"action": {"setlink"}, "data": {link},
}, adminCookie), "/admin")
svcs, _ := d.ListUserServices(uid)
if len(svcs) != 1 || svcs[0].Data != link {
t.Errorf("link not set: %+v", svcs)
}
}
func TestCookieService_AppearsInDashboard(t *testing.T) {
d := newTestDB(t)
srv := server.New(d, server.ServiceURLs{}, nil, server.SMTPConfig{}, server.Config{
CookieURLs: map[string]string{"notes": "https://notes.example.com"},
})
d.SetServiceEnabled("notes", true)
makeApprovedUser(t, d, "alice", "password")
cookie := doLogin(t, srv, "alice", "password")
rr := doGet(srv, "/dashboard", cookie)
assertStatus(t, rr, http.StatusOK)
body := rr.Body.String()
if !strings.Contains(body, "HedgeDoc") {
t.Error("expected notes service label to appear on dashboard")
}
if !strings.Contains(body, "https://notes.example.com") {
t.Error("expected notes URL to appear on dashboard")
}
}
func TestApplyService_CookieTypeBlocked(t *testing.T) {
d := newTestDB(t)
srv := server.New(d, server.ServiceURLs{}, nil, server.SMTPConfig{}, server.Config{})
d.SetServiceEnabled("notes", true)
makeApprovedUser(t, d, "alice", "password")
cookie := doLogin(t, srv, "alice", "password")
assertRedirectTo(t, doPost(srv, "/dashboard/apply", url.Values{
"service": {"notes"},
}, cookie), "/dashboard?err=")
}
func TestAdminUserService_InvalidAction(t *testing.T) {
srv, d := newTestServer(t)
makeAdminUser(t, d, "admin", "adminpass")
adminCookie := doLogin(t, srv, "admin", "adminpass")
rr := doPost(srv, "/admin/user-service", url.Values{
"user_id": {"1"}, "service": {"mail"}, "action": {"noop"},
}, adminCookie)
assertStatus(t, rr, http.StatusBadRequest)
}
func TestAdminUserService_Deny(t *testing.T) {
srv, d := newTestServer(t)
makeAdminUser(t, d, "admin", "adminpass")
adminCookie := doLogin(t, srv, "admin", "adminpass")
d.SetServiceEnabled("ergo", true)
uid := makeApprovedUser(t, d, "alice", "pass")
d.ApplyForService(uid, "ergo")
deny := func(reason string) *httptest.ResponseRecorder {
return doPost(srv, "/admin/user-service", url.Values{
"user_id": {fmt.Sprintf("%d", uid)}, "service": {"ergo"}, "action": {"deny"}, "reason": {reason},
}, adminCookie)
}
assertStatus(t, deny(strings.Repeat("x", 281)), 400)
assertRedirectTo(t, deny(" not\n<b>now</b> "), "/admin")
svcs, _ := d.ListUserServices(uid)
if len(svcs) != 1 || !svcs[0].Denied || svcs[0].Applied || svcs[0].DenyReason != "not <b>now</b>" {
t.Fatalf("not denied: %+v", svcs[0])
}
if body := doGet(srv, "/dashboard", doLogin(t, srv, "alice", "pass")).Body.String(); !strings.Contains(body, "Request denied: not &lt;b&gt;now&lt;/b&gt;") {
t.Error("denial reason not shown escaped on dashboard")
}
if body := doGet(srv, "/admin", adminCookie).Body.String(); !strings.Contains(body, "Service requests (0)") {
t.Error("denied request still queued")
}
if err := d.ApplyForService(uid, "ergo"); err != nil {
t.Fatal(err)
}
svcs, _ = d.ListUserServices(uid)
if !svcs[0].Applied || svcs[0].Denied || svcs[0].DenyReason != "" {
t.Errorf("re-request should clear denial: %+v", svcs[0])
}
d.SetUserServiceEnabled(uid, "ergo", true)
assertRedirectTo(t, deny("late"), "/admin")
svcs, _ = d.ListUserServices(uid)
if !svcs[0].Granted || svcs[0].Denied {
t.Errorf("deny must not touch a granted service: %+v", svcs[0])
}
}