2016-09-05 20:03:58 +00:00
|
|
|
package server
|
2016-08-28 03:59:17 +00:00
|
|
|
|
|
|
|
import (
|
2016-09-01 04:51:38 +00:00
|
|
|
"bytes"
|
2016-08-28 03:59:17 +00:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2016-09-01 04:51:38 +00:00
|
|
|
"io"
|
2016-08-28 03:59:17 +00:00
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
2016-09-08 01:24:11 +00:00
|
|
|
"os"
|
2016-08-28 03:59:17 +00:00
|
|
|
"testing"
|
|
|
|
|
|
|
|
kitlog "github.com/go-kit/kit/log"
|
2016-09-08 01:24:11 +00:00
|
|
|
kithttp "github.com/go-kit/kit/transport/http"
|
|
|
|
"github.com/gorilla/mux"
|
2016-08-28 03:59:17 +00:00
|
|
|
"github.com/kolide/kolide-ose/datastore"
|
|
|
|
"github.com/kolide/kolide-ose/kolide"
|
2016-09-01 04:51:38 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
2016-09-03 17:25:16 +00:00
|
|
|
"golang.org/x/net/context"
|
2016-08-28 03:59:17 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestLogin(t *testing.T) {
|
2016-09-10 01:39:38 +00:00
|
|
|
ds, _ := datastore.New("inmem", "")
|
2016-09-01 04:51:38 +00:00
|
|
|
svc, _ := NewService(testConfig(ds))
|
|
|
|
createTestUsers(t, ds)
|
2016-09-08 01:24:11 +00:00
|
|
|
logger := kitlog.NewLogfmtLogger(os.Stdout)
|
|
|
|
|
|
|
|
opts := []kithttp.ServerOption{
|
|
|
|
kithttp.ServerBefore(
|
|
|
|
setViewerContext(svc, ds, "foobar", logger),
|
|
|
|
),
|
|
|
|
kithttp.ServerErrorLogger(logger),
|
|
|
|
kithttp.ServerAfter(
|
|
|
|
kithttp.SetContentType("application/json; charset=utf-8"),
|
|
|
|
),
|
|
|
|
}
|
|
|
|
r := mux.NewRouter()
|
|
|
|
attachAPIRoutes(r, context.Background(), svc, opts)
|
2016-08-28 03:59:17 +00:00
|
|
|
r.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
fmt.Fprint(w, "index")
|
|
|
|
}))
|
|
|
|
|
|
|
|
server := httptest.NewServer(r)
|
|
|
|
var loginTests = []struct {
|
|
|
|
username string
|
|
|
|
status int
|
|
|
|
password string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
username: "admin1",
|
|
|
|
password: *testUsers["admin1"].Password,
|
|
|
|
status: http.StatusOK,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
username: "nosuchuser",
|
|
|
|
password: "nosuchuser",
|
|
|
|
status: http.StatusUnauthorized,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
username: "admin1",
|
|
|
|
password: "badpassword",
|
|
|
|
status: http.StatusUnauthorized,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tt := range loginTests {
|
|
|
|
p, ok := testUsers[tt.username]
|
|
|
|
if !ok {
|
|
|
|
p = kolide.UserPayload{
|
|
|
|
Username: stringPtr(tt.username),
|
|
|
|
Password: stringPtr("foobar"),
|
|
|
|
Email: stringPtr("admin1@example.com"),
|
|
|
|
Admin: boolPtr(true),
|
|
|
|
}
|
|
|
|
}
|
2016-09-01 04:51:38 +00:00
|
|
|
|
|
|
|
// test sessions
|
|
|
|
testUser, err := ds.User(tt.username)
|
|
|
|
if err != nil && err != datastore.ErrNotFound {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2016-09-08 01:24:11 +00:00
|
|
|
params := loginRequest{
|
2016-09-01 04:51:38 +00:00
|
|
|
Username: tt.username,
|
|
|
|
Password: tt.password,
|
2016-08-28 03:59:17 +00:00
|
|
|
}
|
2016-09-08 01:24:11 +00:00
|
|
|
j, err := json.Marshal(¶ms)
|
2016-09-01 04:51:38 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
requestBody := &nopCloser{bytes.NewBuffer(j)}
|
2016-09-08 01:24:11 +00:00
|
|
|
resp, err := http.Post(server.URL+"/api/v1/kolide/login", "application/json", requestBody)
|
2016-08-28 03:59:17 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if have, want := resp.StatusCode, tt.status; have != want {
|
|
|
|
t.Errorf("have %d, want %d", have, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
var jsn = struct {
|
2016-09-08 01:24:11 +00:00
|
|
|
Token string `json:"token"`
|
2016-08-28 03:59:17 +00:00
|
|
|
ID uint `json:"id"`
|
|
|
|
Username string `json:"username"`
|
|
|
|
Email string `json:"email"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
Admin bool `json:"admin"`
|
|
|
|
Enabled bool `json:"enabled"`
|
|
|
|
NeedsPasswordReset bool `json:"needs_password_reset"`
|
|
|
|
Err string `json:"error,omitempty"`
|
|
|
|
}{}
|
|
|
|
if err := json.NewDecoder(resp.Body).Decode(&jsn); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if tt.status != http.StatusOK {
|
|
|
|
if jsn.Err == "" {
|
|
|
|
t.Errorf("expected json error, got empty result")
|
|
|
|
}
|
|
|
|
continue // skip remaining tests
|
|
|
|
}
|
|
|
|
|
|
|
|
if have, want := jsn.Admin, falseIfNil(p.Admin); have != want {
|
|
|
|
t.Errorf("have %v, want %v", have, want)
|
|
|
|
}
|
|
|
|
|
2016-09-01 04:51:38 +00:00
|
|
|
// ensure that a session was created for our test user and stored
|
|
|
|
sessions, err := ds.FindAllSessionsForUser(testUser.ID)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
assert.Len(t, sessions, 1)
|
|
|
|
|
|
|
|
// ensure the session key is not blank
|
|
|
|
assert.NotEqual(t, "", sessions[0].Key)
|
|
|
|
|
|
|
|
// test logout
|
2016-09-08 02:58:25 +00:00
|
|
|
req, _ := http.NewRequest("POST", server.URL+"/api/v1/kolide/logout", nil)
|
2016-09-08 01:24:11 +00:00
|
|
|
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", jsn.Token))
|
2016-09-01 04:51:38 +00:00
|
|
|
client := &http.Client{}
|
|
|
|
resp, err = client.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if have, want := resp.StatusCode, http.StatusOK; have != want {
|
|
|
|
t.Errorf("have %d, want %d", have, want)
|
|
|
|
}
|
2016-09-08 01:24:11 +00:00
|
|
|
_, err = ioutil.ReadAll(resp.Body)
|
2016-09-01 04:51:38 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
// ensure that our user's session was deleted from the store
|
|
|
|
sessions, err = ds.FindAllSessionsForUser(testUser.ID)
|
|
|
|
assert.Len(t, sessions, 0)
|
2016-08-28 03:59:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-01 04:51:38 +00:00
|
|
|
func createTestUsers(t *testing.T, ds kolide.Datastore) {
|
|
|
|
svc := svcWithNoValidation(testConfig(ds))
|
2016-08-28 03:59:17 +00:00
|
|
|
ctx := context.Background()
|
|
|
|
for _, tt := range testUsers {
|
|
|
|
payload := kolide.UserPayload{
|
2016-09-08 22:57:05 +00:00
|
|
|
Username: tt.Username,
|
|
|
|
Password: tt.Password,
|
|
|
|
Email: tt.Email,
|
|
|
|
Admin: tt.Admin,
|
|
|
|
AdminForcedPasswordReset: tt.AdminForcedPasswordReset,
|
2016-08-28 03:59:17 +00:00
|
|
|
}
|
|
|
|
_, err := svc.NewUser(ctx, payload)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-09-01 04:51:38 +00:00
|
|
|
|
|
|
|
func svcWithNoValidation(config ServiceConfig) kolide.Service {
|
|
|
|
var svc kolide.Service
|
|
|
|
svc = service{
|
|
|
|
ds: config.Datastore,
|
|
|
|
logger: config.Logger,
|
|
|
|
saltKeySize: config.SaltKeySize,
|
|
|
|
bcryptCost: config.BcryptCost,
|
|
|
|
jwtKey: config.JWTKey,
|
|
|
|
cookieName: config.SessionCookieName,
|
|
|
|
}
|
|
|
|
|
|
|
|
return svc
|
|
|
|
}
|
|
|
|
|
|
|
|
func testConfig(ds kolide.Datastore) ServiceConfig {
|
|
|
|
return ServiceConfig{
|
|
|
|
Datastore: ds,
|
|
|
|
Logger: kitlog.NewNopLogger(),
|
|
|
|
BcryptCost: 12,
|
|
|
|
SaltKeySize: 24,
|
|
|
|
SessionCookieName: "KolideSession",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// an io.ReadCloser for new request body
|
|
|
|
type nopCloser struct {
|
|
|
|
io.Reader
|
|
|
|
}
|
|
|
|
|
|
|
|
func (nopCloser) Close() error { return nil }
|