feat: New Group security cards

This commit is contained in:
Harsh Mahajan
2025-12-31 01:20:40 +05:30
parent baf47f8163
commit 1fa67227ed
8 changed files with 250 additions and 319 deletions
@@ -1,26 +1,20 @@
<script lang="ts">
import { Container } from '$lib/layout';
import UpdateMockNumbers from './updateMockNumbers.svelte';
import UpdatePasswordDictionary from './updatePasswordDictionary.svelte';
import UpdatePasswordHistory from './updatePasswordHistory.svelte';
import UpdatePersonalDataCheck from './updatePersonalDataCheck.svelte';
import UpdateSessionAlerts from './updateSessionAlerts.svelte';
import UpdateSessionLength from './updateSessionLength.svelte';
import UpdateSessionsLimit from './updateSessionsLimit.svelte';
import UpdateMembershipPrivacy from './updateMembershipPrivacy.svelte';
import UpdateUsersLimit from './updateUsersLimit.svelte';
import UpdateSessionInvalidation from './updateSessionInvalidation.svelte';
import UpdateSessionLength from './updateSessionLength.svelte';
import UpdateSessionsLimit from './updateSessionsLimit.svelte';
import PasswordPolicies from './passwordPolicies.svelte';
import SessionSecurity from './sessionSecurity.svelte';
</script>
<Container>
<UpdateUsersLimit />
<UpdateSessionLength />
<UpdateSessionsLimit />
<UpdatePasswordHistory />
<UpdatePasswordDictionary />
<UpdatePersonalDataCheck />
<UpdateSessionAlerts />
<UpdateSessionInvalidation />
<PasswordPolicies />
<SessionSecurity />
<UpdateMockNumbers />
<UpdateMembershipPrivacy />
</Container>
@@ -0,0 +1,150 @@
<script lang="ts">
import { invalidate } from '$app/navigation';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { CardGrid } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Button, Form, InputNumber, InputSwitch } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { Typography, Link, Layout } from '@appwrite.io/pink-svelte';
import { project } from '../../store';
import { tick } from 'svelte';
let passwordHistory = $state(5);
let passwordHistoryEnabled = $state(false);
let passwordDictionary = $state(false);
let authPersonalDataCheck = $state(false);
let maxPasswordInputField: InputNumber | null = null;
// Initialize and sync state when project updates
$effect(() => {
const currentProject = $project;
const historyValue = currentProject?.authPasswordHistory;
passwordHistory = historyValue < 1 ? 5 : historyValue;
passwordHistoryEnabled = (historyValue ?? 0) !== 0;
passwordDictionary = currentProject?.authPasswordDictionary ?? false;
authPersonalDataCheck = currentProject?.authPersonalDataCheck ?? false;
});
$effect(() => {
if (passwordHistoryEnabled) {
tick().then(() => {
if (maxPasswordInputField) {
maxPasswordInputField.addInputFocus();
}
});
}
});
const hasChanges = $derived(
passwordHistoryEnabled !== (($project?.authPasswordHistory ?? 0) !== 0) ||
(passwordHistoryEnabled && passwordHistory !== ($project?.authPasswordHistory ?? 0)) ||
passwordDictionary !== ($project?.authPasswordDictionary ?? false) ||
authPersonalDataCheck !== ($project?.authPersonalDataCheck ?? false)
);
async function updatePasswordPolicies() {
try {
// Update password history
await sdk.forConsole.projects.updateAuthPasswordHistory({
projectId: $project.$id,
limit: passwordHistoryEnabled ? passwordHistory : 0
});
// Update password dictionary
await sdk.forConsole.projects.updateAuthPasswordDictionary({
projectId: $project.$id,
enabled: passwordDictionary
});
// Update personal data check
await sdk.forConsole.projects.updatePersonalDataCheck({
projectId: $project.$id,
enabled: authPersonalDataCheck
});
await invalidate(Dependencies.PROJECT);
addNotification({
type: 'success',
message: 'Updated password policies.'
});
trackEvent(Submit.AuthPasswordHistoryUpdate);
trackEvent(Submit.AuthPasswordDictionaryUpdate);
trackEvent(Submit.AuthPersonalDataCheckUpdate);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.AuthPasswordHistoryUpdate);
}
}
</script>
<Form onSubmit={updatePasswordPolicies}>
<CardGrid gap="xxl">
<svelte:fragment slot="title">Password policies</svelte:fragment>
<svelte:fragment slot="aside">
<InputSwitch
bind:value={passwordHistoryEnabled}
id="passwordHistoryEnabled"
label="Password history">
<svelte:fragment slot="description">
<Layout.Stack gap="m">
<Typography.Text>
Enabling this option prevents users from reusing recent passwords by
comparing the new password with their password history.
</Typography.Text>
{#if passwordHistoryEnabled}
<InputNumber
required
max={20}
min={1}
id="password-history"
label="Limit"
bind:value={passwordHistory}
bind:this={maxPasswordInputField}
helper="Maximum 20 passwords." />
{/if}
</Layout.Stack>
</svelte:fragment>
</InputSwitch>
<InputSwitch
bind:value={passwordDictionary}
id="passwordDictionary"
label="Password dictionary">
<svelte:fragment slot="description">
<Typography.Text>
Enabling this option prevent users from setting insecure passwords by
comparing the user's password with the <Link.Anchor
target="_blank"
rel="noopener noreferrer"
class="link"
href="https://github.com/danielmiessler/SecLists/blob/master/Passwords/Common-Credentials/10k-most-common.txt"
>10k most commonly used passwords.</Link.Anchor>
</Typography.Text>
</svelte:fragment>
</InputSwitch>
<InputSwitch
bind:value={authPersonalDataCheck}
id="personalDataCheck"
label="Disallow personal data">
<svelte:fragment slot="description">
<Typography.Text>
Do not allow passwords that contain any part of the user's personal data.
This includes the user's <Typography.Code>name</Typography.Code>, <Typography.Code
>email</Typography.Code
>, or <Typography.Code>phone</Typography.Code>.
</Typography.Text>
</svelte:fragment>
</InputSwitch>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={!hasChanges} submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
@@ -0,0 +1,94 @@
<script lang="ts">
import { invalidate } from '$app/navigation';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { CardGrid } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Button, Form, InputSwitch } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { Typography } from '@appwrite.io/pink-svelte';
import { project as projectStore } from '../../store';
import { page } from '$app/state';
const project = $derived($projectStore ?? page.data?.project);
let authSessionAlerts = $state(false);
let sessionInvalidation = $state(false);
// Initialize state from project
$effect(() => {
authSessionAlerts = project?.authSessionAlerts ?? false;
sessionInvalidation = project?.authInvalidateSessions ?? false;
});
const hasChanges = $derived(
authSessionAlerts !== (project?.authSessionAlerts ?? false) ||
sessionInvalidation !== (project?.authInvalidateSessions ?? false)
);
async function updateSessionSecurity() {
try {
// Update session alerts
await sdk.forConsole.projects.updateSessionAlerts({
projectId: project.$id,
alerts: authSessionAlerts
});
// Update session invalidation
await sdk.forConsole.projects.updateSessionInvalidation({
projectId: project.$id,
enabled: sessionInvalidation
});
await invalidate(Dependencies.PROJECT);
addNotification({
type: 'success',
message: 'Updated session security settings.'
});
trackEvent(Submit.AuthSessionAlertsUpdate);
trackEvent(Submit.AuthInvalidateSesssion);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.AuthSessionAlertsUpdate);
}
}
</script>
<Form onSubmit={updateSessionSecurity}>
<CardGrid gap="xxl">
<svelte:fragment slot="title">Session security</svelte:fragment>
<svelte:fragment slot="aside">
<InputSwitch
bind:value={authSessionAlerts}
id="authSessionAlerts"
label="Session alerts">
<svelte:fragment slot="description">
<Typography.Text>
Enabling this option will send an email to the users when a new session is
created.
</Typography.Text>
</svelte:fragment>
</InputSwitch>
<InputSwitch
bind:value={sessionInvalidation}
id="invalidateSessions"
label="Invalidate sessions">
<svelte:fragment slot="description">
<Typography.Text>
Enabling this option will clear all existing sessions when the user changes
their password.
</Typography.Text>
</svelte:fragment>
</InputSwitch>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={!hasChanges} submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
@@ -1,61 +0,0 @@
<script lang="ts">
import { invalidate } from '$app/navigation';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { CardGrid } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Button, Form, InputSwitch } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { Typography, Link } from '@appwrite.io/pink-svelte';
import { project } from '../../store';
let passwordDictionary = $project?.authPasswordDictionary ?? false;
async function updatePasswordDictionary() {
try {
await sdk.forConsole.projects.updateAuthPasswordDictionary({
projectId: $project.$id,
enabled: passwordDictionary
});
await invalidate(Dependencies.PROJECT);
addNotification({
type: 'success',
message: 'Updated password dictionary check.'
});
trackEvent(Submit.AuthPasswordDictionaryUpdate);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.AuthPasswordDictionaryUpdate);
}
}
</script>
<Form onSubmit={updatePasswordDictionary}>
<CardGrid>
<svelte:fragment slot="title">Password dictionary</svelte:fragment>
<svelte:fragment slot="aside">
<InputSwitch
bind:value={passwordDictionary}
id="passwordDictionary"
label="Password dictionary" />
<Typography.Text>
Enabling this option prevent users from setting insecure passwords by comparing the
user's password with the <Link.Anchor
target="_blank"
rel="noopener noreferrer"
class="link"
href="https://github.com/danielmiessler/SecLists/blob/master/Passwords/Common-Credentials/10k-most-common.txt"
>10k most commonly used passwords.</Link.Anchor>
</Typography.Text>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={passwordDictionary === $project?.authPasswordDictionary} submit>
Update
</Button>
</svelte:fragment>
</CardGrid>
</Form>
@@ -1,80 +0,0 @@
<script lang="ts">
import { invalidate } from '$app/navigation';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { CardGrid } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Button, Form, InputNumber, InputSwitch } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { Typography } from '@appwrite.io/pink-svelte';
import { project } from '../../store';
import { tick } from 'svelte';
let passwordHistory = $project?.authPasswordHistory < 1 ? 5 : $project?.authPasswordHistory;
let passwordHistoryEnabled = ($project?.authPasswordHistory ?? 0) !== 0;
let initialPasswordHistoryEnabled = passwordHistoryEnabled;
async function updatePasswordHistoryLimit() {
try {
await sdk.forConsole.projects.updateAuthPasswordHistory({
projectId: $project.$id,
limit: passwordHistoryEnabled ? passwordHistory : 0
});
await invalidate(Dependencies.PROJECT);
initialPasswordHistoryEnabled = passwordHistoryEnabled;
addNotification({
type: 'success',
message: 'Updated password history limit.'
});
trackEvent(Submit.AuthPasswordHistoryUpdate);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.AuthPasswordHistoryUpdate);
}
}
let maxPasswordInputField: InputNumber | null = null;
$: if (passwordHistoryEnabled && maxPasswordInputField) {
tick().then(() => {
maxPasswordInputField.addInputFocus();
});
}
</script>
<Form onSubmit={updatePasswordHistoryLimit}>
<CardGrid>
<svelte:fragment slot="title">Password history</svelte:fragment>
Set the maximum number of passwords saved per user.
<svelte:fragment slot="aside">
<InputSwitch
bind:value={passwordHistoryEnabled}
id="passwordHistoryEnabled"
label="Password history" />
<Typography.Text>
Enabling this option prevents users from reusing recent passwords by comparing the
new password with their password history.
</Typography.Text>
<InputNumber
required
max={20}
min={1}
id="password-history"
label="Limit"
disabled={!passwordHistoryEnabled}
bind:value={passwordHistory}
helper="Maximum 20 passwords." />
</svelte:fragment>
<svelte:fragment slot="actions">
<Button
disabled={(passwordHistory === $project?.authPasswordHistory ||
$project?.authPasswordHistory === 0) &&
initialPasswordHistoryEnabled === passwordHistoryEnabled}
submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
@@ -1,56 +0,0 @@
<script lang="ts">
import { invalidate } from '$app/navigation';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { CardGrid } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Button, Form, InputSwitch } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { Typography } from '@appwrite.io/pink-svelte';
import { project } from '../../store';
let authPersonalDataCheck = $project?.authPersonalDataCheck ?? false;
async function updatePersonalDataCheck() {
try {
await sdk.forConsole.projects.updatePersonalDataCheck({
projectId: $project.$id,
enabled: authPersonalDataCheck
});
await invalidate(Dependencies.PROJECT);
addNotification({
type: 'success',
message: 'Toggled personal data checks for passwords'
});
trackEvent(Submit.AuthPersonalDataCheckUpdate);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.AuthPersonalDataCheckUpdate);
}
}
</script>
<Form onSubmit={updatePersonalDataCheck}>
<CardGrid>
<svelte:fragment slot="title">Personal data</svelte:fragment>
<svelte:fragment slot="aside">
<InputSwitch
bind:value={authPersonalDataCheck}
id="personalDataCheck"
label="Disallow personal data" />
<Typography.Text>
Do not allow passwords that contain any part of the user's personal data. This
includes the user's <Typography.Code>name</Typography.Code>, <Typography.Code
>email</Typography.Code
>, or <Typography.Code>phone</Typography.Code>.
</Typography.Text>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={authPersonalDataCheck === $project?.authPersonalDataCheck} submit
>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
@@ -1,54 +0,0 @@
<script lang="ts">
import { invalidate } from '$app/navigation';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { CardGrid } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Button, Form, InputSwitch } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { Typography } from '@appwrite.io/pink-svelte';
import { project } from '../../store';
let authSessionAlerts = $project?.authSessionAlerts ?? false;
async function updateSessionAlerts() {
try {
await sdk.forConsole.projects.updateSessionAlerts({
projectId: $project.$id,
alerts: authSessionAlerts
});
await invalidate(Dependencies.PROJECT);
addNotification({
type: 'success',
message: 'Updated session alerts.'
});
trackEvent(Submit.AuthSessionAlertsUpdate);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.AuthSessionAlertsUpdate);
}
}
</script>
<Form onSubmit={updateSessionAlerts}>
<CardGrid>
<svelte:fragment slot="title">Session alerts</svelte:fragment>
<svelte:fragment slot="aside">
<InputSwitch
bind:value={authSessionAlerts}
id="authSessionAlerts"
label="Session alerts" />
<Typography.Text>
Enabling this option will send an email to the users when a new session is created.
</Typography.Text>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={authSessionAlerts === $project?.authSessionAlerts} submit>
Update
</Button>
</svelte:fragment>
</CardGrid>
</Form>
@@ -1,56 +0,0 @@
<script lang="ts">
import { invalidate } from '$app/navigation';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { CardGrid } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Button, Form, InputSwitch } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { Typography } from '@appwrite.io/pink-svelte';
import { project } from '../../store';
let sessionInvalidation = $project?.authInvalidateSessions ?? false;
async function updateSessionInvalidation() {
try {
await sdk.forConsole.projects.updateSessionInvalidation({
projectId: $project.$id,
enabled: sessionInvalidation
});
await invalidate(Dependencies.PROJECT);
addNotification({
type: 'success',
message: 'Updated session invalidation check.'
});
trackEvent(Submit.AuthInvalidateSesssion);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.AuthInvalidateSesssion);
}
}
</script>
<Form onSubmit={updateSessionInvalidation}>
<CardGrid>
<svelte:fragment slot="title">Invalidate sessions</svelte:fragment>
<svelte:fragment slot="aside">
<InputSwitch
bind:value={sessionInvalidation}
id="invalidateSessions"
label="Invalidate sessions" />
<Typography.Text>
Enabling this option will clear all existing sessions when the user changes their
password.
</Typography.Text>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={sessionInvalidation === $project?.authInvalidateSessions} submit>
Update
</Button>
</svelte:fragment>
</CardGrid>
</Form>