update tests
This commit is contained in:
parent
72ff2fbc12
commit
60a5aa3800
10 changed files with 898 additions and 96 deletions
|
@ -1,7 +1,6 @@
|
|||
package cwapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
@ -169,20 +168,13 @@ func (ctx *ApiCtx) Signin() error {
|
|||
func (ctx *ApiCtx) RegisterMachine(machineID string, password string) error {
|
||||
ctx.Creds.User = machineID
|
||||
ctx.Creds.Password = password
|
||||
jsonResp := &ApiResp{}
|
||||
|
||||
req, err := ctx.Http.New().Post(ctx.RegisterPath).BodyJSON(ctx.Creds).Request()
|
||||
resp, err := ctx.Http.Post(ctx.RegisterPath).BodyJSON(ctx.Creds).ReceiveSuccess(jsonResp)
|
||||
if err != nil {
|
||||
return fmt.Errorf("api register machine: HTTP request creation failed: %s", err)
|
||||
}
|
||||
log.Debugf("api register: URL: '%s'", req.URL)
|
||||
|
||||
httpClient := http.Client{Timeout: 20 * time.Second}
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("api register machine: API call failed : %s", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("api register machine: unable to read API response body: %s", err.Error())
|
||||
|
@ -192,10 +184,8 @@ func (ctx *ApiCtx) RegisterMachine(machineID string, password string) error {
|
|||
return fmt.Errorf("api register machine: return bad HTTP code (%d): %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
jsonResp := ApiResp{}
|
||||
err = json.Unmarshal(body, &jsonResp)
|
||||
if err != nil {
|
||||
return fmt.Errorf("api register machine: unable to unmarshall api response '%s': %s", string(body), err.Error())
|
||||
if jsonResp.Message == "" || jsonResp.Message != "OK" || jsonResp.StatusCode != 200 {
|
||||
return fmt.Errorf("api signin failed. http response: %s", body)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -203,19 +193,14 @@ func (ctx *ApiCtx) RegisterMachine(machineID string, password string) error {
|
|||
func (ctx *ApiCtx) ResetPassword(machineID string, password string) error {
|
||||
ctx.Creds.User = machineID
|
||||
ctx.Creds.Password = password
|
||||
jsonResp := &ApiResp{}
|
||||
|
||||
data := map[string]string{"machine_id": ctx.Creds.User, "password": ctx.Creds.Password}
|
||||
req, err := ctx.Http.New().Post(ctx.ResetPwdPath).BodyJSON(data).Request()
|
||||
resp, err := ctx.Http.Post(ctx.ResetPwdPath).BodyJSON(data).ReceiveSuccess(jsonResp)
|
||||
if err != nil {
|
||||
return fmt.Errorf("api reset password: HTTP request creation failed: %s", err)
|
||||
}
|
||||
log.Debugf("api reset: URL: '%s'", req.URL)
|
||||
|
||||
httpClient := http.Client{Timeout: 20 * time.Second}
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("api reset password: API call failed : %s", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
|
@ -227,13 +212,8 @@ func (ctx *ApiCtx) ResetPassword(machineID string, password string) error {
|
|||
return fmt.Errorf("api reset password: return bad HTTP code (%d): %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
jsonResp := ApiResp{}
|
||||
err = json.Unmarshal(body, &jsonResp)
|
||||
if err != nil {
|
||||
return fmt.Errorf("api reset password: unable to unmarshall api response '%s': %s", string(body), err.Error())
|
||||
}
|
||||
if jsonResp.StatusCode != 200 {
|
||||
return fmt.Errorf("api reset password: return bad HTTP code (%d): %s", jsonResp.StatusCode, string(body))
|
||||
if jsonResp.Message == "" || jsonResp.Message != "password updated successfully" || jsonResp.StatusCode != 200 {
|
||||
return fmt.Errorf("api signin failed. http response: %s", body)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,59 +1,51 @@
|
|||
package cwapi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/dghubble/sling"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/tomb.v2"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
const configFile = "./tests/api_config.yaml"
|
||||
const apiVersion = "v1"
|
||||
const apiURL = "https://my_test_endpoint"
|
||||
|
||||
var apiBaseURL = fmt.Sprintf("%s/%s/", apiURL, apiVersion)
|
||||
|
||||
var httpClientMock = &http.Client{
|
||||
Transport: newMockTransport(),
|
||||
}
|
||||
|
||||
type mockTransport struct{}
|
||||
|
||||
func newMockTransport() http.RoundTripper {
|
||||
return &mockTransport{}
|
||||
}
|
||||
|
||||
// Implement http.RoundTripper
|
||||
func (t *mockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
var responseBody string
|
||||
var statusCode int
|
||||
// Create mocked http.Response
|
||||
response := &http.Response{
|
||||
Header: make(http.Header),
|
||||
Request: req,
|
||||
func assertConfigFileEqual(t *testing.T, filepath1 string, filepath2 string) {
|
||||
file1, err := ioutil.ReadFile(filepath1)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to read file '%s': %s", filepath1, err)
|
||||
}
|
||||
response.Header.Set("Content-Type", "application/json")
|
||||
if req.URL.Path == "/v1/signin" {
|
||||
responseBody = `{"statusCode": 200, "message": "crowdsec_api_token"}`
|
||||
statusCode = 200
|
||||
apiCtx1 := &ApiCtx{}
|
||||
if err := yaml.UnmarshalStrict(file1, &apiCtx1); err != nil {
|
||||
t.Fatalf("unable to unmarshall configuration file '%s' : %s", filepath1, err)
|
||||
}
|
||||
response.StatusCode = statusCode
|
||||
response.Body = ioutil.NopCloser(strings.NewReader(responseBody))
|
||||
return response, nil
|
||||
|
||||
file2, err := ioutil.ReadFile(filepath2)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to read file '%s': %s", filepath2, err)
|
||||
}
|
||||
apiCtx2 := &ApiCtx{}
|
||||
if err := yaml.UnmarshalStrict(file2, &apiCtx2); err != nil {
|
||||
t.Fatalf("unable to unmarshall configuration file '%s' : %s", filepath2, err)
|
||||
}
|
||||
assert.Equal(t, apiCtx1, apiCtx2)
|
||||
}
|
||||
|
||||
func TestSignin(t *testing.T) {
|
||||
|
||||
func TestWriteConfig(t *testing.T) {
|
||||
tests := []struct {
|
||||
apiCtx *ApiCtx
|
||||
err error
|
||||
name string
|
||||
configPath string
|
||||
compareToFile string
|
||||
expectedErr bool
|
||||
givenAPICtx *ApiCtx
|
||||
}{
|
||||
{
|
||||
apiCtx: &ApiCtx{
|
||||
name: "basic write config",
|
||||
configPath: "./tests/tmp_api_config.yaml",
|
||||
compareToFile: "./tests/api_config.yaml",
|
||||
expectedErr: false,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
PullPath: "pull",
|
||||
PushPath: "signals",
|
||||
|
@ -62,6 +54,176 @@ func TestSignin(t *testing.T) {
|
|||
ResetPwdPath: "resetpassword",
|
||||
EnrollPath: "enroll",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "test",
|
||||
CfgPassword: "test",
|
||||
Creds: ApiCreds{
|
||||
User: "test",
|
||||
Password: "test",
|
||||
},
|
||||
Muted: false,
|
||||
DebugDump: false,
|
||||
Http: sling.New().Client(httpClientMock).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
err := test.givenAPICtx.WriteConfig(test.configPath)
|
||||
if test.expectedErr && err == nil {
|
||||
t.Fatalf("test '%s' should return an error", test.name)
|
||||
}
|
||||
if !test.expectedErr && err != nil {
|
||||
t.Fatalf("test '%s' returned an error", test.name)
|
||||
}
|
||||
if test.expectedErr {
|
||||
continue
|
||||
}
|
||||
|
||||
assertConfigFileEqual(t, test.configPath, test.compareToFile)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestLoadConfig(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
configPath string
|
||||
expectedErr bool
|
||||
expectedAPICtx *ApiCtx
|
||||
}{
|
||||
{
|
||||
name: "basic load config",
|
||||
configPath: "./tests/api_config.yaml",
|
||||
expectedErr: false,
|
||||
expectedAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
PullPath: "pull",
|
||||
PushPath: "signals",
|
||||
SigninPath: "signin",
|
||||
RegisterPath: "register",
|
||||
ResetPwdPath: "resetpassword",
|
||||
EnrollPath: "enroll",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "test",
|
||||
CfgPassword: "test",
|
||||
Creds: ApiCreds{
|
||||
User: "test",
|
||||
Password: "test",
|
||||
},
|
||||
Muted: false,
|
||||
DebugDump: false,
|
||||
Http: sling.New().Client(httpClientMock).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "load config with bad api version",
|
||||
configPath: "./tests/api_config_bad_api_version.yaml",
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "load config with bad format file",
|
||||
configPath: "./tests/api_config_bad_format.yaml",
|
||||
expectedErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
apiCtx := &ApiCtx{}
|
||||
err := apiCtx.LoadConfig(test.configPath)
|
||||
if test.expectedErr && err == nil {
|
||||
t.Fatalf("test '%s' should return an error", test.name)
|
||||
}
|
||||
if !test.expectedErr && err != nil {
|
||||
t.Fatalf("test '%s' return an error : %s", test.name, err)
|
||||
}
|
||||
if test.expectedErr {
|
||||
continue
|
||||
}
|
||||
apiCtx.Http = test.expectedAPICtx.Http // if we don't do that, assert will fail
|
||||
assert.Equal(t, test.expectedAPICtx, apiCtx)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignin(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
givenAPICtx *ApiCtx
|
||||
expectedErr bool
|
||||
}{
|
||||
{
|
||||
name: "basic api signin",
|
||||
expectedErr: false,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
SigninPath: "signin",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
User: "machine_id",
|
||||
Password: "machine_password",
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
Http: sling.New().Client(httpClientMock).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api signin missing credentials",
|
||||
expectedErr: true,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
SigninPath: "signin",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
Http: sling.New().Client(httpClientMock).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api signin unknown api PATH",
|
||||
expectedErr: true,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
SigninPath: "unknown_path",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
User: "machine_id",
|
||||
Password: "machine_password",
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
Http: sling.New().Client(httpClientMock).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api signin malformed response",
|
||||
expectedErr: true,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
SigninPath: "malformed_response",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
User: "machine_id",
|
||||
Password: "machine_password",
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
Http: sling.New().Client(httpClientMock).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api signin bad response",
|
||||
expectedErr: true,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
SigninPath: "bad_response",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
|
@ -69,27 +231,221 @@ func TestSignin(t *testing.T) {
|
|||
Password: "machine_password",
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
Muted: false,
|
||||
DebugDump: false,
|
||||
Http: sling.New().Client(httpClientMock).Base(apiBaseURL),
|
||||
PusherTomb: tomb.Tomb{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if err := test.apiCtx.Signin(); err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
err := test.givenAPICtx.Signin()
|
||||
if !test.expectedErr && err != nil {
|
||||
t.Fatalf("test '%s' failed : %s", test.name, err)
|
||||
}
|
||||
if test.expectedErr && err == nil {
|
||||
t.Fatalf("test '%s' should return an err", test.name)
|
||||
}
|
||||
log.Printf("test '%s' : OK", test.name)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*func TestRegister(t *testing.T) {
|
||||
prepTest()
|
||||
func TestRegisterMachine(t *testing.T) {
|
||||
|
||||
if err := apiCtx.RegisterMachine(); err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
tests := []struct {
|
||||
name string
|
||||
givenAPICtx *ApiCtx
|
||||
expectedErr bool
|
||||
expectedAPICtx *ApiCtx
|
||||
expectedAPICreds *ApiCreds
|
||||
}{
|
||||
{
|
||||
name: "basic api register machine",
|
||||
expectedErr: false,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
RegisterPath: "register",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
Http: sling.New().Client(httpClientMock).Base(apiBaseURL),
|
||||
},
|
||||
expectedAPICreds: &ApiCreds{
|
||||
User: "machine_id",
|
||||
Password: "machine_password",
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api register unknown api PATH",
|
||||
expectedErr: true,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
RegisterPath: "unknown_path",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
User: "machine_id",
|
||||
Password: "machine_password",
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
Http: sling.New().Client(httpClientMock).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api register malformed response",
|
||||
expectedErr: true,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
RegisterPath: "malformed_response",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
Creds: ApiCreds{
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
Http: sling.New().Client(httpClientMock).Base(apiBaseURL),
|
||||
PusherTomb: tomb.Tomb{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api register bad response",
|
||||
expectedErr: true,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
RegisterPath: "bad_response",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
Http: sling.New().Client(httpClientMock).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
err := test.givenAPICtx.RegisterMachine(test.givenAPICtx.CfgUser, test.givenAPICtx.CfgPassword)
|
||||
if !test.expectedErr && err != nil {
|
||||
t.Fatalf("test '%s' failed : %s", test.name, err)
|
||||
}
|
||||
if test.expectedErr && err == nil {
|
||||
t.Fatalf("test '%s' should return an err", test.name)
|
||||
}
|
||||
if test.expectedAPICreds != nil {
|
||||
assert.Equal(t, *test.expectedAPICreds, test.givenAPICtx.Creds)
|
||||
}
|
||||
log.Printf("test '%s' : OK", test.name)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestResetPassword(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
givenAPICtx *ApiCtx
|
||||
expectedErr bool
|
||||
expectedAPICtx *ApiCtx
|
||||
expectedAPICreds *ApiCreds
|
||||
}{
|
||||
{
|
||||
name: "basic api machine reset password",
|
||||
expectedErr: false,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
ResetPwdPath: "resetpassword",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "new_machine_password",
|
||||
Creds: ApiCreds{
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
Http: sling.New().Client(httpClientMock).Base(apiBaseURL),
|
||||
},
|
||||
expectedAPICreds: &ApiCreds{
|
||||
User: "machine_id",
|
||||
Password: "new_machine_password",
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api reset password unknown api PATH",
|
||||
expectedErr: true,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
ResetPwdPath: "unknown_path",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
User: "machine_id",
|
||||
Password: "machine_password",
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
Http: sling.New().Client(httpClientMock).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api reset password malformed response",
|
||||
expectedErr: true,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
ResetPwdPath: "malformed_response",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
Creds: ApiCreds{
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
Http: sling.New().Client(httpClientMock).Base(apiBaseURL),
|
||||
PusherTomb: tomb.Tomb{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api reset password bad response",
|
||||
expectedErr: true,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
ResetPwdPath: "bad_response",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
Http: sling.New().Client(httpClientMock).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api reset password unknown user",
|
||||
expectedErr: true,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
ResetPwdPath: "resestpassword_unknown_user",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
Http: sling.New().Client(httpClientMock).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
err := test.givenAPICtx.ResetPassword(test.givenAPICtx.CfgUser, test.givenAPICtx.CfgPassword)
|
||||
if !test.expectedErr && err != nil {
|
||||
t.Fatalf("test '%s' failed : %s", test.name, err)
|
||||
}
|
||||
if test.expectedErr && err == nil {
|
||||
t.Fatalf("test '%s' should return an err", test.name)
|
||||
}
|
||||
if test.expectedAPICreds != nil {
|
||||
assert.Equal(t, *test.expectedAPICreds, test.givenAPICtx.Creds)
|
||||
}
|
||||
log.Printf("test '%s' : OK", test.name)
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -3,25 +3,18 @@ package cwapi
|
|||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (ctx *ApiCtx) Enroll(userID string) error {
|
||||
toPush := map[string]string{"user_id": userID}
|
||||
jsonResp := &ApiResp{}
|
||||
|
||||
req, err := ctx.Http.New().Post(ctx.EnrollPath).BodyJSON(&toPush).Request()
|
||||
resp, err := ctx.Http.Post(ctx.EnrollPath).BodyJSON(&toPush).ReceiveSuccess(jsonResp)
|
||||
if err != nil {
|
||||
return fmt.Errorf("api enroll: HTTP request creation failed: %s", err)
|
||||
}
|
||||
log.Debugf("api enroll: URL: '%s'", req.URL)
|
||||
httpClient := http.Client{Timeout: 20 * time.Second}
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("api enroll: API call failed : %s", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
|
@ -31,6 +24,9 @@ func (ctx *ApiCtx) Enroll(userID string) error {
|
|||
if resp.StatusCode != 200 {
|
||||
return fmt.Errorf("api enroll: user '%s' return bad HTTP code (%d): %s", userID, resp.StatusCode, string(body))
|
||||
}
|
||||
if jsonResp.Message == "" || jsonResp.Message != "OK" || jsonResp.StatusCode != 200 {
|
||||
return fmt.Errorf("api user enroll failed. http response: %s", body)
|
||||
}
|
||||
log.Printf("user '%s' is enrolled successfully", string(userID))
|
||||
return nil
|
||||
}
|
||||
|
|
103
pkg/cwapi/enroll_test.go
Normal file
103
pkg/cwapi/enroll_test.go
Normal file
|
@ -0,0 +1,103 @@
|
|||
package cwapi
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/dghubble/sling"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func TestEnroll(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
givenAPICtx *ApiCtx
|
||||
expectedErr bool
|
||||
userID string
|
||||
}{
|
||||
{
|
||||
name: "basic api user enroll",
|
||||
expectedErr: false,
|
||||
userID: "1234",
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
EnrollPath: "enroll",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
User: "machine_id",
|
||||
Password: "machine_password",
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
Http: sling.New().Client(httpClientMock).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api signin unknown api PATH",
|
||||
expectedErr: true,
|
||||
userID: "1234",
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
EnrollPath: "unknown_path",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
User: "machine_id",
|
||||
Password: "machine_password",
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
Http: sling.New().Client(httpClientMock).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api signin malformed response",
|
||||
expectedErr: true,
|
||||
userID: "1234",
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
EnrollPath: "malformed_response",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
User: "machine_id",
|
||||
Password: "machine_password",
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
Http: sling.New().Client(httpClientMock).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api signin bad response",
|
||||
expectedErr: true,
|
||||
userID: "1234",
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
EnrollPath: "bad_response",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
User: "machine_id",
|
||||
Password: "machine_password",
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
Http: sling.New().Client(httpClientMock).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
err := test.givenAPICtx.Enroll(test.userID)
|
||||
if !test.expectedErr && err != nil {
|
||||
t.Fatalf("test '%s' failed : %s", test.name, err)
|
||||
}
|
||||
if test.expectedErr && err == nil {
|
||||
t.Fatalf("test '%s' should return an err", test.name)
|
||||
}
|
||||
log.Printf("test '%s' : OK", test.name)
|
||||
}
|
||||
|
||||
}
|
|
@ -4,7 +4,6 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
|
@ -22,18 +21,11 @@ func (ctx *ApiCtx) pushSignals() error {
|
|||
if len(ctx.toPush) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
req, err := ctx.Http.New().Put(ctx.PushPath).BodyJSON(&ctx.toPush).Request()
|
||||
jsonResp := &ApiResp{}
|
||||
resp, err := ctx.Http.New().Put(ctx.PushPath).BodyJSON(&ctx.toPush).ReceiveSuccess(jsonResp)
|
||||
if err != nil {
|
||||
return fmt.Errorf("api push signal: HTTP request creation failed: %s", err)
|
||||
}
|
||||
log.Debugf("api push: URL: '%s'", req.URL)
|
||||
|
||||
httpClient := http.Client{Timeout: 20 * time.Second}
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("api push signal: API call failed : %s", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
|
@ -57,6 +49,11 @@ func (ctx *ApiCtx) pushSignals() error {
|
|||
return fmt.Errorf("api push signal: return bad HTTP code (%d): %s", resp.StatusCode, string(body))
|
||||
}
|
||||
}
|
||||
|
||||
if resp.StatusCode != 401 && (jsonResp.Message == "" || jsonResp.Message != "OK" || jsonResp.StatusCode != 200) {
|
||||
return fmt.Errorf("api push failed. http response: %s", body)
|
||||
}
|
||||
|
||||
if len(ctx.toPush) > 0 {
|
||||
log.Infof("api push signal: pushed %d signals successfully", len(ctx.toPush))
|
||||
}
|
||||
|
|
248
pkg/cwapi/signals_test.go
Normal file
248
pkg/cwapi/signals_test.go
Normal file
|
@ -0,0 +1,248 @@
|
|||
package cwapi
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
"github.com/dghubble/sling"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var signalList = []types.Event{
|
||||
{
|
||||
Overflow: types.SignalOccurence{
|
||||
Scenario: "crowdsec/test",
|
||||
Bucket_id: "1234",
|
||||
Events_count: 1,
|
||||
Events_sequence: []types.EventSequence{},
|
||||
Start_at: time.Now(),
|
||||
BanApplications: []types.BanApplication{},
|
||||
Stop_at: time.Now(),
|
||||
Source_ip: "1.2.3.4",
|
||||
Source_range: "1.2.3.0/24",
|
||||
Source_AutonomousSystemNumber: "1234",
|
||||
Source_AutonomousSystemOrganization: "TestAS",
|
||||
Source_Country: "FR",
|
||||
Dest_ip: "1.2.3.5",
|
||||
Capacity: 1,
|
||||
Whitelisted: false,
|
||||
Simulation: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
Overflow: types.SignalOccurence{
|
||||
Scenario: "crowdsec/test",
|
||||
Bucket_id: "1235",
|
||||
Events_count: 1,
|
||||
Events_sequence: []types.EventSequence{},
|
||||
Start_at: time.Now(),
|
||||
BanApplications: []types.BanApplication{},
|
||||
Stop_at: time.Now(),
|
||||
Source_ip: "1.2.3.5",
|
||||
Source_range: "1.2.3.0/24",
|
||||
Source_AutonomousSystemNumber: "1234",
|
||||
Source_AutonomousSystemOrganization: "TestAS",
|
||||
Source_Country: "FR",
|
||||
Dest_ip: "1.2.3.6",
|
||||
Capacity: 1,
|
||||
Whitelisted: false,
|
||||
Simulation: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestPushSignal(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
givenAPICtx *ApiCtx
|
||||
expectedErr bool
|
||||
}{
|
||||
{
|
||||
name: "basic api push signal",
|
||||
expectedErr: false,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
PushPath: "signals",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
User: "machine_id",
|
||||
Password: "machine_password",
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
toPush: signalList,
|
||||
Http: sling.New().Client(newMockClient()).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api push signal unknown api PATH",
|
||||
expectedErr: true,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
PushPath: "unknown_path",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
User: "machine_id",
|
||||
Password: "machine_password",
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
toPush: signalList,
|
||||
Http: sling.New().Client(newMockClient()).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api push signal malformed response",
|
||||
expectedErr: true,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
PushPath: "malformed_response",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
User: "machine_id",
|
||||
Password: "machine_password",
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
toPush: signalList,
|
||||
Http: sling.New().Client(newMockClient()).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api push signal bad response",
|
||||
expectedErr: true,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
PushPath: "bad_response",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
User: "machine_id",
|
||||
Password: "machine_password",
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
toPush: signalList,
|
||||
Http: sling.New().Client(newMockClient()).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api push signal empty signal list",
|
||||
expectedErr: false,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
PushPath: "signals",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
User: "machine_id",
|
||||
Password: "machine_password",
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
toPush: []types.Event{},
|
||||
Http: sling.New().Client(newMockClient()).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api push signal expired token",
|
||||
expectedErr: false,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
PushPath: "signals_token_expired",
|
||||
SigninPath: "signin",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
User: "machine_id",
|
||||
Password: "machine_password",
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
toPush: signalList,
|
||||
tokenExpired: false,
|
||||
Http: sling.New().Client(newMockClient()).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api push signal unable to renew expired token",
|
||||
expectedErr: true,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
PushPath: "signals_token_renew_fail",
|
||||
SigninPath: "signin",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
User: "machine_id",
|
||||
Password: "machine_password",
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
toPush: signalList,
|
||||
tokenExpired: false,
|
||||
Http: sling.New().Client(newMockClient()).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api push signal bad response code",
|
||||
expectedErr: true,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
PushPath: "signals_bad_response_code",
|
||||
SigninPath: "signin",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
User: "machine_id",
|
||||
Password: "machine_password",
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
toPush: signalList,
|
||||
tokenExpired: false,
|
||||
Http: sling.New().Client(newMockClient()).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api push signal signin while token expired failed",
|
||||
expectedErr: true,
|
||||
givenAPICtx: &ApiCtx{
|
||||
ApiVersion: "v1",
|
||||
PushPath: "signals_token_expired",
|
||||
SigninPath: "bad_response",
|
||||
BaseURL: "https://my_testendpoint.com",
|
||||
CfgUser: "machine_id",
|
||||
CfgPassword: "machine_password",
|
||||
Creds: ApiCreds{
|
||||
User: "machine_id",
|
||||
Password: "machine_password",
|
||||
Profile: "crowdsec/test1,crowdsec/test2",
|
||||
},
|
||||
toPush: signalList,
|
||||
tokenExpired: false,
|
||||
Http: sling.New().Client(newMockClient()).Base(apiBaseURL),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
err := test.givenAPICtx.pushSignals()
|
||||
if !test.expectedErr && err != nil {
|
||||
t.Fatalf("test '%s' failed : %s", test.name, err)
|
||||
}
|
||||
if test.expectedErr && err == nil {
|
||||
t.Fatalf("test '%s' should return an err", test.name)
|
||||
}
|
||||
if test.expectedErr {
|
||||
continue
|
||||
}
|
||||
log.Printf("test '%s' : OK", test.name)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
version: v1
|
||||
url: https://test_endpoint
|
||||
url: https://my_testendpoint.com
|
||||
signin_path: signin
|
||||
push_path: signals
|
||||
pull_path: pull
|
||||
|
|
10
pkg/cwapi/tests/api_config_bad_api_version.yaml
Normal file
10
pkg/cwapi/tests/api_config_bad_api_version.yaml
Normal file
|
@ -0,0 +1,10 @@
|
|||
version: v0
|
||||
url: https://test_endpoint
|
||||
signin_path: signin
|
||||
push_path: signals
|
||||
pull_path: pull
|
||||
enroll_path: enroll
|
||||
reset_pwd_path: resetpassword
|
||||
register_path: register
|
||||
machine_id: test
|
||||
password: test
|
11
pkg/cwapi/tests/api_config_bad_format.yaml
Normal file
11
pkg/cwapi/tests/api_config_bad_format.yaml
Normal file
|
@ -0,0 +1,11 @@
|
|||
bad_key: test
|
||||
version: v1
|
||||
url: https://test_endpoint
|
||||
signin_path: signin
|
||||
push_path: signals
|
||||
pull_path: pull
|
||||
enroll_path: enroll
|
||||
reset_pwd_path: resetpassword
|
||||
register_path: register
|
||||
machine_id: test
|
||||
password: test
|
101
pkg/cwapi/utils_test.go
Normal file
101
pkg/cwapi/utils_test.go
Normal file
|
@ -0,0 +1,101 @@
|
|||
package cwapi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const configFile = "./tests/api_config.yaml"
|
||||
const apiVersion = "v1"
|
||||
const apiURL = "https://my_test_endpoint"
|
||||
|
||||
var apiBaseURL = fmt.Sprintf("%s/%s/", apiURL, apiVersion)
|
||||
|
||||
var httpClientMock = &http.Client{
|
||||
Transport: newMockTransport(),
|
||||
Timeout: time.Second * 20,
|
||||
}
|
||||
|
||||
type mockTransport struct {
|
||||
nbTryTokenOK int // to test token expiration
|
||||
nbTryTokenNOK int
|
||||
}
|
||||
|
||||
func newMockTransport() http.RoundTripper {
|
||||
return &mockTransport{}
|
||||
}
|
||||
|
||||
func newMockClient() *http.Client {
|
||||
return &http.Client{
|
||||
Transport: newMockTransport(),
|
||||
Timeout: time.Second * 20,
|
||||
}
|
||||
}
|
||||
|
||||
// Implement http.RoundTripper
|
||||
func (t *mockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
var responseBody string
|
||||
var statusCode int
|
||||
// Create mocked http.Response
|
||||
response := &http.Response{
|
||||
Header: make(http.Header),
|
||||
Request: req,
|
||||
}
|
||||
response.Header.Set("Content-Type", "application/json")
|
||||
switch req.URL.Path {
|
||||
case "/v1/signin":
|
||||
responseBody = `{"statusCode": 200, "message": "crowdsec_api_token"}`
|
||||
statusCode = 200
|
||||
case "/v1/register":
|
||||
responseBody = `{"statusCode": 200, "message": "OK"}`
|
||||
statusCode = 200
|
||||
case "/v1/signals":
|
||||
responseBody = `{"statusCode": 200, "message": "OK"}`
|
||||
statusCode = 200
|
||||
case "/v1/signals_token_expired":
|
||||
if t.nbTryTokenOK == 0 {
|
||||
responseBody = `{"statusCode": 200, "message": "crowdsec_api_token"}`
|
||||
statusCode = 401
|
||||
t.nbTryTokenOK++
|
||||
} else {
|
||||
responseBody = `{"statusCode": 200, "message": "OK"}`
|
||||
statusCode = 200
|
||||
}
|
||||
case "/v1/signals_token_renew_fail":
|
||||
if t.nbTryTokenNOK == 0 {
|
||||
responseBody = `{"statusCode": 200, "message": "crowdsec_api_token"}`
|
||||
statusCode = 401
|
||||
t.nbTryTokenNOK++
|
||||
} else {
|
||||
responseBody = `{"statusCode": 500, "message": "token expired"}`
|
||||
statusCode = 500
|
||||
}
|
||||
case "/v1/signals_bad_response_code":
|
||||
responseBody = `{"statusCode": 200, "message": "OK"}`
|
||||
statusCode = 500
|
||||
case "/v1/enroll":
|
||||
responseBody = `{"statusCode": 200, "message": "OK"}`
|
||||
statusCode = 200
|
||||
case "/v1/resetpassword":
|
||||
responseBody = `{"statusCode": 200, "message": "password updated successfully"}`
|
||||
statusCode = 200
|
||||
case "/v1/resetpassword_unknown_user":
|
||||
responseBody = `{"statusCode": 500, "message": "User not found"}`
|
||||
statusCode = 200
|
||||
case "/v1/unknown_path":
|
||||
statusCode = 404
|
||||
responseBody = `{"error": "unknown URI"}`
|
||||
case "/v1/malformed_response":
|
||||
statusCode = 200
|
||||
responseBody = `{"statusCode" : 200, "msg" : "api_token"`
|
||||
case "/v1/bad_response":
|
||||
statusCode = 200
|
||||
responseBody = `{"statusCode" : 200, "msg" : "api_token"}`
|
||||
}
|
||||
response.StatusCode = statusCode
|
||||
response.Body = ioutil.NopCloser(strings.NewReader(responseBody))
|
||||
return response, nil
|
||||
}
|
Loading…
Add table
Reference in a new issue