mirror of
https://github.com/gravitational/teleport.git
synced 2026-11-02 23:31:08 +08:00
252 lines
7.9 KiB
Go
252 lines
7.9 KiB
Go
/*
|
|
* Teleport
|
|
* Copyright (C) 2023 Gravitational, Inc.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package auth
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"slices"
|
|
"time"
|
|
|
|
"github.com/gravitational/trace"
|
|
|
|
"github.com/gravitational/teleport/api/types"
|
|
"github.com/gravitational/teleport/api/types/accesslist"
|
|
apievents "github.com/gravitational/teleport/api/types/events"
|
|
"github.com/gravitational/teleport/lib/authz"
|
|
"github.com/gravitational/teleport/lib/events"
|
|
)
|
|
|
|
// CreateRole creates a role and emits a related audit event.
|
|
func (a *Server) CreateRole(ctx context.Context, role types.Role) (types.Role, error) {
|
|
created, err := a.Services.CreateRole(ctx, role)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
if err := a.emitter.EmitAuditEvent(a.closeCtx, &apievents.RoleCreate{
|
|
Metadata: apievents.Metadata{
|
|
Type: events.RoleCreatedEvent,
|
|
Code: events.RoleCreatedCode,
|
|
},
|
|
UserMetadata: authz.ClientUserMetadata(ctx),
|
|
ResourceMetadata: apievents.ResourceMetadata{
|
|
Name: role.GetName(),
|
|
},
|
|
ConnectionMetadata: authz.ConnectionMetadata(ctx),
|
|
}); err != nil {
|
|
log.WithError(err).Warnf("Failed to emit role create event.")
|
|
}
|
|
return created, nil
|
|
}
|
|
|
|
// UpdateRole updates a role and emits a related audit event.
|
|
func (a *Server) UpdateRole(ctx context.Context, role types.Role) (types.Role, error) {
|
|
created, err := a.Services.UpdateRole(ctx, role)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
if err := a.emitter.EmitAuditEvent(a.closeCtx, &apievents.RoleUpdate{
|
|
Metadata: apievents.Metadata{
|
|
Type: events.RoleUpdatedEvent,
|
|
Code: events.RoleUpdatedCode,
|
|
},
|
|
UserMetadata: authz.ClientUserMetadata(ctx),
|
|
ResourceMetadata: apievents.ResourceMetadata{
|
|
Name: role.GetName(),
|
|
},
|
|
ConnectionMetadata: authz.ConnectionMetadata(ctx),
|
|
}); err != nil {
|
|
log.WithError(err).Warnf("Failed to emit role create event.")
|
|
}
|
|
return created, nil
|
|
}
|
|
|
|
// UpsertRole creates or updates a role and emits a related audit event.
|
|
func (a *Server) UpsertRole(ctx context.Context, role types.Role) (types.Role, error) {
|
|
upserted, err := a.Services.UpsertRole(ctx, role)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
if err := a.emitter.EmitAuditEvent(a.closeCtx, &apievents.RoleCreate{
|
|
Metadata: apievents.Metadata{
|
|
Type: events.RoleCreatedEvent,
|
|
Code: events.RoleCreatedCode,
|
|
},
|
|
UserMetadata: authz.ClientUserMetadata(ctx),
|
|
ResourceMetadata: apievents.ResourceMetadata{
|
|
Name: role.GetName(),
|
|
},
|
|
ConnectionMetadata: authz.ConnectionMetadata(ctx),
|
|
}); err != nil {
|
|
log.WithError(err).Warnf("Failed to emit role create event.")
|
|
}
|
|
return upserted, nil
|
|
}
|
|
|
|
var (
|
|
errDeleteRoleUser = errors.New("failed to delete a role that is still in use by a user, check the system server logs for more details")
|
|
errDeleteRoleCA = errors.New("failed to delete a role that is still in use by a certificate authority, check the system server logs for more details")
|
|
errDeleteRoleAccessList = errors.New("failed to delete a role that is still in use by an access list, check the system server logs for more details")
|
|
)
|
|
|
|
// DeleteRole deletes a role and emits a related audit event.
|
|
func (a *Server) DeleteRole(ctx context.Context, name string) error {
|
|
// check if this role is used by CA or Users
|
|
users, err := a.Services.GetUsers(ctx, false)
|
|
if err != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
for _, u := range users {
|
|
if slices.Contains(u.GetRoles(), name) {
|
|
// Mask the actual error here as it could be used to enumerate users
|
|
// within the system.
|
|
log.Warnf("Failed to delete role: role %v is used by user %v.", name, u.GetName())
|
|
return trace.Wrap(errDeleteRoleUser)
|
|
}
|
|
}
|
|
// check if it's used by some external cert authorities, e.g.
|
|
// cert authorities related to external cluster
|
|
cas, err := a.Services.GetCertAuthorities(ctx, types.UserCA, false)
|
|
if err != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
for _, a := range cas {
|
|
if slices.Contains(a.GetRoles(), name) {
|
|
// Mask the actual error here as it could be used to enumerate users
|
|
// within the system.
|
|
log.Warnf("Failed to delete role: role %v is used by user cert authority %v", name, a.GetClusterName())
|
|
return trace.Wrap(errDeleteRoleCA)
|
|
}
|
|
}
|
|
|
|
var nextToken string
|
|
for {
|
|
var accessLists []*accesslist.AccessList
|
|
var err error
|
|
accessLists, nextToken, err = a.Services.AccessListClient().ListAccessLists(ctx, 0 /* default page size */, nextToken)
|
|
if err != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
|
|
for _, accessList := range accessLists {
|
|
if slices.Contains(accessList.Spec.Grants.Roles, name) {
|
|
log.Warnf("Failed to delete role: role %v is granted by access list %s", name, accessList.GetName())
|
|
return trace.Wrap(errDeleteRoleAccessList)
|
|
}
|
|
|
|
if slices.Contains(accessList.Spec.MembershipRequires.Roles, name) {
|
|
log.Warnf("Failed to delete role: role %v is required by members of access list %s", name, accessList.GetName())
|
|
return trace.Wrap(errDeleteRoleAccessList)
|
|
}
|
|
|
|
if slices.Contains(accessList.Spec.OwnershipRequires.Roles, name) {
|
|
log.Warnf("Failed to delete role: role %v is required by owners of access list %s", name, accessList.GetName())
|
|
return trace.Wrap(errDeleteRoleAccessList)
|
|
}
|
|
}
|
|
|
|
if nextToken == "" {
|
|
break
|
|
}
|
|
}
|
|
|
|
if err := a.Services.DeleteRole(ctx, name); err != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
|
|
if err := a.emitter.EmitAuditEvent(a.closeCtx, &apievents.RoleDelete{
|
|
Metadata: apievents.Metadata{
|
|
Type: events.RoleDeletedEvent,
|
|
Code: events.RoleDeletedCode,
|
|
},
|
|
UserMetadata: authz.ClientUserMetadata(ctx),
|
|
ResourceMetadata: apievents.ResourceMetadata{
|
|
Name: name,
|
|
},
|
|
ConnectionMetadata: authz.ConnectionMetadata(ctx),
|
|
}); err != nil {
|
|
log.WithError(err).Warnf("Failed to emit role delete event.")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// UpsertLock upserts a lock and emits a related audit event.
|
|
func (a *Server) UpsertLock(ctx context.Context, lock types.Lock) error {
|
|
if err := a.Services.UpsertLock(ctx, lock); err != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
|
|
var expiresTime time.Time
|
|
// leave as 0 if no lock expiration was set
|
|
if le := lock.LockExpiry(); le != nil {
|
|
expiresTime = le.UTC()
|
|
}
|
|
um := authz.ClientUserMetadata(ctx)
|
|
if err := a.emitter.EmitAuditEvent(a.closeCtx, &apievents.LockCreate{
|
|
Metadata: apievents.Metadata{
|
|
Type: events.LockCreatedEvent,
|
|
Code: events.LockCreatedCode,
|
|
},
|
|
UserMetadata: um,
|
|
ResourceMetadata: apievents.ResourceMetadata{
|
|
Name: lock.GetName(),
|
|
Expires: expiresTime,
|
|
UpdatedBy: um.User,
|
|
},
|
|
Target: lock.Target(),
|
|
Lock: apievents.LockMetadata{
|
|
Target: lock.Target(),
|
|
},
|
|
}); err != nil {
|
|
log.WithError(err).Warning("Failed to emit lock create event.")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// DeleteLock deletes a lock and emits a related audit event.
|
|
func (a *Server) DeleteLock(ctx context.Context, lockName string) error {
|
|
lock, err := a.Services.GetLock(ctx, lockName)
|
|
if err != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
if err := a.Services.DeleteLock(ctx, lockName); err != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
|
|
if err := a.emitter.EmitAuditEvent(a.closeCtx, &apievents.LockDelete{
|
|
Metadata: apievents.Metadata{
|
|
Type: events.LockDeletedEvent,
|
|
Code: events.LockDeletedCode,
|
|
},
|
|
UserMetadata: authz.ClientUserMetadata(ctx),
|
|
ResourceMetadata: apievents.ResourceMetadata{
|
|
Name: lockName,
|
|
},
|
|
Lock: apievents.LockMetadata{
|
|
Target: lock.Target(),
|
|
},
|
|
}); err != nil {
|
|
log.WithError(err).Warning("Failed to emit lock delete event.")
|
|
}
|
|
return nil
|
|
}
|