Skip to content

Commit

Permalink
[v17] Automatially add IC Account to Account Assignments Access Reque…
Browse files Browse the repository at this point in the history
…sts (#50190)

* Automatially add IC Account to Account Assignments Access Requests

The UI needs access to the account associated with an Account Assignment
in order to display the enclosing Account, otherwise the user will not
be able to see their assogned permission sets.

This patch automatically adds the enclosing account for any account
assignments in a resource access request and allows the user to see
ther human-friendly names in the access request listing.

* Apply suggestions from code review

Co-authored-by: Sakshyam Shah <[email protected]>
Co-authored-by: Marek Smoliński <[email protected]>

* Code review sugestions

---------

Co-authored-by: Sakshyam Shah <[email protected]>
Co-authored-by: Marek Smoliński <[email protected]>
  • Loading branch information
3 people authored Dec 13, 2024
1 parent f0549aa commit 9d3b907
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 42 deletions.
2 changes: 1 addition & 1 deletion api/accessrequest/access_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func GetResourceDetails(ctx context.Context, clusterName string, lister client.L
var resourceIDs []types.ResourceID
for _, resourceID := range ids {
// We're interested in hostname or friendly name details. These apply to
// nodes, app servers, and user groups.
// nodes, app servers, user groups and Identity Center resources.
switch resourceID.Kind {
case types.KindNode, types.KindApp, types.KindUserGroup, types.KindIdentityCenterAccount:
resourceIDs = append(resourceIDs, resourceID)
Expand Down
2 changes: 2 additions & 0 deletions api/types/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -1296,6 +1296,8 @@ var RequestableResourceKinds = []string{
KindKubeCertificateSigningRequest,
KindKubeIngress,
KindSAMLIdPServiceProvider,
KindIdentityCenterAccount,
KindIdentityCenterAccountAssignment,
}

// The list below needs to be kept in sync with `kubernetesResourceKindOptions`
Expand Down
109 changes: 68 additions & 41 deletions lib/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -5099,47 +5099,11 @@ func (a *Server) CreateAccessRequestV2(ctx context.Context, req types.AccessRequ
}

// Look for user groups and associated applications to the request.
requestedResourceIDs := req.GetRequestedResourceIDs()
var additionalResources []types.ResourceID

var userGroups []types.ResourceID
existingApps := map[string]struct{}{}
for _, resource := range requestedResourceIDs {
switch resource.Kind {
case types.KindApp:
existingApps[resource.Name] = struct{}{}
case types.KindUserGroup:
userGroups = append(userGroups, resource)
}
}

for _, resource := range userGroups {
if resource.Kind != types.KindUserGroup {
continue
}

userGroup, err := a.GetUserGroup(ctx, resource.Name)
if err != nil {
return nil, trace.Wrap(err)
}

for _, app := range userGroup.GetApplications() {
// Only add to the request if we haven't already added it.
if _, ok := existingApps[app]; !ok {
additionalResources = append(additionalResources, types.ResourceID{
ClusterName: resource.ClusterName,
Kind: types.KindApp,
Name: app,
})
existingApps[app] = struct{}{}
}
}
}

if len(additionalResources) > 0 {
requestedResourceIDs = append(requestedResourceIDs, additionalResources...)
req.SetRequestedResourceIDs(requestedResourceIDs)
requestedResourceIDs, err := a.appendImplicitlyRequiredResources(ctx, req.GetRequestedResourceIDs())
if err != nil {
return nil, trace.Wrap(err, "adding additional implicitly required resources")
}
req.SetRequestedResourceIDs(requestedResourceIDs)

if req.GetDryRun() {
_, promotions := a.generateAccessRequestPromotions(ctx, req)
Expand Down Expand Up @@ -5169,7 +5133,7 @@ func (a *Server) CreateAccessRequestV2(ctx context.Context, req types.AccessRequ
}
}

err := a.emitter.EmitAuditEvent(a.closeCtx, &apievents.AccessRequestCreate{
err = a.emitter.EmitAuditEvent(a.closeCtx, &apievents.AccessRequestCreate{
Metadata: apievents.Metadata{
Type: events.AccessRequestCreateEvent,
Code: events.AccessRequestCreateCode,
Expand Down Expand Up @@ -5251,6 +5215,69 @@ func (a *Server) CreateAccessRequestV2(ctx context.Context, req types.AccessRequ
return req, nil
}

// appendImplicitlyRequiredResources examines the set of requested resources and adds
// any extra resources that are implicitly required by the request.
func (a *Server) appendImplicitlyRequiredResources(ctx context.Context, resources []types.ResourceID) ([]types.ResourceID, error) {
addedApps := utils.NewSet[string]()
var userGroups []types.ResourceID
var accountAssignments []types.ResourceID

for _, resource := range resources {
switch resource.Kind {
case types.KindApp:
addedApps.Add(resource.Name)
case types.KindUserGroup:
userGroups = append(userGroups, resource)
case types.KindIdentityCenterAccountAssignment:
accountAssignments = append(accountAssignments, resource)
}
}

for _, resource := range userGroups {
userGroup, err := a.GetUserGroup(ctx, resource.Name)
if err != nil {
return nil, trace.Wrap(err)
}

for _, app := range userGroup.GetApplications() {
// Only add to the request if we haven't already added it.
if !addedApps.Contains(app) {
resources = append(resources, types.ResourceID{
ClusterName: resource.ClusterName,
Kind: types.KindApp,
Name: app,
})
addedApps.Add(app)
}
}
}

icAccounts := utils.NewSet[string]()
for _, resource := range accountAssignments {
// The UI needs access to the account associated with an Account Assignment
// in order to display the enclosing Account, otherwise the user will not
// be able to see their assigned permission sets.
assignmentID := services.IdentityCenterAccountAssignmentID(resource.Name)
asmt, err := a.Services.IdentityCenter.GetAccountAssignment(ctx, assignmentID)
if err != nil {
return nil, trace.Wrap(err, "fetching identity center account assignment")
}

if icAccounts.Contains(asmt.GetSpec().GetAccountId()) {
continue
}

resources = append(resources, types.ResourceID{
ClusterName: resource.ClusterName,
Kind: types.KindIdentityCenterAccount,
Name: asmt.GetSpec().GetAccountId(),
})
icAccounts.Add(asmt.GetSpec().GetAccountId())
}

return resources, nil
}

// generateAccessRequestPromotions will return potential access list promotions for an access request. On error, this function will log
// the error and return whatever it has. The caller is expected to deal with the possibility of a nil promotions object.
func (a *Server) generateAccessRequestPromotions(ctx context.Context, req types.AccessRequest) (types.AccessRequest, *types.AccessRequestAllowedPromotions) {
Expand Down

0 comments on commit 9d3b907

Please sign in to comment.