mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-10 20:02:26 +00:00
refactor is licensed and subscribed util functions
This commit is contained in:
@@ -107,6 +107,11 @@ export class Config {
|
||||
process.env.MAXMIND_ASN_PATH = parsedConfig.server.maxmind_asn_path;
|
||||
}
|
||||
|
||||
process.env.DISABLE_ENTERPRISE_FEATURES = parsedConfig.flags
|
||||
?.disable_enterprise_features
|
||||
? "true"
|
||||
: "false";
|
||||
|
||||
this.rawConfig = parsedConfig;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
export async function isLicensedOrSubscribed(orgId: string): Promise<boolean> {
|
||||
export async function isLicensedOrSubscribed(
|
||||
orgId: string,
|
||||
tiers: string[]
|
||||
): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
export async function isSubscribed(orgId: string): Promise<boolean> {
|
||||
export async function isSubscribed(
|
||||
orgId: string,
|
||||
tiers: string[]
|
||||
): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -331,7 +331,8 @@ export const configSchema = z
|
||||
disable_local_sites: z.boolean().optional(),
|
||||
disable_basic_wireguard_sites: z.boolean().optional(),
|
||||
disable_config_managed_domains: z.boolean().optional(),
|
||||
disable_product_help_banners: z.boolean().optional()
|
||||
disable_product_help_banners: z.boolean().optional(),
|
||||
disable_enterprise_features: z.boolean().optional()
|
||||
})
|
||||
.optional(),
|
||||
dns: z
|
||||
|
||||
@@ -78,6 +78,8 @@ export async function checkOrgAccessPolicy(
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: check that the org is subscribed
|
||||
|
||||
// get the needed data
|
||||
|
||||
if (!props.org) {
|
||||
|
||||
@@ -13,16 +13,18 @@
|
||||
|
||||
import { build } from "@server/build";
|
||||
import license from "#private/license/license";
|
||||
import { getOrgTierData } from "#private/lib/billing";
|
||||
import { isSubscribed } from "#private/lib/isSubscribed";
|
||||
|
||||
export async function isLicensedOrSubscribed(orgId: string): Promise<boolean> {
|
||||
export async function isLicensedOrSubscribed(
|
||||
orgId: string,
|
||||
tiers: string[]
|
||||
): Promise<boolean> {
|
||||
if (build === "enterprise") {
|
||||
return await license.isUnlocked();
|
||||
}
|
||||
|
||||
if (build === "saas") {
|
||||
const { tier, active } = await getOrgTierData(orgId);
|
||||
return (tier == "tier1" || tier == "tier2" || tier == "tier3") && active;
|
||||
return isSubscribed(orgId, tiers);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -14,10 +14,14 @@
|
||||
import { build } from "@server/build";
|
||||
import { getOrgTierData } from "#private/lib/billing";
|
||||
|
||||
export async function isSubscribed(orgId: string): Promise<boolean> {
|
||||
export async function isSubscribed(
|
||||
orgId: string,
|
||||
tiers: string[]
|
||||
): Promise<boolean> {
|
||||
if (build === "saas") {
|
||||
const { tier, active } = await getOrgTierData(orgId);
|
||||
return (tier == "tier1" || tier == "tier2" || tier == "tier3") && active;
|
||||
const isTier = (tier && tiers.includes(tier)) || false;
|
||||
return active && isTier;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -17,44 +17,51 @@ import HttpCode from "@server/types/HttpCode";
|
||||
import { build } from "@server/build";
|
||||
import { getOrgTierData } from "#private/lib/billing";
|
||||
|
||||
export async function verifyValidSubscription(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
try {
|
||||
if (build != "saas") {
|
||||
export function verifyValidSubscription(tiers: string[]) {
|
||||
return async function (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
): Promise<any> {
|
||||
try {
|
||||
if (build != "saas") {
|
||||
return next();
|
||||
}
|
||||
|
||||
const orgId =
|
||||
req.params.orgId ||
|
||||
req.body.orgId ||
|
||||
req.query.orgId ||
|
||||
req.userOrgId;
|
||||
|
||||
if (!orgId) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
"Organization ID is required to verify subscription"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const { tier, active } = await getOrgTierData(orgId);
|
||||
const isTier = tiers.includes(tier || "");
|
||||
if (!isTier || !active) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.FORBIDDEN,
|
||||
"Organization does not have an active subscription"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return next();
|
||||
}
|
||||
|
||||
const orgId = req.params.orgId || req.body.orgId || req.query.orgId || req.userOrgId;
|
||||
|
||||
if (!orgId) {
|
||||
} catch (e) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.BAD_REQUEST,
|
||||
"Organization ID is required to verify subscription"
|
||||
HttpCode.INTERNAL_SERVER_ERROR,
|
||||
"Error verifying subscription"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const { tier, active } = await getOrgTierData(orgId);
|
||||
if ((tier == "tier1" || tier == "tier2" || tier == "tier3") && active) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.FORBIDDEN,
|
||||
"Organization does not have an active subscription"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return next();
|
||||
} catch (e) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.INTERNAL_SERVER_ERROR,
|
||||
"Error verifying subscription"
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ authenticated.put(
|
||||
authenticated.post(
|
||||
"/org/:orgId/idp/:idpId/oidc",
|
||||
verifyValidLicense,
|
||||
verifyValidSubscription,
|
||||
verifyValidSubscription(),
|
||||
verifyOrgAccess,
|
||||
verifyIdpAccess,
|
||||
verifyUserHasAction(ActionsEnum.updateIdp),
|
||||
|
||||
@@ -195,27 +195,29 @@ export default function CredentialsPage() {
|
||||
</Alert>
|
||||
)}
|
||||
</SettingsSectionBody>
|
||||
<SettingsSectionFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
setShouldDisconnect(false);
|
||||
setModalOpen(true);
|
||||
}}
|
||||
disabled={isSecurityFeatureDisabled()}
|
||||
>
|
||||
{t("regenerateCredentialsButton")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setShouldDisconnect(true);
|
||||
setModalOpen(true);
|
||||
}}
|
||||
disabled={isSecurityFeatureDisabled()}
|
||||
>
|
||||
{t("remoteExitNodeRegenerateAndDisconnect")}
|
||||
</Button>
|
||||
</SettingsSectionFooter>
|
||||
{!env.flags.disableEnterpriseFeatures && (
|
||||
<SettingsSectionFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
setShouldDisconnect(false);
|
||||
setModalOpen(true);
|
||||
}}
|
||||
disabled={isSecurityFeatureDisabled()}
|
||||
>
|
||||
{t("regenerateCredentialsButton")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setShouldDisconnect(true);
|
||||
setModalOpen(true);
|
||||
}}
|
||||
disabled={isSecurityFeatureDisabled()}
|
||||
>
|
||||
{t("remoteExitNodeRegenerateAndDisconnect")}
|
||||
</Button>
|
||||
</SettingsSectionFooter>
|
||||
)}
|
||||
</SettingsSection>
|
||||
</SettingsContainer>
|
||||
|
||||
|
||||
@@ -183,27 +183,29 @@ export default function CredentialsPage() {
|
||||
</Alert>
|
||||
)}
|
||||
</SettingsSectionBody>
|
||||
<SettingsSectionFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
setShouldDisconnect(false);
|
||||
setModalOpen(true);
|
||||
}}
|
||||
disabled={isSecurityFeatureDisabled()}
|
||||
>
|
||||
{t("regenerateCredentialsButton")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setShouldDisconnect(true);
|
||||
setModalOpen(true);
|
||||
}}
|
||||
disabled={isSecurityFeatureDisabled()}
|
||||
>
|
||||
{t("clientRegenerateAndDisconnect")}
|
||||
</Button>
|
||||
</SettingsSectionFooter>
|
||||
{!env.flags.disableEnterpriseFeatures && (
|
||||
<SettingsSectionFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
setShouldDisconnect(false);
|
||||
setModalOpen(true);
|
||||
}}
|
||||
disabled={isSecurityFeatureDisabled()}
|
||||
>
|
||||
{t("regenerateCredentialsButton")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setShouldDisconnect(true);
|
||||
setModalOpen(true);
|
||||
}}
|
||||
disabled={isSecurityFeatureDisabled()}
|
||||
>
|
||||
{t("clientRegenerateAndDisconnect")}
|
||||
</Button>
|
||||
</SettingsSectionFooter>
|
||||
)}
|
||||
</SettingsSection>
|
||||
|
||||
<OlmInstallCommands
|
||||
|
||||
@@ -152,6 +152,7 @@ export default function GeneralPage() {
|
||||
const [approvalId, setApprovalId] = useState<number | null>(null);
|
||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||
const [, startTransition] = useTransition();
|
||||
const { env } = useEnvContext();
|
||||
|
||||
const showApprovalFeatures = build !== "oss" && isPaidUser;
|
||||
|
||||
@@ -567,231 +568,246 @@ export default function GeneralPage() {
|
||||
</SettingsSection>
|
||||
)}
|
||||
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
{t("deviceSecurity")}
|
||||
</SettingsSectionTitle>
|
||||
<SettingsSectionDescription>
|
||||
{t("deviceSecurityDescription")}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
{!env.flags.disableEnterpriseFeatures && (
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
{t("deviceSecurity")}
|
||||
</SettingsSectionTitle>
|
||||
<SettingsSectionDescription>
|
||||
{t("deviceSecurityDescription")}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
|
||||
<SettingsSectionBody>
|
||||
<PaidFeaturesAlert />
|
||||
{client.posture &&
|
||||
Object.keys(client.posture).length > 0 ? (
|
||||
<>
|
||||
<InfoSections cols={3}>
|
||||
{client.posture.biometricsEnabled !== null &&
|
||||
client.posture.biometricsEnabled !==
|
||||
undefined && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("biometricsEnabled")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{isPaidUser
|
||||
? formatPostureValue(
|
||||
client.posture
|
||||
.biometricsEnabled
|
||||
)
|
||||
: "-"}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
<SettingsSectionBody>
|
||||
<PaidFeaturesAlert />
|
||||
{client.posture &&
|
||||
Object.keys(client.posture).length > 0 ? (
|
||||
<>
|
||||
<InfoSections cols={3}>
|
||||
{client.posture.biometricsEnabled !==
|
||||
null &&
|
||||
client.posture.biometricsEnabled !==
|
||||
undefined && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("biometricsEnabled")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{isPaidUser
|
||||
? formatPostureValue(
|
||||
client.posture
|
||||
.biometricsEnabled
|
||||
)
|
||||
: "-"}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
|
||||
{client.posture.diskEncrypted !== null &&
|
||||
client.posture.diskEncrypted !==
|
||||
undefined && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("diskEncrypted")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{isPaidUser
|
||||
? formatPostureValue(
|
||||
client.posture
|
||||
.diskEncrypted
|
||||
)
|
||||
: "-"}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
{client.posture.diskEncrypted !== null &&
|
||||
client.posture.diskEncrypted !==
|
||||
undefined && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("diskEncrypted")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{isPaidUser
|
||||
? formatPostureValue(
|
||||
client.posture
|
||||
.diskEncrypted
|
||||
)
|
||||
: "-"}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
|
||||
{client.posture.firewallEnabled !== null &&
|
||||
client.posture.firewallEnabled !==
|
||||
undefined && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("firewallEnabled")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{isPaidUser
|
||||
? formatPostureValue(
|
||||
client.posture
|
||||
.firewallEnabled
|
||||
)
|
||||
: "-"}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
{client.posture.firewallEnabled !== null &&
|
||||
client.posture.firewallEnabled !==
|
||||
undefined && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("firewallEnabled")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{isPaidUser
|
||||
? formatPostureValue(
|
||||
client.posture
|
||||
.firewallEnabled
|
||||
)
|
||||
: "-"}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
|
||||
{client.posture.autoUpdatesEnabled !== null &&
|
||||
client.posture.autoUpdatesEnabled !==
|
||||
undefined && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("autoUpdatesEnabled")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{isPaidUser
|
||||
? formatPostureValue(
|
||||
client.posture
|
||||
.autoUpdatesEnabled
|
||||
)
|
||||
: "-"}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
{client.posture.autoUpdatesEnabled !==
|
||||
null &&
|
||||
client.posture.autoUpdatesEnabled !==
|
||||
undefined && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("autoUpdatesEnabled")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{isPaidUser
|
||||
? formatPostureValue(
|
||||
client.posture
|
||||
.autoUpdatesEnabled
|
||||
)
|
||||
: "-"}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
|
||||
{client.posture.tpmAvailable !== null &&
|
||||
client.posture.tpmAvailable !==
|
||||
undefined && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("tpmAvailable")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{isPaidUser
|
||||
? formatPostureValue(
|
||||
client.posture
|
||||
.tpmAvailable
|
||||
)
|
||||
: "-"}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
{client.posture.tpmAvailable !== null &&
|
||||
client.posture.tpmAvailable !==
|
||||
undefined && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("tpmAvailable")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{isPaidUser
|
||||
? formatPostureValue(
|
||||
client.posture
|
||||
.tpmAvailable
|
||||
)
|
||||
: "-"}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
|
||||
{client.posture.windowsAntivirusEnabled !==
|
||||
null &&
|
||||
client.posture.windowsAntivirusEnabled !==
|
||||
undefined && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("windowsAntivirusEnabled")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{isPaidUser
|
||||
? formatPostureValue(
|
||||
client.posture
|
||||
.windowsAntivirusEnabled
|
||||
)
|
||||
: "-"}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
{client.posture.windowsAntivirusEnabled !==
|
||||
null &&
|
||||
client.posture
|
||||
.windowsAntivirusEnabled !==
|
||||
undefined && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t(
|
||||
"windowsAntivirusEnabled"
|
||||
)}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{isPaidUser
|
||||
? formatPostureValue(
|
||||
client.posture
|
||||
.windowsAntivirusEnabled
|
||||
)
|
||||
: "-"}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
|
||||
{client.posture.macosSipEnabled !== null &&
|
||||
client.posture.macosSipEnabled !==
|
||||
undefined && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("macosSipEnabled")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{isPaidUser
|
||||
? formatPostureValue(
|
||||
client.posture
|
||||
.macosSipEnabled
|
||||
)
|
||||
: "-"}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
{client.posture.macosSipEnabled !== null &&
|
||||
client.posture.macosSipEnabled !==
|
||||
undefined && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("macosSipEnabled")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{isPaidUser
|
||||
? formatPostureValue(
|
||||
client.posture
|
||||
.macosSipEnabled
|
||||
)
|
||||
: "-"}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
|
||||
{client.posture.macosGatekeeperEnabled !==
|
||||
null &&
|
||||
client.posture.macosGatekeeperEnabled !==
|
||||
undefined && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("macosGatekeeperEnabled")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{isPaidUser
|
||||
? formatPostureValue(
|
||||
client.posture
|
||||
.macosGatekeeperEnabled
|
||||
)
|
||||
: "-"}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
{client.posture.macosGatekeeperEnabled !==
|
||||
null &&
|
||||
client.posture
|
||||
.macosGatekeeperEnabled !==
|
||||
undefined && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t(
|
||||
"macosGatekeeperEnabled"
|
||||
)}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{isPaidUser
|
||||
? formatPostureValue(
|
||||
client.posture
|
||||
.macosGatekeeperEnabled
|
||||
)
|
||||
: "-"}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
|
||||
{client.posture.macosFirewallStealthMode !==
|
||||
null &&
|
||||
client.posture.macosFirewallStealthMode !==
|
||||
undefined && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("macosFirewallStealthMode")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{isPaidUser
|
||||
? formatPostureValue(
|
||||
client.posture
|
||||
.macosFirewallStealthMode
|
||||
)
|
||||
: "-"}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
{client.posture.macosFirewallStealthMode !==
|
||||
null &&
|
||||
client.posture
|
||||
.macosFirewallStealthMode !==
|
||||
undefined && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t(
|
||||
"macosFirewallStealthMode"
|
||||
)}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{isPaidUser
|
||||
? formatPostureValue(
|
||||
client.posture
|
||||
.macosFirewallStealthMode
|
||||
)
|
||||
: "-"}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
|
||||
{client.posture.linuxAppArmorEnabled !== null &&
|
||||
client.posture.linuxAppArmorEnabled !==
|
||||
undefined && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("linuxAppArmorEnabled")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{isPaidUser
|
||||
? formatPostureValue(
|
||||
client.posture
|
||||
.linuxAppArmorEnabled
|
||||
)
|
||||
: "-"}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
{client.posture.linuxAppArmorEnabled !==
|
||||
null &&
|
||||
client.posture.linuxAppArmorEnabled !==
|
||||
undefined && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("linuxAppArmorEnabled")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{isPaidUser
|
||||
? formatPostureValue(
|
||||
client.posture
|
||||
.linuxAppArmorEnabled
|
||||
)
|
||||
: "-"}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
|
||||
{client.posture.linuxSELinuxEnabled !== null &&
|
||||
client.posture.linuxSELinuxEnabled !==
|
||||
undefined && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("linuxSELinuxEnabled")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{isPaidUser
|
||||
? formatPostureValue(
|
||||
client.posture
|
||||
.linuxSELinuxEnabled
|
||||
)
|
||||
: "-"}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
</InfoSections>
|
||||
</>
|
||||
) : (
|
||||
<div className="text-muted-foreground">
|
||||
{t("noData")}
|
||||
</div>
|
||||
)}
|
||||
</SettingsSectionBody>
|
||||
</SettingsSection>
|
||||
{client.posture.linuxSELinuxEnabled !==
|
||||
null &&
|
||||
client.posture.linuxSELinuxEnabled !==
|
||||
undefined && (
|
||||
<InfoSection>
|
||||
<InfoSectionTitle>
|
||||
{t("linuxSELinuxEnabled")}
|
||||
</InfoSectionTitle>
|
||||
<InfoSectionContent>
|
||||
{isPaidUser
|
||||
? formatPostureValue(
|
||||
client.posture
|
||||
.linuxSELinuxEnabled
|
||||
)
|
||||
: "-"}
|
||||
</InfoSectionContent>
|
||||
</InfoSection>
|
||||
)}
|
||||
</InfoSections>
|
||||
</>
|
||||
) : (
|
||||
<div className="text-muted-foreground">
|
||||
{t("noData")}
|
||||
</div>
|
||||
)}
|
||||
</SettingsSectionBody>
|
||||
</SettingsSection>
|
||||
)}
|
||||
</SettingsContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { getTranslations } from "next-intl/server";
|
||||
import { getCachedOrg } from "@app/lib/api/getCachedOrg";
|
||||
import { getCachedOrgUser } from "@app/lib/api/getCachedOrgUser";
|
||||
import { build } from "@server/build";
|
||||
import { pullEnv } from "@app/lib/pullEnv";
|
||||
|
||||
type GeneralSettingsProps = {
|
||||
children: React.ReactNode;
|
||||
@@ -23,6 +24,7 @@ export default async function GeneralSettingsPage({
|
||||
const { orgId } = await params;
|
||||
|
||||
const user = await verifySession();
|
||||
const env = pullEnv();
|
||||
|
||||
if (!user) {
|
||||
redirect(`/`);
|
||||
@@ -56,10 +58,15 @@ export default async function GeneralSettingsPage({
|
||||
title: t("security"),
|
||||
href: `/{orgId}/settings/general/security`
|
||||
},
|
||||
{
|
||||
title: t("authPage"),
|
||||
href: `/{orgId}/settings/general/auth-page`
|
||||
}
|
||||
// PaidFeaturesAlert
|
||||
...(!env.flags.disableEnterpriseFeatures
|
||||
? [
|
||||
{
|
||||
title: t("authPage"),
|
||||
href: `/{orgId}/settings/general/auth-page`
|
||||
}
|
||||
]
|
||||
: [])
|
||||
];
|
||||
|
||||
return (
|
||||
|
||||
@@ -102,10 +102,13 @@ type SectionFormProps = {
|
||||
|
||||
export default function SecurityPage() {
|
||||
const { org } = useOrgContext();
|
||||
const { env } = useEnvContext();
|
||||
return (
|
||||
<SettingsContainer>
|
||||
<LogRetentionSectionForm org={org.org} />
|
||||
<SecuritySettingsSectionForm org={org.org} />
|
||||
{!env.flags.disableEnterpriseFeatures && (
|
||||
<SecuritySettingsSectionForm org={org.org} />
|
||||
)}
|
||||
</SettingsContainer>
|
||||
);
|
||||
}
|
||||
@@ -135,7 +138,8 @@ function LogRetentionSectionForm({ org }: SectionFormProps) {
|
||||
const { isPaidUser, hasSaasSubscription } = usePaidStatus();
|
||||
|
||||
const [, formAction, loadingSave] = useActionState(performSave, null);
|
||||
const api = createApiClient(useEnvContext());
|
||||
const { env } = useEnvContext();
|
||||
const api = createApiClient({ env });
|
||||
|
||||
async function performSave() {
|
||||
const isValid = await form.trigger();
|
||||
@@ -238,120 +242,144 @@ function LogRetentionSectionForm({ org }: SectionFormProps) {
|
||||
)}
|
||||
/>
|
||||
|
||||
<PaidFeaturesAlert />
|
||||
{!env.flags.disableEnterpriseFeatures && (
|
||||
<>
|
||||
<PaidFeaturesAlert />
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="settingsLogRetentionDaysAccess"
|
||||
render={({ field }) => {
|
||||
const isDisabled = !isPaidUser;
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="settingsLogRetentionDaysAccess"
|
||||
render={({ field }) => {
|
||||
const isDisabled = !isPaidUser;
|
||||
|
||||
return (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("logRetentionAccessLabel")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Select
|
||||
value={field.value.toString()}
|
||||
onValueChange={(value) => {
|
||||
if (!isDisabled) {
|
||||
field.onChange(
|
||||
parseInt(
|
||||
value,
|
||||
10
|
||||
)
|
||||
);
|
||||
}
|
||||
}}
|
||||
disabled={isDisabled}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue
|
||||
placeholder={t(
|
||||
"selectLogRetention"
|
||||
)}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{LOG_RETENTION_OPTIONS.map(
|
||||
(option) => (
|
||||
<SelectItem
|
||||
key={
|
||||
option.value
|
||||
}
|
||||
value={option.value.toString()}
|
||||
>
|
||||
{t(
|
||||
option.label
|
||||
)}
|
||||
</SelectItem>
|
||||
)
|
||||
return (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t(
|
||||
"logRetentionAccessLabel"
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="settingsLogRetentionDaysAction"
|
||||
render={({ field }) => {
|
||||
const isDisabled = !isPaidUser;
|
||||
|
||||
return (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("logRetentionActionLabel")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Select
|
||||
value={field.value.toString()}
|
||||
onValueChange={(value) => {
|
||||
if (!isDisabled) {
|
||||
field.onChange(
|
||||
parseInt(
|
||||
value,
|
||||
10
|
||||
)
|
||||
);
|
||||
}
|
||||
}}
|
||||
disabled={isDisabled}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue
|
||||
placeholder={t(
|
||||
"selectLogRetention"
|
||||
)}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{LOG_RETENTION_OPTIONS.map(
|
||||
(option) => (
|
||||
<SelectItem
|
||||
key={
|
||||
option.value
|
||||
}
|
||||
value={option.value.toString()}
|
||||
>
|
||||
{t(
|
||||
option.label
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Select
|
||||
value={field.value.toString()}
|
||||
onValueChange={(
|
||||
value
|
||||
) => {
|
||||
if (
|
||||
!isDisabled
|
||||
) {
|
||||
field.onChange(
|
||||
parseInt(
|
||||
value,
|
||||
10
|
||||
)
|
||||
);
|
||||
}
|
||||
}}
|
||||
disabled={
|
||||
isDisabled
|
||||
}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue
|
||||
placeholder={t(
|
||||
"selectLogRetention"
|
||||
)}
|
||||
</SelectItem>
|
||||
)
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{LOG_RETENTION_OPTIONS.map(
|
||||
(
|
||||
option
|
||||
) => (
|
||||
<SelectItem
|
||||
key={
|
||||
option.value
|
||||
}
|
||||
value={option.value.toString()}
|
||||
>
|
||||
{t(
|
||||
option.label
|
||||
)}
|
||||
</SelectItem>
|
||||
)
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="settingsLogRetentionDaysAction"
|
||||
render={({ field }) => {
|
||||
const isDisabled = !isPaidUser;
|
||||
|
||||
return (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t(
|
||||
"logRetentionActionLabel"
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Select
|
||||
value={field.value.toString()}
|
||||
onValueChange={(
|
||||
value
|
||||
) => {
|
||||
if (
|
||||
!isDisabled
|
||||
) {
|
||||
field.onChange(
|
||||
parseInt(
|
||||
value,
|
||||
10
|
||||
)
|
||||
);
|
||||
}
|
||||
}}
|
||||
disabled={
|
||||
isDisabled
|
||||
}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue
|
||||
placeholder={t(
|
||||
"selectLogRetention"
|
||||
)}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{LOG_RETENTION_OPTIONS.map(
|
||||
(
|
||||
option
|
||||
) => (
|
||||
<SelectItem
|
||||
key={
|
||||
option.value
|
||||
}
|
||||
value={option.value.toString()}
|
||||
>
|
||||
{t(
|
||||
option.label
|
||||
)}
|
||||
</SelectItem>
|
||||
)
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</form>
|
||||
</Form>
|
||||
</SettingsSectionForm>
|
||||
|
||||
@@ -163,7 +163,9 @@ function MaintenanceSectionForm({
|
||||
const isEnterpriseNotLicensed = build === "enterprise" && !isUnlocked();
|
||||
const isSaasNotSubscribed =
|
||||
build === "saas" && !subscription?.isSubscribed();
|
||||
return isEnterpriseNotLicensed || isSaasNotSubscribed || build === "oss";
|
||||
return (
|
||||
isEnterpriseNotLicensed || isSaasNotSubscribed || build === "oss"
|
||||
);
|
||||
};
|
||||
|
||||
if (!resource.http) {
|
||||
@@ -189,13 +191,14 @@ function MaintenanceSectionForm({
|
||||
className="space-y-4"
|
||||
id="maintenance-settings-form"
|
||||
>
|
||||
<PaidFeaturesAlert></PaidFeaturesAlert>
|
||||
<PaidFeaturesAlert />
|
||||
<FormField
|
||||
control={maintenanceForm.control}
|
||||
name="maintenanceModeEnabled"
|
||||
render={({ field }) => {
|
||||
const isDisabled =
|
||||
isSecurityFeatureDisabled() || resource.http === false;
|
||||
isSecurityFeatureDisabled() ||
|
||||
resource.http === false;
|
||||
|
||||
return (
|
||||
<FormItem>
|
||||
@@ -415,7 +418,7 @@ function MaintenanceSectionForm({
|
||||
<Button
|
||||
type="submit"
|
||||
loading={maintenanceSaveLoading}
|
||||
disabled={maintenanceSaveLoading || !isPaidUser }
|
||||
disabled={maintenanceSaveLoading || !isPaidUser}
|
||||
form="maintenance-settings-form"
|
||||
>
|
||||
{t("saveSettings")}
|
||||
@@ -741,10 +744,12 @@ export default function GeneralForm() {
|
||||
</SettingsSectionFooter>
|
||||
</SettingsSection>
|
||||
|
||||
<MaintenanceSectionForm
|
||||
resource={resource}
|
||||
updateResource={updateResource}
|
||||
/>
|
||||
{!env.flags.disableEnterpriseFeatures && (
|
||||
<MaintenanceSectionForm
|
||||
resource={resource}
|
||||
updateResource={updateResource}
|
||||
/>
|
||||
)}
|
||||
</SettingsContainer>
|
||||
|
||||
<Credenza
|
||||
|
||||
@@ -271,27 +271,29 @@ export default function CredentialsPage() {
|
||||
</Alert>
|
||||
)}
|
||||
</SettingsSectionBody>
|
||||
<SettingsSectionFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
setShouldDisconnect(false);
|
||||
setModalOpen(true);
|
||||
}}
|
||||
disabled={isSecurityFeatureDisabled()}
|
||||
>
|
||||
{t("regenerateCredentialsButton")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setShouldDisconnect(true);
|
||||
setModalOpen(true);
|
||||
}}
|
||||
disabled={isSecurityFeatureDisabled()}
|
||||
>
|
||||
{t("siteRegenerateAndDisconnect")}
|
||||
</Button>
|
||||
</SettingsSectionFooter>
|
||||
{!env.flags.disableEnterpriseFeatures && (
|
||||
<SettingsSectionFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
setShouldDisconnect(false);
|
||||
setModalOpen(true);
|
||||
}}
|
||||
disabled={isSecurityFeatureDisabled()}
|
||||
>
|
||||
{t("regenerateCredentialsButton")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setShouldDisconnect(true);
|
||||
setModalOpen(true);
|
||||
}}
|
||||
disabled={isSecurityFeatureDisabled()}
|
||||
>
|
||||
{t("siteRegenerateAndDisconnect")}
|
||||
</Button>
|
||||
</SettingsSectionFooter>
|
||||
)}
|
||||
</SettingsSection>
|
||||
|
||||
<NewtSiteInstallCommands
|
||||
@@ -383,14 +385,16 @@ export default function CredentialsPage() {
|
||||
</>
|
||||
)}
|
||||
</SettingsSectionBody>
|
||||
<SettingsSectionFooter>
|
||||
<Button
|
||||
onClick={() => setModalOpen(true)}
|
||||
disabled={isSecurityFeatureDisabled()}
|
||||
>
|
||||
{t("siteRegenerateAndDisconnect")}
|
||||
</Button>
|
||||
</SettingsSectionFooter>
|
||||
{!env.flags.disableEnterpriseFeatures && (
|
||||
<SettingsSectionFooter>
|
||||
<Button
|
||||
onClick={() => setModalOpen(true)}
|
||||
disabled={isSecurityFeatureDisabled()}
|
||||
>
|
||||
{t("siteRegenerateAndDisconnect")}
|
||||
</Button>
|
||||
</SettingsSectionFooter>
|
||||
)}
|
||||
</SettingsSection>
|
||||
)}
|
||||
</SettingsContainer>
|
||||
|
||||
@@ -121,16 +121,27 @@ export const orgNavSections = (env?: Env): SidebarNavSection[] => [
|
||||
href: "/{orgId}/settings/access/roles",
|
||||
icon: <Users className="size-4 flex-none" />
|
||||
},
|
||||
{
|
||||
title: "sidebarIdentityProviders",
|
||||
href: "/{orgId}/settings/idp",
|
||||
icon: <Fingerprint className="size-4 flex-none" />
|
||||
},
|
||||
{
|
||||
title: "sidebarApprovals",
|
||||
href: "/{orgId}/settings/access/approvals",
|
||||
icon: <UserCog className="size-4 flex-none" />
|
||||
},
|
||||
// PaidFeaturesAlert
|
||||
...((build === "oss" && !env?.flags.disableEnterpriseFeatures) ||
|
||||
build === "saas" ||
|
||||
env?.flags.useOrgOnlyIdp
|
||||
? [
|
||||
{
|
||||
title: "sidebarIdentityProviders",
|
||||
href: "/{orgId}/settings/idp",
|
||||
icon: <Fingerprint className="size-4 flex-none" />
|
||||
}
|
||||
]
|
||||
: []),
|
||||
...(!env?.flags.disableEnterpriseFeatures
|
||||
? [
|
||||
{
|
||||
title: "sidebarApprovals",
|
||||
href: "/{orgId}/settings/access/approvals",
|
||||
icon: <UserCog className="size-4 flex-none" />
|
||||
}
|
||||
]
|
||||
: []),
|
||||
{
|
||||
title: "sidebarShareableLinks",
|
||||
href: "/{orgId}/settings/share-links",
|
||||
@@ -147,16 +158,20 @@ export const orgNavSections = (env?: Env): SidebarNavSection[] => [
|
||||
href: "/{orgId}/settings/logs/request",
|
||||
icon: <SquareMousePointer className="size-4 flex-none" />
|
||||
},
|
||||
{
|
||||
title: "sidebarLogsAccess",
|
||||
href: "/{orgId}/settings/logs/access",
|
||||
icon: <ScanEye className="size-4 flex-none" />
|
||||
},
|
||||
{
|
||||
title: "sidebarLogsAction",
|
||||
href: "/{orgId}/settings/logs/action",
|
||||
icon: <Logs className="size-4 flex-none" />
|
||||
}
|
||||
...(!env?.flags.disableEnterpriseFeatures
|
||||
? [
|
||||
{
|
||||
title: "sidebarLogsAccess",
|
||||
href: "/{orgId}/settings/logs/access",
|
||||
icon: <ScanEye className="size-4 flex-none" />
|
||||
},
|
||||
{
|
||||
title: "sidebarLogsAction",
|
||||
href: "/{orgId}/settings/logs/action",
|
||||
icon: <Logs className="size-4 flex-none" />
|
||||
}
|
||||
]
|
||||
: [])
|
||||
];
|
||||
|
||||
const analytics = {
|
||||
|
||||
@@ -51,6 +51,7 @@ export default function CreateRoleForm({
|
||||
const { org } = useOrgContext();
|
||||
const t = useTranslations();
|
||||
const { isPaidUser } = usePaidStatus();
|
||||
const { env } = useEnvContext();
|
||||
|
||||
const formSchema = z.object({
|
||||
name: z
|
||||
@@ -161,50 +162,56 @@ export default function CreateRoleForm({
|
||||
)}
|
||||
/>
|
||||
|
||||
<PaidFeaturesAlert />
|
||||
{!env.flags.disableEnterpriseFeatures && (
|
||||
<>
|
||||
<PaidFeaturesAlert />
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="requireDeviceApproval"
|
||||
render={({ field }) => (
|
||||
<FormItem className="my-2">
|
||||
<FormControl>
|
||||
<CheckboxWithLabel
|
||||
{...field}
|
||||
disabled={!isPaidUser}
|
||||
value="on"
|
||||
checked={form.watch(
|
||||
"requireDeviceApproval"
|
||||
)}
|
||||
onCheckedChange={(
|
||||
checked
|
||||
) => {
|
||||
if (
|
||||
checked !==
|
||||
"indeterminate"
|
||||
) {
|
||||
form.setValue(
|
||||
"requireDeviceApproval",
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="requireDeviceApproval"
|
||||
render={({ field }) => (
|
||||
<FormItem className="my-2">
|
||||
<FormControl>
|
||||
<CheckboxWithLabel
|
||||
{...field}
|
||||
disabled={
|
||||
!isPaidUser
|
||||
}
|
||||
value="on"
|
||||
checked={form.watch(
|
||||
"requireDeviceApproval"
|
||||
)}
|
||||
onCheckedChange={(
|
||||
checked
|
||||
);
|
||||
}
|
||||
}}
|
||||
label={t(
|
||||
"requireDeviceApproval"
|
||||
)}
|
||||
/>
|
||||
</FormControl>
|
||||
) => {
|
||||
if (
|
||||
checked !==
|
||||
"indeterminate"
|
||||
) {
|
||||
form.setValue(
|
||||
"requireDeviceApproval",
|
||||
checked
|
||||
);
|
||||
}
|
||||
}}
|
||||
label={t(
|
||||
"requireDeviceApproval"
|
||||
)}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormDescription>
|
||||
{t(
|
||||
"requireDeviceApprovalDescription"
|
||||
)}
|
||||
</FormDescription>
|
||||
<FormDescription>
|
||||
{t(
|
||||
"requireDeviceApprovalDescription"
|
||||
)}
|
||||
</FormDescription>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</form>
|
||||
</Form>
|
||||
</CredenzaBody>
|
||||
|
||||
@@ -59,6 +59,7 @@ export default function EditRoleForm({
|
||||
const { org } = useOrgContext();
|
||||
const t = useTranslations();
|
||||
const { isPaidUser } = usePaidStatus();
|
||||
const { env } = useEnvContext();
|
||||
|
||||
const formSchema = z.object({
|
||||
name: z
|
||||
@@ -168,50 +169,57 @@ export default function EditRoleForm({
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<PaidFeaturesAlert />
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="requireDeviceApproval"
|
||||
render={({ field }) => (
|
||||
<FormItem className="my-2">
|
||||
<FormControl>
|
||||
<CheckboxWithLabel
|
||||
{...field}
|
||||
disabled={!isPaidUser}
|
||||
value="on"
|
||||
checked={form.watch(
|
||||
"requireDeviceApproval"
|
||||
)}
|
||||
onCheckedChange={(
|
||||
checked
|
||||
) => {
|
||||
if (
|
||||
checked !==
|
||||
"indeterminate"
|
||||
) {
|
||||
form.setValue(
|
||||
"requireDeviceApproval",
|
||||
{!env.flags.disableEnterpriseFeatures && (
|
||||
<>
|
||||
<PaidFeaturesAlert />
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="requireDeviceApproval"
|
||||
render={({ field }) => (
|
||||
<FormItem className="my-2">
|
||||
<FormControl>
|
||||
<CheckboxWithLabel
|
||||
{...field}
|
||||
disabled={
|
||||
!isPaidUser
|
||||
}
|
||||
value="on"
|
||||
checked={form.watch(
|
||||
"requireDeviceApproval"
|
||||
)}
|
||||
onCheckedChange={(
|
||||
checked
|
||||
);
|
||||
}
|
||||
}}
|
||||
label={t(
|
||||
"requireDeviceApproval"
|
||||
)}
|
||||
/>
|
||||
</FormControl>
|
||||
) => {
|
||||
if (
|
||||
checked !==
|
||||
"indeterminate"
|
||||
) {
|
||||
form.setValue(
|
||||
"requireDeviceApproval",
|
||||
checked
|
||||
);
|
||||
}
|
||||
}}
|
||||
label={t(
|
||||
"requireDeviceApproval"
|
||||
)}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormDescription>
|
||||
{t(
|
||||
"requireDeviceApprovalDescription"
|
||||
)}
|
||||
</FormDescription>
|
||||
<FormDescription>
|
||||
{t(
|
||||
"requireDeviceApprovalDescription"
|
||||
)}
|
||||
</FormDescription>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</form>
|
||||
</Form>
|
||||
</CredenzaBody>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
||||
import { ExternalLink, KeyRound, Sparkles } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import Link from "next/link";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
|
||||
const bannerClassName =
|
||||
"mb-6 border-primary/30 bg-linear-to-br from-primary/10 via-background to-background overflow-hidden";
|
||||
@@ -15,6 +16,12 @@ const bannerRowClassName =
|
||||
export function PaidFeaturesAlert() {
|
||||
const t = useTranslations();
|
||||
const { hasSaasSubscription, hasEnterpriseLicense } = usePaidStatus();
|
||||
const { env } = useEnvContext();
|
||||
|
||||
if (env.flags.disableEnterpriseFeatures) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{build === "saas" && !hasSaasSubscription ? (
|
||||
|
||||
@@ -4,7 +4,6 @@ import { createContext } from "react";
|
||||
type SubscriptionStatusContextType = {
|
||||
subscriptionStatus: GetOrgSubscriptionResponse | null;
|
||||
updateSubscriptionStatus: (updatedSite: GetOrgSubscriptionResponse) => void;
|
||||
isActive: () => boolean;
|
||||
getTier: () => { tier: string | null; active: boolean };
|
||||
isSubscribed: () => boolean;
|
||||
subscribed: boolean;
|
||||
|
||||
@@ -8,14 +8,29 @@ export function usePaidStatus() {
|
||||
|
||||
// Check if features are disabled due to licensing/subscription
|
||||
const hasEnterpriseLicense = build === "enterprise" && isUnlocked();
|
||||
const hasSaasSubscription =
|
||||
build === "saas" &&
|
||||
subscription?.isSubscribed() &&
|
||||
subscription.isActive();
|
||||
const tierData = subscription?.getTier();
|
||||
const hasSaasSubscription = build === "saas" && tierData?.active;
|
||||
|
||||
function isPaidUser(tiers: string[]): boolean {
|
||||
if (hasEnterpriseLicense) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
hasSaasSubscription &&
|
||||
tierData?.tier &&
|
||||
tiers.includes(tierData.tier)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return {
|
||||
hasEnterpriseLicense,
|
||||
hasSaasSubscription,
|
||||
isPaidUser: hasEnterpriseLicense || hasSaasSubscription
|
||||
isPaidUser,
|
||||
subscriptionTier: tierData?.tier
|
||||
};
|
||||
}
|
||||
|
||||
@@ -65,7 +65,11 @@ export function pullEnv(): Env {
|
||||
? true
|
||||
: false,
|
||||
useOrgOnlyIdp:
|
||||
process.env.USE_ORG_ONLY_IDP === "true" ? true : false
|
||||
process.env.USE_ORG_ONLY_IDP === "true" ? true : false,
|
||||
disableEnterpriseFeatures:
|
||||
process.env.DISABLE_ENTERPRISE_FEATURES === "true"
|
||||
? true
|
||||
: false
|
||||
},
|
||||
|
||||
branding: {
|
||||
|
||||
@@ -35,6 +35,7 @@ export type Env = {
|
||||
usePangolinDns: boolean;
|
||||
disableProductHelpBanners: boolean;
|
||||
useOrgOnlyIdp: boolean;
|
||||
disableEnterpriseFeatures: boolean;
|
||||
};
|
||||
branding: {
|
||||
appName?: string;
|
||||
|
||||
@@ -31,16 +31,6 @@ export function SubscriptionStatusProvider({
|
||||
});
|
||||
};
|
||||
|
||||
const isActive = () => {
|
||||
if (subscriptionStatus?.subscriptions) {
|
||||
// Check if any subscription is active
|
||||
return subscriptionStatus.subscriptions.some(
|
||||
(sub) => sub.subscription?.status === "active"
|
||||
);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const getTier = () => {
|
||||
if (subscriptionStatus?.subscriptions) {
|
||||
// Iterate through all subscriptions
|
||||
@@ -65,9 +55,6 @@ export function SubscriptionStatusProvider({
|
||||
};
|
||||
|
||||
const isSubscribed = () => {
|
||||
if (build === "enterprise") {
|
||||
return true;
|
||||
}
|
||||
const { tier, active } = getTier();
|
||||
return (
|
||||
(tier == "tier1" || tier == "tier2" || tier == "tier3") &&
|
||||
@@ -82,7 +69,6 @@ export function SubscriptionStatusProvider({
|
||||
value={{
|
||||
subscriptionStatus: subscriptionStatusState,
|
||||
updateSubscriptionStatus,
|
||||
isActive,
|
||||
getTier,
|
||||
isSubscribed,
|
||||
subscribed
|
||||
|
||||
Reference in New Issue
Block a user