uplink/cmd: Export a RegisterAccess method.

Gateway-MT requires integration tests, which would be aided by having an
exported RegisterAccess() method in uplink/cmd.

To support this change, a little of the Uplink cmd logic was shifted around
and a method was made public.  I also normalized finding the access
between accessInspect and accessRegister.

Change-Id: I29369296521c2cc179e27233f5451b95f46109d8
This commit is contained in:
Bill Thorp 2020-12-17 17:25:23 -05:00 committed by Bill Thorp
parent 876e1be3b5
commit 1b0424cad6
3 changed files with 122 additions and 86 deletions

View File

@ -61,7 +61,7 @@ func init() {
registerCmd := &cobra.Command{
Use: "register [ACCESS]",
Short: "Register your access for use with a hosted gateway.",
RunE: registerAccess,
RunE: accessRegister,
Args: cobra.MaximumNArgs(1),
}
@ -90,25 +90,9 @@ func accessList(cmd *cobra.Command, args []string) (err error) {
}
func accessInspect(cmd *cobra.Command, args []string) (err error) {
var access *uplink.Access
if len(args) == 0 {
access, err = inspectCfg.GetAccess()
if err != nil {
return err
}
} else {
firstArg := args[0]
access, err = inspectCfg.GetNamedAccess(firstArg)
if err != nil {
return err
}
if access == nil {
if access, err = uplink.ParseAccess(firstArg); err != nil {
return err
}
}
access, err := getAccessFromArgZeroOrConfig(inspectCfg, args)
if err != nil {
return errs.New("no access specified: %w", err)
}
serializedAccesss, err := access.Serialize()
@ -149,78 +133,92 @@ func parseAccess(access string) (sa string, apiKey string, ea string, err error)
return p.SatelliteAddr, apiKey, ea, nil
}
func registerAccess(cmd *cobra.Command, args []string) (err error) {
if len(args) == 0 {
return errs.New("no access specified")
}
_, err = register(args[0], registerCfg.AuthService, registerCfg.Public)
return err
}
func register(accessRaw, authService string, public bool) (accessKey string, err error) {
if authService == "" {
return "", errs.New("no auth service address provided")
}
// try assuming that accessRaw is a named access
access, err := registerCfg.GetNamedAccess(accessRaw)
if err == nil && access != nil {
accessRaw, err = access.Serialize()
if err != nil {
return "", errs.New("error serializing named access '%s': %w", accessRaw, err)
}
}
postData, err := json.Marshal(map[string]interface{}{
"access_grant": accessRaw,
"public": public,
})
func accessRegister(cmd *cobra.Command, args []string) (err error) {
access, err := getAccessFromArgZeroOrConfig(inspectCfg, args)
if err != nil {
return accessKey, errs.Wrap(err)
return errs.New("no access specified: %w", err)
}
resp, err := http.Post(fmt.Sprintf("%s/v1/access", authService), "application/json", bytes.NewReader(postData))
accessKey, secretKey, endpoint, err := RegisterAccess(access, registerCfg.AuthService, registerCfg.Public)
if err != nil {
return "", err
}
defer func() { err = errs.Combine(err, resp.Body.Close()) }()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
respBody := make(map[string]string)
if err := json.Unmarshal(body, &respBody); err != nil {
return "", errs.New("unexpected response from auth service: %s", string(body))
}
accessKey, ok := respBody["access_key_id"]
if !ok {
return "", errs.New("access_key_id missing in response")
}
secretKey, ok := respBody["secret_key"]
if !ok {
return "", errs.New("secret_key missing in response")
return err
}
fmt.Println("========== CREDENTIALS ===================================================================")
fmt.Println("Access Key ID: ", accessKey)
fmt.Println("Secret Key : ", secretKey)
fmt.Println("Endpoint : ", respBody["endpoint"])
fmt.Println("Endpoint : ", endpoint)
// update AWS credential file if requested
if registerCfg.AWSProfile != "" {
credentialsPath, err := getAwsCredentialsPath()
if err != nil {
return "", err
return err
}
err = writeAWSCredentials(credentialsPath, registerCfg.AWSProfile, accessKey, secretKey)
if err != nil {
return "", err
return err
}
}
return accessKey, nil
return nil
}
func getAccessFromArgZeroOrConfig(config AccessConfig, args []string) (access *uplink.Access, err error) {
if len(args) != 0 {
access, err = inspectCfg.GetNamedAccess(args[0])
if err != nil {
return nil, err
}
if access != nil {
return access, nil
}
return uplink.ParseAccess(args[0])
}
return inspectCfg.GetAccess()
}
// RegisterAccess registers an access grant with a Gateway Authorization Service.
func RegisterAccess(access *uplink.Access, authService string, public bool) (accessKey, secretKey, endpoint string, err error) {
if authService == "" {
return "", "", "", errs.New("no auth service address provided")
}
accesssSerialized, err := access.Serialize()
if err != nil {
return "", "", "", errs.Wrap(err)
}
postData, err := json.Marshal(map[string]interface{}{
"access_grant": accesssSerialized,
"public": public,
})
if err != nil {
return accessKey, "", "", errs.Wrap(err)
}
resp, err := http.Post(fmt.Sprintf("%s/v1/access", authService), "application/json", bytes.NewReader(postData))
if err != nil {
return "", "", "", err
}
defer func() { err = errs.Combine(err, resp.Body.Close()) }()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", "", "", err
}
respBody := make(map[string]string)
if err := json.Unmarshal(body, &respBody); err != nil {
return "", "", "", errs.New("unexpected response from auth service: %s", string(body))
}
accessKey, ok := respBody["access_key_id"]
if !ok {
return "", "", "", errs.New("access_key_id missing in response")
}
secretKey, ok = respBody["secret_key"]
if !ok {
return "", "", "", errs.New("secret_key missing in response")
}
return accessKey, secretKey, respBody["endpoint"], nil
}
// getAwsCredentialsPath returns the expected AWS credentials path.

View File

@ -0,0 +1,38 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information
package cmd_test
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"storj.io/common/testcontext"
"storj.io/storj/cmd/uplink/cmd"
"storj.io/storj/private/testplanet"
)
func TestRegisterAccess(t *testing.T) {
testplanet.Run(t, testplanet.Config{
SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 1,
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
// mock the auth service
ts := httptest.NewServer(
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, `{"access_key_id":"1", "secret_key":"2", "endpoint":"3"}`)
}))
defer ts.Close()
// make sure we get back things
access := planet.Uplinks[0].Access[planet.Satellites[0].ID()]
accessKey, secretKey, endpoint, err := cmd.RegisterAccess(access, ts.URL, true)
require.NoError(t, err)
assert.Equal(t, "1", accessKey)
assert.Equal(t, "2", secretKey)
assert.Equal(t, "3", endpoint)
})
}

