mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-31 20:39:37 +08:00
* Removes desktop logins from the ACL and adds them to each Desktop resource as returned to the frontend. This behavior parallels what's currently being done with other similar resources such as Nodes, and fixes a bug where users were able to see all the logins permitted by their roles on every desktop "connect" dropdown, even if the role permitting that login didn't apply to a given desktop. * Extracts common logic out of GetWindowsDesktopLogins (now GetAllowedWindowsDesktopLogins), uses it to create a GetAllowedServerLogins as well, tests both of them together, removing EnumerateServerLogins * reduce unnecessary looping by using maps * fixes flakiness by sorting lists * uses require.ElementsMatch rather than manually sorting lists * consolidates duplicated logic * Consolidates separate server and desktop logic into a single GetAllowedLoginsForResource * fixes tests * fixes import order and test
485 lines
12 KiB
Go
485 lines
12 KiB
Go
/*
|
|
Copyright 2022 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 ui
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
apidefaults "github.com/gravitational/teleport/api/defaults"
|
|
"github.com/gravitational/teleport/api/types"
|
|
"github.com/gravitational/teleport/lib/services"
|
|
)
|
|
|
|
func TestStripProtocolAndPort(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cases := []struct {
|
|
uri string
|
|
expected string
|
|
}{
|
|
{uri: "rediss://redis.example.com:6379?mode=cluster", expected: "redis.example.com"},
|
|
{uri: "rediss://redis.example.com:6379", expected: "redis.example.com"},
|
|
{uri: "https://abc12345.snowflakecomputing.com", expected: "abc12345.snowflakecomputing.com"},
|
|
{uri: "mongodb://mongo1.example.com:27017,mongo2.example.com:27017/?replicaSet=rs0&readPreference=secondary", expected: "mongo1.example.com"},
|
|
{uri: "mongodb+srv://cluster0.abcd.mongodb.net", expected: "cluster0.abcd.mongodb.net"},
|
|
{uri: "mongo.example.com:27017", expected: "mongo.example.com"},
|
|
{uri: "example.com", expected: "example.com"},
|
|
{uri: "", expected: ""},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
hostname := stripProtocolAndPort(tc.uri)
|
|
require.Equal(t, tc.expected, hostname)
|
|
}
|
|
}
|
|
|
|
func TestGetAllowedKubeUsersAndGroupsForCluster(t *testing.T) {
|
|
devEnvRole := &types.RoleV6{
|
|
Spec: types.RoleSpecV6{
|
|
Allow: types.RoleConditions{
|
|
KubeUsers: []string{"devuser"},
|
|
KubeGroups: []string{"devgroup"},
|
|
Namespaces: []string{apidefaults.Namespace},
|
|
KubernetesLabels: types.Labels{
|
|
"env": []string{"dev"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
prodEnvRole := &types.RoleV6{
|
|
Spec: types.RoleSpecV6{
|
|
Allow: types.RoleConditions{
|
|
KubeUsers: []string{"produser"},
|
|
KubeGroups: []string{"prodgroup"},
|
|
Namespaces: []string{apidefaults.Namespace},
|
|
KubernetesLabels: types.Labels{
|
|
"env": []string{"prod"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
anyEnvRole := &types.RoleV6{
|
|
Spec: types.RoleSpecV6{
|
|
Allow: types.RoleConditions{
|
|
KubeUsers: []string{"anyenvrole"},
|
|
KubeGroups: []string{"anyenvgroup"},
|
|
Namespaces: []string{apidefaults.Namespace},
|
|
KubernetesLabels: types.Labels{
|
|
"env": []string{"*"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
rootUser := &types.RoleV6{
|
|
Spec: types.RoleSpecV6{
|
|
Allow: types.RoleConditions{
|
|
KubeUsers: []string{"root"},
|
|
KubeGroups: []string{"rootgroup"},
|
|
Namespaces: []string{apidefaults.Namespace},
|
|
KubernetesLabels: types.Labels{
|
|
"*": []string{"*"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
roleWithMultipleLabels := &types.RoleV6{
|
|
Spec: types.RoleSpecV6{
|
|
Allow: types.RoleConditions{
|
|
KubeUsers: []string{"multiplelabelsuser"},
|
|
KubeGroups: []string{"multiplelabelsgroup"},
|
|
Namespaces: []string{apidefaults.Namespace},
|
|
KubernetesLabels: types.Labels{
|
|
"region": []string{"*"},
|
|
"env": []string{"dev"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
tt := []struct {
|
|
name string
|
|
cluster types.KubeCluster
|
|
roleSet services.RoleSet
|
|
expectedUsers []string
|
|
expectedGroups []string
|
|
}{
|
|
{
|
|
name: "env dev user and group is added",
|
|
cluster: makeTestKubeCluster(t, map[string]string{
|
|
"env": "dev",
|
|
}),
|
|
roleSet: services.NewRoleSet(devEnvRole),
|
|
expectedUsers: []string{"devuser"},
|
|
expectedGroups: []string{"devgroup"},
|
|
},
|
|
{
|
|
name: "env prod user and group is added",
|
|
cluster: makeTestKubeCluster(t, map[string]string{
|
|
"env": "prod",
|
|
}),
|
|
roleSet: services.NewRoleSet(prodEnvRole),
|
|
expectedUsers: []string{"produser"},
|
|
expectedGroups: []string{"prodgroup"},
|
|
},
|
|
{
|
|
name: "only the correct prod is added",
|
|
cluster: makeTestKubeCluster(t, map[string]string{
|
|
"env": "prod",
|
|
}),
|
|
roleSet: services.NewRoleSet(prodEnvRole, devEnvRole),
|
|
expectedUsers: []string{"produser"},
|
|
expectedGroups: []string{"prodgroup"},
|
|
},
|
|
{
|
|
name: "users and groups from role not authorized are denied",
|
|
cluster: makeTestKubeCluster(t, map[string]string{
|
|
"env": "staging",
|
|
}),
|
|
roleSet: services.NewRoleSet(devEnvRole, prodEnvRole),
|
|
expectedUsers: nil,
|
|
expectedGroups: nil,
|
|
},
|
|
{
|
|
name: "role with wildcard gets group and user",
|
|
cluster: makeTestKubeCluster(t, map[string]string{
|
|
"env": "prod",
|
|
}),
|
|
roleSet: services.NewRoleSet(anyEnvRole),
|
|
expectedUsers: []string{"anyenvrole"},
|
|
expectedGroups: []string{"anyenvgroup"},
|
|
},
|
|
{
|
|
name: "can return multiple users and groups",
|
|
cluster: makeTestKubeCluster(t, map[string]string{
|
|
"env": "prod",
|
|
}),
|
|
roleSet: services.NewRoleSet(anyEnvRole, prodEnvRole),
|
|
expectedUsers: []string{"anyenvrole", "produser"},
|
|
expectedGroups: []string{"anyenvgroup", "prodgroup"},
|
|
},
|
|
{
|
|
name: "can return multiple users and groups from same role",
|
|
cluster: makeTestKubeCluster(t, map[string]string{
|
|
"env": "prod",
|
|
}),
|
|
roleSet: services.NewRoleSet(&types.RoleV6{
|
|
Spec: types.RoleSpecV6{
|
|
Allow: types.RoleConditions{
|
|
KubeUsers: []string{"role1", "role2", "role3"},
|
|
Namespaces: []string{apidefaults.Namespace},
|
|
KubernetesLabels: types.Labels{
|
|
"env": []string{"*"},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
expectedUsers: []string{"role1", "role2", "role3"},
|
|
},
|
|
{
|
|
name: "works with full access",
|
|
cluster: makeTestKubeCluster(t, map[string]string{
|
|
"env": "prod",
|
|
}),
|
|
roleSet: services.NewRoleSet(rootUser),
|
|
expectedUsers: []string{"root"},
|
|
expectedGroups: []string{"rootgroup"},
|
|
},
|
|
{
|
|
name: "works with server with multiple labels",
|
|
cluster: makeTestKubeCluster(t, map[string]string{
|
|
"env": "prod",
|
|
"region": "us-east-1",
|
|
}),
|
|
roleSet: services.NewRoleSet(prodEnvRole),
|
|
expectedUsers: []string{"produser"},
|
|
expectedGroups: []string{"prodgroup"},
|
|
},
|
|
{
|
|
name: "don't add login from unrelated labels",
|
|
cluster: makeTestKubeCluster(t, map[string]string{
|
|
"env": "dev",
|
|
}),
|
|
roleSet: services.NewRoleSet(&types.RoleV6{
|
|
Spec: types.RoleSpecV6{
|
|
Allow: types.RoleConditions{
|
|
KubeGroups: []string{"anyregiongroup"},
|
|
Namespaces: []string{apidefaults.Namespace},
|
|
KubernetesLabels: types.Labels{
|
|
"region": []string{"*"},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
expectedUsers: nil,
|
|
expectedGroups: nil,
|
|
},
|
|
{
|
|
name: "works with roles with multiple labels that role shouldn't access",
|
|
cluster: makeTestKubeCluster(t, map[string]string{
|
|
"env": "dev",
|
|
}),
|
|
roleSet: services.NewRoleSet(roleWithMultipleLabels),
|
|
expectedUsers: nil,
|
|
expectedGroups: nil,
|
|
},
|
|
{
|
|
name: "works with roles with multiple labels that role shouldn't access",
|
|
cluster: makeTestKubeCluster(t, map[string]string{
|
|
"env": "dev",
|
|
"region": "us-west-1",
|
|
}),
|
|
roleSet: services.NewRoleSet(roleWithMultipleLabels),
|
|
expectedUsers: []string{"multiplelabelsuser"},
|
|
expectedGroups: []string{"multiplelabelsgroup"},
|
|
},
|
|
{
|
|
name: "works with roles with regular expressions",
|
|
cluster: makeTestKubeCluster(t, map[string]string{
|
|
"region": "us-west-1",
|
|
}),
|
|
roleSet: services.NewRoleSet(&types.RoleV6{
|
|
Spec: types.RoleSpecV6{
|
|
Allow: types.RoleConditions{
|
|
KubeUsers: []string{"rolewithregexpuser"},
|
|
Namespaces: []string{apidefaults.Namespace},
|
|
KubernetesLabels: types.Labels{
|
|
"region": []string{"^us-west-1|eu-central-1$"},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
expectedUsers: []string{"rolewithregexpuser"},
|
|
},
|
|
{
|
|
name: "works with denied roles",
|
|
cluster: makeTestKubeCluster(t, map[string]string{
|
|
"env": "dev",
|
|
}),
|
|
roleSet: services.NewRoleSet(devEnvRole, &types.RoleV6{
|
|
Spec: types.RoleSpecV6{
|
|
Deny: types.RoleConditions{
|
|
KubeUsers: []string{"devuser"},
|
|
KubeGroups: []string{"devgroup"},
|
|
Namespaces: []string{apidefaults.Namespace},
|
|
KubernetesLabels: types.Labels{
|
|
"env": []string{"*"},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
expectedUsers: nil,
|
|
},
|
|
{
|
|
name: "works with denied roles of unrelated labels",
|
|
cluster: makeTestKubeCluster(t, map[string]string{
|
|
"env": "dev",
|
|
}),
|
|
roleSet: services.NewRoleSet(devEnvRole, &types.RoleV6{
|
|
Spec: types.RoleSpecV6{
|
|
Deny: types.RoleConditions{
|
|
KubeUsers: []string{"devuser"},
|
|
KubeGroups: []string{"devgroup"},
|
|
Namespaces: []string{apidefaults.Namespace},
|
|
KubernetesLabels: types.Labels{
|
|
"region": []string{"*"},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
expectedUsers: nil,
|
|
},
|
|
}
|
|
for _, tc := range tt[:1] {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
users, groups := getAllowedKubeUsersAndGroupsForCluster(tc.roleSet, tc.cluster)
|
|
require.Equal(t, tc.expectedUsers, users)
|
|
require.Equal(t, tc.expectedGroups, groups)
|
|
})
|
|
}
|
|
}
|
|
|
|
// makeTestKubeCluster creates a kube cluster with labels and an empty spec.
|
|
func makeTestKubeCluster(t *testing.T, labels map[string]string) types.KubeCluster {
|
|
s, err := types.NewKubernetesClusterV3(
|
|
types.Metadata{
|
|
Name: "kube_cluster",
|
|
Labels: labels,
|
|
},
|
|
types.KubernetesClusterSpecV3{},
|
|
)
|
|
require.NoError(t, err)
|
|
return s
|
|
}
|
|
|
|
func TestMakeClusterHiddenLabels(t *testing.T) {
|
|
type testCase struct {
|
|
name string
|
|
clusters []types.KubeCluster
|
|
expectedLabels [][]Label
|
|
roleSet services.RoleSet
|
|
}
|
|
|
|
testCases := []testCase{
|
|
{
|
|
name: "Single server with internal label",
|
|
clusters: []types.KubeCluster{
|
|
makeTestKubeCluster(t, map[string]string{
|
|
"teleport.internal/test": "value1",
|
|
"label2": "value2",
|
|
}),
|
|
},
|
|
expectedLabels: [][]Label{
|
|
{
|
|
{
|
|
Name: "label2",
|
|
Value: "value2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
clusters := MakeKubeClusters(tc.clusters, tc.roleSet)
|
|
for i, cluster := range clusters {
|
|
require.Equal(t, tc.expectedLabels[i], cluster.Labels)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMakeServersHiddenLabels(t *testing.T) {
|
|
type testCase struct {
|
|
name string
|
|
clusterName string
|
|
servers []types.Server
|
|
expectedLabels [][]Label
|
|
roleSet services.RoleSet
|
|
}
|
|
|
|
testCases := []testCase{
|
|
{
|
|
name: "Single server with internal label",
|
|
clusterName: "cluster1",
|
|
servers: []types.Server{
|
|
makeTestServer(t, "server1", map[string]string{
|
|
"simple": "value1",
|
|
"teleport.internal/app": "app1",
|
|
}),
|
|
},
|
|
expectedLabels: [][]Label{
|
|
{
|
|
{
|
|
Name: "simple",
|
|
Value: "value1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
servers, err := MakeServers(tc.clusterName, tc.servers, tc.roleSet)
|
|
require.NoError(t, err)
|
|
for i, server := range servers {
|
|
require.Equal(t, tc.expectedLabels[i], server.Labels)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func makeTestServer(t *testing.T, name string, labels map[string]string) types.Server {
|
|
server, err := types.NewServerWithLabels(name, types.KindNode, types.ServerSpecV2{}, labels)
|
|
require.NoError(t, err)
|
|
return server
|
|
}
|
|
|
|
func TestMakeDatabaseHiddenLabels(t *testing.T) {
|
|
inputDb := &types.DatabaseV3{
|
|
Metadata: types.Metadata{
|
|
Name: "db name",
|
|
Labels: map[string]string{
|
|
"label": "value1",
|
|
"teleport.internal/label2": "value2",
|
|
},
|
|
},
|
|
}
|
|
|
|
outputDb := MakeDatabase(inputDb, nil, nil)
|
|
|
|
require.Equal(t, []Label{
|
|
{
|
|
Name: "label",
|
|
Value: "value1",
|
|
},
|
|
}, outputDb.Labels)
|
|
}
|
|
|
|
func TestMakeDesktopHiddenLabel(t *testing.T) {
|
|
windowsDesktop, err := types.NewWindowsDesktopV3(
|
|
"test",
|
|
map[string]string{
|
|
"teleport.internal/t2": "tt",
|
|
"label3": "value2",
|
|
},
|
|
types.WindowsDesktopSpecV3{Addr: "addr"},
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
desktop, err := MakeDesktop(windowsDesktop, services.NewRoleSet())
|
|
require.NoError(t, err)
|
|
labels := []Label{
|
|
{
|
|
Name: "label3",
|
|
Value: "value2",
|
|
},
|
|
}
|
|
|
|
require.Equal(t, labels, desktop.Labels)
|
|
}
|
|
|
|
func TestMakeDesktopServiceHiddenLabel(t *testing.T) {
|
|
windowsDesktopService := &types.WindowsDesktopServiceV3{
|
|
ResourceHeader: types.ResourceHeader{
|
|
Metadata: types.Metadata{
|
|
Labels: map[string]string{
|
|
"teleport.internal/t2": "tt",
|
|
"label3": "value2",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
desktopService := MakeDesktopService(windowsDesktopService)
|
|
labels := []Label{
|
|
{
|
|
Name: "label3",
|
|
Value: "value2",
|
|
},
|
|
}
|
|
|
|
require.Equal(t, labels, desktopService.Labels)
|
|
}
|