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 }