Files
teleport/lib/modules/modules_test.go
T
Nick Marais 194536acb1 feat(beams): Entitlement and BeamsUI mode (#65083)
* Register beams entitlement and UI mode

# Conflicts:
#	api/client/proto/authservice.pb.go

* Register in web UI

* Revert moving t.Cleanup

* Prevent BeamsUI being enabled without the Beams entitlement

* Add string length safety

* Reuse cluster for tests
2026-04-09 15:45:53 +00:00

311 lines
12 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 modules_test
import (
"context"
"math"
"os"
"testing"
"github.com/stretchr/testify/require"
"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/entitlements"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/auth/authtest"
"github.com/gravitational/teleport/lib/modules"
"github.com/gravitational/teleport/lib/modules/modulestest"
)
func TestMain(m *testing.M) {
modules.SetInsecureTestMode(true)
os.Exit(m.Run())
}
func TestOSSModules(t *testing.T) {
require.False(t, modules.GetModules().IsBoringBinary())
require.Equal(t, modules.BuildOSS, modules.GetModules().BuildType())
}
func TestValidateAuthPreferenceOnCloud(t *testing.T) {
ctx := context.Background()
testServer, err := authtest.NewAuthServer(authtest.AuthServerConfig{
Dir: t.TempDir(),
})
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, testServer.Close()) })
modulestest.SetTestModules(t, modulestest.Modules{
TestBuildType: modules.BuildEnterprise,
TestFeatures: modules.Features{
Cloud: true,
},
})
s, err := authtest.NewTestTLSServer(authtest.TLSServerConfig{
APIConfig: &auth.APIConfig{
AuthServer: testServer.AuthServer,
Authorizer: testServer.Authorizer,
ScopedAuthorizer: testServer.ScopedAuthorizer,
AuditLog: testServer.AuditLog,
Emitter: testServer.AuditLog,
},
AuthServer: testServer,
})
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, s.Close()) })
clt, err := s.NewClient(authtest.TestAdmin())
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, clt.Close()) })
ap := types.DefaultAuthPreference()
ap.SetSignatureAlgorithmSuite(types.SignatureAlgorithmSuite_SIGNATURE_ALGORITHM_SUITE_LEGACY)
authPref, err := clt.UpsertAuthPreference(ctx, ap)
require.NoError(t, err)
authPref.SetSecondFactor(constants.SecondFactorOff)
_, err = clt.UpdateAuthPreference(ctx, authPref)
require.ErrorContains(t, err, "rpc error: code = Unknown desc = cannot disable multi-factor authentication")
// Validate the storage layer doesn't enforce module validation.
authPref, err = testServer.AuthServer.UpdateAuthPreference(ctx, authPref)
require.NoError(t, err)
require.False(t, authPref.IsSecondFactorEnabled())
authPref, err = testServer.AuthServer.UpsertAuthPreference(ctx, authPref)
require.NoError(t, err)
require.False(t, authPref.IsSecondFactorEnabled())
}
func TestValidateSessionRecordingConfigOnCloud(t *testing.T) {
ctx := context.Background()
testServer, err := authtest.NewAuthServer(authtest.AuthServerConfig{
Dir: t.TempDir(),
})
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, testServer.Close()) })
modulestest.SetTestModules(t, modulestest.Modules{
TestBuildType: modules.BuildEnterprise,
TestFeatures: modules.Features{
Cloud: true,
},
})
recConfig := types.DefaultSessionRecordingConfig()
_, err = testServer.AuthServer.UpsertSessionRecordingConfig(ctx, recConfig)
require.NoError(t, err)
recConfig.SetMode(types.RecordAtProxy)
_, err = testServer.AuthServer.UpsertSessionRecordingConfig(ctx, recConfig)
require.EqualError(t, err, "cannot set proxy recording mode on Cloud")
}
func TestFeatures_ToProto(t *testing.T) {
expected := &proto.Features{
CustomTheme: "dark",
ProductType: 1,
SupportType: 1,
AccessControls: true,
AccessGraph: true,
AdvancedAccessWorkflows: true,
AutomaticUpgrades: true,
Cloud: true,
IsStripeManaged: true,
IsUsageBased: true,
Plugins: true,
Questionnaire: true,
RecoveryCodes: true,
AccessMonitoringConfigured: false,
Entitlements: map[string]*proto.EntitlementInfo{
string(entitlements.AccessGraph): {Enabled: true},
string(entitlements.AccessGraphDemoMode): {Enabled: true},
string(entitlements.AccessLists): {Enabled: true, Limit: 111},
string(entitlements.AccessMonitoring): {Enabled: true, Limit: 2113},
string(entitlements.AccessRequests): {Enabled: true, Limit: 39},
string(entitlements.App): {Enabled: false},
string(entitlements.Beams): {Enabled: true},
string(entitlements.CloudAuditLogRetention): {Enabled: true},
string(entitlements.ClientIPRestrictions): {Enabled: true},
string(entitlements.DB): {Enabled: true},
string(entitlements.Desktop): {Enabled: true},
string(entitlements.DeviceTrust): {Enabled: true, Limit: 103},
string(entitlements.ExternalAuditStorage): {Enabled: true},
string(entitlements.FeatureHiding): {Enabled: true},
string(entitlements.HSM): {Enabled: true},
string(entitlements.Identity): {Enabled: true},
string(entitlements.JoinActiveSessions): {Enabled: true},
string(entitlements.K8s): {Enabled: true},
string(entitlements.LicenseAutoUpdate): {Enabled: true},
string(entitlements.MobileDeviceManagement): {Enabled: true},
string(entitlements.OIDC): {Enabled: true},
string(entitlements.OktaSCIM): {Enabled: true},
string(entitlements.OktaUserSync): {Enabled: true},
string(entitlements.Policy): {Enabled: true},
string(entitlements.SAML): {Enabled: true},
string(entitlements.SessionLocks): {Enabled: true},
string(entitlements.UpsellAlert): {Enabled: true},
string(entitlements.UsageReporting): {Enabled: true},
string(entitlements.UnrestrictedManagedUpdates): {Enabled: true},
string(entitlements.WorkloadClusters): {Enabled: true},
},
// Deprecated fields
// TODO(michellescripts) DELETE IN v21.0.0
Policy: &proto.PolicyFeature{
Enabled: true,
},
AccessGraphDemoMode: true,
ClientIPRestrictions: true,
BeamsUI: true,
}
f := modules.Features{
CustomTheme: "dark",
ProductType: 1,
SupportType: 1,
AccessControls: true,
AccessGraph: true,
AdvancedAccessWorkflows: true,
Assist: true,
AutomaticUpgrades: true,
Cloud: true,
IsStripeManaged: true,
IsUsageBasedBilling: true,
Plugins: true,
Questionnaire: true,
RecoveryCodes: true,
AccessMonitoringConfigured: false,
CloudAnonymizationKey: []byte("001"),
BeamsUI: true,
Entitlements: map[entitlements.EntitlementKind]modules.EntitlementInfo{
entitlements.AccessGraph: {Enabled: true, Limit: 0},
entitlements.AccessGraphDemoMode: {Enabled: true, Limit: 0},
entitlements.AccessLists: {Enabled: true, Limit: 111},
entitlements.AccessMonitoring: {Enabled: true, Limit: 2113},
entitlements.AccessRequests: {Enabled: true, Limit: 39},
entitlements.App: {Enabled: false, Limit: 0},
entitlements.Beams: {Enabled: true, Limit: 0},
entitlements.ClientIPRestrictions: {Enabled: true, Limit: 0},
entitlements.CloudAuditLogRetention: {Enabled: true, Limit: 0},
entitlements.DB: {Enabled: true, Limit: 0},
entitlements.Desktop: {Enabled: true, Limit: 0},
entitlements.DeviceTrust: {Enabled: true, Limit: 103},
entitlements.ExternalAuditStorage: {Enabled: true, Limit: 0},
entitlements.FeatureHiding: {Enabled: true, Limit: 0},
entitlements.HSM: {Enabled: true, Limit: 0},
entitlements.Identity: {Enabled: true, Limit: 0},
entitlements.JoinActiveSessions: {Enabled: true, Limit: 0},
entitlements.K8s: {Enabled: true, Limit: 0},
entitlements.LicenseAutoUpdate: {Enabled: true, Limit: 0},
entitlements.MobileDeviceManagement: {Enabled: true, Limit: 0},
entitlements.OIDC: {Enabled: true, Limit: 0},
entitlements.OktaSCIM: {Enabled: true, Limit: 0},
entitlements.OktaUserSync: {Enabled: true, Limit: 0},
entitlements.Policy: {Enabled: true, Limit: 0},
entitlements.SAML: {Enabled: true, Limit: 0},
entitlements.SessionLocks: {Enabled: true, Limit: 0},
entitlements.UpsellAlert: {Enabled: true, Limit: 0},
entitlements.UsageReporting: {Enabled: true, Limit: 0},
entitlements.UnrestrictedManagedUpdates: {Enabled: true, Limit: 0},
entitlements.WorkloadClusters: {Enabled: true, Limit: 0},
},
}
actual := f.ToProto()
require.Equal(t, expected, actual)
}
func TestFeatures_GetEntitlement(t *testing.T) {
f := modules.Features{
Entitlements: map[entitlements.EntitlementKind]modules.EntitlementInfo{
entitlements.AccessLists: {Enabled: true, Limit: 111},
entitlements.K8s: {Enabled: false},
entitlements.SAML: {},
},
}
actual := f.GetEntitlement(entitlements.AccessLists)
require.Equal(t, modules.EntitlementInfo{Enabled: true, Limit: 111}, actual)
actual = f.GetEntitlement(entitlements.K8s)
require.Equal(t, modules.EntitlementInfo{Enabled: false}, actual)
actual = f.GetEntitlement(entitlements.SAML)
require.Equal(t, modules.EntitlementInfo{}, actual)
actual = f.GetEntitlement(entitlements.UsageReporting)
require.Equal(t, modules.EntitlementInfo{}, actual)
}
func TestEntitlementInfo_UnderLimit(t *testing.T) {
testCases := []struct {
name string
count int
uut modules.EntitlementInfo
assert require.BoolAssertionFunc
}{
{
name: "disabled is always out of limit",
count: 10,
uut: modules.EntitlementInfo{Enabled: false, Limit: 10000},
assert: require.False,
},
{
name: "within limits returns true",
count: 10,
uut: modules.EntitlementInfo{Enabled: true, Limit: 10000},
assert: require.True,
},
{
name: "at limits returns false",
count: 10000,
uut: modules.EntitlementInfo{Enabled: true, Limit: 10000},
assert: require.False,
},
{
name: "above limits returns false",
count: 10001,
uut: modules.EntitlementInfo{Enabled: true, Limit: 10000},
assert: require.False,
},
{
name: "zero limit implies max",
count: math.MaxInt64,
uut: modules.EntitlementInfo{Enabled: true, Limit: 0},
assert: require.True,
},
{
name: "handles above MaxInt32",
count: math.MaxInt32 + 1,
uut: modules.EntitlementInfo{Enabled: true, Limit: math.MaxInt32},
assert: require.False,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
tc.assert(t, tc.uut.UnderLimit(tc.count))
})
}
}