Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor internal packages #1116

Merged
merged 5 commits into from
Mar 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 0 additions & 82 deletions internal/httpauth/auth.go

This file was deleted.

68 changes: 24 additions & 44 deletions internal/httpauth/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ const (
invalidNonceErrorMessage = "SW-Nonce does not match"
)

// Error is the object returned to the client when there's an error.
type Error struct {
Error string `json:"error"`
}

// NextNonceResponse represents a ServeHTTP response for json encoding
type NextNonceResponse struct {
Edge cipher.PubKey `json:"edge"`
Expand All @@ -42,14 +47,12 @@ type HTTPError struct {
}

// Client implements Client for auth services.
// As Client needs to dial both with reusing address and without it, it uses two http clients: reuseClient and client.
type Client struct {
// atomic requires 64-bit alignment for struct field access
nonce uint64
mu sync.Mutex
reqMu sync.Mutex
client *http.Client
reuseClient *http.Client
key cipher.PubKey
sec cipher.SecKey
addr string // sanitized address of the client, which may differ from addr used in NewClient
Expand All @@ -67,7 +70,6 @@ func NewClient(ctx context.Context, addr string, key cipher.PubKey, sec cipher.S
mLog *logging.MasterLogger) (*Client, error) {
c := &Client{
client: client,
reuseClient: client,
key: key,
sec: sec,
addr: sanitizedAddr(addr),
Expand All @@ -85,28 +87,6 @@ func NewClient(ctx context.Context, addr string, key cipher.PubKey, sec cipher.S
return c, nil
}

// Header returns headers for httpauth.
func (c *Client) Header() (http.Header, error) {
nonce := c.getCurrentNonce()
body := make([]byte, 0)
sign, err := Sign(body, nonce, c.sec)
if err != nil {
return nil, err
}

header := make(http.Header)

// use nonce, later, if no err from req update such nonce
header.Set("SW-Nonce", strconv.FormatUint(uint64(nonce), 10))
header.Set("SW-Sig", sign.Hex())
header.Set("SW-Public", c.key.Hex())
if c.clientPublicIP != "" {
header.Set("SW-PublicIP", c.clientPublicIP)
}

return header, nil
}

// Do performs a new authenticated Request and returns the response. Internally, if the request was
// successful nonce is incremented
func (c *Client) Do(req *http.Request) (*http.Response, error) {
Expand Down Expand Up @@ -189,7 +169,7 @@ func (c *Client) Nonce(ctx context.Context, key cipher.PubKey) (Nonce, error) {
}()

if resp.StatusCode != http.StatusOK {
return 0, fmt.Errorf("error getting current nonce: status: %d <- %v", resp.StatusCode, extractError(resp.Body))
return 0, fmt.Errorf("error getting current nonce: status: %d <- %v", resp.StatusCode, extractHTTPError(resp.Body))
}

var nr NextNonceResponse
Expand All @@ -200,22 +180,6 @@ func (c *Client) Nonce(ctx context.Context, key cipher.PubKey) (Nonce, error) {
return nr.NextNonce, nil
}

// ReuseClient returns HTTP client that reuses port for dialing.
func (c *Client) ReuseClient() *http.Client {
c.mu.Lock()
defer c.mu.Unlock()

return c.reuseClient
}

// SetTransport sets transport for HTTP client that reuses port for dialing.
func (c *Client) SetTransport(transport http.RoundTripper) {
c.mu.Lock()
defer c.mu.Unlock()

c.reuseClient.Transport = transport
}

// SetNonce sets client current nonce to given nonce
func (c *Client) SetNonce(n Nonce) {
atomic.StoreUint64(&c.nonce, uint64(n))
Expand Down Expand Up @@ -301,8 +265,8 @@ func sanitizedAddr(addr string) string {
return u.String()
}

// extractError returns the decoded error message from Body.
func extractError(r io.Reader) error {
// extractHTTPError returns the decoded error message from Body.
func extractHTTPError(r io.Reader) error {
var serverError HTTPResponse

body, err := ioutil.ReadAll(r)
Expand All @@ -316,3 +280,19 @@ func extractError(r io.Reader) error {

return errors.New(serverError.Error.Message)
}

// ExtractError returns the decoded error message from Body.
func ExtractError(r io.Reader) error {
var apiError Error

body, err := ioutil.ReadAll(r)
if err != nil {
return err
}

if err := json.Unmarshal(body, &apiError); err != nil {
return errors.New(string(body))
}

return errors.New(apiError.Error)
}
22 changes: 22 additions & 0 deletions internal/httpauth/nonce.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package httpauth

import (
"fmt"

"github.com/skycoin/dmsg/cipher"
)

// Nonce is used to sign requests in order to avoid replay attack
type Nonce uint64

func (n Nonce) String() string { return fmt.Sprintf("%d", n) }

// PayloadWithNonce returns the concatenation of payload and nonce.
func PayloadWithNonce(payload []byte, nonce Nonce) []byte {
return []byte(fmt.Sprintf("%s%d", string(payload), nonce))
}

// Sign signs the Hash of payload and nonce
func Sign(payload []byte, nonce Nonce, sec cipher.SecKey) (cipher.Sig, error) {
return cipher.SignPayload(PayloadWithNonce(payload, nonce), sec)
}
24 changes: 0 additions & 24 deletions internal/testhelpers/testhelpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,10 @@ package testhelpers

import (
"errors"
"testing"
"time"

"github.com/stretchr/testify/require"
)

const timeout = 5 * time.Second

// NoErr is used with the mock interface to return from its methods.
var NoErr error

// Err is used with the mock interface to return some error from its methods.
var Err = errors.New("error")

// WithinTimeout tries to read an error from error channel within timeout and returns it.
// If timeout exceeds, nil value is returned.
func WithinTimeout(ch <-chan error) error {
select {
case err := <-ch:
return err
case <-time.After(timeout):
return nil
}
}

// NoErrorN performs require.NoError on multiple errors
func NoErrorN(t *testing.T, errs ...error) {
for _, err := range errs {
require.NoError(t, err)
}
}
27 changes: 1 addition & 26 deletions internal/utclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@ package utclient
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"time"

Expand All @@ -21,11 +17,6 @@ import (

//go:generate mockery -name APIClient -case underscore -inpkg

// Error is the object returned to the client when there's an error.
type Error struct {
Error string `json:"error"`
}

// APIClient implements uptime tracker API client.
type APIClient interface {
UpdateVisorUptime(context.Context) error
Expand Down Expand Up @@ -102,24 +93,8 @@ func (c *httpClient) UpdateVisorUptime(ctx context.Context) error {
}()

if resp.StatusCode != http.StatusOK {
return fmt.Errorf("status: %d, error: %w", resp.StatusCode, extractError(resp.Body))
return fmt.Errorf("status: %d, error: %w", resp.StatusCode, httpauth.ExtractError(resp.Body))
}

return nil
}

// extractError returns the decoded error message from Body.
func extractError(r io.Reader) error {
var apiError Error

body, err := ioutil.ReadAll(r)
if err != nil {
return err
}

if err := json.Unmarshal(body, &apiError); err != nil {
return errors.New(string(body))
}

return errors.New(apiError.Error)
}
32 changes: 0 additions & 32 deletions internal/vpn/net.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,8 @@ package vpn
import (
"encoding/json"
"fmt"
"io"
"net"
"time"

"github.com/skycoin/dmsg/cipher"
"github.com/skycoin/dmsg/noise"

"github.com/skycoin/skywire/pkg/app/appnet"
)

// WriteJSONWithTimeout marshals `data` and sends it over the `conn` with the specified write `timeout`.
Expand Down Expand Up @@ -92,29 +86,3 @@ func ReadJSON(conn net.Conn, data interface{}) error {

return nil
}

// WrapRWWithNoise wraps `conn` with noise.
func WrapRWWithNoise(conn net.Conn, initiator bool, pk cipher.PubKey, sk cipher.SecKey) (io.ReadWriter, error) {
remoteAddr, isAppConn := conn.RemoteAddr().(appnet.Addr)
if isAppConn {
ns, err := noise.New(noise.HandshakeKK, noise.Config{
LocalPK: pk,
LocalSK: sk,
RemotePK: remoteAddr.PubKey,
Initiator: initiator,
})
if err != nil {
return nil, fmt.Errorf("failed to prepare stream noise object: %w", err)
}

rw := noise.NewReadWriter(conn, ns)
if err := rw.Handshake(HSTimeout); err != nil {
return nil, fmt.Errorf("error performing noise handshake: %w", err)
}

return rw, nil
}

// shouldn't happen, but no encryption in this case
return conn, nil
}
Loading