Files
teleport/lib/auth/saml.go
T
Michael Wilson 49c64aabcb Move the authorizer into its own package. (#22776)
* Move the authorizer into its own package.

The Authorizer and associated functions and constants have been moved into
their own package `authz`. This will allow us to more easily create separate
services for gRPC functionality so that we're not pushing as much into the
`lib/auth` package.

Though this PR is large, the only actual code change here is making
`ContextForLocalUser` exported and changing the `permissions_test.go` so that
it doesn't rely on the `lib/auth` package.

* Use context functions.

* Test and linting fixes.

* Add in a temporary alias file to ensure e still compiles.

* Use functions instead of var aliases for performance reasons.

* Adding in godocs, cleaning up tests.

* Fix tests.

* Add a few more aliases.
2023-03-09 02:21:10 +00:00

185 lines
7.1 KiB
Go

/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package auth
import (
"context"
"encoding/json"
"github.com/gravitational/trace"
"github.com/gravitational/teleport/api/types"
apievents "github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/lib/authz"
"github.com/gravitational/teleport/lib/events"
"github.com/gravitational/teleport/lib/services"
)
// ErrSAMLRequiresEnterprise is the error returned by the SAML methods when not
// using the Enterprise edition of Teleport.
//
// TODO(zmb3): ideally we would wrap ErrRequiresEnterprise here, but
// we can't currently propagate wrapped errors across the gRPC boundary,
// and we want tctl to display a clean user-facing message in this case
var ErrSAMLRequiresEnterprise = trace.AccessDenied("SAML is only available in Teleport Enterprise")
// SAMLService are the methods that the auth server delegates to a plugin for
// implementing the SAML connector. These are the core functions of SAML
// authentication - the connector CRUD operations and Get methods are
// implemeneted in auth.Server and provide no connector-specific logic.
type SAMLService interface {
// CreateSAMLAuthRequest creates SAML AuthnRequest
CreateSAMLAuthRequest(ctx context.Context, req types.SAMLAuthRequest) (*types.SAMLAuthRequest, error)
// ValidateSAMLResponse validates SAML auth response
ValidateSAMLResponse(ctx context.Context, re string, connectorID string) (*SAMLAuthResponse, error)
}
// UpsertSAMLConnector creates or updates a SAML connector.
func (a *Server) UpsertSAMLConnector(ctx context.Context, connector types.SAMLConnector) error {
// Validate the SAML connector here, because even though Services.UpsertSAMLConnector
// also validates, it does not have a RoleGetter to use to validate the roles, so
// has to pass `nil` for the second argument.
if err := services.ValidateSAMLConnector(connector, a); err != nil {
return trace.Wrap(err)
}
if err := a.Services.UpsertSAMLConnector(ctx, connector); err != nil {
return trace.Wrap(err)
}
if err := a.emitter.EmitAuditEvent(ctx, &apievents.SAMLConnectorCreate{
Metadata: apievents.Metadata{
Type: events.SAMLConnectorCreatedEvent,
Code: events.SAMLConnectorCreatedCode,
},
UserMetadata: authz.ClientUserMetadata(ctx),
ResourceMetadata: apievents.ResourceMetadata{
Name: connector.GetName(),
},
}); err != nil {
log.WithError(err).Warn("Failed to emit SAML connector create event.")
}
return nil
}
// DeleteSAMLConnector deletes a SAML connector.
func (a *Server) DeleteSAMLConnector(ctx context.Context, connectorID string) error {
if err := a.Services.DeleteSAMLConnector(ctx, connectorID); err != nil {
return trace.Wrap(err)
}
if err := a.emitter.EmitAuditEvent(ctx, &apievents.SAMLConnectorDelete{
Metadata: apievents.Metadata{
Type: events.SAMLConnectorDeletedEvent,
Code: events.SAMLConnectorDeletedCode,
},
UserMetadata: authz.ClientUserMetadata(ctx),
ResourceMetadata: apievents.ResourceMetadata{
Name: connectorID,
},
}); err != nil {
log.WithError(err).Warn("Failed to emit SAML connector delete event.")
}
return nil
}
// CreateSAMLAuthRequest delegates the method call to the samlAuthService if present,
// or returns a NotImplemented error if not present.
func (a *Server) CreateSAMLAuthRequest(ctx context.Context, req types.SAMLAuthRequest) (*types.SAMLAuthRequest, error) {
if a.samlAuthService == nil {
return nil, trace.Wrap(ErrSAMLRequiresEnterprise)
}
rq, err := a.samlAuthService.CreateSAMLAuthRequest(ctx, req)
return rq, trace.Wrap(err)
}
// ValidateSAMLResponse delegates the method call to the samlAuthService if present,
// or returns a NotImplemented error if not present.
func (a *Server) ValidateSAMLResponse(ctx context.Context, re string, connectorID string) (*SAMLAuthResponse, error) {
if a.samlAuthService == nil {
return nil, trace.Wrap(ErrSAMLRequiresEnterprise)
}
resp, err := a.samlAuthService.ValidateSAMLResponse(ctx, re, connectorID)
return resp, trace.Wrap(err)
}
// SAMLAuthResponse is returned when auth server validated callback parameters
// returned from SAML identity provider
type SAMLAuthResponse struct {
// Username is an authenticated teleport username
Username string `json:"username"`
// Identity contains validated SAML identity
Identity types.ExternalIdentity `json:"identity"`
// Web session will be generated by auth server if requested in SAMLAuthRequest
Session types.WebSession `json:"session,omitempty"`
// Cert will be generated by certificate authority
Cert []byte `json:"cert,omitempty"`
// TLSCert is a PEM encoded TLS certificate
TLSCert []byte `json:"tls_cert,omitempty"`
// Req is an original SAML auth request
Req SAMLAuthRequest `json:"req"`
// HostSigners is a list of signing host public keys
// trusted by proxy, used in console login
HostSigners []types.CertAuthority `json:"host_signers"`
}
// SAMLAuthRequest is a SAML auth request that supports standard json marshaling.
type SAMLAuthRequest struct {
// ID is a unique request ID.
ID string `json:"id"`
// PublicKey is an optional public key, users want these
// keys to be signed by auth servers user CA in case
// of successful auth.
PublicKey []byte `json:"public_key"`
// CSRFToken is associated with user web session token.
CSRFToken string `json:"csrf_token"`
// CreateWebSession indicates if user wants to generate a web
// session after successful authentication.
CreateWebSession bool `json:"create_web_session"`
// ClientRedirectURL is a URL client wants to be redirected
// after successful authentication.
ClientRedirectURL string `json:"client_redirect_url"`
}
// ValidateSAMLResponseReq is the request made by the proxy to validate
// and activate a login via SAML.
type ValidateSAMLResponseReq struct {
Response string `json:"response"`
ConnectorID string `json:"connector_id,omitempty"`
}
// SAMLAuthRawResponse is returned when auth server validated callback parameters
// returned from SAML provider
type SAMLAuthRawResponse struct {
// Username is authenticated teleport username
Username string `json:"username"`
// Identity contains validated OIDC identity
Identity types.ExternalIdentity `json:"identity"`
// Web session will be generated by auth server if requested in OIDCAuthRequest
Session json.RawMessage `json:"session,omitempty"`
// Cert will be generated by certificate authority
Cert []byte `json:"cert,omitempty"`
// Req is original oidc auth request
Req SAMLAuthRequest `json:"req"`
// HostSigners is a list of signing host public keys
// trusted by proxy, used in console login
HostSigners []json.RawMessage `json:"host_signers"`
// TLSCert is TLS certificate authority certificate
TLSCert []byte `json:"tls_cert,omitempty"`
}