All checks were successful
Build & push Docker image / build (push) Successful in 2m22s
771 lines
23 KiB
Go
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 <b>now</b>") {
|
|
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])
|
|
}
|
|
}
|