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

     1  /*
     2  Copyright 2020 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  	"net/url"
    21  	"slices"
    22  	"time"
    23  
    24  	"github.com/gravitational/trace"
    25  	"golang.org/x/crypto/ssh"
    26  
    27  	"github.com/gravitational/teleport/api/constants"
    28  	"github.com/gravitational/teleport/api/defaults"
    29  	"github.com/gravitational/teleport/api/utils"
    30  )
    31  
    32  // OIDCConnector specifies configuration for Open ID Connect compatible external
    33  // identity provider, e.g. google in some organization
    34  type OIDCConnector interface {
    35  	// ResourceWithSecrets provides common methods for objects
    36  	ResourceWithSecrets
    37  	ResourceWithOrigin
    38  	// Issuer URL is the endpoint of the provider, e.g. https://accounts.google.com
    39  	GetIssuerURL() string
    40  	// ClientID is id for authentication client (in our case it's our Auth server)
    41  	GetClientID() string
    42  	// ClientSecret is used to authenticate our client and should not
    43  	// be visible to end user
    44  	GetClientSecret() string
    45  	// GetRedirectURLs returns list of redirect URLs.
    46  	GetRedirectURLs() []string
    47  	// GetACR returns the Authentication Context Class Reference (ACR) value.
    48  	GetACR() string
    49  	// GetProvider returns the identity provider.
    50  	GetProvider() string
    51  	// Display - Friendly name for this provider.
    52  	GetDisplay() string
    53  	// Scope is additional scopes set by provider
    54  	GetScope() []string
    55  	// ClaimsToRoles specifies dynamic mapping from claims to roles
    56  	GetClaimsToRoles() []ClaimMapping
    57  	// GetClaims returns list of claims expected by mappings
    58  	GetClaims() []string
    59  	// GetTraitMappings converts gets all claim mappings in the
    60  	// generic trait mapping format.
    61  	GetTraitMappings() TraitMappingSet
    62  	// SetClientSecret sets client secret to some value
    63  	SetClientSecret(secret string)
    64  	// SetClientID sets id for authentication client (in our case it's our Auth server)
    65  	SetClientID(string)
    66  	// SetIssuerURL sets the endpoint of the provider
    67  	SetIssuerURL(string)
    68  	// SetRedirectURLs sets the list of redirectURLs
    69  	SetRedirectURLs([]string)
    70  	// SetPrompt sets OIDC prompt value
    71  	SetPrompt(string)
    72  	// GetPrompt returns OIDC prompt value,
    73  	GetPrompt() string
    74  	// SetACR sets the Authentication Context Class Reference (ACR) value.
    75  	SetACR(string)
    76  	// SetProvider sets the identity provider.
    77  	SetProvider(string)
    78  	// SetScope sets additional scopes set by provider
    79  	SetScope([]string)
    80  	// SetClaimsToRoles sets dynamic mapping from claims to roles
    81  	SetClaimsToRoles([]ClaimMapping)
    82  	// GetUsernameClaim gets the name of the claim from the OIDC connector to be used as the user's username.
    83  	GetUsernameClaim() string
    84  	// SetDisplay sets friendly name for this provider.
    85  	SetDisplay(string)
    86  	// GetGoogleServiceAccountURI returns path to google service account URI
    87  	GetGoogleServiceAccountURI() string
    88  	// GetGoogleServiceAccount returns google service account json for Google
    89  	GetGoogleServiceAccount() string
    90  	// SetGoogleServiceAccount sets the google service account json contents
    91  	SetGoogleServiceAccount(string)
    92  	// GetGoogleAdminEmail returns a google admin user email
    93  	// https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority
    94  	// "Note: Although you can use service accounts in applications that run from a Google Workspace (formerly G Suite) domain, service accounts are not members of your Google Workspace account and aren’t subject to domain policies set by  administrators. For example, a policy set in the Google Workspace admin console to restrict the ability of end users to share documents outside of the domain would not apply to service accounts."
    95  	GetGoogleAdminEmail() string
    96  	// GetAllowUnverifiedEmail returns true if unverified emails should be allowed in received users.
    97  	GetAllowUnverifiedEmail() bool
    98  	// GetMaxAge returns the amount of time that user logins are
    99  	// valid for and true if MaxAge is set. If a user logs in, but then
   100  	// does not login again within this time period, they will be forced
   101  	// to re-authenticate.
   102  	GetMaxAge() (time.Duration, bool)
   103  }
   104  
   105  // NewOIDCConnector returns a new OIDCConnector based off a name and OIDCConnectorSpecV3.
   106  func NewOIDCConnector(name string, spec OIDCConnectorSpecV3) (OIDCConnector, error) {
   107  	o := &OIDCConnectorV3{
   108  		Metadata: Metadata{
   109  			Name: name,
   110  		},
   111  		Spec: spec,
   112  	}
   113  	if err := o.CheckAndSetDefaults(); err != nil {
   114  		return nil, trace.Wrap(err)
   115  	}
   116  	return o, nil
   117  }
   118  
   119  // SetPrompt sets OIDC prompt value
   120  func (o *OIDCConnectorV3) SetPrompt(p string) {
   121  	o.Spec.Prompt = p
   122  }
   123  
   124  // GetPrompt returns OIDC prompt value,
   125  // * if not set, default to select_account for backwards compatibility
   126  // * if set to none, it will be omitted
   127  // * and any other non empty value, pass it as is
   128  func (o *OIDCConnectorV3) GetPrompt() string {
   129  	if o.Spec.Prompt == "" {
   130  		return constants.OIDCPromptSelectAccount
   131  	}
   132  	if o.Spec.Prompt == constants.OIDCPromptNone {
   133  		return ""
   134  	}
   135  	return o.Spec.Prompt
   136  }
   137  
   138  // GetGoogleServiceAccountURI returns an optional path to google service account file
   139  func (o *OIDCConnectorV3) GetGoogleServiceAccountURI() string {
   140  	return o.Spec.GoogleServiceAccountURI
   141  }
   142  
   143  // GetGoogleServiceAccount returns a string representing a Google service account
   144  func (o *OIDCConnectorV3) GetGoogleServiceAccount() string {
   145  	return o.Spec.GoogleServiceAccount
   146  }
   147  
   148  // SetGoogleServiceAccount sets a string representing a Google service account
   149  func (o *OIDCConnectorV3) SetGoogleServiceAccount(s string) {
   150  	o.Spec.GoogleServiceAccount = s
   151  }
   152  
   153  // GetGoogleAdminEmail returns a google admin user email
   154  func (o *OIDCConnectorV3) GetGoogleAdminEmail() string {
   155  	return o.Spec.GoogleAdminEmail
   156  }
   157  
   158  // GetVersion returns resource version
   159  func (o *OIDCConnectorV3) GetVersion() string {
   160  	return o.Version
   161  }
   162  
   163  // GetSubKind returns resource sub kind
   164  func (o *OIDCConnectorV3) GetSubKind() string {
   165  	return o.SubKind
   166  }
   167  
   168  // SetSubKind sets resource subkind
   169  func (o *OIDCConnectorV3) SetSubKind(s string) {
   170  	o.SubKind = s
   171  }
   172  
   173  // GetKind returns resource kind
   174  func (o *OIDCConnectorV3) GetKind() string {
   175  	return o.Kind
   176  }
   177  
   178  // GetResourceID returns resource ID
   179  func (o *OIDCConnectorV3) GetResourceID() int64 {
   180  	return o.Metadata.ID
   181  }
   182  
   183  // SetResourceID sets resource ID
   184  func (o *OIDCConnectorV3) SetResourceID(id int64) {
   185  	o.Metadata.ID = id
   186  }
   187  
   188  // GetRevision returns the revision
   189  func (o *OIDCConnectorV3) GetRevision() string {
   190  	return o.Metadata.GetRevision()
   191  }
   192  
   193  // SetRevision sets the revision
   194  func (o *OIDCConnectorV3) SetRevision(rev string) {
   195  	o.Metadata.SetRevision(rev)
   196  }
   197  
   198  // WithoutSecrets returns an instance of resource without secrets.
   199  func (o *OIDCConnectorV3) WithoutSecrets() Resource {
   200  	if o.GetClientSecret() == "" && o.GetGoogleServiceAccount() == "" {
   201  		return o
   202  	}
   203  	o2 := *o
   204  
   205  	o2.SetClientSecret("")
   206  	o2.SetGoogleServiceAccount("")
   207  
   208  	return &o2
   209  }
   210  
   211  // V3 returns V3 version of the resource
   212  func (o *OIDCConnectorV3) V3() *OIDCConnectorV3 {
   213  	return o
   214  }
   215  
   216  // SetDisplay sets friendly name for this provider.
   217  func (o *OIDCConnectorV3) SetDisplay(display string) {
   218  	o.Spec.Display = display
   219  }
   220  
   221  // GetMetadata returns object metadata
   222  func (o *OIDCConnectorV3) GetMetadata() Metadata {
   223  	return o.Metadata
   224  }
   225  
   226  // Origin returns the origin value of the resource.
   227  func (o *OIDCConnectorV3) Origin() string {
   228  	return o.Metadata.Origin()
   229  }
   230  
   231  // SetOrigin sets the origin value of the resource.
   232  func (o *OIDCConnectorV3) SetOrigin(origin string) {
   233  	o.Metadata.SetOrigin(origin)
   234  }
   235  
   236  // SetExpiry sets expiry time for the object
   237  func (o *OIDCConnectorV3) SetExpiry(expires time.Time) {
   238  	o.Metadata.SetExpiry(expires)
   239  }
   240  
   241  // Expiry returns object expiry setting
   242  func (o *OIDCConnectorV3) Expiry() time.Time {
   243  	return o.Metadata.Expiry()
   244  }
   245  
   246  // GetName returns the name of the connector
   247  func (o *OIDCConnectorV3) GetName() string {
   248  	return o.Metadata.GetName()
   249  }
   250  
   251  // SetName sets client secret to some value
   252  func (o *OIDCConnectorV3) SetName(name string) {
   253  	o.Metadata.SetName(name)
   254  }
   255  
   256  // SetIssuerURL sets client secret to some value
   257  func (o *OIDCConnectorV3) SetIssuerURL(issuerURL string) {
   258  	o.Spec.IssuerURL = issuerURL
   259  }
   260  
   261  // SetRedirectURLs sets the list of redirectURLs
   262  func (o *OIDCConnectorV3) SetRedirectURLs(redirectURLs []string) {
   263  	o.Spec.RedirectURLs = redirectURLs
   264  }
   265  
   266  // SetACR sets the Authentication Context Class Reference (ACR) value.
   267  func (o *OIDCConnectorV3) SetACR(acrValue string) {
   268  	o.Spec.ACR = acrValue
   269  }
   270  
   271  // SetProvider sets the identity provider.
   272  func (o *OIDCConnectorV3) SetProvider(identityProvider string) {
   273  	o.Spec.Provider = identityProvider
   274  }
   275  
   276  // SetScope sets additional scopes set by provider
   277  func (o *OIDCConnectorV3) SetScope(scope []string) {
   278  	o.Spec.Scope = scope
   279  }
   280  
   281  // SetClaimsToRoles sets dynamic mapping from claims to roles
   282  func (o *OIDCConnectorV3) SetClaimsToRoles(claims []ClaimMapping) {
   283  	o.Spec.ClaimsToRoles = claims
   284  }
   285  
   286  // SetClientID sets id for authentication client (in our case it's our Auth server)
   287  func (o *OIDCConnectorV3) SetClientID(clintID string) {
   288  	o.Spec.ClientID = clintID
   289  }
   290  
   291  // SetClientSecret sets client secret to some value
   292  func (o *OIDCConnectorV3) SetClientSecret(secret string) {
   293  	o.Spec.ClientSecret = secret
   294  }
   295  
   296  // GetIssuerURL is the endpoint of the provider, e.g. https://accounts.google.com
   297  func (o *OIDCConnectorV3) GetIssuerURL() string {
   298  	return o.Spec.IssuerURL
   299  }
   300  
   301  // GetClientID is id for authentication client (in our case it's our Auth server)
   302  func (o *OIDCConnectorV3) GetClientID() string {
   303  	return o.Spec.ClientID
   304  }
   305  
   306  // GetClientSecret is used to authenticate our client and should not
   307  // be visible to end user
   308  func (o *OIDCConnectorV3) GetClientSecret() string {
   309  	return o.Spec.ClientSecret
   310  }
   311  
   312  // GetRedirectURLs returns a list of the connector's redirect URLs.
   313  func (o *OIDCConnectorV3) GetRedirectURLs() []string {
   314  	return o.Spec.RedirectURLs
   315  }
   316  
   317  // GetACR returns the Authentication Context Class Reference (ACR) value.
   318  func (o *OIDCConnectorV3) GetACR() string {
   319  	return o.Spec.ACR
   320  }
   321  
   322  // GetProvider returns the identity provider.
   323  func (o *OIDCConnectorV3) GetProvider() string {
   324  	return o.Spec.Provider
   325  }
   326  
   327  // GetDisplay - Friendly name for this provider.
   328  func (o *OIDCConnectorV3) GetDisplay() string {
   329  	if o.Spec.Display != "" {
   330  		return o.Spec.Display
   331  	}
   332  	return o.GetName()
   333  }
   334  
   335  // GetScope is additional scopes set by provider
   336  func (o *OIDCConnectorV3) GetScope() []string {
   337  	return o.Spec.Scope
   338  }
   339  
   340  // GetUsernameClaim gets the name of the claim from the OIDC connector to be used as the user's username.
   341  func (o *OIDCConnectorV3) GetUsernameClaim() string {
   342  	return o.Spec.UsernameClaim
   343  }
   344  
   345  // GetClaimsToRoles specifies dynamic mapping from claims to roles
   346  func (o *OIDCConnectorV3) GetClaimsToRoles() []ClaimMapping {
   347  	return o.Spec.ClaimsToRoles
   348  }
   349  
   350  // GetClaims returns list of claims expected by mappings
   351  func (o *OIDCConnectorV3) GetClaims() []string {
   352  	var out []string
   353  	for _, mapping := range o.Spec.ClaimsToRoles {
   354  		out = append(out, mapping.Claim)
   355  	}
   356  	return utils.Deduplicate(out)
   357  }
   358  
   359  // GetTraitMappings returns the OIDCConnector's TraitMappingSet
   360  func (o *OIDCConnectorV3) GetTraitMappings() TraitMappingSet {
   361  	tms := make([]TraitMapping, 0, len(o.Spec.ClaimsToRoles))
   362  	for _, mapping := range o.Spec.ClaimsToRoles {
   363  		tms = append(tms, TraitMapping{
   364  			Trait: mapping.Claim,
   365  			Value: mapping.Value,
   366  			Roles: mapping.Roles,
   367  		})
   368  	}
   369  	return TraitMappingSet(tms)
   370  }
   371  
   372  // setStaticFields sets static resource header and metadata fields.
   373  func (o *OIDCConnectorV3) setStaticFields() {
   374  	o.Kind = KindOIDCConnector
   375  }
   376  
   377  // CheckAndSetDefaults checks and set default values for any missing fields.
   378  func (o *OIDCConnectorV3) CheckAndSetDefaults() error {
   379  	o.setStaticFields()
   380  
   381  	switch o.Version {
   382  	case V2, V3:
   383  		// V2 is also supported
   384  	case "":
   385  		o.Version = V3
   386  	default:
   387  		return trace.BadParameter("Version: invalid OIDC connector version %v", o.Version)
   388  	}
   389  
   390  	if err := o.Metadata.CheckAndSetDefaults(); err != nil {
   391  		return trace.Wrap(err)
   392  	}
   393  
   394  	if name := o.Metadata.Name; slices.Contains(constants.SystemConnectors, name) {
   395  		return trace.BadParameter("ID: invalid connector name, %v is a reserved name", name)
   396  	}
   397  
   398  	if o.Spec.ClientID == "" {
   399  		return trace.BadParameter("ClientID: missing client id")
   400  	}
   401  
   402  	if len(o.GetClaimsToRoles()) == 0 {
   403  		return trace.BadParameter("claims_to_roles is empty, authorization with connector would never assign any roles")
   404  	}
   405  	for _, v := range o.Spec.ClaimsToRoles {
   406  		if len(v.Roles) == 0 {
   407  			return trace.BadParameter("add roles in claims_to_roles")
   408  		}
   409  	}
   410  
   411  	if _, err := url.Parse(o.GetIssuerURL()); err != nil {
   412  		return trace.BadParameter("bad IssuerURL '%v', err: %v", o.GetIssuerURL(), err)
   413  	}
   414  
   415  	if len(o.GetRedirectURLs()) == 0 {
   416  		return trace.BadParameter("RedirectURL: missing redirect_url")
   417  	}
   418  	for _, redirectURL := range o.GetRedirectURLs() {
   419  		if _, err := url.Parse(redirectURL); err != nil {
   420  			return trace.BadParameter("bad RedirectURL '%v', err: %v", redirectURL, err)
   421  		}
   422  	}
   423  
   424  	if o.GetGoogleServiceAccountURI() != "" && o.GetGoogleServiceAccount() != "" {
   425  		return trace.BadParameter("one of either google_service_account_uri or google_service_account is supported, not both")
   426  	}
   427  
   428  	if o.GetGoogleServiceAccountURI() != "" {
   429  		uri, err := utils.ParseSessionsURI(o.GetGoogleServiceAccountURI())
   430  		if err != nil {
   431  			return trace.Wrap(err)
   432  		}
   433  		if uri.Scheme != "file" {
   434  			return trace.BadParameter("only file:// scheme is supported for google_service_account_uri")
   435  		}
   436  		if o.GetGoogleAdminEmail() == "" {
   437  			return trace.BadParameter("whenever google_service_account_uri is specified, google_admin_email should be set as well, read https://developers.google.com/identity/protools/OAuth2ServiceAccount#delegatingauthority for more details")
   438  		}
   439  	}
   440  
   441  	if o.GetGoogleServiceAccount() != "" {
   442  		if o.GetGoogleAdminEmail() == "" {
   443  			return trace.BadParameter("whenever google_service_account is specified, google_admin_email should be set as well, read https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority for more details")
   444  		}
   445  	}
   446  
   447  	if o.Spec.MaxAge != nil {
   448  		maxAge := o.Spec.MaxAge.Value.Duration()
   449  		if maxAge < 0 {
   450  			return trace.BadParameter("max_age cannot be negative")
   451  		}
   452  		if maxAge.Round(time.Second) != maxAge {
   453  			return trace.BadParameter("max_age must be a multiple of seconds")
   454  		}
   455  	}
   456  
   457  	return nil
   458  }
   459  
   460  // GetAllowUnverifiedEmail returns true if unverified emails should be allowed in received users.
   461  func (o *OIDCConnectorV3) GetAllowUnverifiedEmail() bool {
   462  	return o.Spec.AllowUnverifiedEmail
   463  }
   464  
   465  // GetMaxAge returns the amount of time that user logins are
   466  // valid for and true if MaxAge is set. If a user logs in, but then
   467  // does not login again within this time period, they will be forced
   468  // to re-authenticate.
   469  func (o *OIDCConnectorV3) GetMaxAge() (time.Duration, bool) {
   470  	if o.Spec.MaxAge == nil {
   471  		return 0, false
   472  	}
   473  	return o.Spec.MaxAge.Value.Duration(), true
   474  }
   475  
   476  // Check returns nil if all parameters are great, err otherwise
   477  func (i *OIDCAuthRequest) Check() error {
   478  	if i.ConnectorID == "" {
   479  		return trace.BadParameter("ConnectorID: missing value")
   480  	}
   481  	if i.StateToken == "" {
   482  		return trace.BadParameter("StateToken: missing value")
   483  	}
   484  	if len(i.PublicKey) != 0 {
   485  		_, _, _, _, err := ssh.ParseAuthorizedKey(i.PublicKey)
   486  		if err != nil {
   487  			return trace.BadParameter("PublicKey: bad key: %v", err)
   488  		}
   489  		if (i.CertTTL > defaults.MaxCertDuration) || (i.CertTTL < defaults.MinCertDuration) {
   490  			return trace.BadParameter("CertTTL: wrong certificate TTL")
   491  		}
   492  	}
   493  
   494  	// we could collapse these two checks into one, but the error message would become ambiguous.
   495  	if i.SSOTestFlow && i.ConnectorSpec == nil {
   496  		return trace.BadParameter("ConnectorSpec cannot be nil when SSOTestFlow is true")
   497  	}
   498  
   499  	if !i.SSOTestFlow && i.ConnectorSpec != nil {
   500  		return trace.BadParameter("ConnectorSpec must be nil when SSOTestFlow is false")
   501  	}
   502  
   503  	return nil
   504  }