Skip to content

Commit

Permalink
feat: add two auth tests
Browse files Browse the repository at this point in the history
  • Loading branch information
hacdias committed Nov 15, 2023
1 parent 72ccfb7 commit 767dc37
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 23 deletions.
29 changes: 29 additions & 0 deletions client/rpc/auth/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package auth

import "net/http"

var _ http.RoundTripper = &AuthorizedRoundTripper{}

type AuthorizedRoundTripper struct {
authorization string
roundTripper http.RoundTripper
}

// NewAuthorizedRoundTripper creates a new [http.RoundTripper] that will set the
// Authorization HTTP header with the value of [secret]. The given [roundTripper] is
// the base [http.RoundTripper]. If it is nil, [http.DefaultTransport] is used.
func NewAuthorizedRoundTripper(secret string, roundTripper http.RoundTripper) http.RoundTripper {
if roundTripper == nil {
roundTripper = http.DefaultTransport
}

return &AuthorizedRoundTripper{
authorization: secret,
roundTripper: roundTripper,
}
}

func (tp *AuthorizedRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
r.Header.Set("Authorization", tp.authorization)
return tp.roundTripper.RoundTrip(r)
}
18 changes: 2 additions & 16 deletions cmd/ipfs/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
cmdhttp "github.com/ipfs/go-ipfs-cmds/http"
logging "github.com/ipfs/go-log"
ipfs "github.com/ipfs/kubo"
"github.com/ipfs/kubo/client/rpc/auth"
"github.com/ipfs/kubo/cmd/ipfs/util"
oldcmds "github.com/ipfs/kubo/commands"
"github.com/ipfs/kubo/core"
Expand Down Expand Up @@ -327,10 +328,7 @@ func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) {

apiSecret, specified := req.Options[corecmds.ApiSecretOption].(string)
if specified {
tpt = &roundTripperWithAuthorization{
apiSecret: apiSecret,
roundTripper: tpt,
}
tpt = auth.NewAuthorizedRoundTripper(apiSecret, tpt)
}

Check warning on line 332 in cmd/ipfs/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/ipfs/main.go#L329-L332

Added lines #L329 - L332 were not covered by tests

httpClient := &http.Client{
Expand All @@ -348,18 +346,6 @@ func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) {
return tracingWrappedExecutor{cmdhttp.NewClient(host, opts...)}, nil
}

var _ http.RoundTripper = &roundTripperWithAuthorization{}

type roundTripperWithAuthorization struct {
apiSecret string
roundTripper http.RoundTripper
}

func (tp *roundTripperWithAuthorization) RoundTrip(r *http.Request) (*http.Response, error) {
r.Header.Set("Authorization", tp.apiSecret)
return tp.roundTripper.RoundTrip(r)
}

type tracingWrappedExecutor struct {
exec cmds.Executor
}
Expand Down
71 changes: 71 additions & 0 deletions test/cli/auth_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package cli

import (
"testing"

"github.com/ipfs/kubo/client/rpc/auth"
"github.com/ipfs/kubo/config"
"github.com/ipfs/kubo/test/cli/harness"
"github.com/stretchr/testify/assert"
)

func TestAuth(t *testing.T) {
t.Parallel()

makeAndStartProtectedNode := func(t *testing.T, authorizations map[string]*config.RPCAuthScope) *harness.Node {
authorizations["test-node-starter"] = &config.RPCAuthScope{
HTTPAuthSecret: "bearer:test-node-starter",
AllowedPaths: []string{"/api/v0"},
}

node := harness.NewT(t).NewNode().Init()
node.UpdateConfig(func(cfg *config.Config) {
cfg.API.Authorizations = authorizations
})
node.StartDaemonWithAuthorization("Bearer test-node-starter")
return node
}

t.Run("Follows Allowed Paths", func(t *testing.T) {
t.Parallel()

node := makeAndStartProtectedNode(t, map[string]*config.RPCAuthScope{
"userA": {
HTTPAuthSecret: "bearer:userAToken",
AllowedPaths: []string{"/api/v0/id"},
},
})

apiClient := node.APIClient()
apiClient.Client.Transport = auth.NewAuthorizedRoundTripper("Bearer userAToken", apiClient.Client.Transport)

// Can access ID.
resp := apiClient.Post("/api/v0/id", nil)
assert.Equal(t, 200, resp.StatusCode)

// But not Ping.
resp = apiClient.Post("/api/v0/ping", nil)
assert.Equal(t, 403, resp.StatusCode)

node.StopDaemon()
})

t.Run("Generic Allowed Path Gives Full Access", func(t *testing.T) {
t.Parallel()

node := makeAndStartProtectedNode(t, map[string]*config.RPCAuthScope{
"userA": {
HTTPAuthSecret: "bearer:userAToken",
AllowedPaths: []string{"/api/v0"},
},
})

apiClient := node.APIClient()
apiClient.Client.Transport = auth.NewAuthorizedRoundTripper("Bearer userAToken", apiClient.Client.Transport)

resp := apiClient.Post("/api/v0/id", nil)
assert.Equal(t, 200, resp.StatusCode)

node.StopDaemon()
})
}
29 changes: 22 additions & 7 deletions test/cli/harness/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ func (n *Node) Init(ipfsArgs ...string) *Node {
// harness.RunWithStdout(os.Stdout),
// },
// })
func (n *Node) StartDaemonWithReq(req RunRequest) *Node {
func (n *Node) StartDaemonWithReq(req RunRequest, authorization string) *Node {
alive := n.IsAlive()
if alive {
log.Panicf("node %d is already running", n.ID)
Expand All @@ -239,14 +239,20 @@ func (n *Node) StartDaemonWithReq(req RunRequest) *Node {
n.Daemon = res

log.Debugf("node %d started, checking API", n.ID)
n.WaitOnAPI()
n.WaitOnAPI(authorization)
return n
}

func (n *Node) StartDaemon(ipfsArgs ...string) *Node {
return n.StartDaemonWithReq(RunRequest{
Args: ipfsArgs,
})
}, "")
}

func (n *Node) StartDaemonWithAuthorization(secret string, ipfsArgs ...string) *Node {
return n.StartDaemonWithReq(RunRequest{
Args: ipfsArgs,
}, secret)
}

func (n *Node) signalAndWait(watch <-chan struct{}, signal os.Signal, t time.Duration) bool {
Expand Down Expand Up @@ -337,7 +343,7 @@ func (n *Node) TryAPIAddr() (multiaddr.Multiaddr, error) {
return ma, nil
}

func (n *Node) checkAPI() bool {
func (n *Node) checkAPI(authorization string) bool {
apiAddr, err := n.TryAPIAddr()
if err != nil {
log.Debugf("node %d API addr not available yet: %s", n.ID, err.Error())
Expand All @@ -353,7 +359,16 @@ func (n *Node) checkAPI() bool {
}
url := fmt.Sprintf("http://%s:%s/api/v0/id", ip, port)
log.Debugf("checking API for node %d at %s", n.ID, url)
httpResp, err := http.Post(url, "", nil)

req, err := http.NewRequest(http.MethodPost, url, nil)
if err != nil {
panic(err)
}
if authorization != "" {
req.Header.Set("Authorization", authorization)
}

httpResp, err := http.DefaultClient.Do(req)
if err != nil {
log.Debugf("node %d API check error: %s", err.Error())
return false
Expand Down Expand Up @@ -402,10 +417,10 @@ func (n *Node) PeerID() peer.ID {
return id
}

func (n *Node) WaitOnAPI() *Node {
func (n *Node) WaitOnAPI(authorization string) *Node {
log.Debugf("waiting on API for node %d", n.ID)
for i := 0; i < 50; i++ {
if n.checkAPI() {
if n.checkAPI(authorization) {
log.Debugf("daemon API found, daemon stdout: %s", n.Daemon.Stdout.String())
return n
}
Expand Down

0 comments on commit 767dc37

Please sign in to comment.