mirror of
https://github.com/twofas/2fas-server.git
synced 2025-01-07 06:55:49 +01:00
Merge pull request #44 from twofas/develop/pass-sync-master
This commit is contained in:
commit
963533dc4e
1
.env
1
.env
@ -15,6 +15,7 @@ SECURITY_RATE_LIMIT_BE=100
|
|||||||
SECURITY_RATE_LIMIT_MOBILE=100
|
SECURITY_RATE_LIMIT_MOBILE=100
|
||||||
|
|
||||||
PASS_ADDR=:8082
|
PASS_ADDR=:8082
|
||||||
|
FAKE_MOBILE_PUSH=true
|
||||||
|
|
||||||
AWS_ACCESS_KEY_ID=test
|
AWS_ACCESS_KEY_ID=test
|
||||||
AWS_SECRET_ACCESS_KEY=test
|
AWS_SECRET_ACCESS_KEY=test
|
||||||
|
11
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
11
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
blank_issues_enabled: true
|
||||||
|
contact_links:
|
||||||
|
- name: Help center
|
||||||
|
url: https://2fas.com/help-center/
|
||||||
|
about: Check out our extensive FaQ and video guides!
|
||||||
|
- name: Discord
|
||||||
|
url: https://discord.gg/q4cP6qh2g5
|
||||||
|
about: Need support or have a question? Our Discord members are there to help!
|
||||||
|
- name: Reddit
|
||||||
|
url: https://www.reddit.com/r/2fas_com/
|
||||||
|
about: Get support and discuss 2FAS with our Reddit community!
|
25
.github/workflows/go.yml
vendored
Normal file
25
.github/workflows/go.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
name: Go
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main", "develop/pass" ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: '1.22'
|
||||||
|
|
||||||
|
- name: Unit Test
|
||||||
|
run: make unit-tests-ci
|
||||||
|
|
||||||
|
- name: e2e Test
|
||||||
|
run: make ci-e2e
|
28
Makefile
28
Makefile
@ -15,25 +15,33 @@ migration: ## create database migrations file
|
|||||||
migration-up: ## apply all available migrations
|
migration-up: ## apply all available migrations
|
||||||
docker compose run -u ${USERID}:${USERID} --rm api migrate up
|
docker compose run -u ${USERID}:${USERID} --rm api migrate up
|
||||||
|
|
||||||
|
.PHONY: up
|
||||||
up: ## run all applications in stack
|
up: ## run all applications in stack
|
||||||
docker compose build
|
docker compose build
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
|
|
||||||
|
.PHONY: unit-tests
|
||||||
|
unit-tests: ## run unit tests without e2e-tests directory.
|
||||||
|
go test -race -count=1 `go list ./... | grep -v e2e-tests`
|
||||||
|
|
||||||
test: ## run unit tests
|
.PHONY: unit-tests-ci
|
||||||
go test ./internal/...
|
unit-tests-ci: ## run unit tests without e2e-tests directory (multiple times to find race conditions).
|
||||||
|
go test -race -count=50 -failfast `go list ./... | grep -v e2e-tests`
|
||||||
|
|
||||||
|
.PHONY: ci-e2e
|
||||||
|
ci-e2e: up
|
||||||
|
go run ./e2e-tests/scripts/wait-ready/main.go -addr=':80;:8081;:8082'
|
||||||
|
@$(MAKE) tests-e2e
|
||||||
|
|
||||||
|
.PHONY: tests-e2e
|
||||||
tests-e2e: ## run end to end tests
|
tests-e2e: ## run end to end tests
|
||||||
## There is some race condition when running tests as go test -count=1 ./tests/... Come back at some point and fix it
|
## There is some race condition when running tests as go test -count=1 ./tests/... Come back at some point and fix it
|
||||||
go test ./tests/browser_extension/... -count=1
|
go test ./e2e-tests/browser_extension/... -count=1
|
||||||
go test ./tests/icons/... -count=1
|
go test ./e2e-tests/icons/... -count=1
|
||||||
go test ./tests/mobile/... -count=1
|
go test ./e2e-tests/mobile/... -count=1
|
||||||
go test ./tests/support/... -count=1
|
go test ./e2e-tests/support/... -count=1
|
||||||
go test ./tests/system/... -count=1
|
go test ./e2e-tests/system/... -count=1
|
||||||
go test ./tests/pass/... -count=1
|
PASS_ADDR="localhost:8088" go test ./e2e-tests/pass/... -count=1
|
||||||
|
|
||||||
|
|
||||||
vendor-licenses: ## report vendor licenses
|
vendor-licenses: ## report vendor licenses
|
||||||
go-licenses report ./cmd/api --template licenses.tpl > licenses.json 2> licenses-errors
|
go-licenses report ./cmd/api --template licenses.tpl > licenses.json 2> licenses-errors
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
logging.WithDefaultField("service_name", "admin_api")
|
logging.Init(logging.Fields{"service_name": "admin_api"})
|
||||||
|
|
||||||
config.LoadConfiguration()
|
config.LoadConfiguration()
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
logging.WithDefaultField("service_name", "api")
|
logging.Init(logging.Fields{"service_name": "api"})
|
||||||
|
|
||||||
config.LoadConfiguration()
|
config.LoadConfiguration()
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
logging.WithDefaultField("service_name", "pass")
|
logging.Init(logging.Fields{"service_name": "pass"})
|
||||||
|
|
||||||
var cfg config.PassConfig
|
var cfg config.PassConfig
|
||||||
err := envconfig.Process("", &cfg)
|
err := envconfig.Process("", &cfg)
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
logging.WithDefaultField("service_name", "websocket_api")
|
logging.Init(logging.Fields{"service_name": "websocket_api"})
|
||||||
|
|
||||||
config.LoadConfiguration()
|
config.LoadConfiguration()
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ func initViper(configFilePath string) {
|
|||||||
|
|
||||||
err := viper.ReadInConfig()
|
err := viper.ReadInConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.Fatal("failed to read the configuration file: %s", err)
|
logging.Fatalf("failed to read the configuration file: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = viper.Unmarshal(&Config)
|
err = viper.Unmarshal(&Config)
|
||||||
|
@ -89,10 +89,11 @@ services:
|
|||||||
group_add:
|
group_add:
|
||||||
- '1000'
|
- '1000'
|
||||||
ports:
|
ports:
|
||||||
- "8084:8082"
|
- "8088:8082"
|
||||||
environment:
|
environment:
|
||||||
# overwrite AWS_ENDPOINT from .env file. One in env is used to running app from local also.
|
# overwrite AWS_ENDPOINT from .env file. One in env is used to running app from local also.
|
||||||
AWS_ENDPOINT: http://localstack-main:4566
|
AWS_ENDPOINT: http://localstack-main:4566
|
||||||
|
AWS_REGION: us-east-1
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
depends_on:
|
depends_on:
|
||||||
@ -113,7 +114,7 @@ services:
|
|||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
volumes:
|
volumes:
|
||||||
- "./tests/localstack_init.sh:/etc/localstack/init/ready.d/localstack_init.sh" # ready hook
|
- "./e2e-tests/localstack_init.sh:/etc/localstack/init/ready.d/localstack_init.sh" # ready hook
|
||||||
- "./data/localstack:/var/lib/localstack"
|
- "./data/localstack:/var/lib/localstack"
|
||||||
- "/var/run/docker.sock:/var/run/docker.sock"
|
- "/var/run/docker.sock:/var/run/docker.sock"
|
||||||
|
|
||||||
|
@ -0,0 +1,121 @@
|
|||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"github.com/twofas/2fas-server/e2e-tests"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBrowserExtensionTwoFactorAuthTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(BrowserExtensionTwoFactorAuthTestSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
type BrowserExtensionTwoFactorAuthTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BrowserExtensionTwoFactorAuthTestSuite) SetupTest() {
|
||||||
|
e2e_tests.RemoveAllMobileDevices(s.T())
|
||||||
|
e2e_tests.RemoveAllBrowserExtensions(s.T())
|
||||||
|
e2e_tests.RemoveAllBrowserExtensionsDevices(s.T())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BrowserExtensionTwoFactorAuthTestSuite) TestRequest2FaToken() {
|
||||||
|
browserExtension := e2e_tests.CreateBrowserExtension(s.T(), "go-ext")
|
||||||
|
|
||||||
|
var tokenRequest *e2e_tests.AuthTokenRequestResponse
|
||||||
|
request2FaTokenPayload := []byte(`{"domain":"https://facebook.com/path/nested"}`)
|
||||||
|
e2e_tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/commands/request_2fa_token", request2FaTokenPayload, &tokenRequest)
|
||||||
|
|
||||||
|
assert.Equal(s.T(), browserExtension.Id, tokenRequest.ExtensionId)
|
||||||
|
|
||||||
|
var tokenRequestById *e2e_tests.AuthTokenRequestResponse
|
||||||
|
e2e_tests.DoAPISuccessGet(s.T(), "browser_extensions/"+browserExtension.Id+"/2fa_requests/"+tokenRequest.Id, &tokenRequestById)
|
||||||
|
assert.Equal(s.T(), tokenRequest.Id, tokenRequestById.Id)
|
||||||
|
assert.Equal(s.T(), "https://facebook.com", tokenRequestById.Domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BrowserExtensionTwoFactorAuthTestSuite) TestFindAll2FaRequestsForBrowserExtension() {
|
||||||
|
browserExtension := e2e_tests.CreateBrowserExtension(s.T(), "go-ext")
|
||||||
|
|
||||||
|
facebook2FaTokenRequest := []byte(`{"domain":"facebook.com"}`)
|
||||||
|
e2e_tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/commands/request_2fa_token", facebook2FaTokenRequest, nil)
|
||||||
|
|
||||||
|
google2FaTokenRequest := []byte(`{"domain":"google.com"}`)
|
||||||
|
e2e_tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/commands/request_2fa_token", google2FaTokenRequest, nil)
|
||||||
|
|
||||||
|
var tokenRequestsCollection []*e2e_tests.AuthTokenRequestResponse
|
||||||
|
e2e_tests.DoAPISuccessGet(s.T(), "browser_extensions/"+browserExtension.Id+"/2fa_requests", &tokenRequestsCollection)
|
||||||
|
|
||||||
|
assert.Len(s.T(), tokenRequestsCollection, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BrowserExtensionTwoFactorAuthTestSuite) TestClose2FaTokenRequest() {
|
||||||
|
var tokenRequest *e2e_tests.AuthTokenRequestResponse
|
||||||
|
browserExtension := e2e_tests.CreateBrowserExtension(s.T(), "go-ext")
|
||||||
|
tokenRequestPayload := []byte(`{"domain":"facebook.com"}`)
|
||||||
|
e2e_tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/commands/request_2fa_token", tokenRequestPayload, &tokenRequest)
|
||||||
|
closeTokenRequestPayload := []byte(`{"status":"completed"}`)
|
||||||
|
e2e_tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/2fa_requests/"+tokenRequest.Id+"/commands/close_2fa_request", closeTokenRequestPayload, nil)
|
||||||
|
|
||||||
|
var closedTokenRequest *e2e_tests.AuthTokenRequestResponse
|
||||||
|
e2e_tests.DoAPISuccessGet(s.T(), "browser_extensions/"+browserExtension.Id+"/2fa_requests/"+tokenRequest.Id, &closedTokenRequest)
|
||||||
|
assert.Equal(s.T(), "completed", closedTokenRequest.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BrowserExtensionTwoFactorAuthTestSuite) TestCloseNotExisting2FaTokenRequest() {
|
||||||
|
notExistingTokenRequestId := uuid.New()
|
||||||
|
browserExtension := e2e_tests.CreateBrowserExtension(s.T(), "go-ext")
|
||||||
|
|
||||||
|
closeTokenRequestPayload := []byte(`{"status":"completed"}`)
|
||||||
|
e2e_tests.DoAPIPostAndAssertCode(s.T(), 404, "browser_extensions/"+browserExtension.Id+"/2fa_requests/"+notExistingTokenRequestId.String()+"/commands/close_2fa_request", closeTokenRequestPayload, nil)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BrowserExtensionTwoFactorAuthTestSuite) TestDoNotReturnClosed2FaRequests() {
|
||||||
|
var tokenRequest *e2e_tests.AuthTokenRequestResponse
|
||||||
|
browserExtension := e2e_tests.CreateBrowserExtension(s.T(), "go-ext")
|
||||||
|
tokenRequestPayload := []byte(`{"domain":"facebook.com"}`)
|
||||||
|
e2e_tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/commands/request_2fa_token", tokenRequestPayload, &tokenRequest)
|
||||||
|
|
||||||
|
closeTokenRequestPayload := []byte(`{"status":"completed"}`)
|
||||||
|
e2e_tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/2fa_requests/"+tokenRequest.Id+"/commands/close_2fa_request", closeTokenRequestPayload, nil)
|
||||||
|
|
||||||
|
var response []*e2e_tests.AuthTokenRequestResponse
|
||||||
|
e2e_tests.DoAPISuccessGet(s.T(), "browser_extensions/"+browserExtension.Id+"/2fa_requests", &response)
|
||||||
|
assert.Len(s.T(), response, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BrowserExtensionTwoFactorAuthTestSuite) TestTerminate2FaRequest() {
|
||||||
|
var tokenRequest *e2e_tests.AuthTokenRequestResponse
|
||||||
|
browserExtension := e2e_tests.CreateBrowserExtension(s.T(), "go-ext")
|
||||||
|
tokenRequestPayload := []byte(`{"domain":"facebook.com"}`)
|
||||||
|
e2e_tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/commands/request_2fa_token", tokenRequestPayload, &tokenRequest)
|
||||||
|
|
||||||
|
closeTokenRequestPayload := []byte(`{"status":"terminated"}`)
|
||||||
|
e2e_tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/2fa_requests/"+tokenRequest.Id+"/commands/close_2fa_request", closeTokenRequestPayload, nil)
|
||||||
|
|
||||||
|
var response *e2e_tests.AuthTokenRequestResponse
|
||||||
|
e2e_tests.DoAPISuccessGet(s.T(), "browser_extensions/"+browserExtension.Id+"/2fa_requests/"+tokenRequest.Id, &response)
|
||||||
|
assert.Equal(s.T(), "terminated", response.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BrowserExtensionTwoFactorAuthTestSuite) TestClose2FaRequest() {
|
||||||
|
device, devicePubKey := e2e_tests.CreateDevice(s.T(), "SM-955F", "fcm-token")
|
||||||
|
browserExtension := e2e_tests.CreateBrowserExtension(s.T(), "go-ext")
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExtension, device)
|
||||||
|
|
||||||
|
var tokenRequest *e2e_tests.AuthTokenRequestResponse
|
||||||
|
request2FaTokenPayload := []byte(`{"domain":"domain.com"}`)
|
||||||
|
e2e_tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/commands/request_2fa_token", request2FaTokenPayload, &tokenRequest)
|
||||||
|
|
||||||
|
closeTokenRequestPayload := []byte(`{"status":"completed"}`)
|
||||||
|
e2e_tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/2fa_requests/"+tokenRequest.Id+"/commands/close_2fa_request", closeTokenRequestPayload, nil)
|
||||||
|
|
||||||
|
var closedTokenRequest *e2e_tests.AuthTokenRequestResponse
|
||||||
|
e2e_tests.DoAPISuccessGet(s.T(), "browser_extensions/"+browserExtension.Id+"/2fa_requests/"+tokenRequest.Id, &closedTokenRequest)
|
||||||
|
assert.Equal(s.T(), "completed", closedTokenRequest.Status)
|
||||||
|
}
|
@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/twofas/2fas-server/tests"
|
"github.com/twofas/2fas-server/e2e-tests"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTwoFactorAuthTestSuite(t *testing.T) {
|
func TestTwoFactorAuthTestSuite(t *testing.T) {
|
||||||
@ -18,33 +18,33 @@ type TwoFactorAuthTestSuite struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *TwoFactorAuthTestSuite) SetupTest() {
|
func (s *TwoFactorAuthTestSuite) SetupTest() {
|
||||||
tests.RemoveAllMobileDevices(s.T())
|
e2e_tests.RemoveAllMobileDevices(s.T())
|
||||||
tests.RemoveAllBrowserExtensions(s.T())
|
e2e_tests.RemoveAllBrowserExtensions(s.T())
|
||||||
tests.RemoveAllBrowserExtensionsDevices(s.T())
|
e2e_tests.RemoveAllBrowserExtensionsDevices(s.T())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TwoFactorAuthTestSuite) TestBrowserExtensionAuthFullFlow() {
|
func (s *TwoFactorAuthTestSuite) TestBrowserExtensionAuthFullFlow() {
|
||||||
device, devicePubKey := tests.CreateDevice(s.T(), "SM-955F", "some-token")
|
device, devicePubKey := e2e_tests.CreateDevice(s.T(), "SM-955F", "some-token")
|
||||||
browserExtension := tests.CreateBrowserExtension(s.T(), "go-ext")
|
browserExtension := e2e_tests.CreateBrowserExtension(s.T(), "go-ext")
|
||||||
|
|
||||||
websocketTestListener := tests.NewWebsocketTestListener("browser_extensions/" + browserExtension.Id)
|
websocketTestListener := e2e_tests.NewWebsocketTestListener("browser_extensions/" + browserExtension.Id)
|
||||||
websocketConnection := websocketTestListener.StartListening()
|
websocketConnection := websocketTestListener.StartListening()
|
||||||
defer websocketConnection.Close()
|
defer websocketConnection.Close()
|
||||||
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExtension, device)
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExtension, device)
|
||||||
|
|
||||||
assertDeviceHasPairedExtension(s.T(), device, browserExtension)
|
assertDeviceHasPairedExtension(s.T(), device, browserExtension)
|
||||||
assertBrowserExtensionHasPairedDevice(s.T(), browserExtension, device)
|
assertBrowserExtensionHasPairedDevice(s.T(), browserExtension, device)
|
||||||
expectedPairingSuccessWebsocket := createPairingSuccessWebsocketMessage(browserExtension, device, devicePubKey)
|
expectedPairingSuccessWebsocket := createPairingSuccessWebsocketMessage(browserExtension, device, devicePubKey)
|
||||||
websocketTestListener.AssertMessageHasBeenReceived(s.T(), expectedPairingSuccessWebsocket)
|
websocketTestListener.AssertMessageHasBeenReceived(s.T(), expectedPairingSuccessWebsocket)
|
||||||
|
|
||||||
tokenRequest := tests.Request2FaToken(s.T(), "facebook.com", browserExtension.Id)
|
tokenRequest := e2e_tests.Request2FaToken(s.T(), "facebook.com", browserExtension.Id)
|
||||||
|
|
||||||
extensionTokenRequestWebsocketListener := tests.NewWebsocketTestListener("browser_extensions/" + browserExtension.Id + "/2fa_requests/" + tokenRequest.Id)
|
extensionTokenRequestWebsocketListener := e2e_tests.NewWebsocketTestListener("browser_extensions/" + browserExtension.Id + "/2fa_requests/" + tokenRequest.Id)
|
||||||
extensionTokenRequestWebsocketConnection := extensionTokenRequestWebsocketListener.StartListening()
|
extensionTokenRequestWebsocketConnection := extensionTokenRequestWebsocketListener.StartListening()
|
||||||
defer extensionTokenRequestWebsocketConnection.Close()
|
defer extensionTokenRequestWebsocketConnection.Close()
|
||||||
|
|
||||||
tests.Send2FaTokenToExtension(s.T(), browserExtension.Id, device.Id, tokenRequest.Id, "2fa-token")
|
e2e_tests.Send2FaTokenToExtension(s.T(), browserExtension.Id, device.Id, tokenRequest.Id, "2fa-token")
|
||||||
|
|
||||||
expected2FaTokenWebsocket := createBrowserExtensionReceived2FaTokenMessage(browserExtension.Id, device.Id, tokenRequest.Id)
|
expected2FaTokenWebsocket := createBrowserExtensionReceived2FaTokenMessage(browserExtension.Id, device.Id, tokenRequest.Id)
|
||||||
extensionTokenRequestWebsocketListener.AssertMessageHasBeenReceived(s.T(), expected2FaTokenWebsocket)
|
extensionTokenRequestWebsocketListener.AssertMessageHasBeenReceived(s.T(), expected2FaTokenWebsocket)
|
||||||
@ -70,7 +70,7 @@ func createBrowserExtensionReceived2FaTokenMessage(extensionId, deviceId, reques
|
|||||||
return string(message)
|
return string(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createPairingSuccessWebsocketMessage(browserExtension *tests.BrowserExtensionResponse, device *tests.DeviceResponse, devicePubKey string) string {
|
func createPairingSuccessWebsocketMessage(browserExtension *e2e_tests.BrowserExtensionResponse, device *e2e_tests.DeviceResponse, devicePubKey string) string {
|
||||||
expectedPairingWebsocketMessageRaw := &struct {
|
expectedPairingWebsocketMessageRaw := &struct {
|
||||||
Event string `json:"event"`
|
Event string `json:"event"`
|
||||||
BrowserExtensionId string `json:"browser_extension_id"`
|
BrowserExtensionId string `json:"browser_extension_id"`
|
||||||
@ -88,17 +88,17 @@ func createPairingSuccessWebsocketMessage(browserExtension *tests.BrowserExtensi
|
|||||||
return string(message)
|
return string(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertBrowserExtensionHasPairedDevice(t *testing.T, browserExtension *tests.BrowserExtensionResponse, device *tests.DeviceResponse) {
|
func assertBrowserExtensionHasPairedDevice(t *testing.T, browserExtension *e2e_tests.BrowserExtensionResponse, device *e2e_tests.DeviceResponse) {
|
||||||
var browserExtensionDevices []*tests.DeviceResponse
|
var browserExtensionDevices []*e2e_tests.DeviceResponse
|
||||||
tests.DoAPISuccessGet(t, "browser_extensions/"+browserExtension.Id+"/devices", &browserExtensionDevices)
|
e2e_tests.DoAPISuccessGet(t, "browser_extensions/"+browserExtension.Id+"/devices", &browserExtensionDevices)
|
||||||
|
|
||||||
assert.Len(t, browserExtensionDevices, 1)
|
assert.Len(t, browserExtensionDevices, 1)
|
||||||
assert.Equal(t, device.Id, browserExtensionDevices[0].Id)
|
assert.Equal(t, device.Id, browserExtensionDevices[0].Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertDeviceHasPairedExtension(t *testing.T, device *tests.DeviceResponse, browserExtension *tests.BrowserExtensionResponse) {
|
func assertDeviceHasPairedExtension(t *testing.T, device *e2e_tests.DeviceResponse, browserExtension *e2e_tests.BrowserExtensionResponse) {
|
||||||
var deviceBrowserExtensions []*tests.BrowserExtensionResponse
|
var deviceBrowserExtensions []*e2e_tests.BrowserExtensionResponse
|
||||||
tests.DoAPISuccessGet(t, "mobile/devices/"+device.Id+"/browser_extensions", &deviceBrowserExtensions)
|
e2e_tests.DoAPISuccessGet(t, "mobile/devices/"+device.Id+"/browser_extensions", &deviceBrowserExtensions)
|
||||||
|
|
||||||
assert.Len(t, deviceBrowserExtensions, 1)
|
assert.Len(t, deviceBrowserExtensions, 1)
|
||||||
assert.Equal(t, browserExtension.Id, deviceBrowserExtensions[0].Id)
|
assert.Equal(t, browserExtension.Id, deviceBrowserExtensions[0].Id)
|
@ -5,11 +5,11 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/twofas/2fas-server/tests"
|
"github.com/twofas/2fas-server/e2e-tests"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_BrowserExtensionLogging(t *testing.T) {
|
func Test_BrowserExtensionLogging(t *testing.T) {
|
||||||
browserExtension := tests.CreateBrowserExtension(t, "go-ext")
|
browserExtension := e2e_tests.CreateBrowserExtension(t, "go-ext")
|
||||||
|
|
||||||
log := &struct {
|
log := &struct {
|
||||||
Level string `json:"level"`
|
Level string `json:"level"`
|
||||||
@ -20,7 +20,7 @@ func Test_BrowserExtensionLogging(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
payload, _ := json.Marshal(log)
|
payload, _ := json.Marshal(log)
|
||||||
tests.DoAPISuccessPost(t, "/browser_extensions/"+browserExtension.Id+"/commands/store_log", payload, nil)
|
e2e_tests.DoAPISuccessPost(t, "/browser_extensions/"+browserExtension.Id+"/commands/store_log", payload, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_NotExistingBrowserExtensionLogging(t *testing.T) {
|
func Test_NotExistingBrowserExtensionLogging(t *testing.T) {
|
||||||
@ -35,5 +35,5 @@ func Test_NotExistingBrowserExtensionLogging(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
payload, _ := json.Marshal(log)
|
payload, _ := json.Marshal(log)
|
||||||
tests.DoAPISuccessPost(t, "/browser_extensions/"+someId.String()+"/commands/store_log", payload, nil)
|
e2e_tests.DoAPISuccessPost(t, "/browser_extensions/"+someId.String()+"/commands/store_log", payload, nil)
|
||||||
}
|
}
|
207
e2e-tests/browser_extension/browser_extension_pairing_test.go
Normal file
207
e2e-tests/browser_extension/browser_extension_pairing_test.go
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"github.com/twofas/2fas-server/e2e-tests"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBrowserExtensionPairingTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(BrowserExtensionPairingTestSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
type BrowserExtensionPairingTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BrowserExtensionPairingTestSuite) SetupTest() {
|
||||||
|
e2e_tests.RemoveAllBrowserExtensions(s.T())
|
||||||
|
e2e_tests.RemoveAllBrowserExtensionsDevices(s.T())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BrowserExtensionPairingTestSuite) TestPairBrowserExtensionWithMobileDevice() {
|
||||||
|
browserExt := e2e_tests.CreateBrowserExtension(s.T(), "go-test")
|
||||||
|
_, err := uuid.Parse(browserExt.Id)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
device, devicePubKey := e2e_tests.CreateDevice(s.T(), "go-test-device", "some-device-id")
|
||||||
|
_, err = uuid.Parse(device.Id)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExt, device)
|
||||||
|
|
||||||
|
var extensionDevice *e2e_tests.DevicePairedBrowserExtensionResponse
|
||||||
|
e2e_tests.DoAPISuccessGet(s.T(), "/browser_extensions/"+browserExt.Id+"/devices/"+device.Id, &extensionDevice)
|
||||||
|
|
||||||
|
assert.Equal(s.T(), extensionDevice.Id, device.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BrowserExtensionPairingTestSuite) TestDoNotFindNotPairedBrowserExtensionMobileDevice() {
|
||||||
|
browserExt := e2e_tests.CreateBrowserExtension(s.T(), "go-test")
|
||||||
|
_, err := uuid.Parse(browserExt.Id)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
device, _ := e2e_tests.CreateDevice(s.T(), "go-test-device", "some-device-id")
|
||||||
|
|
||||||
|
response := e2e_tests.DoAPIGet(s.T(), "/browser_extensions/"+browserExt.Id+"/devices/"+device.Id, nil)
|
||||||
|
|
||||||
|
assert.Equal(s.T(), 404, response.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BrowserExtensionPairingTestSuite) TestPairBrowserExtensionWithMultipleDevices() {
|
||||||
|
browserExt := e2e_tests.CreateBrowserExtension(s.T(), "go-test")
|
||||||
|
_, err := uuid.Parse(browserExt.Id)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
device1, devicePubKey1 := e2e_tests.CreateDevice(s.T(), "go-test-device-1", "some-device-id-1")
|
||||||
|
device2, devicePubKey2 := e2e_tests.CreateDevice(s.T(), "go-test-device-2", "some-device-id-2")
|
||||||
|
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey1, browserExt, device1)
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey2, browserExt, device2)
|
||||||
|
|
||||||
|
extensionDevices := e2e_tests.GetExtensionDevices(s.T(), browserExt.Id)
|
||||||
|
|
||||||
|
assert.Len(s.T(), extensionDevices, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BrowserExtensionPairingTestSuite) TestRemoveBrowserExtensionPairedDevice() {
|
||||||
|
browserExt := e2e_tests.CreateBrowserExtension(s.T(), "go-test")
|
||||||
|
|
||||||
|
device1, devicePubKey1 := e2e_tests.CreateDevice(s.T(), "go-test-device-1", "some-device-id-1")
|
||||||
|
device2, devicePubKey2 := e2e_tests.CreateDevice(s.T(), "go-test-device-2", "some-device-id-2")
|
||||||
|
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey1, browserExt, device1)
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey2, browserExt, device2)
|
||||||
|
|
||||||
|
extensionDevices := getExtensionPairedDevices(s.T(), browserExt)
|
||||||
|
assert.Len(s.T(), extensionDevices, 2)
|
||||||
|
|
||||||
|
e2e_tests.DoAPISuccessDelete(s.T(), "/browser_extensions/"+browserExt.Id+"/devices/"+device1.Id)
|
||||||
|
|
||||||
|
extensionDevices = getExtensionPairedDevices(s.T(), browserExt)
|
||||||
|
assert.Len(s.T(), extensionDevices, 1)
|
||||||
|
assert.Equal(s.T(), device2.Id, extensionDevices[0].Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BrowserExtensionPairingTestSuite) TestRemoveBrowserExtensionPairedDeviceTwice() {
|
||||||
|
browserExt := e2e_tests.CreateBrowserExtension(s.T(), "go-test")
|
||||||
|
|
||||||
|
device, devicePubKey := e2e_tests.CreateDevice(s.T(), "go-test-device-1", "some-device-id-1")
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExt, device)
|
||||||
|
|
||||||
|
e2e_tests.DoAPISuccessDelete(s.T(), "/browser_extensions/"+browserExt.Id+"/devices/"+device.Id)
|
||||||
|
response := e2e_tests.DoAPIRequest(s.T(), "/browser_extensions/"+browserExt.Id+"/devices/"+device.Id, http.MethodDelete, nil /*payload*/, nil /*resp*/)
|
||||||
|
|
||||||
|
assert.Equal(s.T(), 404, response.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BrowserExtensionPairingTestSuite) TestRemoveAllBrowserExtensionPairedDevices() {
|
||||||
|
browserExt := e2e_tests.CreateBrowserExtension(s.T(), "go-test")
|
||||||
|
device1, devicePubKey1 := e2e_tests.CreateDevice(s.T(), "go-test-device-1", "some-device-id1")
|
||||||
|
device2, devicePubKey2 := e2e_tests.CreateDevice(s.T(), "go-test-device-2", "some-device-id2")
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey1, browserExt, device1)
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey2, browserExt, device2)
|
||||||
|
|
||||||
|
e2e_tests.DoAPISuccessDelete(s.T(), "/browser_extensions/"+browserExt.Id+"/devices")
|
||||||
|
|
||||||
|
extensionDevices := e2e_tests.GetExtensionDevices(s.T(), browserExt.Id)
|
||||||
|
assert.Len(s.T(), extensionDevices, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BrowserExtensionPairingTestSuite) TestGetPairedDevicesWhichIDoNotOwn() {
|
||||||
|
browserExt1 := e2e_tests.CreateBrowserExtension(s.T(), "go-test-1")
|
||||||
|
browserExt2 := e2e_tests.CreateBrowserExtension(s.T(), "go-test-2")
|
||||||
|
|
||||||
|
device1, devicePubKey1 := e2e_tests.CreateDevice(s.T(), "go-test-device-1", "some-device-id-1")
|
||||||
|
device2, devicePubKey2 := e2e_tests.CreateDevice(s.T(), "go-test-device-2", "some-device-id-2")
|
||||||
|
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey1, browserExt1, device1)
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey2, browserExt2, device2)
|
||||||
|
|
||||||
|
firstExtensionDevices := getExtensionPairedDevices(s.T(), browserExt1)
|
||||||
|
assert.Len(s.T(), firstExtensionDevices, 1)
|
||||||
|
assert.Equal(s.T(), device1.Id, firstExtensionDevices[0].Id)
|
||||||
|
|
||||||
|
secondExtensionDevices := getExtensionPairedDevices(s.T(), browserExt2)
|
||||||
|
assert.Len(s.T(), secondExtensionDevices, 1)
|
||||||
|
assert.Equal(s.T(), device2.Id, secondExtensionDevices[0].Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BrowserExtensionPairingTestSuite) TestGetPairedDevicesByInvalidExtensionId() {
|
||||||
|
browserExt1 := e2e_tests.CreateBrowserExtension(s.T(), "go-test-1")
|
||||||
|
browserExt2 := e2e_tests.CreateBrowserExtension(s.T(), "go-test-2")
|
||||||
|
|
||||||
|
device1, devicePubKey1 := e2e_tests.CreateDevice(s.T(), "go-test-device-1", "some-device-id-1")
|
||||||
|
device2, devicePubKey2 := e2e_tests.CreateDevice(s.T(), "go-test-device-2", "some-device-id-2")
|
||||||
|
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey1, browserExt1, device1)
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey2, browserExt2, device2)
|
||||||
|
|
||||||
|
invalidResp := map[string]any{}
|
||||||
|
response := e2e_tests.DoAPIGet(s.T(), "/browser_extensions/some-invalid-id/devices/", &invalidResp)
|
||||||
|
assert.Equal(s.T(), 400, response.StatusCode)
|
||||||
|
assert.Contains(s.T(), invalidResp["Reason"], `Field validation for 'ExtensionId' failed on the 'uuid4'`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BrowserExtensionPairingTestSuite) TestGetPairedDevicesByNotExistingExtensionId() {
|
||||||
|
browserExt1 := e2e_tests.CreateBrowserExtension(s.T(), "go-test-1")
|
||||||
|
browserExt2 := e2e_tests.CreateBrowserExtension(s.T(), "go-test-2")
|
||||||
|
|
||||||
|
device1, devicePubKey1 := e2e_tests.CreateDevice(s.T(), "go-test-device-1", "some-device-id-1")
|
||||||
|
device2, devicePubKey2 := e2e_tests.CreateDevice(s.T(), "go-test-device-2", "some-device-id-2")
|
||||||
|
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey1, browserExt1, device1)
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey2, browserExt2, device2)
|
||||||
|
|
||||||
|
notExistingExtensionId := uuid.New()
|
||||||
|
var firstExtensionDevices []*e2e_tests.ExtensionPairedDeviceResponse
|
||||||
|
e2e_tests.DoAPISuccessGet(s.T(), "/browser_extensions/"+notExistingExtensionId.String()+"/devices/", &firstExtensionDevices)
|
||||||
|
assert.Len(s.T(), firstExtensionDevices, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BrowserExtensionPairingTestSuite) TestShareExtensionPublicKeyWithMobileDevice() {
|
||||||
|
browserExt := e2e_tests.CreateBrowserExtensionWithPublicKey(s.T(), "go-test", "b64-rsa-pub-key")
|
||||||
|
_, err := uuid.Parse(browserExt.Id)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
device, devicePubKey := e2e_tests.CreateDevice(s.T(), "go-test-device", "some-device-id")
|
||||||
|
_, err = uuid.Parse(device.Id)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
result := e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExt, device)
|
||||||
|
assert.Equal(s.T(), "b64-rsa-pub-key", result.ExtensionPublicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BrowserExtensionPairingTestSuite) TestCannotPairSameDeviceAndExtensionTwice() {
|
||||||
|
browserExtension := e2e_tests.CreateBrowserExtensionWithPublicKey(s.T(), "go-test", "b64-rsa-pub-key")
|
||||||
|
device, devicePubKey := e2e_tests.CreateDevice(s.T(), "go-test-device", "some-device-id")
|
||||||
|
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExtension, device)
|
||||||
|
|
||||||
|
payload := struct {
|
||||||
|
ExtensionId string `json:"extension_id"`
|
||||||
|
DeviceName string `json:"device_name"`
|
||||||
|
DevicePublicKey string `json:"device_public_key"`
|
||||||
|
}{
|
||||||
|
ExtensionId: browserExtension.Id,
|
||||||
|
DeviceName: device.Name,
|
||||||
|
DevicePublicKey: "device-pub-key",
|
||||||
|
}
|
||||||
|
|
||||||
|
pairingResult := new(e2e_tests.PairingResultResponse)
|
||||||
|
payloadJson, _ := json.Marshal(payload)
|
||||||
|
|
||||||
|
e2e_tests.DoAPIPostAndAssertCode(s.T(), 409, "/mobile/devices/"+device.Id+"/browser_extensions", payloadJson, pairingResult)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getExtensionPairedDevices(t *testing.T, browserExt *e2e_tests.BrowserExtensionResponse) []*e2e_tests.ExtensionPairedDeviceResponse {
|
||||||
|
var extensionDevices []*e2e_tests.ExtensionPairedDeviceResponse
|
||||||
|
e2e_tests.DoAPISuccessGet(t, "/browser_extensions/"+browserExt.Id+"/devices/", &extensionDevices)
|
||||||
|
return extensionDevices
|
||||||
|
}
|
@ -8,8 +8,8 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
"github.com/twofas/2fas-server/e2e-tests"
|
||||||
"github.com/twofas/2fas-server/internal/common/crypto"
|
"github.com/twofas/2fas-server/internal/common/crypto"
|
||||||
"github.com/twofas/2fas-server/tests"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBrowserExtensionTestSuite(t *testing.T) {
|
func TestBrowserExtensionTestSuite(t *testing.T) {
|
||||||
@ -21,7 +21,7 @@ type BrowserExtensionTestSuite struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *BrowserExtensionTestSuite) SetupTest() {
|
func (s *BrowserExtensionTestSuite) SetupTest() {
|
||||||
tests.RemoveAllBrowserExtensions(s.T())
|
e2e_tests.RemoveAllBrowserExtensions(s.T())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BrowserExtensionTestSuite) TestCreateBrowserExtension() {
|
func (s *BrowserExtensionTestSuite) TestCreateBrowserExtension() {
|
||||||
@ -47,13 +47,13 @@ func (s *BrowserExtensionTestSuite) TestCreateBrowserExtension() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *BrowserExtensionTestSuite) TestUpdateBrowserExtension() {
|
func (s *BrowserExtensionTestSuite) TestUpdateBrowserExtension() {
|
||||||
browserExt := tests.CreateBrowserExtension(s.T(), "go-test")
|
browserExt := e2e_tests.CreateBrowserExtension(s.T(), "go-test")
|
||||||
|
|
||||||
payload := []byte(`{"name": "updated-extension-name"}`)
|
payload := []byte(`{"name": "updated-extension-name"}`)
|
||||||
tests.DoAPISuccessPut(s.T(), "/browser_extensions/"+browserExt.Id, payload, nil)
|
e2e_tests.DoAPISuccessPut(s.T(), "/browser_extensions/"+browserExt.Id, payload, nil)
|
||||||
|
|
||||||
var browserExtension *tests.BrowserExtensionResponse
|
var browserExtension *e2e_tests.BrowserExtensionResponse
|
||||||
tests.DoAPISuccessGet(s.T(), "/browser_extensions/"+browserExt.Id, &browserExtension)
|
e2e_tests.DoAPISuccessGet(s.T(), "/browser_extensions/"+browserExt.Id, &browserExtension)
|
||||||
|
|
||||||
assert.Equal(s.T(), "updated-extension-name", browserExtension.Name)
|
assert.Equal(s.T(), "updated-extension-name", browserExtension.Name)
|
||||||
}
|
}
|
||||||
@ -62,16 +62,16 @@ func (s *BrowserExtensionTestSuite) TestUpdateNotExistingBrowserExtension() {
|
|||||||
id := uuid.New()
|
id := uuid.New()
|
||||||
|
|
||||||
payload := []byte(`{"name": "updated-extension-name"}`)
|
payload := []byte(`{"name": "updated-extension-name"}`)
|
||||||
response := tests.DoAPIRequest(s.T(), "/browser_extensions/"+id.String(), http.MethodPut, payload, nil)
|
response := e2e_tests.DoAPIRequest(s.T(), "/browser_extensions/"+id.String(), http.MethodPut, payload, nil)
|
||||||
|
|
||||||
assert.Equal(s.T(), 404, response.StatusCode)
|
assert.Equal(s.T(), 404, response.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BrowserExtensionTestSuite) TestUpdateBrowserExtensionSetEmptyName() {
|
func (s *BrowserExtensionTestSuite) TestUpdateBrowserExtensionSetEmptyName() {
|
||||||
browserExt := tests.CreateBrowserExtension(s.T(), "go-test")
|
browserExt := e2e_tests.CreateBrowserExtension(s.T(), "go-test")
|
||||||
|
|
||||||
payload := []byte(`{"name": ""}`)
|
payload := []byte(`{"name": ""}`)
|
||||||
response := tests.DoAPIRequest(s.T(), "/browser_extensions/"+browserExt.Id, http.MethodPut, payload, nil)
|
response := e2e_tests.DoAPIRequest(s.T(), "/browser_extensions/"+browserExt.Id, http.MethodPut, payload, nil)
|
||||||
|
|
||||||
assert.Equal(s.T(), 400, response.StatusCode)
|
assert.Equal(s.T(), 400, response.StatusCode)
|
||||||
}
|
}
|
||||||
@ -79,8 +79,8 @@ func (s *BrowserExtensionTestSuite) TestUpdateBrowserExtensionSetEmptyName() {
|
|||||||
func (s *BrowserExtensionTestSuite) TestDoNotFindNotExistingExtension() {
|
func (s *BrowserExtensionTestSuite) TestDoNotFindNotExistingExtension() {
|
||||||
notExistingId := uuid.New()
|
notExistingId := uuid.New()
|
||||||
|
|
||||||
var browserExtension *tests.BrowserExtensionResponse
|
var browserExtension *e2e_tests.BrowserExtensionResponse
|
||||||
response := tests.DoAPIGet(s.T(), "/browser_extensions/"+notExistingId.String(), &browserExtension)
|
response := e2e_tests.DoAPIGet(s.T(), "/browser_extensions/"+notExistingId.String(), &browserExtension)
|
||||||
|
|
||||||
assert.Equal(s.T(), 404, response.StatusCode)
|
assert.Equal(s.T(), 404, response.StatusCode)
|
||||||
}
|
}
|
||||||
@ -92,6 +92,6 @@ func createBrowserExtension(t *testing.T, name string) *http.Response {
|
|||||||
|
|
||||||
payload := []byte(fmt.Sprintf(`{"name":"%s","browser_name":"go-browser","browser_version":"0.1","public_key":"%s"}`, name, pubKey))
|
payload := []byte(fmt.Sprintf(`{"name":"%s","browser_name":"go-browser","browser_version":"0.1","public_key":"%s"}`, name, pubKey))
|
||||||
|
|
||||||
return tests.DoAPIRequest(t, "/browser_extensions", http.MethodPost, payload, nil)
|
return e2e_tests.DoAPIRequest(t, "/browser_extensions", http.MethodPost, payload, nil)
|
||||||
|
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package tests
|
package e2e_tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
@ -1,4 +1,4 @@
|
|||||||
package tests
|
package e2e_tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/twofas/2fas-server/tests"
|
"github.com/twofas/2fas-server/e2e-tests"
|
||||||
)
|
)
|
||||||
|
|
||||||
type iconsCollectionResponse struct {
|
type iconsCollectionResponse struct {
|
||||||
@ -26,7 +26,7 @@ type IconsCollectionsTestSuite struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *IconsCollectionsTestSuite) SetupTest() {
|
func (s *IconsCollectionsTestSuite) SetupTest() {
|
||||||
tests.RemoveAllMobileIconsCollections(s.T())
|
e2e_tests.RemoveAllMobileIconsCollections(s.T())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *IconsCollectionsTestSuite) TestCreateIconsCollection() {
|
func (s *IconsCollectionsTestSuite) TestCreateIconsCollection() {
|
||||||
@ -39,7 +39,7 @@ func (s *IconsCollectionsTestSuite) TestCreateIconsCollection() {
|
|||||||
`)
|
`)
|
||||||
|
|
||||||
var IconsCollection *iconsCollectionResponse
|
var IconsCollection *iconsCollectionResponse
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/icons/collections", payload, &IconsCollection)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/icons/collections", payload, &IconsCollection)
|
||||||
|
|
||||||
assert.Equal(s.T(), "facebook", IconsCollection.Name)
|
assert.Equal(s.T(), "facebook", IconsCollection.Name)
|
||||||
assert.Equal(s.T(), "desc", IconsCollection.Description)
|
assert.Equal(s.T(), "desc", IconsCollection.Description)
|
||||||
@ -55,7 +55,7 @@ func (s *IconsCollectionsTestSuite) TestUpdateIconsCollection() {
|
|||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
var iconsCollection *iconsCollectionResponse
|
var iconsCollection *iconsCollectionResponse
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/icons/collections", payload, &iconsCollection)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/icons/collections", payload, &iconsCollection)
|
||||||
|
|
||||||
updatePayload := []byte(`
|
updatePayload := []byte(`
|
||||||
{
|
{
|
||||||
@ -65,7 +65,7 @@ func (s *IconsCollectionsTestSuite) TestUpdateIconsCollection() {
|
|||||||
`)
|
`)
|
||||||
|
|
||||||
var updatedIconsCollection *iconsCollectionResponse
|
var updatedIconsCollection *iconsCollectionResponse
|
||||||
tests.DoAdminSuccessPut(s.T(), "mobile/icons/collections/"+iconsCollection.Id, updatePayload, &updatedIconsCollection)
|
e2e_tests.DoAdminSuccessPut(s.T(), "mobile/icons/collections/"+iconsCollection.Id, updatePayload, &updatedIconsCollection)
|
||||||
|
|
||||||
assert.Equal(s.T(), "meta", updatedIconsCollection.Name)
|
assert.Equal(s.T(), "meta", updatedIconsCollection.Name)
|
||||||
assert.Equal(s.T(), []string{"icon-1", "icon-2"}, updatedIconsCollection.Icons)
|
assert.Equal(s.T(), []string{"icon-1", "icon-2"}, updatedIconsCollection.Icons)
|
||||||
@ -79,11 +79,11 @@ func (s *IconsCollectionsTestSuite) TestDeleteIconsCollection() {
|
|||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
var iconsCollection *iconsCollectionResponse
|
var iconsCollection *iconsCollectionResponse
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/icons/collections", payload, &iconsCollection)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/icons/collections", payload, &iconsCollection)
|
||||||
|
|
||||||
tests.DoAdminSuccessDelete(s.T(), "mobile/icons/collections/"+iconsCollection.Id)
|
e2e_tests.DoAdminSuccessDelete(s.T(), "mobile/icons/collections/"+iconsCollection.Id)
|
||||||
|
|
||||||
response := tests.DoAPIGet(s.T(), "mobile/icons/collections/"+iconsCollection.Id, nil)
|
response := e2e_tests.DoAPIGet(s.T(), "mobile/icons/collections/"+iconsCollection.Id, nil)
|
||||||
assert.Equal(s.T(), 404, response.StatusCode)
|
assert.Equal(s.T(), 404, response.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +94,7 @@ func (s *IconsCollectionsTestSuite) TestFindAllIconsCollections() {
|
|||||||
"icons":["icon-1", "icon-2"]
|
"icons":["icon-1", "icon-2"]
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/icons/collections", payload, nil)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/icons/collections", payload, nil)
|
||||||
|
|
||||||
payload2 := []byte(`
|
payload2 := []byte(`
|
||||||
{
|
{
|
||||||
@ -103,10 +103,10 @@ func (s *IconsCollectionsTestSuite) TestFindAllIconsCollections() {
|
|||||||
"icons":["123e4567-e89b-12d3-a456-426614174000"]
|
"icons":["123e4567-e89b-12d3-a456-426614174000"]
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/icons/collections", payload2, nil)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/icons/collections", payload2, nil)
|
||||||
|
|
||||||
var IconsCollections []*iconsCollectionResponse
|
var IconsCollections []*iconsCollectionResponse
|
||||||
tests.DoAPISuccessGet(s.T(), "mobile/icons/collections", &IconsCollections)
|
e2e_tests.DoAPISuccessGet(s.T(), "mobile/icons/collections", &IconsCollections)
|
||||||
assert.Len(s.T(), IconsCollections, 2)
|
assert.Len(s.T(), IconsCollections, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,10 +119,10 @@ func (s *IconsCollectionsTestSuite) TestFindIconsCollection() {
|
|||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
var createdIconsCollection *iconsCollectionResponse
|
var createdIconsCollection *iconsCollectionResponse
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/icons/collections", payload, &createdIconsCollection)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/icons/collections", payload, &createdIconsCollection)
|
||||||
|
|
||||||
var IconsCollection *iconsCollectionResponse
|
var IconsCollection *iconsCollectionResponse
|
||||||
tests.DoAPISuccessGet(s.T(), "mobile/icons/collections/"+createdIconsCollection.Id, &IconsCollection)
|
e2e_tests.DoAPISuccessGet(s.T(), "mobile/icons/collections/"+createdIconsCollection.Id, &IconsCollection)
|
||||||
|
|
||||||
assert.Equal(s.T(), "just-one", IconsCollection.Name)
|
assert.Equal(s.T(), "just-one", IconsCollection.Name)
|
||||||
}
|
}
|
@ -9,8 +9,8 @@ import (
|
|||||||
"github.com/jaswdr/faker"
|
"github.com/jaswdr/faker"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
"github.com/twofas/2fas-server/e2e-tests"
|
||||||
"github.com/twofas/2fas-server/internal/api/icons/app/queries"
|
"github.com/twofas/2fas-server/internal/api/icons/app/queries"
|
||||||
"github.com/twofas/2fas-server/tests"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIconsRequestsTestSuite(t *testing.T) {
|
func TestIconsRequestsTestSuite(t *testing.T) {
|
||||||
@ -22,10 +22,10 @@ type IconsRequestsTestSuite struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *IconsRequestsTestSuite) SetupTest() {
|
func (s *IconsRequestsTestSuite) SetupTest() {
|
||||||
tests.RemoveAllMobileWebServices(s.T())
|
e2e_tests.RemoveAllMobileWebServices(s.T())
|
||||||
tests.RemoveAllMobileIcons(s.T())
|
e2e_tests.RemoveAllMobileIcons(s.T())
|
||||||
tests.RemoveAllMobileIconsCollections(s.T())
|
e2e_tests.RemoveAllMobileIconsCollections(s.T())
|
||||||
tests.RemoveAllMobileIconsRequests(s.T())
|
e2e_tests.RemoveAllMobileIconsRequests(s.T())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *IconsRequestsTestSuite) TestCreateIconRequest() {
|
func (s *IconsRequestsTestSuite) TestCreateIconRequest() {
|
||||||
@ -58,15 +58,15 @@ func (s *IconsRequestsTestSuite) TestCreateIconRequestWithNotAllowedIconDimensio
|
|||||||
|
|
||||||
var iconRequest *queries.IconRequestPresenter
|
var iconRequest *queries.IconRequestPresenter
|
||||||
|
|
||||||
tests.DoAPIPostAndAssertCode(s.T(), 400, "mobile/icons/requests", payload, &iconRequest)
|
e2e_tests.DoAPIPostAndAssertCode(s.T(), 400, "mobile/icons/requests", payload, &iconRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *IconsRequestsTestSuite) TestDeleteIconRequest() {
|
func (s *IconsRequestsTestSuite) TestDeleteIconRequest() {
|
||||||
iconRequest := createIconRequest(s.T(), "service")
|
iconRequest := createIconRequest(s.T(), "service")
|
||||||
|
|
||||||
tests.DoAdminSuccessDelete(s.T(), "mobile/icons/requests/"+iconRequest.Id)
|
e2e_tests.DoAdminSuccessDelete(s.T(), "mobile/icons/requests/"+iconRequest.Id)
|
||||||
|
|
||||||
response := tests.DoAPIGet(s.T(), "mobile/icons/requests/"+iconRequest.Id, nil)
|
response := e2e_tests.DoAPIGet(s.T(), "mobile/icons/requests/"+iconRequest.Id, nil)
|
||||||
assert.Equal(s.T(), 404, response.StatusCode)
|
assert.Equal(s.T(), 404, response.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ func (s *IconsRequestsTestSuite) TestFindAllIconsRequests() {
|
|||||||
createIconRequest(s.T(), "service2")
|
createIconRequest(s.T(), "service2")
|
||||||
|
|
||||||
var iconsRequests []*queries.IconRequestPresenter
|
var iconsRequests []*queries.IconRequestPresenter
|
||||||
tests.DoAPISuccessGet(s.T(), "mobile/icons/requests", &iconsRequests)
|
e2e_tests.DoAPISuccessGet(s.T(), "mobile/icons/requests", &iconsRequests)
|
||||||
|
|
||||||
assert.Len(s.T(), iconsRequests, 2)
|
assert.Len(s.T(), iconsRequests, 2)
|
||||||
}
|
}
|
||||||
@ -84,7 +84,7 @@ func (s *IconsRequestsTestSuite) TestFindIconRequest() {
|
|||||||
iconRequest := createIconRequest(s.T(), "service")
|
iconRequest := createIconRequest(s.T(), "service")
|
||||||
|
|
||||||
var searchResult *queries.IconPresenter
|
var searchResult *queries.IconPresenter
|
||||||
tests.DoAdminSuccessGet(s.T(), "mobile/icons/requests/"+iconRequest.Id, &searchResult)
|
e2e_tests.DoAdminSuccessGet(s.T(), "mobile/icons/requests/"+iconRequest.Id, &searchResult)
|
||||||
|
|
||||||
assert.Equal(s.T(), "service", searchResult.Name)
|
assert.Equal(s.T(), "service", searchResult.Name)
|
||||||
}
|
}
|
||||||
@ -93,7 +93,7 @@ func (s *IconsRequestsTestSuite) TestTransformIconRequestIntoWebService() {
|
|||||||
iconRequest := createIconRequest(s.T(), "service")
|
iconRequest := createIconRequest(s.T(), "service")
|
||||||
|
|
||||||
var result *queries.WebServicePresenter
|
var result *queries.WebServicePresenter
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/icons/requests/"+iconRequest.Id+"/commands/transform_to_web_service", nil, &result)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/icons/requests/"+iconRequest.Id+"/commands/transform_to_web_service", nil, &result)
|
||||||
|
|
||||||
assert.Equal(s.T(), "service", result.Name)
|
assert.Equal(s.T(), "service", result.Name)
|
||||||
}
|
}
|
||||||
@ -103,10 +103,10 @@ func (s *IconsRequestsTestSuite) TestTransformSingleIconRequestsIntoWebServiceFr
|
|||||||
createIconRequest(s.T(), "service")
|
createIconRequest(s.T(), "service")
|
||||||
|
|
||||||
var result *queries.WebServicePresenter
|
var result *queries.WebServicePresenter
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/icons/requests/"+iconRequest.Id+"/commands/transform_to_web_service", nil, &result)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/icons/requests/"+iconRequest.Id+"/commands/transform_to_web_service", nil, &result)
|
||||||
|
|
||||||
var icons []*queries.IconPresenter
|
var icons []*queries.IconPresenter
|
||||||
tests.DoAPIGet(s.T(), "mobile/icons", &icons)
|
e2e_tests.DoAPIGet(s.T(), "mobile/icons", &icons)
|
||||||
|
|
||||||
assert.Len(s.T(), icons, 1)
|
assert.Len(s.T(), icons, 1)
|
||||||
}
|
}
|
||||||
@ -116,7 +116,7 @@ func (s *IconsRequestsTestSuite) TestTransformIconRequestWithAlreadyExistingWebS
|
|||||||
iconRequest := createIconRequest(s.T(), webService.Name)
|
iconRequest := createIconRequest(s.T(), webService.Name)
|
||||||
|
|
||||||
var result *queries.WebServicePresenter
|
var result *queries.WebServicePresenter
|
||||||
tests.DoAdminPostAndAssertCode(s.T(), 409, "mobile/icons/requests/"+iconRequest.Id+"/commands/transform_to_web_service", nil, &result)
|
e2e_tests.DoAdminPostAndAssertCode(s.T(), 409, "mobile/icons/requests/"+iconRequest.Id+"/commands/transform_to_web_service", nil, &result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *IconsRequestsTestSuite) TestUpdateWebServiceFromIconRequest() {
|
func (s *IconsRequestsTestSuite) TestUpdateWebServiceFromIconRequest() {
|
||||||
@ -125,7 +125,7 @@ func (s *IconsRequestsTestSuite) TestUpdateWebServiceFromIconRequest() {
|
|||||||
|
|
||||||
var result *queries.WebServicePresenter
|
var result *queries.WebServicePresenter
|
||||||
payload := []byte(`{"web_service_id":"` + webService.Id + `"}`)
|
payload := []byte(`{"web_service_id":"` + webService.Id + `"}`)
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/icons/requests/"+iconRequest.Id+"/commands/update_web_service", payload, &result)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/icons/requests/"+iconRequest.Id+"/commands/update_web_service", payload, &result)
|
||||||
|
|
||||||
assert.Equal(s.T(), webService.Name, result.Name)
|
assert.Equal(s.T(), webService.Name, result.Name)
|
||||||
|
|
||||||
@ -163,7 +163,7 @@ func createIconRequest(t *testing.T, serviceName string) *queries.IconRequestPre
|
|||||||
|
|
||||||
var iconRequest *queries.IconRequestPresenter
|
var iconRequest *queries.IconRequestPresenter
|
||||||
|
|
||||||
tests.DoAPISuccessPost(t, "mobile/icons/requests", payload, &iconRequest)
|
e2e_tests.DoAPISuccessPost(t, "mobile/icons/requests", payload, &iconRequest)
|
||||||
|
|
||||||
return iconRequest
|
return iconRequest
|
||||||
}
|
}
|
@ -8,8 +8,8 @@ import (
|
|||||||
"github.com/jaswdr/faker"
|
"github.com/jaswdr/faker"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
"github.com/twofas/2fas-server/e2e-tests"
|
||||||
query "github.com/twofas/2fas-server/internal/api/icons/app/queries"
|
query "github.com/twofas/2fas-server/internal/api/icons/app/queries"
|
||||||
"github.com/twofas/2fas-server/tests"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func createIcon(t *testing.T) *query.IconPresenter {
|
func createIcon(t *testing.T) *query.IconPresenter {
|
||||||
@ -34,7 +34,7 @@ func createIcon(t *testing.T) *query.IconPresenter {
|
|||||||
|
|
||||||
var icon *query.IconPresenter
|
var icon *query.IconPresenter
|
||||||
|
|
||||||
tests.DoAdminAPISuccessPost(t, "mobile/icons", payload, &icon)
|
e2e_tests.DoAdminAPISuccessPost(t, "mobile/icons", payload, &icon)
|
||||||
|
|
||||||
return icon
|
return icon
|
||||||
}
|
}
|
||||||
@ -48,7 +48,7 @@ type IconsTestSuite struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *IconsTestSuite) SetupTest() {
|
func (s *IconsTestSuite) SetupTest() {
|
||||||
tests.DoAdminSuccessDelete(s.T(), "mobile/icons")
|
e2e_tests.DoAdminSuccessDelete(s.T(), "mobile/icons")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *IconsTestSuite) TestCreateIcon() {
|
func (s *IconsTestSuite) TestCreateIcon() {
|
||||||
@ -67,7 +67,7 @@ func (s *IconsTestSuite) TestUpdateIcon() {
|
|||||||
`)
|
`)
|
||||||
|
|
||||||
var updatedIcon *query.IconPresenter
|
var updatedIcon *query.IconPresenter
|
||||||
tests.DoAdminSuccessPut(s.T(), "mobile/icons/"+icon.Id, updatePayload, &updatedIcon)
|
e2e_tests.DoAdminSuccessPut(s.T(), "mobile/icons/"+icon.Id, updatePayload, &updatedIcon)
|
||||||
|
|
||||||
assert.Equal(s.T(), "meta", updatedIcon.Name)
|
assert.Equal(s.T(), "meta", updatedIcon.Name)
|
||||||
}
|
}
|
||||||
@ -75,9 +75,9 @@ func (s *IconsTestSuite) TestUpdateIcon() {
|
|||||||
func (s *IconsTestSuite) TestDeleteIcon() {
|
func (s *IconsTestSuite) TestDeleteIcon() {
|
||||||
icon := createIcon(s.T())
|
icon := createIcon(s.T())
|
||||||
|
|
||||||
tests.DoAdminSuccessDelete(s.T(), "mobile/icons/"+icon.Id)
|
e2e_tests.DoAdminSuccessDelete(s.T(), "mobile/icons/"+icon.Id)
|
||||||
|
|
||||||
response := tests.DoAPIGet(s.T(), "mobile/icons/"+icon.Id, nil)
|
response := e2e_tests.DoAPIGet(s.T(), "mobile/icons/"+icon.Id, nil)
|
||||||
assert.Equal(s.T(), 404, response.StatusCode)
|
assert.Equal(s.T(), 404, response.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ func (s *IconsTestSuite) TestFindAllIcons() {
|
|||||||
createIcon(s.T())
|
createIcon(s.T())
|
||||||
|
|
||||||
var Icons []*query.IconPresenter
|
var Icons []*query.IconPresenter
|
||||||
tests.DoAPISuccessGet(s.T(), "mobile/icons", &Icons)
|
e2e_tests.DoAPISuccessGet(s.T(), "mobile/icons", &Icons)
|
||||||
|
|
||||||
assert.Len(s.T(), Icons, 2)
|
assert.Len(s.T(), Icons, 2)
|
||||||
}
|
}
|
||||||
@ -95,7 +95,7 @@ func (s *IconsTestSuite) TestFindIcon() {
|
|||||||
icon := createIcon(s.T())
|
icon := createIcon(s.T())
|
||||||
|
|
||||||
var searchResult *query.IconPresenter
|
var searchResult *query.IconPresenter
|
||||||
tests.DoAPISuccessGet(s.T(), "mobile/icons/"+icon.Id, &searchResult)
|
e2e_tests.DoAPISuccessGet(s.T(), "mobile/icons/"+icon.Id, &searchResult)
|
||||||
|
|
||||||
assert.Equal(s.T(), "facebook", searchResult.Name)
|
assert.Equal(s.T(), "facebook", searchResult.Name)
|
||||||
}
|
}
|
@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/twofas/2fas-server/tests"
|
"github.com/twofas/2fas-server/e2e-tests"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWebServicesDumpTestSuite(t *testing.T) {
|
func TestWebServicesDumpTestSuite(t *testing.T) {
|
||||||
@ -19,16 +19,16 @@ type WebServicesDumpTestSuite struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebServicesDumpTestSuite) SetupTest() {
|
func (s *WebServicesDumpTestSuite) SetupTest() {
|
||||||
tests.RemoveAllMobileIcons(s.T())
|
e2e_tests.RemoveAllMobileIcons(s.T())
|
||||||
tests.RemoveAllMobileIconsCollections(s.T())
|
e2e_tests.RemoveAllMobileIconsCollections(s.T())
|
||||||
tests.RemoveAllMobileWebServices(s.T())
|
e2e_tests.RemoveAllMobileWebServices(s.T())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebServicesDumpTestSuite) TestWebServicesDump() {
|
func (s *WebServicesDumpTestSuite) TestWebServicesDump() {
|
||||||
createWebService(s.T())
|
createWebService(s.T())
|
||||||
createWebService(s.T())
|
createWebService(s.T())
|
||||||
|
|
||||||
response := tests.DoAPIGet(s.T(), "mobile/web_services/dump", nil)
|
response := e2e_tests.DoAPIGet(s.T(), "mobile/web_services/dump", nil)
|
||||||
|
|
||||||
assert.Equal(s.T(), 200, response.StatusCode)
|
assert.Equal(s.T(), 200, response.StatusCode)
|
||||||
}
|
}
|
||||||
@ -48,7 +48,7 @@ func createWebService(t *testing.T) *webServiceResponse {
|
|||||||
|
|
||||||
var webService *webServiceResponse
|
var webService *webServiceResponse
|
||||||
|
|
||||||
tests.DoAdminAPISuccessPost(t, "mobile/web_services", payload, &webService)
|
e2e_tests.DoAdminAPISuccessPost(t, "mobile/web_services", payload, &webService)
|
||||||
|
|
||||||
return webService
|
return webService
|
||||||
}
|
}
|
||||||
@ -66,7 +66,7 @@ func createIconsCollection(t *testing.T) *iconsCollectionResponse {
|
|||||||
|
|
||||||
var createdIconsCollection *iconsCollectionResponse
|
var createdIconsCollection *iconsCollectionResponse
|
||||||
|
|
||||||
tests.DoAdminAPISuccessPost(t, "mobile/icons/collections", payload, &createdIconsCollection)
|
e2e_tests.DoAdminAPISuccessPost(t, "mobile/icons/collections", payload, &createdIconsCollection)
|
||||||
|
|
||||||
return createdIconsCollection
|
return createdIconsCollection
|
||||||
}
|
}
|
@ -5,8 +5,8 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
"github.com/twofas/2fas-server/e2e-tests"
|
||||||
"github.com/twofas/2fas-server/internal/api/icons/app/command"
|
"github.com/twofas/2fas-server/internal/api/icons/app/command"
|
||||||
"github.com/twofas/2fas-server/tests"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type webServiceResponse struct {
|
type webServiceResponse struct {
|
||||||
@ -30,7 +30,7 @@ type WebServicesTestSuite struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebServicesTestSuite) SetupTest() {
|
func (s *WebServicesTestSuite) SetupTest() {
|
||||||
tests.RemoveAllMobileWebServices(s.T())
|
e2e_tests.RemoveAllMobileWebServices(s.T())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebServicesTestSuite) TestCreateWebService() {
|
func (s *WebServicesTestSuite) TestCreateWebService() {
|
||||||
@ -45,7 +45,7 @@ func (s *WebServicesTestSuite) TestCreateWebService() {
|
|||||||
`)
|
`)
|
||||||
|
|
||||||
var webService *webServiceResponse
|
var webService *webServiceResponse
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/web_services", payload, &webService)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/web_services", payload, &webService)
|
||||||
|
|
||||||
assert.Equal(s.T(), "facebook", webService.Name)
|
assert.Equal(s.T(), "facebook", webService.Name)
|
||||||
assert.Equal(s.T(), "desc", webService.Description)
|
assert.Equal(s.T(), "desc", webService.Description)
|
||||||
@ -65,8 +65,8 @@ func (s *WebServicesTestSuite) TestCreateWebServiceWithAlreadyExistingName() {
|
|||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
|
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/web_services", payload, nil)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/web_services", payload, nil)
|
||||||
tests.DoAdminPostAndAssertCode(s.T(), 409, "mobile/web_services", payload, nil)
|
e2e_tests.DoAdminPostAndAssertCode(s.T(), 409, "mobile/web_services", payload, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebServicesTestSuite) TestCreateWebServiceWithMatchRules() {
|
func (s *WebServicesTestSuite) TestCreateWebServiceWithMatchRules() {
|
||||||
@ -80,7 +80,7 @@ func (s *WebServicesTestSuite) TestCreateWebServiceWithMatchRules() {
|
|||||||
`)
|
`)
|
||||||
|
|
||||||
var webService *webServiceResponse
|
var webService *webServiceResponse
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/web_services", payload, &webService)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/web_services", payload, &webService)
|
||||||
|
|
||||||
assert.Equal(s.T(), []*command.MatchRule{{
|
assert.Equal(s.T(), []*command.MatchRule{{
|
||||||
Field: "label",
|
Field: "label",
|
||||||
@ -101,7 +101,7 @@ func (s *WebServicesTestSuite) TestUpdateWebService() {
|
|||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
var webService *webServiceResponse
|
var webService *webServiceResponse
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/web_services", payload, &webService)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/web_services", payload, &webService)
|
||||||
|
|
||||||
updatePayload := []byte(`{
|
updatePayload := []byte(`{
|
||||||
"name":"meta",
|
"name":"meta",
|
||||||
@ -112,7 +112,7 @@ func (s *WebServicesTestSuite) TestUpdateWebService() {
|
|||||||
`)
|
`)
|
||||||
|
|
||||||
var updatedWebService *webServiceResponse
|
var updatedWebService *webServiceResponse
|
||||||
tests.DoAdminSuccessPut(s.T(), "mobile/web_services/"+webService.Id, updatePayload, &updatedWebService)
|
e2e_tests.DoAdminSuccessPut(s.T(), "mobile/web_services/"+webService.Id, updatePayload, &updatedWebService)
|
||||||
|
|
||||||
assert.Equal(s.T(), "meta", updatedWebService.Name)
|
assert.Equal(s.T(), "meta", updatedWebService.Name)
|
||||||
assert.Equal(s.T(), []string{"meta", "facebook"}, updatedWebService.Issuers)
|
assert.Equal(s.T(), []string{"meta", "facebook"}, updatedWebService.Issuers)
|
||||||
@ -130,7 +130,7 @@ func (s *WebServicesTestSuite) TestUpdateWebServiceMatchRule() {
|
|||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
var webService *webServiceResponse
|
var webService *webServiceResponse
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/web_services", payload, &webService)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/web_services", payload, &webService)
|
||||||
|
|
||||||
updatePayload := []byte(`{
|
updatePayload := []byte(`{
|
||||||
"name":"meta",
|
"name":"meta",
|
||||||
@ -141,7 +141,7 @@ func (s *WebServicesTestSuite) TestUpdateWebServiceMatchRule() {
|
|||||||
`)
|
`)
|
||||||
|
|
||||||
var updatedWebService *webServiceResponse
|
var updatedWebService *webServiceResponse
|
||||||
tests.DoAdminSuccessPut(s.T(), "mobile/web_services/"+webService.Id, updatePayload, &updatedWebService)
|
e2e_tests.DoAdminSuccessPut(s.T(), "mobile/web_services/"+webService.Id, updatePayload, &updatedWebService)
|
||||||
|
|
||||||
assert.Equal(s.T(), "issuer", updatedWebService.MatchRules[0].Field)
|
assert.Equal(s.T(), "issuer", updatedWebService.MatchRules[0].Field)
|
||||||
assert.Equal(s.T(), "facebook.pl", updatedWebService.MatchRules[0].Text)
|
assert.Equal(s.T(), "facebook.pl", updatedWebService.MatchRules[0].Text)
|
||||||
@ -160,11 +160,11 @@ func (s *WebServicesTestSuite) TestDeleteWebService() {
|
|||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
var webService *webServiceResponse
|
var webService *webServiceResponse
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/web_services", payload, &webService)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/web_services", payload, &webService)
|
||||||
|
|
||||||
tests.DoAdminSuccessDelete(s.T(), "mobile/web_services/"+webService.Id)
|
e2e_tests.DoAdminSuccessDelete(s.T(), "mobile/web_services/"+webService.Id)
|
||||||
|
|
||||||
response := tests.DoAPIGet(s.T(), "mobile/web_services/"+webService.Id, nil)
|
response := e2e_tests.DoAPIGet(s.T(), "mobile/web_services/"+webService.Id, nil)
|
||||||
assert.Equal(s.T(), 404, response.StatusCode)
|
assert.Equal(s.T(), 404, response.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,7 +178,7 @@ func (s *WebServicesTestSuite) TestFindAllWebServices() {
|
|||||||
"icons_collections":["123e4567-e89b-12d3-a456-426614174000"]
|
"icons_collections":["123e4567-e89b-12d3-a456-426614174000"]
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/web_services", payload, nil)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/web_services", payload, nil)
|
||||||
|
|
||||||
payload2 := []byte(`
|
payload2 := []byte(`
|
||||||
{
|
{
|
||||||
@ -189,10 +189,10 @@ func (s *WebServicesTestSuite) TestFindAllWebServices() {
|
|||||||
"icons_collections":["123e4567-e89b-12d3-a456-426614174000"]
|
"icons_collections":["123e4567-e89b-12d3-a456-426614174000"]
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/web_services", payload2, nil)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/web_services", payload2, nil)
|
||||||
|
|
||||||
var webServices []*webServiceResponse
|
var webServices []*webServiceResponse
|
||||||
tests.DoAPISuccessGet(s.T(), "mobile/web_services", &webServices)
|
e2e_tests.DoAPISuccessGet(s.T(), "mobile/web_services", &webServices)
|
||||||
assert.Len(s.T(), webServices, 2)
|
assert.Len(s.T(), webServices, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,10 +207,10 @@ func (s *WebServicesTestSuite) TestFindWebService() {
|
|||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
var createdWebService *webServiceResponse
|
var createdWebService *webServiceResponse
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/web_services", payload, &createdWebService)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/web_services", payload, &createdWebService)
|
||||||
|
|
||||||
var webService *webServiceResponse
|
var webService *webServiceResponse
|
||||||
tests.DoAPISuccessGet(s.T(), "mobile/web_services/"+createdWebService.Id, &webService)
|
e2e_tests.DoAPISuccessGet(s.T(), "mobile/web_services/"+createdWebService.Id, &webService)
|
||||||
|
|
||||||
assert.Equal(s.T(), "just-one", webService.Name)
|
assert.Equal(s.T(), "just-one", webService.Name)
|
||||||
}
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"github.com/twofas/2fas-server/e2e-tests"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMobileDeviceExtensionIntegrationTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(MobileDeviceExtensionIntegrationTestSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
type MobileDeviceExtensionIntegrationTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MobileDeviceExtensionIntegrationTestSuite) SetupTest() {
|
||||||
|
e2e_tests.RemoveAllMobileDevices(s.T())
|
||||||
|
e2e_tests.RemoveAllBrowserExtensions(s.T())
|
||||||
|
e2e_tests.RemoveAllBrowserExtensionsDevices(s.T())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MobileDeviceExtensionIntegrationTestSuite) TestGetPending2FaRequests() {
|
||||||
|
device, devicePubKey := e2e_tests.CreateDevice(s.T(), "SM-955F", "fcm-token")
|
||||||
|
browserExtension := e2e_tests.CreateBrowserExtension(s.T(), "go-ext")
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExtension, device)
|
||||||
|
|
||||||
|
var tokenRequest *e2e_tests.AuthTokenRequestResponse
|
||||||
|
request2FaTokenPayload := []byte(`{"domain":"domain.com"}`)
|
||||||
|
e2e_tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/commands/request_2fa_token", request2FaTokenPayload, &tokenRequest)
|
||||||
|
|
||||||
|
var tokenRequestsCollection []*e2e_tests.AuthTokenRequestResponse
|
||||||
|
e2e_tests.DoAPISuccessGet(s.T(), "mobile/devices/"+device.Id+"/browser_extensions/2fa_requests", &tokenRequestsCollection)
|
||||||
|
assert.Len(s.T(), tokenRequestsCollection, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MobileDeviceExtensionIntegrationTestSuite) TestDoNotReturnCompleted2FaRequests() {
|
||||||
|
device, devicePubKey := e2e_tests.CreateDevice(s.T(), "SM-955F", "fcm-token")
|
||||||
|
browserExtension := e2e_tests.CreateBrowserExtension(s.T(), "go-ext")
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExtension, device)
|
||||||
|
|
||||||
|
var tokenRequest *e2e_tests.AuthTokenRequestResponse
|
||||||
|
request2FaTokenPayload := []byte(`{"domain":"domain.com"}`)
|
||||||
|
e2e_tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/commands/request_2fa_token", request2FaTokenPayload, &tokenRequest)
|
||||||
|
|
||||||
|
closeTokenRequestPayload := []byte(`{"status":"completed"}`)
|
||||||
|
e2e_tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/2fa_requests/"+tokenRequest.Id+"/commands/close_2fa_request", closeTokenRequestPayload, nil)
|
||||||
|
|
||||||
|
var tokenRequestsCollection []*e2e_tests.AuthTokenRequestResponse
|
||||||
|
e2e_tests.DoAPISuccessGet(s.T(), "mobile/devices/"+device.Id+"/browser_extensions/2fa_requests", &tokenRequestsCollection)
|
||||||
|
assert.Len(s.T(), tokenRequestsCollection, 0)
|
||||||
|
}
|
97
e2e-tests/mobile/mobile_device_extension_test.go
Normal file
97
e2e-tests/mobile/mobile_device_extension_test.go
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"github.com/twofas/2fas-server/e2e-tests"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMobileDeviceExtensionTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(MobileDeviceExtensionTestSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
type MobileDeviceExtensionTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MobileDeviceExtensionTestSuite) SetupTest() {
|
||||||
|
e2e_tests.RemoveAllMobileDevices(s.T())
|
||||||
|
e2e_tests.RemoveAllBrowserExtensions(s.T())
|
||||||
|
e2e_tests.RemoveAllBrowserExtensionsDevices(s.T())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MobileDeviceExtensionTestSuite) TestDoNotFindExtensionsForNotExistingDevice() {
|
||||||
|
notExistingDeviceId := uuid.New()
|
||||||
|
|
||||||
|
response := e2e_tests.DoAPIGet(s.T(), "/mobile/devices/"+notExistingDeviceId.String()+"/browser_extensions", nil)
|
||||||
|
|
||||||
|
assert.Equal(s.T(), 404, response.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MobileDeviceExtensionTestSuite) TestDoNotFindNotExistingMobileDeviceExtension() {
|
||||||
|
browserExt := e2e_tests.CreateBrowserExtension(s.T(), "go-test")
|
||||||
|
device, devicePubKey := e2e_tests.CreateDevice(s.T(), "go-test-device", "some-device-id")
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExt, device)
|
||||||
|
|
||||||
|
notExistingExtensionId := uuid.New()
|
||||||
|
response := e2e_tests.DoAPIGet(s.T(), "/mobile/devices/"+device.Id+"/browser_extensions/"+notExistingExtensionId.String(), nil)
|
||||||
|
|
||||||
|
assert.Equal(s.T(), 404, response.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MobileDeviceExtensionTestSuite) Test_FindExtensionForDevice() {
|
||||||
|
browserExt := e2e_tests.CreateBrowserExtension(s.T(), "go-test")
|
||||||
|
device, devicePubKey := e2e_tests.CreateDevice(s.T(), "go-test-device", "some-device-id")
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExt, device)
|
||||||
|
|
||||||
|
var deviceBrowserExtension *e2e_tests.BrowserExtensionResponse
|
||||||
|
e2e_tests.DoAPISuccessGet(s.T(), "/mobile/devices/"+device.Id+"/browser_extensions/"+browserExt.Id, &deviceBrowserExtension)
|
||||||
|
|
||||||
|
assert.Equal(s.T(), browserExt.Id, deviceBrowserExtension.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MobileDeviceExtensionTestSuite) Test_FindAllDeviceExtensions() {
|
||||||
|
browserExt1 := e2e_tests.CreateBrowserExtension(s.T(), "go-test-1")
|
||||||
|
browserExt2 := e2e_tests.CreateBrowserExtension(s.T(), "go-test-2")
|
||||||
|
device, devicePubKey := e2e_tests.CreateDevice(s.T(), "go-test-device", "some-device-id")
|
||||||
|
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExt1, device)
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExt2, device)
|
||||||
|
|
||||||
|
var deviceBrowserExtensions []*e2e_tests.BrowserExtensionResponse
|
||||||
|
e2e_tests.DoAPISuccessGet(s.T(), "/mobile/devices/"+device.Id+"/browser_extensions/", &deviceBrowserExtensions)
|
||||||
|
|
||||||
|
assert.Len(s.T(), deviceBrowserExtensions, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MobileDeviceExtensionTestSuite) Test_DisconnectExtensionFromDevice() {
|
||||||
|
browserExt1 := e2e_tests.CreateBrowserExtension(s.T(), "go-test")
|
||||||
|
browserExt2 := e2e_tests.CreateBrowserExtension(s.T(), "go-test")
|
||||||
|
device, devicePubKey := e2e_tests.CreateDevice(s.T(), "go-test-device", "some-device-id")
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExt1, device)
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExt2, device)
|
||||||
|
|
||||||
|
e2e_tests.DoAPISuccessDelete(s.T(), "/mobile/devices/"+device.Id+"/browser_extensions/"+browserExt1.Id)
|
||||||
|
|
||||||
|
var deviceBrowserExtension1 *e2e_tests.BrowserExtensionResponse
|
||||||
|
response := e2e_tests.DoAPIGet(s.T(), "/mobile/devices/"+device.Id+"/browser_extensions/"+browserExt1.Id, &deviceBrowserExtension1)
|
||||||
|
assert.Equal(s.T(), 404, response.StatusCode)
|
||||||
|
|
||||||
|
var deviceBrowserExtension2 *e2e_tests.BrowserExtensionResponse
|
||||||
|
e2e_tests.DoAPISuccessGet(s.T(), "/mobile/devices/"+device.Id+"/browser_extensions/"+browserExt2.Id, &deviceBrowserExtension2)
|
||||||
|
assert.Equal(s.T(), browserExt2.Id, deviceBrowserExtension2.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MobileDeviceExtensionTestSuite) TestExtensionHasAlreadyBeenConnected() {
|
||||||
|
extension := e2e_tests.CreateBrowserExtension(s.T(), "go-test")
|
||||||
|
device, devicePubKey := e2e_tests.CreateDevice(s.T(), "go-test-device", "some-device-id")
|
||||||
|
e2e_tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, extension, device)
|
||||||
|
|
||||||
|
payload := []byte(fmt.Sprintf(`{"extension_id":"%s","device_name":"%s","device_public_key":"%s"}`, extension.Id, device.Name, devicePubKey))
|
||||||
|
|
||||||
|
e2e_tests.DoAPIPostAndAssertCode(s.T(), 409, "/mobile/devices/"+device.Id+"/browser_extensions", payload, nil)
|
||||||
|
}
|
@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/twofas/2fas-server/tests"
|
"github.com/twofas/2fas-server/e2e-tests"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMobileDeviceTestSuite(t *testing.T) {
|
func TestMobileDeviceTestSuite(t *testing.T) {
|
||||||
@ -19,7 +19,7 @@ type MobileDeviceTestSuite struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *MobileDeviceTestSuite) SetupTest() {
|
func (s *MobileDeviceTestSuite) SetupTest() {
|
||||||
tests.RemoveAllMobileDevices(s.T())
|
e2e_tests.RemoveAllMobileDevices(s.T())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MobileDeviceTestSuite) TestCreateMobileDevice() {
|
func (s *MobileDeviceTestSuite) TestCreateMobileDevice() {
|
||||||
@ -49,5 +49,5 @@ func (s *MobileDeviceTestSuite) TestCreateMobileDevice() {
|
|||||||
|
|
||||||
func createDevice(t *testing.T, name, fcmToken string) *http.Response {
|
func createDevice(t *testing.T, name, fcmToken string) *http.Response {
|
||||||
payload := []byte(fmt.Sprintf(`{"name":"%s","platform":"android","fcm_token":"%s"}`, name, fcmToken))
|
payload := []byte(fmt.Sprintf(`{"name":"%s","platform":"android","fcm_token":"%s"}`, name, fcmToken))
|
||||||
return tests.DoAPIRequest(t, "mobile/devices", http.MethodPost, payload, nil)
|
return e2e_tests.DoAPIRequest(t, "mobile/devices", http.MethodPost, payload, nil)
|
||||||
}
|
}
|
@ -7,8 +7,8 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
"github.com/twofas/2fas-server/e2e-tests"
|
||||||
query "github.com/twofas/2fas-server/internal/api/mobile/app/queries"
|
query "github.com/twofas/2fas-server/internal/api/mobile/app/queries"
|
||||||
"github.com/twofas/2fas-server/tests"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMobileNotificationsTestSuite(t *testing.T) {
|
func TestMobileNotificationsTestSuite(t *testing.T) {
|
||||||
@ -20,7 +20,7 @@ type MobileNotificationsTestSuite struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *MobileNotificationsTestSuite) SetupTest() {
|
func (s *MobileNotificationsTestSuite) SetupTest() {
|
||||||
tests.RemoveAllMobileNotifications(s.T())
|
e2e_tests.RemoveAllMobileNotifications(s.T())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MobileNotificationsTestSuite) TestCreateMobileNotification() {
|
func (s *MobileNotificationsTestSuite) TestCreateMobileNotification() {
|
||||||
@ -28,7 +28,7 @@ func (s *MobileNotificationsTestSuite) TestCreateMobileNotification() {
|
|||||||
|
|
||||||
var notification *query.MobileNotificationPresenter
|
var notification *query.MobileNotificationPresenter
|
||||||
|
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/notifications", payload, ¬ification)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/notifications", payload, ¬ification)
|
||||||
|
|
||||||
assert.Equal(s.T(), "android", notification.Platform)
|
assert.Equal(s.T(), "android", notification.Platform)
|
||||||
assert.Equal(s.T(), "0.1", notification.Version)
|
assert.Equal(s.T(), "0.1", notification.Version)
|
||||||
@ -40,11 +40,11 @@ func (s *MobileNotificationsTestSuite) TestCreateMobileNotification() {
|
|||||||
func (s *MobileNotificationsTestSuite) TestUpdateMobileNotification() {
|
func (s *MobileNotificationsTestSuite) TestUpdateMobileNotification() {
|
||||||
payload := []byte(`{"icon":"features","platform":"android","link":"2fas.com","message":"demo","version":"0.1"}`)
|
payload := []byte(`{"icon":"features","platform":"android","link":"2fas.com","message":"demo","version":"0.1"}`)
|
||||||
var notification *query.MobileNotificationPresenter
|
var notification *query.MobileNotificationPresenter
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/notifications", payload, ¬ification)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/notifications", payload, ¬ification)
|
||||||
|
|
||||||
payload = []byte(`{"icon":"youtube","platform":"ios","link":"new-2fas.com","message":"new-demo","version":"1.1"}`)
|
payload = []byte(`{"icon":"youtube","platform":"ios","link":"new-2fas.com","message":"new-demo","version":"1.1"}`)
|
||||||
var updatedNotification *query.MobileNotificationPresenter
|
var updatedNotification *query.MobileNotificationPresenter
|
||||||
tests.DoAdminSuccessPut(s.T(), "mobile/notifications/"+notification.Id, payload, &updatedNotification)
|
e2e_tests.DoAdminSuccessPut(s.T(), "mobile/notifications/"+notification.Id, payload, &updatedNotification)
|
||||||
|
|
||||||
assert.Equal(s.T(), "ios", updatedNotification.Platform)
|
assert.Equal(s.T(), "ios", updatedNotification.Platform)
|
||||||
assert.Equal(s.T(), "1.1", updatedNotification.Version)
|
assert.Equal(s.T(), "1.1", updatedNotification.Version)
|
||||||
@ -56,18 +56,18 @@ func (s *MobileNotificationsTestSuite) TestUpdateMobileNotification() {
|
|||||||
func (s *MobileNotificationsTestSuite) TestDeleteMobileNotification() {
|
func (s *MobileNotificationsTestSuite) TestDeleteMobileNotification() {
|
||||||
payload := []byte(`{"icon":"features","platform":"android","link":"2fas.com","message":"demo","version":"0.1"}`)
|
payload := []byte(`{"icon":"features","platform":"android","link":"2fas.com","message":"demo","version":"0.1"}`)
|
||||||
var notification *query.MobileNotificationPresenter
|
var notification *query.MobileNotificationPresenter
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/notifications", payload, ¬ification)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/notifications", payload, ¬ification)
|
||||||
|
|
||||||
tests.DoAdminSuccessDelete(s.T(), "mobile/notifications/"+notification.Id)
|
e2e_tests.DoAdminSuccessDelete(s.T(), "mobile/notifications/"+notification.Id)
|
||||||
|
|
||||||
response := tests.DoAPIGet(s.T(), "mobile/notifications/"+notification.Id, nil)
|
response := e2e_tests.DoAPIGet(s.T(), "mobile/notifications/"+notification.Id, nil)
|
||||||
assert.Equal(s.T(), 404, response.StatusCode)
|
assert.Equal(s.T(), 404, response.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MobileNotificationsTestSuite) TestDeleteNotExistingMobileNotification() {
|
func (s *MobileNotificationsTestSuite) TestDeleteNotExistingMobileNotification() {
|
||||||
id := uuid.New()
|
id := uuid.New()
|
||||||
|
|
||||||
response := tests.DoAPIRequest(s.T(), "mobile/notifications/"+id.String(), http.MethodDelete, nil /*payload*/, nil /*resp*/)
|
response := e2e_tests.DoAPIRequest(s.T(), "mobile/notifications/"+id.String(), http.MethodDelete, nil /*payload*/, nil /*resp*/)
|
||||||
|
|
||||||
assert.Equal(s.T(), 404, response.StatusCode)
|
assert.Equal(s.T(), 404, response.StatusCode)
|
||||||
}
|
}
|
||||||
@ -75,14 +75,14 @@ func (s *MobileNotificationsTestSuite) TestDeleteNotExistingMobileNotification()
|
|||||||
func (s *MobileNotificationsTestSuite) TestFindAllNotifications() {
|
func (s *MobileNotificationsTestSuite) TestFindAllNotifications() {
|
||||||
payload1 := []byte(`{"icon":"features","platform":"android","link":"2fas.com","message":"demo","version":"0.1"}`)
|
payload1 := []byte(`{"icon":"features","platform":"android","link":"2fas.com","message":"demo","version":"0.1"}`)
|
||||||
var notification1 *query.MobileNotificationPresenter
|
var notification1 *query.MobileNotificationPresenter
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/notifications", payload1, ¬ification1)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/notifications", payload1, ¬ification1)
|
||||||
|
|
||||||
payload2 := []byte(`{"icon":"youtube","platform":"android","link":"2fas.com","message":"demo2","version":"1.1"}`)
|
payload2 := []byte(`{"icon":"youtube","platform":"android","link":"2fas.com","message":"demo2","version":"1.1"}`)
|
||||||
var notification2 *query.MobileNotificationPresenter
|
var notification2 *query.MobileNotificationPresenter
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/notifications", payload2, ¬ification2)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/notifications", payload2, ¬ification2)
|
||||||
|
|
||||||
var collection []*query.MobileNotificationPresenter
|
var collection []*query.MobileNotificationPresenter
|
||||||
tests.DoAPISuccessGet(s.T(), "mobile/notifications", &collection)
|
e2e_tests.DoAPISuccessGet(s.T(), "mobile/notifications", &collection)
|
||||||
|
|
||||||
assert.Len(s.T(), collection, 2)
|
assert.Len(s.T(), collection, 2)
|
||||||
}
|
}
|
||||||
@ -90,7 +90,7 @@ func (s *MobileNotificationsTestSuite) TestFindAllNotifications() {
|
|||||||
func (s *MobileNotificationsTestSuite) TestDoNotFindNotifications() {
|
func (s *MobileNotificationsTestSuite) TestDoNotFindNotifications() {
|
||||||
var collection []*query.MobileNotificationPresenter
|
var collection []*query.MobileNotificationPresenter
|
||||||
|
|
||||||
tests.DoAPISuccessGet(s.T(), "mobile/notifications", &collection)
|
e2e_tests.DoAPISuccessGet(s.T(), "mobile/notifications", &collection)
|
||||||
|
|
||||||
assert.Len(s.T(), collection, 0)
|
assert.Len(s.T(), collection, 0)
|
||||||
}
|
}
|
||||||
@ -98,10 +98,10 @@ func (s *MobileNotificationsTestSuite) TestDoNotFindNotifications() {
|
|||||||
func (s *MobileNotificationsTestSuite) TestPublishNotification() {
|
func (s *MobileNotificationsTestSuite) TestPublishNotification() {
|
||||||
payload := []byte(`{"icon":"features","platform":"android","link":"2fas.com","message":"demo","version":"0.1"}`)
|
payload := []byte(`{"icon":"features","platform":"android","link":"2fas.com","message":"demo","version":"0.1"}`)
|
||||||
var notification *query.MobileNotificationPresenter
|
var notification *query.MobileNotificationPresenter
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/notifications", payload, ¬ification)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/notifications", payload, ¬ification)
|
||||||
|
|
||||||
var publishedNotification *query.MobileNotificationPresenter
|
var publishedNotification *query.MobileNotificationPresenter
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/notifications/"+notification.Id+"/commands/publish", payload, &publishedNotification)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/notifications/"+notification.Id+"/commands/publish", payload, &publishedNotification)
|
||||||
|
|
||||||
assert.NotEmpty(s.T(), "published_at", notification.PublishedAt)
|
assert.NotEmpty(s.T(), "published_at", notification.PublishedAt)
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/twofas/2fas-server/tests"
|
"github.com/twofas/2fas-server/e2e-tests"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ func Test_MobileApiBandwidthAbuse(t *testing.T) {
|
|||||||
eg.SetLimit(noOfWorkers)
|
eg.SetLimit(noOfWorkers)
|
||||||
for i := 0; i < noOfRequest; i++ {
|
for i := 0; i < noOfRequest; i++ {
|
||||||
eg.Go(func() error {
|
eg.Go(func() error {
|
||||||
resp := tests.DoAPIGet(t, "/mobile/devices/"+someId.String()+"/browser_extensions", nil)
|
resp := e2e_tests.DoAPIGet(t, "/mobile/devices/"+someId.String()+"/browser_extensions", nil)
|
||||||
|
|
||||||
responseCh <- resp.StatusCode
|
responseCh <- resp.StatusCode
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ func Test_BrowserExtensionApiBandwidthAbuse(t *testing.T) {
|
|||||||
eg.SetLimit(noOfWorkers)
|
eg.SetLimit(noOfWorkers)
|
||||||
for i := 0; i < noOfRequest; i++ {
|
for i := 0; i < noOfRequest; i++ {
|
||||||
eg.Go(func() error {
|
eg.Go(func() error {
|
||||||
resp := tests.DoAPIGet(t, "/browser_extensions/"+someId.String(), nil)
|
resp := e2e_tests.DoAPIGet(t, "/browser_extensions/"+someId.String(), nil)
|
||||||
|
|
||||||
responseCh <- resp.StatusCode
|
responseCh <- resp.StatusCode
|
||||||
|
|
@ -106,7 +106,7 @@ func getMobileToken(fcm string) (string, error) {
|
|||||||
MobileSyncConfirmToken string `json:"mobile_sync_confirm_token"`
|
MobileSyncConfirmToken string `json:"mobile_sync_confirm_token"`
|
||||||
}
|
}
|
||||||
if err := request("GET", fmt.Sprintf("/mobile/sync/%s/token", fcm), "", nil, &resp); err != nil {
|
if err := request("GET", fmt.Sprintf("/mobile/sync/%s/token", fcm), "", nil, &resp); err != nil {
|
||||||
return "", fmt.Errorf("failed to get mobile token")
|
return "", fmt.Errorf("failed to get mobile token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp.MobileSyncConfirmToken, nil
|
return resp.MobileSyncConfirmToken, nil
|
@ -1,4 +1,4 @@
|
|||||||
package tests
|
package e2e_tests
|
||||||
|
|
||||||
type DeviceResponse struct {
|
type DeviceResponse struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
50
e2e-tests/scripts/wait-ready/main.go
Normal file
50
e2e-tests/scripts/wait-ready/main.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
addrFlag := flag.String("addr", ":80;:8081;:8082", "list of addresses to check sep by ;")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
addresses := strings.Split(*addrFlag, ";")
|
||||||
|
if len(addresses) < 1 {
|
||||||
|
log.Fatal("-addr value not provided")
|
||||||
|
}
|
||||||
|
for _, address := range addresses {
|
||||||
|
running := waitForApp(address, 30*time.Second)
|
||||||
|
if !running {
|
||||||
|
log.Fatal("App not running on addr: ", address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// waitForApp returns true if app is listening on provided address.
|
||||||
|
// If it cannot connect up to specified timeout, it returns false.
|
||||||
|
func waitForApp(address string, timeout time.Duration) bool {
|
||||||
|
done := make(chan struct{})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
_, err := net.DialTimeout("tcp", address, time.Second)
|
||||||
|
if err != nil {
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
close(done)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
timeoutCh := time.After(timeout)
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
return true
|
||||||
|
case <-timeoutCh:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
@ -12,8 +12,8 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
"github.com/twofas/2fas-server/e2e-tests"
|
||||||
query "github.com/twofas/2fas-server/internal/api/support/app/queries"
|
query "github.com/twofas/2fas-server/internal/api/support/app/queries"
|
||||||
"github.com/twofas/2fas-server/tests"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDebugLogsAuditTestSuite(t *testing.T) {
|
func TestDebugLogsAuditTestSuite(t *testing.T) {
|
||||||
@ -25,7 +25,7 @@ type DebugLogsAuditTestSuite struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *DebugLogsAuditTestSuite) SetupTest() {
|
func (s *DebugLogsAuditTestSuite) SetupTest() {
|
||||||
tests.DoAdminSuccessDelete(s.T(), "mobile/support/debug_logs/audit")
|
e2e_tests.DoAdminSuccessDelete(s.T(), "mobile/support/debug_logs/audit")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DebugLogsAuditTestSuite) TestCreateDebugLogsAuditClaim() {
|
func (s *DebugLogsAuditTestSuite) TestCreateDebugLogsAuditClaim() {
|
||||||
@ -33,7 +33,7 @@ func (s *DebugLogsAuditTestSuite) TestCreateDebugLogsAuditClaim() {
|
|||||||
|
|
||||||
auditClaim := new(query.DebugLogsAuditPresenter)
|
auditClaim := new(query.DebugLogsAuditPresenter)
|
||||||
|
|
||||||
tests.DoAdminAPISuccessPost(s.T(), "mobile/support/debug_logs/audit/claim", payload, auditClaim)
|
e2e_tests.DoAdminAPISuccessPost(s.T(), "mobile/support/debug_logs/audit/claim", payload, auditClaim)
|
||||||
|
|
||||||
assert.Equal(s.T(), "app-user", auditClaim.Username)
|
assert.Equal(s.T(), "app-user", auditClaim.Username)
|
||||||
assert.Equal(s.T(), "some description", auditClaim.Description)
|
assert.Equal(s.T(), "some description", auditClaim.Description)
|
||||||
@ -44,7 +44,7 @@ func (s *DebugLogsAuditTestSuite) TestUpdateDebugLogsAuditClaim() {
|
|||||||
|
|
||||||
var updatedAuditClaim *query.DebugLogsAuditPresenter
|
var updatedAuditClaim *query.DebugLogsAuditPresenter
|
||||||
updatePayload := []byte(`{"username": "app-user-1", "description": "another description"}`)
|
updatePayload := []byte(`{"username": "app-user-1", "description": "another description"}`)
|
||||||
tests.DoAdminSuccessPut(s.T(), "mobile/support/debug_logs/audit/claim/"+auditClaim.Id, updatePayload, &updatedAuditClaim)
|
e2e_tests.DoAdminSuccessPut(s.T(), "mobile/support/debug_logs/audit/claim/"+auditClaim.Id, updatePayload, &updatedAuditClaim)
|
||||||
|
|
||||||
assert.Equal(s.T(), "app-user-1", updatedAuditClaim.Username)
|
assert.Equal(s.T(), "app-user-1", updatedAuditClaim.Username)
|
||||||
assert.Equal(s.T(), "another description", updatedAuditClaim.Description)
|
assert.Equal(s.T(), "another description", updatedAuditClaim.Description)
|
||||||
@ -121,7 +121,7 @@ func (s *DebugLogsAuditTestSuite) TestGetDebugLogsAudit() {
|
|||||||
auditClaim := createDebugLogsAuditClaim(s.T(), "user1", "desc1")
|
auditClaim := createDebugLogsAuditClaim(s.T(), "user1", "desc1")
|
||||||
|
|
||||||
audit := new(query.DebugLogsAuditPresenter)
|
audit := new(query.DebugLogsAuditPresenter)
|
||||||
tests.DoAdminSuccessGet(s.T(), "mobile/support/debug_logs/audit/"+auditClaim.Id, audit)
|
e2e_tests.DoAdminSuccessGet(s.T(), "mobile/support/debug_logs/audit/"+auditClaim.Id, audit)
|
||||||
|
|
||||||
assert.Equal(s.T(), auditClaim.Id, audit.Id)
|
assert.Equal(s.T(), auditClaim.Id, audit.Id)
|
||||||
assert.Equal(s.T(), "user1", audit.Username)
|
assert.Equal(s.T(), "user1", audit.Username)
|
||||||
@ -131,9 +131,9 @@ func (s *DebugLogsAuditTestSuite) TestGetDebugLogsAudit() {
|
|||||||
func (s *DebugLogsAuditTestSuite) TestDeleteDebugLogsAudit() {
|
func (s *DebugLogsAuditTestSuite) TestDeleteDebugLogsAudit() {
|
||||||
auditClaim := createDebugLogsAuditClaim(s.T(), "user1", "desc1")
|
auditClaim := createDebugLogsAuditClaim(s.T(), "user1", "desc1")
|
||||||
|
|
||||||
tests.DoAdminSuccessDelete(s.T(), "mobile/support/debug_logs/audit/"+auditClaim.Id)
|
e2e_tests.DoAdminSuccessDelete(s.T(), "mobile/support/debug_logs/audit/"+auditClaim.Id)
|
||||||
|
|
||||||
response := tests.DoAPIGet(s.T(), "mobile/support/debug_logs/audit/"+auditClaim.Id, nil)
|
response := e2e_tests.DoAPIGet(s.T(), "mobile/support/debug_logs/audit/"+auditClaim.Id, nil)
|
||||||
assert.Equal(s.T(), 404, response.StatusCode)
|
assert.Equal(s.T(), 404, response.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ func (s *DebugLogsAuditTestSuite) TestFindAllDebugLogsAudit() {
|
|||||||
createDebugLogsAuditClaim(s.T(), "user2", "desc2")
|
createDebugLogsAuditClaim(s.T(), "user2", "desc2")
|
||||||
|
|
||||||
var audits []*query.DebugLogsAuditPresenter
|
var audits []*query.DebugLogsAuditPresenter
|
||||||
tests.DoAdminSuccessGet(s.T(), "mobile/support/debug_logs/audit", &audits)
|
e2e_tests.DoAdminSuccessGet(s.T(), "mobile/support/debug_logs/audit", &audits)
|
||||||
|
|
||||||
assert.Len(s.T(), audits, 2)
|
assert.Len(s.T(), audits, 2)
|
||||||
}
|
}
|
||||||
@ -151,7 +151,7 @@ func createDebugLogsAuditClaim(t *testing.T, username, description string) *quer
|
|||||||
payload := []byte(`{"username": "` + username + `", "description": "` + description + `"}`)
|
payload := []byte(`{"username": "` + username + `", "description": "` + description + `"}`)
|
||||||
|
|
||||||
auditClaim := new(query.DebugLogsAuditPresenter)
|
auditClaim := new(query.DebugLogsAuditPresenter)
|
||||||
tests.DoAdminAPISuccessPost(t, "mobile/support/debug_logs/audit/claim", payload, auditClaim)
|
e2e_tests.DoAdminAPISuccessPost(t, "mobile/support/debug_logs/audit/claim", payload, auditClaim)
|
||||||
|
|
||||||
return auditClaim
|
return auditClaim
|
||||||
}
|
}
|
@ -6,11 +6,11 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/twofas/2fas-server/tests"
|
"github.com/twofas/2fas-server/e2e-tests"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_Default404Response(t *testing.T) {
|
func Test_Default404Response(t *testing.T) {
|
||||||
response := tests.DoAPIGet(t, "some/not/existing/endpoint", nil)
|
response := e2e_tests.DoAPIGet(t, "some/not/existing/endpoint", nil)
|
||||||
|
|
||||||
rawBody, err := io.ReadAll(response.Body)
|
rawBody, err := io.ReadAll(response.Body)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
@ -1,4 +1,4 @@
|
|||||||
package tests
|
package e2e_tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
@ -58,7 +58,8 @@ type Request2FaTokenHandler struct {
|
|||||||
Pusher push.Pusher
|
Pusher push.Pusher
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Request2FaTokenHandler) Handle(cmd *Request2FaToken) error {
|
func (h *Request2FaTokenHandler) Handle(ctx context.Context, cmd *Request2FaToken) error {
|
||||||
|
log := logging.FromContext(ctx)
|
||||||
extId, _ := uuid.Parse(cmd.ExtensionId)
|
extId, _ := uuid.Parse(cmd.ExtensionId)
|
||||||
|
|
||||||
browserExtension, err := h.BrowserExtensionsRepository.FindById(extId)
|
browserExtension, err := h.BrowserExtensionsRepository.FindById(extId)
|
||||||
@ -87,7 +88,7 @@ func (h *Request2FaTokenHandler) Handle(cmd *Request2FaToken) error {
|
|||||||
|
|
||||||
for _, device := range pairedDevices {
|
for _, device := range pairedDevices {
|
||||||
if device.FcmToken == "" {
|
if device.FcmToken == "" {
|
||||||
logging.WithFields(logging.Fields{
|
log.WithFields(logging.Fields{
|
||||||
"extension_id": extId.String(),
|
"extension_id": extId.String(),
|
||||||
"device_id": device.Id.String(),
|
"device_id": device.Id.String(),
|
||||||
"token_request_id": cmd.Id,
|
"token_request_id": cmd.Id,
|
||||||
@ -117,7 +118,7 @@ func (h *Request2FaTokenHandler) Handle(cmd *Request2FaToken) error {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if err != nil && !messaging.IsUnregistered(err) {
|
if err != nil && !messaging.IsUnregistered(err) {
|
||||||
logging.WithFields(logging.Fields{
|
log.WithFields(logging.Fields{
|
||||||
"extension_id": extId.String(),
|
"extension_id": extId.String(),
|
||||||
"device_id": device.Id.String(),
|
"device_id": device.Id.String(),
|
||||||
"token_request_id": cmd.Id,
|
"token_request_id": cmd.Id,
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/twofas/2fas-server/internal/api/browser_extension/domain"
|
"github.com/twofas/2fas-server/internal/api/browser_extension/domain"
|
||||||
"github.com/twofas/2fas-server/internal/common/logging"
|
"github.com/twofas/2fas-server/internal/common/logging"
|
||||||
@ -18,7 +20,7 @@ type StoreLogEventHandler struct {
|
|||||||
BrowserExtensionsRepository domain.BrowserExtensionRepository
|
BrowserExtensionsRepository domain.BrowserExtensionRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *StoreLogEventHandler) Handle(cmd *StoreLogEvent) {
|
func (h *StoreLogEventHandler) Handle(ctx context.Context, cmd *StoreLogEvent) {
|
||||||
extId, _ := uuid.Parse(cmd.ExtensionId)
|
extId, _ := uuid.Parse(cmd.ExtensionId)
|
||||||
|
|
||||||
_, err := h.BrowserExtensionsRepository.FindById(extId)
|
_, err := h.BrowserExtensionsRepository.FindById(extId)
|
||||||
@ -35,12 +37,12 @@ func (h *StoreLogEventHandler) Handle(cmd *StoreLogEvent) {
|
|||||||
|
|
||||||
switch cmd.Level {
|
switch cmd.Level {
|
||||||
case "info":
|
case "info":
|
||||||
logging.WithFields(context).Info(cmd.Message)
|
logging.FromContext(ctx).WithFields(context).Info(cmd.Message)
|
||||||
case "warning":
|
case "warning":
|
||||||
logging.WithFields(context).Warning(cmd.Message)
|
logging.FromContext(ctx).WithFields(context).Warning(cmd.Message)
|
||||||
case "error":
|
case "error":
|
||||||
logging.WithFields(context).Error(cmd.Message)
|
logging.FromContext(ctx).WithFields(context).Error(cmd.Message)
|
||||||
case "debug":
|
case "debug":
|
||||||
logging.WithFields(context).Debug(cmd.Message)
|
logging.FromContext(ctx).WithFields(context).Debug(cmd.Message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ func BrowserExtensionBandwidthAuditMiddleware(rateLimiter rate_limit.RateLimiter
|
|||||||
limitReached := rateLimiter.Test(c, key, rate)
|
limitReached := rateLimiter.Test(c, key, rate)
|
||||||
|
|
||||||
if limitReached {
|
if limitReached {
|
||||||
logging.WithFields(logging.Fields{
|
logging.FromContext(c.Request.Context()).WithFields(logging.Fields{
|
||||||
"type": "security",
|
"type": "security",
|
||||||
"uri": c.Request.URL.String(),
|
"uri": c.Request.URL.String(),
|
||||||
"browser_extension_id": extensionId,
|
"browser_extension_id": extensionId,
|
||||||
|
@ -43,7 +43,7 @@ func (r *RoutesHandler) Log(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
r.cqrs.Commands.StoreLogEvent.Handle(cmd)
|
r.cqrs.Commands.StoreLogEvent.Handle(c.Request.Context(), cmd)
|
||||||
|
|
||||||
c.JSON(200, api.NewOk("Log has been stored"))
|
c.JSON(200, api.NewOk("Log has been stored"))
|
||||||
}
|
}
|
||||||
@ -311,7 +311,7 @@ func (r *RoutesHandler) Request2FaToken(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = r.cqrs.Commands.Request2FaToken.Handle(cmd)
|
err = r.cqrs.Commands.Request2FaToken.Handle(c.Request.Context(), cmd)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(500, api.NewInternalServerError(err))
|
c.JSON(500, api.NewInternalServerError(err))
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/avast/retry-go/v4"
|
"github.com/avast/retry-go/v4"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
|
||||||
"github.com/twofas/2fas-server/internal/api/browser_extension/domain"
|
"github.com/twofas/2fas-server/internal/api/browser_extension/domain"
|
||||||
"github.com/twofas/2fas-server/internal/api/mobile/adapters"
|
"github.com/twofas/2fas-server/internal/api/mobile/adapters"
|
||||||
"github.com/twofas/2fas-server/internal/common/logging"
|
"github.com/twofas/2fas-server/internal/common/logging"
|
||||||
@ -41,15 +44,14 @@ type Send2FaTokenHandler struct {
|
|||||||
WebsocketClient *websocket.WebsocketApiClient
|
WebsocketClient *websocket.WebsocketApiClient
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Send2FaTokenHandler) Handle(cmd *Send2FaToken) error {
|
func (h *Send2FaTokenHandler) Handle(ctx context.Context, cmd *Send2FaToken) error {
|
||||||
extId, _ := uuid.Parse(cmd.ExtensionId)
|
extId, _ := uuid.Parse(cmd.ExtensionId)
|
||||||
|
log := logging.FromContext(ctx).WithFields(logging.Fields{
|
||||||
logging.WithFields(logging.Fields{
|
|
||||||
"browser_extension_id": cmd.ExtensionId,
|
"browser_extension_id": cmd.ExtensionId,
|
||||||
"device_id": cmd.DeviceId,
|
"device_id": cmd.DeviceId,
|
||||||
"token": cmd.Token,
|
|
||||||
"token_request_id": cmd.TokenRequestId,
|
"token_request_id": cmd.TokenRequestId,
|
||||||
}).Info("Start command `Send2FaToken`")
|
})
|
||||||
|
log.Info("Start command `Send2FaToken`")
|
||||||
|
|
||||||
browserExtension, err := h.BrowserExtensionsRepository.FindById(extId)
|
browserExtension, err := h.BrowserExtensionsRepository.FindById(extId)
|
||||||
|
|
||||||
@ -70,9 +72,9 @@ func (h *Send2FaTokenHandler) Handle(cmd *Send2FaToken) error {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.WithFields(logging.Fields{
|
log.WithFields(logging.Fields{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"message": message,
|
"websocket_message": message,
|
||||||
}).Error("Cannot send websocket message")
|
}).Error("Cannot send websocket message")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ func MobileIpAbuseAuditMiddleware(rateLimiter rate_limit.RateLimiter, rateLimitV
|
|||||||
limitReached := rateLimiter.Test(c, key, rate)
|
limitReached := rateLimiter.Test(c, key, rate)
|
||||||
|
|
||||||
if limitReached {
|
if limitReached {
|
||||||
logging.WithFields(logging.Fields{
|
logging.FromContext(c.Request.Context()).WithFields(logging.Fields{
|
||||||
"type": "security",
|
"type": "security",
|
||||||
"uri": c.Request.URL.String(),
|
"uri": c.Request.URL.String(),
|
||||||
"device_id": deviceId,
|
"device_id": deviceId,
|
||||||
|
@ -2,6 +2,7 @@ package ports
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-playground/validator/v10"
|
"github.com/go-playground/validator/v10"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@ -289,7 +290,7 @@ func (r *RoutesHandler) Send2FaToken(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = r.cqrs.Commands.Send2FaToken.Handle(cmd)
|
err = r.cqrs.Commands.Send2FaToken.Handle(c.Request.Context(), cmd)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(500, api.NewInternalServerError(err))
|
c.JSON(500, api.NewInternalServerError(err))
|
||||||
|
@ -1,147 +0,0 @@
|
|||||||
package http
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/twofas/2fas-server/internal/common/logging"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var tunedHttpTransport = &http.Transport{
|
|
||||||
MaxIdleConns: 1024,
|
|
||||||
MaxIdleConnsPerHost: 1024,
|
|
||||||
TLSHandshakeTimeout: 10 * time.Second,
|
|
||||||
DialContext: (&net.Dialer{
|
|
||||||
Timeout: 60 * time.Second,
|
|
||||||
KeepAlive: 60 * time.Second,
|
|
||||||
}).DialContext,
|
|
||||||
}
|
|
||||||
|
|
||||||
type HttpClient struct {
|
|
||||||
client *http.Client
|
|
||||||
baseUrl *url.URL
|
|
||||||
credentialsCallback func(r *http.Request)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *HttpClient) CredentialsProvider(credentialsCallback func(r *http.Request)) {
|
|
||||||
w.credentialsCallback = credentialsCallback
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *HttpClient) Post(ctx context.Context, path string, result interface{}, data interface{}) error {
|
|
||||||
req, err := w.newJsonRequest("POST", path, data)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return w.executeRequest(ctx, req, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *HttpClient) newJsonRequest(method, path string, body interface{}) (*http.Request, error) {
|
|
||||||
var buf io.ReadWriter
|
|
||||||
|
|
||||||
logging.WithFields(logging.Fields{
|
|
||||||
"method": method,
|
|
||||||
"body": body,
|
|
||||||
}).Debug("HTTP Request")
|
|
||||||
|
|
||||||
if body != nil {
|
|
||||||
buf = new(bytes.Buffer)
|
|
||||||
|
|
||||||
encoder := json.NewEncoder(buf)
|
|
||||||
err := encoder.Encode(body)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return w.newRequest(method, path, buf, "application/json")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *HttpClient) newRequest(method, path string, buf io.Reader, contentType string) (*http.Request, error) {
|
|
||||||
u, err := w.baseUrl.Parse(path)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequest(method, u.String(), buf)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("Content-Type", contentType)
|
|
||||||
|
|
||||||
return req, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *HttpClient) executeRequest(ctx context.Context, req *http.Request, v interface{}) error {
|
|
||||||
req = req.WithContext(ctx)
|
|
||||||
|
|
||||||
if w.credentialsCallback != nil {
|
|
||||||
w.credentialsCallback(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := w.client.Do(req)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
responseData, err := w.checkError(resp)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if v == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
responseDataReader := bytes.NewReader(responseData)
|
|
||||||
|
|
||||||
err = json.NewDecoder(responseDataReader).Decode(v)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *HttpClient) checkError(r *http.Response) ([]byte, error) {
|
|
||||||
errorResponse := &ErrorResponse{}
|
|
||||||
|
|
||||||
responseData, err := ioutil.ReadAll(r.Body)
|
|
||||||
|
|
||||||
if err == nil && responseData != nil {
|
|
||||||
json.Unmarshal(responseData, errorResponse)
|
|
||||||
}
|
|
||||||
|
|
||||||
if httpCode := r.StatusCode; 200 <= httpCode && httpCode <= 300 {
|
|
||||||
return responseData, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
errorResponse.Status = r.StatusCode
|
|
||||||
|
|
||||||
return responseData, errorResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHttpClient(baseUrl string) *HttpClient {
|
|
||||||
clientBaseUrl, err := url.Parse(baseUrl)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &HttpClient{
|
|
||||||
client: &http.Client{Transport: tunedHttpTransport},
|
|
||||||
baseUrl: clientBaseUrl,
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,9 +5,37 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
|
||||||
"github.com/twofas/2fas-server/internal/common/logging"
|
"github.com/twofas/2fas-server/internal/common/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CorrelationIdHeader = "X-Correlation-ID"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
RequestId string
|
||||||
|
CorrelationId string
|
||||||
|
)
|
||||||
|
|
||||||
|
func LoggingMiddleware() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
requestId := uuid.New().String()
|
||||||
|
correlationId := c.Request.Header.Get(CorrelationIdHeader)
|
||||||
|
if correlationId == "" {
|
||||||
|
correlationId = uuid.New().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
ctxWithLog := logging.AddToContext(c.Request.Context(), logging.WithFields(map[string]any{
|
||||||
|
"correlation_id": correlationId,
|
||||||
|
"request_id": requestId,
|
||||||
|
}))
|
||||||
|
c.Request = c.Request.WithContext(ctxWithLog)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func RequestJsonLogger() gin.HandlerFunc {
|
func RequestJsonLogger() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
@ -17,22 +45,19 @@ func RequestJsonLogger() gin.HandlerFunc {
|
|||||||
|
|
||||||
c.Request.Body = io.NopCloser(&buf)
|
c.Request.Body = io.NopCloser(&buf)
|
||||||
|
|
||||||
logging.WithFields(logging.Fields{
|
log := logging.FromContext(c.Request.Context())
|
||||||
"method": c.Request.Method,
|
|
||||||
"path": c.Request.URL.Path,
|
log.WithFields(logging.Fields{
|
||||||
"headers": c.Request.Header,
|
"method": c.Request.Method,
|
||||||
"body": string(body),
|
"path": c.Request.URL.Path,
|
||||||
"request_id": c.GetString(RequestIdKey),
|
"body": string(body),
|
||||||
"correlation_id": c.GetString(CorrelationIdKey),
|
|
||||||
}).Info("Request")
|
}).Info("Request")
|
||||||
|
|
||||||
c.Next()
|
c.Next()
|
||||||
|
|
||||||
logging.WithFields(logging.Fields{
|
log.WithFields(logging.Fields{
|
||||||
"method": c.Request.Method,
|
"method": c.Request.Method,
|
||||||
"path": c.Request.URL.Path,
|
"path": c.Request.URL.Path,
|
||||||
"request_id": c.GetString(RequestIdKey),
|
|
||||||
"correlation_id": c.GetString(CorrelationIdKey),
|
|
||||||
}).Info("Response")
|
}).Info("Response")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,50 +1,11 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/twofas/2fas-server/internal/common/logging"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
RequestIdKey = "request_id"
|
|
||||||
|
|
||||||
CorrelationIdKey = "correlation_id"
|
|
||||||
CorrelationIdHeader = "X-Correlation-ID"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
RequestId string
|
|
||||||
CorrelationId string
|
|
||||||
)
|
|
||||||
|
|
||||||
func RequestIdMiddleware() gin.HandlerFunc {
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
RequestId = uuid.New().String()
|
|
||||||
|
|
||||||
c.Set(RequestIdKey, RequestId)
|
|
||||||
|
|
||||||
logging.WithDefaultField(RequestIdKey, RequestId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func CorrelationIdMiddleware() gin.HandlerFunc {
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
c.Set(CorrelationIdKey, uuid.New().String())
|
|
||||||
|
|
||||||
CorrelationId = c.Request.Header.Get(CorrelationIdHeader)
|
|
||||||
|
|
||||||
if CorrelationId == "" {
|
|
||||||
CorrelationId = uuid.New().String()
|
|
||||||
}
|
|
||||||
|
|
||||||
logging.WithDefaultField(CorrelationIdKey, CorrelationId)
|
|
||||||
|
|
||||||
c.Set(CorrelationIdKey, CorrelationId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BodySizeLimitMiddleware(requestBytesLimit int64) gin.HandlerFunc {
|
func BodySizeLimitMiddleware(requestBytesLimit int64) gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
var w http.ResponseWriter = c.Writer
|
var w http.ResponseWriter = c.Writer
|
||||||
|
@ -9,8 +9,7 @@ func RunHttpServer(addr string, init func(engine *gin.Engine)) {
|
|||||||
|
|
||||||
router.Use(gin.Recovery())
|
router.Use(gin.Recovery())
|
||||||
router.Use(corsMiddleware())
|
router.Use(corsMiddleware())
|
||||||
router.Use(RequestIdMiddleware())
|
router.Use(LoggingMiddleware())
|
||||||
router.Use(CorrelationIdMiddleware())
|
|
||||||
router.Use(RequestJsonLogger())
|
router.Use(RequestJsonLogger())
|
||||||
|
|
||||||
init(router)
|
init(router)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package logging
|
package logging
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
@ -8,104 +9,92 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: do not log reuse on every request.
|
type Fields = logrus.Fields
|
||||||
type Fields map[string]interface{}
|
|
||||||
|
|
||||||
var (
|
type FieldLogger interface {
|
||||||
customLogger = New()
|
logrus.FieldLogger
|
||||||
defaultFields = logrus.Fields{}
|
|
||||||
defaultFieldsMutex = sync.RWMutex{}
|
|
||||||
)
|
|
||||||
|
|
||||||
func New() *logrus.Logger {
|
|
||||||
logger := logrus.New()
|
|
||||||
|
|
||||||
logger.SetFormatter(&logrus.JSONFormatter{
|
|
||||||
FieldMap: logrus.FieldMap{
|
|
||||||
logrus.FieldKeyTime: "timestamp",
|
|
||||||
logrus.FieldKeyLevel: "level",
|
|
||||||
logrus.FieldKeyMsg: "message",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
logger.SetLevel(logrus.InfoLevel)
|
|
||||||
|
|
||||||
return logger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithDefaultField(key, value string) *logrus.Logger {
|
var (
|
||||||
defaultFieldsMutex.Lock()
|
log logrus.FieldLogger
|
||||||
defer defaultFieldsMutex.Unlock()
|
once sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
defaultFields[key] = value
|
// Init initialize global instance of logging library.
|
||||||
|
func Init(fields Fields) FieldLogger {
|
||||||
|
once.Do(func() {
|
||||||
|
logger := logrus.New()
|
||||||
|
|
||||||
return customLogger
|
logger.SetFormatter(&logrus.JSONFormatter{
|
||||||
|
FieldMap: logrus.FieldMap{
|
||||||
|
logrus.FieldKeyTime: "timestamp",
|
||||||
|
logrus.FieldKeyLevel: "level",
|
||||||
|
logrus.FieldKeyMsg: "message",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
logger.SetLevel(logrus.InfoLevel)
|
||||||
|
|
||||||
|
log = logger.WithFields(fields)
|
||||||
|
})
|
||||||
|
return log
|
||||||
|
}
|
||||||
|
|
||||||
|
type ctxKey int
|
||||||
|
|
||||||
|
const (
|
||||||
|
loggerKey ctxKey = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
func AddToContext(ctx context.Context, log FieldLogger) context.Context {
|
||||||
|
return context.WithValue(ctx, loggerKey, log)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromContext(ctx context.Context) FieldLogger {
|
||||||
|
log, ok := ctx.Value(loggerKey).(FieldLogger)
|
||||||
|
if ok {
|
||||||
|
return log
|
||||||
|
}
|
||||||
|
return log
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithFields(fields Fields) FieldLogger {
|
||||||
|
return log.WithFields(fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithField(key string, value any) FieldLogger {
|
||||||
|
return log.WithField(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Info(args ...interface{}) {
|
func Info(args ...interface{}) {
|
||||||
defaultFieldsMutex.Lock()
|
log.Info(args...)
|
||||||
defer defaultFieldsMutex.Unlock()
|
|
||||||
|
|
||||||
customLogger.WithFields(defaultFields).Info(args...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Infof(format string, args ...interface{}) {
|
func Infof(format string, args ...interface{}) {
|
||||||
defaultFieldsMutex.Lock()
|
log.Infof(format, args...)
|
||||||
defer defaultFieldsMutex.Unlock()
|
|
||||||
|
|
||||||
customLogger.WithFields(defaultFields).Infof(format, args...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Error(args ...interface{}) {
|
func Error(args ...interface{}) {
|
||||||
defaultFieldsMutex.Lock()
|
log.Error(args...)
|
||||||
defer defaultFieldsMutex.Unlock()
|
|
||||||
|
|
||||||
customLogger.WithFields(defaultFields).Error(args...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Errorf(format string, args ...interface{}) {
|
func Errorf(format string, args ...interface{}) {
|
||||||
defaultFieldsMutex.Lock()
|
log.Errorf(format, args...)
|
||||||
defer defaultFieldsMutex.Unlock()
|
|
||||||
|
|
||||||
customLogger.WithFields(defaultFields).Errorf(format, args...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Warning(args ...interface{}) {
|
func Warning(args ...interface{}) {
|
||||||
defaultFieldsMutex.Lock()
|
log.Warning(args...)
|
||||||
defer defaultFieldsMutex.Unlock()
|
|
||||||
|
|
||||||
customLogger.WithFields(defaultFields).Warning(args...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Fatal(args ...interface{}) {
|
func Fatal(args ...interface{}) {
|
||||||
defaultFieldsMutex.Lock()
|
log.Fatal(args...)
|
||||||
defer defaultFieldsMutex.Unlock()
|
|
||||||
|
|
||||||
customLogger.WithFields(defaultFields).Fatal(args...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithField(key string, value interface{}) *logrus.Entry {
|
func Fatalf(format string, args ...interface{}) {
|
||||||
defaultFieldsMutex.Lock()
|
log.Fatalf(format, args...)
|
||||||
defer defaultFieldsMutex.Unlock()
|
|
||||||
|
|
||||||
return customLogger.
|
|
||||||
WithFields(defaultFields).
|
|
||||||
WithField(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithFields(fields Fields) *logrus.Entry {
|
|
||||||
defaultFieldsMutex.Lock()
|
|
||||||
defer defaultFieldsMutex.Unlock()
|
|
||||||
|
|
||||||
return customLogger.
|
|
||||||
WithFields(logrus.Fields(fields)).
|
|
||||||
WithFields(defaultFields)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func LogCommand(command interface{}) {
|
func LogCommand(command interface{}) {
|
||||||
defaultFieldsMutex.Lock()
|
|
||||||
defer defaultFieldsMutex.Unlock()
|
|
||||||
|
|
||||||
context, _ := json.Marshal(command)
|
context, _ := json.Marshal(command)
|
||||||
|
|
||||||
commandName := reflect.TypeOf(command).Elem().Name()
|
commandName := reflect.TypeOf(command).Elem().Name()
|
||||||
@ -113,8 +102,7 @@ func LogCommand(command interface{}) {
|
|||||||
var commandAsFields logrus.Fields
|
var commandAsFields logrus.Fields
|
||||||
json.Unmarshal(context, &commandAsFields)
|
json.Unmarshal(context, &commandAsFields)
|
||||||
|
|
||||||
customLogger.
|
log.
|
||||||
WithFields(defaultFields).
|
|
||||||
WithFields(logrus.Fields{
|
WithFields(logrus.Fields{
|
||||||
"command_name": commandName,
|
"command_name": commandName,
|
||||||
"command": commandAsFields,
|
"command": commandAsFields,
|
||||||
@ -122,13 +110,8 @@ func LogCommand(command interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func LogCommandFailed(command interface{}, err error) {
|
func LogCommandFailed(command interface{}, err error) {
|
||||||
defaultFieldsMutex.Lock()
|
|
||||||
defer defaultFieldsMutex.Unlock()
|
|
||||||
|
|
||||||
commandName := reflect.TypeOf(command).Elem().Name()
|
commandName := reflect.TypeOf(command).Elem().Name()
|
||||||
|
log.
|
||||||
customLogger.
|
|
||||||
WithFields(defaultFields).
|
|
||||||
WithFields(logrus.Fields{
|
WithFields(logrus.Fields{
|
||||||
"reason": err.Error(),
|
"reason": err.Error(),
|
||||||
}).Info("Command failed" + commandName)
|
}).Info("Command failed" + commandName)
|
||||||
|
@ -38,7 +38,7 @@ func (r *RedisRateLimit) Test(ctx context.Context, key string, rate Rate) bool {
|
|||||||
Period: rate.TimeUnit,
|
Period: rate.TimeUnit,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.WithFields(logging.Fields{
|
logging.FromContext(ctx).WithFields(logging.Fields{
|
||||||
"type": "security",
|
"type": "security",
|
||||||
}).Warnf("Could not check rate limit: %v", err)
|
}).Warnf("Could not check rate limit: %v", err)
|
||||||
|
|
||||||
|
@ -3,10 +3,11 @@ package recovery
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/twofas/2fas-server/internal/common/logging"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/twofas/2fas-server/internal/common/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RecoveryMiddleware() gin.HandlerFunc {
|
func RecoveryMiddleware() gin.HandlerFunc {
|
||||||
|
@ -30,7 +30,7 @@ func IPAbuseAuditMiddleware(rateLimiter rate_limit.RateLimiter, rateLimitValue i
|
|||||||
limitReached := rateLimiter.Test(c, key, rate)
|
limitReached := rateLimiter.Test(c, key, rate)
|
||||||
|
|
||||||
if limitReached {
|
if limitReached {
|
||||||
logging.WithFields(logging.Fields{
|
logging.FromContext(c.Request.Context()).WithFields(logging.Fields{
|
||||||
"type": "security",
|
"type": "security",
|
||||||
"uri": c.Request.URL.String(),
|
"uri": c.Request.URL.String(),
|
||||||
"ip": c.ClientIP(),
|
"ip": c.ClientIP(),
|
||||||
|
@ -2,12 +2,15 @@ package websocket
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/gorilla/websocket"
|
"fmt"
|
||||||
app_http "github.com/twofas/2fas-server/internal/common/http"
|
|
||||||
"github.com/twofas/2fas-server/internal/common/logging"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
|
||||||
|
app_http "github.com/twofas/2fas-server/internal/common/http"
|
||||||
|
"github.com/twofas/2fas-server/internal/common/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WebsocketApiClient struct {
|
type WebsocketApiClient struct {
|
||||||
@ -21,13 +24,15 @@ func NewWebsocketApiClient(websocketApiUrl string) *WebsocketApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ws *WebsocketApiClient) SendMessage(uri string, message interface{}) error {
|
func (ws *WebsocketApiClient) SendMessage(uri string, message interface{}) error {
|
||||||
u, _ := url.Parse(ws.wsAddr)
|
u, err := url.Parse(ws.wsAddr)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse %q: %w", ws.wsAddr, err)
|
||||||
|
}
|
||||||
u.Path = path.Join(u.Path, uri)
|
u.Path = path.Join(u.Path, uri)
|
||||||
|
|
||||||
msg, err := json.Marshal(message)
|
msg, err := json.Marshal(message)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to marshal message: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
logging.WithFields(logging.Fields{
|
logging.WithFields(logging.Fields{
|
||||||
@ -40,23 +45,20 @@ func (ws *WebsocketApiClient) SendMessage(uri string, message interface{}) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
c, _, err := websocket.DefaultDialer.Dial(u.String(), requestHeaders)
|
c, _, err := websocket.DefaultDialer.Dial(u.String(), requestHeaders)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to dial: %q: %w", u.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.WriteMessage(websocket.TextMessage, msg)
|
err = c.WriteMessage(websocket.TextMessage, msg)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.WithField("error", err.Error()).Error("Cannot send websocket message")
|
logging.WithField("error", err.Error()).Error("Cannot send websocket message")
|
||||||
return err
|
return fmt.Errorf("failed to write message to the conection: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
err = c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.WithField("error", err.Error()).Error("Cannot close websocket connection")
|
logging.WithField("error", err.Error()).Error("Cannot close websocket connection")
|
||||||
return err
|
return fmt.Errorf("failed to write close message to the conection: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -7,8 +7,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/twofas/2fas-server/internal/common/logging"
|
"github.com/twofas/2fas-server/internal/common/logging"
|
||||||
"github.com/twofas/2fas-server/internal/pass/connection"
|
"github.com/twofas/2fas-server/internal/pass/connection"
|
||||||
"github.com/twofas/2fas-server/internal/pass/sign"
|
"github.com/twofas/2fas-server/internal/pass/sign"
|
||||||
@ -128,7 +126,7 @@ func (p *Pairing) ServePairingWS(w http.ResponseWriter, r *http.Request, extID s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Pairing) isExtensionPaired(ctx context.Context, extID string, log *logrus.Entry) (PairingInfo, bool) {
|
func (p *Pairing) isExtensionPaired(ctx context.Context, extID string, log logging.FieldLogger) (PairingInfo, bool) {
|
||||||
pairingInfo, err := p.store.GetPairingInfo(ctx, extID)
|
pairingInfo, err := p.store.GetPairingInfo(ctx, extID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("Failed to get pairing info")
|
log.Warn("Failed to get pairing info")
|
||||||
|
@ -59,9 +59,7 @@ func NewServer(cfg config.PassConfig) *Server {
|
|||||||
|
|
||||||
router := gin.New()
|
router := gin.New()
|
||||||
router.Use(recovery.RecoveryMiddleware())
|
router.Use(recovery.RecoveryMiddleware())
|
||||||
router.Use(httphelpers.RequestIdMiddleware())
|
router.Use(httphelpers.LoggingMiddleware())
|
||||||
router.Use(httphelpers.CorrelationIdMiddleware())
|
|
||||||
// TODO: don't log auth headers.
|
|
||||||
router.Use(httphelpers.RequestJsonLogger())
|
router.Use(httphelpers.RequestJsonLogger())
|
||||||
|
|
||||||
router.GET("/health", func(context *gin.Context) {
|
router.GET("/health", func(context *gin.Context) {
|
||||||
|
@ -8,10 +8,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
|
||||||
"github.com/aws/aws-sdk-go/service/kms"
|
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
@ -70,16 +66,6 @@ func createTestService(t *testing.T) Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSignAndVerify(t *testing.T) {
|
func TestSignAndVerify(t *testing.T) {
|
||||||
sess, err := session.NewSession(&aws.Config{
|
|
||||||
Region: aws.String("us-east-1"),
|
|
||||||
Credentials: credentials.NewStaticCredentials("test", "test", ""),
|
|
||||||
S3ForcePathStyle: aws.Bool(true),
|
|
||||||
Endpoint: aws.String("http://localhost:4566"),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
kmsClient := kms.New(sess)
|
|
||||||
srv := createTestService(t)
|
srv := createTestService(t)
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
@ -128,18 +114,7 @@ func TestSignAndVerify(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "invalid signature",
|
name: "invalid signature",
|
||||||
tokenFn: func() string {
|
tokenFn: func() string {
|
||||||
resp, err := kmsClient.CreateKey(&kms.CreateKeyInput{
|
serviceWithAnotherKey := createTestService(t)
|
||||||
KeySpec: aws.String("ECC_NIST_P256"),
|
|
||||||
KeyUsage: aws.String("SIGN_VERIFY"),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
serviceWithAnotherKey, err := NewService(*resp.KeyMetadata.KeyId, kmsClient)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
token, err := serviceWithAnotherKey.SignAndEncode(Message{
|
token, err := serviceWithAnotherKey.SignAndEncode(Message{
|
||||||
ConnectionID: uuid.New().String(),
|
ConnectionID: uuid.New().String(),
|
||||||
ExpiresAt: now.Add(-time.Hour),
|
ExpiresAt: now.Add(-time.Hour),
|
||||||
|
@ -75,7 +75,7 @@ func (s *Syncing) ServeSyncingRequestWS(w http.ResponseWriter, r *http.Request,
|
|||||||
if err := s.sendTokenAndCloseConn(fcmToken, conn); err != nil {
|
if err := s.sendTokenAndCloseConn(fcmToken, conn); err != nil {
|
||||||
log.Errorf("Failed to send token: %v", err)
|
log.Errorf("Failed to send token: %v", err)
|
||||||
}
|
}
|
||||||
log.Infof("Paring ws finished")
|
log.Infof("Sync ws finished")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ func (s *Syncing) ServeSyncingRequestWS(w http.ResponseWriter, r *http.Request,
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-maxWaitC:
|
case <-maxWaitC:
|
||||||
log.Info("Closing paring ws after timeout")
|
log.Info("Closing sync ws after timeout")
|
||||||
return nil
|
return nil
|
||||||
case <-connectedCheckTicker.C:
|
case <-connectedCheckTicker.C:
|
||||||
if syncConfirmed := s.isSyncConfirmed(r.Context(), fcmToken); syncConfirmed {
|
if syncConfirmed := s.isSyncConfirmed(r.Context(), fcmToken); syncConfirmed {
|
||||||
@ -97,7 +97,7 @@ func (s *Syncing) ServeSyncingRequestWS(w http.ResponseWriter, r *http.Request,
|
|||||||
log.Errorf("Failed to send token: %v", err)
|
log.Errorf("Failed to send token: %v", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
log.Infof("Paring ws finished")
|
log.Infof("Sync ws finished")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,7 @@ func NewServer(addr string) *Server {
|
|||||||
router := gin.New()
|
router := gin.New()
|
||||||
|
|
||||||
router.Use(recovery.RecoveryMiddleware())
|
router.Use(recovery.RecoveryMiddleware())
|
||||||
router.Use(http.RequestIdMiddleware())
|
router.Use(http.LoggingMiddleware())
|
||||||
router.Use(http.CorrelationIdMiddleware())
|
|
||||||
router.Use(http.RequestJsonLogger())
|
router.Use(http.RequestJsonLogger())
|
||||||
|
|
||||||
connectionHandler := common.NewConnectionHandler()
|
connectionHandler := common.NewConnectionHandler()
|
||||||
|
@ -2,9 +2,11 @@ package common
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
|
|
||||||
"github.com/twofas/2fas-server/internal/common/logging"
|
"github.com/twofas/2fas-server/internal/common/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -43,6 +45,8 @@ type Client struct {
|
|||||||
|
|
||||||
// Buffered channel of outbound messages.
|
// Buffered channel of outbound messages.
|
||||||
send chan []byte
|
send chan []byte
|
||||||
|
|
||||||
|
sendMtx *sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// readPump pumps messages from the websocket connection to the hub.
|
// readPump pumps messages from the websocket connection to the hub.
|
||||||
@ -50,7 +54,7 @@ type Client struct {
|
|||||||
// The application runs readPump in a per-connection goroutine. The application
|
// The application runs readPump in a per-connection goroutine. The application
|
||||||
// ensures that there is at most one reader on a connection by executing all
|
// ensures that there is at most one reader on a connection by executing all
|
||||||
// reads from this goroutine.
|
// reads from this goroutine.
|
||||||
func (c *Client) readPump() {
|
func (c *Client) readPump(log logging.FieldLogger) {
|
||||||
defer func() {
|
defer func() {
|
||||||
c.hub.unregisterClient(c)
|
c.hub.unregisterClient(c)
|
||||||
c.conn.Close()
|
c.conn.Close()
|
||||||
@ -69,11 +73,11 @@ func (c *Client) readPump() {
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if websocket.IsUnexpectedCloseError(err, acceptedCloseStatus...) {
|
if websocket.IsUnexpectedCloseError(err, acceptedCloseStatus...) {
|
||||||
logging.WithFields(logging.Fields{
|
log.WithFields(logging.Fields{
|
||||||
"reason": err.Error(),
|
"reason": err.Error(),
|
||||||
}).Error("Websocket connection closed unexpected")
|
}).Error("Websocket connection closed unexpected")
|
||||||
} else {
|
} else {
|
||||||
logging.WithFields(logging.Fields{
|
log.WithFields(logging.Fields{
|
||||||
"reason": err.Error(),
|
"reason": err.Error(),
|
||||||
}).Info("Connection closed")
|
}).Info("Connection closed")
|
||||||
}
|
}
|
||||||
@ -133,3 +137,27 @@ func (c *Client) writePump() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) sendMsg(bb []byte) bool {
|
||||||
|
c.sendMtx.Lock()
|
||||||
|
defer c.sendMtx.Unlock()
|
||||||
|
|
||||||
|
if c.send == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
c.send <- bb
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) close() {
|
||||||
|
c.sendMtx.Lock()
|
||||||
|
defer c.sendMtx.Unlock()
|
||||||
|
|
||||||
|
if c.send == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
close(c.send)
|
||||||
|
c.send = nil
|
||||||
|
}
|
||||||
|
@ -42,18 +42,18 @@ func (h *ConnectionHandler) Handler() gin.HandlerFunc {
|
|||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
channel := c.Request.URL.Path
|
channel := c.Request.URL.Path
|
||||||
|
|
||||||
logging.WithDefaultField("channel", channel)
|
log := logging.FromContext(c.Request.Context()).WithField("channel", channel)
|
||||||
|
|
||||||
logging.Info("New channel subscriber")
|
log.Info("New channel subscriber")
|
||||||
|
|
||||||
h.serveWs(c.Writer, c.Request, channel)
|
h.serveWs(c.Writer, c.Request, channel, log)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *ConnectionHandler) serveWs(w http.ResponseWriter, r *http.Request, channel string) {
|
func (h *ConnectionHandler) serveWs(w http.ResponseWriter, r *http.Request, channel string, log logging.FieldLogger) {
|
||||||
conn, err := upgrader.Upgrade(w, r, nil)
|
conn, err := upgrader.Upgrade(w, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.Errorf("Failed to upgrade connection: %v", err)
|
log.Errorf("Failed to upgrade connection: %v", err)
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -65,7 +65,7 @@ func (h *ConnectionHandler) serveWs(w http.ResponseWriter, r *http.Request, chan
|
|||||||
})
|
})
|
||||||
|
|
||||||
go recovery.DoNotPanic(func() {
|
go recovery.DoNotPanic(func() {
|
||||||
client.readPump()
|
client.readPump(log)
|
||||||
})
|
})
|
||||||
|
|
||||||
go recovery.DoNotPanic(func() {
|
go recovery.DoNotPanic(func() {
|
||||||
@ -73,7 +73,7 @@ func (h *ConnectionHandler) serveWs(w http.ResponseWriter, r *http.Request, chan
|
|||||||
timeout := time.After(disconnectAfter)
|
timeout := time.After(disconnectAfter)
|
||||||
|
|
||||||
<-timeout
|
<-timeout
|
||||||
logging.Info("Connection closed after", disconnectAfter)
|
log.Info("Connection closed after", disconnectAfter)
|
||||||
|
|
||||||
client.hub.unregisterClient(client)
|
client.hub.unregisterClient(client)
|
||||||
client.conn.Close()
|
client.conn.Close()
|
||||||
|
@ -28,7 +28,7 @@ func (h *Hub) unregisterClient(c *Client) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
close(c.send)
|
c.close()
|
||||||
if h.isEmpty() {
|
if h.isEmpty() {
|
||||||
h.onHubHasNoClients(h.id)
|
h.onHubHasNoClients(h.id)
|
||||||
}
|
}
|
||||||
@ -39,9 +39,8 @@ func (h *Hub) sendToClient(c *Client, msg []byte) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
select {
|
ok = c.sendMsg(msg)
|
||||||
case c.send <- msg:
|
if !ok {
|
||||||
default:
|
|
||||||
h.unregisterClient(c)
|
h.unregisterClient(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ func (h *hubPool) registerClient(channel string, conn *websocket.Conn) (*Client,
|
|||||||
defer h.mtx.Unlock()
|
defer h.mtx.Unlock()
|
||||||
|
|
||||||
hub := h.getOrCreateHub(channel)
|
hub := h.getOrCreateHub(channel)
|
||||||
client := &Client{hub: hub, conn: conn, send: make(chan []byte, 256)}
|
client := &Client{hub: hub, conn: conn, send: make(chan []byte, 256), sendMtx: &sync.Mutex{}}
|
||||||
hub.registerClient(client)
|
hub.registerClient(client)
|
||||||
|
|
||||||
// handler (caller of this method) isn't really interested in hub,
|
// handler (caller of this method) isn't really interested in hub,
|
||||||
|
@ -50,27 +50,44 @@ func TestCreateRemoveConcurrently(t *testing.T) {
|
|||||||
hp := newHubPool()
|
hp := newHubPool()
|
||||||
const channelsNo = 100
|
const channelsNo = 100
|
||||||
const clientsPerChannel = 1000
|
const clientsPerChannel = 1000
|
||||||
|
const messagesSentToEachHub = 100
|
||||||
|
|
||||||
hubs := &sync.Map{}
|
hubs := &sync.Map{}
|
||||||
|
|
||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
|
// First we create `channelsNo` goroutines. Each of them creates `clientsPerChannel` sub-goroutines.
|
||||||
|
// This gives us `channelsNo*clientsPerChannel` sub go-routines and `channelsNo` parent goroutines.
|
||||||
|
// Each of them will call `wg.Done() once and we can't progress until all of them are done.
|
||||||
|
wg.Add(channelsNo*clientsPerChannel + channelsNo)
|
||||||
|
// We will close `channelsNo*clientsPerChannel + channelsNo` clients. We create fakeReadPump for each of them and
|
||||||
|
// wait for it to finish.
|
||||||
wg.Add(channelsNo * clientsPerChannel)
|
wg.Add(channelsNo * clientsPerChannel)
|
||||||
|
|
||||||
for i := 0; i < channelsNo; i++ {
|
for i := 0; i < channelsNo; i++ {
|
||||||
channelID := fmt.Sprintf("channel-%d", i)
|
channelID := fmt.Sprintf("channel-%d", i)
|
||||||
|
|
||||||
|
c, h := hp.registerClient(channelID, &websocket.Conn{})
|
||||||
|
hubs.Store(h, struct{}{})
|
||||||
|
go fakeReadPump(c.send, &wg)
|
||||||
go func() {
|
go func() {
|
||||||
|
for i := 0; i < messagesSentToEachHub; i++ {
|
||||||
|
h.broadcastMsg([]byte("test"))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
for j := 0; j < clientsPerChannel; j++ {
|
for j := 0; j < clientsPerChannel; j++ {
|
||||||
c, h := hp.registerClient(channelID, &websocket.Conn{})
|
c, h := hp.registerClient(channelID, &websocket.Conn{})
|
||||||
hubs.Store(h, struct{}{})
|
go fakeReadPump(c.send, &wg)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
h.unregisterClient(c)
|
h.unregisterClient(c)
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
_, h := hp.registerClient(channelID, &websocket.Conn{})
|
|
||||||
hubs.Store(h, struct{}{})
|
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
for c, hub := range hp.hubs {
|
for c, hub := range hp.hubs {
|
||||||
@ -89,3 +106,9 @@ func TestCreateRemoveConcurrently(t *testing.T) {
|
|||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fakeReadPump(c chan []byte, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
for range c {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,121 +0,0 @@
|
|||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/suite"
|
|
||||||
"github.com/twofas/2fas-server/tests"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBrowserExtensionTwoFactorAuthTestSuite(t *testing.T) {
|
|
||||||
suite.Run(t, new(BrowserExtensionTwoFactorAuthTestSuite))
|
|
||||||
}
|
|
||||||
|
|
||||||
type BrowserExtensionTwoFactorAuthTestSuite struct {
|
|
||||||
suite.Suite
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BrowserExtensionTwoFactorAuthTestSuite) SetupTest() {
|
|
||||||
tests.RemoveAllMobileDevices(s.T())
|
|
||||||
tests.RemoveAllBrowserExtensions(s.T())
|
|
||||||
tests.RemoveAllBrowserExtensionsDevices(s.T())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BrowserExtensionTwoFactorAuthTestSuite) TestRequest2FaToken() {
|
|
||||||
browserExtension := tests.CreateBrowserExtension(s.T(), "go-ext")
|
|
||||||
|
|
||||||
var tokenRequest *tests.AuthTokenRequestResponse
|
|
||||||
request2FaTokenPayload := []byte(`{"domain":"https://facebook.com/path/nested"}`)
|
|
||||||
tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/commands/request_2fa_token", request2FaTokenPayload, &tokenRequest)
|
|
||||||
|
|
||||||
assert.Equal(s.T(), browserExtension.Id, tokenRequest.ExtensionId)
|
|
||||||
|
|
||||||
var tokenRequestById *tests.AuthTokenRequestResponse
|
|
||||||
tests.DoAPISuccessGet(s.T(), "browser_extensions/"+browserExtension.Id+"/2fa_requests/"+tokenRequest.Id, &tokenRequestById)
|
|
||||||
assert.Equal(s.T(), tokenRequest.Id, tokenRequestById.Id)
|
|
||||||
assert.Equal(s.T(), "https://facebook.com", tokenRequestById.Domain)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BrowserExtensionTwoFactorAuthTestSuite) TestFindAll2FaRequestsForBrowserExtension() {
|
|
||||||
browserExtension := tests.CreateBrowserExtension(s.T(), "go-ext")
|
|
||||||
|
|
||||||
facebook2FaTokenRequest := []byte(`{"domain":"facebook.com"}`)
|
|
||||||
tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/commands/request_2fa_token", facebook2FaTokenRequest, nil)
|
|
||||||
|
|
||||||
google2FaTokenRequest := []byte(`{"domain":"google.com"}`)
|
|
||||||
tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/commands/request_2fa_token", google2FaTokenRequest, nil)
|
|
||||||
|
|
||||||
var tokenRequestsCollection []*tests.AuthTokenRequestResponse
|
|
||||||
tests.DoAPISuccessGet(s.T(), "browser_extensions/"+browserExtension.Id+"/2fa_requests", &tokenRequestsCollection)
|
|
||||||
|
|
||||||
assert.Len(s.T(), tokenRequestsCollection, 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BrowserExtensionTwoFactorAuthTestSuite) TestClose2FaTokenRequest() {
|
|
||||||
var tokenRequest *tests.AuthTokenRequestResponse
|
|
||||||
browserExtension := tests.CreateBrowserExtension(s.T(), "go-ext")
|
|
||||||
tokenRequestPayload := []byte(`{"domain":"facebook.com"}`)
|
|
||||||
tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/commands/request_2fa_token", tokenRequestPayload, &tokenRequest)
|
|
||||||
closeTokenRequestPayload := []byte(`{"status":"completed"}`)
|
|
||||||
tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/2fa_requests/"+tokenRequest.Id+"/commands/close_2fa_request", closeTokenRequestPayload, nil)
|
|
||||||
|
|
||||||
var closedTokenRequest *tests.AuthTokenRequestResponse
|
|
||||||
tests.DoAPISuccessGet(s.T(), "browser_extensions/"+browserExtension.Id+"/2fa_requests/"+tokenRequest.Id, &closedTokenRequest)
|
|
||||||
assert.Equal(s.T(), "completed", closedTokenRequest.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BrowserExtensionTwoFactorAuthTestSuite) TestCloseNotExisting2FaTokenRequest() {
|
|
||||||
notExistingTokenRequestId := uuid.New()
|
|
||||||
browserExtension := tests.CreateBrowserExtension(s.T(), "go-ext")
|
|
||||||
|
|
||||||
closeTokenRequestPayload := []byte(`{"status":"completed"}`)
|
|
||||||
tests.DoAPIPostAndAssertCode(s.T(), 404, "browser_extensions/"+browserExtension.Id+"/2fa_requests/"+notExistingTokenRequestId.String()+"/commands/close_2fa_request", closeTokenRequestPayload, nil)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BrowserExtensionTwoFactorAuthTestSuite) TestDoNotReturnClosed2FaRequests() {
|
|
||||||
var tokenRequest *tests.AuthTokenRequestResponse
|
|
||||||
browserExtension := tests.CreateBrowserExtension(s.T(), "go-ext")
|
|
||||||
tokenRequestPayload := []byte(`{"domain":"facebook.com"}`)
|
|
||||||
tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/commands/request_2fa_token", tokenRequestPayload, &tokenRequest)
|
|
||||||
|
|
||||||
closeTokenRequestPayload := []byte(`{"status":"completed"}`)
|
|
||||||
tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/2fa_requests/"+tokenRequest.Id+"/commands/close_2fa_request", closeTokenRequestPayload, nil)
|
|
||||||
|
|
||||||
var response []*tests.AuthTokenRequestResponse
|
|
||||||
tests.DoAPISuccessGet(s.T(), "browser_extensions/"+browserExtension.Id+"/2fa_requests", &response)
|
|
||||||
assert.Len(s.T(), response, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BrowserExtensionTwoFactorAuthTestSuite) TestTerminate2FaRequest() {
|
|
||||||
var tokenRequest *tests.AuthTokenRequestResponse
|
|
||||||
browserExtension := tests.CreateBrowserExtension(s.T(), "go-ext")
|
|
||||||
tokenRequestPayload := []byte(`{"domain":"facebook.com"}`)
|
|
||||||
tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/commands/request_2fa_token", tokenRequestPayload, &tokenRequest)
|
|
||||||
|
|
||||||
closeTokenRequestPayload := []byte(`{"status":"terminated"}`)
|
|
||||||
tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/2fa_requests/"+tokenRequest.Id+"/commands/close_2fa_request", closeTokenRequestPayload, nil)
|
|
||||||
|
|
||||||
var response *tests.AuthTokenRequestResponse
|
|
||||||
tests.DoAPISuccessGet(s.T(), "browser_extensions/"+browserExtension.Id+"/2fa_requests/"+tokenRequest.Id, &response)
|
|
||||||
assert.Equal(s.T(), "terminated", response.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BrowserExtensionTwoFactorAuthTestSuite) TestClose2FaRequest() {
|
|
||||||
device, devicePubKey := tests.CreateDevice(s.T(), "SM-955F", "fcm-token")
|
|
||||||
browserExtension := tests.CreateBrowserExtension(s.T(), "go-ext")
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExtension, device)
|
|
||||||
|
|
||||||
var tokenRequest *tests.AuthTokenRequestResponse
|
|
||||||
request2FaTokenPayload := []byte(`{"domain":"domain.com"}`)
|
|
||||||
tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/commands/request_2fa_token", request2FaTokenPayload, &tokenRequest)
|
|
||||||
|
|
||||||
closeTokenRequestPayload := []byte(`{"status":"completed"}`)
|
|
||||||
tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/2fa_requests/"+tokenRequest.Id+"/commands/close_2fa_request", closeTokenRequestPayload, nil)
|
|
||||||
|
|
||||||
var closedTokenRequest *tests.AuthTokenRequestResponse
|
|
||||||
tests.DoAPISuccessGet(s.T(), "browser_extensions/"+browserExtension.Id+"/2fa_requests/"+tokenRequest.Id, &closedTokenRequest)
|
|
||||||
assert.Equal(s.T(), "completed", closedTokenRequest.Status)
|
|
||||||
}
|
|
@ -1,207 +0,0 @@
|
|||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"github.com/stretchr/testify/suite"
|
|
||||||
"github.com/twofas/2fas-server/tests"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBrowserExtensionPairingTestSuite(t *testing.T) {
|
|
||||||
suite.Run(t, new(BrowserExtensionPairingTestSuite))
|
|
||||||
}
|
|
||||||
|
|
||||||
type BrowserExtensionPairingTestSuite struct {
|
|
||||||
suite.Suite
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BrowserExtensionPairingTestSuite) SetupTest() {
|
|
||||||
tests.RemoveAllBrowserExtensions(s.T())
|
|
||||||
tests.RemoveAllBrowserExtensionsDevices(s.T())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BrowserExtensionPairingTestSuite) TestPairBrowserExtensionWithMobileDevice() {
|
|
||||||
browserExt := tests.CreateBrowserExtension(s.T(), "go-test")
|
|
||||||
_, err := uuid.Parse(browserExt.Id)
|
|
||||||
require.NoError(s.T(), err)
|
|
||||||
|
|
||||||
device, devicePubKey := tests.CreateDevice(s.T(), "go-test-device", "some-device-id")
|
|
||||||
_, err = uuid.Parse(device.Id)
|
|
||||||
require.NoError(s.T(), err)
|
|
||||||
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExt, device)
|
|
||||||
|
|
||||||
var extensionDevice *tests.DevicePairedBrowserExtensionResponse
|
|
||||||
tests.DoAPISuccessGet(s.T(), "/browser_extensions/"+browserExt.Id+"/devices/"+device.Id, &extensionDevice)
|
|
||||||
|
|
||||||
assert.Equal(s.T(), extensionDevice.Id, device.Id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BrowserExtensionPairingTestSuite) TestDoNotFindNotPairedBrowserExtensionMobileDevice() {
|
|
||||||
browserExt := tests.CreateBrowserExtension(s.T(), "go-test")
|
|
||||||
_, err := uuid.Parse(browserExt.Id)
|
|
||||||
require.NoError(s.T(), err)
|
|
||||||
|
|
||||||
device, _ := tests.CreateDevice(s.T(), "go-test-device", "some-device-id")
|
|
||||||
|
|
||||||
response := tests.DoAPIGet(s.T(), "/browser_extensions/"+browserExt.Id+"/devices/"+device.Id, nil)
|
|
||||||
|
|
||||||
assert.Equal(s.T(), 404, response.StatusCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BrowserExtensionPairingTestSuite) TestPairBrowserExtensionWithMultipleDevices() {
|
|
||||||
browserExt := tests.CreateBrowserExtension(s.T(), "go-test")
|
|
||||||
_, err := uuid.Parse(browserExt.Id)
|
|
||||||
require.NoError(s.T(), err)
|
|
||||||
|
|
||||||
device1, devicePubKey1 := tests.CreateDevice(s.T(), "go-test-device-1", "some-device-id-1")
|
|
||||||
device2, devicePubKey2 := tests.CreateDevice(s.T(), "go-test-device-2", "some-device-id-2")
|
|
||||||
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey1, browserExt, device1)
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey2, browserExt, device2)
|
|
||||||
|
|
||||||
extensionDevices := tests.GetExtensionDevices(s.T(), browserExt.Id)
|
|
||||||
|
|
||||||
assert.Len(s.T(), extensionDevices, 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BrowserExtensionPairingTestSuite) TestRemoveBrowserExtensionPairedDevice() {
|
|
||||||
browserExt := tests.CreateBrowserExtension(s.T(), "go-test")
|
|
||||||
|
|
||||||
device1, devicePubKey1 := tests.CreateDevice(s.T(), "go-test-device-1", "some-device-id-1")
|
|
||||||
device2, devicePubKey2 := tests.CreateDevice(s.T(), "go-test-device-2", "some-device-id-2")
|
|
||||||
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey1, browserExt, device1)
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey2, browserExt, device2)
|
|
||||||
|
|
||||||
extensionDevices := getExtensionPairedDevices(s.T(), browserExt)
|
|
||||||
assert.Len(s.T(), extensionDevices, 2)
|
|
||||||
|
|
||||||
tests.DoAPISuccessDelete(s.T(), "/browser_extensions/"+browserExt.Id+"/devices/"+device1.Id)
|
|
||||||
|
|
||||||
extensionDevices = getExtensionPairedDevices(s.T(), browserExt)
|
|
||||||
assert.Len(s.T(), extensionDevices, 1)
|
|
||||||
assert.Equal(s.T(), device2.Id, extensionDevices[0].Id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BrowserExtensionPairingTestSuite) TestRemoveBrowserExtensionPairedDeviceTwice() {
|
|
||||||
browserExt := tests.CreateBrowserExtension(s.T(), "go-test")
|
|
||||||
|
|
||||||
device, devicePubKey := tests.CreateDevice(s.T(), "go-test-device-1", "some-device-id-1")
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExt, device)
|
|
||||||
|
|
||||||
tests.DoAPISuccessDelete(s.T(), "/browser_extensions/"+browserExt.Id+"/devices/"+device.Id)
|
|
||||||
response := tests.DoAPIRequest(s.T(), "/browser_extensions/"+browserExt.Id+"/devices/"+device.Id, http.MethodDelete, nil /*payload*/, nil /*resp*/)
|
|
||||||
|
|
||||||
assert.Equal(s.T(), 404, response.StatusCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BrowserExtensionPairingTestSuite) TestRemoveAllBrowserExtensionPairedDevices() {
|
|
||||||
browserExt := tests.CreateBrowserExtension(s.T(), "go-test")
|
|
||||||
device1, devicePubKey1 := tests.CreateDevice(s.T(), "go-test-device-1", "some-device-id1")
|
|
||||||
device2, devicePubKey2 := tests.CreateDevice(s.T(), "go-test-device-2", "some-device-id2")
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey1, browserExt, device1)
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey2, browserExt, device2)
|
|
||||||
|
|
||||||
tests.DoAPISuccessDelete(s.T(), "/browser_extensions/"+browserExt.Id+"/devices")
|
|
||||||
|
|
||||||
extensionDevices := tests.GetExtensionDevices(s.T(), browserExt.Id)
|
|
||||||
assert.Len(s.T(), extensionDevices, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BrowserExtensionPairingTestSuite) TestGetPairedDevicesWhichIDoNotOwn() {
|
|
||||||
browserExt1 := tests.CreateBrowserExtension(s.T(), "go-test-1")
|
|
||||||
browserExt2 := tests.CreateBrowserExtension(s.T(), "go-test-2")
|
|
||||||
|
|
||||||
device1, devicePubKey1 := tests.CreateDevice(s.T(), "go-test-device-1", "some-device-id-1")
|
|
||||||
device2, devicePubKey2 := tests.CreateDevice(s.T(), "go-test-device-2", "some-device-id-2")
|
|
||||||
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey1, browserExt1, device1)
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey2, browserExt2, device2)
|
|
||||||
|
|
||||||
firstExtensionDevices := getExtensionPairedDevices(s.T(), browserExt1)
|
|
||||||
assert.Len(s.T(), firstExtensionDevices, 1)
|
|
||||||
assert.Equal(s.T(), device1.Id, firstExtensionDevices[0].Id)
|
|
||||||
|
|
||||||
secondExtensionDevices := getExtensionPairedDevices(s.T(), browserExt2)
|
|
||||||
assert.Len(s.T(), secondExtensionDevices, 1)
|
|
||||||
assert.Equal(s.T(), device2.Id, secondExtensionDevices[0].Id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BrowserExtensionPairingTestSuite) TestGetPairedDevicesByInvalidExtensionId() {
|
|
||||||
browserExt1 := tests.CreateBrowserExtension(s.T(), "go-test-1")
|
|
||||||
browserExt2 := tests.CreateBrowserExtension(s.T(), "go-test-2")
|
|
||||||
|
|
||||||
device1, devicePubKey1 := tests.CreateDevice(s.T(), "go-test-device-1", "some-device-id-1")
|
|
||||||
device2, devicePubKey2 := tests.CreateDevice(s.T(), "go-test-device-2", "some-device-id-2")
|
|
||||||
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey1, browserExt1, device1)
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey2, browserExt2, device2)
|
|
||||||
|
|
||||||
invalidResp := map[string]any{}
|
|
||||||
response := tests.DoAPIGet(s.T(), "/browser_extensions/some-invalid-id/devices/", &invalidResp)
|
|
||||||
assert.Equal(s.T(), 400, response.StatusCode)
|
|
||||||
assert.Contains(s.T(), invalidResp["Reason"], `Field validation for 'ExtensionId' failed on the 'uuid4'`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BrowserExtensionPairingTestSuite) TestGetPairedDevicesByNotExistingExtensionId() {
|
|
||||||
browserExt1 := tests.CreateBrowserExtension(s.T(), "go-test-1")
|
|
||||||
browserExt2 := tests.CreateBrowserExtension(s.T(), "go-test-2")
|
|
||||||
|
|
||||||
device1, devicePubKey1 := tests.CreateDevice(s.T(), "go-test-device-1", "some-device-id-1")
|
|
||||||
device2, devicePubKey2 := tests.CreateDevice(s.T(), "go-test-device-2", "some-device-id-2")
|
|
||||||
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey1, browserExt1, device1)
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey2, browserExt2, device2)
|
|
||||||
|
|
||||||
notExistingExtensionId := uuid.New()
|
|
||||||
var firstExtensionDevices []*tests.ExtensionPairedDeviceResponse
|
|
||||||
tests.DoAPISuccessGet(s.T(), "/browser_extensions/"+notExistingExtensionId.String()+"/devices/", &firstExtensionDevices)
|
|
||||||
assert.Len(s.T(), firstExtensionDevices, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BrowserExtensionPairingTestSuite) TestShareExtensionPublicKeyWithMobileDevice() {
|
|
||||||
browserExt := tests.CreateBrowserExtensionWithPublicKey(s.T(), "go-test", "b64-rsa-pub-key")
|
|
||||||
_, err := uuid.Parse(browserExt.Id)
|
|
||||||
require.NoError(s.T(), err)
|
|
||||||
|
|
||||||
device, devicePubKey := tests.CreateDevice(s.T(), "go-test-device", "some-device-id")
|
|
||||||
_, err = uuid.Parse(device.Id)
|
|
||||||
require.NoError(s.T(), err)
|
|
||||||
|
|
||||||
result := tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExt, device)
|
|
||||||
assert.Equal(s.T(), "b64-rsa-pub-key", result.ExtensionPublicKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BrowserExtensionPairingTestSuite) TestCannotPairSameDeviceAndExtensionTwice() {
|
|
||||||
browserExtension := tests.CreateBrowserExtensionWithPublicKey(s.T(), "go-test", "b64-rsa-pub-key")
|
|
||||||
device, devicePubKey := tests.CreateDevice(s.T(), "go-test-device", "some-device-id")
|
|
||||||
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExtension, device)
|
|
||||||
|
|
||||||
payload := struct {
|
|
||||||
ExtensionId string `json:"extension_id"`
|
|
||||||
DeviceName string `json:"device_name"`
|
|
||||||
DevicePublicKey string `json:"device_public_key"`
|
|
||||||
}{
|
|
||||||
ExtensionId: browserExtension.Id,
|
|
||||||
DeviceName: device.Name,
|
|
||||||
DevicePublicKey: "device-pub-key",
|
|
||||||
}
|
|
||||||
|
|
||||||
pairingResult := new(tests.PairingResultResponse)
|
|
||||||
payloadJson, _ := json.Marshal(payload)
|
|
||||||
|
|
||||||
tests.DoAPIPostAndAssertCode(s.T(), 409, "/mobile/devices/"+device.Id+"/browser_extensions", payloadJson, pairingResult)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getExtensionPairedDevices(t *testing.T, browserExt *tests.BrowserExtensionResponse) []*tests.ExtensionPairedDeviceResponse {
|
|
||||||
var extensionDevices []*tests.ExtensionPairedDeviceResponse
|
|
||||||
tests.DoAPISuccessGet(t, "/browser_extensions/"+browserExt.Id+"/devices/", &extensionDevices)
|
|
||||||
return extensionDevices
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/suite"
|
|
||||||
"github.com/twofas/2fas-server/tests"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMobileDeviceExtensionIntegrationTestSuite(t *testing.T) {
|
|
||||||
suite.Run(t, new(MobileDeviceExtensionIntegrationTestSuite))
|
|
||||||
}
|
|
||||||
|
|
||||||
type MobileDeviceExtensionIntegrationTestSuite struct {
|
|
||||||
suite.Suite
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *MobileDeviceExtensionIntegrationTestSuite) SetupTest() {
|
|
||||||
tests.RemoveAllMobileDevices(s.T())
|
|
||||||
tests.RemoveAllBrowserExtensions(s.T())
|
|
||||||
tests.RemoveAllBrowserExtensionsDevices(s.T())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *MobileDeviceExtensionIntegrationTestSuite) TestGetPending2FaRequests() {
|
|
||||||
device, devicePubKey := tests.CreateDevice(s.T(), "SM-955F", "fcm-token")
|
|
||||||
browserExtension := tests.CreateBrowserExtension(s.T(), "go-ext")
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExtension, device)
|
|
||||||
|
|
||||||
var tokenRequest *tests.AuthTokenRequestResponse
|
|
||||||
request2FaTokenPayload := []byte(`{"domain":"domain.com"}`)
|
|
||||||
tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/commands/request_2fa_token", request2FaTokenPayload, &tokenRequest)
|
|
||||||
|
|
||||||
var tokenRequestsCollection []*tests.AuthTokenRequestResponse
|
|
||||||
tests.DoAPISuccessGet(s.T(), "mobile/devices/"+device.Id+"/browser_extensions/2fa_requests", &tokenRequestsCollection)
|
|
||||||
assert.Len(s.T(), tokenRequestsCollection, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *MobileDeviceExtensionIntegrationTestSuite) TestDoNotReturnCompleted2FaRequests() {
|
|
||||||
device, devicePubKey := tests.CreateDevice(s.T(), "SM-955F", "fcm-token")
|
|
||||||
browserExtension := tests.CreateBrowserExtension(s.T(), "go-ext")
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExtension, device)
|
|
||||||
|
|
||||||
var tokenRequest *tests.AuthTokenRequestResponse
|
|
||||||
request2FaTokenPayload := []byte(`{"domain":"domain.com"}`)
|
|
||||||
tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/commands/request_2fa_token", request2FaTokenPayload, &tokenRequest)
|
|
||||||
|
|
||||||
closeTokenRequestPayload := []byte(`{"status":"completed"}`)
|
|
||||||
tests.DoAPISuccessPost(s.T(), "browser_extensions/"+browserExtension.Id+"/2fa_requests/"+tokenRequest.Id+"/commands/close_2fa_request", closeTokenRequestPayload, nil)
|
|
||||||
|
|
||||||
var tokenRequestsCollection []*tests.AuthTokenRequestResponse
|
|
||||||
tests.DoAPISuccessGet(s.T(), "mobile/devices/"+device.Id+"/browser_extensions/2fa_requests", &tokenRequestsCollection)
|
|
||||||
assert.Len(s.T(), tokenRequestsCollection, 0)
|
|
||||||
}
|
|
@ -1,97 +0,0 @@
|
|||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/suite"
|
|
||||||
"github.com/twofas/2fas-server/tests"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMobileDeviceExtensionTestSuite(t *testing.T) {
|
|
||||||
suite.Run(t, new(MobileDeviceExtensionTestSuite))
|
|
||||||
}
|
|
||||||
|
|
||||||
type MobileDeviceExtensionTestSuite struct {
|
|
||||||
suite.Suite
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *MobileDeviceExtensionTestSuite) SetupTest() {
|
|
||||||
tests.RemoveAllMobileDevices(s.T())
|
|
||||||
tests.RemoveAllBrowserExtensions(s.T())
|
|
||||||
tests.RemoveAllBrowserExtensionsDevices(s.T())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *MobileDeviceExtensionTestSuite) TestDoNotFindExtensionsForNotExistingDevice() {
|
|
||||||
notExistingDeviceId := uuid.New()
|
|
||||||
|
|
||||||
response := tests.DoAPIGet(s.T(), "/mobile/devices/"+notExistingDeviceId.String()+"/browser_extensions", nil)
|
|
||||||
|
|
||||||
assert.Equal(s.T(), 404, response.StatusCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *MobileDeviceExtensionTestSuite) TestDoNotFindNotExistingMobileDeviceExtension() {
|
|
||||||
browserExt := tests.CreateBrowserExtension(s.T(), "go-test")
|
|
||||||
device, devicePubKey := tests.CreateDevice(s.T(), "go-test-device", "some-device-id")
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExt, device)
|
|
||||||
|
|
||||||
notExistingExtensionId := uuid.New()
|
|
||||||
response := tests.DoAPIGet(s.T(), "/mobile/devices/"+device.Id+"/browser_extensions/"+notExistingExtensionId.String(), nil)
|
|
||||||
|
|
||||||
assert.Equal(s.T(), 404, response.StatusCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *MobileDeviceExtensionTestSuite) Test_FindExtensionForDevice() {
|
|
||||||
browserExt := tests.CreateBrowserExtension(s.T(), "go-test")
|
|
||||||
device, devicePubKey := tests.CreateDevice(s.T(), "go-test-device", "some-device-id")
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExt, device)
|
|
||||||
|
|
||||||
var deviceBrowserExtension *tests.BrowserExtensionResponse
|
|
||||||
tests.DoAPISuccessGet(s.T(), "/mobile/devices/"+device.Id+"/browser_extensions/"+browserExt.Id, &deviceBrowserExtension)
|
|
||||||
|
|
||||||
assert.Equal(s.T(), browserExt.Id, deviceBrowserExtension.Id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *MobileDeviceExtensionTestSuite) Test_FindAllDeviceExtensions() {
|
|
||||||
browserExt1 := tests.CreateBrowserExtension(s.T(), "go-test-1")
|
|
||||||
browserExt2 := tests.CreateBrowserExtension(s.T(), "go-test-2")
|
|
||||||
device, devicePubKey := tests.CreateDevice(s.T(), "go-test-device", "some-device-id")
|
|
||||||
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExt1, device)
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExt2, device)
|
|
||||||
|
|
||||||
var deviceBrowserExtensions []*tests.BrowserExtensionResponse
|
|
||||||
tests.DoAPISuccessGet(s.T(), "/mobile/devices/"+device.Id+"/browser_extensions/", &deviceBrowserExtensions)
|
|
||||||
|
|
||||||
assert.Len(s.T(), deviceBrowserExtensions, 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *MobileDeviceExtensionTestSuite) Test_DisconnectExtensionFromDevice() {
|
|
||||||
browserExt1 := tests.CreateBrowserExtension(s.T(), "go-test")
|
|
||||||
browserExt2 := tests.CreateBrowserExtension(s.T(), "go-test")
|
|
||||||
device, devicePubKey := tests.CreateDevice(s.T(), "go-test-device", "some-device-id")
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExt1, device)
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, browserExt2, device)
|
|
||||||
|
|
||||||
tests.DoAPISuccessDelete(s.T(), "/mobile/devices/"+device.Id+"/browser_extensions/"+browserExt1.Id)
|
|
||||||
|
|
||||||
var deviceBrowserExtension1 *tests.BrowserExtensionResponse
|
|
||||||
response := tests.DoAPIGet(s.T(), "/mobile/devices/"+device.Id+"/browser_extensions/"+browserExt1.Id, &deviceBrowserExtension1)
|
|
||||||
assert.Equal(s.T(), 404, response.StatusCode)
|
|
||||||
|
|
||||||
var deviceBrowserExtension2 *tests.BrowserExtensionResponse
|
|
||||||
tests.DoAPISuccessGet(s.T(), "/mobile/devices/"+device.Id+"/browser_extensions/"+browserExt2.Id, &deviceBrowserExtension2)
|
|
||||||
assert.Equal(s.T(), browserExt2.Id, deviceBrowserExtension2.Id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *MobileDeviceExtensionTestSuite) TestExtensionHasAlreadyBeenConnected() {
|
|
||||||
extension := tests.CreateBrowserExtension(s.T(), "go-test")
|
|
||||||
device, devicePubKey := tests.CreateDevice(s.T(), "go-test-device", "some-device-id")
|
|
||||||
tests.PairDeviceWithBrowserExtension(s.T(), devicePubKey, extension, device)
|
|
||||||
|
|
||||||
payload := []byte(fmt.Sprintf(`{"extension_id":"%s","device_name":"%s","device_public_key":"%s"}`, extension.Id, device.Name, devicePubKey))
|
|
||||||
|
|
||||||
tests.DoAPIPostAndAssertCode(s.T(), 409, "/mobile/devices/"+device.Id+"/browser_extensions", payload, nil)
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user