github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/types/plugin.go (about)

     1  /*
     2  Copyright 2022 Gravitational, Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package types
    18  
    19  import (
    20  	"time"
    21  
    22  	"github.com/gravitational/trace"
    23  
    24  	"github.com/gravitational/teleport/api/utils"
    25  )
    26  
    27  // PluginType represents the type of the plugin
    28  type PluginType string
    29  
    30  // AllPluginTypes is a list of all plugins known to Teleport.
    31  var AllPluginTypes = []PluginType{
    32  	PluginTypeServiceNow,
    33  	PluginTypeSlack,
    34  	PluginTypeOpenAI,
    35  	PluginTypeOkta,
    36  	PluginTypeJamf,
    37  	PluginTypeJira,
    38  	PluginTypeOpsgenie,
    39  	PluginTypePagerDuty,
    40  	PluginTypeMattermost,
    41  	PluginTypeDiscord,
    42  	PluginTypeEntraID,
    43  }
    44  
    45  const (
    46  	// PluginTypeUnknown is returned when no plugin type matches.
    47  	PluginTypeUnknown PluginType = ""
    48  	// PluginTypeServiceNow is the Servicenow access request plugin
    49  	PluginTypeServiceNow = "servicenow"
    50  	// PluginTypeSlack is the Slack access request plugin
    51  	PluginTypeSlack = "slack"
    52  	// PluginTypeOpenAI is the OpenAI plugin
    53  	PluginTypeOpenAI = "openai"
    54  	// PluginTypeOkta is the Okta plugin
    55  	PluginTypeOkta = "okta"
    56  	// PluginTypeJamf is the Jamf MDM plugin
    57  	PluginTypeJamf = "jamf"
    58  	// PluginTypeJira is the Jira access plugin
    59  	PluginTypeJira = "jira"
    60  	// PluginTypeOpsgenie is the Opsgenie access request plugin
    61  	PluginTypeOpsgenie = "opsgenie"
    62  	// PluginTypePagerDuty is the PagerDuty access plugin
    63  	PluginTypePagerDuty = "pagerduty"
    64  	// PluginTypeMattermost is the PagerDuty access plugin
    65  	PluginTypeMattermost = "mattermost"
    66  	// PluginTypeDiscord indicates the Discord access plugin
    67  	PluginTypeDiscord = "discord"
    68  	// PluginTypeGitlab indicates the Gitlab access plugin
    69  	PluginTypeGitlab = "gitlab"
    70  	// PluginTypeEntraID indicates the Entra ID sync plugin
    71  	PluginTypeEntraID = "entra-id"
    72  )
    73  
    74  // PluginSubkind represents the type of the plugin, e.g., access request, MDM etc.
    75  type PluginSubkind string
    76  
    77  const (
    78  	// PluginSubkindUnknown is returned when no plugin subkind matches.
    79  	PluginSubkindUnknown PluginSubkind = ""
    80  	// PluginSubkindMDM represents MDM plugins collectively
    81  	PluginSubkindMDM = "mdm"
    82  	// PluginSubkindAccess represents access request plugins collectively
    83  	PluginSubkindAccess = "access"
    84  	// PluginSubkindAccessGraph represents access graph plugins collectively
    85  	PluginSubkindAccessGraph = "accessgraph"
    86  )
    87  
    88  // Plugin represents a plugin instance
    89  type Plugin interface {
    90  	// ResourceWithSecrets provides common resource methods.
    91  	ResourceWithSecrets
    92  	Clone() Plugin
    93  	GetCredentials() PluginCredentials
    94  	GetStatus() PluginStatus
    95  	GetType() PluginType
    96  	SetCredentials(PluginCredentials) error
    97  	SetStatus(PluginStatus) error
    98  	GetGeneration() string
    99  }
   100  
   101  // PluginCredentials are the credentials embedded in Plugin
   102  type PluginCredentials interface {
   103  	GetOauth2AccessToken() *PluginOAuth2AccessTokenCredentials
   104  	GetStaticCredentialsRef() *PluginStaticCredentialsRef
   105  }
   106  
   107  // PluginStatus is the plugin status
   108  type PluginStatus interface {
   109  	GetCode() PluginStatusCode
   110  }
   111  
   112  // NewPluginV1 creates a new PluginV1 resource.
   113  func NewPluginV1(metadata Metadata, spec PluginSpecV1, creds *PluginCredentialsV1) *PluginV1 {
   114  	p := &PluginV1{
   115  		Metadata: metadata,
   116  		Spec:     spec,
   117  	}
   118  	if creds != nil {
   119  		p.SetCredentials(creds)
   120  	}
   121  
   122  	return p
   123  }
   124  
   125  // CheckAndSetDefaults checks validity of all parameters and sets defaults.
   126  func (p *PluginV1) CheckAndSetDefaults() error {
   127  	p.setStaticFields()
   128  
   129  	if err := p.Metadata.CheckAndSetDefaults(); err != nil {
   130  		return trace.Wrap(err)
   131  	}
   132  
   133  	switch settings := p.Spec.Settings.(type) {
   134  	case *PluginSpecV1_SlackAccessPlugin:
   135  		// Check settings.
   136  		if settings.SlackAccessPlugin == nil {
   137  			return trace.BadParameter("settings must be set")
   138  		}
   139  		if err := settings.SlackAccessPlugin.CheckAndSetDefaults(); err != nil {
   140  			return trace.Wrap(err)
   141  		}
   142  
   143  		if p.Credentials == nil {
   144  			// TODO: after credential exchange during creation is implemented,
   145  			// this should validate that credentials are not empty
   146  			break
   147  		}
   148  		if p.Credentials.GetOauth2AccessToken() == nil {
   149  			return trace.BadParameter("Slack access plugin can only be used with OAuth2 access token credential type")
   150  		}
   151  		if err := p.Credentials.GetOauth2AccessToken().CheckAndSetDefaults(); err != nil {
   152  			return trace.Wrap(err)
   153  		}
   154  	case *PluginSpecV1_Openai:
   155  		if p.Credentials == nil {
   156  			return trace.BadParameter("credentials must be set")
   157  		}
   158  
   159  		bearer := p.Credentials.GetBearerToken()
   160  		if bearer == nil {
   161  			return trace.BadParameter("openai plugin must be used with the bearer token credential type")
   162  		}
   163  		if bearer.Token == "" {
   164  			return trace.BadParameter("Token must be specified")
   165  		}
   166  	case *PluginSpecV1_Opsgenie:
   167  		if settings.Opsgenie == nil {
   168  			return trace.BadParameter("missing opsgenie settings")
   169  		}
   170  		if err := settings.Opsgenie.CheckAndSetDefaults(); err != nil {
   171  			return trace.Wrap(err)
   172  		}
   173  
   174  		staticCreds := p.Credentials.GetStaticCredentialsRef()
   175  		if staticCreds == nil {
   176  			return trace.BadParameter("opsgenie plugin must be used with the static credentials ref type")
   177  		}
   178  		if len(staticCreds.Labels) == 0 {
   179  			return trace.BadParameter("labels must be specified")
   180  		}
   181  	case *PluginSpecV1_Mattermost:
   182  		if settings.Mattermost == nil {
   183  			return trace.BadParameter("missing Mattermost settings")
   184  		}
   185  		if err := settings.Mattermost.CheckAndSetDefaults(); err != nil {
   186  			return trace.Wrap(err)
   187  		}
   188  
   189  		staticCreds := p.Credentials.GetStaticCredentialsRef()
   190  		if staticCreds == nil {
   191  			return trace.BadParameter("Mattermost plugin must be used with the static credentials ref type")
   192  		}
   193  		if len(staticCreds.Labels) == 0 {
   194  			return trace.BadParameter("labels must be specified")
   195  		}
   196  	case *PluginSpecV1_Jamf:
   197  		// Check Jamf settings.
   198  		if settings.Jamf == nil {
   199  			return trace.BadParameter("missing Jamf settings")
   200  		}
   201  		if err := settings.Jamf.CheckAndSetDefaults(); err != nil {
   202  			return trace.Wrap(err)
   203  		}
   204  		if p.Credentials == nil {
   205  			return trace.BadParameter("credentials must be set")
   206  		}
   207  		staticCreds := p.Credentials.GetStaticCredentialsRef()
   208  		if staticCreds == nil {
   209  			return trace.BadParameter("jamf plugin must be used with the static credentials ref type")
   210  		}
   211  		if len(staticCreds.Labels) == 0 {
   212  			return trace.BadParameter("labels must be specified")
   213  		}
   214  
   215  	case *PluginSpecV1_Jira:
   216  		if settings.Jira == nil {
   217  			return trace.BadParameter("missing Jira settings")
   218  		}
   219  
   220  		if err := settings.Jira.CheckAndSetDefaults(); err != nil {
   221  			return trace.Wrap(err)
   222  		}
   223  
   224  		if p.Credentials == nil {
   225  			return trace.BadParameter("credentials must be set")
   226  		}
   227  
   228  		staticCreds := p.Credentials.GetStaticCredentialsRef()
   229  		if staticCreds == nil {
   230  			return trace.BadParameter("jira plugin must be used with the static credentials ref type")
   231  		}
   232  
   233  		if len(staticCreds.Labels) == 0 {
   234  			return trace.BadParameter("labels must be specified")
   235  		}
   236  
   237  	case *PluginSpecV1_Okta:
   238  		// Check settings.
   239  		if settings.Okta == nil {
   240  			return trace.BadParameter("missing Okta settings")
   241  		}
   242  		if err := settings.Okta.CheckAndSetDefaults(); err != nil {
   243  			return trace.Wrap(err)
   244  		}
   245  
   246  		if p.Credentials == nil {
   247  			return trace.BadParameter("credentials must be set")
   248  		}
   249  		staticCreds := p.Credentials.GetStaticCredentialsRef()
   250  		if staticCreds == nil {
   251  			return trace.BadParameter("okta plugin must be used with the static credentials ref type")
   252  		}
   253  		if len(staticCreds.Labels) == 0 {
   254  			return trace.BadParameter("labels must be specified")
   255  		}
   256  	case *PluginSpecV1_PagerDuty:
   257  		if settings.PagerDuty == nil {
   258  			return trace.BadParameter("missing PagerDuty settings")
   259  		}
   260  		if err := settings.PagerDuty.CheckAndSetDefaults(); err != nil {
   261  			return trace.Wrap(err)
   262  		}
   263  
   264  	case *PluginSpecV1_Discord:
   265  		if settings.Discord == nil {
   266  			return trace.BadParameter("missing Discord settings")
   267  		}
   268  		if err := settings.Discord.CheckAndSetDefaults(); err != nil {
   269  			return trace.Wrap(err)
   270  		}
   271  
   272  		staticCreds := p.Credentials.GetStaticCredentialsRef()
   273  		if staticCreds == nil {
   274  			return trace.BadParameter("Discord plugin must be used with the static credentials ref type")
   275  		}
   276  	case *PluginSpecV1_ServiceNow:
   277  		if settings.ServiceNow == nil {
   278  			return trace.BadParameter("missing ServiceNow settings")
   279  		}
   280  		if err := settings.ServiceNow.CheckAndSetDefaults(); err != nil {
   281  			return trace.Wrap(err)
   282  		}
   283  
   284  		staticCreds := p.Credentials.GetStaticCredentialsRef()
   285  		if staticCreds == nil {
   286  			return trace.BadParameter("ServiceNow plugin must be used with the static credentials ref type")
   287  		}
   288  	case *PluginSpecV1_Gitlab:
   289  		if settings.Gitlab == nil {
   290  			return trace.BadParameter("missing Gitlab settings")
   291  		}
   292  		if err := settings.Gitlab.Validate(); err != nil {
   293  			return trace.Wrap(err)
   294  		}
   295  
   296  		staticCreds := p.Credentials.GetStaticCredentialsRef()
   297  		if staticCreds == nil {
   298  			return trace.BadParameter("Gitlab plugin must be used with the static credentials ref type")
   299  		}
   300  	case *PluginSpecV1_EntraId:
   301  		if settings.EntraId == nil {
   302  			return trace.BadParameter("missing Entra ID settings")
   303  		}
   304  		if err := settings.EntraId.Validate(); err != nil {
   305  			return trace.Wrap(err)
   306  		}
   307  	default:
   308  		return trace.BadParameter("settings are not set or have an unknown type")
   309  	}
   310  
   311  	return nil
   312  }
   313  
   314  // WithoutSecrets returns an instance of resource without secrets.
   315  func (p *PluginV1) WithoutSecrets() Resource {
   316  	if p.Credentials == nil {
   317  		return p
   318  	}
   319  
   320  	p2 := p.Clone().(*PluginV1)
   321  	p2.SetCredentials(nil)
   322  	return p2
   323  }
   324  
   325  func (p *PluginV1) setStaticFields() {
   326  	p.Kind = KindPlugin
   327  	p.Version = V1
   328  }
   329  
   330  // Clone returns a copy of the Plugin instance
   331  func (p *PluginV1) Clone() Plugin {
   332  	return utils.CloneProtoMsg(p)
   333  }
   334  
   335  // GetVersion returns resource version
   336  func (p *PluginV1) GetVersion() string {
   337  	return p.Version
   338  }
   339  
   340  // GetKind returns resource kind
   341  func (p *PluginV1) GetKind() string {
   342  	return p.Kind
   343  }
   344  
   345  // GetSubKind returns resource sub kind
   346  func (p *PluginV1) GetSubKind() string {
   347  	return p.SubKind
   348  }
   349  
   350  // SetSubKind sets resource subkind
   351  func (p *PluginV1) SetSubKind(s string) {
   352  	p.SubKind = s
   353  }
   354  
   355  // GetResourceID returns resource ID
   356  func (p *PluginV1) GetResourceID() int64 {
   357  	return p.Metadata.ID
   358  }
   359  
   360  // SetResourceID sets resource ID
   361  func (p *PluginV1) SetResourceID(id int64) {
   362  	p.Metadata.ID = id
   363  }
   364  
   365  // GetRevision returns the revision
   366  func (p *PluginV1) GetRevision() string {
   367  	return p.Metadata.GetRevision()
   368  }
   369  
   370  // SetRevision sets the revision
   371  func (p *PluginV1) SetRevision(rev string) {
   372  	p.Metadata.SetRevision(rev)
   373  }
   374  
   375  // GetMetadata returns object metadata
   376  func (p *PluginV1) GetMetadata() Metadata {
   377  	return p.Metadata
   378  }
   379  
   380  // SetMetadata sets object metadata
   381  func (p *PluginV1) SetMetadata(meta Metadata) {
   382  	p.Metadata = meta
   383  }
   384  
   385  // Expiry returns expiry time for the object
   386  func (p *PluginV1) Expiry() time.Time {
   387  	return p.Metadata.Expiry()
   388  }
   389  
   390  // SetExpiry sets expiry time for the object
   391  func (p *PluginV1) SetExpiry(expires time.Time) {
   392  	p.Metadata.SetExpiry(expires)
   393  }
   394  
   395  // GetName returns the name of the User
   396  func (p *PluginV1) GetName() string {
   397  	return p.Metadata.Name
   398  }
   399  
   400  // SetName sets the name of the User
   401  func (p *PluginV1) SetName(e string) {
   402  	p.Metadata.Name = e
   403  }
   404  
   405  // GetCredentials implements Plugin
   406  func (p *PluginV1) GetCredentials() PluginCredentials {
   407  	return p.Credentials
   408  }
   409  
   410  // SetCredentials implements Plugin
   411  func (p *PluginV1) SetCredentials(creds PluginCredentials) error {
   412  	if creds == nil {
   413  		p.Credentials = nil
   414  		return nil
   415  	}
   416  	switch creds := creds.(type) {
   417  	case *PluginCredentialsV1:
   418  		p.Credentials = creds
   419  	default:
   420  		return trace.BadParameter("unsupported plugin credential type %T", creds)
   421  	}
   422  	return nil
   423  }
   424  
   425  // GetStatus implements Plugin
   426  func (p *PluginV1) GetStatus() PluginStatus {
   427  	return p.Status
   428  }
   429  
   430  // SetStatus implements Plugin
   431  func (p *PluginV1) SetStatus(status PluginStatus) error {
   432  	if status == nil {
   433  		p.Status = PluginStatusV1{}
   434  		return nil
   435  	}
   436  	p.Status = PluginStatusV1{
   437  		Code: status.GetCode(),
   438  	}
   439  	return nil
   440  }
   441  
   442  // GetGeneration returns the plugin generation.
   443  func (p *PluginV1) GetGeneration() string {
   444  	return p.Spec.Generation
   445  }
   446  
   447  // GetType implements Plugin
   448  func (p *PluginV1) GetType() PluginType {
   449  	switch p.Spec.Settings.(type) {
   450  	case *PluginSpecV1_SlackAccessPlugin:
   451  		return PluginTypeSlack
   452  	case *PluginSpecV1_Openai:
   453  		return PluginTypeOpenAI
   454  	case *PluginSpecV1_Okta:
   455  		return PluginTypeOkta
   456  	case *PluginSpecV1_Jamf:
   457  		return PluginTypeJamf
   458  	case *PluginSpecV1_Jira:
   459  		return PluginTypeJira
   460  	case *PluginSpecV1_Opsgenie:
   461  		return PluginTypeOpsgenie
   462  	case *PluginSpecV1_PagerDuty:
   463  		return PluginTypePagerDuty
   464  	case *PluginSpecV1_Mattermost:
   465  		return PluginTypeMattermost
   466  	case *PluginSpecV1_Discord:
   467  		return PluginTypeDiscord
   468  	case *PluginSpecV1_ServiceNow:
   469  		return PluginTypeServiceNow
   470  	case *PluginSpecV1_Gitlab:
   471  		return PluginTypeGitlab
   472  	case *PluginSpecV1_EntraId:
   473  		return PluginTypeEntraID
   474  	default:
   475  		return PluginTypeUnknown
   476  	}
   477  }
   478  
   479  // CheckAndSetDefaults validates and set the default values
   480  func (s *PluginSlackAccessSettings) CheckAndSetDefaults() error {
   481  	if s.FallbackChannel == "" {
   482  		return trace.BadParameter("fallback_channel must be set")
   483  	}
   484  
   485  	return nil
   486  }
   487  
   488  // CheckAndSetDefaults validates and set the default values.
   489  func (s *PluginOktaSettings) CheckAndSetDefaults() error {
   490  	if s.OrgUrl == "" {
   491  		return trace.BadParameter("org_url must be set")
   492  	}
   493  
   494  	// If sync settings is not set, upgrade the legacy values to a
   495  	// to a new SyncSettings block
   496  	if s.SyncSettings == nil {
   497  		// TODO(mdwn): Remove upgrade once modifications have been made in enterprise.
   498  		s.SyncSettings = &PluginOktaSyncSettings{
   499  			SyncUsers:      s.EnableUserSync,
   500  			SsoConnectorId: s.SsoConnectorId,
   501  		}
   502  	}
   503  
   504  	if s.SyncSettings.SyncUsers && s.SyncSettings.SsoConnectorId == "" {
   505  		return trace.BadParameter("sso_connector_id must be set when user sync enabled")
   506  	}
   507  
   508  	if s.SyncSettings.SyncAccessLists && len(s.SyncSettings.DefaultOwners) == 0 {
   509  		return trace.BadParameter("default owners must be set when access list import is enabled")
   510  	}
   511  
   512  	return nil
   513  }
   514  
   515  // CheckAndSetDefaults validates and set the default values
   516  func (s *PluginOpsgenieAccessSettings) CheckAndSetDefaults() error {
   517  	if s.ApiEndpoint == "" {
   518  		return trace.BadParameter("opsgenie api endpoint url must be set")
   519  	}
   520  	return nil
   521  }
   522  
   523  // CheckAndSetDefaults validates and set the default values.
   524  func (s *PluginJamfSettings) CheckAndSetDefaults() error {
   525  	if s.JamfSpec.ApiEndpoint == "" {
   526  		return trace.BadParameter("api endpoint must be set")
   527  	}
   528  
   529  	return nil
   530  }
   531  
   532  func (s *PluginJiraSettings) CheckAndSetDefaults() error {
   533  	if s.ServerUrl == "" {
   534  		return trace.BadParameter("Jira server URL must be set")
   535  	}
   536  
   537  	if s.ProjectKey == "" {
   538  		return trace.BadParameter("Jira project key must be set")
   539  	}
   540  
   541  	if s.IssueType == "" {
   542  		return trace.BadParameter("Jira issue type must be set")
   543  	}
   544  
   545  	return nil
   546  }
   547  
   548  // CheckAndSetDefaults validates and set the default values
   549  func (s *PluginMattermostSettings) CheckAndSetDefaults() error {
   550  	if s.ServerUrl == "" {
   551  		return trace.BadParameter("server url is required")
   552  	}
   553  
   554  	// If one field is defined, both should be required.
   555  	if len(s.Channel) > 0 || len(s.Team) > 0 {
   556  		if len(s.Team) == 0 {
   557  			return trace.BadParameter("team is required")
   558  		}
   559  		if len(s.Channel) == 0 {
   560  			return trace.BadParameter("channel is required")
   561  		}
   562  	}
   563  	return nil
   564  }
   565  
   566  // CheckAndSetDefaults validates and set the default values
   567  func (c *PluginOAuth2AuthorizationCodeCredentials) CheckAndSetDefaults() error {
   568  	if c.AuthorizationCode == "" {
   569  		return trace.BadParameter("authorization_code must be set")
   570  	}
   571  	if c.RedirectUri == "" {
   572  		return trace.BadParameter("redirect_uri must be set")
   573  	}
   574  
   575  	return nil
   576  }
   577  
   578  // CheckAndSetDefaults validates and set the default PagerDuty values
   579  func (c *PluginPagerDutySettings) CheckAndSetDefaults() error {
   580  	if c.ApiEndpoint == "" {
   581  		return trace.BadParameter("api_endpoint must be set")
   582  	}
   583  
   584  	if c.UserEmail == "" {
   585  		return trace.BadParameter("user_email must be set")
   586  	}
   587  	return nil
   588  }
   589  
   590  func (c *PluginDiscordSettings) CheckAndSetDefaults() error {
   591  	if len(c.RoleToRecipients) == 0 {
   592  		return trace.BadParameter("role_to_recipients must be set")
   593  	}
   594  
   595  	if _, present := c.RoleToRecipients[Wildcard]; !present {
   596  		return trace.BadParameter("role_to_recipients must contain default entry `*`")
   597  	}
   598  
   599  	return nil
   600  }
   601  
   602  // CheckAndSetDefaults checks that the required fields for the servicenow plugin are set.
   603  func (c *PluginServiceNowSettings) CheckAndSetDefaults() error {
   604  	if c.ApiEndpoint == "" {
   605  		return trace.BadParameter("API endpoint must be set")
   606  	}
   607  
   608  	return nil
   609  }
   610  
   611  // CheckAndSetDefaults validates and set the default values
   612  func (c *PluginOAuth2AccessTokenCredentials) CheckAndSetDefaults() error {
   613  	if c.AccessToken == "" {
   614  		return trace.BadParameter("access_token must be set")
   615  	}
   616  	if c.RefreshToken == "" {
   617  		return trace.BadParameter("refresh_token must be set")
   618  	}
   619  	c.Expires = c.Expires.UTC()
   620  
   621  	return nil
   622  }
   623  
   624  func (c *PluginEntraIDSettings) Validate() error {
   625  	if c.SyncSettings == nil {
   626  		return trace.BadParameter("sync_settings must be set")
   627  	}
   628  	if len(c.SyncSettings.DefaultOwners) == 0 {
   629  		return trace.BadParameter("sync_settings.default_owners must be set")
   630  	}
   631  
   632  	return nil
   633  }
   634  
   635  // GetCode returns the status code
   636  func (c PluginStatusV1) GetCode() PluginStatusCode {
   637  	return c.Code
   638  }
   639  
   640  // CheckAndSetDefaults checks that the required fields for the Gitlab plugin are set.
   641  func (c *PluginGitlabSettings) Validate() error {
   642  	if c.ApiEndpoint == "" {
   643  		return trace.BadParameter("API endpoint must be set")
   644  	}
   645  
   646  	return nil
   647  }