Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions api/types/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ import (
"github.com/gravitational/teleport/api/utils"
)

// UserType is the user's types that indicates where it was created.
type UserType string

const (
// UserTypeSSO identifies a user that was created from an SSO provider.
UserTypeSSO UserType = "sso"
// UserTypeLocal identifies a user that was created in Teleport itself and has no connection to an external identity.
UserTypeLocal UserType = "local"
)

// User represents teleport embedded user or external user.
type User interface {
// ResourceWithSecrets provides common resource properties
Expand Down Expand Up @@ -99,6 +109,8 @@ type User interface {
GetCreatedBy() CreatedBy
// SetCreatedBy sets created by information
SetCreatedBy(CreatedBy)
// GetUserType indicates if the User was created by an SSO Provider or locally.
GetUserType() UserType
// GetTraits gets the trait map for this user used to populate role variables.
GetTraits() map[string][]string
// SetTraits sets the trait map for this user used to populate role variables.
Expand Down Expand Up @@ -404,6 +416,15 @@ func (u UserV2) GetGCPServiceAccounts() []string {
return u.getTrait(constants.TraitGCPServiceAccounts)
}

// GetUserType indicates if the User was created by an SSO Provider or locally.
func (u UserV2) GetUserType() UserType {
if u.GetCreatedBy().Connector == nil {
return UserTypeLocal
}

return UserTypeSSO
}

func (u *UserV2) String() string {
return fmt.Sprintf("User(name=%v, roles=%v, identities=%v)", u.Metadata.Name, u.Spec.Roles, u.Spec.OIDCIdentities)
}
Expand Down
1,148 changes: 579 additions & 569 deletions gen/proto/go/prehog/v1alpha/teleport.pb.go

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions gen/proto/js/prehog/v1alpha/teleport_pb.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 31 additions & 1 deletion gen/proto/js/prehog/v1alpha/teleport_pb.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 12 additions & 1 deletion lib/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -2073,6 +2073,7 @@ func generateCert(a *Server, req certRequest, caType types.CertAuthType) (*proto
AssetTag: req.deviceExtensions.AssetTag,
CredentialID: req.deviceExtensions.CredentialID,
},
UserType: req.user.GetUserType(),
}

var signedTLSCert []byte
Expand Down Expand Up @@ -4314,7 +4315,17 @@ func (a *Server) SubmitUsageEvent(ctx context.Context, req *proto.SubmitUsageEve
return trace.Wrap(err)
}

event, err := usagereporter.ConvertUsageEvent(req.GetEvent(), username)
userIsSSO, err := authz.GetClientUserIsSSO(ctx)
if err != nil {
return trace.Wrap(err)
}

userMetadata := usagereporter.UserMetadata{
Username: username,
IsSSO: userIsSSO,
}

event, err := usagereporter.ConvertUsageEvent(req.GetEvent(), userMetadata)
if err != nil {
return trace.Wrap(err)
}
Expand Down
2 changes: 1 addition & 1 deletion lib/auth/auth_with_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -2607,7 +2607,7 @@ func (a *ServerWithRoles) generateUserCerts(ctx context.Context, req proto.UserC
}

// Do not allow SSO users to be impersonated.
if req.Username != a.context.User.GetName() && user.GetCreatedBy().Connector != nil {
if req.Username != a.context.User.GetName() && user.GetUserType() == types.UserTypeSSO {
log.Warningf("User %v tried to issue a cert for externally managed user %v, this is not supported.", a.context.User.GetName(), req.Username)
return nil, trace.AccessDenied("access denied")
}
Expand Down
2 changes: 1 addition & 1 deletion lib/auth/bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ func (s *Server) generateInitialBotCerts(ctx context.Context, username string, p
}

// Do not allow SSO users to be impersonated.
if user.GetCreatedBy().Connector != nil {
if user.GetUserType() == types.UserTypeSSO {
log.Warningf("Tried to issue a renewable cert for externally managed user %v, this is not supported.", username)
return nil, trace.AccessDenied("access denied")
}
Expand Down
34 changes: 28 additions & 6 deletions lib/authz/permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@ func (a *authorizer) authorizeRemoteUser(ctx context.Context, u RemoteUser) (*Co
LoginIP: u.Identity.LoginIP,
PinnedIP: u.Identity.PinnedIP,
PrivateKeyPolicy: u.Identity.PrivateKeyPolicy,
UserType: u.Identity.UserType,
}
if checker.PinSourceIP() && identity.PinnedIP == "" {
return nil, trace.AccessDenied("pinned IP is required for the user, but is not present on identity")
Expand Down Expand Up @@ -931,21 +932,42 @@ func ClientUsername(ctx context.Context) string {
return identity.Username
}

// GetClientUsername returns the username of a remote HTTP client making the call.
// If ctx didn't pass through auth middleware or did not come from an HTTP
// request, returns an error.
func GetClientUsername(ctx context.Context) (string, error) {
func userIdentityFromContext(ctx context.Context) (*tlsca.Identity, error) {
userWithIdentity, err := UserFromContext(ctx)
if err != nil {
return "", trace.AccessDenied("missing identity")
return nil, trace.AccessDenied("missing identity")
}

identity := userWithIdentity.GetIdentity()
if identity.Username == "" {
return "", trace.AccessDenied("missing identity username")
return nil, trace.AccessDenied("missing identity username")
}

return &identity, nil
}

// GetClientUsername returns the username of a remote HTTP client making the call.
// If ctx didn't pass through auth middleware or did not come from an HTTP
// request, returns an error.
func GetClientUsername(ctx context.Context) (string, error) {
identity, err := userIdentityFromContext(ctx)
if err != nil {
return "", trace.Wrap(err)
}
return identity.Username, nil
}

// GetClientUserIsSSO extracts the identity of a remote HTTP client and indicates whether that is an SSO user.
// If ctx didn't pass through auth middleware or did not come from an HTTP
// request, returns an error.
func GetClientUserIsSSO(ctx context.Context) (bool, error) {
identity, err := userIdentityFromContext(ctx)
if err != nil {
return false, trace.Wrap(err)
}
return identity.UserType == types.UserTypeSSO, nil
}

// ClientImpersonator returns the impersonator username of a remote client
// making the call. If not present, returns an empty string
func ClientImpersonator(ctx context.Context) string {
Expand Down
26 changes: 26 additions & 0 deletions lib/authz/permissions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,32 @@ func upsertLockWithPutEvent(ctx context.Context, t *testing.T, client *testClien
}
}

func TestGetClientUserIsSSO(t *testing.T) {
ctx := context.Background()

u := LocalUser{
Username: "someuser",
Identity: tlsca.Identity{
Username: "someuser",
Groups: []string{"somerole"},
},
}

// Non SSO user must return false
nonSSOUserCtx := context.WithValue(ctx, contextUser, u)

isSSO, err := GetClientUserIsSSO(nonSSOUserCtx)
require.NoError(t, err)
require.False(t, isSSO, "expected a non-SSO user")

// An SSO user must return true
u.Identity.UserType = types.UserTypeSSO
ssoUserCtx := context.WithValue(ctx, contextUser, u)
localUserIsSSO, err := GetClientUserIsSSO(ssoUserCtx)
require.NoError(t, err)
require.True(t, localUserIsSSO, "expected an SSO user")
}

func TestAuthorizer_Authorize_deviceTrust(t *testing.T) {
t.Parallel()

Expand Down
3 changes: 3 additions & 0 deletions lib/tlsca/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ type Identity struct {

// DeviceExtensions holds device-aware extensions for the identity.
DeviceExtensions DeviceExtensions

// UserType indicates if the User was created by an SSO Provider or locally.
UserType types.UserType
}

// RouteToApp holds routing information for applications.
Expand Down
Loading