Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions api/types/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,9 @@ type Role interface {
// GetIdentityCenterAccountAssignments fetches the allow or deny Account
// Assignments for the role
GetIdentityCenterAccountAssignments(RoleConditionType) []IdentityCenterAccountAssignment
// GetIdentityCenterAccountAssignments sets the allow or deny Account
// Assignments for the role
SetIdentityCenterAccountAssignments(RoleConditionType, []IdentityCenterAccountAssignment)
}

// NewRole constructs new standard V7 role.
Expand Down Expand Up @@ -2068,6 +2071,16 @@ func (r *RoleV6) GetIdentityCenterAccountAssignments(rct RoleConditionType) []Id
return r.Spec.Deny.AccountAssignments
}

// SetIdentityCenterAccountAssignments sets the allow or deny Identity Center
// Account Assignments for the role
func (r *RoleV6) SetIdentityCenterAccountAssignments(rct RoleConditionType, assignments []IdentityCenterAccountAssignment) {
cond := &r.Spec.Deny
if rct == Allow {
cond = &r.Spec.Allow
}
cond.AccountAssignments = assignments
}

// LabelMatcherKinds is the complete list of resource kinds that support label
// matchers.
var LabelMatcherKinds = []string{
Expand Down
5 changes: 5 additions & 0 deletions constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,11 @@ const (
// access to Okta resources. This will be used by the Okta requester role to
// search for Okta resources.
SystemOktaAccessRoleName = "okta-access"

// SystemIdentityCenterAccessRoleName specifies the name of a system role
// that grants a user access to AWS Identity Center resources via
// Access Requests.
SystemIdentityCenterAccessRoleName = "aws-ic-access"
)

var PresetRoles = []string{PresetEditorRoleName, PresetAccessRoleName, PresetAuditorRoleName}
Expand Down
1 change: 1 addition & 0 deletions lib/auth/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -1031,6 +1031,7 @@ func GetPresetRoles() []types.Role {
services.NewSystemOktaAccessRole(),
services.NewSystemOktaRequesterRole(),
services.NewPresetTerraformProviderRole(),
services.NewSystemIdentityCenterAccessRole(),
}

// Certain `New$FooRole()` functions will return a nil role if the
Expand Down
1 change: 1 addition & 0 deletions lib/auth/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1115,6 +1115,7 @@ func TestPresets(t *testing.T) {
enterpriseSystemRoleNames := []string{
teleport.SystemAutomaticAccessApprovalRoleName,
teleport.SystemOktaAccessRoleName,
teleport.SystemIdentityCenterAccessRoleName,
}

enterpriseUsers := []types.User{
Expand Down
132 changes: 122 additions & 10 deletions lib/services/presets.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ import (
"github.com/gravitational/teleport/api/constants"
apidefaults "github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/types/common"
apiutils "github.com/gravitational/teleport/api/utils"
"github.com/gravitational/teleport/lib/modules"
"github.com/gravitational/teleport/lib/utils"
)

// NewSystemAutomaticAccessApproverRole creates a new Role that is allowed to
Expand Down Expand Up @@ -577,6 +579,32 @@ func NewSystemOktaRequesterRole() types.Role {
return role
}

// NewSystemIdentityCenterAccessRole creates a role that allows access to AWS
// IdentityCenter resources via Access Requests
func NewSystemIdentityCenterAccessRole() types.Role {
if modules.GetModules().BuildType() != modules.BuildEnterprise {
return nil
}
return &types.RoleV6{
Kind: types.KindRole,
Version: types.V7,
Metadata: types.Metadata{
Name: teleport.SystemIdentityCenterAccessRoleName,
Namespace: apidefaults.Namespace,
Description: "Access AWS IAM Identity Center resources",
Labels: map[string]string{
types.TeleportInternalResourceType: types.SystemResource,
types.OriginLabel: common.OriginAWSIdentityCenter,
},
},
Spec: types.RoleSpecV6{
Allow: types.RoleConditions{
AccountAssignments: defaultAllowAccountAssignments(true)[teleport.SystemIdentityCenterAccessRoleName],
},
},
}
}

// NewPresetTerraformProviderRole returns a new pre-defined role for the Teleport Terraform provider.
// This role can edit any Terraform-supported resource.
func NewPresetTerraformProviderRole() types.Role {
Expand Down Expand Up @@ -716,6 +744,7 @@ func defaultAllowAccessRequestConditions(enterprise bool) map[string]*types.Acce
SearchAsRoles: []string{
teleport.PresetAccessRoleName,
teleport.PresetGroupAccessRoleName,
teleport.SystemIdentityCenterAccessRoleName,
},
},
teleport.SystemOktaRequesterRoleName: {
Expand All @@ -739,10 +768,12 @@ func defaultAllowAccessReviewConditions(enterprise bool) map[string]*types.Acces
PreviewAsRoles: []string{
teleport.PresetAccessRoleName,
teleport.PresetGroupAccessRoleName,
teleport.SystemIdentityCenterAccessRoleName,
},
Roles: []string{
teleport.PresetAccessRoleName,
teleport.PresetGroupAccessRoleName,
teleport.SystemIdentityCenterAccessRoleName,
},
},
}
Expand All @@ -751,6 +782,21 @@ func defaultAllowAccessReviewConditions(enterprise bool) map[string]*types.Acces
return map[string]*types.AccessReviewConditions{}
}

func defaultAllowAccountAssignments(enterprise bool) map[string][]types.IdentityCenterAccountAssignment {
if enterprise {
return map[string][]types.IdentityCenterAccountAssignment{
teleport.SystemIdentityCenterAccessRoleName: {
{
Account: types.Wildcard,
PermissionSet: types.Wildcard,
},
},
}
}

return map[string][]types.IdentityCenterAccountAssignment{}
}

// AddRoleDefaults adds default role attributes to a preset role.
// Only attributes whose resources are not already defined (either allowing or denying) are added.
func AddRoleDefaults(role types.Role) (types.Role, error) {
Expand Down Expand Up @@ -852,18 +898,18 @@ func AddRoleDefaults(role types.Role) (types.Role, error) {
}
}

if role.GetAccessRequestConditions(types.Allow).IsEmpty() {
arc := defaultAllowAccessRequestConditions(enterprise)[role.GetName()]
if arc != nil {
role.SetAccessRequestConditions(types.Allow, *arc)
changed = true
}
if roleUpdated := applyAccessRequestConditionDefaults(role, enterprise); roleUpdated {
changed = true
}

if role.GetAccessReviewConditions(types.Allow).IsEmpty() {
arc := defaultAllowAccessReviewConditions(enterprise)[role.GetName()]
if arc != nil {
role.SetAccessReviewConditions(types.Allow, *arc)
if roleUpdated := applyAccessReviewConditionDefaults(role, enterprise); roleUpdated {
changed = true
}

if len(role.GetIdentityCenterAccountAssignments(types.Allow)) == 0 {
assignments := defaultAllowAccountAssignments(enterprise)[role.GetName()]
if assignments != nil {
role.SetIdentityCenterAccountAssignments(types.Allow, assignments)
changed = true
}
}
Expand All @@ -875,6 +921,72 @@ func AddRoleDefaults(role types.Role) (types.Role, error) {
return role, nil
}

func mergeStrings(dst, src []string) (merged []string, changed bool) {
items := utils.NewSet[string](dst...)
items.Add(src...)
if len(items) == len(dst) {
return dst, false
}
dst = items.Elements()
slices.Sort(dst)
return dst, true
}

func applyAccessRequestConditionDefaults(role types.Role, enterprise bool) bool {
defaults := defaultAllowAccessRequestConditions(enterprise)[role.GetName()]
if defaults == nil {
return false
}

target := role.GetAccessRequestConditions(types.Allow)
changed := false
if target.IsEmpty() {
target = *defaults
changed = true
} else {
var rolesUpdated bool

target.Roles, rolesUpdated = mergeStrings(target.Roles, defaults.Roles)
changed = changed || rolesUpdated

target.SearchAsRoles, rolesUpdated = mergeStrings(target.SearchAsRoles, defaults.SearchAsRoles)
changed = changed || rolesUpdated
}

if changed {
role.SetAccessRequestConditions(types.Allow, target)
}

return changed
}

func applyAccessReviewConditionDefaults(role types.Role, enterprise bool) bool {
defaults := defaultAllowAccessReviewConditions(enterprise)[role.GetName()]
if defaults == nil {
return false
}

target := role.GetAccessReviewConditions(types.Allow)
changed := false
if target.IsEmpty() {
target = *defaults
changed = true
} else {
var rolesUpdated bool

target.Roles, rolesUpdated = mergeStrings(target.Roles, defaults.Roles)
changed = changed || rolesUpdated

target.PreviewAsRoles, rolesUpdated = mergeStrings(target.PreviewAsRoles, defaults.PreviewAsRoles)
changed = changed || rolesUpdated
}

if changed {
role.SetAccessReviewConditions(types.Allow, target)
}
return changed
}

func labelMatchersUnset(role types.Role, kind string) (bool, error) {
for _, cond := range []types.RoleConditionType{types.Allow, types.Deny} {
labelMatchers, err := role.GetLabelMatchers(cond, kind)
Expand Down
96 changes: 87 additions & 9 deletions lib/services/presets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -349,13 +349,41 @@ func TestAddRoleDefaults(t *testing.T) {
Spec: types.RoleSpecV6{
Allow: types.RoleConditions{
ReviewRequests: &types.AccessReviewConditions{
Roles: []string{"some-role"},
Roles: []string{"some-role"},
PreviewAsRoles: []string{"preview-role"},
},
},
},
},
enterprise: true,
expectedErr: require.NoError,
reviewNotEmpty: true,
expected: &types.RoleV6{
Metadata: types.Metadata{
Name: teleport.PresetReviewerRoleName,
Labels: map[string]string{
types.TeleportInternalResourceType: types.PresetResource,
},
},
Spec: types.RoleSpecV6{
Allow: types.RoleConditions{
ReviewRequests: &types.AccessReviewConditions{
Roles: []string{
teleport.PresetAccessRoleName,
teleport.SystemIdentityCenterAccessRoleName,
teleport.PresetGroupAccessRoleName,
"some-role",
},
PreviewAsRoles: []string{
teleport.PresetAccessRoleName,
teleport.SystemIdentityCenterAccessRoleName,
teleport.PresetGroupAccessRoleName,
"preview-role",
},
},
},
},
},
enterprise: true,
expectedErr: noChange,
},
{
name: "requester (not enterprise)",
Expand Down Expand Up @@ -411,6 +439,25 @@ func TestAddRoleDefaults(t *testing.T) {
{
name: "requester (enterprise, existing requests)",
role: &types.RoleV6{
Metadata: types.Metadata{
Name: teleport.PresetRequesterRoleName,
Labels: map[string]string{
types.TeleportInternalResourceType: types.PresetResource,
},
},
Spec: types.RoleSpecV6{
Allow: types.RoleConditions{
Request: &types.AccessRequestConditions{
Roles: []string{"some-role"},
SearchAsRoles: []string{"search-as-role"},
},
},
},
},
enterprise: true,
expectedErr: require.NoError,
accessRequestsNotEmpty: true,
expected: &types.RoleV6{
Metadata: types.Metadata{
Name: teleport.PresetRequesterRoleName,
Labels: map[string]string{
Expand All @@ -421,12 +468,16 @@ func TestAddRoleDefaults(t *testing.T) {
Allow: types.RoleConditions{
Request: &types.AccessRequestConditions{
Roles: []string{"some-role"},
SearchAsRoles: []string{
teleport.PresetAccessRoleName,
teleport.SystemIdentityCenterAccessRoleName,
teleport.PresetGroupAccessRoleName,
"search-as-role",
},
},
},
},
},
enterprise: true,
expectedErr: noChange,
},
{
name: "okta resources (not enterprise)",
Expand Down Expand Up @@ -555,8 +606,28 @@ func TestAddRoleDefaults(t *testing.T) {
},
},
},
enterprise: true,
expectedErr: noChange,
enterprise: true,
expectedErr: require.NoError,
accessRequestsNotEmpty: true,
expected: &types.RoleV6{
Metadata: types.Metadata{
Name: teleport.SystemOktaRequesterRoleName,
Labels: map[string]string{
types.TeleportInternalResourceType: types.SystemResource,
types.OriginLabel: types.OriginOkta,
},
},
Spec: types.RoleSpecV6{
Allow: types.RoleConditions{
Request: &types.AccessRequestConditions{
Roles: []string{"some-role"},
SearchAsRoles: []string{
teleport.SystemOktaAccessRoleName,
},
},
},
},
},
},
}

Expand All @@ -574,8 +645,15 @@ func TestAddRoleDefaults(t *testing.T) {
require.Empty(t, cmp.Diff(role, test.expected))

if test.expected != nil {
require.Equal(t, test.reviewNotEmpty, !role.GetAccessReviewConditions(types.Allow).IsEmpty())
require.Equal(t, test.accessRequestsNotEmpty, !role.GetAccessRequestConditions(types.Allow).IsEmpty())
require.Equal(t, test.reviewNotEmpty,
!role.GetAccessReviewConditions(types.Allow).IsEmpty(),
"Expected populated Access Review Conditions (%t)",
test.reviewNotEmpty)

require.Equal(t, test.accessRequestsNotEmpty,
!role.GetAccessRequestConditions(types.Allow).IsEmpty(),
"Expected populated Access Request Conditions (%t)",
test.accessRequestsNotEmpty)
}
})
}
Expand Down