Files
teleport/lib/services/identity.go
T
Michael WilsonandRoman Tkachenko 383ad7a5de SAML IdP session objects. (#21521)
* SAML IdP session objects.

The upcoming SAML IdP feature requires session storage in the backend. To
support that, the `WebSession` that has been adapted for app sessions and
Snowflake sessions has been adapted for SAML IdP sessions.

The SAML IdP will be encoding the session structure as JSON and packing it
into the new `data` field within the web session.

* Update api/types/session.go

Co-authored-by: Roman Tkachenko <roman@goteleport.com>

* Use specific structure instead of a JSON blob.

* Update the create SAML session request.

* Make SAML session a pointer.

* Add in link to crewjam/saml object.

---------

Co-authored-by: Roman Tkachenko <roman@goteleport.com>
2023-02-13 19:52:33 +00:00

382 lines
16 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
Copyright 2015 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 services implements API services exposed by Teleport:
// * presence service that takes care of heartbeats
// * web service that takes care of web logins
// * ca service - certificate authorities
package services
import (
"context"
"crypto"
"time"
"github.com/gravitational/trace"
"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/types"
wantypes "github.com/gravitational/teleport/api/types/webauthn"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/defaults"
)
// UserGetter is responsible for getting users
type UserGetter interface {
// GetUser returns a user by name
GetUser(user string, withSecrets bool) (types.User, error)
}
// UsersService is responsible for basic user management
type UsersService interface {
UserGetter
// UpdateUser updates an existing user.
UpdateUser(ctx context.Context, user types.User) error
// UpsertUser updates parameters about user
UpsertUser(user types.User) error
// CompareAndSwapUser updates an existing user, but fails if the user does
// not match an expected backend value.
CompareAndSwapUser(ctx context.Context, new, existing types.User) error
// DeleteUser deletes a user with all the keys from the backend
DeleteUser(ctx context.Context, user string) error
// GetUsers returns a list of users registered with the local auth server
GetUsers(withSecrets bool) ([]types.User, error)
// DeleteAllUsers deletes all users
DeleteAllUsers() error
}
// Identity is responsible for managing user entries and external identities
type Identity interface {
// CreateUser creates user, only if the user entry does not exist
CreateUser(user types.User) error
// UsersService implements most methods
UsersService
// AddUserLoginAttempt logs user login attempt
AddUserLoginAttempt(user string, attempt LoginAttempt, ttl time.Duration) error
// GetUserLoginAttempts returns user login attempts
GetUserLoginAttempts(user string) ([]LoginAttempt, error)
// DeleteUserLoginAttempts removes all login attempts of a user. Should be
// called after successful login.
DeleteUserLoginAttempts(user string) error
// GetUserByOIDCIdentity returns a user by its specified OIDC Identity, returns first
// user specified with this identity
GetUserByOIDCIdentity(id types.ExternalIdentity) (types.User, error)
// GetUserBySAMLIdentity returns a user by its specified OIDC Identity, returns first
// user specified with this identity
GetUserBySAMLIdentity(id types.ExternalIdentity) (types.User, error)
// GetUserByGithubIdentity returns a user by its specified Github identity
GetUserByGithubIdentity(id types.ExternalIdentity) (types.User, error)
// UpsertPasswordHash upserts user password hash
UpsertPasswordHash(user string, hash []byte) error
// GetPasswordHash returns the password hash for a given user
GetPasswordHash(user string) ([]byte, error)
// UpsertUsedTOTPToken upserts a TOTP token to the backend so it can't be used again
// during the 30 second window it's valid.
UpsertUsedTOTPToken(user string, otpToken string) error
// GetUsedTOTPToken returns the last successfully used TOTP token.
GetUsedTOTPToken(user string) (string, error)
// UpsertPassword upserts new password and OTP token
UpsertPassword(user string, password []byte) error
// UpsertWebauthnLocalAuth creates or updates the local auth configuration for
// Webauthn.
// WebauthnLocalAuth is a component of LocalAuthSecrets.
// Automatically indexes the WebAuthn user ID for lookup by
// GetTeleportUserByWebauthnID.
UpsertWebauthnLocalAuth(ctx context.Context, user string, wla *types.WebauthnLocalAuth) error
// GetWebauthnLocalAuth retrieves the existing local auth configuration for
// Webauthn, if any.
// WebauthnLocalAuth is a component of LocalAuthSecrets.
GetWebauthnLocalAuth(ctx context.Context, user string) (*types.WebauthnLocalAuth, error)
// GetTeleportUserByWebauthnID reads a Teleport username from a WebAuthn user
// ID (aka user handle).
// See UpsertWebauthnLocalAuth and types.WebauthnLocalAuth.
GetTeleportUserByWebauthnID(ctx context.Context, webID []byte) (string, error)
// UpsertWebauthnSessionData creates or updates WebAuthn session data in
// storage, for the purpose of later verifying an authentication or
// registration challenge.
// Session data is expected to expire according to backend settings.
UpsertWebauthnSessionData(ctx context.Context, user, sessionID string, sd *wantypes.SessionData) error
// GetWebauthnSessionData retrieves a previously-stored session data by ID,
// if it exists and has not expired.
GetWebauthnSessionData(ctx context.Context, user, sessionID string) (*wantypes.SessionData, error)
// DeleteWebauthnSessionData deletes session data by ID, if it exists and has
// not expired.
DeleteWebauthnSessionData(ctx context.Context, user, sessionID string) error
// UpsertGlobalWebauthnSessionData creates or updates WebAuthn session data in
// storage, for the purpose of later verifying an authentication challenge.
// Session data is expected to expire according to backend settings.
// Used for passwordless challenges.
UpsertGlobalWebauthnSessionData(ctx context.Context, scope, id string, sd *wantypes.SessionData) error
// GetGlobalWebauthnSessionData retrieves previously-stored session data by ID,
// if it exists and has not expired.
// Used for passwordless challenges.
GetGlobalWebauthnSessionData(ctx context.Context, scope, id string) (*wantypes.SessionData, error)
// DeleteGlobalWebauthnSessionData deletes session data by ID, if it exists
// and has not expired.
DeleteGlobalWebauthnSessionData(ctx context.Context, scope, id string) error
// UpsertMFADevice upserts an MFA device for the user.
UpsertMFADevice(ctx context.Context, user string, d *types.MFADevice) error
// GetMFADevices gets all MFA devices for the user.
GetMFADevices(ctx context.Context, user string, withSecrets bool) ([]*types.MFADevice, error)
// DeleteMFADevice deletes an MFA device for the user by ID.
DeleteMFADevice(ctx context.Context, user, id string) error
// UpsertOIDCConnector upserts OIDC Connector
UpsertOIDCConnector(ctx context.Context, connector types.OIDCConnector) error
// DeleteOIDCConnector deletes OIDC Connector
DeleteOIDCConnector(ctx context.Context, connectorID string) error
// GetOIDCConnector returns OIDC connector data, withSecrets adds or removes client secret from return results
GetOIDCConnector(ctx context.Context, id string, withSecrets bool) (types.OIDCConnector, error)
// GetOIDCConnectors returns registered connectors, withSecrets adds or removes client secret from return results
GetOIDCConnectors(ctx context.Context, withSecrets bool) ([]types.OIDCConnector, error)
// CreateOIDCAuthRequest creates new auth request
CreateOIDCAuthRequest(ctx context.Context, req types.OIDCAuthRequest, ttl time.Duration) error
// GetOIDCAuthRequest returns OIDC auth request if found
GetOIDCAuthRequest(ctx context.Context, stateToken string) (*types.OIDCAuthRequest, error)
// UpsertSAMLConnector upserts SAML Connector
UpsertSAMLConnector(ctx context.Context, connector types.SAMLConnector) error
// DeleteSAMLConnector deletes OIDC Connector
DeleteSAMLConnector(ctx context.Context, connectorID string) error
// GetSAMLConnector returns OIDC connector data, withSecrets adds or removes secrets from return results
GetSAMLConnector(ctx context.Context, id string, withSecrets bool) (types.SAMLConnector, error)
// GetSAMLConnectors returns registered connectors, withSecrets adds or removes secret from return results
GetSAMLConnectors(ctx context.Context, withSecrets bool) ([]types.SAMLConnector, error)
// CreateSAMLAuthRequest creates new auth request
CreateSAMLAuthRequest(ctx context.Context, req types.SAMLAuthRequest, ttl time.Duration) error
// GetSAMLAuthRequest returns SAML auth request if found
GetSAMLAuthRequest(ctx context.Context, id string) (*types.SAMLAuthRequest, error)
// CreateSSODiagnosticInfo creates new SSO diagnostic info record.
CreateSSODiagnosticInfo(ctx context.Context, authKind string, authRequestID string, entry types.SSODiagnosticInfo) error
// GetSSODiagnosticInfo returns SSO diagnostic info records.
GetSSODiagnosticInfo(ctx context.Context, authKind string, authRequestID string) (*types.SSODiagnosticInfo, error)
// UpsertGithubConnector creates or updates a new Github connector
UpsertGithubConnector(ctx context.Context, connector types.GithubConnector) error
// GetGithubConnectors returns all configured Github connectors
GetGithubConnectors(ctx context.Context, withSecrets bool) ([]types.GithubConnector, error)
// GetGithubConnector returns a Github connector by its name
GetGithubConnector(ctx context.Context, name string, withSecrets bool) (types.GithubConnector, error)
// DeleteGithubConnector deletes a Github connector by its name
DeleteGithubConnector(ctx context.Context, name string) error
// CreateGithubAuthRequest creates a new auth request for Github OAuth2 flow
CreateGithubAuthRequest(ctx context.Context, req types.GithubAuthRequest) error
// GetGithubAuthRequest retrieves Github auth request by the token
GetGithubAuthRequest(ctx context.Context, stateToken string) (*types.GithubAuthRequest, error)
// CreateUserToken creates a new user token.
CreateUserToken(ctx context.Context, token types.UserToken) (types.UserToken, error)
// DeleteUserToken deletes a user token.
DeleteUserToken(ctx context.Context, tokenID string) error
// GetUserTokens returns all user tokens.
GetUserTokens(ctx context.Context) ([]types.UserToken, error)
// GetUserToken returns a user token by id.
GetUserToken(ctx context.Context, tokenID string) (types.UserToken, error)
// UpsertUserTokenSecrets upserts a user token secrets.
UpsertUserTokenSecrets(ctx context.Context, secrets types.UserTokenSecrets) error
// GetUserTokenSecrets returns a user token secrets.
GetUserTokenSecrets(ctx context.Context, tokenID string) (types.UserTokenSecrets, error)
// UpsertRecoveryCodes upserts a user's new recovery codes.
UpsertRecoveryCodes(ctx context.Context, user string, recovery *types.RecoveryCodesV1) error
// GetRecoveryCodes gets a user's recovery codes.
GetRecoveryCodes(ctx context.Context, user string, withSecrets bool) (*types.RecoveryCodesV1, error)
// CreateUserRecoveryAttempt logs user recovery attempt.
CreateUserRecoveryAttempt(ctx context.Context, user string, attempt *types.RecoveryAttempt) error
// GetUserRecoveryAttempts returns user recovery attempts sorted by oldest to latest time.
GetUserRecoveryAttempts(ctx context.Context, user string) ([]*types.RecoveryAttempt, error)
// DeleteUserRecoveryAttempts removes all recovery attempts of a user.
DeleteUserRecoveryAttempts(ctx context.Context, user string) error
// UpsertKeyAttestationData upserts a verified public key attestation response.
UpsertKeyAttestationData(ctx context.Context, attestationData *keys.AttestationData, ttl time.Duration) error
// GetKeyAttestationData gets a verified public key attestation response.
GetKeyAttestationData(ctx context.Context, publicKey crypto.PublicKey) (*keys.AttestationData, error)
types.WebSessionsGetter
types.WebTokensGetter
// AppSession defines application session features.
AppSession
// SnowflakeSession defines Snowflake session features.
SnowflakeSession
// SAMLIdPSession defines SAML IdP session features.
SAMLIdPSession
}
// AppSession defines application session features.
type AppSession interface {
// GetAppSession gets an application web session.
GetAppSession(context.Context, types.GetAppSessionRequest) (types.WebSession, error)
// GetAppSessions gets all application web sessions.
GetAppSessions(context.Context) ([]types.WebSession, error)
// ListAppSessions gets a paginated list of application web sessions.
ListAppSessions(ctx context.Context, pageSize int, pageToken, user string) ([]types.WebSession, string, error)
// UpsertAppSession upserts an application web session.
UpsertAppSession(context.Context, types.WebSession) error
// DeleteAppSession removes an application web session.
DeleteAppSession(context.Context, types.DeleteAppSessionRequest) error
// DeleteAllAppSessions removes all application web sessions.
DeleteAllAppSessions(context.Context) error
// DeleteUserAppSessions deletes all user’s application sessions.
DeleteUserAppSessions(ctx context.Context, req *proto.DeleteUserAppSessionsRequest) error
}
// SnowflakeSession defines Snowflake session features.
type SnowflakeSession interface {
// GetSnowflakeSession gets a Snowflake web session.
GetSnowflakeSession(context.Context, types.GetSnowflakeSessionRequest) (types.WebSession, error)
// GetSnowflakeSessions gets all Snowflake web sessions.
GetSnowflakeSessions(context.Context) ([]types.WebSession, error)
// UpsertSnowflakeSession upserts a Snowflake web session.
UpsertSnowflakeSession(context.Context, types.WebSession) error
// DeleteSnowflakeSession removes a Snowflake web session.
DeleteSnowflakeSession(context.Context, types.DeleteSnowflakeSessionRequest) error
// DeleteAllSnowflakeSessions removes all Snowflake web sessions.
DeleteAllSnowflakeSessions(context.Context) error
}
// SAMLIdPSession defines SAML IdP session features.
type SAMLIdPSession interface {
// GetSAMLIdPSession gets a SAML IdP session.
GetSAMLIdPSession(context.Context, types.GetSAMLIdPSessionRequest) (types.WebSession, error)
// ListSAMLIdPSessions gets a paginated list of SAML IdP sessions.
ListSAMLIdPSessions(ctx context.Context, pageSize int, pageToken, user string) ([]types.WebSession, string, error)
// UpsertSAMLIdPSession upserts a SAML IdP session.
UpsertSAMLIdPSession(context.Context, types.WebSession) error
// DeleteSAMLIdPSession removes a SAML IdP session.
DeleteSAMLIdPSession(context.Context, types.DeleteSAMLIdPSessionRequest) error
// DeleteAllSAMLIdPSessions removes all SAML IdP sessions.
DeleteAllSAMLIdPSessions(context.Context) error
// DeleteUserSAMLIdPSessions deletes all of a user's SAML IdP sessions.
DeleteUserSAMLIdPSessions(ctx context.Context, user string) error
}
// VerifyPassword makes sure password satisfies our requirements (relaxed),
// mostly to avoid putting garbage in
func VerifyPassword(password []byte) error {
if len(password) < defaults.MinPasswordLength {
return trace.BadParameter(
"password is too short, min length is %v", defaults.MinPasswordLength)
}
if len(password) > defaults.MaxPasswordLength {
return trace.BadParameter(
"password is too long, max length is %v", defaults.MaxPasswordLength)
}
return nil
}
// Users represents a slice of users,
// makes it sort compatible (sorts by username)
type Users []types.User
func (u Users) Len() int {
return len(u)
}
func (u Users) Less(i, j int) bool {
return u[i].GetName() < u[j].GetName()
}
func (u Users) Swap(i, j int) {
u[i], u[j] = u[j], u[i]
}
// SortedLoginAttempts sorts login attempts by time
type SortedLoginAttempts []LoginAttempt
// Len returns length of a role list
func (s SortedLoginAttempts) Len() int {
return len(s)
}
// Less stacks latest attempts to the end of the list
func (s SortedLoginAttempts) Less(i, j int) bool {
return s[i].Time.Before(s[j].Time)
}
// Swap swaps two attempts
func (s SortedLoginAttempts) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// LastFailed calculates last x successive attempts are failed
func LastFailed(x int, attempts []LoginAttempt) bool {
var failed int
for i := len(attempts) - 1; i >= 0; i-- {
if !attempts[i].Success {
failed++
} else {
return false
}
if failed >= x {
return true
}
}
return false
}