Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new astro cli commands to support RBAC in Orbit #214

Merged
merged 8 commits into from
May 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,13 @@ install: build
uninstall:
$(eval DESTDIR ?= $(GOBIN))
rm $(GOBIN)/$(OUTPUT)

ifeq (debug,$(firstword $(MAKECMDGOALS)))
# use the rest as arguments for "debug"
DEBUG_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
$(eval $(DEBUG_ARGS):;@:)
endif

debug:
echo $(RUN_ARGS)
dlv debug github.com/astronomer/astro-cli -- $(DEBUG_ARGS)
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ ex.
```yaml
local:
enabled: true
houston: http://localhost:8870/v1
houston: http://localhost:8871/v1
orbit: http://localhost:5000
```

Expand Down
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
var (
// Debug bool
workspaceId string
role string
RootCmd = &cobra.Command{
Use: "astro",
Short: "Astronomer - CLI",
Expand Down
11 changes: 11 additions & 0 deletions cmd/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,14 @@ func coalesceWorkspace() (string, error) {

return "", errors.New("no valid workspace source found")
}

func validateRole(role string) error {
validRoles := []string{"WORKSPACE_ADMIN", "WORKSPACE_EDITOR", "WORKSPACE_VIEWER"}

for _, validRole := range validRoles {
if role == validRole {
return nil
}
}
return errors.Errorf("please use one of: %s", strings.Join(validRoles, ", "))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@schnie Please take a look on error message? sounds good?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks fine to me

}
52 changes: 50 additions & 2 deletions cmd/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ var (
RunE: workspaceUserAdd,
}

workspaceUserUpdateCmd = &cobra.Command{
Use: "update user role",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good

Short: "Update a user's role for a workspace",
Long: "Update a user's role for a workspace",
Args: cobra.ExactArgs(1),
RunE: workspaceUserUpdate,
}

workspaceUserRmCmd = &cobra.Command{
Use: "remove EMAIL",
Aliases: []string{"rm"},
Expand All @@ -88,6 +96,13 @@ var (
Args: cobra.ExactArgs(1),
RunE: workspaceUserRm,
}

workspaceUserListCmd = &cobra.Command{
Use: "list",
Short: "List users in a workspace",
Long: "List users in a workspace",
RunE: workspaceUserList,
}
)

func init() {
Expand Down Expand Up @@ -116,10 +131,18 @@ func init() {
// workspace user add
workspaceUserRootCmd.AddCommand(workspaceUserAddCmd)
workspaceUserAddCmd.PersistentFlags().StringVar(&workspaceId, "workspace-id", "", "workspace assigned to deployment")
workspaceUserAddCmd.PersistentFlags().StringVar(&role, "role", "WORKSPACE_VIEWER", "role assigned to user")

// workspace user update
workspaceUserRootCmd.AddCommand(workspaceUserUpdateCmd)
workspaceUserUpdateCmd.PersistentFlags().StringVar(&role, "role", "WORKSPACE_VIEWER", "role assigned to user")

// workspace user remove
workspaceUserRootCmd.AddCommand(workspaceUserRmCmd)
workspaceUserRmCmd.PersistentFlags().StringVar(&workspaceId, "workspace-id", "", "workspace assigned to deployment")

// workspace user list
workspaceUserRootCmd.AddCommand(workspaceUserListCmd)
}

func workspaceCreate(cmd *cobra.Command, args []string) error {
Expand Down Expand Up @@ -163,13 +186,30 @@ func workspaceUserAdd(cmd *cobra.Command, args []string) error {
ws, err := coalesceWorkspace()
if err != nil {
return errors.Wrap(err, "failed to find a valid workspace")
// fmt.Println("Default workspace id not set, set default workspace id or pass a workspace in via the --workspace-id flag")
}

if err := validateRole(role); err != nil {
return errors.Wrap(err, "failed to find a valid role")
}

// Silence Usage as we have now validated command input
cmd.SilenceUsage = true
return workspace.Add(ws, args[0], role)
}

func workspaceUserUpdate(cmd *cobra.Command, args []string) error {
ws, err := coalesceWorkspace()
if err != nil {
return errors.Wrap(err, "failed to find a valid workspace")
}

return workspace.Add(ws, args[0])
if err := validateRole(role); err != nil {
return errors.Wrap(err, "failed to find a valid role")
}

// Silence Usage as we have now validated command input
cmd.SilenceUsage = true
return workspace.UpdateRole(ws, args[0], role)
}

func workspaceUserRm(cmd *cobra.Command, args []string) error {
Expand Down Expand Up @@ -197,3 +237,11 @@ func workspaceSwitch(cmd *cobra.Command, args []string) error {

return workspace.Switch(id)
}

func workspaceUserList(cmd *cobra.Command, args []string) error {
ws, err := coalesceWorkspace()
if err != nil {
return errors.Wrap(err, "failed to find a valid workspace")
}
return workspace.ListRoles(ws)
}
24 changes: 21 additions & 3 deletions houston/queries.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package houston


var (
AuthConfigGetRequest = `
query GetAuthConfig($redirect: String) {
Expand Down Expand Up @@ -220,6 +219,13 @@ var (
active
createdAt
updatedAt
roleBindings {
role
user {
id
username
}
}
}
}`

Expand Down Expand Up @@ -261,21 +267,33 @@ var (
}`

WorkspaceUserAddRequest = `
mutation AddWorkspaceUser($workspaceId: Uuid!, $email: String!) {
workspaceAddUser(workspaceUuid: $workspaceId, email: $email) {
mutation AddWorkspaceUser($workspaceId: Uuid!, $email: String!, $role: Role!) {
workspaceAddUser(workspaceUuid: $workspaceId, email: $email, role: $role) {
id
label
description
active
users {
id
username
roleBindings {
role
}
}
createdAt
updatedAt
}
}`

WorkspaceUserUpdateRequest = `
mutation workspaceUpdateUserRole($workspaceUuid: Uuid!, $email: String!, $role: Role!) {
workspaceUpdateUserRole(
workspaceUuid: $workspaceUuid
email: $email
role: $role
)
}`

WorkspaceUserRemoveRequest = `
mutation RemoveWorkspaceUser(
$workspaceId: Uuid!
Expand Down
50 changes: 30 additions & 20 deletions houston/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,25 @@ package houston
// Response wraps all houston response structs used for json marashalling
type Response struct {
Data struct {
AddWorkspaceUser *Workspace `json:"workspaceAddUser,omitempty"`
RemoveWorkspaceUser *Workspace `json:"workspaceRemoveUser,omitempty"`
CreateDeployment *Deployment `json:"createDeployment,omitempty"`
CreateToken *AuthUser `json:"createToken,omitempty"`
CreateServiceAccount *ServiceAccount `json:"createServiceAccount,omitempty"`
CreateUser *AuthUser `json:"createUser,omitempty"`
CreateWorkspace *Workspace `json:"createWorkspace,omitempty"`
DeleteDeployment *Deployment `json:"deleteDeployment,omitempty"`
DeleteServiceAccount *ServiceAccount `json:"deleteServiceAccount,omitempty"`
DeleteWorkspace *Workspace `json:"deleteWorkspace,omitempty"`
GetDeployments []Deployment `json:"deployments,omitempty"`
GetAuthConfig *AuthConfig `json:"authConfig,omitempty"`
GetServiceAccounts []ServiceAccount `json:"serviceAccounts,omitempty"`
GetUsers []User `json:"users,omitempty"`
GetWorkspaces []Workspace `json:"workspaces,omitempty"`
UpdateDeployment *Deployment `json:"updateDeployment,omitempty"`
UpdateWorkspace *Workspace `json:"updateWorkspace,omitempty"`
DeploymentLog []DeploymentLog `json:"logs,omitempty"`
AddWorkspaceUser *Workspace `json:"workspaceAddUser,omitempty"`
RemoveWorkspaceUser *Workspace `json:"workspaceRemoveUser,omitempty"`
CreateDeployment *Deployment `json:"createDeployment,omitempty"`
CreateToken *AuthUser `json:"createToken,omitempty"`
CreateServiceAccount *ServiceAccount `json:"createServiceAccount,omitempty"`
CreateUser *AuthUser `json:"createUser,omitempty"`
CreateWorkspace *Workspace `json:"createWorkspace,omitempty"`
DeleteDeployment *Deployment `json:"deleteDeployment,omitempty"`
DeleteServiceAccount *ServiceAccount `json:"deleteServiceAccount,omitempty"`
DeleteWorkspace *Workspace `json:"deleteWorkspace,omitempty"`
GetDeployments []Deployment `json:"deployments,omitempty"`
GetAuthConfig *AuthConfig `json:"authConfig,omitempty"`
GetServiceAccounts []ServiceAccount `json:"serviceAccounts,omitempty"`
GetUsers []User `json:"users,omitempty"`
GetWorkspaces []Workspace `json:"workspaces,omitempty"`
UpdateDeployment *Deployment `json:"updateDeployment,omitempty"`
UpdateWorkspace *Workspace `json:"updateWorkspace,omitempty"`
DeploymentLog []DeploymentLog `json:"logs,omitempty"`
WorkspaceUpdateUserRole string `json:"workspaceUpdateUserRole,omitempty"`
} `json:"data"`
Errors []Error `json:"errors,omitempty"`
}
Expand Down Expand Up @@ -124,6 +125,14 @@ type User struct {
// profile
}

type RoleBinding struct {
Role string `json:"role"`
User struct {
Id string `json:"id"`
Username string `json:"username"`
} `json:"user"`
}

// Workspace contains all components of an Astronomer Workspace
type Workspace struct {
Id string `json:"id"`
Expand All @@ -132,8 +141,9 @@ type Workspace struct {
Active bool `json:"active"`
Users []User `json:"users"`
// groups
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
RoleBindings []RoleBinding `json:"roleBindings"`
}

// DeploymentLog contains all log related to deployment components
Expand Down
63 changes: 57 additions & 6 deletions workspace/user.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package workspace

import (
"fmt"

"github.com/astronomer/astro-cli/houston"
"github.com/astronomer/astro-cli/pkg/printutil"
)
Expand All @@ -12,11 +14,11 @@ var (
}
)

// Add a user to a workspace
func Add(workspaceId, email string) error {
// Add a user to a workspace with specified role
func Add(workspaceId, email, role string) error {
req := houston.Request{
Query: houston.WorkspaceUserAddRequest,
Variables: map[string]interface{}{"workspaceId": workspaceId, "email": email},
Variables: map[string]interface{}{"workspaceId": workspaceId, "email": email, "role": role},
}

r, err := req.Do()
Expand All @@ -25,9 +27,15 @@ func Add(workspaceId, email string) error {
}
w := r.Data.AddWorkspaceUser

utab.AddRow([]string{w.Label, w.Id, email}, false)
utab.SuccessMsg = "Successfully added user to workspace"
utab.Print()
tab := printutil.Table{
Padding: []int{44, 50},
DynamicPadding: true,
Header: []string{"NAME", "WORKSPACE ID", "EMAIL", "ROLE"},
}

tab.AddRow([]string{w.Label, w.Id, email, role}, false)
tab.SuccessMsg = fmt.Sprintf("Successfully added %s to %s", email, w.Label)
tab.Print()

return nil
}
Expand All @@ -50,3 +58,46 @@ func Remove(workspaceId, email string) error {
utab.Print()
return nil
}

// ListRoles print users and roles from a workspace
func ListRoles(workspaceId string) error {
req := houston.Request{
Query: houston.WorkspacesGetRequest,
Variables: map[string]interface{}{"workspaceId": workspaceId},
}
r, err := req.Do()

if err != nil {
return err
}
workspace := r.Data.GetWorkspaces[0]

tab := printutil.Table{
Padding: []int{44, 50},
DynamicPadding: true,
Header: []string{"USERNAME", "ID", "ROLE"},
}
for _, role := range workspace.RoleBindings {
var color bool
tab.AddRow([]string{role.User.Username, role.User.Id, role.Role}, color)
}

tab.Print()
return nil
}

func UpdateRole(workspaceId, email, role string) error {
req := houston.Request{
Query: houston.WorkspaceUserUpdateRequest,
Variables: map[string]interface{}{"workspaceUuid": workspaceId, "email": email, "role": role},
}
r, err := req.Do()

if err != nil {
return err
}
newRole := r.Data.WorkspaceUpdateUserRole

fmt.Printf("Role has been changed from %s to %s for user %s", role, newRole, email)
return nil
}