mirror of
https://github.com/twofas/2fas-server.git
synced 2024-12-04 16:20:13 +01:00
feat: e2e tests for pass
This commit is contained in:
parent
417d7d25b2
commit
ca51ed9c79
2
go.mod
2
go.mod
@ -14,7 +14,7 @@ require (
|
||||
github.com/go-sql-driver/mysql v1.7.1
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0
|
||||
github.com/google/uuid v1.3.1
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/gorilla/websocket v1.5.1
|
||||
github.com/jaswdr/faker v1.16.0
|
||||
github.com/kelseyhightower/envconfig v1.4.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
|
2
go.sum
2
go.sum
@ -229,6 +229,8 @@ github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qK
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
|
192
tests/pass/pass_test.go
Normal file
192
tests/pass/pass_test.go
Normal file
@ -0,0 +1,192 @@
|
||||
package pass
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type ConfigureBrowserExtensionResponse struct {
|
||||
BrowserExtensionPairingToken string `json:"browser_extension_pairing_token"`
|
||||
ConnectionToken string `json:"connection_token"`
|
||||
}
|
||||
|
||||
var (
|
||||
httpClient = http.DefaultClient
|
||||
wsDialer = websocket.DefaultDialer
|
||||
)
|
||||
|
||||
func TestPassHappyFlow(t *testing.T) {
|
||||
const api = "localhost:8082"
|
||||
|
||||
resp, err := configureBrowserExtension("http://" + api + "/browser_extension/configure")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to configure browser extension: %v", err)
|
||||
}
|
||||
|
||||
browserExtensionDone := make(chan struct{})
|
||||
mobileDone := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
defer close(browserExtensionDone)
|
||||
|
||||
err := browserExtensionWaitForConfirm("ws://"+api+"/browser_extension/wait_for_connection", resp.BrowserExtensionPairingToken)
|
||||
if err != nil {
|
||||
t.Errorf("Error when Browser Extension waited for confirm: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = proxyWebSocket(
|
||||
"ws://"+api+"/browser_extension/proxy_to_mobile",
|
||||
resp.BrowserExtensionPairingToken,
|
||||
"sent from browser extension",
|
||||
"sent from mobile")
|
||||
if err != nil {
|
||||
t.Errorf("Browser Extension: proxy failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
}()
|
||||
go func() {
|
||||
defer close(mobileDone)
|
||||
|
||||
err := confirmMobile("http://"+api+"/mobile/confirm", resp.ConnectionToken)
|
||||
if err != nil {
|
||||
t.Errorf("Mobile: confirm failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = proxyWebSocket(
|
||||
"ws://"+api+"/mobile/proxy_to_browser_extension",
|
||||
resp.BrowserExtensionPairingToken,
|
||||
"sent from mobile",
|
||||
"sent from browser extension",
|
||||
)
|
||||
if err != nil {
|
||||
t.Errorf("Mobile: proxy failed: %v", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
<-browserExtensionDone
|
||||
<-mobileDone
|
||||
}
|
||||
|
||||
func browserExtensionWaitForConfirm(url, token string) error {
|
||||
var resp struct {
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
conn, err := dialWS(url, token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
conn.SetReadDeadline(time.Now().Add(time.Second))
|
||||
_, message, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading from connection: %w", err)
|
||||
}
|
||||
if err := json.Unmarshal(message, &resp); err != nil {
|
||||
return fmt.Errorf("failed to decode message: %w", err)
|
||||
}
|
||||
const expectedStatus = "ok"
|
||||
if resp.Status != expectedStatus {
|
||||
return fmt.Errorf("received status %q, expected %q", resp.Status, expectedStatus)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func configureBrowserExtension(endpoint string) (ConfigureBrowserExtensionResponse, error) {
|
||||
req, err := http.NewRequest("POST", endpoint, bytes.NewBufferString(`{"extension_id":"9317f6c1-0470-41c3-b735-542f2c6b70c3"}`))
|
||||
if err != nil {
|
||||
return ConfigureBrowserExtensionResponse{}, fmt.Errorf("failed to create http request: %w", err)
|
||||
}
|
||||
httpResp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return ConfigureBrowserExtensionResponse{}, fmt.Errorf("failed perform the request: %w", err)
|
||||
}
|
||||
defer httpResp.Body.Close()
|
||||
|
||||
var resp ConfigureBrowserExtensionResponse
|
||||
m := json.NewDecoder(httpResp.Body)
|
||||
if err := m.Decode(&resp); err != nil {
|
||||
return resp, fmt.Errorf("failed to decode the response: %w", err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func confirmMobile(endpoint, connectionToken string) error {
|
||||
req, err := http.NewRequest("POST", endpoint, bytes.NewBufferString(`{"device_id":"d317f6c1-0470-41c3-b735-542f2c6b70c8"})`))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to prepare the reqest: %w", err)
|
||||
}
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", connectionToken))
|
||||
|
||||
httpResp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to perform the reqest: %w", err)
|
||||
}
|
||||
defer httpResp.Body.Close()
|
||||
|
||||
if httpResp.StatusCode > 299 {
|
||||
return fmt.Errorf("unexpected response: %s", httpResp.Status)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func proxyWebSocket(endpoint string, token string, writeMsg, expectedReadMsg string) error {
|
||||
conn, err := dialWS(endpoint, token)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
doneReading := make(chan error)
|
||||
|
||||
go func() {
|
||||
defer close(doneReading)
|
||||
_, message, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
doneReading <- fmt.Errorf("faile to read message: %w", err)
|
||||
return
|
||||
}
|
||||
if string(message) != expectedReadMsg {
|
||||
doneReading <- fmt.Errorf("expected to read %q, read %q", expectedReadMsg, string(message))
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
if err := conn.WriteMessage(websocket.TextMessage, []byte(writeMsg)); err != nil {
|
||||
return fmt.Errorf("failed to write message: %w", err)
|
||||
}
|
||||
err, _ = <-doneReading
|
||||
if err != nil {
|
||||
return fmt.Errorf("error when reading: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func dialWS(url, auth string) (*websocket.Conn, error) {
|
||||
authEncodedAsProtocol := fmt.Sprintf("base64url.bearer.authorization.2pass.io.%s", base64.RawURLEncoding.EncodeToString([]byte(auth)))
|
||||
|
||||
conn, _, err := wsDialer.Dial(url, http.Header{
|
||||
"Sec-WebSocket-Protocol": []string{
|
||||
"2pass.io",
|
||||
authEncodedAsProtocol,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to dial ws %q: %v", url, err)
|
||||
}
|
||||
return conn, nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user