mirror of
https://github.com/gravitational/teleport.git
synced 2026-09-19 11:00:37 +08:00
Remove deprecated features and entitlement backfills (#59618)
This commit is contained in:
+1031
-1864
File diff suppressed because it is too large
Load Diff
@@ -87,54 +87,13 @@ type WebConfig struct {
|
||||
SessionSummarizerEnabled bool `json:"sessionSummarizerEnabled,omitempty"`
|
||||
// entitlements define a customer’s access to a specific features
|
||||
Entitlements map[string]EntitlementInfo `json:"entitlements,omitempty"`
|
||||
|
||||
// Deprecated Fields
|
||||
// Deprecated: IsTeam is true if [Features.ProductType] = Team
|
||||
// Prefer checking the cluster features over this flag, as this will be removed.
|
||||
IsTeam bool `json:"isTeam"`
|
||||
// HideInaccessibleFeatures is true when features should be undiscoverable to users without the necessary permissions.
|
||||
// Usually, in order to encourage discoverability of features, we show UI elements even if the user doesn't have permission to access them,
|
||||
// this flag disables that behavior.
|
||||
// Deprecated, use entitlements
|
||||
HideInaccessibleFeatures bool `json:"hideInaccessibleFeatures"`
|
||||
// IsIGSEnabled is true if [Features.IdentityGovernance] = true
|
||||
// Deprecated, use entitlements
|
||||
IsIGSEnabled bool `json:"isIgsEnabled"`
|
||||
// IsPolicyEnabled is true if [Features.Policy] = true
|
||||
// Deprecated, use entitlements
|
||||
IsPolicyEnabled bool `json:"isPolicyEnabled"`
|
||||
// TODO (avatus) delete in v18
|
||||
// IsPolicyRoleVisualizerEnabled is the graph visualizer for diffs made
|
||||
// when editing roles in the Web UI. This defaults to true, but has an environment
|
||||
// variable to turn off if needed TELEPORT_UNSTABLE_DISABLE_ROLE_VISUALIZER=true
|
||||
IsPolicyRoleVisualizerEnabled bool `json:"isPolicyRoleVisualizerEnabled"`
|
||||
// featureLimits define limits for features.
|
||||
// Typically used with feature teasers if feature is not enabled for the
|
||||
// product type eg: Team product contains teasers to upgrade to Enterprise.
|
||||
// Deprecated, use entitlements
|
||||
FeatureLimits FeatureLimits `json:"featureLimits"`
|
||||
// ExternalAuditStorage indicates whether the EAS feature is enabled in the cluster.
|
||||
// Deprecated, use entitlements
|
||||
ExternalAuditStorage bool `json:"externalAuditStorage"`
|
||||
// JoinActiveSessions indicates whether joining active sessions via web UI is enabled
|
||||
// Deprecated, use entitlements
|
||||
JoinActiveSessions bool `json:"joinActiveSessions"`
|
||||
// AccessRequests indicates whether access requests are enabled
|
||||
// Deprecated, use entitlements
|
||||
AccessRequests bool `json:"accessRequests"`
|
||||
// TrustedDevices indicates whether trusted devices page is enabled
|
||||
// Deprecated, use entitlements
|
||||
TrustedDevices bool `json:"trustedDevices"`
|
||||
// OIDC indicates whether the OIDC integration flow is enabled
|
||||
// Deprecated, use entitlements
|
||||
OIDC bool `json:"oidc"`
|
||||
// SAML indicates whether the SAML integration flow is enabled
|
||||
// Deprecated, use entitlements
|
||||
SAML bool `json:"saml"`
|
||||
// MobileDeviceManagement indicates whether adding Jamf plugin is enabled
|
||||
// Deprecated, use entitlements
|
||||
MobileDeviceManagement bool `json:"mobileDeviceManagement"`
|
||||
// IdentitySecurity contains identity security features and settings.
|
||||
// The individual identity security entitlements should be read from the Entitlements field.
|
||||
IdentitySecurity IdentitySecurity `json:"identitySecurity"`
|
||||
}
|
||||
|
||||
@@ -161,21 +120,6 @@ type EntitlementInfo struct {
|
||||
Limit int32 `json:"limit"`
|
||||
}
|
||||
|
||||
// featureLimits define limits for features.
|
||||
// Typically used with feature teasers if feature is not enabled for the
|
||||
// product type eg: Team product contains teasers to upgrade to Enterprise.
|
||||
type FeatureLimits struct {
|
||||
// Limit for the number of access list creatable when feature is
|
||||
// not enabled.
|
||||
AccessListCreateLimit int `json:"accessListCreateLimit"`
|
||||
// Defines the max number of days to include in an access report if
|
||||
// feature is not enabled.
|
||||
AccessMonitoringMaxReportRangeLimit int `json:"accessMonitoringMaxReportRangeLimit"`
|
||||
// AccessRequestMonthlyRequestLimit is the usage-based limit for the number of
|
||||
// access requests created in a calendar month.
|
||||
AccessRequestMonthlyRequestLimit int `json:"AccessRequestMonthlyRequestLimit"`
|
||||
}
|
||||
|
||||
// UIConfig provides config options for the web UI served by the proxy service.
|
||||
type UIConfig struct {
|
||||
// ScrollbackLines is the max number of lines the UI terminal can display in its history
|
||||
|
||||
@@ -541,33 +541,16 @@ enum SupportType {
|
||||
|
||||
// Features are auth server features.
|
||||
message Features {
|
||||
// Kubernetes enables Kubernetes Access product
|
||||
// Deprecated remove in v18; leverage entitlements
|
||||
bool Kubernetes = 1 [(gogoproto.jsontag) = "kubernetes"];
|
||||
// App enables Application Access product
|
||||
// Deprecated remove in v18; leverage entitlements
|
||||
bool App = 2 [(gogoproto.jsontag) = "app"];
|
||||
// DB enables database access product
|
||||
// Deprecated remove in v18; leverage entitlements
|
||||
bool DB = 3 [(gogoproto.jsontag) = "db"];
|
||||
// OIDC enables OIDC connectors
|
||||
// Deprecated remove in v18; leverage entitlements
|
||||
bool OIDC = 4 [(gogoproto.jsontag) = "oidc"];
|
||||
// SAML enables SAML connectors
|
||||
// Deprecated remove in v18; leverage entitlements
|
||||
bool SAML = 5 [(gogoproto.jsontag) = "saml"];
|
||||
// Deprecated features, use entitlements
|
||||
reserved 1, 2, 3, 4, 5, 9, 10, 18, 19, 20, 21, 23, 25, 26, 31, 33, 34;
|
||||
reserved "Kubernetes", "App", "DB", "OIDC", "SAML", "HSM", "Desktop", "Assist", "DeviceTrust", "FeatureHiding", "AccessRequests", "IdentityGovernance", "AccessList", "AccessMonitoring", "ExternalAuditStorage", "JoinActiveSessions", "MobileDeviceManagement";
|
||||
|
||||
// AccessControls enables FIPS access controls
|
||||
bool AccessControls = 6 [(gogoproto.jsontag) = "access_controls"];
|
||||
// AdvancedAccessWorkflows is currently set to the value of the Cloud AccessRequests entitlement
|
||||
bool AdvancedAccessWorkflows = 7 [(gogoproto.jsontag) = "advanced_access_workflows"];
|
||||
// Cloud enables some cloud-related features
|
||||
bool Cloud = 8 [(gogoproto.jsontag) = "cloud"];
|
||||
// HSM enables PKCS#11 HSM support
|
||||
// Deprecated remove in v18; leverage entitlements
|
||||
bool HSM = 9 [(gogoproto.jsontag) = "hsm"];
|
||||
// Desktop enables desktop access product
|
||||
// Deprecated remove in v18; leverage entitlements
|
||||
bool Desktop = 10 [(gogoproto.jsontag) = "desktop"];
|
||||
reserved 11; // bool ModeratedSessions
|
||||
reserved 12; // bool MachineID
|
||||
reserved 13; // bool ResourceAccessRequests
|
||||
@@ -579,57 +562,27 @@ message Features {
|
||||
bool AutomaticUpgrades = 16 [(gogoproto.jsontag) = "automatic_upgrades"];
|
||||
// IsUsageBased enables some usage-based billing features
|
||||
bool IsUsageBased = 17 [(gogoproto.jsontag) = "is_usage_based"];
|
||||
// Assist enables the Assistant feature
|
||||
// Deprecated remove in v18; leverage entitlements
|
||||
bool Assist = 18 [(gogoproto.jsontag) = "assist"];
|
||||
// DeviceTrust holds its namesake feature settings.
|
||||
// Deprecated remove in v18; leverage entitlements
|
||||
DeviceTrustFeature DeviceTrust = 19 [(gogoproto.jsontag) = "device_trust,omitempty"];
|
||||
// FeatureHiding enables hiding features from being discoverable for users who don't have the necessary permissions.
|
||||
// Deprecated remove in v18; leverage entitlements
|
||||
bool FeatureHiding = 20 [(gogoproto.jsontag) = "feature_hiding,omitempty"];
|
||||
// AccessRequests holds its namesake feature settings.
|
||||
// Deprecated remove in v18; leverage entitlements
|
||||
AccessRequestsFeature AccessRequests = 21 [(gogoproto.jsontag) = "access_requests,omitempty"];
|
||||
// CustomTheme holds the name of WebUI custom theme.
|
||||
string CustomTheme = 22 [(gogoproto.jsontag) = "custom_theme,omitempty"];
|
||||
// IdentityGovernance indicates whether IGS related features are enabled:
|
||||
// access list, access request, access monitoring, device trust.
|
||||
// Deprecated remove in v18; leverage entitlements
|
||||
bool IdentityGovernance = 23 [(gogoproto.jsontag) = "identity_governance,omitempty"];
|
||||
// AccessGraph enables the usage of access graph.
|
||||
// NOTE: this is a legacy flag that is currently used to signal
|
||||
// that Access Graph integration is *enabled* on a cluster.
|
||||
// *Access* to the feature is gated on the `Policy` flag.
|
||||
// *Access* to the feature is gated on the `Policy` entitlement.
|
||||
// TODO(justinas): remove this field once "TAG enabled" status is moved to a resource in the backend.
|
||||
bool AccessGraph = 24 [(gogoproto.jsontag) = "access_graph,omitempty"];
|
||||
// AccessListFeature holds its namesake feature settings.
|
||||
// Deprecated remove in v18; leverage entitlements
|
||||
AccessListFeature AccessList = 25 [(gogoproto.jsontag) = "access_list,omitempty"];
|
||||
// AccessMonitoringFeature holds its namesake feature settings.
|
||||
// Deprecated remove in v18; leverage entitlements for access and AccessMonitoringConfigured for enabled
|
||||
AccessMonitoringFeature AccessMonitoring = 26 [(gogoproto.jsontag) = "access_monitoring,omitempty"];
|
||||
// ProductType describes the product being used.
|
||||
ProductType ProductType = 27 [(gogoproto.jsontag) = "product_type,omitempty"];
|
||||
// Policy enables the Teleport Policy feature set.
|
||||
// At the time of writing, this includes Teleport Access Graph (TAG).
|
||||
// Deprecated remove in v18; leverage entitlements
|
||||
// TODO(michellescripts) DELETE IN v21.0.0
|
||||
// Deprecated, use entitlements
|
||||
PolicyFeature Policy = 28 [(gogoproto.jsontag) = "policy,omitempty"];
|
||||
// Questionnaire indicates whether cluster users should get an onboarding questionnaire
|
||||
bool Questionnaire = 29 [(gogoproto.jsontag) = "questionnaire,omitempty"];
|
||||
// IsStripeManaged indicates if the cluster billing is managed via Stripe
|
||||
bool IsStripeManaged = 30 [(gogoproto.jsontag) = "is_stripe_managed,omitempty"];
|
||||
// ExternalAuditStorage indicates whether the EAS feature is enabled in the cluster.
|
||||
// Deprecated remove in v18; leverage entitlements
|
||||
bool ExternalAuditStorage = 31 [(gogoproto.jsontag) = "external_audit_storage,omitempty"];
|
||||
// SupportType indicates the type of the customer's support
|
||||
SupportType SupportType = 32 [(gogoproto.jsontag) = "support_type,omitempty"];
|
||||
// JoinActiveSessions indicates whether joining active sessions via web UI is enabled
|
||||
// Deprecated remove in v18; leverage entitlements
|
||||
bool JoinActiveSessions = 33 [(gogoproto.jsontag) = "join_active_sessions,omitempty"];
|
||||
// MobileDeviceManagement indicates whether endpoint management (like Jamf Plugin) can be used in the cluster
|
||||
// Deprecated remove in v18; leverage entitlements
|
||||
bool MobileDeviceManagement = 34 [(gogoproto.jsontag) = "mobile_device_management,omitempty"];
|
||||
// entitlements define a customer’s access to a specific features
|
||||
map<string, EntitlementInfo> entitlements = 35;
|
||||
// AccessMonitoringConfigured contributes to the enablement of access monitoring.
|
||||
@@ -639,8 +592,12 @@ message Features {
|
||||
reserved 37;
|
||||
reserved "CloudAnonymizationKey";
|
||||
// AccessGraphDemoMode enables the ability to opt-in to a demo mode of Access Graph with limited features.
|
||||
// TODO(michellescripts) DELETE IN v21.0.0
|
||||
// Deprecated, use entitlements
|
||||
bool AccessGraphDemoMode = 38 [(gogoproto.jsontag) = "access_graph_demo_mode,omitempty"];
|
||||
// ClientIPRestrictions allows Cloud users to setup a client IP allowlist
|
||||
// TODO(michellescripts) DELETE IN v21.0.0
|
||||
// Deprecated, use entitlements
|
||||
bool ClientIPRestrictions = 39 [(gogoproto.jsontag) = "client_ip_restrictions,omitempty"];
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package entitlements
|
||||
|
||||
import "github.com/gravitational/teleport/api/client/proto"
|
||||
|
||||
type EntitlementKind string
|
||||
|
||||
// The EntitlementKind list should be 1:1 with the Features & FeatureStrings in salescenter/product/product.go,
|
||||
@@ -64,68 +62,3 @@ var AllEntitlements = []EntitlementKind{
|
||||
OktaUserSync, Policy, SAML, SessionLocks, UnrestrictedManagedUpdates, UpsellAlert, UsageReporting, LicenseAutoUpdate, AccessGraphDemoMode,
|
||||
ClientIPRestrictions, WorkloadClusters,
|
||||
}
|
||||
|
||||
// BackfillFeatures ensures entitlements are backwards compatible.
|
||||
// If Entitlements are present, there are no changes.
|
||||
// If Entitlements are not present, it sets the entitlements based on legacy field values.
|
||||
// TODO(michellescripts) DELETE IN 18.0.0
|
||||
func BackfillFeatures(features *proto.Features) {
|
||||
if len(features.Entitlements) > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
features.Entitlements = getBaseEntitlements(features.GetEntitlements())
|
||||
|
||||
// Entitlements: All records are {enabled: false}; update to equal legacy feature value
|
||||
features.Entitlements[string(ExternalAuditStorage)] = &proto.EntitlementInfo{Enabled: features.GetExternalAuditStorage()}
|
||||
features.Entitlements[string(FeatureHiding)] = &proto.EntitlementInfo{Enabled: features.GetFeatureHiding()}
|
||||
features.Entitlements[string(Identity)] = &proto.EntitlementInfo{Enabled: features.GetIdentityGovernance()}
|
||||
features.Entitlements[string(JoinActiveSessions)] = &proto.EntitlementInfo{Enabled: features.GetJoinActiveSessions()}
|
||||
features.Entitlements[string(MobileDeviceManagement)] = &proto.EntitlementInfo{Enabled: features.GetMobileDeviceManagement()}
|
||||
features.Entitlements[string(OIDC)] = &proto.EntitlementInfo{Enabled: features.GetOIDC()}
|
||||
features.Entitlements[string(Policy)] = &proto.EntitlementInfo{Enabled: features.GetPolicy().GetEnabled()}
|
||||
features.Entitlements[string(SAML)] = &proto.EntitlementInfo{Enabled: features.GetSAML()}
|
||||
features.Entitlements[string(K8s)] = &proto.EntitlementInfo{Enabled: features.GetKubernetes()}
|
||||
features.Entitlements[string(App)] = &proto.EntitlementInfo{Enabled: features.GetApp()}
|
||||
features.Entitlements[string(DB)] = &proto.EntitlementInfo{Enabled: features.GetDB()}
|
||||
features.Entitlements[string(Desktop)] = &proto.EntitlementInfo{Enabled: features.GetDesktop()}
|
||||
features.Entitlements[string(HSM)] = &proto.EntitlementInfo{Enabled: features.GetHSM()}
|
||||
features.Entitlements[string(AccessGraphDemoMode)] = &proto.EntitlementInfo{Enabled: features.GetAccessGraphDemoMode()}
|
||||
|
||||
// set default Identity fields to legacy feature value
|
||||
features.Entitlements[string(AccessLists)] = &proto.EntitlementInfo{Enabled: true, Limit: features.GetAccessList().GetCreateLimit()}
|
||||
features.Entitlements[string(AccessMonitoring)] = &proto.EntitlementInfo{Enabled: features.GetAccessMonitoring().GetEnabled(), Limit: features.GetAccessMonitoring().GetMaxReportRangeLimit()}
|
||||
features.Entitlements[string(AccessRequests)] = &proto.EntitlementInfo{Enabled: features.GetAccessRequests().GetMonthlyRequestLimit() > 0, Limit: features.GetAccessRequests().GetMonthlyRequestLimit()}
|
||||
features.Entitlements[string(DeviceTrust)] = &proto.EntitlementInfo{Enabled: features.GetDeviceTrust().GetEnabled(), Limit: features.GetDeviceTrust().GetDevicesUsageLimit()}
|
||||
// override Identity Package features if Identity is enabled: set true and clear limit
|
||||
if features.GetIdentityGovernance() {
|
||||
features.Entitlements[string(AccessLists)] = &proto.EntitlementInfo{Enabled: true}
|
||||
features.Entitlements[string(AccessMonitoring)] = &proto.EntitlementInfo{Enabled: true}
|
||||
features.Entitlements[string(AccessRequests)] = &proto.EntitlementInfo{Enabled: true}
|
||||
features.Entitlements[string(DeviceTrust)] = &proto.EntitlementInfo{Enabled: true}
|
||||
features.Entitlements[string(OktaSCIM)] = &proto.EntitlementInfo{Enabled: true}
|
||||
features.Entitlements[string(OktaUserSync)] = &proto.EntitlementInfo{Enabled: true}
|
||||
features.Entitlements[string(SessionLocks)] = &proto.EntitlementInfo{Enabled: true}
|
||||
}
|
||||
}
|
||||
|
||||
// getBaseEntitlements takes a cloud entitlement set and returns a modules Entitlement set
|
||||
func getBaseEntitlements(protoEntitlements map[string]*proto.EntitlementInfo) map[string]*proto.EntitlementInfo {
|
||||
all := AllEntitlements
|
||||
result := make(map[string]*proto.EntitlementInfo, len(all))
|
||||
|
||||
for _, e := range all {
|
||||
al, ok := protoEntitlements[string(e)]
|
||||
if !ok {
|
||||
result[string(e)] = &proto.EntitlementInfo{}
|
||||
continue
|
||||
}
|
||||
|
||||
result[string(e)] = &proto.EntitlementInfo{
|
||||
Enabled: al.Enabled,
|
||||
Limit: al.Limit,
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -1,319 +0,0 @@
|
||||
// Teleport
|
||||
// Copyright (C) 2024 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 entitlements
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
|
||||
"github.com/gravitational/teleport/api/client/proto"
|
||||
apiutils "github.com/gravitational/teleport/api/utils"
|
||||
)
|
||||
|
||||
func TestBackfillFeatures(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
features *proto.Features
|
||||
expected map[string]*proto.EntitlementInfo
|
||||
}{
|
||||
{
|
||||
name: "entitlements present; keeps entitlement values",
|
||||
features: &proto.Features{
|
||||
DeviceTrust: nil,
|
||||
AccessRequests: nil,
|
||||
AccessList: nil,
|
||||
AccessMonitoring: nil,
|
||||
Policy: nil,
|
||||
CustomTheme: "",
|
||||
ProductType: 0,
|
||||
SupportType: 0,
|
||||
Kubernetes: false,
|
||||
App: false,
|
||||
DB: false,
|
||||
OIDC: false,
|
||||
SAML: false,
|
||||
AccessControls: false,
|
||||
AdvancedAccessWorkflows: false,
|
||||
Cloud: false,
|
||||
HSM: false,
|
||||
Desktop: false,
|
||||
RecoveryCodes: false,
|
||||
Plugins: false,
|
||||
AutomaticUpgrades: false,
|
||||
IsUsageBased: false,
|
||||
Assist: false,
|
||||
FeatureHiding: false,
|
||||
IdentityGovernance: false,
|
||||
AccessGraph: false,
|
||||
Questionnaire: false,
|
||||
IsStripeManaged: false,
|
||||
ExternalAuditStorage: false,
|
||||
JoinActiveSessions: false,
|
||||
MobileDeviceManagement: false,
|
||||
AccessMonitoringConfigured: false,
|
||||
Entitlements: map[string]*proto.EntitlementInfo{
|
||||
string(AccessLists): {Enabled: true, Limit: 111},
|
||||
string(AccessMonitoring): {Enabled: true, Limit: 2113},
|
||||
string(AccessRequests): {Enabled: true, Limit: 39},
|
||||
string(App): {Enabled: false},
|
||||
string(CloudAuditLogRetention): {Enabled: true},
|
||||
string(DB): {Enabled: true},
|
||||
string(Desktop): {Enabled: true},
|
||||
string(DeviceTrust): {Enabled: true, Limit: 103},
|
||||
string(ExternalAuditStorage): {Enabled: true},
|
||||
string(FeatureHiding): {Enabled: true},
|
||||
string(HSM): {Enabled: true},
|
||||
string(Identity): {Enabled: true},
|
||||
string(JoinActiveSessions): {Enabled: true},
|
||||
string(K8s): {Enabled: true},
|
||||
string(MobileDeviceManagement): {Enabled: true},
|
||||
string(OIDC): {Enabled: true},
|
||||
string(OktaSCIM): {Enabled: true},
|
||||
string(OktaUserSync): {Enabled: true},
|
||||
string(Policy): {Enabled: true},
|
||||
string(SAML): {Enabled: true},
|
||||
string(SessionLocks): {Enabled: true},
|
||||
string(UpsellAlert): {Enabled: true},
|
||||
string(UsageReporting): {Enabled: true},
|
||||
string(LicenseAutoUpdate): {Enabled: true},
|
||||
string(AccessGraphDemoMode): {Enabled: true},
|
||||
string(UnrestrictedManagedUpdates): {Enabled: true},
|
||||
string(ClientIPRestrictions): {Enabled: true},
|
||||
string(WorkloadClusters): {Enabled: true},
|
||||
},
|
||||
},
|
||||
expected: map[string]*proto.EntitlementInfo{
|
||||
string(AccessLists): {Enabled: true, Limit: 111},
|
||||
string(AccessMonitoring): {Enabled: true, Limit: 2113},
|
||||
string(AccessRequests): {Enabled: true, Limit: 39},
|
||||
string(App): {Enabled: false},
|
||||
string(CloudAuditLogRetention): {Enabled: true},
|
||||
string(DB): {Enabled: true},
|
||||
string(Desktop): {Enabled: true},
|
||||
string(DeviceTrust): {Enabled: true, Limit: 103},
|
||||
string(ExternalAuditStorage): {Enabled: true},
|
||||
string(FeatureHiding): {Enabled: true},
|
||||
string(HSM): {Enabled: true},
|
||||
string(Identity): {Enabled: true},
|
||||
string(JoinActiveSessions): {Enabled: true},
|
||||
string(K8s): {Enabled: true},
|
||||
string(MobileDeviceManagement): {Enabled: true},
|
||||
string(OIDC): {Enabled: true},
|
||||
string(OktaSCIM): {Enabled: true},
|
||||
string(OktaUserSync): {Enabled: true},
|
||||
string(Policy): {Enabled: true},
|
||||
string(SAML): {Enabled: true},
|
||||
string(SessionLocks): {Enabled: true},
|
||||
string(UpsellAlert): {Enabled: true},
|
||||
string(UsageReporting): {Enabled: true},
|
||||
string(LicenseAutoUpdate): {Enabled: true},
|
||||
string(AccessGraphDemoMode): {Enabled: true},
|
||||
string(UnrestrictedManagedUpdates): {Enabled: true},
|
||||
string(ClientIPRestrictions): {Enabled: true},
|
||||
string(WorkloadClusters): {Enabled: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "entitlements not present; identity on - sets legacy fields & drops limits",
|
||||
features: &proto.Features{
|
||||
DeviceTrust: &proto.DeviceTrustFeature{
|
||||
Enabled: true,
|
||||
DevicesUsageLimit: 33,
|
||||
},
|
||||
AccessRequests: &proto.AccessRequestsFeature{
|
||||
MonthlyRequestLimit: 22,
|
||||
},
|
||||
AccessList: &proto.AccessListFeature{
|
||||
CreateLimit: 44,
|
||||
},
|
||||
AccessMonitoring: &proto.AccessMonitoringFeature{
|
||||
Enabled: true,
|
||||
MaxReportRangeLimit: 55,
|
||||
},
|
||||
Policy: &proto.PolicyFeature{
|
||||
Enabled: true,
|
||||
},
|
||||
CustomTheme: "",
|
||||
ProductType: 0,
|
||||
SupportType: 0,
|
||||
Kubernetes: true,
|
||||
App: true,
|
||||
DB: true,
|
||||
OIDC: true,
|
||||
SAML: true,
|
||||
AccessControls: true,
|
||||
AdvancedAccessWorkflows: true,
|
||||
Cloud: true,
|
||||
HSM: true,
|
||||
Desktop: true,
|
||||
RecoveryCodes: true,
|
||||
Plugins: true,
|
||||
AutomaticUpgrades: true,
|
||||
IsUsageBased: true,
|
||||
Assist: true,
|
||||
FeatureHiding: true,
|
||||
IdentityGovernance: true,
|
||||
AccessGraph: true,
|
||||
Questionnaire: true,
|
||||
IsStripeManaged: true,
|
||||
ExternalAuditStorage: true,
|
||||
JoinActiveSessions: true,
|
||||
MobileDeviceManagement: true,
|
||||
AccessMonitoringConfigured: true,
|
||||
},
|
||||
expected: map[string]*proto.EntitlementInfo{
|
||||
string(AccessLists): {Enabled: true},
|
||||
string(AccessMonitoring): {Enabled: true},
|
||||
string(AccessRequests): {Enabled: true},
|
||||
string(App): {Enabled: true},
|
||||
string(DB): {Enabled: true},
|
||||
string(Desktop): {Enabled: true},
|
||||
string(DeviceTrust): {Enabled: true},
|
||||
string(ExternalAuditStorage): {Enabled: true},
|
||||
string(FeatureHiding): {Enabled: true},
|
||||
string(HSM): {Enabled: true},
|
||||
string(Identity): {Enabled: true},
|
||||
string(JoinActiveSessions): {Enabled: true},
|
||||
string(K8s): {Enabled: true},
|
||||
string(MobileDeviceManagement): {Enabled: true},
|
||||
string(OIDC): {Enabled: true},
|
||||
string(OktaSCIM): {Enabled: true},
|
||||
string(OktaUserSync): {Enabled: true},
|
||||
string(Policy): {Enabled: true},
|
||||
string(SAML): {Enabled: true},
|
||||
string(SessionLocks): {Enabled: true},
|
||||
// defaults, no legacy equivalent
|
||||
string(UsageReporting): {Enabled: false},
|
||||
string(UpsellAlert): {Enabled: false},
|
||||
string(CloudAuditLogRetention): {Enabled: false},
|
||||
string(LicenseAutoUpdate): {Enabled: false},
|
||||
string(AccessGraphDemoMode): {Enabled: false},
|
||||
string(UnrestrictedManagedUpdates): {Enabled: false},
|
||||
string(ClientIPRestrictions): {Enabled: false},
|
||||
string(WorkloadClusters): {Enabled: false},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "entitlements not present; identity off - sets legacy fields",
|
||||
features: &proto.Features{
|
||||
DeviceTrust: &proto.DeviceTrustFeature{
|
||||
Enabled: true,
|
||||
DevicesUsageLimit: 33,
|
||||
},
|
||||
AccessRequests: &proto.AccessRequestsFeature{
|
||||
MonthlyRequestLimit: 22,
|
||||
},
|
||||
AccessList: &proto.AccessListFeature{
|
||||
CreateLimit: 44,
|
||||
},
|
||||
AccessMonitoring: &proto.AccessMonitoringFeature{
|
||||
Enabled: true,
|
||||
MaxReportRangeLimit: 55,
|
||||
},
|
||||
Policy: &proto.PolicyFeature{
|
||||
Enabled: true,
|
||||
},
|
||||
CustomTheme: "",
|
||||
ProductType: 0,
|
||||
SupportType: 0,
|
||||
Kubernetes: true,
|
||||
App: true,
|
||||
DB: true,
|
||||
OIDC: true,
|
||||
SAML: true,
|
||||
AccessControls: true,
|
||||
AdvancedAccessWorkflows: true,
|
||||
Cloud: true,
|
||||
HSM: true,
|
||||
Desktop: true,
|
||||
RecoveryCodes: true,
|
||||
Plugins: true,
|
||||
AutomaticUpgrades: true,
|
||||
IsUsageBased: true,
|
||||
Assist: true,
|
||||
FeatureHiding: true,
|
||||
IdentityGovernance: false,
|
||||
AccessGraph: true,
|
||||
Questionnaire: true,
|
||||
IsStripeManaged: true,
|
||||
ExternalAuditStorage: true,
|
||||
JoinActiveSessions: true,
|
||||
MobileDeviceManagement: true,
|
||||
AccessMonitoringConfigured: true,
|
||||
},
|
||||
expected: map[string]*proto.EntitlementInfo{
|
||||
string(AccessLists): {Enabled: true, Limit: 44},
|
||||
string(AccessMonitoring): {Enabled: true, Limit: 55},
|
||||
string(AccessRequests): {Enabled: true, Limit: 22},
|
||||
string(DeviceTrust): {Enabled: true, Limit: 33},
|
||||
string(App): {Enabled: true},
|
||||
string(DB): {Enabled: true},
|
||||
string(Desktop): {Enabled: true},
|
||||
string(ExternalAuditStorage): {Enabled: true},
|
||||
string(FeatureHiding): {Enabled: true},
|
||||
string(HSM): {Enabled: true},
|
||||
string(JoinActiveSessions): {Enabled: true},
|
||||
string(K8s): {Enabled: true},
|
||||
string(MobileDeviceManagement): {Enabled: true},
|
||||
string(OIDC): {Enabled: true},
|
||||
string(Policy): {Enabled: true},
|
||||
string(SAML): {Enabled: true},
|
||||
|
||||
// defaults, no legacy equivalent
|
||||
string(UsageReporting): {Enabled: false},
|
||||
string(UpsellAlert): {Enabled: false},
|
||||
string(CloudAuditLogRetention): {Enabled: false},
|
||||
string(LicenseAutoUpdate): {Enabled: false},
|
||||
string(AccessGraphDemoMode): {Enabled: false},
|
||||
string(UnrestrictedManagedUpdates): {Enabled: false},
|
||||
string(ClientIPRestrictions): {Enabled: false},
|
||||
string(WorkloadClusters): {Enabled: false},
|
||||
// Identity off, fields false
|
||||
string(Identity): {Enabled: false},
|
||||
string(SessionLocks): {Enabled: false},
|
||||
string(OktaSCIM): {Enabled: false},
|
||||
string(OktaUserSync): {Enabled: false},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no entitlements",
|
||||
features: &proto.Features{},
|
||||
expected: func() map[string]*proto.EntitlementInfo {
|
||||
out := map[string]*proto.EntitlementInfo{
|
||||
string(AccessLists): {Enabled: true},
|
||||
}
|
||||
for _, e := range AllEntitlements {
|
||||
if _, ok := out[string(e)]; !ok {
|
||||
out[string(e)] = &proto.EntitlementInfo{}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}(),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cloned := apiutils.CloneProtoMsg(tt.features)
|
||||
|
||||
BackfillFeatures(cloned)
|
||||
require.Empty(t, cmp.Diff(tt.expected, cloned.Entitlements, protocmp.Transform()))
|
||||
})
|
||||
}
|
||||
}
|
||||
+8
-36
@@ -117,7 +117,7 @@ func (e EntitlementInfo) UnderLimit(count int) bool {
|
||||
|
||||
// ToProto converts Features into proto.Features
|
||||
func (f Features) ToProto() *proto.Features {
|
||||
protoF := &proto.Features{
|
||||
return &proto.Features{
|
||||
Cloud: f.Cloud,
|
||||
CustomTheme: f.CustomTheme,
|
||||
IsStripeManaged: f.IsStripeManaged,
|
||||
@@ -133,42 +133,14 @@ func (f Features) ToProto() *proto.Features {
|
||||
RecoveryCodes: f.RecoveryCodes,
|
||||
AccessMonitoringConfigured: f.AccessMonitoringConfigured,
|
||||
Entitlements: f.EntitlementsToProto(),
|
||||
}
|
||||
|
||||
// remove setLegacyLogic in v18
|
||||
setLegacyLogic(protoF, f)
|
||||
return protoF
|
||||
}
|
||||
|
||||
// setLegacyLogic sets the deprecated fields; to be removed in v18 - use entitlements
|
||||
func setLegacyLogic(protoF *proto.Features, f Features) {
|
||||
protoF.Kubernetes = f.GetEntitlement(entitlements.K8s).Enabled
|
||||
protoF.App = f.GetEntitlement(entitlements.App).Enabled
|
||||
protoF.DB = f.GetEntitlement(entitlements.DB).Enabled
|
||||
protoF.OIDC = f.GetEntitlement(entitlements.OIDC).Enabled
|
||||
protoF.SAML = f.GetEntitlement(entitlements.SAML).Enabled
|
||||
protoF.HSM = f.GetEntitlement(entitlements.HSM).Enabled
|
||||
protoF.Desktop = f.GetEntitlement(entitlements.Desktop).Enabled
|
||||
protoF.FeatureHiding = f.GetEntitlement(entitlements.FeatureHiding).Enabled
|
||||
protoF.IdentityGovernance = f.GetEntitlement(entitlements.Identity).Enabled
|
||||
protoF.ExternalAuditStorage = f.GetEntitlement(entitlements.ExternalAuditStorage).Enabled
|
||||
protoF.JoinActiveSessions = f.GetEntitlement(entitlements.JoinActiveSessions).Enabled
|
||||
protoF.MobileDeviceManagement = f.GetEntitlement(entitlements.MobileDeviceManagement).Enabled
|
||||
|
||||
protoF.DeviceTrust = &proto.DeviceTrustFeature{
|
||||
Enabled: f.GetEntitlement(entitlements.DeviceTrust).Enabled, DevicesUsageLimit: f.GetEntitlement(entitlements.DeviceTrust).Limit,
|
||||
}
|
||||
protoF.AccessRequests = &proto.AccessRequestsFeature{
|
||||
MonthlyRequestLimit: f.GetEntitlement(entitlements.AccessRequests).Limit,
|
||||
}
|
||||
protoF.AccessMonitoring = &proto.AccessMonitoringFeature{
|
||||
Enabled: f.AccessMonitoringConfigured, MaxReportRangeLimit: f.GetEntitlement(entitlements.AccessMonitoring).Limit,
|
||||
}
|
||||
protoF.AccessList = &proto.AccessListFeature{
|
||||
CreateLimit: f.GetEntitlement(entitlements.AccessLists).Limit,
|
||||
}
|
||||
protoF.Policy = &proto.PolicyFeature{
|
||||
Enabled: f.GetEntitlement(entitlements.Policy).Enabled,
|
||||
// TODO(michellescripts) DELETE IN v21.0.0
|
||||
// Deprecated, use entitlements
|
||||
Policy: &proto.PolicyFeature{
|
||||
Enabled: f.GetEntitlement(entitlements.Policy).Enabled,
|
||||
},
|
||||
AccessGraphDemoMode: f.GetEntitlement(entitlements.AccessGraphDemoMode).Enabled,
|
||||
ClientIPRestrictions: f.GetEntitlement(entitlements.ClientIPRestrictions).Enabled,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -167,36 +167,13 @@ func TestFeatures_ToProto(t *testing.T) {
|
||||
string(entitlements.ClientIPRestrictions): {Enabled: true},
|
||||
string(entitlements.WorkloadClusters): {Enabled: true},
|
||||
},
|
||||
// Legacy Fields; remove in v18
|
||||
Kubernetes: true,
|
||||
App: false,
|
||||
DB: true,
|
||||
OIDC: true,
|
||||
SAML: true,
|
||||
HSM: true,
|
||||
Desktop: true,
|
||||
FeatureHiding: true,
|
||||
IdentityGovernance: true,
|
||||
ExternalAuditStorage: true,
|
||||
JoinActiveSessions: true,
|
||||
MobileDeviceManagement: true,
|
||||
DeviceTrust: &proto.DeviceTrustFeature{
|
||||
Enabled: true,
|
||||
DevicesUsageLimit: 103,
|
||||
},
|
||||
AccessRequests: &proto.AccessRequestsFeature{
|
||||
MonthlyRequestLimit: 39,
|
||||
},
|
||||
AccessMonitoring: &proto.AccessMonitoringFeature{
|
||||
Enabled: false, // set to value of AccessMonitoringConfigured
|
||||
MaxReportRangeLimit: 2113,
|
||||
},
|
||||
AccessList: &proto.AccessListFeature{
|
||||
CreateLimit: 111,
|
||||
},
|
||||
// Deprecated fields
|
||||
// TODO(michellescripts) DELETE IN v21.0.0
|
||||
Policy: &proto.PolicyFeature{
|
||||
Enabled: true,
|
||||
},
|
||||
AccessGraphDemoMode: true,
|
||||
ClientIPRestrictions: true,
|
||||
}
|
||||
|
||||
f := modules.Features{
|
||||
|
||||
@@ -48,7 +48,6 @@ import (
|
||||
apiutils "github.com/gravitational/teleport/api/utils"
|
||||
"github.com/gravitational/teleport/api/utils/keys"
|
||||
"github.com/gravitational/teleport/api/utils/retryutils"
|
||||
"github.com/gravitational/teleport/entitlements"
|
||||
"github.com/gravitational/teleport/lib"
|
||||
"github.com/gravitational/teleport/lib/auth"
|
||||
"github.com/gravitational/teleport/lib/auth/authclient"
|
||||
@@ -1363,10 +1362,7 @@ func (process *TeleportProcess) getConnector(clientIdentity, serverIdentity *sta
|
||||
}
|
||||
|
||||
// Set cluster features and return successfully with a working connector.
|
||||
// TODO(michellescripts) remove clone & compatibility check in v18
|
||||
cloned := apiutils.CloneProtoMsg(pingResponse.GetServerFeatures())
|
||||
entitlements.BackfillFeatures(cloned)
|
||||
process.setClusterFeatures(cloned)
|
||||
process.setClusterFeatures(pingResponse.GetServerFeatures())
|
||||
process.setAuthSubjectiveAddr(pingResponse.RemoteAddr)
|
||||
process.logger.InfoContext(process.ExitContext(), "features loaded from auth server", "identity", clientIdentity.ID.Role, "features", pingResponse.GetServerFeatures())
|
||||
|
||||
|
||||
+2
-82
@@ -2034,7 +2034,6 @@ func (h *Handler) getWebConfig(w http.ResponseWriter, r *http.Request, p httprou
|
||||
UI: h.getUIConfig(r.Context()),
|
||||
IsPolicyRoleVisualizerEnabled: !disableRoleVisualizer,
|
||||
IsDashboard: services.IsDashboard(clusterFeatures),
|
||||
IsTeam: false,
|
||||
IsUsageBasedBilling: clusterFeatures.GetIsUsageBased(),
|
||||
AutomaticUpgrades: automaticUpgradesEnabled,
|
||||
AutomaticUpgradesTargetVersion: automaticUpgradesTargetVersion,
|
||||
@@ -2044,7 +2043,8 @@ func (h *Handler) getWebConfig(w http.ResponseWriter, r *http.Request, p httprou
|
||||
PremiumSupport: clusterFeatures.GetSupportType() == proto.SupportType_SUPPORT_TYPE_PREMIUM,
|
||||
PlayableDatabaseProtocols: player.SupportedDatabaseProtocols,
|
||||
SessionSummarizerEnabled: sessionSummarizerEnabled,
|
||||
// Entitlements are reset/overridden in setEntitlementsWithLegacyLogic until setEntitlementsWithLegacyLogic is removed in v18
|
||||
// if Entitlements are not present, GetWebCfgEntitlements will return a map of entitlement to {enabled:false}
|
||||
// if Entitlements are present, GetWebCfgEntitlements will populate the fields appropriately
|
||||
Entitlements: GetWebCfgEntitlements(clusterFeatures.GetEntitlements()),
|
||||
IdentitySecurity: webclient.IdentitySecurity{
|
||||
IsClusterLicensed: modules.GetProtoEntitlement(&clusterFeatures, entitlements.Policy).Enabled,
|
||||
@@ -2053,9 +2053,6 @@ func (h *Handler) getWebConfig(w http.ResponseWriter, r *http.Request, p httprou
|
||||
},
|
||||
}
|
||||
|
||||
// Set entitlements with backwards field compatibility
|
||||
setEntitlementsWithLegacyLogic(&webCfg, clusterFeatures)
|
||||
|
||||
resource, err := h.cfg.ProxyClient.GetClusterName(r.Context())
|
||||
if err != nil {
|
||||
h.logger.WarnContext(r.Context(), "Failed to query cluster name", "error", err)
|
||||
@@ -2220,83 +2217,6 @@ func (h *Handler) getUserMatchedAuthConnectors(w http.ResponseWriter, r *http.Re
|
||||
}, nil
|
||||
}
|
||||
|
||||
// setEntitlementsWithLegacyLogic ensures entitlements on webCfg are backwards compatible
|
||||
// If Entitlements are present, will set the legacy fields equal to the equivalent entitlement value
|
||||
// i.e. webCfg.IsIGSEnabled = clusterFeatures.Entitlements[entitlements.Identity].Enabled
|
||||
// && webCfg.Entitlements[entitlements.Identity] = clusterFeatures.Entitlements[entitlements.Identity].Enabled
|
||||
// If Entitlements are not present, will set the legacy fields AND the entitlement equal to the legacy feature
|
||||
// i.e. webCfg.IsIGSEnabled = clusterFeatures.GetIdentityGovernance()
|
||||
// && webCfg.Entitlements[entitlements.Identity] = clusterFeatures.GetIdentityGovernance()
|
||||
// todo (michellescripts) remove in v18; & inline entitlement logic above
|
||||
func setEntitlementsWithLegacyLogic(webCfg *webclient.WebConfig, clusterFeatures proto.Features) {
|
||||
// if Entitlements are not present, GetWebCfgEntitlements will return a map of entitlement to {enabled:false}
|
||||
// if Entitlements are present, GetWebCfgEntitlements will populate the fields appropriately
|
||||
webCfg.Entitlements = GetWebCfgEntitlements(clusterFeatures.GetEntitlements())
|
||||
|
||||
if ent := clusterFeatures.GetEntitlements(); len(ent) > 0 {
|
||||
// webCfg.Entitlements: No update as they are set above
|
||||
// webCfg.<legacy fields>: set equal to entitlement value
|
||||
webCfg.AccessRequests = modules.GetProtoEntitlement(&clusterFeatures, entitlements.AccessRequests).Enabled
|
||||
webCfg.ExternalAuditStorage = modules.GetProtoEntitlement(&clusterFeatures, entitlements.ExternalAuditStorage).Enabled
|
||||
webCfg.HideInaccessibleFeatures = modules.GetProtoEntitlement(&clusterFeatures, entitlements.FeatureHiding).Enabled
|
||||
webCfg.IsIGSEnabled = modules.GetProtoEntitlement(&clusterFeatures, entitlements.Identity).Enabled
|
||||
webCfg.IsPolicyEnabled = modules.GetProtoEntitlement(&clusterFeatures, entitlements.Policy).Enabled
|
||||
webCfg.JoinActiveSessions = modules.GetProtoEntitlement(&clusterFeatures, entitlements.JoinActiveSessions).Enabled
|
||||
webCfg.MobileDeviceManagement = modules.GetProtoEntitlement(&clusterFeatures, entitlements.MobileDeviceManagement).Enabled
|
||||
webCfg.OIDC = modules.GetProtoEntitlement(&clusterFeatures, entitlements.OIDC).Enabled
|
||||
webCfg.SAML = modules.GetProtoEntitlement(&clusterFeatures, entitlements.SAML).Enabled
|
||||
webCfg.TrustedDevices = modules.GetProtoEntitlement(&clusterFeatures, entitlements.DeviceTrust).Enabled
|
||||
webCfg.FeatureLimits = webclient.FeatureLimits{
|
||||
AccessListCreateLimit: int(modules.GetProtoEntitlement(&clusterFeatures, entitlements.AccessLists).Limit),
|
||||
AccessMonitoringMaxReportRangeLimit: int(modules.GetProtoEntitlement(&clusterFeatures, entitlements.AccessMonitoring).Limit),
|
||||
AccessRequestMonthlyRequestLimit: int(modules.GetProtoEntitlement(&clusterFeatures, entitlements.AccessRequests).Limit),
|
||||
}
|
||||
|
||||
} else {
|
||||
// webCfg.Entitlements: All records are {enabled: false}; update to equal legacy feature value
|
||||
webCfg.Entitlements[string(entitlements.ExternalAuditStorage)] = webclient.EntitlementInfo{Enabled: clusterFeatures.GetExternalAuditStorage()}
|
||||
webCfg.Entitlements[string(entitlements.FeatureHiding)] = webclient.EntitlementInfo{Enabled: clusterFeatures.GetFeatureHiding()}
|
||||
webCfg.Entitlements[string(entitlements.Identity)] = webclient.EntitlementInfo{Enabled: clusterFeatures.GetIdentityGovernance()}
|
||||
webCfg.Entitlements[string(entitlements.JoinActiveSessions)] = webclient.EntitlementInfo{Enabled: clusterFeatures.GetJoinActiveSessions()}
|
||||
webCfg.Entitlements[string(entitlements.MobileDeviceManagement)] = webclient.EntitlementInfo{Enabled: clusterFeatures.GetMobileDeviceManagement()}
|
||||
webCfg.Entitlements[string(entitlements.OIDC)] = webclient.EntitlementInfo{Enabled: clusterFeatures.GetOIDC()}
|
||||
webCfg.Entitlements[string(entitlements.Policy)] = webclient.EntitlementInfo{Enabled: clusterFeatures.GetPolicy() != nil && clusterFeatures.GetPolicy().Enabled}
|
||||
webCfg.Entitlements[string(entitlements.SAML)] = webclient.EntitlementInfo{Enabled: clusterFeatures.GetSAML()}
|
||||
// set default Identity fields to legacy feature value
|
||||
webCfg.Entitlements[string(entitlements.AccessLists)] = webclient.EntitlementInfo{Enabled: true, Limit: clusterFeatures.GetAccessList().GetCreateLimit()}
|
||||
webCfg.Entitlements[string(entitlements.AccessMonitoring)] = webclient.EntitlementInfo{Enabled: clusterFeatures.GetAccessMonitoring().GetEnabled(), Limit: clusterFeatures.GetAccessMonitoring().GetMaxReportRangeLimit()}
|
||||
webCfg.Entitlements[string(entitlements.AccessRequests)] = webclient.EntitlementInfo{Enabled: clusterFeatures.GetAccessRequests().GetMonthlyRequestLimit() > 0, Limit: clusterFeatures.GetAccessRequests().GetMonthlyRequestLimit()}
|
||||
webCfg.Entitlements[string(entitlements.DeviceTrust)] = webclient.EntitlementInfo{Enabled: clusterFeatures.GetDeviceTrust().GetEnabled(), Limit: clusterFeatures.GetDeviceTrust().GetDevicesUsageLimit()}
|
||||
// override Identity Package features if Identity is enabled: set true and clear limit
|
||||
if clusterFeatures.GetIdentityGovernance() {
|
||||
webCfg.Entitlements[string(entitlements.AccessLists)] = webclient.EntitlementInfo{Enabled: true}
|
||||
webCfg.Entitlements[string(entitlements.AccessMonitoring)] = webclient.EntitlementInfo{Enabled: true}
|
||||
webCfg.Entitlements[string(entitlements.AccessRequests)] = webclient.EntitlementInfo{Enabled: true}
|
||||
webCfg.Entitlements[string(entitlements.DeviceTrust)] = webclient.EntitlementInfo{Enabled: true}
|
||||
webCfg.Entitlements[string(entitlements.OktaSCIM)] = webclient.EntitlementInfo{Enabled: true}
|
||||
webCfg.Entitlements[string(entitlements.OktaUserSync)] = webclient.EntitlementInfo{Enabled: true}
|
||||
webCfg.Entitlements[string(entitlements.SessionLocks)] = webclient.EntitlementInfo{Enabled: true}
|
||||
}
|
||||
|
||||
// webCfg.<legacy fields>: set equal to legacy feature value
|
||||
webCfg.AccessRequests = clusterFeatures.GetAccessRequests().GetMonthlyRequestLimit() > 0
|
||||
webCfg.ExternalAuditStorage = clusterFeatures.GetExternalAuditStorage()
|
||||
webCfg.HideInaccessibleFeatures = clusterFeatures.GetFeatureHiding()
|
||||
webCfg.IsIGSEnabled = clusterFeatures.GetIdentityGovernance()
|
||||
webCfg.IsPolicyEnabled = clusterFeatures.GetPolicy() != nil && clusterFeatures.GetPolicy().Enabled
|
||||
webCfg.JoinActiveSessions = clusterFeatures.GetJoinActiveSessions()
|
||||
webCfg.MobileDeviceManagement = clusterFeatures.GetMobileDeviceManagement()
|
||||
webCfg.OIDC = clusterFeatures.GetOIDC()
|
||||
webCfg.SAML = clusterFeatures.GetSAML()
|
||||
webCfg.TrustedDevices = clusterFeatures.GetDeviceTrust().GetEnabled()
|
||||
webCfg.FeatureLimits = webclient.FeatureLimits{
|
||||
AccessListCreateLimit: int(clusterFeatures.GetAccessList().GetCreateLimit()),
|
||||
AccessMonitoringMaxReportRangeLimit: int(clusterFeatures.GetAccessMonitoring().GetMaxReportRangeLimit()),
|
||||
AccessRequestMonthlyRequestLimit: int(clusterFeatures.GetAccessRequests().GetMonthlyRequestLimit()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetWebCfgEntitlements takes a cloud entitlement set and returns a modules Entitlement set
|
||||
func GetWebCfgEntitlements(protoEntitlements map[string]*proto.EntitlementInfo) map[string]webclient.EntitlementInfo {
|
||||
all := entitlements.AllEntitlements
|
||||
|
||||
+5
-610
@@ -4900,12 +4900,11 @@ func TestGetWebConfig_WithEntitlements(t *testing.T) {
|
||||
PrivateKeyPolicy: keys.PrivateKeyPolicyNone,
|
||||
MOTD: MOTD,
|
||||
},
|
||||
CanJoinSessions: true,
|
||||
ProxyClusterName: env.server.ClusterName(),
|
||||
IsCloud: false,
|
||||
AutomaticUpgrades: false,
|
||||
JoinActiveSessions: true,
|
||||
Edition: testModules.BuildType(),
|
||||
CanJoinSessions: true,
|
||||
ProxyClusterName: env.server.ClusterName(),
|
||||
IsCloud: false,
|
||||
AutomaticUpgrades: false,
|
||||
Edition: testModules.BuildType(),
|
||||
Entitlements: map[string]webclient.EntitlementInfo{
|
||||
string(entitlements.AccessLists): {Enabled: false},
|
||||
string(entitlements.AccessMonitoring): {Enabled: false},
|
||||
@@ -4996,9 +4995,7 @@ func TestGetWebConfig_WithEntitlements(t *testing.T) {
|
||||
expectedCfg.IsUsageBasedBilling = true
|
||||
expectedCfg.AutomaticUpgrades = true
|
||||
expectedCfg.AutomaticUpgradesTargetVersion = "v" + teleport.Version
|
||||
expectedCfg.JoinActiveSessions = false
|
||||
expectedCfg.Edition = testModules.BuildType()
|
||||
expectedCfg.TrustedDevices = true
|
||||
expectedCfg.Entitlements[string(entitlements.App)] = webclient.EntitlementInfo{Enabled: false}
|
||||
expectedCfg.Entitlements[string(entitlements.DB)] = webclient.EntitlementInfo{Enabled: true, Limit: 22}
|
||||
expectedCfg.Entitlements[string(entitlements.DeviceTrust)] = webclient.EntitlementInfo{Enabled: true, Limit: 33}
|
||||
@@ -5027,7 +5024,6 @@ func TestGetWebConfig_WithEntitlements(t *testing.T) {
|
||||
}
|
||||
env.proxies[0].client = mockClient
|
||||
expectedCfg.AutomaticUpgrades = false
|
||||
expectedCfg.TrustedDevices = false
|
||||
expectedCfg.Entitlements[string(entitlements.DB)] = webclient.EntitlementInfo{Enabled: false}
|
||||
expectedCfg.Entitlements[string(entitlements.Desktop)] = webclient.EntitlementInfo{Enabled: false}
|
||||
expectedCfg.Entitlements[string(entitlements.DeviceTrust)] = webclient.EntitlementInfo{Enabled: false}
|
||||
@@ -5056,94 +5052,6 @@ func TestGetWebConfig_WithEntitlements(t *testing.T) {
|
||||
}, time.Second*5, time.Millisecond*50)
|
||||
}
|
||||
|
||||
func TestGetWebConfig_LegacyFeatureLimits(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
testModules := &modulestest.Modules{
|
||||
TestBuildType: modules.BuildOSS,
|
||||
TestFeatures: modules.Features{
|
||||
ProductType: modules.ProductTypeTeam,
|
||||
IsUsageBasedBilling: true,
|
||||
IsStripeManaged: true,
|
||||
Questionnaire: true,
|
||||
Entitlements: map[entitlements.EntitlementKind]modules.EntitlementInfo{
|
||||
entitlements.Identity: {Enabled: true},
|
||||
entitlements.AccessLists: {Enabled: true, Limit: 5},
|
||||
entitlements.AccessMonitoring: {Enabled: true, Limit: 10},
|
||||
},
|
||||
},
|
||||
}
|
||||
env := newWebPack(t, 1, withModules(testModules))
|
||||
|
||||
expectedCfg := webclient.WebConfig{
|
||||
Auth: webclient.WebConfigAuthSettings{
|
||||
SecondFactor: constants.SecondFactorOff,
|
||||
LocalAuthEnabled: true,
|
||||
AuthType: constants.Local,
|
||||
PrivateKeyPolicy: keys.PrivateKeyPolicyNone,
|
||||
},
|
||||
CanJoinSessions: true,
|
||||
Edition: testModules.TestBuildType,
|
||||
ProxyClusterName: env.server.ClusterName(),
|
||||
FeatureLimits: webclient.FeatureLimits{
|
||||
AccessListCreateLimit: 5,
|
||||
AccessMonitoringMaxReportRangeLimit: 10,
|
||||
},
|
||||
IsTeam: false,
|
||||
IsIGSEnabled: true,
|
||||
IsStripeManaged: true,
|
||||
Questionnaire: true,
|
||||
IsUsageBasedBilling: true,
|
||||
Entitlements: map[string]webclient.EntitlementInfo{
|
||||
string(entitlements.AccessLists): {Enabled: true, Limit: 5},
|
||||
string(entitlements.AccessMonitoring): {Enabled: true, Limit: 10},
|
||||
string(entitlements.AccessRequests): {Enabled: false},
|
||||
string(entitlements.App): {Enabled: false},
|
||||
string(entitlements.CloudAuditLogRetention): {Enabled: false},
|
||||
string(entitlements.DB): {Enabled: false},
|
||||
string(entitlements.Desktop): {Enabled: false},
|
||||
string(entitlements.DeviceTrust): {Enabled: false},
|
||||
string(entitlements.ExternalAuditStorage): {Enabled: false},
|
||||
string(entitlements.FeatureHiding): {Enabled: false},
|
||||
string(entitlements.HSM): {Enabled: false},
|
||||
string(entitlements.Identity): {Enabled: true},
|
||||
string(entitlements.JoinActiveSessions): {Enabled: false},
|
||||
string(entitlements.K8s): {Enabled: false},
|
||||
string(entitlements.MobileDeviceManagement): {Enabled: false},
|
||||
string(entitlements.OIDC): {Enabled: false},
|
||||
string(entitlements.OktaSCIM): {Enabled: false},
|
||||
string(entitlements.OktaUserSync): {Enabled: false},
|
||||
string(entitlements.Policy): {Enabled: false},
|
||||
string(entitlements.SAML): {Enabled: false},
|
||||
string(entitlements.SessionLocks): {Enabled: false},
|
||||
string(entitlements.UpsellAlert): {Enabled: false},
|
||||
string(entitlements.UsageReporting): {Enabled: false},
|
||||
string(entitlements.LicenseAutoUpdate): {Enabled: false},
|
||||
string(entitlements.AccessGraphDemoMode): {Enabled: false},
|
||||
string(entitlements.UnrestrictedManagedUpdates): {Enabled: false},
|
||||
string(entitlements.ClientIPRestrictions): {Enabled: false},
|
||||
string(entitlements.WorkloadClusters): {Enabled: false},
|
||||
},
|
||||
PlayableDatabaseProtocols: player.SupportedDatabaseProtocols,
|
||||
IsPolicyRoleVisualizerEnabled: true,
|
||||
}
|
||||
|
||||
clt := env.proxies[0].newClient(t)
|
||||
// Make a request.
|
||||
endpoint := clt.Endpoint("web", "config.js")
|
||||
re, err := clt.Get(ctx, endpoint, nil)
|
||||
require.NoError(t, err)
|
||||
require.True(t, bytes.HasPrefix(re.Bytes(), []byte("var GRV_CONFIG")))
|
||||
|
||||
// Response is type application/javascript, we need to strip off the variable name
|
||||
// and the semicolon at the end, then we are left with json like object.
|
||||
var cfg webclient.WebConfig
|
||||
res := bytes.ReplaceAll(re.Bytes(), []byte("var GRV_CONFIG = "), []byte{})
|
||||
err = json.Unmarshal(res[:len(res)-1], &cfg)
|
||||
require.NoError(t, err)
|
||||
diff := cmp.Diff(expectedCfg, cfg)
|
||||
require.Empty(t, diff)
|
||||
}
|
||||
|
||||
func TestCreatePrivilegeToken(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newWebPack(t, 1)
|
||||
@@ -11356,519 +11264,6 @@ func TestUnstartedServerShutdown(t *testing.T) {
|
||||
require.NoError(t, srv.Shutdown(context.Background()))
|
||||
}
|
||||
|
||||
func Test_setEntitlementsWithLegacyLogic(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config *webclient.WebConfig
|
||||
clusterFeatures authproto.Features
|
||||
expected *webclient.WebConfig
|
||||
}{
|
||||
{
|
||||
name: "sets entitlements",
|
||||
config: &webclient.WebConfig{},
|
||||
clusterFeatures: authproto.Features{
|
||||
AccessControls: false,
|
||||
AccessGraph: false,
|
||||
AccessList: &authproto.AccessListFeature{
|
||||
CreateLimit: 10,
|
||||
},
|
||||
AccessMonitoring: &authproto.AccessMonitoringFeature{
|
||||
Enabled: false,
|
||||
MaxReportRangeLimit: 20,
|
||||
},
|
||||
AccessMonitoringConfigured: false,
|
||||
AccessRequests: &authproto.AccessRequestsFeature{
|
||||
MonthlyRequestLimit: 30,
|
||||
},
|
||||
AdvancedAccessWorkflows: false,
|
||||
App: false,
|
||||
Assist: false,
|
||||
AutomaticUpgrades: false,
|
||||
Cloud: false,
|
||||
CustomTheme: "theme",
|
||||
DB: false,
|
||||
Desktop: false,
|
||||
DeviceTrust: &authproto.DeviceTrustFeature{
|
||||
Enabled: false,
|
||||
DevicesUsageLimit: 40,
|
||||
},
|
||||
ExternalAuditStorage: false,
|
||||
FeatureHiding: false,
|
||||
HSM: false,
|
||||
IdentityGovernance: false,
|
||||
IsStripeManaged: false,
|
||||
IsUsageBased: false,
|
||||
JoinActiveSessions: false,
|
||||
Kubernetes: false,
|
||||
MobileDeviceManagement: false,
|
||||
OIDC: false,
|
||||
Plugins: false,
|
||||
Policy: nil,
|
||||
ProductType: 0,
|
||||
Questionnaire: false,
|
||||
RecoveryCodes: false,
|
||||
SAML: false,
|
||||
SupportType: 0,
|
||||
// since present, becomes source of truth for feature enablement
|
||||
Entitlements: map[string]*authproto.EntitlementInfo{
|
||||
string(entitlements.AccessLists): {Enabled: true, Limit: 99},
|
||||
string(entitlements.AccessMonitoring): {Enabled: true, Limit: 99},
|
||||
string(entitlements.AccessRequests): {Enabled: true, Limit: 99},
|
||||
string(entitlements.App): {Enabled: true, Limit: 99},
|
||||
string(entitlements.CloudAuditLogRetention): {Enabled: true, Limit: 99},
|
||||
string(entitlements.DB): {Enabled: true, Limit: 99},
|
||||
string(entitlements.Desktop): {Enabled: true, Limit: 99},
|
||||
string(entitlements.DeviceTrust): {Enabled: true, Limit: 99},
|
||||
string(entitlements.ExternalAuditStorage): {Enabled: true, Limit: 99},
|
||||
string(entitlements.FeatureHiding): {Enabled: true, Limit: 99},
|
||||
string(entitlements.HSM): {Enabled: true, Limit: 99},
|
||||
string(entitlements.Identity): {Enabled: true, Limit: 99},
|
||||
string(entitlements.JoinActiveSessions): {Enabled: true, Limit: 99},
|
||||
string(entitlements.K8s): {Enabled: true, Limit: 99},
|
||||
string(entitlements.MobileDeviceManagement): {Enabled: true, Limit: 99},
|
||||
string(entitlements.OIDC): {Enabled: true, Limit: 99},
|
||||
string(entitlements.OktaSCIM): {Enabled: true, Limit: 99},
|
||||
string(entitlements.OktaUserSync): {Enabled: true, Limit: 99},
|
||||
string(entitlements.Policy): {Enabled: true, Limit: 99},
|
||||
string(entitlements.SAML): {Enabled: true, Limit: 99},
|
||||
string(entitlements.SessionLocks): {Enabled: true, Limit: 99},
|
||||
string(entitlements.UpsellAlert): {Enabled: true, Limit: 99},
|
||||
string(entitlements.UsageReporting): {Enabled: true, Limit: 99},
|
||||
string(entitlements.LicenseAutoUpdate): {Enabled: true, Limit: 99},
|
||||
string(entitlements.AccessGraphDemoMode): {Enabled: true, Limit: 99},
|
||||
string(entitlements.UnrestrictedManagedUpdates): {Enabled: true, Limit: 99},
|
||||
string(entitlements.ClientIPRestrictions): {Enabled: true, Limit: 99},
|
||||
string(entitlements.WorkloadClusters): {Enabled: true, Limit: 99},
|
||||
},
|
||||
},
|
||||
expected: &webclient.WebConfig{
|
||||
Auth: webclient.WebConfigAuthSettings{},
|
||||
AutomaticUpgrades: false,
|
||||
AutomaticUpgradesTargetVersion: "",
|
||||
CanJoinSessions: false,
|
||||
CustomTheme: "",
|
||||
Edition: "",
|
||||
IsCloud: false,
|
||||
IsDashboard: false,
|
||||
IsStripeManaged: false,
|
||||
IsTeam: false,
|
||||
IsUsageBasedBilling: false,
|
||||
PlayableDatabaseProtocols: nil,
|
||||
PremiumSupport: false,
|
||||
ProxyClusterName: "",
|
||||
Questionnaire: false,
|
||||
RecoveryCodesEnabled: false,
|
||||
TunnelPublicAddress: "",
|
||||
UI: webclient.UIConfig{},
|
||||
// set by the equivalent entitlement value
|
||||
AccessRequests: true,
|
||||
ExternalAuditStorage: true,
|
||||
HideInaccessibleFeatures: true,
|
||||
IsIGSEnabled: true,
|
||||
IsPolicyEnabled: true,
|
||||
JoinActiveSessions: true,
|
||||
MobileDeviceManagement: true,
|
||||
OIDC: true,
|
||||
SAML: true,
|
||||
TrustedDevices: true,
|
||||
FeatureLimits: webclient.FeatureLimits{
|
||||
AccessListCreateLimit: 99,
|
||||
AccessMonitoringMaxReportRangeLimit: 99,
|
||||
AccessRequestMonthlyRequestLimit: 99,
|
||||
},
|
||||
Entitlements: map[string]webclient.EntitlementInfo{
|
||||
string(entitlements.AccessLists): {Enabled: true, Limit: 99},
|
||||
string(entitlements.AccessMonitoring): {Enabled: true, Limit: 99},
|
||||
string(entitlements.AccessRequests): {Enabled: true, Limit: 99},
|
||||
string(entitlements.App): {Enabled: true, Limit: 99},
|
||||
string(entitlements.CloudAuditLogRetention): {Enabled: true, Limit: 99},
|
||||
string(entitlements.DB): {Enabled: true, Limit: 99},
|
||||
string(entitlements.Desktop): {Enabled: true, Limit: 99},
|
||||
string(entitlements.DeviceTrust): {Enabled: true, Limit: 99},
|
||||
string(entitlements.ExternalAuditStorage): {Enabled: true, Limit: 99},
|
||||
string(entitlements.FeatureHiding): {Enabled: true, Limit: 99},
|
||||
string(entitlements.HSM): {Enabled: true, Limit: 99},
|
||||
string(entitlements.Identity): {Enabled: true, Limit: 99},
|
||||
string(entitlements.JoinActiveSessions): {Enabled: true, Limit: 99},
|
||||
string(entitlements.K8s): {Enabled: true, Limit: 99},
|
||||
string(entitlements.MobileDeviceManagement): {Enabled: true, Limit: 99},
|
||||
string(entitlements.OIDC): {Enabled: true, Limit: 99},
|
||||
string(entitlements.OktaSCIM): {Enabled: true, Limit: 99},
|
||||
string(entitlements.OktaUserSync): {Enabled: true, Limit: 99},
|
||||
string(entitlements.Policy): {Enabled: true, Limit: 99},
|
||||
string(entitlements.SAML): {Enabled: true, Limit: 99},
|
||||
string(entitlements.SessionLocks): {Enabled: true, Limit: 99},
|
||||
string(entitlements.UpsellAlert): {Enabled: true, Limit: 99},
|
||||
string(entitlements.UsageReporting): {Enabled: true, Limit: 99},
|
||||
string(entitlements.LicenseAutoUpdate): {Enabled: true, Limit: 99},
|
||||
string(entitlements.AccessGraphDemoMode): {Enabled: true, Limit: 99},
|
||||
string(entitlements.UnrestrictedManagedUpdates): {Enabled: true, Limit: 99},
|
||||
string(entitlements.ClientIPRestrictions): {Enabled: true, Limit: 99},
|
||||
string(entitlements.WorkloadClusters): {Enabled: true, Limit: 99},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sets legacy features when no entitlements are present (Identity true)",
|
||||
config: &webclient.WebConfig{},
|
||||
clusterFeatures: authproto.Features{
|
||||
AccessControls: false,
|
||||
AccessGraph: false,
|
||||
AccessMonitoringConfigured: false,
|
||||
AdvancedAccessWorkflows: false,
|
||||
App: false,
|
||||
Assist: false,
|
||||
AutomaticUpgrades: false,
|
||||
Cloud: false,
|
||||
CustomTheme: "",
|
||||
DB: false,
|
||||
Desktop: false,
|
||||
HSM: false,
|
||||
IsStripeManaged: false,
|
||||
IsUsageBased: false,
|
||||
Kubernetes: false,
|
||||
Plugins: false,
|
||||
ProductType: 0,
|
||||
Questionnaire: false,
|
||||
RecoveryCodes: false,
|
||||
SupportType: 0,
|
||||
// not present
|
||||
Entitlements: nil,
|
||||
// will set equivalent entitlement values
|
||||
ExternalAuditStorage: true,
|
||||
FeatureHiding: true,
|
||||
IdentityGovernance: true,
|
||||
JoinActiveSessions: true,
|
||||
MobileDeviceManagement: true,
|
||||
OIDC: true,
|
||||
SAML: true,
|
||||
AccessRequests: &authproto.AccessRequestsFeature{
|
||||
MonthlyRequestLimit: 88,
|
||||
},
|
||||
AccessList: &authproto.AccessListFeature{
|
||||
CreateLimit: 88,
|
||||
},
|
||||
AccessMonitoring: &authproto.AccessMonitoringFeature{
|
||||
Enabled: true,
|
||||
MaxReportRangeLimit: 88,
|
||||
},
|
||||
DeviceTrust: &authproto.DeviceTrustFeature{
|
||||
Enabled: true,
|
||||
DevicesUsageLimit: 88,
|
||||
},
|
||||
Policy: &authproto.PolicyFeature{
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
expected: &webclient.WebConfig{
|
||||
Auth: webclient.WebConfigAuthSettings{},
|
||||
AutomaticUpgrades: false,
|
||||
AutomaticUpgradesTargetVersion: "",
|
||||
CanJoinSessions: false,
|
||||
CustomTheme: "",
|
||||
Edition: "",
|
||||
IsCloud: false,
|
||||
IsDashboard: false,
|
||||
IsStripeManaged: false,
|
||||
IsTeam: false,
|
||||
IsUsageBasedBilling: false,
|
||||
PlayableDatabaseProtocols: nil,
|
||||
PremiumSupport: false,
|
||||
ProxyClusterName: "",
|
||||
Questionnaire: false,
|
||||
RecoveryCodesEnabled: false,
|
||||
TunnelPublicAddress: "",
|
||||
UI: webclient.UIConfig{},
|
||||
// set to legacy feature
|
||||
AccessRequests: true,
|
||||
ExternalAuditStorage: true,
|
||||
HideInaccessibleFeatures: true,
|
||||
IsIGSEnabled: true,
|
||||
IsPolicyEnabled: true,
|
||||
JoinActiveSessions: true,
|
||||
MobileDeviceManagement: true,
|
||||
OIDC: true,
|
||||
SAML: true,
|
||||
TrustedDevices: true,
|
||||
FeatureLimits: webclient.FeatureLimits{
|
||||
AccessListCreateLimit: 88,
|
||||
AccessMonitoringMaxReportRangeLimit: 88,
|
||||
AccessRequestMonthlyRequestLimit: 88,
|
||||
},
|
||||
Entitlements: map[string]webclient.EntitlementInfo{
|
||||
// no equivalent legacy feature; defaults to false
|
||||
string(entitlements.App): {Enabled: false},
|
||||
string(entitlements.CloudAuditLogRetention): {Enabled: false},
|
||||
string(entitlements.DB): {Enabled: false},
|
||||
string(entitlements.Desktop): {Enabled: false},
|
||||
string(entitlements.HSM): {Enabled: false},
|
||||
string(entitlements.K8s): {Enabled: false},
|
||||
string(entitlements.UpsellAlert): {Enabled: false},
|
||||
string(entitlements.UsageReporting): {Enabled: false},
|
||||
string(entitlements.LicenseAutoUpdate): {Enabled: false},
|
||||
string(entitlements.AccessGraphDemoMode): {Enabled: false},
|
||||
string(entitlements.UnrestrictedManagedUpdates): {Enabled: false},
|
||||
string(entitlements.ClientIPRestrictions): {Enabled: false},
|
||||
string(entitlements.WorkloadClusters): {Enabled: false},
|
||||
|
||||
// set to equivalent legacy feature
|
||||
string(entitlements.ExternalAuditStorage): {Enabled: true},
|
||||
string(entitlements.FeatureHiding): {Enabled: true},
|
||||
string(entitlements.Identity): {Enabled: true},
|
||||
string(entitlements.JoinActiveSessions): {Enabled: true},
|
||||
string(entitlements.MobileDeviceManagement): {Enabled: true},
|
||||
string(entitlements.OIDC): {Enabled: true},
|
||||
string(entitlements.Policy): {Enabled: true},
|
||||
string(entitlements.SAML): {Enabled: true},
|
||||
// set to legacy feature "IsIGSEnabled"; true so set true and clear limits
|
||||
string(entitlements.AccessLists): {Enabled: true},
|
||||
string(entitlements.AccessMonitoring): {Enabled: true},
|
||||
string(entitlements.AccessRequests): {Enabled: true},
|
||||
string(entitlements.DeviceTrust): {Enabled: true},
|
||||
string(entitlements.OktaSCIM): {Enabled: true},
|
||||
string(entitlements.OktaUserSync): {Enabled: true},
|
||||
string(entitlements.SessionLocks): {Enabled: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sets legacy features when no entitlements are present (Identity false)",
|
||||
config: &webclient.WebConfig{},
|
||||
clusterFeatures: authproto.Features{
|
||||
AccessControls: false,
|
||||
AccessGraph: false,
|
||||
AccessMonitoringConfigured: false,
|
||||
AdvancedAccessWorkflows: false,
|
||||
App: false,
|
||||
Assist: false,
|
||||
AutomaticUpgrades: false,
|
||||
Cloud: false,
|
||||
CustomTheme: "",
|
||||
DB: false,
|
||||
Desktop: false,
|
||||
HSM: false,
|
||||
IsStripeManaged: false,
|
||||
IsUsageBased: false,
|
||||
Kubernetes: false,
|
||||
Plugins: false,
|
||||
ProductType: 0,
|
||||
Questionnaire: false,
|
||||
RecoveryCodes: false,
|
||||
SupportType: 0,
|
||||
// not present
|
||||
Entitlements: nil,
|
||||
// will set equivalent entitlement values
|
||||
ExternalAuditStorage: true,
|
||||
FeatureHiding: true,
|
||||
IdentityGovernance: false,
|
||||
JoinActiveSessions: true,
|
||||
MobileDeviceManagement: true,
|
||||
OIDC: true,
|
||||
SAML: true,
|
||||
AccessRequests: &authproto.AccessRequestsFeature{
|
||||
MonthlyRequestLimit: 88,
|
||||
},
|
||||
AccessList: &authproto.AccessListFeature{
|
||||
CreateLimit: 88,
|
||||
},
|
||||
AccessMonitoring: &authproto.AccessMonitoringFeature{
|
||||
Enabled: true,
|
||||
MaxReportRangeLimit: 88,
|
||||
},
|
||||
DeviceTrust: &authproto.DeviceTrustFeature{
|
||||
Enabled: true,
|
||||
DevicesUsageLimit: 88,
|
||||
},
|
||||
Policy: &authproto.PolicyFeature{
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
expected: &webclient.WebConfig{
|
||||
Auth: webclient.WebConfigAuthSettings{},
|
||||
AutomaticUpgrades: false,
|
||||
AutomaticUpgradesTargetVersion: "",
|
||||
CanJoinSessions: false,
|
||||
CustomTheme: "",
|
||||
Edition: "",
|
||||
IsCloud: false,
|
||||
IsDashboard: false,
|
||||
IsStripeManaged: false,
|
||||
IsTeam: false,
|
||||
IsUsageBasedBilling: false,
|
||||
PlayableDatabaseProtocols: nil,
|
||||
PremiumSupport: false,
|
||||
ProxyClusterName: "",
|
||||
Questionnaire: false,
|
||||
RecoveryCodesEnabled: false,
|
||||
TunnelPublicAddress: "",
|
||||
UI: webclient.UIConfig{},
|
||||
// set to legacy feature
|
||||
AccessRequests: true,
|
||||
ExternalAuditStorage: true,
|
||||
HideInaccessibleFeatures: true,
|
||||
IsIGSEnabled: false,
|
||||
IsPolicyEnabled: true,
|
||||
JoinActiveSessions: true,
|
||||
MobileDeviceManagement: true,
|
||||
OIDC: true,
|
||||
SAML: true,
|
||||
TrustedDevices: true,
|
||||
FeatureLimits: webclient.FeatureLimits{
|
||||
AccessListCreateLimit: 88,
|
||||
AccessMonitoringMaxReportRangeLimit: 88,
|
||||
AccessRequestMonthlyRequestLimit: 88,
|
||||
},
|
||||
Entitlements: map[string]webclient.EntitlementInfo{
|
||||
// no equivalent legacy feature; defaults to false
|
||||
string(entitlements.App): {Enabled: false},
|
||||
string(entitlements.CloudAuditLogRetention): {Enabled: false},
|
||||
string(entitlements.DB): {Enabled: false},
|
||||
string(entitlements.Desktop): {Enabled: false},
|
||||
string(entitlements.HSM): {Enabled: false},
|
||||
string(entitlements.K8s): {Enabled: false},
|
||||
string(entitlements.UpsellAlert): {Enabled: false},
|
||||
string(entitlements.UsageReporting): {Enabled: false},
|
||||
|
||||
// set to equivalent legacy feature
|
||||
string(entitlements.ExternalAuditStorage): {Enabled: true},
|
||||
string(entitlements.FeatureHiding): {Enabled: true},
|
||||
string(entitlements.Identity): {Enabled: false},
|
||||
string(entitlements.JoinActiveSessions): {Enabled: true},
|
||||
string(entitlements.MobileDeviceManagement): {Enabled: true},
|
||||
string(entitlements.OIDC): {Enabled: true},
|
||||
string(entitlements.Policy): {Enabled: true},
|
||||
string(entitlements.SAML): {Enabled: true},
|
||||
string(entitlements.AccessGraphDemoMode): {Enabled: false},
|
||||
string(entitlements.UnrestrictedManagedUpdates): {Enabled: false},
|
||||
string(entitlements.ClientIPRestrictions): {Enabled: false},
|
||||
string(entitlements.WorkloadClusters): {Enabled: false},
|
||||
|
||||
// set to legacy feature "IsIGSEnabled"; false so set value and keep limits
|
||||
string(entitlements.AccessLists): {Enabled: true, Limit: 88},
|
||||
string(entitlements.AccessMonitoring): {Enabled: true, Limit: 88},
|
||||
string(entitlements.AccessRequests): {Enabled: true, Limit: 88},
|
||||
string(entitlements.DeviceTrust): {Enabled: true, Limit: 88},
|
||||
string(entitlements.OktaSCIM): {Enabled: false},
|
||||
string(entitlements.OktaUserSync): {Enabled: false},
|
||||
string(entitlements.SessionLocks): {Enabled: false},
|
||||
string(entitlements.LicenseAutoUpdate): {Enabled: false},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "retains non-feature field values",
|
||||
config: &webclient.WebConfig{
|
||||
Auth: webclient.WebConfigAuthSettings{
|
||||
LocalAuthEnabled: true,
|
||||
AllowPasswordless: true,
|
||||
MOTD: "some-message",
|
||||
},
|
||||
PlayableDatabaseProtocols: []string{"play-able"},
|
||||
UI: webclient.UIConfig{
|
||||
ScrollbackLines: 10,
|
||||
ShowResources: "foo",
|
||||
},
|
||||
Edition: "edition",
|
||||
TunnelPublicAddress: "0000",
|
||||
AutomaticUpgradesTargetVersion: "99",
|
||||
CustomTheme: "theme",
|
||||
CanJoinSessions: true,
|
||||
IsCloud: true,
|
||||
RecoveryCodesEnabled: true,
|
||||
IsDashboard: true,
|
||||
IsUsageBasedBilling: true,
|
||||
AutomaticUpgrades: true,
|
||||
Questionnaire: true,
|
||||
IsStripeManaged: true,
|
||||
PremiumSupport: true,
|
||||
},
|
||||
clusterFeatures: authproto.Features{
|
||||
DeviceTrust: &authproto.DeviceTrustFeature{},
|
||||
AccessRequests: &authproto.AccessRequestsFeature{},
|
||||
AccessList: &authproto.AccessListFeature{},
|
||||
AccessMonitoring: &authproto.AccessMonitoringFeature{},
|
||||
Policy: &authproto.PolicyFeature{},
|
||||
},
|
||||
expected: &webclient.WebConfig{
|
||||
Auth: webclient.WebConfigAuthSettings{
|
||||
LocalAuthEnabled: true,
|
||||
AllowPasswordless: true,
|
||||
MOTD: "some-message",
|
||||
},
|
||||
PlayableDatabaseProtocols: []string{"play-able"},
|
||||
UI: webclient.UIConfig{
|
||||
ScrollbackLines: 10,
|
||||
ShowResources: "foo",
|
||||
},
|
||||
Edition: "edition",
|
||||
TunnelPublicAddress: "0000",
|
||||
AutomaticUpgradesTargetVersion: "99",
|
||||
CustomTheme: "theme",
|
||||
CanJoinSessions: true,
|
||||
IsCloud: true,
|
||||
RecoveryCodesEnabled: true,
|
||||
IsDashboard: true,
|
||||
IsUsageBasedBilling: true,
|
||||
AutomaticUpgrades: true,
|
||||
Questionnaire: true,
|
||||
IsStripeManaged: true,
|
||||
PremiumSupport: true,
|
||||
// Default; not under test
|
||||
ProxyClusterName: "",
|
||||
FeatureLimits: webclient.FeatureLimits{},
|
||||
IsTeam: false,
|
||||
HideInaccessibleFeatures: false,
|
||||
IsIGSEnabled: false,
|
||||
IsPolicyEnabled: false,
|
||||
ExternalAuditStorage: false,
|
||||
JoinActiveSessions: false,
|
||||
AccessRequests: false,
|
||||
TrustedDevices: false,
|
||||
OIDC: false,
|
||||
SAML: false,
|
||||
MobileDeviceManagement: false,
|
||||
Entitlements: map[string]webclient.EntitlementInfo{
|
||||
string(entitlements.AccessLists): {Enabled: true}, // AccessLists had no previous behavior from an enablement perspective; so we default to true
|
||||
string(entitlements.AccessMonitoring): {Enabled: false},
|
||||
string(entitlements.AccessRequests): {Enabled: false},
|
||||
string(entitlements.App): {Enabled: false},
|
||||
string(entitlements.CloudAuditLogRetention): {Enabled: false},
|
||||
string(entitlements.DB): {Enabled: false},
|
||||
string(entitlements.Desktop): {Enabled: false},
|
||||
string(entitlements.DeviceTrust): {Enabled: false},
|
||||
string(entitlements.ExternalAuditStorage): {Enabled: false},
|
||||
string(entitlements.FeatureHiding): {Enabled: false},
|
||||
string(entitlements.HSM): {Enabled: false},
|
||||
string(entitlements.Identity): {Enabled: false},
|
||||
string(entitlements.JoinActiveSessions): {Enabled: false},
|
||||
string(entitlements.K8s): {Enabled: false},
|
||||
string(entitlements.MobileDeviceManagement): {Enabled: false},
|
||||
string(entitlements.OIDC): {Enabled: false},
|
||||
string(entitlements.OktaSCIM): {Enabled: false},
|
||||
string(entitlements.OktaUserSync): {Enabled: false},
|
||||
string(entitlements.Policy): {Enabled: false},
|
||||
string(entitlements.SAML): {Enabled: false},
|
||||
string(entitlements.SessionLocks): {Enabled: false},
|
||||
string(entitlements.UpsellAlert): {Enabled: false},
|
||||
string(entitlements.UsageReporting): {Enabled: false},
|
||||
string(entitlements.LicenseAutoUpdate): {Enabled: false},
|
||||
string(entitlements.AccessGraphDemoMode): {Enabled: false},
|
||||
string(entitlements.UnrestrictedManagedUpdates): {Enabled: false},
|
||||
string(entitlements.ClientIPRestrictions): {Enabled: false},
|
||||
string(entitlements.WorkloadClusters): {Enabled: false},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
setEntitlementsWithLegacyLogic(tt.config, tt.clusterFeatures)
|
||||
|
||||
assert.Equal(t, tt.expected, tt.config)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestPingWithSAMLURL asserts that /webapi/ping and other endpoints necessary
|
||||
// for local user login return successfully even when a SAML connector is
|
||||
// configured with an entity descriptor URL that hangs when requested.
|
||||
|
||||
@@ -20,7 +20,6 @@ package web
|
||||
|
||||
import (
|
||||
"github.com/gravitational/teleport/api/client/proto"
|
||||
"github.com/gravitational/teleport/entitlements"
|
||||
)
|
||||
|
||||
// SetClusterFeatures sets the flags for supported and unsupported features.
|
||||
@@ -30,7 +29,6 @@ func (h *Handler) SetClusterFeatures(features proto.Features) {
|
||||
h.Mutex.Lock()
|
||||
defer h.Mutex.Unlock()
|
||||
|
||||
entitlements.BackfillFeatures(&features)
|
||||
h.clusterFeatures = features
|
||||
}
|
||||
|
||||
|
||||
@@ -62,9 +62,7 @@ func (m *mockedFeatureGetter) setFeatures(f proto.Features) {
|
||||
func TestFeaturesWatcher(t *testing.T) {
|
||||
synctest.Test(t, func(t *testing.T) {
|
||||
mockClient := &mockedFeatureGetter{features: proto.Features{
|
||||
Kubernetes: true,
|
||||
Entitlements: map[string]*proto.EntitlementInfo{},
|
||||
AccessRequests: &proto.AccessRequestsFeature{},
|
||||
Entitlements: map[string]*proto.EntitlementInfo{},
|
||||
}}
|
||||
|
||||
ctx, cancel := context.WithCancel(t.Context())
|
||||
@@ -94,21 +92,15 @@ func TestFeaturesWatcher(t *testing.T) {
|
||||
// after starting the watcher, handler.GetClusterFeatures should return
|
||||
// values matching the client's response
|
||||
features := proto.Features{
|
||||
Kubernetes: true,
|
||||
Entitlements: map[string]*proto.EntitlementInfo{},
|
||||
AccessRequests: &proto.AccessRequestsFeature{},
|
||||
Entitlements: map[string]*proto.EntitlementInfo{},
|
||||
}
|
||||
entitlements.BackfillFeatures(&features)
|
||||
expected := utils.CloneProtoMsg(&features)
|
||||
require.Equal(t, *expected, handler.GetClusterFeatures())
|
||||
|
||||
// update values once again and check if the features are properly updated
|
||||
features = proto.Features{
|
||||
Kubernetes: false,
|
||||
Entitlements: map[string]*proto.EntitlementInfo{},
|
||||
AccessRequests: &proto.AccessRequestsFeature{},
|
||||
Entitlements: map[string]*proto.EntitlementInfo{},
|
||||
}
|
||||
entitlements.BackfillFeatures(&features)
|
||||
mockClient.setFeatures(features)
|
||||
|
||||
time.Sleep(handler.cfg.FeatureWatchInterval)
|
||||
@@ -119,7 +111,6 @@ func TestFeaturesWatcher(t *testing.T) {
|
||||
|
||||
// test updating entitlements
|
||||
features = proto.Features{
|
||||
Kubernetes: true,
|
||||
Entitlements: map[string]*proto.EntitlementInfo{
|
||||
string(entitlements.ExternalAuditStorage): {Enabled: true},
|
||||
string(entitlements.AccessLists): {Enabled: true},
|
||||
@@ -127,16 +118,13 @@ func TestFeaturesWatcher(t *testing.T) {
|
||||
string(entitlements.App): {Enabled: true},
|
||||
string(entitlements.CloudAuditLogRetention): {Enabled: true},
|
||||
},
|
||||
AccessRequests: &proto.AccessRequestsFeature{},
|
||||
}
|
||||
entitlements.BackfillFeatures(&features)
|
||||
mockClient.setFeatures(features)
|
||||
|
||||
time.Sleep(handler.cfg.FeatureWatchInterval)
|
||||
synctest.Wait()
|
||||
|
||||
expected = &proto.Features{
|
||||
Kubernetes: true,
|
||||
Entitlements: map[string]*proto.EntitlementInfo{
|
||||
string(entitlements.ExternalAuditStorage): {Enabled: true},
|
||||
string(entitlements.AccessLists): {Enabled: true},
|
||||
@@ -144,9 +132,7 @@ func TestFeaturesWatcher(t *testing.T) {
|
||||
string(entitlements.App): {Enabled: true},
|
||||
string(entitlements.CloudAuditLogRetention): {Enabled: true},
|
||||
},
|
||||
AccessRequests: &proto.AccessRequestsFeature{},
|
||||
}
|
||||
entitlements.BackfillFeatures(expected)
|
||||
require.Equal(t, *expected, handler.GetClusterFeatures())
|
||||
|
||||
// stop watcher and ensure it stops updating features
|
||||
@@ -154,13 +140,8 @@ func TestFeaturesWatcher(t *testing.T) {
|
||||
synctest.Wait()
|
||||
|
||||
features = proto.Features{
|
||||
Kubernetes: !features.Kubernetes,
|
||||
App: !features.App,
|
||||
DB: true,
|
||||
Entitlements: map[string]*proto.EntitlementInfo{},
|
||||
AccessRequests: &proto.AccessRequestsFeature{},
|
||||
Entitlements: map[string]*proto.EntitlementInfo{},
|
||||
}
|
||||
entitlements.BackfillFeatures(&features)
|
||||
mockClient.setFeatures(features)
|
||||
notExpected := utils.CloneProtoMsg(&features)
|
||||
|
||||
|
||||
@@ -76,26 +76,10 @@ const cfg = {
|
||||
isUsageBasedBilling: false,
|
||||
hideInaccessibleFeatures: false,
|
||||
customTheme: '',
|
||||
/** @deprecated */
|
||||
isTeam: false,
|
||||
isStripeManaged: false,
|
||||
hasQuestionnaire: false,
|
||||
externalAuditStorage: false,
|
||||
premiumSupport: false,
|
||||
accessRequests: false,
|
||||
/** @deprecated Use entitlements instead. */
|
||||
trustedDevices: false,
|
||||
oidc: false,
|
||||
saml: false,
|
||||
/** @deprecated Use entitlements instead. */
|
||||
joinActiveSessions: false,
|
||||
/** @deprecated Use entitlements instead. */
|
||||
mobileDeviceManagement: false,
|
||||
/** @deprecated Use entitlements instead. */
|
||||
isIgsEnabled: false,
|
||||
|
||||
// isPolicyEnabled refers to the Teleport Policy product
|
||||
isPolicyEnabled: false,
|
||||
|
||||
// sessionSummarizerEnabled refers to the AI session summary feature
|
||||
sessionSummarizerEnabled: false,
|
||||
@@ -114,20 +98,19 @@ const cfg = {
|
||||
// see `getNonExactRoutes` for details about non-exact routes
|
||||
nonExactRoutes: [],
|
||||
|
||||
// featureLimits define limits for features.
|
||||
/** @deprecated Use entitlements instead. */
|
||||
featureLimits: {
|
||||
/** @deprecated Use entitlements instead. */
|
||||
accessListCreateLimit: 0,
|
||||
/** @deprecated Use entitlements instead. */
|
||||
accessMonitoringMaxReportRangeLimit: 0,
|
||||
/** @deprecated Use entitlements instead. */
|
||||
AccessRequestMonthlyRequestLimit: 0,
|
||||
},
|
||||
|
||||
// default entitlements to false
|
||||
entitlements: defaultEntitlements,
|
||||
|
||||
/** @deprecated Use entitlements instead; remove in v20 */
|
||||
accessRequests: false,
|
||||
/** @deprecated Use entitlements instead; remove in v20 */
|
||||
oidc: false,
|
||||
/** @deprecated Use entitlements instead; remove in v20 */
|
||||
saml: false,
|
||||
// isPolicyEnabled refers to the Teleport Policy product
|
||||
/** @deprecated Use entitlements instead; remove in v20 */
|
||||
isPolicyEnabled: false,
|
||||
|
||||
ui: {
|
||||
scrollbackLines: 1000,
|
||||
showResources: 'requestable',
|
||||
|
||||
Reference in New Issue
Block a user