Skip to content

Commit f88b50e

Browse files
committed
Adds edit support for IC plugin to tctl edit
This patch expands the plugin resource unmarshaler in `tctl` to handle the heterogenous filters used by the Identity Center plugin settings. It also adds support for the IC status block. These changes allow a user to edit the plugin resource via `tctl edit` after installation.
1 parent f802b09 commit f88b50e

File tree

2 files changed

+135
-2
lines changed

2 files changed

+135
-2
lines changed

tool/tctl/common/collection.go

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1618,11 +1618,15 @@ func (p *pluginResourceWrapper) UnmarshalJSON(data []byte) error {
16181618
settingsEntraID = "entra_id"
16191619
settingsDatadogIncidentManagement = "datadog_incident_management"
16201620
settingsEmailAccessPlugin = "email_access_plugin"
1621+
settingsAWSIdentityCenter = "aws_ic"
16211622
)
16221623
type unknownPluginType struct {
16231624
Spec struct {
16241625
Settings map[string]json.RawMessage `json:"Settings"`
16251626
} `json:"spec"`
1627+
Status struct {
1628+
Details map[string]json.RawMessage `json:"Details"`
1629+
} `json:"status"`
16261630
Credentials struct {
16271631
Credentials map[string]json.RawMessage `json:"Credentials"`
16281632
} `json:"credentials"`
@@ -1658,8 +1662,7 @@ func (p *pluginResourceWrapper) UnmarshalJSON(data []byte) error {
16581662
}
16591663
}
16601664

