diff --git a/pkg/granted/request/close.go b/pkg/granted/request/close.go index b6020321..74c741f9 100644 --- a/pkg/granted/request/close.go +++ b/pkg/granted/request/close.go @@ -5,10 +5,12 @@ import ( "fmt" "connectrpc.com/connect" + "github.com/common-fate/cli/printdiags" "github.com/common-fate/clio" "github.com/common-fate/grab" "github.com/common-fate/granted/pkg/cfaws" "github.com/common-fate/granted/pkg/cfcfg" + "github.com/common-fate/sdk/config" "github.com/common-fate/sdk/eid" accessv1alpha1 "github.com/common-fate/sdk/gen/commonfate/access/v1alpha1" "github.com/common-fate/sdk/service/access/grants" @@ -21,7 +23,7 @@ var closeCommand = cli.Command{ Name: "close", Usage: "Close an active Just-In-Time access to a particular entitlement", Flags: []cli.Flag{ - &cli.StringFlag{Name: "aws-profile", Required: true, Usage: "Close a JIT access for a particular AWS profile"}, + &cli.StringFlag{Name: "aws-profile", Required: false, Usage: "Close a JIT access for a particular AWS profile"}, &cli.StringFlag{Name: "request-id", Required: false, Usage: "Close a JIT access for a particular access request ID"}, }, Action: func(c *cli.Context) error { @@ -30,63 +32,119 @@ var closeCommand = cli.Command{ return err } - profileName := c.String("aws-profile") + accessRequestID := c.String("request-id") - profile, err := profiles.LoadInitialisedProfile(c.Context, profileName) - if err != nil { - return err - } + if accessRequestID != "" { + ctx := c.Context - cfg, err := cfcfg.Load(c.Context, profile) - if err != nil { - return fmt.Errorf("failed to load cfconfig, cannot check for active grants, %w", err) - } + cfg, err := config.LoadDefault(ctx) + if err != nil { + return err + } - grantsClient := grants.NewFromConfig(cfg) - idClient := identitysvc.NewFromConfig(cfg) - callerID, err := idClient.GetCallerIdentity(c.Context, connect.NewRequest(&accessv1alpha1.GetCallerIdentityRequest{})) - if err != nil { - return err - } - target := eid.New("AWS::Account", profile.AWSConfig.SSOAccountID) - - grants, err := grab.AllPages(c.Context, func(ctx context.Context, nextToken *string) ([]*accessv1alpha1.Grant, *string, error) { - grants, err := grantsClient.QueryGrants(c.Context, connect.NewRequest(&accessv1alpha1.QueryGrantsRequest{ - Principal: callerID.Msg.Principal.Eid, - Target: target.ToAPI(), - // This API needs to be updated to use specifiers, for now, fetch all active grants and check for a match on the role name - // Role: eid.New("AWS::Account", profile.AWSConfig.SSOAccountID).ToAPI(), - Status: accessv1alpha1.GrantStatus_GRANT_STATUS_ACTIVE.Enum(), + client := request.NewFromConfig(cfg) + + getRes, err := client.GetAccessRequest(ctx, connect.NewRequest(&accessv1alpha1.GetAccessRequestRequest{ + Id: accessRequestID, })) + clio.Debugw("result", "getAccessRequest", getRes) if err != nil { - return nil, nil, err + return fmt.Errorf("failed to get access request: , %w", err) } - return grants.Msg.Grants, &grants.Msg.NextPageToken, nil - }) - if err != nil { - clearCacheProfileIfExists(profileName) - return fmt.Errorf("failed to query for active grants: %w", err) + // check if access request has grants that need to be closed + grants := getRes.Msg.AccessRequest.Grants + + needsDeprovisioning := false + for _, grant := range grants { + + if grant.Status == accessv1alpha1.GrantStatus_GRANT_STATUS_ACTIVE && grant.ProvisioningStatus != accessv1alpha1.ProvisioningStatus(accessv1alpha1.ProvisioningStatus_PROVISIONING_STATUS_ATTEMPTING) { + needsDeprovisioning = true + break + } + } + + if !needsDeprovisioning { + return fmt.Errorf("access request %s has no grants that need to be closed", accessRequestID) + } + + closeRes, err := client.CloseAccessRequest(ctx, connect.NewRequest(&accessv1alpha1.CloseAccessRequestRequest{ + Id: accessRequestID, + })) + clio.Debugw("result", "closeAccessRequest", closeRes) + if err != nil { + return fmt.Errorf("failed to close access request: , %w", err) + } + + haserrors := printdiags.Print(closeRes.Msg.Diagnostics, nil) + if !haserrors { + clio.Successf("access request %s is now closed", accessRequestID) + } + + return nil } - accessClient := request.NewFromConfig(cfg) + profileName := c.String("aws-profile") - for _, grant := range grants { - if grant.Role.Name == profile.AWSConfig.SSORoleName { - clio.Debugw("found active grant matching the profile, attempting to close grant", "grant", grant) + if profileName != "" { + profile, err := profiles.LoadInitialisedProfile(c.Context, profileName) + if err != nil { + return err + } - res, err := accessClient.CloseAccessRequest(c.Context, connect.NewRequest(&accessv1alpha1.CloseAccessRequestRequest{ - Id: grant.AccessRequestId, + cfg, err := cfcfg.Load(c.Context, profile) + if err != nil { + return fmt.Errorf("failed to load cfconfig, cannot check for active grants, %w", err) + } + + grantsClient := grants.NewFromConfig(cfg) + idClient := identitysvc.NewFromConfig(cfg) + callerID, err := idClient.GetCallerIdentity(c.Context, connect.NewRequest(&accessv1alpha1.GetCallerIdentityRequest{})) + if err != nil { + return err + } + target := eid.New("AWS::Account", profile.AWSConfig.SSOAccountID) + + grants, err := grab.AllPages(c.Context, func(ctx context.Context, nextToken *string) ([]*accessv1alpha1.Grant, *string, error) { + grants, err := grantsClient.QueryGrants(c.Context, connect.NewRequest(&accessv1alpha1.QueryGrantsRequest{ + Principal: callerID.Msg.Principal.Eid, + Target: target.ToAPI(), + // This API needs to be updated to use specifiers, for now, fetch all active grants and check for a match on the role name + // Role: eid.New("AWS::Account", profile.AWSConfig.SSOAccountID).ToAPI(), + Status: accessv1alpha1.GrantStatus_GRANT_STATUS_ACTIVE.Enum(), })) - clio.Debugw("result", "res", res) if err != nil { - return err + return nil, nil, err + } + return grants.Msg.Grants, &grants.Msg.NextPageToken, nil + }) + + if err != nil { + clearCacheProfileIfExists(profileName) + return fmt.Errorf("failed to query for active grants: %w", err) + } + + accessClient := request.NewFromConfig(cfg) + + for _, grant := range grants { + if grant.Role.Name == profile.AWSConfig.SSORoleName { + clio.Debugw("found active grant matching the profile, attempting to close grant", "grant", grant) + + res, err := accessClient.CloseAccessRequest(c.Context, connect.NewRequest(&accessv1alpha1.CloseAccessRequestRequest{ + Id: grant.AccessRequestId, + })) + clio.Debugw("result", "res", res) + if err != nil { + return err + } + clio.Successf("access to target %s and role %s is now closed", target, profile.AWSConfig.SSORoleName) + return nil } - clio.Successf("access to target %s and role %s is now closed", target, profile.AWSConfig.SSORoleName) - return nil } + + return fmt.Errorf("no active Access Request found for target %s and role %s", target, profile.AWSConfig.SSORoleName) } - return fmt.Errorf("no active Access Request found for target %s and role %s", target, profile.AWSConfig.SSORoleName) + return nil }, }