111 lines
4.2 KiB
Go
111 lines
4.2 KiB
Go
package server_test
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
)
|
|
|
|
func internalAuth(srv http.Handler, secret string, body map[string]string) *httptest.ResponseRecorder {
|
|
b, _ := json.Marshal(body)
|
|
req := httptest.NewRequest(http.MethodPost, "/internal/auth", bytes.NewReader(b))
|
|
if secret != "" {
|
|
req.Header.Set("Authorization", "Bearer "+secret)
|
|
}
|
|
rr := httptest.NewRecorder()
|
|
srv.ServeHTTP(rr, req)
|
|
return rr
|
|
}
|
|
|
|
func TestInternalAuthService(t *testing.T) {
|
|
const secret = "test-secret"
|
|
srv, d := newSecretServer(t, secret)
|
|
|
|
id := makeApprovedUser(t, d, "Xmppfan", "hunter2hunter2")
|
|
if err := d.SetUserServiceEnabled(id, "prosody", true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
makeApprovedUser(t, d, "noxmpp", "hunter2hunter2")
|
|
|
|
cases := []struct {
|
|
name string
|
|
secret string
|
|
body map[string]string
|
|
want int
|
|
}{
|
|
{"enabled, folded name", secret, map[string]string{"username": "xmppfan", "password": "hunter2hunter2", "service": "prosody"}, http.StatusOK},
|
|
{"service not enabled", secret, map[string]string{"username": "noxmpp", "password": "hunter2hunter2", "service": "prosody"}, http.StatusForbidden},
|
|
{"other service", secret, map[string]string{"username": "Xmppfan", "password": "hunter2hunter2", "service": "ergo"}, http.StatusForbidden},
|
|
{"wrong password", secret, map[string]string{"username": "Xmppfan", "password": "nope", "service": "prosody"}, http.StatusUnauthorized},
|
|
{"no service keeps old behaviour", secret, map[string]string{"username": "noxmpp", "password": "hunter2hunter2"}, http.StatusOK},
|
|
{"no service is case-sensitive", secret, map[string]string{"username": "xmppfan", "password": "hunter2hunter2"}, http.StatusUnauthorized},
|
|
{"bad bearer", "wrong", map[string]string{"username": "Xmppfan", "password": "hunter2hunter2", "service": "prosody"}, http.StatusUnauthorized},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if rr := internalAuth(srv, tc.secret, tc.body); rr.Code != tc.want {
|
|
t.Errorf("status: got %d, want %d", rr.Code, tc.want)
|
|
}
|
|
})
|
|
}
|
|
|
|
rr := internalAuth(srv, secret, map[string]string{"username": "xmppfan", "password": "hunter2hunter2", "service": "prosody"})
|
|
var resp struct{ Username string }
|
|
if err := json.NewDecoder(rr.Body).Decode(&resp); err != nil || resp.Username != "Xmppfan" {
|
|
t.Errorf("canonical username: got %q (%v)", resp.Username, err)
|
|
}
|
|
}
|
|
|
|
func TestInternalAuthServiceLimiter(t *testing.T) {
|
|
const secret = "test-secret"
|
|
srv, d := newSecretServer(t, secret)
|
|
id := makeApprovedUser(t, d, "target", "hunter2hunter2")
|
|
d.SetUserServiceEnabled(id, "ergo", true)
|
|
|
|
for i := 0; i < 10; i++ {
|
|
internalAuth(srv, secret, map[string]string{"username": "TARGET", "password": "guess", "service": "ergo"})
|
|
}
|
|
rr := internalAuth(srv, secret, map[string]string{"username": "target", "password": "hunter2hunter2", "service": "ergo"})
|
|
if rr.Code != http.StatusTooManyRequests {
|
|
t.Errorf("after 10 failures: got %d, want 429", rr.Code)
|
|
}
|
|
rr = internalAuth(srv, secret, map[string]string{"username": "target", "password": "hunter2hunter2", "service": "prosody"})
|
|
if rr.Code == http.StatusTooManyRequests {
|
|
t.Error("limiter should be per service")
|
|
}
|
|
}
|
|
|
|
func TestInternalUsers(t *testing.T) {
|
|
const secret = "test-secret"
|
|
srv, d := newSecretServer(t, secret)
|
|
id := makeApprovedUser(t, d, "alice", "hunter2hunter2")
|
|
d.SetUserServiceEnabled(id, "prosody", true)
|
|
makeApprovedUser(t, d, "bob", "hunter2hunter2")
|
|
|
|
get := func(secret, q string) *httptest.ResponseRecorder {
|
|
req := httptest.NewRequest(http.MethodGet, "/internal/users"+q, nil)
|
|
if secret != "" {
|
|
req.Header.Set("Authorization", "Bearer "+secret)
|
|
}
|
|
rr := httptest.NewRecorder()
|
|
srv.ServeHTTP(rr, req)
|
|
return rr
|
|
}
|
|
|
|
rr := get(secret, "?service=prosody")
|
|
var resp struct{ Users []string }
|
|
if rr.Code != http.StatusOK || json.NewDecoder(rr.Body).Decode(&resp) != nil {
|
|
t.Fatalf("status %d", rr.Code)
|
|
}
|
|
if len(resp.Users) != 1 || resp.Users[0] != "alice" {
|
|
t.Errorf("want [alice], got %v", resp.Users)
|
|
}
|
|
if rr := get("", "?service=prosody"); rr.Code != http.StatusUnauthorized {
|
|
t.Errorf("no bearer: got %d", rr.Code)
|
|
}
|
|
if rr := get(secret, ""); rr.Code != http.StatusBadRequest {
|
|
t.Errorf("no service: got %d", rr.Code)
|
|
}
|
|
}
|