Skip to content

Commit

Permalink
Allow access key configuration from "lakefs superuser" command
Browse files Browse the repository at this point in the history
Makes writing some integrations much easier.  Fixes #908.
  • Loading branch information
arielshaqed committed Nov 15, 2020
1 parent 148b8e8 commit 331f771
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 29 deletions.
2 changes: 1 addition & 1 deletion api/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func createDefaultAdminUser(authService auth.Service, t *testing.T) *authmodel.C
Username: "admin",
}

creds, err := auth.SetupAdminUser(authService, user)
creds, err := auth.SetupAdminUser(authService, &authmodel.SuperuserConfiguration{User: *user})
testutil.Must(t, err)
return creds
}
Expand Down
7 changes: 7 additions & 0 deletions auth/model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ type User struct {
Username string `db:"display_name" json:"display_name"`
}

// SuperuserConfiguration requests a particular configuration for a superuser.
type SuperuserConfiguration struct {
User
AccessKeyID string
SecretAccessKey string
}

type Group struct {
ID int `db:"id"`
CreatedAt time.Time `db:"created_at"`
Expand Down
15 changes: 10 additions & 5 deletions auth/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type Service interface {

// credentials
CreateCredentials(username string) (*model.Credential, error)
AddCredentials(username, accessKeyID, secretAccessKey string) (*model.Credential, error)
DeleteCredentials(username, accessKeyID string) error
GetCredentialsForUser(username, accessKeyID string) (*model.Credential, error)
GetCredentials(accessKeyID string) (*model.Credential, error)
Expand Down Expand Up @@ -639,10 +640,14 @@ func (s *DBAuthService) ListPolicies(params *model.PaginationParams) ([]*model.P
}

func (s *DBAuthService) CreateCredentials(username string) (*model.Credential, error) {
accessKeyID := genAccessKeyID()
secretAccessKey := genAccessSecretKey()
return s.AddCredentials(username, accessKeyID, secretAccessKey)
}

func (s *DBAuthService) AddCredentials(username, accessKeyID, secretAccessKey string) (*model.Credential, error) {
now := time.Now()
accessKey := genAccessKeyID()
secretKey := genAccessSecretKey()
encryptedKey, err := s.encryptSecret(secretKey)
encryptedKey, err := s.encryptSecret(secretAccessKey)
if err != nil {
return nil, err
}
Expand All @@ -652,8 +657,8 @@ func (s *DBAuthService) CreateCredentials(username string) (*model.Credential, e
return nil, err
}
c := &model.Credential{
AccessKeyID: accessKey,
AccessSecretKey: secretKey,
AccessKeyID: accessKeyID,
AccessSecretKey: secretAccessKey,
AccessSecretKeyEncryptedBytes: encryptedKey,
IssuedDate: now,
UserID: user.ID,
Expand Down
28 changes: 18 additions & 10 deletions auth/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ func SetupBaseGroups(authService Service, ts time.Time) error {
return nil
}

func SetupAdminUser(authService Service, user *model.User) (*model.Credential, error) {
func SetupAdminUser(authService Service, superuser *model.SuperuserConfiguration) (*model.Credential, error) {
now := time.Now()

// Setup the basic groups and policies
Expand All @@ -207,10 +207,10 @@ func SetupAdminUser(authService Service, user *model.User) (*model.Credential, e
return nil, err
}

return AddAdminUser(authService, user)
return AddAdminUser(authService, superuser)
}

func AddAdminUser(authService Service, user *model.User) (*model.Credential, error) {
func AddAdminUser(authService Service, user *model.SuperuserConfiguration) (*model.Credential, error) {
const adminGroupName = "Admins"

// verify admin group exists
Expand All @@ -220,7 +220,7 @@ func AddAdminUser(authService Service, user *model.User) (*model.Credential, err
}

// create admin user
err = authService.CreateUser(user)
err = authService.CreateUser(&user.User)
if err != nil {
return nil, fmt.Errorf("create user - %w", err)
}
Expand All @@ -229,19 +229,27 @@ func AddAdminUser(authService Service, user *model.User) (*model.Credential, err
return nil, fmt.Errorf("add user to group - %w", err)
}

// Generate and return a key pair
creds, err := authService.CreateCredentials(user.Username)
if err != nil {
return nil, fmt.Errorf("create credentials for %s %w", user.Username, err)
var creds *model.Credential
if user.AccessKeyID == "" {
// Generate and return a key pair
creds, err = authService.CreateCredentials(user.Username)
if err != nil {
return nil, fmt.Errorf("create credentials for %s %w", user.Username, err)
}
} else {
creds, err = authService.AddCredentials(user.Username, user.AccessKeyID, user.SecretAccessKey)
if err != nil {
return nil, fmt.Errorf("add credentials for %s %w", user.Username, err)
}
}
return creds, nil
}

func CreateInitialAdminUser(authService Service, metadataManger MetadataManager, username string) (*model.Credential, error) {
adminUser := &model.User{
adminUser := &model.SuperuserConfiguration{User: model.User{
CreatedAt: time.Now(),
Username: username,
}
}}
// create first admin user
cred, err := SetupAdminUser(authService, adminUser)
if err != nil {
Expand Down
9 changes: 7 additions & 2 deletions cmd/lakefs/cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ var initCmd = &cobra.Command{
dbPool := db.BuildDatabaseConnection(cfg.GetDatabaseParams())
defer dbPool.Close()

userName, _ := cmd.Flags().GetString("user-name")
userName, err := cmd.Flags().GetString("user-name")
if err != nil {
fmt.Printf("user-name: %s\n", err)
os.Exit(1)
}

authService := auth.NewDBAuthService(
dbPool,
Expand Down Expand Up @@ -64,6 +68,7 @@ var initCmd = &cobra.Command{
//nolint:gochecknoinits
func init() {
rootCmd.AddCommand(initCmd)
initCmd.Flags().String("user-name", "", "an identifier for the user (e.g. \"jane.doe\")")
f := initCmd.Flags()
f.String("user-name", "", "an identifier for the user (e.g. \"jane.doe\")")
_ = initCmd.MarkFlagRequired("user-name")
}
32 changes: 27 additions & 5 deletions cmd/lakefs/cmd/superuser.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,21 @@ var superuserCmd = &cobra.Command{
dbPool := db.BuildDatabaseConnection(cfg.GetDatabaseParams())
defer dbPool.Close()

userName, _ := cmd.Flags().GetString("user-name")
userName, err := cmd.Flags().GetString("user-name")
if err != nil {
fmt.Printf("user-name: %s\n", err)
os.Exit(1)
}
accessKeyID, err := cmd.Flags().GetString("access-key-id")
if err != nil {
fmt.Printf("access-key-id: %s\n", err)
os.Exit(1)
}
secretAccessKey, err := cmd.Flags().GetString("secret-access-key")
if err != nil {
fmt.Printf("secret-access-key: %s\n", err)
os.Exit(1)
}

authService := auth.NewDBAuthService(
dbPool,
Expand All @@ -33,9 +47,13 @@ var superuserCmd = &cobra.Command{
authMetadataManager := auth.NewDBMetadataManager(config.Version, dbPool)
metadataProvider := stats.BuildMetadataProvider(logging.Default(), cfg)
metadata := stats.NewMetadata(logging.Default(), cfg, authMetadataManager, metadataProvider)
credentials, err := auth.AddAdminUser(authService, &model.User{
CreatedAt: time.Now(),
Username: userName,
credentials, err := auth.AddAdminUser(authService, &model.SuperuserConfiguration{
User: model.User{
CreatedAt: time.Now(),
Username: userName,
},
AccessKeyID: accessKeyID,
SecretAccessKey: secretAccessKey,
})
if err != nil {
fmt.Printf("Failed to setup admin user: %s\n", err)
Expand All @@ -59,6 +77,10 @@ var superuserCmd = &cobra.Command{
//nolint:gochecknoinits
func init() {
rootCmd.AddCommand(superuserCmd)
superuserCmd.Flags().String("user-name", "", "an identifier for the user (e.g. \"jane.doe\")")
f := superuserCmd.Flags()
f.String("user-name", "", "an identifier for the user (e.g. \"jane.doe\")")
f.String("access-key-id", "", "create this access key ID for the user (for ease of integration)")
f.String("secret-access-key", "", "use this access key secret (potentially insecure, use carefully for ease of integration)")

_ = superuserCmd.MarkFlagRequired("user-name")
}
5 changes: 3 additions & 2 deletions docs/assets/js/swagger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2026,10 +2026,10 @@ paths:
- export
- branches
operationId: run
summary: export branch
summary: hook to be called in order to execute continuous export on branch
responses:
201:
description: export successfully started
description: continuous export successfully started
schema:
description: "export ID"
type: string
Expand Down Expand Up @@ -2111,3 +2111,4 @@ paths:
$ref: "#/definitions/config"
401:
$ref: "#/responses/Unauthorized"

10 changes: 6 additions & 4 deletions loadtest/local_load_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,13 @@ func TestLocalLoad(t *testing.T) {
ts := httptest.NewServer(handler)
defer ts.Close()

user := &authmodel.User{
CreatedAt: time.Now(),
Username: "admin",
superuser := &authmodel.SuperuserConfiguration{
User: authmodel.User{
CreatedAt: time.Now(),
Username: "admin",
},
}
credentials, err := auth.SetupAdminUser(authService, user)
credentials, err := auth.SetupAdminUser(authService, superuser)
testutil.Must(t, err)

testConfig := Config{
Expand Down
62 changes: 62 additions & 0 deletions nessie/auth_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package nessie

import (
"net/url"
"testing"

"github.com/treeverse/lakefs/auth"
"github.com/treeverse/lakefs/auth/crypt"
"github.com/treeverse/lakefs/auth/model"
"github.com/treeverse/lakefs/config"
"github.com/treeverse/lakefs/db"

httptransport "github.com/go-openapi/runtime/client"
"github.com/spf13/viper"
genclient "github.com/treeverse/lakefs/api/gen/client"
)

const (
accessKeyID = "Sneakers"
secretAccessKey = "Setec Astronomy"
)

func TestSuperuserWithPassedCreds(t *testing.T) {
ctx, _, _ := setupTest(t)

cfg := config.NewConfig()
dbParams := cfg.GetDatabaseParams()
pool := db.BuildDatabaseConnection(dbParams)

authService := auth.NewDBAuthService(
pool,
crypt.NewSecretStore(cfg.GetAuthEncryptionSecret()),
cfg.GetAuthCacheConfig())

_, err := auth.SetupAdminUser(authService, &model.SuperuserConfiguration{
User: model.User{
Username: "cosmo",
},
AccessKeyID: accessKeyID,
SecretAccessKey: secretAccessKey,
})
if err != nil {
t.Fatal("failed to setup admin user: ", err)
}

// Set up the client to use this authentication.
endpointURL := viper.GetString("endpoint_url")
u, err := url.Parse(endpointURL)
if err != nil {
t.Fatalf("Failed to parse endpoint URL %s: %s", endpointURL, err)
}
apiBasePath := genclient.DefaultBasePath
if u.Path != "" {
apiBasePath = u.Path
}
r := httptransport.New(u.Host, apiBasePath, []string{u.Scheme})
r.DefaultAuthentication = httptransport.BasicAuth(accessKeyID, secretAccessKey)
client.Transport = r

// Use it for some minimal test
listRepositories(t, ctx)
}

0 comments on commit 331f771

Please sign in to comment.