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
10 changes: 10 additions & 0 deletions api/proto/teleport/legacy/types/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5055,6 +5055,8 @@ message PluginSpecV1 {
PluginSlackAccessSettings slack_access_plugin = 1;
// Settings for the Opsgenie access plugin
PluginOpsgenieAccessSettings opsgenie = 2;
// Settings for the Okta plugin
PluginOktaSettings okta = 4;
}
}

Expand All @@ -5077,6 +5079,14 @@ message PluginOpsgenieAccessSettings {
repeated string default_schedules = 4;
}

// Defines settings for the Okta plugin.
message PluginOktaSettings {
option (gogoproto.equal) = true;

// OrgUrl is the Okta organization URL to use for API communication.
string org_url = 1;
}

message PluginBootstrapCredentialsV1 {
oneof credentials {
PluginOAuth2AuthorizationCodeCredentials oauth2_authorization_code = 1;
Expand Down
32 changes: 32 additions & 0 deletions api/types/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ const (
PluginTypeUnknown PluginType = ""
// PluginTypeSlack is the Slack access request plugin
PluginTypeSlack = "slack"
// PluginTypeOkta is the Okta plugin
PluginTypeOkta = "okta"
)

// Plugin represents a plugin instance
Expand Down Expand Up @@ -98,6 +100,25 @@ func (p *PluginV1) CheckAndSetDefaults() error {
if err := p.Credentials.GetOauth2AccessToken().CheckAndSetDefaults(); err != nil {
return trace.Wrap(err)
}
case *PluginSpecV1_Okta:
// Check settings.
if settings.Okta == nil {
return trace.BadParameter("missing Okta settings")
}
if err := settings.Okta.CheckAndSetDefaults(); err != nil {
return trace.Wrap(err)
}

if p.Credentials == nil {
return trace.BadParameter("credentials must be set")
}
bearer := p.Credentials.GetBearerToken()
if bearer == nil {
return trace.BadParameter("okta plugin must be used with the bearer token credential type")
}
if (bearer.Token == "") == (bearer.TokenFile == "") {
return trace.BadParameter("exactly one of Token and TokenFile must be specified")
}
default:
return trace.BadParameter("settings are not set or have an unknown type")
}
Expand Down Expand Up @@ -228,6 +249,8 @@ func (p *PluginV1) GetType() PluginType {
switch p.Spec.Settings.(type) {
case *PluginSpecV1_SlackAccessPlugin:
return PluginTypeSlack
case *PluginSpecV1_Okta:
return PluginTypeOkta
default:
return PluginTypeUnknown
}
Expand All @@ -242,6 +265,15 @@ func (s *PluginSlackAccessSettings) CheckAndSetDefaults() error {
return nil
}

// CheckAndSetDefaults validates and set the default values.
func (s *PluginOktaSettings) CheckAndSetDefaults() error {
if s.OrgUrl == "" {
return trace.BadParameter("org_url must be set")
}

return nil
}

// CheckAndSetDefaults validates and set the default values
func (c *PluginOAuth2AuthorizationCodeCredentials) CheckAndSetDefaults() error {
if c.AuthorizationCode == "" {
Expand Down
110 changes: 110 additions & 0 deletions api/types/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"testing"
"time"

"github.com/gravitational/trace"
"github.com/stretchr/testify/require"
)

Expand All @@ -46,3 +47,112 @@ func TestPluginWithoutSecrets(t *testing.T) {
plugin = plugin.WithoutSecrets().(*PluginV1)
require.Nil(t, plugin.Credentials)
}

func TestPluginOktaValidation(t *testing.T) {
testCases := []struct {
name string
settings *PluginSpecV1_Okta
creds *PluginCredentialsV1
assertErr require.ErrorAssertionFunc
}{
{
name: "no settings",
settings: &PluginSpecV1_Okta{
Okta: nil,
},
creds: nil,
assertErr: func(t require.TestingT, err error, args ...any) {
require.Error(t, err)
require.True(t, trace.IsBadParameter(err))
require.Contains(t, err.Error(), "missing Okta settings")
},
},
{
name: "no org URL",
settings: &PluginSpecV1_Okta{
Okta: &PluginOktaSettings{},
},
creds: nil,
assertErr: func(t require.TestingT, err error, args ...any) {
require.Error(t, err)
require.True(t, trace.IsBadParameter(err))
require.Contains(t, err.Error(), "org_url must be set")
},
},
{
name: "no credentials inner",
settings: &PluginSpecV1_Okta{
Okta: &PluginOktaSettings{
OrgUrl: "https://test.okta.com",
},
},
creds: &PluginCredentialsV1{},
assertErr: func(t require.TestingT, err error, args ...any) {
require.Error(t, err)
require.True(t, trace.IsBadParameter(err))
require.Contains(t, err.Error(), "must be used with the bearer token credential type")
},
},
{
name: "invalid credential type (oauth2)",
settings: &PluginSpecV1_Okta{
Okta: &PluginOktaSettings{
OrgUrl: "https://test.okta.com",
},
},
creds: &PluginCredentialsV1{
Credentials: &PluginCredentialsV1_Oauth2AccessToken{},
},
assertErr: func(t require.TestingT, err error, args ...any) {
require.Error(t, err)
require.True(t, trace.IsBadParameter(err))
require.Contains(t, err.Error(), "must be used with the bearer token credential type")
},
},
{
name: "valid credentials (token)",
settings: &PluginSpecV1_Okta{
Okta: &PluginOktaSettings{
OrgUrl: "https://test.okta.com",
},
},
creds: &PluginCredentialsV1{
Credentials: &PluginCredentialsV1_BearerToken{
BearerToken: &PluginBearerTokenCredentials{
Token: "xxx-abc",
},
},
},
assertErr: func(t require.TestingT, err error, args ...any) {
require.NoError(t, err)
},
},
{
name: "valid credentials (token file)",
settings: &PluginSpecV1_Okta{
Okta: &PluginOktaSettings{
OrgUrl: "https://test.okta.com",
},
},
creds: &PluginCredentialsV1{
Credentials: &PluginCredentialsV1_BearerToken{
BearerToken: &PluginBearerTokenCredentials{
TokenFile: "/var/lib/secrets/okta_token",
},
},
},
assertErr: func(t require.TestingT, err error, args ...any) {
require.NoError(t, err)
},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
plugin := NewPluginV1(Metadata{Name: "foobar"}, PluginSpecV1{
Settings: tc.settings,
}, tc.creds)
tc.assertErr(t, plugin.CheckAndSetDefaults())
})
}
}
Loading