Skip to content

Commit 7e84f15

Browse files
committed
api: add http basic auth support
1 parent 12baadc commit 7e84f15

6 files changed

Lines changed: 73 additions & 17 deletions

File tree

api/api.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,14 @@ func (h *Handler) setupRouter(address string) (*echo.Echo, error) {
106106
e.Use(middleware.Recover())
107107
}
108108

109+
if h.conf.HttpBasicAuth.Password != "" {
110+
username := h.conf.HttpBasicAuth.Username
111+
password := h.conf.HttpBasicAuth.Password
112+
e.Use(middleware.BasicAuth(func(u, p string, _ echo.Context) (bool, error) {
113+
return u == username && p == password, nil
114+
}))
115+
}
116+
109117
// Routes
110118

111119
// Metrics
@@ -205,10 +213,15 @@ func (cv *customValidator) Validate(i interface{}) error {
205213

206214
type Error struct {
207215
Message string `json:"error"`
216+
// AuthMessage is an error from Echo framework, particularly from Basic Auth middleware
217+
AuthMessage string `json:"message"`
208218
}
209219

210220
func (e Error) Error() string {
211-
return e.Message
221+
if e.Message != "" && e.AuthMessage != "" {
222+
return fmt.Sprintf("error: %s; message: %s", e.Message, e.AuthMessage)
223+
}
224+
return e.Message + e.AuthMessage
212225
}
213226

214227
func ErrorMessage(message string) Error {

api/apiclient/client.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package apiclient
33
import (
44
"bytes"
55
"encoding/json"
6+
"fmt"
67
"io"
78
"net/http"
89
"net/url"
@@ -30,6 +31,30 @@ func New(address string) *Client {
3031
}
3132
}
3233

34+
func NewWithAuth(address, username, password string) *Client {
35+
c := New(address)
36+
if password != "" {
37+
c.cli.Transport = &basicAuthTransport{
38+
username: username,
39+
password: password,
40+
inner: c.cli.Transport,
41+
}
42+
}
43+
return c
44+
}
45+
46+
type basicAuthTransport struct {
47+
username string
48+
password string
49+
inner http.RoundTripper
50+
}
51+
52+
func (t *basicAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
53+
req = req.Clone(req.Context())
54+
req.SetBasicAuth(t.username, t.password)
55+
return t.inner.RoundTrip(req)
56+
}
57+
3358
func (c *Client) KnownPeers() ([]entity.KnownPeersResponse, error) {
3459
knownPeers := make([]entity.KnownPeersResponse, 0)
3560
err := c.sendGetRequest(api.GetKnownPeersPath, &knownPeers)
@@ -206,9 +231,9 @@ func (c *Client) readResponseBody(resp *http.Response, responseRef interface{})
206231
apiError := api.Error{}
207232
err := json.NewDecoder(resp.Body).Decode(&apiError)
208233
if err != nil {
209-
return err
234+
return fmt.Errorf("status code: %d, error decoding json: %w", resp.StatusCode, err)
210235
}
211-
return apiError
236+
return fmt.Errorf("status code: %d, error: %w", resp.StatusCode, apiError)
212237
} else if responseRef != nil {
213238
return json.NewDecoder(resp.Body).Decode(responseRef)
214239
}

application_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func TestRemovePeer(t *testing.T) {
4545
ts.NoError(err)
4646

4747
peer2From1, err := peer1.api.KnownPeerConfig(peer2.PeerID())
48-
ts.EqualError(err, "peer not found")
48+
ts.EqualError(err, "status code: 404, error: peer not found")
4949
ts.Nil(peer2From1)
5050
_, blockedPeerExists := peer1.app.Conf.GetBlockedPeer(peer2.PeerID())
5151
ts.True(blockedPeerExists)
@@ -262,7 +262,7 @@ func TestUniquePeerAlias(t *testing.T) {
262262
time.Sleep(200 * time.Millisecond)
263263

264264
err = peer1.api.SendFriendRequest(peer3.PeerID(), "peer", "")
265-
ts.EqualError(err, api.ErrorPeerAliasIsNotUniq)
265+
ts.EqualError(err, "status code: 400, error: "+api.ErrorPeerAliasIsNotUniq)
266266
}
267267

268268
func TestUpdateUseAsExitNodeConfig(t *testing.T) {

cli/cli.go

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,16 @@ func (a *Application) init() {
108108
Usage: fmt.Sprintf("awl api address, example: %s", defaultApiAddr),
109109
Required: false,
110110
},
111+
&cli.StringFlag{
112+
Name: "api_user",
113+
Usage: "username for api basic auth",
114+
Required: false,
115+
},
116+
&cli.StringFlag{
117+
Name: "api_password",
118+
Usage: "password for api basic auth",
119+
Required: false,
120+
},
111121
},
112122
Commands: []*cli.Command{
113123
{
@@ -466,17 +476,24 @@ func (a *Application) init() {
466476
}
467477

468478
func (a *Application) initApiConnection(c *cli.Context) error {
479+
username := c.String("api_user")
480+
password := c.String("api_password")
481+
469482
apiAddr := c.String("api_addr")
470483
if apiAddr != "" {
471-
return a.initApiFromAddr(apiAddr)
484+
return a.initApiFromAddr(apiAddr, username, password)
472485
}
473486

474487
conf, errConfig := config.LoadConfig(eventbus.NewBus())
475488
if errConfig == nil {
476-
return a.initApiFromAddr(conf.HttpListenAddress)
489+
if username == "" && password == "" {
490+
username = conf.HttpBasicAuth.Username
491+
password = conf.HttpBasicAuth.Password
492+
}
493+
return a.initApiFromAddr(conf.HttpListenAddress, username, password)
477494
}
478495

479-
errDefault := a.initApiFromAddr(defaultApiAddr)
496+
errDefault := a.initApiFromAddr(defaultApiAddr, username, password)
480497
if errDefault == nil {
481498
return nil
482499
}
@@ -487,8 +504,8 @@ func (a *Application) initApiConnection(c *cli.Context) error {
487504
return errors.New("no connection to api server")
488505
}
489506

490-
func (a *Application) initApiFromAddr(addr string) error {
491-
api := apiclient.New(addr)
507+
func (a *Application) initApiFromAddr(addr, username, password string) error {
508+
api := apiclient.NewWithAuth(addr, username, password)
492509
_, err := api.PeerInfo()
493510
if err != nil {
494511
return fmt.Errorf("could not access api on address %s: %v", addr, err)

config/config.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ type (
4848
LoggerLevel string `json:"loggerLevel"`
4949
HttpListenAddress string `json:"httpListenAddress"`
5050
HttpListenOnAdminHost bool `json:"httpListenOnAdminHost"`
51+
HttpBasicAuth HttpBasicAuthConfig `json:"httpBasicAuth"`
5152
P2pNode P2pNodeConfig `json:"p2pNode"`
5253
VPNConfig VPNConfig `json:"vpn"`
5354
SOCKS5 SOCKS5Config `json:"socks5"`
@@ -123,6 +124,10 @@ type (
123124
TrayAutoCheckEnabled bool `json:"trayAutoCheckEnabled"`
124125
TrayAutoCheckInterval string `json:"trayAutoCheckInterval"`
125126
}
127+
HttpBasicAuthConfig struct {
128+
Username string `json:"username"`
129+
Password string `json:"password"`
130+
}
126131
)
127132

128133
func (c *Config) Save() {

test_suite_test.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import (
2323
"github.com/libp2p/go-libp2p/p2p/host/eventbus"
2424
"github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem"
2525
rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager"
26-
simlibp2p "github.com/libp2p/go-libp2p/x/simlibp2p"
26+
"github.com/libp2p/go-libp2p/x/simlibp2p"
2727
"github.com/marcopolo/simnet"
2828
"github.com/multiformats/go-multiaddr"
2929
"github.com/stretchr/testify/require"
@@ -87,7 +87,7 @@ func (ts *TestSuite) NewTestPeer(disableLogging bool) TestPeer {
8787
multiaddr.StringCast("/ip4/127.0.0.1/tcp/0"),
8888
multiaddr.StringCast("/ip4/127.0.0.1/udp/0/quic-v1"),
8989
}
90-
return ts.newTestPeer(disableLogging, listenAddrs, nil)
90+
return ts.newTestPeerWithConfig(disableLogging, listenAddrs, nil, nil)
9191
}
9292

9393
type ConfigModifier func(*config.Config)
@@ -106,10 +106,6 @@ type SOCKS5PeerConfig struct {
106106
ProxyingEnabled bool
107107
}
108108

109-
func (ts *TestSuite) newTestPeer(disableLogging bool, listenAddrs []multiaddr.Multiaddr, extraLibp2pOpts []libp2p.Option) TestPeer {
110-
return ts.newTestPeerWithConfig(disableLogging, listenAddrs, extraLibp2pOpts, nil)
111-
}
112-
113109
func (ts *TestSuite) newTestPeerWithSOCKS5(disableLogging bool, listenAddrs []multiaddr.Multiaddr, extraLibp2pOpts []libp2p.Option, socks5Conf *SOCKS5PeerConfig) TestPeer {
114110
return ts.newTestPeerWithConfig(disableLogging, listenAddrs, extraLibp2pOpts, func(c *config.Config) {
115111
if socks5Conf != nil {
@@ -178,7 +174,7 @@ func (ts *TestSuite) newTestPeerWithConfig(disableLogging bool, listenAddrs []mu
178174

179175
tp := TestPeer{
180176
app: app,
181-
api: apiclient.New(app.Api.Address()),
177+
api: apiclient.NewWithAuth(app.Api.Address(), app.Conf.HttpBasicAuth.Username, app.Conf.HttpBasicAuth.Password),
182178
tun: testTUN,
183179
}
184180

0 commit comments

Comments
 (0)