diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index 8795226d2f284..1410c6921504f 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -1665,6 +1665,7 @@ func (h *Handler) getWebConfig(w http.ResponseWriter, r *http.Request, p httprou RecoveryCodesEnabled: clusterFeatures.GetRecoveryCodes(), UI: h.getUIConfig(r.Context()), IsDashboard: services.IsDashboard(clusterFeatures), + IsTeam: false, IsUsageBasedBilling: clusterFeatures.GetIsUsageBased(), AutomaticUpgrades: automaticUpgradesEnabled, AutomaticUpgradesTargetVersion: automaticUpgradesTargetVersion, @@ -1672,29 +1673,12 @@ func (h *Handler) getWebConfig(w http.ResponseWriter, r *http.Request, p httprou Questionnaire: clusterFeatures.GetQuestionnaire(), IsStripeManaged: clusterFeatures.GetIsStripeManaged(), PremiumSupport: clusterFeatures.GetSupportType() == proto.SupportType_SUPPORT_TYPE_PREMIUM, - Entitlements: GetWebCfgEntitlements(clusterFeatures.GetEntitlements()), PlayableDatabaseProtocols: player.SupportedDatabaseProtocols, - - // Set legacy fields - // TODO(mcbattirola): remove isTeam when it is no longer used - IsTeam: clusterFeatures.GetProductType() == proto.ProductType_PRODUCT_TYPE_TEAM, - AccessRequests: modules.GetProtoEntitlement(&clusterFeatures, entitlements.AccessRequests).Enabled, - ExternalAuditStorage: modules.GetProtoEntitlement(&clusterFeatures, entitlements.ExternalAuditStorage).Enabled, - HideInaccessibleFeatures: modules.GetProtoEntitlement(&clusterFeatures, entitlements.FeatureHiding).Enabled, - IsIGSEnabled: modules.GetProtoEntitlement(&clusterFeatures, entitlements.Identity).Enabled, - IsPolicyEnabled: modules.GetProtoEntitlement(&clusterFeatures, entitlements.Policy).Enabled, - JoinActiveSessions: modules.GetProtoEntitlement(&clusterFeatures, entitlements.JoinActiveSessions).Enabled, - MobileDeviceManagement: modules.GetProtoEntitlement(&clusterFeatures, entitlements.MobileDeviceManagement).Enabled, - OIDC: modules.GetProtoEntitlement(&clusterFeatures, entitlements.OIDC).Enabled, - SAML: modules.GetProtoEntitlement(&clusterFeatures, entitlements.SAML).Enabled, - TrustedDevices: modules.GetProtoEntitlement(&clusterFeatures, entitlements.DeviceTrust).Enabled, - 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), - }, } + // Set entitlements with backwards field compatibility + setEntitlementsWithLegacyLogic(&webCfg, clusterFeatures) + resource, err := h.cfg.ProxyClient.GetClusterName() if err != nil { h.log.WithError(err).Warn("Failed to query cluster name.") @@ -1711,6 +1695,84 @@ func (h *Handler) getWebConfig(w http.ResponseWriter, r *http.Request, p httprou return nil, 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 clusterFeatures.GetEntitlements() != nil && len(clusterFeatures.GetEntitlements()) > 0 { + // webCfg.Entitlements: No update as they are set above + // webCfg.: 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().MonthlyRequestLimit > 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.: set equal to legacy feature value + webCfg.AccessRequests = clusterFeatures.GetAccessRequests().MonthlyRequestLimit > 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 diff --git a/lib/web/apiserver_test.go b/lib/web/apiserver_test.go index 0448541253976..07653014f8f8d 100644 --- a/lib/web/apiserver_test.go +++ b/lib/web/apiserver_test.go @@ -78,6 +78,8 @@ import ( "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + clientproto "github.com/gravitational/teleport/api/client/proto" + "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/breaker" authproto "github.com/gravitational/teleport/api/client/proto" @@ -4515,7 +4517,7 @@ func TestApplicationWebSessionsDeletedAfterLogout(t *testing.T) { require.Empty(t, collectAppSessions(context.Background())) } -func TestGetWebConfig(t *testing.T) { +func TestGetWebConfig_WithEntitlements(t *testing.T) { ctx := context.Background() env := newWebPack(t, 1) @@ -4571,29 +4573,29 @@ func TestGetWebConfig(t *testing.T) { JoinActiveSessions: true, Edition: modules.BuildOSS, // testBuildType is empty Entitlements: map[string]webclient.EntitlementInfo{ - "AccessLists": {Enabled: false}, - "AccessMonitoring": {Enabled: false}, - "AccessRequests": {Enabled: false}, - "App": {Enabled: true}, - "CloudAuditLogRetention": {Enabled: false}, - "DB": {Enabled: true}, - "Desktop": {Enabled: true}, - "DeviceTrust": {Enabled: false}, - "ExternalAuditStorage": {Enabled: false}, - "FeatureHiding": {Enabled: false}, - "HSM": {Enabled: false}, - "Identity": {Enabled: false}, - "JoinActiveSessions": {Enabled: true}, - "K8s": {Enabled: true}, - "MobileDeviceManagement": {Enabled: false}, - "OIDC": {Enabled: false}, - "OktaSCIM": {Enabled: false}, - "OktaUserSync": {Enabled: false}, - "Policy": {Enabled: false}, - "SAML": {Enabled: false}, - "SessionLocks": {Enabled: false}, - "UpsellAlert": {Enabled: false}, - "UsageReporting": {Enabled: false}, + string(entitlements.AccessLists): {Enabled: false}, + string(entitlements.AccessMonitoring): {Enabled: false}, + string(entitlements.AccessRequests): {Enabled: false}, + string(entitlements.App): {Enabled: true}, + string(entitlements.CloudAuditLogRetention): {Enabled: false}, + string(entitlements.DB): {Enabled: true}, + string(entitlements.Desktop): {Enabled: true}, + 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: true}, + string(entitlements.K8s): {Enabled: true}, + 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}, }, TunnelPublicAddress: "", RecoveryCodesEnabled: false, @@ -4655,12 +4657,12 @@ func TestGetWebConfig(t *testing.T) { expectedCfg.JoinActiveSessions = false expectedCfg.Edition = "" // testBuildType is empty expectedCfg.TrustedDevices = true - expectedCfg.Entitlements["App"] = webclient.EntitlementInfo{Enabled: false} - expectedCfg.Entitlements["DB"] = webclient.EntitlementInfo{Enabled: true, Limit: 22} - expectedCfg.Entitlements["DeviceTrust"] = webclient.EntitlementInfo{Enabled: true, Limit: 33} - expectedCfg.Entitlements["Desktop"] = webclient.EntitlementInfo{Enabled: true, Limit: 44} - expectedCfg.Entitlements["JoinActiveSessions"] = webclient.EntitlementInfo{Enabled: false} - expectedCfg.Entitlements["K8s"] = webclient.EntitlementInfo{Enabled: false} + 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} + expectedCfg.Entitlements[string(entitlements.Desktop)] = webclient.EntitlementInfo{Enabled: true, Limit: 44} + expectedCfg.Entitlements[string(entitlements.JoinActiveSessions)] = webclient.EntitlementInfo{Enabled: false} + expectedCfg.Entitlements[string(entitlements.K8s)] = webclient.EntitlementInfo{Enabled: false} // request and verify enabled features are enabled. re, err = clt.Get(ctx, endpoint, nil) @@ -4681,9 +4683,9 @@ func TestGetWebConfig(t *testing.T) { env.proxies[0].client = mockClient expectedCfg.AutomaticUpgrades = false expectedCfg.TrustedDevices = false - expectedCfg.Entitlements["DB"] = webclient.EntitlementInfo{Enabled: false} - expectedCfg.Entitlements["Desktop"] = webclient.EntitlementInfo{Enabled: false} - expectedCfg.Entitlements["DeviceTrust"] = webclient.EntitlementInfo{Enabled: 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} // update modules but NOT the expected config modules.SetTestModules(t, &modules.TestModules{ @@ -4703,7 +4705,7 @@ func TestGetWebConfig(t *testing.T) { require.Equal(t, expectedCfg, cfg) } -func TestGetWebConfig_LegacyIdentityFeatureLimits(t *testing.T) { +func TestGetWebConfig_LegacyFeatureLimits(t *testing.T) { ctx := context.Background() env := newWebPack(t, 1) @@ -10514,3 +10516,490 @@ func TestUnstartedServerShutdown(t *testing.T) { // Shutdown the server before starting it shouldn't panic. 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: &clientproto.AccessListFeature{ + CreateLimit: 10, + }, + AccessMonitoring: &clientproto.AccessMonitoringFeature{ + Enabled: false, + MaxReportRangeLimit: 20, + }, + AccessMonitoringConfigured: false, + AccessRequests: &clientproto.AccessRequestsFeature{ + MonthlyRequestLimit: 30, + }, + AdvancedAccessWorkflows: false, + App: false, + Assist: false, + AutomaticUpgrades: false, + Cloud: false, + CustomTheme: "theme", + DB: false, + Desktop: false, + DeviceTrust: &clientproto.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}, + }, + }, + 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}, + }, + }, + }, + { + 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: &clientproto.AccessRequestsFeature{ + MonthlyRequestLimit: 88, + }, + AccessList: &clientproto.AccessListFeature{ + CreateLimit: 88, + }, + AccessMonitoring: &clientproto.AccessMonitoringFeature{ + Enabled: true, + MaxReportRangeLimit: 88, + }, + DeviceTrust: &clientproto.DeviceTrustFeature{ + Enabled: true, + DevicesUsageLimit: 88, + }, + Policy: &clientproto.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}, + + // 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: &clientproto.AccessRequestsFeature{ + MonthlyRequestLimit: 88, + }, + AccessList: &clientproto.AccessListFeature{ + CreateLimit: 88, + }, + AccessMonitoring: &clientproto.AccessMonitoringFeature{ + Enabled: true, + MaxReportRangeLimit: 88, + }, + DeviceTrust: &clientproto.DeviceTrustFeature{ + Enabled: true, + DevicesUsageLimit: 88, + }, + Policy: &clientproto.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}, + // 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}, + }, + }, + }, + { + 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: &clientproto.DeviceTrustFeature{}, + AccessRequests: &clientproto.AccessRequestsFeature{}, + AccessList: &clientproto.AccessListFeature{}, + AccessMonitoring: &clientproto.AccessMonitoringFeature{}, + Policy: &clientproto.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}, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + setEntitlementsWithLegacyLogic(tt.config, tt.clusterFeatures) + + assert.Equal(t, tt.expected, tt.config) + }) + } +}