Skip to content

Commit

Permalink
netiq: Add NetIQ plugin boilerplate code (#50476)
Browse files Browse the repository at this point in the history
This PR introduces the necessary protobuf changes to accomodate NetIQ
plugin.

Part of gravitational/access-graph#634

Signed-off-by: Tiago Silva <[email protected]>
  • Loading branch information
tigrato authored Dec 20, 2024
1 parent 75f5b28 commit 3fb3dc4
Show file tree
Hide file tree
Showing 4 changed files with 2,910 additions and 2,039 deletions.
28 changes: 28 additions & 0 deletions api/proto/teleport/legacy/types/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6470,6 +6470,8 @@ message PluginSpecV1 {
PluginEmailSettings email = 17;
// Settings for the Microsoft Teams plugin
PluginMSTeamsSettings msteams = 18;
// Settings for the OpenTex NetIQ plugin.
PluginNetIQSettings net_iq = 19;
}

// generation contains a unique ID that should:
Expand Down Expand Up @@ -6896,6 +6898,18 @@ message PluginMSTeamsSettings {
string default_recipient = 5;
}

// PluginNetIQSettings defines the settings for a NetIQ integration plugin
message PluginNetIQSettings {
option (gogoproto.equal) = true;
// oauth_issuer_endpoint is the NetIQ Oauth Issuer endpoint.
// Usually, it's equal to https://osp.domain.ext/a/idm/auth/oauth2
string oauth_issuer_endpoint = 1;
// api_endpoint is the IDM PROV Rest API location.
string api_endpoint = 2;
// insecure_skip_verify controls whether the NetIQ certificate validation should be skipped.
bool insecure_skip_verify = 3;
}

message PluginBootstrapCredentialsV1 {
oneof credentials {
PluginOAuth2AuthorizationCodeCredentials oauth2_authorization_code = 1;
Expand Down Expand Up @@ -6934,6 +6948,8 @@ message PluginStatusV1 {
PluginOktaStatusV1 okta = 7;
// AWSIC holds status details for the AWS Identity Center plugin.
PluginAWSICStatusV1 aws_ic = 8;
// NetIQ holds status details for the NetIQ plugin.
PluginNetIQStatusV1 net_iq = 9;
}

// last_raw_error variable stores the most recent raw error message received from an API or service.
Expand All @@ -6943,6 +6959,18 @@ message PluginStatusV1 {
string last_raw_error = 6;
}

// PluginNetIQStatusV1 is the status details for the NetIQ plugin.
message PluginNetIQStatusV1 {
// imported_users is the number of users imported from NetIQ eDirectory.
uint32 imported_users = 1;
// imported_groups is the number of groups imported from NetIQ eDirectory.
uint32 imported_groups = 2;
// imported_roles is the number of roles imported from NetIQ eDirectory.
uint32 imported_roles = 3;
// imported_resources is the number of resources imported from NetIQ eDirectory.
uint32 imported_resources = 4;
}

// PluginGitlabStatusV1 is the status details for the Gitlab plugin.
message PluginGitlabStatusV1 {
// imported_users is the number of users imported from Gitlab.
Expand Down
36 changes: 36 additions & 0 deletions api/types/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package types

import (
"net/url"
"time"

"github.com/gravitational/trace"
Expand Down Expand Up @@ -83,6 +84,8 @@ const (
PluginTypeEmail = "email"
// PluginTypeMSTeams indicates a Microsoft Teams integration
PluginTypeMSTeams = "msteams"
// PluginTypeNetIQ indicates a NetIQ integration
PluginTypeNetIQ = "netiq"
)

// PluginSubkind represents the type of the plugin, e.g., access request, MDM etc.
Expand Down Expand Up @@ -377,6 +380,20 @@ func (p *PluginV1) CheckAndSetDefaults() error {
if len(staticCreds.Labels) == 0 {
return trace.BadParameter("labels must be specified")
}
case *PluginSpecV1_NetIq:
if settings.NetIq == nil {
return trace.BadParameter("missing NetIQ settings")
}
if err := settings.NetIq.Validate(); err != nil {
return trace.Wrap(err)
}
staticCreds := p.Credentials.GetStaticCredentialsRef()
if staticCreds == nil {
return trace.BadParameter("NetIQ plugin must be used with the static credentials ref type")
}
if len(staticCreds.Labels) == 0 {
return trace.BadParameter("labels must be specified")
}
default:
return nil
}
Expand Down Expand Up @@ -837,3 +854,22 @@ func (c *PluginGitlabSettings) Validate() error {

return nil
}

func (c *PluginNetIQSettings) Validate() error {
if c.OauthIssuerEndpoint == "" {
return trace.BadParameter("oauth_issuer endpoint must be set")
}

if _, err := url.Parse(c.OauthIssuerEndpoint); err != nil {
return trace.BadParameter("oauth_issuer endpoint must be a valid URL")
}

if c.ApiEndpoint == "" {
return trace.BadParameter("api_endpoint must be set")
}
if _, err := url.Parse(c.ApiEndpoint); err != nil {
return trace.BadParameter("api_endpoint endpoint must be a valid URL")
}

return nil
}
75 changes: 75 additions & 0 deletions api/types/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1336,3 +1336,78 @@ func TestPluginEmailSettings(t *testing.T) {
})
}
}

func TestNetIQPluginSettings(t *testing.T) {

validSettings := func() *PluginSpecV1_NetIq {
return &PluginSpecV1_NetIq{
NetIq: &PluginNetIQSettings{
OauthIssuerEndpoint: "https://example.com",
ApiEndpoint: "https://example.com",
},
}
}
testCases := []struct {
name string
mutateSettings func(*PluginSpecV1_NetIq)
assertErr require.ErrorAssertionFunc
}{
{
name: "valid",
mutateSettings: nil,
assertErr: require.NoError,
},
{
name: "missing OauthIssuer",
mutateSettings: func(s *PluginSpecV1_NetIq) {
s.NetIq.OauthIssuerEndpoint = ""
},
assertErr: requireNamedBadParameterError("oauth_issuer"),
},
{
name: "missing ApiEndpoint",
mutateSettings: func(s *PluginSpecV1_NetIq) {
s.NetIq.ApiEndpoint = ""
},
assertErr: requireNamedBadParameterError("api_endpoint"),
},
{
name: "incorrect OauthIssuer",
mutateSettings: func(s *PluginSpecV1_NetIq) {
s.NetIq.OauthIssuerEndpoint = "invalidURL%%#"
},
assertErr: requireNamedBadParameterError("oauth_issuer"),
},
{
name: "missing ApiEndpoint",
mutateSettings: func(s *PluginSpecV1_NetIq) {
s.NetIq.ApiEndpoint = "invalidURL%%#"
},
assertErr: requireNamedBadParameterError("api_endpoint"),
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
settings := validSettings()
if tc.mutateSettings != nil {
tc.mutateSettings(settings)
}

plugin := NewPluginV1(
Metadata{Name: "uut"},
PluginSpecV1{Settings: settings},
&PluginCredentialsV1{
Credentials: &PluginCredentialsV1_StaticCredentialsRef{
StaticCredentialsRef: &PluginStaticCredentialsRef{
Labels: map[string]string{
"label1": "value1",
},
},
},
})
tc.assertErr(t, plugin.CheckAndSetDefaults())
})
}

}
Loading

0 comments on commit 3fb3dc4

Please sign in to comment.