1661-
for k := range unknownPlugin.Spec.Settings {
1662-
1665+
for k, value := range unknownPlugin.Spec.Settings {
16631666
switch k {
16641667
case settingsSlackAccessPlugin:
16651668
p.PluginV1.Spec.Settings = &types.PluginSpecV1_SlackAccessPlugin{}
@@ -1689,17 +1692,99 @@ func (p *pluginResourceWrapper) UnmarshalJSON(data []byte) error {
16891692
p.PluginV1.Spec.Settings = &types.PluginSpecV1_Datadog{}
16901693
case settingsEmailAccessPlugin:
16911694
p.PluginV1.Spec.Settings = &types.PluginSpecV1_Email{}
1695+
case settingsAWSIdentityCenter:
1696+
settings := &types.PluginSpecV1_AwsIc{
1697+
AwsIc: &types.PluginAWSICSettings{},
1698+
}
1699+
p.PluginV1.Spec.Settings = settings
1700+
1701+
unmshallingWrapper := icSettingsWrapper{inner: settings.AwsIc}
1702+
if err := json.Unmarshal(value, &unmshallingWrapper); err != nil {
1703+
return trace.Wrap(err)
1704+
}
16921705
default:
16931706
return trace.BadParameter("unsupported plugin type: %v", k)
16941707
}
16951708
}
16961709

1710+
if len(unknownPlugin.Status.Details) > 1 {
1711+
return trace.BadParameter("malformed status details")
1712+
}
1713+
for k := range unknownPlugin.Status.Details {
1714+
switch k {
1715+
case settingsAWSIdentityCenter:
1716+
p.PluginV1.Status.Details = &types.PluginStatusV1_AwsIc{}
1717+
}
1718+
}
1719+
16971720
if err := json.Unmarshal(data, &p.PluginV1); err != nil {
16981721
return err
16991722
}
17001723
return nil
17011724
}
17021725

1726+
// icSettingsWrapper is a wrapper around the Identity Center plugin settings to
1727+
// provide custom unmarshalling.
1728+
type icSettingsWrapper struct {
1729+
inner *types.PluginAWSICSettings
1730+
}
1731+
1732+
// UnmarshalJSON implements custom JSON-unmarshaling for the Identity Center
1733+
// plugin settings. This custom unmarshaler is required to unpack the structure
1734+
// of the polymorphic filters in the plugin settings, which otherise cannot be
1735+
// unpacked.
1736+
func (s *icSettingsWrapper) UnmarshalJSON(data []byte) error {
1737+
type resourceFilter struct {
1738+
Include map[string]json.RawMessage `json:"Include"`
1739+
}
1740+
1741+
var settings struct {
1742+
AccountFilters []resourceFilter `json:"aws_accounts_filters"`
1743+
GroupFilters []resourceFilter `json:"group_sync_filters"`
1744+
}
1745+
1746+
// unpackFilters only creates the structure of the filters so that the
1747+
// normal JSON unmarshaller knows how to fill in the actual values
1748+
unpackFilters := func(src []resourceFilter) ([]*types.AWSICResourceFilter, error) {
1749+
var dst []*types.AWSICResourceFilter
1750+
for _, f := range src {
1751+
if len(f.Include) != 1 {
1752+
return nil, trace.BadParameter("Malformed filter")
1753+
}
1754+
for k := range f.Include {
1755+
switch k {
1756+
case "id":
1757+
dst = append(dst, &types.AWSICResourceFilter{Include: &types.AWSICResourceFilter_Id{}})
1758+
1759+
case "name_regex":
1760+
dst = append(dst, &types.AWSICResourceFilter{Include: &types.AWSICResourceFilter_NameRegex{}})
1761+
1762+
default:
1763+
return nil, trace.BadParameter("Unexpected filter key: %s", k)
1764+
}
1765+
}
1766+
}
1767+
return dst, nil
1768+
}
1769+
1770+
if err := json.Unmarshal(data, &settings); err != nil {
1771+
return trace.Wrap(err)
1772+
}
1773+
1774+
var err error
1775+
s.inner.AwsAccountsFilters, err = unpackFilters(settings.AccountFilters)
1776+
if err != nil {
1777+
return trace.Wrap(err)
1778+
}
1779+
1780+
s.inner.GroupSyncFilters, err = unpackFilters(settings.GroupFilters)
1781+
if err != nil {
1782+
return trace.Wrap(err)
1783+
}
1784+
1785+
return nil
1786+
}
1787+
17031788
func (c *pluginCollection) resources() []types.Resource {
17041789
r := make([]types.Resource, len(c.plugins))
17051790
for i, resource := range c.plugins {

tool/tctl/common/resource_command_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import (
4646
headerv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1"
4747
userprovisioningpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/userprovisioning/v2"
4848
"github.com/gravitational/teleport/api/types"
49+
apicommon "github.com/gravitational/teleport/api/types/common"
4950
"github.com/gravitational/teleport/api/types/discoveryconfig"
5051
"github.com/gravitational/teleport/api/types/header"
5152
"github.com/gravitational/teleport/entitlements"
@@ -2577,6 +2578,53 @@ func TestPluginResourceWrapper(t *testing.T) {
25772578
},
25782579
},
25792580
},
2581+
{
2582+
name: "identity center",
2583+
plugin: types.PluginV1{
2584+
Metadata: types.Metadata{
2585+
Name: apicommon.OriginAWSIdentityCenter,
2586+
Labels: map[string]string{
2587+
"teleport.dev/hosted-plugin": "true",
2588+
},
2589+
},
2590+
Spec: types.PluginSpecV1{
2591+
Settings: &types.PluginSpecV1_AwsIc{
2592+
AwsIc: &types.PluginAWSICSettings{
2593+
IntegrationName: apicommon.OriginAWSIdentityCenter,
2594+
Region: "ap-south-2",
2595+
Arn: "some:arn",
2596+
ProvisioningSpec: &types.AWSICProvisioningSpec{
2597+
BaseUrl: "https://scim.example.com/v2",
2598+
},
2599+
AccessListDefaultOwners: []string{"root"},
2600+
CredentialsSource: types.AWSICCredentialsSource_AWSIC_CREDENTIALS_SOURCE_SYSTEM,
2601+
UserSyncFilters: []*types.AWSICUserSyncFilter{
2602+
{Labels: map[string]string{types.OriginLabel: types.OriginOkta}},
2603+
{Labels: map[string]string{types.OriginLabel: types.OriginEntraID}},
2604+
},
2605+
GroupSyncFilters: []*types.AWSICResourceFilter{
2606+
{Include: &types.AWSICResourceFilter_NameRegex{NameRegex: `^Group #\\d+$`}},
2607+
{Include: &types.AWSICResourceFilter_Id{Id: "42"}},
2608+
},
2609+
AwsAccountsFilters: []*types.AWSICResourceFilter{
2610+
{Include: &types.AWSICResourceFilter_Id{Id: "314159"}},
2611+
{Include: &types.AWSICResourceFilter_NameRegex{NameRegex: `^Account #\\d+$`}},
2612+
},
2613+
},
2614+
},
2615+
},
2616+
Status: types.PluginStatusV1{
2617+
Code: types.PluginStatusCode_RUNNING,
2618+
Details: &types.PluginStatusV1_AwsIc{
2619+
AwsIc: &types.PluginAWSICStatusV1{
2620+
GroupImportStatus: &types.AWSICGroupImportStatus{
2621+
StatusCode: types.AWSICGroupImportStatusCode_DONE,
2622+
},
2623+
},
2624+
},
2625+
},
2626+
},
2627+
},
25802628
}
25812629

25822630
for _, tc := range tests {

0 commit comments

Comments
 (0)