storageode/apikey: added service, CLI issue api key

Change-Id: I840cd0fdbd8dca884eefbd111f21fd3990c11e68
This commit is contained in:
Qweder93 2020-11-09 17:22:30 +02:00 committed by Nikolai Siedov
parent 2b59640f18
commit a17cd9aa3e
7 changed files with 121 additions and 19 deletions

View File

@ -24,6 +24,7 @@ import (
"storj.io/storj/pkg/revocation"
_ "storj.io/storj/private/version" // This attaches version information during release builds.
"storj.io/storj/storagenode"
"storj.io/storj/storagenode/apikeys"
"storj.io/storj/storagenode/storagenodedb"
)
@ -85,6 +86,11 @@ var (
RunE: cmdGracefulExitStatus,
Annotations: map[string]string{"type": "helper"},
}
issueAPITokenCmd = &cobra.Command{
Use: "issue-apikey",
Short: "Issue apikey for mnd",
RunE: cmdIssue,
}
runCfg StorageNodeFlags
setupCfg StorageNodeFlags
@ -119,6 +125,7 @@ func init() {
rootCmd.AddCommand(dashboardCmd)
rootCmd.AddCommand(gracefulExitInitCmd)
rootCmd.AddCommand(gracefulExitStatusCmd)
rootCmd.AddCommand(issueAPITokenCmd)
process.Bind(runCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
process.Bind(setupCmd, &setupCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir), cfgstruct.SetupMode())
process.Bind(configCmd, &setupCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir), cfgstruct.SetupMode())
@ -126,6 +133,7 @@ func init() {
process.Bind(dashboardCmd, &dashboardCfg, defaults, cfgstruct.ConfDir(defaultDiagDir))
process.Bind(gracefulExitInitCmd, &diagCfg, defaults, cfgstruct.ConfDir(defaultDiagDir))
process.Bind(gracefulExitStatusCmd, &diagCfg, defaults, cfgstruct.ConfDir(defaultDiagDir))
process.Bind(issueAPITokenCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
}
func cmdRun(cmd *cobra.Command, args []string) (err error) {
@ -286,6 +294,36 @@ func cmdConfig(cmd *cobra.Command, args []string) (err error) {
return fpath.EditFile(conf)
}
func cmdIssue(cmd *cobra.Command, args []string) (err error) {
ctx, _ := process.Ctx(cmd)
ident, err := runCfg.Identity.Load()
if err != nil {
zap.L().Fatal("Failed to load identity.", zap.Error(err))
} else {
zap.L().Info("Identity loaded.", zap.Stringer("Node ID", ident.ID))
}
db, err := storagenodedb.OpenExisting(ctx, zap.L().Named("db"), diagCfg.DatabaseConfig())
if err != nil {
return errs.New("Error starting master database on storage node: %v", err)
}
defer func() {
err = errs.Combine(err, db.Close())
}()
service := apikeys.NewService(db.Secret())
apiKey, err := service.Issue(ctx)
if err != nil {
return errs.New("Error while trying to issue new api key: %v", err)
}
fmt.Print(apiKey.Secret.String())
return
}
func cmdDiag(cmd *cobra.Command, args []string) (err error) {
ctx, _ := process.Ctx(cmd)

View File

@ -1,7 +1,7 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package apikey
package apikeys
import (
"bytes"
@ -41,8 +41,8 @@ type APIKey struct {
CreatedAt time.Time `json:"createdAt"`
}
// NewSecretToken creates new apikey token.
func NewSecretToken() (Secret, error) {
// NewSecret creates new apikey secret.
func NewSecret() (Secret, error) {
var b [32]byte
_, err := rand.Read(b[:])

View File

@ -1,7 +1,7 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package apikey_test
package apikeys_test
import (
"testing"
@ -11,20 +11,20 @@ import (
"storj.io/common/testcontext"
"storj.io/storj/storagenode"
"storj.io/storj/storagenode/apikey"
"storj.io/storj/storagenode/apikeys"
"storj.io/storj/storagenode/storagenodedb/storagenodedbtest"
)
func TestSecretDB(t *testing.T) {
storagenodedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db storagenode.DB) {
secrets := db.Secret()
token, err := apikey.NewSecretToken()
token, err := apikeys.NewSecret()
assert.NoError(t, err)
token2, err := apikey.NewSecretToken()
token2, err := apikeys.NewSecret()
assert.NoError(t, err)
t.Run("Test StoreSecret", func(t *testing.T) {
err := secrets.Store(ctx, apikey.APIKey{
err := secrets.Store(ctx, apikeys.APIKey{
Secret: token,
CreatedAt: time.Now().UTC(),
})

View File

@ -0,0 +1,64 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package apikeys
import (
"context"
"time"
"github.com/spacemonkeygo/monkit/v3"
"github.com/zeebo/errs"
)
var (
// ErrService defines secret service error.
ErrService = errs.Class("secret service error")
mon = monkit.Package()
)
// Service responsible for operations with storagenode's uniq secret.
//
// architecture: Service
type Service struct {
store DB
}
// NewService is a constructor for service.
func NewService(db DB) *Service {
return &Service{store: db}
}
// Issue generates new api key and stores it into db.
func (service *Service) Issue(ctx context.Context) (apiKey APIKey, err error) {
defer mon.Task()(&ctx)(&err)
secret, err := NewSecret()
if err != nil {
return APIKey{}, ErrService.Wrap(err)
}
apiKey.Secret = secret
apiKey.CreatedAt = time.Now()
err = service.store.Store(ctx, apiKey)
if err != nil {
return APIKey{}, ErrService.Wrap(err)
}
return apiKey, nil
}
// Check returns error if api key does not exists.
func (service *Service) Check(ctx context.Context, secret Secret) (err error) {
defer mon.Task()(&ctx)(&err)
return service.store.Check(ctx, secret)
}
// Remove revokes apikey, deletes it from db.
func (service *Service) Remove(ctx context.Context, secret Secret) (err error) {
defer mon.Task()(&ctx)(&err)
return ErrService.Wrap(service.store.Revoke(ctx, secret))
}

View File

@ -31,7 +31,7 @@ import (
"storj.io/storj/private/version/checker"
"storj.io/storj/storage"
"storj.io/storj/storage/filestore"
"storj.io/storj/storagenode/apikey"
"storj.io/storj/storagenode/apikeys"
"storj.io/storj/storagenode/bandwidth"
"storj.io/storj/storagenode/collector"
"storj.io/storj/storagenode/console"
@ -88,7 +88,7 @@ type DB interface {
Notifications() notifications.DB
Payout() payout.DB
Pricing() pricing.DB
Secret() apikey.DB
Secret() apikeys.DB
Preflight(ctx context.Context) error
}

View File

@ -10,11 +10,11 @@ import (
"github.com/zeebo/errs"
"storj.io/storj/storagenode/apikey"
"storj.io/storj/storagenode/apikeys"
)
// ensures that secretDB implements apikey.DB interface.
var _ apikey.DB = (*secretDB)(nil)
// ensures that secretDB implements apikeys.DB interface.
var _ apikeys.DB = (*secretDB)(nil)
// ErrSecret represents errors from the apikey database.
var ErrSecret = errs.Class("apikey db error")
@ -28,7 +28,7 @@ type secretDB struct {
}
// Store stores apikey into database.
func (db *secretDB) Store(ctx context.Context, secret apikey.APIKey) (err error) {
func (db *secretDB) Store(ctx context.Context, secret apikeys.APIKey) (err error) {
defer mon.Task()(&ctx)(&err)
query := `INSERT INTO secret (
@ -45,7 +45,7 @@ func (db *secretDB) Store(ctx context.Context, secret apikey.APIKey) (err error)
}
// Check checks if apikey exists in db by token.
func (db *secretDB) Check(ctx context.Context, token apikey.Secret) (err error) {
func (db *secretDB) Check(ctx context.Context, token apikeys.Secret) (err error) {
defer mon.Task()(&ctx)(&err)
var bytes []uint8
@ -62,7 +62,7 @@ func (db *secretDB) Check(ctx context.Context, token apikey.Secret) (err error)
)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return apikey.ErrNoSecret.Wrap(err)
return apikeys.ErrNoSecret.Wrap(err)
}
return ErrSecret.Wrap(err)
}
@ -71,7 +71,7 @@ func (db *secretDB) Check(ctx context.Context, token apikey.Secret) (err error)
}
// Revoke removes apikey from db.
func (db *secretDB) Revoke(ctx context.Context, secret apikey.Secret) (err error) {
func (db *secretDB) Revoke(ctx context.Context, secret apikeys.Secret) (err error) {
defer mon.Task()(&ctx)(&err)
query := `DELETE FROM secret WHERE token = ?`

View File

@ -25,7 +25,7 @@ import (
"storj.io/storj/private/tagsql"
"storj.io/storj/storage"
"storj.io/storj/storage/filestore"
"storj.io/storj/storagenode/apikey"
"storj.io/storj/storagenode/apikeys"
"storj.io/storj/storagenode/bandwidth"
"storj.io/storj/storagenode/notifications"
"storj.io/storj/storagenode/orders"
@ -544,7 +544,7 @@ func (db *DB) Pricing() pricing.DB {
}
// Secret returns instance of the Secret database.
func (db *DB) Secret() apikey.DB {
func (db *DB) Secret() apikeys.DB {
return db.secretDB
}