From 920b4945cd5471881dc0a4cabd16ad615130c6fe Mon Sep 17 00:00:00 2001 From: jonaustin09 Date: Thu, 12 Oct 2023 13:24:34 -0400 Subject: [PATCH] feat: Closes #236, Added 3 optional fields in iam user account, UserID, GroupID, ProjectID --- auth/iam.go | 9 ++++--- auth/iam_internal.go | 9 ++++--- cmd/versitygw/admin.go | 42 +++++++++++++++++++++++++++++---- s3api/controllers/admin.go | 15 +++++++----- s3api/controllers/admin_test.go | 20 +++++++++++++--- 5 files changed, 75 insertions(+), 20 deletions(-) diff --git a/auth/iam.go b/auth/iam.go index 8ab83d6..90a0fe1 100644 --- a/auth/iam.go +++ b/auth/iam.go @@ -21,9 +21,12 @@ import ( // Account is a gateway IAM account type Account struct { - Access string `json:"access"` - Secret string `json:"secret"` - Role string `json:"role"` + Access string `json:"access"` + Secret string `json:"secret"` + Role string `json:"role"` + UserID int `json:"userID"` + GroupID int `json:"groupID"` + ProjectID int `json:"projectID"` } // IAMService is the interface for all IAM service implementations diff --git a/auth/iam_internal.go b/auth/iam_internal.go index 0358bae..260ae5b 100644 --- a/auth/iam_internal.go +++ b/auth/iam_internal.go @@ -135,9 +135,12 @@ func (s *IAMServiceInternal) ListUserAccounts() ([]Account, error) { var accs []Account for _, k := range keys { accs = append(accs, Account{ - Access: k, - Secret: conf.AccessAccounts[k].Secret, - Role: conf.AccessAccounts[k].Role, + Access: k, + Secret: conf.AccessAccounts[k].Secret, + Role: conf.AccessAccounts[k].Role, + UserID: conf.AccessAccounts[k].UserID, + GroupID: conf.AccessAccounts[k].GroupID, + ProjectID: conf.AccessAccounts[k].ProjectID, }) } diff --git a/cmd/versitygw/admin.go b/cmd/versitygw/admin.go index fdffa7d..def968f 100644 --- a/cmd/versitygw/admin.go +++ b/cmd/versitygw/admin.go @@ -15,6 +15,7 @@ package main import ( + "bytes" "crypto/sha256" "encoding/hex" "encoding/json" @@ -67,6 +68,21 @@ func adminCommand() *cli.Command { Required: true, Aliases: []string{"r"}, }, + &cli.IntFlag{ + Name: "user-id", + Usage: "userID for the new user", + Aliases: []string{"ui"}, + }, + &cli.IntFlag{ + Name: "group-id", + Usage: "groupID for the new user", + Aliases: []string{"gi"}, + }, + &cli.IntFlag{ + Name: "project-id", + Usage: "projectID for the new user", + Aliases: []string{"pi"}, + }, }, }, { @@ -144,6 +160,7 @@ func adminCommand() *cli.Command { func createUser(ctx *cli.Context) error { access, secret, role := ctx.String("access"), ctx.String("secret"), ctx.String("role") + userID, groupID, projectID := ctx.Int("user-id"), ctx.Int("group-id"), ctx.Int("projectID") if access == "" || secret == "" { return fmt.Errorf("invalid input parameters for the new user") } @@ -151,14 +168,28 @@ func createUser(ctx *cli.Context) error { return fmt.Errorf("invalid input parameter for role") } - req, err := http.NewRequest(http.MethodPatch, fmt.Sprintf("%v/create-user?access=%v&secret=%v&role=%v", adminEndpoint, access, secret, role), nil) + acc := auth.Account{ + Access: access, + Secret: secret, + Role: role, + UserID: userID, + GroupID: groupID, + ProjectID: projectID, + } + + accJson, err := json.Marshal(acc) + if err != nil { + return fmt.Errorf("failed to parse user data: %w", err) + } + + req, err := http.NewRequest(http.MethodPatch, fmt.Sprintf("%v/create-user", adminEndpoint), bytes.NewBuffer(accJson)) if err != nil { return fmt.Errorf("failed to send the request: %w", err) } signer := v4.NewSigner() - hashedPayload := sha256.Sum256([]byte{}) + hashedPayload := sha256.Sum256(accJson) hexPayload := hex.EncodeToString(hashedPayload[:]) req.Header.Set("X-Amz-Content-Sha256", hexPayload) @@ -262,6 +293,7 @@ func listUsers(ctx *cli.Context) error { if err := json.Unmarshal(body, &accs); err != nil { return err } + fmt.Println(accs) printAcctTable(accs) @@ -280,10 +312,10 @@ const ( func printAcctTable(accs []auth.Account) { w := new(tabwriter.Writer) w.Init(os.Stdout, minwidth, tabwidth, padding, padchar, flags) - fmt.Fprintln(w, "Account\tRole") - fmt.Fprintln(w, "-------\t----") + fmt.Fprintln(w, "Account\tRole\tUserID\tGroupID\tProjectID") + fmt.Fprintln(w, "-------\t----\t------\t-------\t---------") for _, acc := range accs { - fmt.Fprintf(w, "%v\t%v\n", acc.Access, acc.Role) + fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\n", acc.Access, acc.Role, acc.UserID, acc.GroupID, acc.ProjectID) } fmt.Fprintln(w) w.Flush() diff --git a/s3api/controllers/admin.go b/s3api/controllers/admin.go index 5f9446e..b2e5f00 100644 --- a/s3api/controllers/admin.go +++ b/s3api/controllers/admin.go @@ -15,6 +15,7 @@ package controllers import ( + "encoding/json" "fmt" "github.com/gofiber/fiber/v2" @@ -32,19 +33,21 @@ func NewAdminController(iam auth.IAMService, be backend.Backend) AdminController } func (c AdminController) CreateUser(ctx *fiber.Ctx) error { - access, secret, role := ctx.Query("access"), ctx.Query("secret"), ctx.Query("role") acct := ctx.Locals("account").(auth.Account) - if acct.Role != "admin" { return fmt.Errorf("access denied: only admin users have access to this resource") } - if role != "user" && role != "admin" { + var usr auth.Account + err := json.Unmarshal(ctx.Body(), &usr) + if err != nil { + return fmt.Errorf("failed to parse request body: %w", err) + } + + if usr.Role != "user" && usr.Role != "admin" { return fmt.Errorf("invalid parameters: user role have to be one of the following: 'user', 'admin'") } - user := auth.Account{Access: access, Secret: secret, Role: role} - - err := c.iam.CreateAccount(user) + err = c.iam.CreateAccount(usr) if err != nil { return fmt.Errorf("failed to create a user: %w", err) } diff --git a/s3api/controllers/admin_test.go b/s3api/controllers/admin_test.go index 09478b3..cdd8299 100644 --- a/s3api/controllers/admin_test.go +++ b/s3api/controllers/admin_test.go @@ -15,7 +15,9 @@ package controllers import ( + "bytes" "context" + "encoding/json" "fmt" "net/http" "net/http/httptest" @@ -55,6 +57,18 @@ func TestAdminController_CreateUser(t *testing.T) { return ctx.Next() }) + usr := auth.Account{ + Access: "access", + Secret: "secret", + Role: "invalid role", + } + + user, _ := json.Marshal(&usr) + + usr.Role = "admin" + + succUsr, _ := json.Marshal(&usr) + appErr.Patch("/create-user", adminController.CreateUser) tests := []struct { @@ -68,7 +82,7 @@ func TestAdminController_CreateUser(t *testing.T) { name: "Admin-create-user-success", app: app, args: args{ - req: httptest.NewRequest(http.MethodPatch, "/create-user?access=test&secret=test&role=user", nil), + req: httptest.NewRequest(http.MethodPatch, "/create-user", bytes.NewBuffer(succUsr)), }, wantErr: false, statusCode: 200, @@ -77,7 +91,7 @@ func TestAdminController_CreateUser(t *testing.T) { name: "Admin-create-user-invalid-user-role", app: app, args: args{ - req: httptest.NewRequest(http.MethodPatch, "/create-user?access=test&secret=test&role=invalid", nil), + req: httptest.NewRequest(http.MethodPatch, "/create-user", bytes.NewBuffer(user)), }, wantErr: false, statusCode: 500, @@ -86,7 +100,7 @@ func TestAdminController_CreateUser(t *testing.T) { name: "Admin-create-user-invalid-requester-role", app: appErr, args: args{ - req: httptest.NewRequest(http.MethodPatch, "/create-user?access=test&secret=test&role=admin", nil), + req: httptest.NewRequest(http.MethodPatch, "/create-user", nil), }, wantErr: false, statusCode: 500,