Remove the auth namespaces REST API (#50999)

Eliminates the namespace CRUD API as part of
https://github.com/gravitational/teleport/issues/49509.

The most notable change introduce here is with flushCache in the
auth helpers. It was ironically using namespaces because they
were deprecated, however, it has since been updated to use a role
instead. The helpers.go file was also updated not to rely on testify
in attempt to prevent it from being included in production builds.

Updates https://github.com/gravitational/teleport/issues/6394.
This commit is contained in:
rosstimothy
2025-01-15 14:21:23 +00:00
committed by GitHub
parent 228b392330
commit efaec73e21
19 changed files with 65 additions and 650 deletions
-55
View File
@@ -144,12 +144,6 @@ func NewAPIServer(config *APIConfig) (http.Handler, error) {
// TODO(strideynet): REMOVE IN 18.0.0 - this method is now gRPC
srv.POST("/:version/tokens/register", srv.WithAuth(srv.registerUsingToken))
// Namespaces
srv.POST("/:version/namespaces", srv.WithAuth(srv.upsertNamespace))
srv.GET("/:version/namespaces", srv.WithAuth(srv.getNamespaces))
srv.GET("/:version/namespaces/:namespace", srv.WithAuth(srv.getNamespace))
srv.DELETE("/:version/namespaces/:namespace", srv.WithAuth(srv.deleteNamespace))
// cluster configuration
srv.GET("/:version/configuration/name", srv.WithAuth(srv.getClusterName))
srv.POST("/:version/configuration/name", srv.WithAuth(srv.setClusterName))
@@ -669,55 +663,6 @@ func (s *APIServer) searchSessionEvents(auth *ServerWithRoles, w http.ResponseWr
return eventsList, nil
}
type upsertNamespaceReq struct {
Namespace types.Namespace `json:"namespace"`
}
func (s *APIServer) upsertNamespace(auth *ServerWithRoles, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req *upsertNamespaceReq
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
if err := auth.UpsertNamespace(req.Namespace); err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
func (s *APIServer) getNamespaces(auth *ServerWithRoles, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
namespaces, err := auth.GetNamespaces()
if err != nil {
return nil, trace.Wrap(err)
}
return namespaces, nil
}
func (s *APIServer) getNamespace(auth *ServerWithRoles, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
name := p.ByName("namespace")
if !types.IsValidNamespace(name) {
return nil, trace.BadParameter("invalid namespace %q", name)
}
namespace, err := auth.GetNamespace(name)
if err != nil {
return nil, trace.Wrap(err)
}
return namespace, nil
}
func (s *APIServer) deleteNamespace(auth *ServerWithRoles, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
name := p.ByName("namespace")
if !types.IsValidNamespace(name) {
return nil, trace.BadParameter("invalid namespace %q", name)
}
err := auth.DeleteNamespace(name)
if err != nil {
return nil, trace.Wrap(err)
}
return message("ok"), nil
}
func (s *APIServer) getClusterName(auth *ServerWithRoles, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
cn, err := auth.GetClusterName()
if err != nil {
-15
View File
@@ -5065,21 +5065,6 @@ func (a *Server) GetWebSessionInfo(ctx context.Context, user, sessionID string)
return sess.WithoutSecrets(), nil
}
func (a *Server) DeleteNamespace(namespace string) error {
ctx := context.TODO()
if namespace == apidefaults.Namespace {
return trace.AccessDenied("can't delete default namespace")
}
nodes, err := a.GetNodes(ctx, namespace)
if err != nil {
return trace.Wrap(err)
}
if len(nodes) != 0 {
return trace.BadParameter("can't delete namespace %v that has %v registered nodes", namespace, len(nodes))
}
return a.Services.DeleteNamespace(namespace)
}
// IterateRoles is a helper used to read a page of roles with a custom matcher, used by access-control logic to handle
// per-resource read permissions.
func (a *Server) IterateRoles(ctx context.Context, req *proto.ListRolesRequest, match func(*types.RoleV6) (bool, error)) ([]*types.RoleV6, string, error) {
-4
View File
@@ -194,10 +194,6 @@ func newTestPack(
return p, trace.Wrap(err)
}
if err := p.a.UpsertNamespace(types.DefaultNamespace()); err != nil {
return p, trace.Wrap(err)
}
return p, nil
}
-32
View File
@@ -4346,38 +4346,6 @@ func (a *ServerWithRoles) findSessionEndEvent(ctx context.Context, sid session.I
return nil, trace.NotFound("session end event not found for session ID %q", sid)
}
// GetNamespaces returns a list of namespaces
func (a *ServerWithRoles) GetNamespaces() ([]types.Namespace, error) {
if err := a.action(types.KindNamespace, types.VerbList, types.VerbRead); err != nil {
return nil, trace.Wrap(err)
}
return a.authServer.GetNamespaces()
}
// GetNamespace returns namespace by name
func (a *ServerWithRoles) GetNamespace(name string) (*types.Namespace, error) {
if err := a.action(types.KindNamespace, types.VerbRead); err != nil {
return nil, trace.Wrap(err)
}
return a.authServer.GetNamespace(name)
}
// UpsertNamespace upserts namespace
func (a *ServerWithRoles) UpsertNamespace(ns types.Namespace) error {
if err := a.action(types.KindNamespace, types.VerbCreate, types.VerbUpdate); err != nil {
return trace.Wrap(err)
}
return a.authServer.UpsertNamespace(ns)
}
// DeleteNamespace deletes namespace by name
func (a *ServerWithRoles) DeleteNamespace(name string) error {
if err := a.action(types.KindNamespace, types.VerbDelete); err != nil {
return trace.Wrap(err)
}
return a.authServer.DeleteNamespace(name)
}
// GetRoles returns a list of roles
func (a *ServerWithRoles) GetRoles(ctx context.Context) ([]types.Role, error) {
// delegate all access-control logic to ListRoles, which will eventually
-54
View File
@@ -145,12 +145,6 @@ type ReadNodeAccessPoint interface {
// GetRoles returns a list of roles
GetRoles(ctx context.Context) ([]types.Role, error)
// GetNamespaces returns a list of namespaces
GetNamespaces() ([]types.Namespace, error)
// GetNamespace returns namespace by name
GetNamespace(name string) (*types.Namespace, error)
// GetNetworkRestrictions returns networking restrictions for restricted shell to enforce
GetNetworkRestrictions(ctx context.Context) (types.NetworkRestrictions, error)
}
@@ -209,12 +203,6 @@ type ReadProxyAccessPoint interface {
// GetUser returns a services.User for this cluster.
GetUser(ctx context.Context, name string, withSecrets bool) (types.User, error)
// GetNamespaces returns a list of namespaces
GetNamespaces() ([]types.Namespace, error)
// GetNamespace returns namespace by name
GetNamespace(name string) (*types.Namespace, error)
// GetNode returns a node by name and namespace.
GetNode(ctx context.Context, namespace, name string) (types.Server, error)
@@ -382,12 +370,6 @@ type ReadRemoteProxyAccessPoint interface {
// GetRoles returns a list of roles
GetRoles(ctx context.Context) ([]types.Role, error)
// GetNamespaces returns a list of namespaces
GetNamespaces() ([]types.Namespace, error)
// GetNamespace returns namespace by name
GetNamespace(name string) (*types.Namespace, error)
// GetNode returns a node by name and namespace.
GetNode(ctx context.Context, namespace, name string) (types.Server, error)
@@ -476,12 +458,6 @@ type ReadKubernetesAccessPoint interface {
// GetRoles returns a list of roles
GetRoles(ctx context.Context) ([]types.Role, error)
// GetNamespaces returns a list of namespaces
GetNamespaces() ([]types.Namespace, error)
// GetNamespace returns namespace by name
GetNamespace(name string) (*types.Namespace, error)
// GetKubernetesServers returns a list of kubernetes servers registered in the cluster
GetKubernetesServers(context.Context) ([]types.KubeServer, error)
@@ -555,12 +531,6 @@ type ReadAppsAccessPoint interface {
// GetProxies returns a list of proxy servers registered in the cluster
GetProxies() ([]types.Server, error)
// GetNamespaces returns a list of namespaces
GetNamespaces() ([]types.Namespace, error)
// GetNamespace returns namespace by name
GetNamespace(name string) (*types.Namespace, error)
// GetApps returns all application resources.
GetApps(ctx context.Context) ([]types.Application, error)
@@ -622,12 +592,6 @@ type ReadDatabaseAccessPoint interface {
// GetProxies returns a list of proxy servers registered in the cluster
GetProxies() ([]types.Server, error)
// GetNamespaces returns a list of namespaces
GetNamespaces() ([]types.Namespace, error)
// GetNamespace returns namespace by name
GetNamespace(name string) (*types.Namespace, error)
// GetDatabases returns all database resources.
GetDatabases(ctx context.Context) ([]types.Database, error)
@@ -686,12 +650,6 @@ type ReadWindowsDesktopAccessPoint interface {
// GetRoles returns a list of roles
GetRoles(ctx context.Context) ([]types.Role, error)
// GetNamespaces returns a list of namespaces
GetNamespaces() ([]types.Namespace, error)
// GetNamespace returns namespace by name
GetNamespace(name string) (*types.Namespace, error)
// GetWindowsDesktops returns windows desktop hosts.
GetWindowsDesktops(ctx context.Context, filter types.WindowsDesktopFilter) ([]types.WindowsDesktop, error)
@@ -732,12 +690,6 @@ type ReadDiscoveryAccessPoint interface {
// GetClusterName gets the name of the cluster from the backend.
GetClusterName(opts ...services.MarshalOption) (types.ClusterName, error)
// GetNamespaces returns a list of namespaces
GetNamespaces() ([]types.Namespace, error)
// GetNamespace returns namespace by name
GetNamespace(name string) (*types.Namespace, error)
// GetNodes returns a list of registered servers for this cluster.
GetNodes(ctx context.Context, namespace string) ([]types.Server, error)
// GetKubernetesCluster returns a kubernetes cluster resource identified by name.
@@ -998,12 +950,6 @@ type Cache interface {
// GetSessionRecordingConfig returns session recording configuration.
GetSessionRecordingConfig(ctx context.Context) (types.SessionRecordingConfig, error)
// GetNamespaces returns a list of namespaces
GetNamespaces() ([]types.Namespace, error)
// GetNamespace returns namespace by name
GetNamespace(name string) (*types.Namespace, error)
// GetNode returns a node by name and namespace.
GetNode(ctx context.Context, namespace, name string) (types.Server, error)
-42
View File
@@ -40,7 +40,6 @@ import (
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/httplib"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/utils"
)
// HTTPClientConfig contains configuration for an HTTP client.
@@ -797,47 +796,6 @@ func (c *HTTPClient) ValidateGithubAuthCallback(ctx context.Context, q url.Value
return &response, nil
}
// GetNamespaces returns a list of namespaces
func (c *HTTPClient) GetNamespaces() ([]types.Namespace, error) {
out, err := c.Get(context.TODO(), c.Endpoint("namespaces"), url.Values{})
if err != nil {
return nil, trace.Wrap(err)
}
var re []types.Namespace
if err := utils.FastUnmarshal(out.Bytes(), &re); err != nil {
return nil, trace.Wrap(err)
}
return re, nil
}
// GetNamespace returns namespace by name
func (c *HTTPClient) GetNamespace(name string) (*types.Namespace, error) {
if name == "" {
return nil, trace.BadParameter("missing namespace name")
}
out, err := c.Get(context.TODO(), c.Endpoint("namespaces", name), url.Values{})
if err != nil {
return nil, trace.Wrap(err)
}
return services.UnmarshalNamespace(out.Bytes())
}
type upsertNamespaceReq struct {
Namespace types.Namespace `json:"namespace"`
}
// UpsertNamespace upserts namespace
func (c *HTTPClient) UpsertNamespace(ns types.Namespace) error {
_, err := c.PostJSON(context.TODO(), c.Endpoint("namespaces"), upsertNamespaceReq{Namespace: ns})
return trace.Wrap(err)
}
// DeleteNamespace deletes namespace by name
func (c *HTTPClient) DeleteNamespace(name string) error {
_, err := c.Delete(context.TODO(), c.Endpoint("namespaces", name))
return trace.Wrap(err)
}
// GetClusterName returns a cluster name
func (c *HTTPClient) GetClusterName(opts ...services.MarshalOption) (types.ClusterName, error) {
out, err := c.Get(context.TODO(), c.Endpoint("configuration", "name"), url.Values{})
+37 -22
View File
@@ -31,7 +31,6 @@ import (
"github.com/google/uuid"
"github.com/gravitational/trace"
"github.com/jonboulle/clockwork"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"golang.org/x/crypto/bcrypt"
"golang.org/x/crypto/ssh"
@@ -1113,11 +1112,11 @@ func (t *TestTLSServer) CloneClient(tt *testing.T, clt *authclient.Client) *auth
},
CircuitBreakerConfig: breaker.NoopBreakerConfig(),
})
require.NoError(tt, err)
if err != nil {
tt.Fatalf("error creating auth client: %v", err.Error())
}
tt.Cleanup(func() {
require.NoError(tt, newClient.Close())
})
tt.Cleanup(func() { _ = newClient.Close() })
return newClient
}
@@ -1458,12 +1457,12 @@ func CreateUserAndRoleWithoutRoles(clt clt, username string, allowedLogins []str
// flushClt is the set of methods expected by the flushCache helper.
type flushClt interface {
// GetNamespace returns namespace by name
GetNamespace(name string) (*types.Namespace, error)
// UpsertNamespace upserts namespace
UpsertNamespace(types.Namespace) error
// DeleteNamespace deletes namespace by name
DeleteNamespace(name string) error
// GetRole returns role by name
GetRole(ctx context.Context, name string) (types.Role, error)
// CreateRole creates a new role.
CreateRole(context.Context, types.Role) (types.Role, error)
// DeleteRole deletes the role by name.
DeleteRole(ctx context.Context, name string) error
}
// flushCache is a helper for waiting until preceding changes have propagated to the
@@ -1473,19 +1472,35 @@ type flushClt interface {
// write events for different keys show up in the order in which the writes were performed, which
// is not necessarily true for all backends.
func flushCache(t *testing.T, clt flushClt) {
ctx := context.Background()
// the pattern of writing a resource and then waiting for it to appear
// works for any resource type (when using memory backend). we use namespaces
// here because namespaces are deprecated and therefore unlikely to interfer
// with tests.
// works for any resource type (when using memory backend).
name := strings.ReplaceAll(uuid.NewString(), "-", "")
defer clt.DeleteNamespace(name)
defer clt.DeleteRole(ctx, name)
ns, err := types.NewNamespace(name)
require.NoError(t, err)
role, err := types.NewRole(name, types.RoleSpecV6{})
if err != nil {
t.Fatalf("Failed to instantiate new role: %v", err)
}
require.NoError(t, clt.UpsertNamespace(ns))
require.Eventually(t, func() bool {
_, err := clt.GetNamespace(name)
return err == nil
}, time.Second*20, time.Millisecond*200)
role, err = clt.CreateRole(ctx, role)
if err != nil {
t.Fatalf("Failed to create new role: %v", err)
}
ctx, cancel := context.WithTimeout(ctx, 20*time.Second)
defer cancel()
for {
r, err := clt.GetRole(ctx, name)
if err == nil && r.GetRevision() == role.GetRevision() {
return
}
select {
case <-time.After(200 * time.Millisecond):
case <-ctx.Done():
t.Fatal("Time out waiting for role to be replicated")
}
}
}
-8
View File
@@ -43,7 +43,6 @@ import (
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/client/proto"
apidefaults "github.com/gravitational/teleport/api/defaults"
clusterconfigpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/clusterconfig/v1"
dbobjectimportrulev1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/dbobjectimportrule/v1"
machineidv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/machineid/v1"
@@ -501,13 +500,6 @@ func initCluster(ctx context.Context, cfg InitConfig, asrv *Server) error {
return trace.Wrap(asrv.SetStaticTokens(cfg.StaticTokens))
})
g.Go(func() error {
_, span := cfg.Tracer.Start(gctx, "auth/SetClusterNamespace")
defer span.End()
asrv.logger.InfoContext(ctx, "Creating namespace", "namespace", apidefaults.Namespace)
return trace.Wrap(asrv.UpsertNamespace(types.DefaultNamespace()))
})
var cn types.ClusterName
g.Go(func() error {
_, span := cfg.Tracer.Start(gctx, "auth/SetClusterName")
+25 -43
View File
@@ -40,6 +40,7 @@ import (
"github.com/jonboulle/clockwork"
"go.opentelemetry.io/otel"
"github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/utils"
@@ -179,21 +180,15 @@ func checkPendingTime(iid *imds.InstanceIdentityDocument, provisionToken types.P
}
func nodeExists(ctx context.Context, presence services.Presence, hostID string) (bool, error) {
namespaces, err := presence.GetNamespaces()
if err != nil {
_, err := presence.GetNode(ctx, defaults.Namespace, hostID)
switch {
case trace.IsNotFound(err):
return false, nil
case err != nil:
return false, trace.Wrap(err)
default:
return true, nil
}
for _, namespace := range namespaces {
_, err := presence.GetNode(ctx, namespace.GetName(), hostID)
if trace.IsNotFound(err) {
continue
} else if err != nil {
return false, trace.Wrap(err)
} else {
return true, nil
}
}
return false, nil
}
func proxyExists(ctx context.Context, presence services.Presence, hostID string) (bool, error) {
@@ -223,59 +218,46 @@ func kubeExists(ctx context.Context, presence services.Presence, hostID string)
}
func appExists(ctx context.Context, presence services.Presence, hostID string) (bool, error) {
namespaces, err := presence.GetNamespaces()
apps, err := presence.GetApplicationServers(ctx, defaults.Namespace)
if err != nil {
return false, trace.Wrap(err)
}
for _, namespace := range namespaces {
apps, err := presence.GetApplicationServers(ctx, namespace.GetName())
if err != nil {
return false, trace.Wrap(err)
}
for _, app := range apps {
if app.GetName() == hostID {
return true, nil
}
for _, app := range apps {
if app.GetName() == hostID {
return true, nil
}
}
return false, nil
}
func dbExists(ctx context.Context, presence services.Presence, hostID string) (bool, error) {
namespaces, err := presence.GetNamespaces()
dbs, err := presence.GetDatabaseServers(ctx, defaults.Namespace)
if err != nil {
return false, trace.Wrap(err)
}
for _, namespace := range namespaces {
dbs, err := presence.GetDatabaseServers(ctx, namespace.GetName())
if err != nil {
return false, trace.Wrap(err)
}
for _, db := range dbs {
if db.GetName() == hostID {
return true, nil
}
for _, db := range dbs {
if db.GetName() == hostID {
return true, nil
}
}
return false, nil
}
func oktaExists(ctx context.Context, presence services.Presence, hostID string) (bool, error) {
namespaces, err := presence.GetNamespaces()
apps, err := presence.GetApplicationServers(ctx, defaults.Namespace)
if err != nil {
return false, trace.Wrap(err)
}
for _, namespace := range namespaces {
apps, err := presence.GetApplicationServers(ctx, namespace.GetName())
if err != nil {
return false, trace.Wrap(err)
}
for _, app := range apps {
if app.GetName() == hostID && app.Origin() == types.OriginOkta {
return true, nil
}
for _, app := range apps {
if app.GetName() == hostID && app.Origin() == types.OriginOkta {
return true, nil
}
}
return false, nil
}
+1 -1
View File
@@ -805,7 +805,7 @@ func TestHostUniqueCheck(t *testing.T) {
// request should fail
_, err = a.RegisterUsingToken(ctx, &request)
expectedErr := &trace.AccessDeniedError{}
require.ErrorAs(t, err, &expectedErr)
require.ErrorAs(t, err, &expectedErr, err)
}
})
}
-42
View File
@@ -142,7 +142,6 @@ func ForAuth(cfg Config) Config {
{Kind: types.KindToken},
{Kind: types.KindUser},
{Kind: types.KindRole},
{Kind: types.KindNamespace},
{Kind: types.KindNode},
{Kind: types.KindProxy},
{Kind: types.KindAuthServer},
@@ -225,7 +224,6 @@ func ForProxy(cfg Config) Config {
{Kind: types.KindUIConfig},
{Kind: types.KindUser},
{Kind: types.KindRole},
{Kind: types.KindNamespace},
{Kind: types.KindNode},
{Kind: types.KindProxy},
{Kind: types.KindAuthServer},
@@ -277,7 +275,6 @@ func ForRemoteProxy(cfg Config) Config {
{Kind: types.KindSessionRecordingConfig},
{Kind: types.KindUser},
{Kind: types.KindRole},
{Kind: types.KindNamespace},
{Kind: types.KindNode},
{Kind: types.KindProxy},
{Kind: types.KindAuthServer},
@@ -315,10 +312,6 @@ func ForNode(cfg Config) Config {
{Kind: types.KindClusterAuthPreference},
{Kind: types.KindSessionRecordingConfig},
{Kind: types.KindRole},
// Node only needs to "know" about default
// namespace events to avoid matching too much
// data about other namespaces or node events
{Kind: types.KindNamespace, Name: apidefaults.Namespace},
{Kind: types.KindNetworkRestrictions},
{Kind: types.KindStaticHostUser},
}
@@ -339,7 +332,6 @@ func ForKubernetes(cfg Config) Config {
{Kind: types.KindSessionRecordingConfig},
{Kind: types.KindUser},
{Kind: types.KindRole},
{Kind: types.KindNamespace, Name: apidefaults.Namespace},
{Kind: types.KindKubeServer},
{Kind: types.KindKubernetesCluster},
{Kind: types.KindKubeWaitingContainer},
@@ -361,9 +353,6 @@ func ForApps(cfg Config) Config {
{Kind: types.KindUser},
{Kind: types.KindRole},
{Kind: types.KindProxy},
// Applications only need to "know" about default namespace events to avoid
// matching too much data about other namespaces or events.
{Kind: types.KindNamespace, Name: apidefaults.Namespace},
{Kind: types.KindApp},
}
cfg.QueueSize = defaults.AppsQueueSize
@@ -383,9 +372,6 @@ func ForDatabases(cfg Config) Config {
{Kind: types.KindUser},
{Kind: types.KindRole},
{Kind: types.KindProxy},
// Databases only need to "know" about default namespace events to
// avoid matching too much data about other namespaces or events.
{Kind: types.KindNamespace, Name: apidefaults.Namespace},
{Kind: types.KindDatabase},
}
cfg.QueueSize = defaults.DatabasesQueueSize
@@ -404,7 +390,6 @@ func ForWindowsDesktop(cfg Config) Config {
{Kind: types.KindSessionRecordingConfig},
{Kind: types.KindUser},
{Kind: types.KindRole},
{Kind: types.KindNamespace, Name: apidefaults.Namespace},
{Kind: types.KindWindowsDesktopService},
{Kind: types.KindWindowsDesktop},
{Kind: types.KindDynamicWindowsDesktop},
@@ -419,7 +404,6 @@ func ForDiscovery(cfg Config) Config {
cfg.Watches = []types.WatchKind{
{Kind: types.KindCertAuthority, LoadSecrets: false, Filter: makeAllKnownCAsFilter().IntoMap()},
{Kind: types.KindClusterName},
{Kind: types.KindNamespace, Name: apidefaults.Namespace},
{Kind: types.KindNode},
{Kind: types.KindKubernetesCluster},
{Kind: types.KindKubeServer},
@@ -2178,32 +2162,6 @@ func (c *Cache) GetRole(ctx context.Context, name string) (types.Role, error) {
return role, err
}
// GetNamespace returns namespace
func (c *Cache) GetNamespace(name string) (*types.Namespace, error) {
_, span := c.Tracer.Start(context.TODO(), "cache/GetNamespace")
defer span.End()
rg, err := readCollectionCache(c, c.collections.namespaces)
if err != nil {
return nil, trace.Wrap(err)
}
defer rg.Release()
return rg.reader.GetNamespace(name)
}
// GetNamespaces is a part of auth.Cache implementation
func (c *Cache) GetNamespaces() ([]types.Namespace, error) {
_, span := c.Tracer.Start(context.TODO(), "cache/GetNamespaces")
defer span.End()
rg, err := readCollectionCache(c, c.collections.namespaces)
if err != nil {
return nil, trace.Wrap(err)
}
defer rg.Release()
return rg.reader.GetNamespaces()
}
// GetNode finds and returns a node by name and namespace.
func (c *Cache) GetNode(ctx context.Context, namespace, name string) (types.Server, error) {
ctx, span := c.Tracer.Start(ctx, "cache/GetNode")
-65
View File
@@ -187,10 +187,6 @@ func newPackForAuth(t *testing.T) *testPack {
return newTestPack(t, ForAuth)
}
func newPackForProxy(t *testing.T) *testPack {
return newTestPack(t, ForProxy)
}
func newTestPack(t *testing.T, setupConfig SetupConfigFn) *testPack {
pack, err := newPack(t.TempDir(), setupConfig)
require.NoError(t, err)
@@ -1604,66 +1600,6 @@ func TestClusterName(t *testing.T) {
require.Empty(t, cmp.Diff(outName, clusterName, cmpopts.IgnoreFields(types.Metadata{}, "Revision")))
}
// TestNamespaces tests caching of namespaces
func TestNamespaces(t *testing.T) {
t.Parallel()
p := newPackForProxy(t)
t.Cleanup(p.Close)
v, err := types.NewNamespace("universe")
require.NoError(t, err)
ns := &v
err = p.presenceS.UpsertNamespace(*ns)
require.NoError(t, err)
ns, err = p.presenceS.GetNamespace(ns.GetName())
require.NoError(t, err)
select {
case event := <-p.eventsC:
require.Equal(t, EventProcessed, event.Type)
case <-time.After(time.Second):
t.Fatalf("timeout waiting for event")
}
out, err := p.cache.GetNamespace(ns.GetName())
require.NoError(t, err)
require.Empty(t, cmp.Diff(ns, out, cmpopts.IgnoreFields(types.Metadata{}, "Revision")))
// update namespace metadata
ns.Metadata.Labels = map[string]string{"a": "b"}
require.NoError(t, err)
err = p.presenceS.UpsertNamespace(*ns)
require.NoError(t, err)
ns, err = p.presenceS.GetNamespace(ns.GetName())
require.NoError(t, err)
select {
case event := <-p.eventsC:
require.Equal(t, EventProcessed, event.Type)
case <-time.After(time.Second):
t.Fatalf("timeout waiting for event")
}
out, err = p.cache.GetNamespace(ns.GetName())
require.NoError(t, err)
require.Empty(t, cmp.Diff(ns, out, cmpopts.IgnoreFields(types.Metadata{}, "Revision")))
err = p.presenceS.DeleteNamespace(ns.GetName())
require.NoError(t, err)
select {
case <-p.eventsC:
case <-time.After(time.Second):
t.Fatalf("timeout waiting for event")
}
_, err = p.cache.GetNamespace(ns.GetName())
require.True(t, trace.IsNotFound(err))
}
// TestUsers tests caching of users
func TestUsers(t *testing.T) {
t.Parallel()
@@ -3279,7 +3215,6 @@ func TestRelativeExpiryOnlyForAuth(t *testing.T) {
c.Clock = clock
c.target = "llama"
c.Watches = []types.WatchKind{
{Kind: types.KindNamespace},
{Kind: types.KindNode},
{Kind: types.KindCertAuthority},
}
-54
View File
@@ -142,7 +142,6 @@ type cacheCollections struct {
staticHostUsers collectionReader[staticHostUserGetter]
kubeServers collectionReader[kubeServerGetter]
locks collectionReader[services.LockGetter]
namespaces collectionReader[namespaceGetter]
networkRestrictions collectionReader[networkRestrictionGetter]
oktaAssignments collectionReader[oktaAssignmentGetter]
oktaImportRules collectionReader[oktaImportRuleGetter]
@@ -302,15 +301,6 @@ func setupCollections(c *Cache, watches []types.WatchKind) (*cacheCollections, e
watch: watch,
}
collections.byKind[resourceKind] = collections.roles
case types.KindNamespace:
if c.Presence == nil {
return nil, trace.BadParameter("missing parameter Presence")
}
collections.namespaces = &genericCollection[*types.Namespace, namespaceGetter, namespaceExecutor]{
cache: c,
watch: watch,
}
collections.byKind[resourceKind] = collections.namespaces
case types.KindNode:
if c.Presence == nil {
return nil, trace.BadParameter("missing parameter Presence")
@@ -1073,50 +1063,6 @@ type nodeGetter interface {
var _ executor[types.Server, nodeGetter] = nodeExecutor{}
type namespaceExecutor struct{}
func (namespaceExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*types.Namespace, error) {
namespaces, err := cache.Presence.GetNamespaces()
if err != nil {
return nil, trace.Wrap(err)
}
derefNamespaces := make([]*types.Namespace, len(namespaces))
for i := range namespaces {
ns := namespaces[i]
derefNamespaces[i] = &ns
}
return derefNamespaces, nil
}
func (namespaceExecutor) upsert(ctx context.Context, cache *Cache, resource *types.Namespace) error {
return cache.presenceCache.UpsertNamespace(*resource)
}
func (namespaceExecutor) deleteAll(ctx context.Context, cache *Cache) error {
return cache.presenceCache.DeleteAllNamespaces()
}
func (namespaceExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error {
return cache.presenceCache.DeleteNamespace(resource.GetName())
}
func (namespaceExecutor) isSingleton() bool { return false }
func (namespaceExecutor) getReader(cache *Cache, cacheOK bool) namespaceGetter {
if cacheOK {
return cache.presenceCache
}
return cache.Config.Presence
}
type namespaceGetter interface {
GetNamespaces() ([]types.Namespace, error)
GetNamespace(name string) (*types.Namespace, error)
}
var _ executor[*types.Namespace, namespaceGetter] = namespaceExecutor{}
type certAuthorityExecutor struct {
// extracted from watch.Filter, to avoid rebuilding on every event
filter types.CertAuthorityFilter
-8
View File
@@ -2960,14 +2960,6 @@ func (process *TeleportProcess) initSSH() error {
"use builtin namespace %q, or omit the 'namespace' config option.", ns, apidefaults.Namespace)
}
namespace := types.ProcessNamespace(cfg.SSH.Namespace)
_, err = authClient.GetNamespace(namespace)
if err != nil {
if trace.IsNotFound(err) {
return trace.NotFound(
"namespace %v is not found, ask your system administrator to create this namespace so you can register nodes there.", namespace)
}
return trace.Wrap(err)
}
if auditd.IsLoginUIDSet() {
const warn = "Login UID is set, but it shouldn't be. Incorrect login UID breaks session ID when using auditd. " +
-85
View File
@@ -62,91 +62,6 @@ func NewPresenceService(b backend.Backend) *PresenceService {
}
}
// DeleteAllNamespaces deletes all namespaces
func (s *PresenceService) DeleteAllNamespaces() error {
startKey := backend.ExactKey(namespacesPrefix)
endKey := backend.RangeEnd(startKey)
return s.DeleteRange(context.TODO(), startKey, endKey)
}
// GetNamespaces returns a list of namespaces
func (s *PresenceService) GetNamespaces() ([]types.Namespace, error) {
startKey := backend.ExactKey(namespacesPrefix)
endKey := backend.RangeEnd(startKey)
result, err := s.GetRange(context.TODO(), startKey, endKey, backend.NoLimit)
if err != nil {
return nil, trace.Wrap(err)
}
out := make([]types.Namespace, 0, len(result.Items))
for _, item := range result.Items {
if !item.Key.HasSuffix(backend.NewKey(paramsPrefix)) {
continue
}
ns, err := services.UnmarshalNamespace(
item.Value, services.WithExpires(item.Expires), services.WithRevision(item.Revision))
if err != nil {
return nil, trace.Wrap(err)
}
out = append(out, *ns)
}
sort.Sort(types.SortedNamespaces(out))
return out, nil
}
// UpsertNamespace upserts namespace
func (s *PresenceService) UpsertNamespace(n types.Namespace) error {
if err := n.CheckAndSetDefaults(); err != nil {
return trace.Wrap(err)
}
rev := n.GetRevision()
value, err := services.MarshalNamespace(n)
if err != nil {
return trace.Wrap(err)
}
item := backend.Item{
Key: backend.NewKey(namespacesPrefix, n.Metadata.Name, paramsPrefix),
Value: value,
Expires: n.Metadata.Expiry(),
Revision: rev,
}
_, err = s.Put(context.TODO(), item)
if err != nil {
return trace.Wrap(err)
}
return nil
}
// GetNamespace returns a namespace by name
func (s *PresenceService) GetNamespace(name string) (*types.Namespace, error) {
if name == "" {
return nil, trace.BadParameter("missing namespace name")
}
item, err := s.Get(context.TODO(), backend.NewKey(namespacesPrefix, name, paramsPrefix))
if err != nil {
if trace.IsNotFound(err) {
return nil, trace.NotFound("namespace %q is not found", name)
}
return nil, trace.Wrap(err)
}
return services.UnmarshalNamespace(
item.Value, services.WithExpires(item.Expires), services.WithRevision(item.Revision))
}
// DeleteNamespace deletes a namespace with all the keys from the backend
func (s *PresenceService) DeleteNamespace(namespace string) error {
if namespace == "" {
return trace.BadParameter("missing namespace name")
}
err := s.Delete(context.TODO(), backend.NewKey(namespacesPrefix, namespace, paramsPrefix))
if err != nil {
if trace.IsNotFound(err) {
return trace.NotFound("namespace %q is not found", namespace)
}
}
return trace.Wrap(err)
}
// GetServerInfos returns a stream of ServerInfos.
func (s *PresenceService) GetServerInfos(ctx context.Context) stream.Stream[types.ServerInfo] {
startKey := backend.ExactKey(serverInfoPrefix)
-15
View File
@@ -126,21 +126,6 @@ type Presence interface {
// ListReverseTunnels returns a page of ReverseTunnels.
ListReverseTunnels(ctx context.Context, pageSize int, pageToken string) ([]types.ReverseTunnel, string, error)
// GetNamespaces returns a list of namespaces
GetNamespaces() ([]types.Namespace, error)
// GetNamespace returns namespace by name
GetNamespace(name string) (*types.Namespace, error)
// DeleteAllNamespaces deletes all namespaces
DeleteAllNamespaces() error
// UpsertNamespace upserts namespace
UpsertNamespace(types.Namespace) error
// DeleteNamespace deletes namespace by name
DeleteNamespace(name string) error
// GetServerInfos returns a stream of ServerInfos.
GetServerInfos(ctx context.Context) stream.Stream[types.ServerInfo]
-84
View File
@@ -833,32 +833,6 @@ func (s *ServicesTestSuite) RolesCRUD(t *testing.T) {
require.True(t, trace.IsNotFound(err))
}
func (s *ServicesTestSuite) NamespacesCRUD(t *testing.T) {
out, err := s.PresenceS.GetNamespaces()
require.NoError(t, err)
require.Empty(t, out)
ns := types.Namespace{
Kind: types.KindNamespace,
Version: types.V2,
Metadata: types.Metadata{
Name: apidefaults.Namespace,
Namespace: apidefaults.Namespace,
},
}
err = s.PresenceS.UpsertNamespace(ns)
require.NoError(t, err)
nsout, err := s.PresenceS.GetNamespace(ns.Metadata.Name)
require.NoError(t, err)
require.Empty(t, cmp.Diff(nsout, &ns))
err = s.PresenceS.DeleteNamespace(ns.Metadata.Name)
require.NoError(t, err)
_, err = s.PresenceS.GetNamespace(ns.Metadata.Name)
require.True(t, trace.IsNotFound(err))
}
func (s *ServicesTestSuite) SAMLCRUD(t *testing.T) {
ctx := context.Background()
connector := &types.SAMLConnectorV2{
@@ -1714,32 +1688,6 @@ func (s *ServicesTestSuite) Events(t *testing.T) {
return token
},
},
{
name: "Namespace",
kind: types.WatchKind{
Kind: types.KindNamespace,
},
crud: func(context.Context) types.Resource {
ns := types.Namespace{
Kind: types.KindNamespace,
Version: types.V2,
Metadata: types.Metadata{
Name: "testnamespace",
Namespace: apidefaults.Namespace,
},
}
err := s.PresenceS.UpsertNamespace(ns)
require.NoError(t, err)
out, err := s.PresenceS.GetNamespace(ns.Metadata.Name)
require.NoError(t, err)
err = s.PresenceS.DeleteNamespace(ns.Metadata.Name)
require.NoError(t, err)
return out
},
},
{
name: "Static tokens",
kind: types.WatchKind{
@@ -1927,38 +1875,6 @@ func (s *ServicesTestSuite) Events(t *testing.T) {
AllowPartialSuccess: true,
})
// Namespace with a name
testCases = []eventTest{
{
name: "Namespace with a name",
kind: types.WatchKind{
Kind: types.KindNamespace,
Name: "shmest",
},
crud: func(context.Context) types.Resource {
ns := types.Namespace{
Kind: types.KindNamespace,
Version: types.V2,
Metadata: types.Metadata{
Name: "shmest",
Namespace: apidefaults.Namespace,
},
}
err := s.PresenceS.UpsertNamespace(ns)
require.NoError(t, err)
out, err := s.PresenceS.GetNamespace(ns.Metadata.Name)
require.NoError(t, err)
err = s.PresenceS.DeleteNamespace(ns.Metadata.Name)
require.NoError(t, err)
return out
},
},
}
s.runEventsTests(t, testCases, types.Watch{Kinds: eventsTestKinds(testCases)})
// tests that a watch fails given an unknown kind when the partial success mode is not enabled
s.runUnknownEventsTest(t, types.Watch{Kinds: []types.WatchKind{
{Kind: types.KindNamespace},
+1 -9
View File
@@ -2954,16 +2954,8 @@ type getSiteNamespacesResponse struct {
//
// {"namespaces": [{..namespace resource...}]}
func (h *Handler) getSiteNamespaces(w http.ResponseWriter, r *http.Request, _ httprouter.Params, c *SessionContext, site reversetunnelclient.RemoteSite) (interface{}, error) {
clt, err := site.GetClient()
if err != nil {
return nil, trace.Wrap(err)
}
namespaces, err := clt.GetNamespaces()
if err != nil {
return nil, trace.Wrap(err)
}
return getSiteNamespacesResponse{
Namespaces: namespaces,
Namespaces: []types.Namespace{types.DefaultNamespace()},
}, nil
}
+1 -12
View File
@@ -2360,18 +2360,7 @@ func (rc *ResourceCommand) getCollection(ctx context.Context, client *authclient
warnAboutDynamicLabelsInDenyRule(ctx, rc.config.Logger, role)
return &roleCollection{roles: []types.Role{role}}, nil
case types.KindNamespace:
if rc.ref.Name == "" {
namespaces, err := client.GetNamespaces()
if err != nil {
return nil, trace.Wrap(err)
}
return &namespaceCollection{namespaces: namespaces}, nil
}
ns, err := client.GetNamespace(rc.ref.Name)
if err != nil {
return nil, trace.Wrap(err)
}
return &namespaceCollection{namespaces: []types.Namespace{*ns}}, nil
return &namespaceCollection{namespaces: []types.Namespace{types.DefaultNamespace()}}, nil
case types.KindTrustedCluster:
if rc.ref.Name == "" {
trustedClusters, err := client.GetTrustedClusters(ctx)