View File

@ -61,7 +61,7 @@ func init() {
}
func shareMain(cmd *cobra.Command, args []string) (err error) {
newAccessData, sharePrefixes, permission, err := createAccessGrant(args)
newAccess, newAccessData, sharePrefixes, permission, err := createAccessGrant(args)
if err != nil {
return err
}
@ -70,7 +70,7 @@ func shareMain(cmd *cobra.Command, args []string) (err error) {
if shareCfg.Register || shareCfg.URL || shareCfg.DNS != "" {
isPublic := (shareCfg.Public || shareCfg.URL || shareCfg.DNS != "")
accessKey, err = register(newAccessData, shareCfg.AuthService, isPublic)
accessKey, _, _, err = RegisterAccess(newAccess, shareCfg.AuthService, isPublic)
if err != nil {
return err
}
@ -105,15 +105,15 @@ func shareMain(cmd *cobra.Command, args []string) (err error) {
}
// Creates access grant for allowed path prefixes.
func createAccessGrant(args []string) (newAccessData string, sharePrefixes []sharePrefixExtension, permission uplink.Permission, err error) {
func createAccessGrant(args []string) (newAccess *uplink.Access, newAccessData string, sharePrefixes []sharePrefixExtension, permission uplink.Permission, err error) {
now := time.Now()
notBefore, err := parseHumanDate(shareCfg.NotBefore, now)
if err != nil {
return newAccessData, sharePrefixes, permission, err
return newAccess, newAccessData, sharePrefixes, permission, err
}
notAfter, err := parseHumanDate(shareCfg.NotAfter, now)
if err != nil {
return newAccessData, sharePrefixes, permission, err
return newAccess, newAccessData, sharePrefixes, permission, err
}
if len(shareCfg.AllowedPathPrefix) == 0 {
@ -128,10 +128,10 @@ func createAccessGrant(args []string) (newAccessData string, sharePrefixes []sha
for _, path := range shareCfg.AllowedPathPrefix {
p, err := fpath.New(path)
if err != nil {
return newAccessData, sharePrefixes, permission, err
return newAccess, newAccessData, sharePrefixes, permission, err
}
if p.IsLocal() {
return newAccessData, sharePrefixes, permission, errs.New("required path must be remote: %q", path)
return newAccess, newAccessData, sharePrefixes, permission, errs.New("required path must be remote: %q", path)
}
uplinkSharePrefix := uplink.SharePrefix{
@ -147,7 +147,7 @@ func createAccessGrant(args []string) (newAccessData string, sharePrefixes []sha
access, err := shareCfg.GetAccess()
if err != nil {
return newAccessData, sharePrefixes, permission, err
return newAccess, newAccessData, sharePrefixes, permission, err
}
permission = uplink.Permission{}
@ -158,19 +158,19 @@ func createAccessGrant(args []string) (newAccessData string, sharePrefixes []sha
permission.NotBefore = notBefore
permission.NotAfter = notAfter
newAccess, err := access.Share(permission, uplinkSharePrefixes...)
newAccess, err = access.Share(permission, uplinkSharePrefixes...)
if err != nil {
return newAccessData, sharePrefixes, permission, err
return newAccess, newAccessData, sharePrefixes, permission, err
}
newAccessData, err = newAccess.Serialize()
if err != nil {
return newAccessData, sharePrefixes, permission, err
return newAccess, newAccessData, sharePrefixes, permission, err
}
satelliteAddr, _, _, err := parseAccess(newAccessData)
if err != nil {
return newAccessData, sharePrefixes, permission, err
return newAccess, newAccessData, sharePrefixes, permission, err
}
fmt.Println("Sharing access to satellite", satelliteAddr)
@ -185,7 +185,7 @@ func createAccessGrant(args []string) (newAccessData string, sharePrefixes []sha
fmt.Println("=========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS ===========")
fmt.Println("Access :", newAccessData)
return newAccessData, sharePrefixes, permission, nil
return newAccess, newAccessData, sharePrefixes, permission, nil
}
// Creates linksharing url for allowed path prefixes.