storj/cmd/uplinkng/external_access.go

139 lines
3.1 KiB
Go
Raw Normal View History

// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
package main
import (
"context"
"encoding/json"
"os"
"strings"
"github.com/zeebo/errs"
"storj.io/uplink"
)
func (ex *external) loadAccesses() error {
if ex.access.accesses != nil {
return nil
}
fh, err := os.Open(ex.AccessInfoFile())
if os.IsNotExist(err) {
return nil
} else if err != nil {
return errs.Wrap(err)
}
defer func() { _ = fh.Close() }()
var jsonInput struct {
Default string
Accesses map[string]string
}
if err := json.NewDecoder(fh).Decode(&jsonInput); err != nil {
return errs.Wrap(err)
}
ex.access.defaultName = jsonInput.Default
ex.access.accesses = jsonInput.Accesses
ex.access.loaded = true
return nil
}
func (ex *external) OpenAccess(accessName string) (access *uplink.Access, err error) {
accessDefault, accesses, err := ex.GetAccessInfo(true)
if err != nil {
return nil, err
}
if accessName != "" {
accessDefault = accessName
}
if data, ok := accesses[accessDefault]; ok {
access, err = uplink.ParseAccess(data)
} else {
access, err = uplink.ParseAccess(accessDefault)
// TODO: if this errors then it's probably a name so don't report an error
// that says "it failed to parse"
}
if err != nil {
return nil, err
}
return access, nil
}
func (ex *external) GetAccessInfo(required bool) (string, map[string]string, error) {
if !ex.access.loaded {
if err := ex.loadAccesses(); err != nil {
return "", nil, err
}
if required && !ex.access.loaded {
return "", nil, errs.New("No accesses configured. Use 'access save' to create one")
}
}
// return a copy to avoid mutations messing things up
accesses := make(map[string]string)
for name, accessData := range ex.access.accesses {
accesses[name] = accessData
}
return ex.access.defaultName, accesses, nil
}
// SaveAccessInfo writes out the access file using the provided values.
func (ex *external) SaveAccessInfo(defaultName string, accesses map[string]string) error {
// TODO(jeff): write it atomically
accessFh, err := os.OpenFile(ex.AccessInfoFile(), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return errs.Wrap(err)
}
defer func() { _ = accessFh.Close() }()
var jsonOutput = struct {
Default string
Accesses map[string]string
}{
Default: defaultName,
Accesses: accesses,
}
data, err := json.MarshalIndent(jsonOutput, "", "\t")
if err != nil {
return errs.Wrap(err)
}
if _, err := accessFh.Write(data); err != nil {
return errs.Wrap(err)
}
if err := accessFh.Sync(); err != nil {
return errs.Wrap(err)
}
if err := accessFh.Close(); err != nil {
return errs.Wrap(err)
}
return nil
}
func (ex *external) RequestAccess(ctx context.Context, token, passphrase string) (*uplink.Access, error) {
idx := strings.IndexByte(token, '/')
if idx == -1 {
return nil, errs.New("invalid setup token. should be 'satelliteAddress/apiKey'")
}
satelliteAddr, apiKey := token[:idx], token[idx+1:]
access, err := uplink.RequestAccessWithPassphrase(ctx, satelliteAddr, apiKey, passphrase)
if err != nil {
return nil, errs.Wrap(err)
}
return access, nil
}