Skip to content

Commit

Permalink
Replace isTeam with new flags (#39794)
Browse files Browse the repository at this point in the history
* Add new flags to modules

* Add deprecated comments to isTeam

* Make support flag an enum

* Add a TODO instead of deprecating flag right away

Co-authored-by: Alan Parra <[email protected]>

* Add gogoproto.jsontag to new fields

* remove meaning from enum zero value

* Reuse proto.SupportType instead of suping

* Replace isTeam for new flags in web app

* Update proto file: rename enum 0 value to unspecified;fix json tag casing

* Reuse proto file instead of aliasing it

* undo removing method by accident

* Add MobileDeviceManagement field to web config struct

* Improve comments

* Fix tests

* Simplify lockedFeatures object

* Include JoinActiveSessions in the web config object

* Remove more instances of isTeam

* Apply suggestions from code review - improve comments

Co-authored-by: Michelle Bergquist <[email protected]>

* Use consistent comments to remove isTeam

* Update godocs comments

* Revert removign `isTeam` from stories it still should

* Fix godoc typo

Co-authored-by: Michelle Bergquist <[email protected]>

---------

Co-authored-by: Alan Parra <[email protected]>
Co-authored-by: Michelle Bergquist <[email protected]>
  • Loading branch information
3 people authored Apr 30, 2024
1 parent a9ebad1 commit ddbd9ee
Show file tree
Hide file tree
Showing 19 changed files with 99 additions and 78 deletions.
23 changes: 22 additions & 1 deletion api/client/webclient/webconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,35 @@ type WebConfig struct {
HideInaccessibleFeatures bool `json:"hideInaccessibleFeatures"`
// CustomTheme is a string that represents the name of the custom theme that the WebUI should use.
CustomTheme string `json:"customTheme"`
// IsTeam is true if [Features.ProductType] = Team
// 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"`
// IsIGSEnabled is true if [Features.IdentityGovernance] = true
IsIGSEnabled bool `json:"isIgsEnabled"`
// 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.
FeatureLimits FeatureLimits `json:"featureLimits"`
// Questionnaire indicates whether cluster users should get an onboarding questionnaire
Questionnaire bool `json:"questionnaire"`
// IsStripeManaged indicates if the cluster billing & lifecycle is managed via Stripe
IsStripeManaged bool `json:"isStripeManaged"`
// ExternalAuditStorage indicates whether the EAS feature is enabled in the cluster.
ExternalAuditStorage bool `json:"externalAuditStorage"`
// PremiumSupport indicates whether the customer has premium support
PremiumSupport bool `json:"premiumSupport"`
// JoinActiveSessions indicates whether joining active sessions via web UI is enabled
JoinActiveSessions bool `json:"joinActiveSessions"`
// AccessRequests indicates whether access requests are enabled
AccessRequests bool `json:"accessRequests"`
// TrustedDevices indicates whether trusted devices page is enabled
TrustedDevices bool `json:"trustedDevices"`
// OIDC indicates whether the OIDC integration flow is enabled
OIDC bool `json:"oidc"`
// SAML indicates whether the SAML integration flow is enabled
SAML bool `json:"saml"`
// MobileDeviceManagement indicates whether adding Jamf plugin is enabled
MobileDeviceManagement bool `json:"mobileDeviceManagement"`
}

// featureLimits define limits for features.
Expand Down
2 changes: 1 addition & 1 deletion lib/httplib/httpheaders.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ var indexCSPStringCache *cspCache = newCSPCache()

func getIndexContentSecurityPolicyString(cfg proto.Features, urlPath string) string {
// Check for result with this cfg and urlPath in cache
withStripe := cfg.GetProductType() == proto.ProductType_PRODUCT_TYPE_TEAM
withStripe := cfg.GetIsStripeManaged()
key := fmt.Sprintf("%v-%v", withStripe, urlPath)
if cspString, ok := indexCSPStringCache.get(key); ok {
return cspString
Expand Down
8 changes: 4 additions & 4 deletions lib/httplib/httplib_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,8 +295,8 @@ func TestSetIndexContentSecurityPolicy(t *testing.T) {
},
},
{
name: "for cloud based usage, Team product (with stripe, no wasm)",
features: proto.Features{Cloud: true, IsUsageBased: true, ProductType: proto.ProductType_PRODUCT_TYPE_TEAM},
name: "for cloud based usage, Stripe managed product (with stripe, no wasm)",
features: proto.Features{Cloud: true, IsUsageBased: true, IsStripeManaged: true},
urlPath: "/web/index.js",
expectedCspVals: map[string]string{
"default-src": "'self'",
Expand Down Expand Up @@ -346,8 +346,8 @@ func TestSetIndexContentSecurityPolicy(t *testing.T) {
},
},
{
name: "for cloud based usage & desktop session, Team product (with stripe, with wasm)",
features: proto.Features{Cloud: true, IsUsageBased: true, ProductType: proto.ProductType_PRODUCT_TYPE_TEAM},
name: "for cloud based usage & desktop session, Stripe managed product (with stripe, with wasm)",
features: proto.Features{Cloud: true, IsUsageBased: true, IsStripeManaged: true},
urlPath: "/web/cluster/:clusterId/desktops/:desktopName/:username",
expectedCspVals: map[string]string{
"default-src": "'self'",
Expand Down
2 changes: 1 addition & 1 deletion lib/integrations/externalauditstorage/configurator.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ func NewDraftConfigurator(ctx context.Context, ecaSvc ExternalAuditStorageGetter

func newConfigurator(ctx context.Context, spec *externalauditstorage.ExternalAuditStorageSpec, integrationSvc services.IntegrationsGetter, alertService ClusterAlertService, optFns ...func(*Options)) (*Configurator, error) {
// ExternalAuditStorage is only available in Cloud Enterprise
if !modules.GetModules().Features().Cloud || modules.GetModules().Features().IsTeam() {
if !modules.GetModules().Features().Cloud || !modules.GetModules().Features().ExternalAuditStorage {
return &Configurator{isUsed: false}, nil
}

Expand Down
20 changes: 10 additions & 10 deletions lib/integrations/externalauditstorage/configurator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ func TestConfiguratorIsUsed(t *testing.T) {
name: "cloud enterprise without config",
modules: &modules.TestModules{
TestFeatures: modules.Features{
Cloud: true,
IsUsageBasedBilling: false,
Cloud: true,
ExternalAuditStorage: true,
},
},
wantIsUsed: false,
Expand All @@ -112,8 +112,8 @@ func TestConfiguratorIsUsed(t *testing.T) {
name: "cloud enterprise with only draft",
modules: &modules.TestModules{
TestFeatures: modules.Features{
Cloud: true,
IsUsageBasedBilling: false,
Cloud: true,
ExternalAuditStorage: true,
},
},
// Just create draft, External Audit Storage should be disabled, it's
Expand All @@ -129,8 +129,8 @@ func TestConfiguratorIsUsed(t *testing.T) {
name: "cloud enterprise with cluster config",
modules: &modules.TestModules{
TestFeatures: modules.Features{
Cloud: true,
IsUsageBasedBilling: false,
Cloud: true,
ExternalAuditStorage: true,
},
},
// Create draft and promote it to cluster.
Expand Down Expand Up @@ -178,8 +178,8 @@ func TestCredentialsCache(t *testing.T) {

modules.SetTestModules(t, &modules.TestModules{
TestFeatures: modules.Features{
Cloud: true,
IsUsageBasedBilling: false,
Cloud: true,
ExternalAuditStorage: true,
},
})

Expand Down Expand Up @@ -316,8 +316,8 @@ func TestDraftConfigurator(t *testing.T) {

modules.SetTestModules(t, &modules.TestModules{
TestFeatures: modules.Features{
Cloud: true,
IsUsageBasedBilling: false,
Cloud: true,
ExternalAuditStorage: true,
},
})

Expand Down
2 changes: 1 addition & 1 deletion lib/modules/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ func (f Features) IGSEnabled() bool {
return f.IdentityGovernanceSecurity
}

// TODO(mcbattirola): Deprecate IsTeam once it's unused.
// TODO(mcbattirola): remove isTeam when it is no longer used
func (f Features) IsTeam() bool {
return f.ProductType == ProductTypeTeam
}
Expand Down
3 changes: 2 additions & 1 deletion lib/service/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,8 @@ func TestAthenaAuditLogSetup(t *testing.T) {
ctx := context.Background()
modules.SetTestModules(t, &modules.TestModules{
TestFeatures: modules.Features{
Cloud: true,
Cloud: true,
ExternalAuditStorage: true,
},
})

Expand Down
16 changes: 15 additions & 1 deletion lib/web/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1667,6 +1667,9 @@ func (h *Handler) getWebConfig(w http.ResponseWriter, r *http.Request, p httprou
h.log.WithError(err).Error("Cannot read target version")
}

// TODO(mcbattirola): remove isTeam when it is no longer used
isTeam := clusterFeatures.GetProductType() == proto.ProductType_PRODUCT_TYPE_TEAM

webCfg := webclient.WebConfig{
Auth: authSettings,
CanJoinSessions: canJoinSessions,
Expand All @@ -1681,13 +1684,24 @@ func (h *Handler) getWebConfig(w http.ResponseWriter, r *http.Request, p httprou
AssistEnabled: assistEnabled,
HideInaccessibleFeatures: clusterFeatures.GetFeatureHiding(),
CustomTheme: clusterFeatures.GetCustomTheme(),
IsTeam: clusterFeatures.GetProductType() == proto.ProductType_PRODUCT_TYPE_TEAM,
IsIGSEnabled: clusterFeatures.GetIdentityGovernance(),
FeatureLimits: webclient.FeatureLimits{
AccessListCreateLimit: int(clusterFeatures.GetAccessList().GetCreateLimit()),
AccessMonitoringMaxReportRangeLimit: int(clusterFeatures.GetAccessMonitoring().GetMaxReportRangeLimit()),
AccessRequestMonthlyRequestLimit: int(clusterFeatures.GetAccessRequests().GetMonthlyRequestLimit()),
},
Questionnaire: clusterFeatures.GetQuestionnaire(),
IsStripeManaged: clusterFeatures.GetIsStripeManaged(),
ExternalAuditStorage: clusterFeatures.GetExternalAuditStorage(),
PremiumSupport: clusterFeatures.GetSupportType() == proto.SupportType_SUPPORT_TYPE_PREMIUM,
AccessRequests: clusterFeatures.GetAccessRequests().MonthlyRequestLimit > 0,
TrustedDevices: clusterFeatures.GetDeviceTrust().GetEnabled(),
OIDC: clusterFeatures.GetOIDC(),
SAML: clusterFeatures.GetSAML(),
MobileDeviceManagement: clusterFeatures.GetMobileDeviceManagement(),
JoinActiveSessions: clusterFeatures.GetJoinActiveSessions(),
// TODO(mcbattirola): remove isTeam when it is no longer used
IsTeam: isTeam,
}

resource, err := h.cfg.ProxyClient.GetClusterName()
Expand Down
22 changes: 15 additions & 7 deletions lib/web/apiserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4497,11 +4497,12 @@ func TestGetWebConfig(t *testing.T) {
PrivateKeyPolicy: keys.PrivateKeyPolicyNone,
MOTD: MOTD,
},
CanJoinSessions: true,
ProxyClusterName: env.server.ClusterName(),
IsCloud: false,
AssistEnabled: false,
AutomaticUpgrades: false,
CanJoinSessions: true,
ProxyClusterName: env.server.ClusterName(),
IsCloud: false,
AssistEnabled: false,
AutomaticUpgrades: false,
JoinActiveSessions: true,
}

// Make a request.
Expand Down Expand Up @@ -4551,6 +4552,7 @@ func TestGetWebConfig(t *testing.T) {
expectedCfg.AutomaticUpgrades = true
expectedCfg.AutomaticUpgradesTargetVersion = "v" + teleport.Version
expectedCfg.AssistEnabled = false
expectedCfg.JoinActiveSessions = false

// request and verify enabled features are enabled.
re, err = clt.Get(ctx, endpoint, nil)
Expand Down Expand Up @@ -4603,6 +4605,9 @@ func TestGetWebConfig_IGSFeatureLimits(t *testing.T) {
AccessMonitoring: modules.AccessMonitoringFeature{
MaxReportRangeLimit: 10,
},
IsUsageBasedBilling: true,
IsStripeManaged: true,
Questionnaire: true,
},
})

Expand All @@ -4619,8 +4624,11 @@ func TestGetWebConfig_IGSFeatureLimits(t *testing.T) {
AccessListCreateLimit: 5,
AccessMonitoringMaxReportRangeLimit: 10,
},
IsTeam: true,
IsIGSEnabled: true,
IsTeam: true,
IsIGSEnabled: true,
IsStripeManaged: true,
Questionnaire: true,
IsUsageBasedBilling: true,
}

// Make a request.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function IntegrationTiles({
hasIntegrationAccess?: boolean;
hasExternalAuditStorage?: boolean;
}) {
const isCloudEnterprise = cfg.isCloud && !cfg.isTeam;
const externalAuditStorageEnabled = cfg.externalAuditStorage;
const isOnpremEnterprise = cfg.isEnterprise && !cfg.isCloud;

return (
Expand Down Expand Up @@ -76,7 +76,7 @@ export function IntegrationTiles({
</IntegrationTile>
{!isOnpremEnterprise && (
<IntegrationTile
disabled={!hasExternalAuditStorage || !isCloudEnterprise}
disabled={!hasExternalAuditStorage || !externalAuditStorageEnabled}
as={hasExternalAuditStorage ? Link : null}
to={
hasExternalAuditStorage
Expand All @@ -93,7 +93,7 @@ export function IntegrationTiles({
<Text>AWS External Audit Storage</Text>
{renderExternalAuditStorageBadge(
hasExternalAuditStorage,
isCloudEnterprise
externalAuditStorageEnabled
)}
</IntegrationTile>
)}
Expand Down
3 changes: 2 additions & 1 deletion web/packages/teleport/src/Sessions/useSessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { context, trace } from '@opentelemetry/api';
import { Session } from 'teleport/services/session';

import Ctx from 'teleport/teleportContext';
import cfg from 'teleport/config';

const tracer = trace.getTracer('userSessions');

Expand Down Expand Up @@ -57,6 +58,6 @@ export default function useSessions(ctx: Ctx, clusterId: string) {
sessions,
// moderated is available with any enterprise editions
showModeratedSessionsCTA: !ctx.isEnterprise,
showActiveSessionsCTA: ctx.lockedFeatures.activeSessions,
showActiveSessionsCTA: !cfg.joinActiveSessions,
};
}
2 changes: 1 addition & 1 deletion web/packages/teleport/src/Support/Support.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function SupportContainer({ children }: { children?: React.ReactNode }) {
const cluster = ctx.storeUser.state.cluster;

// showCTA returns the premium support value for enterprise customers and true for OSS users
const showCTA = cfg.isEnterprise ? ctx.lockedFeatures.premiumSupport : true;
const showCTA = cfg.isEnterprise ? !cfg.premiumSupport : true;

return (
<Support
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,12 @@ import { CtaEvent, userEventService } from 'teleport/services/userEvent';

import { ButtonLockedFeature } from './ButtonLockedFeature';

const defaultIsTeamFlag = cfg.isTeam;
const defaultIsEnterpriseFlag = cfg.isEnterprise;

describe('buttonLockedFeature', () => {
afterEach(() => {
jest.resetAllMocks();

cfg.isTeam = defaultIsTeamFlag;
cfg.isEnterprise = defaultIsEnterpriseFlag;
});

Expand All @@ -59,35 +57,8 @@ describe('buttonLockedFeature', () => {
expect(screen.queryByTestId('locked-icon')).not.toBeInTheDocument();
});

test('it has upgrade-team href for Team Plan', () => {
const version = ctx.storeUser.state.cluster.authVersion;
cfg.isTeam = true;
cfg.isEnterprise = true;

renderWithContext(
<ButtonLockedFeature noIcon={true}>text</ButtonLockedFeature>
);
expect(screen.getByText('text').closest('a')).toHaveAttribute(
'href',
`https://goteleport.com/r/upgrade-team?e_${version}&utm_campaign=CTA_UNSPECIFIED`
);

renderWithContext(
<ButtonLockedFeature noIcon={true} event={CtaEvent.CTA_ACCESS_REQUESTS}>
othertext
</ButtonLockedFeature>
);
expect(screen.getByText('othertext').closest('a')).toHaveAttribute(
'href',
`https://goteleport.com/r/upgrade-team?e_${version}&utm_campaign=${
CtaEvent[CtaEvent.CTA_ACCESS_REQUESTS]
}`
);
});

test('it has upgrade-community href for community edition', () => {
const version = ctx.storeUser.state.cluster.authVersion;
cfg.isTeam = false;
cfg.isEnterprise = false;
renderWithContext(
<ButtonLockedFeature noIcon={true}>text</ButtonLockedFeature>,
Expand Down Expand Up @@ -118,7 +89,6 @@ describe('buttonLockedFeature', () => {

test('it has upgrade-igs href for Enterprise + IGS Plan', () => {
const version = ctx.storeUser.state.cluster.authVersion;
cfg.isTeam = false;
cfg.isEnterprise = true;

renderWithContext(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ describe('externalAuditStorageCta', () => {
});

cfg.isCloud = isCloud;
ctx.lockedFeatures.externalCloudAudit = lockedFeature;
cfg.externalAuditStorage = lockedFeature;

jest
.spyOn(storageService, 'getExternalAuditStorageCtaDisabled')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import { ButtonLockedFeature } from '../ButtonLockedFeature';
export const ExternalAuditStorageCta = () => {
const [showCta, setShowCta] = useState<boolean>(false);
const ctx = useTeleport();
const featureEnabled = !ctx.lockedFeatures.externalCloudAudit;
const featureEnabled = !cfg.externalAuditStorage;
const userHasAccess = ctx.getFeatureFlags().enrollIntegrationsOrPlugins;

useEffect(() => {
Expand Down
17 changes: 15 additions & 2 deletions web/packages/teleport/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,29 @@ const cfg = {
// IsUsageBasedBilling determines if the user subscription is usage-based (pay-as-you-go).
// Historically, this flag used to refer to "Cloud Team" product,
// but with the new EUB (Enterprise Usage Based) product, it can mean either EUB or Team.
// Use the `isTeam` config flag to determine if product used is Team.
// EUB can be determined from a combination of existing config flags eg: `isCloud && !isTeam`.
// Prefer using feature flags to determine if something should be enabled or not.
// If you have no other options, use the `isStripeManaged` config flag to determine if product used is Team.
// EUB can be determined from a combination of existing config flags eg: `isUsageBasedBilling && !isStripeManaged`.
isUsageBasedBilling: false,
hideInaccessibleFeatures: false,
customTheme: '',
/**
* isTeam is true if [Features.ProductType] == Team
* @deprecated use other flags do determine cluster features istead of relying on isTeam
* TODO(mcbattirola): remove isTeam when it is no longer used
*/
isTeam: false,
isStripeManaged: false,
hasQuestionnaire: false,
externalAuditStorage: false,
premiumSupport: false,
accessRequests: false,
trustedDevices: false,
oidc: false,
saml: false,
joinActiveSessions: false,
mobileDeviceManagement: false,

// isIgsEnabled refers to Identity Governance & Security product.
// It refers to a group of features: access request, device trust,
// access list, and access monitoring.
Expand Down
Loading

0 comments on commit ddbd9ee

Please sign in to comment.