Skip to content

Commit 66d0d8d

Browse files
authored
Merge pull request #2747 from appwrite/feat-SER-1037-Group-security-cards
feat: New Grouped security cards
2 parents 732b785 + c687c00 commit 66d0d8d

File tree

9 files changed

+261
-320
lines changed

9 files changed

+261
-320
lines changed

src/lib/actions/analytics.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ export enum Submit {
268268
AuthSessionAlertsUpdate = 'submit_auth_session_alerts_update',
269269
AuthMembershipPrivacyUpdate = 'submit_auth_membership_privacy_update',
270270
AuthMockNumbersUpdate = 'submit_auth_mock_numbers_update',
271-
AuthInvalidateSesssion = 'submit_auth_invalidate_session',
271+
AuthInvalidateSession = 'submit_auth_invalidate_session',
272272
SessionsLengthUpdate = 'submit_sessions_length_update',
273273
SessionsLimitUpdate = 'submit_sessions_limit_update',
274274
SessionDelete = 'submit_session_delete',
Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,23 @@
11
<script lang="ts">
22
import { Container } from '$lib/layout';
3+
import type { PageProps } from './$types';
34
import UpdateMockNumbers from './updateMockNumbers.svelte';
4-
import UpdatePasswordDictionary from './updatePasswordDictionary.svelte';
5-
import UpdatePasswordHistory from './updatePasswordHistory.svelte';
6-
import UpdatePersonalDataCheck from './updatePersonalDataCheck.svelte';
7-
import UpdateSessionAlerts from './updateSessionAlerts.svelte';
8-
import UpdateSessionLength from './updateSessionLength.svelte';
9-
import UpdateSessionsLimit from './updateSessionsLimit.svelte';
105
import UpdateMembershipPrivacy from './updateMembershipPrivacy.svelte';
116
import UpdateUsersLimit from './updateUsersLimit.svelte';
12-
import UpdateSessionInvalidation from './updateSessionInvalidation.svelte';
7+
import UpdateSessionLength from './updateSessionLength.svelte';
8+
import UpdateSessionsLimit from './updateSessionsLimit.svelte';
9+
import PasswordPolicies from './passwordPolicies.svelte';
10+
import SessionSecurity from './sessionSecurity.svelte';
11+
12+
let { data }: PageProps = $props();
1313
</script>
1414

1515
<Container>
1616
<UpdateUsersLimit />
1717
<UpdateSessionLength />
1818
<UpdateSessionsLimit />
19-
<UpdatePasswordHistory />
20-
<UpdatePasswordDictionary />
21-
<UpdatePersonalDataCheck />
22-
<UpdateSessionAlerts />
23-
<UpdateSessionInvalidation />
19+
<PasswordPolicies project={data.project} />
20+
<SessionSecurity project={data.project} />
2421
<UpdateMockNumbers />
2522
<UpdateMembershipPrivacy />
2623
</Container>
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
<script lang="ts">
2+
import { invalidate } from '$app/navigation';
3+
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
4+
import { CardGrid } from '$lib/components';
5+
import { Dependencies } from '$lib/constants';
6+
import { Button, Form, InputNumber, InputSwitch } from '$lib/elements/forms';
7+
import { addNotification } from '$lib/stores/notifications';
8+
import { sdk } from '$lib/stores/sdk';
9+
import { Typography, Link, Layout } from '@appwrite.io/pink-svelte';
10+
import type { Models } from '@appwrite.io/console';
11+
import { onMount } from 'svelte';
12+
13+
let {
14+
project
15+
}: {
16+
project: Models.Project;
17+
} = $props();
18+
19+
let lastValidLimit = $state(5);
20+
let passwordHistory = $state(5);
21+
let passwordDictionary = $state(false);
22+
let passwordHistoryEnabled = $state(false);
23+
let authPersonalDataCheck = $state(false);
24+
25+
onMount(() => {
26+
// update initial states here in onMount.
27+
const historyValue = project.authPasswordHistory;
28+
if (historyValue && historyValue > 0) {
29+
passwordHistory = historyValue;
30+
lastValidLimit = historyValue;
31+
}
32+
33+
passwordHistoryEnabled = (historyValue ?? 0) !== 0;
34+
passwordDictionary = project.authPasswordDictionary ?? false;
35+
authPersonalDataCheck = project.authPersonalDataCheck ?? false;
36+
});
37+
38+
$effect(() => {
39+
// restore last valid limit when enabling
40+
if (passwordHistoryEnabled && passwordHistory < 1) {
41+
passwordHistory = lastValidLimit;
42+
}
43+
});
44+
45+
const hasChanges = $derived.by(() => {
46+
const dictChanged = passwordDictionary !== (project.authPasswordDictionary ?? false);
47+
const dataCheckChanged = authPersonalDataCheck !== (project.authPersonalDataCheck ?? false);
48+
const historyChanged =
49+
passwordHistoryEnabled !== ((project.authPasswordHistory ?? 0) !== 0);
50+
const limitChanged =
51+
passwordHistoryEnabled &&
52+
Number(passwordHistory) !== (project.authPasswordHistory ?? 0);
53+
54+
return historyChanged || dictChanged || dataCheckChanged || limitChanged;
55+
});
56+
57+
async function updatePasswordPolicies() {
58+
try {
59+
const projectSdk = sdk.forConsole.projects;
60+
61+
await projectSdk.updateAuthPasswordHistory({
62+
projectId: project.$id,
63+
limit: passwordHistoryEnabled ? passwordHistory : 0
64+
});
65+
66+
await projectSdk.updateAuthPasswordDictionary({
67+
projectId: project.$id,
68+
enabled: passwordDictionary
69+
});
70+
71+
await projectSdk.updatePersonalDataCheck({
72+
projectId: project.$id,
73+
enabled: authPersonalDataCheck
74+
});
75+
76+
await invalidate(Dependencies.PROJECT);
77+
addNotification({
78+
type: 'success',
79+
message: 'Updated password policies.'
80+
});
81+
trackEvent(Submit.AuthPasswordHistoryUpdate);
82+
trackEvent(Submit.AuthPasswordDictionaryUpdate);
83+
trackEvent(Submit.AuthPersonalDataCheckUpdate);
84+
} catch (error) {
85+
addNotification({
86+
type: 'error',
87+
message: error.message
88+
});
89+
trackError(error, Submit.AuthPasswordHistoryUpdate);
90+
}
91+
}
92+
</script>
93+
94+
<Form onSubmit={updatePasswordPolicies}>
95+
<CardGrid gap="xxl">
96+
<svelte:fragment slot="title">Password policies</svelte:fragment>
97+
<svelte:fragment slot="aside">
98+
<InputSwitch
99+
bind:value={passwordHistoryEnabled}
100+
id="passwordHistoryEnabled"
101+
label="Password history">
102+
<svelte:fragment slot="description">
103+
<Layout.Stack gap="m">
104+
<Typography.Text>
105+
Enabling this option prevents users from reusing recent passwords by
106+
comparing the new password with their password history.
107+
</Typography.Text>
108+
{#if passwordHistoryEnabled}
109+
<InputNumber
110+
required
111+
max={20}
112+
min={1}
113+
autofocus
114+
label="Limit"
115+
id="password-history"
116+
bind:value={passwordHistory}
117+
helper="Maximum 20 passwords." />
118+
{/if}
119+
</Layout.Stack>
120+
</svelte:fragment>
121+
</InputSwitch>
122+
123+
<InputSwitch
124+
bind:value={passwordDictionary}
125+
id="passwordDictionary"
126+
label="Password dictionary">
127+
<svelte:fragment slot="description">
128+
<Typography.Text>
129+
Enabling this option prevents users from setting insecure passwords by
130+
comparing the user's password with the <Link.Anchor
131+
target="_blank"
132+
rel="noopener noreferrer"
133+
class="link"
134+
href="https://github.com/danielmiessler/SecLists/blob/master/Passwords/Common-Credentials/10k-most-common.txt"
135+
>10k most commonly used passwords.</Link.Anchor>
136+
</Typography.Text>
137+
</svelte:fragment>
138+
</InputSwitch>
139+
140+
<InputSwitch
141+
bind:value={authPersonalDataCheck}
142+
id="personalDataCheck"
143+
label="Disallow personal data">
144+
<svelte:fragment slot="description">
145+
<Typography.Text>
146+
Do not allow passwords that contain any part of the user's personal data.
147+
This includes the user's <Typography.Code>name</Typography.Code>, <Typography.Code
148+
>email</Typography.Code
149+
>, or <Typography.Code>phone</Typography.Code>.
150+
</Typography.Text>
151+
</svelte:fragment>
152+
</InputSwitch>
153+
</svelte:fragment>
154+
155+
<svelte:fragment slot="actions">
156+
<Button disabled={!hasChanges} submit>Update</Button>
157+
</svelte:fragment>
158+
</CardGrid>
159+
</Form>
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<script lang="ts">
2+
import { invalidate } from '$app/navigation';
3+
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
4+
import { CardGrid } from '$lib/components';
5+
import { Dependencies } from '$lib/constants';
6+
import { Button, Form, InputSwitch } from '$lib/elements/forms';
7+
import { addNotification } from '$lib/stores/notifications';
8+
import { sdk } from '$lib/stores/sdk';
9+
import { Typography } from '@appwrite.io/pink-svelte';
10+
import type { Models } from '@appwrite.io/console';
11+
import { onMount } from 'svelte';
12+
13+
let { project }: { project: Models.Project } = $props();
14+
15+
let authSessionAlerts = $state(false);
16+
let sessionInvalidation = $state(false);
17+
18+
onMount(() => {
19+
authSessionAlerts = project?.authSessionAlerts ?? false;
20+
sessionInvalidation = project?.authInvalidateSessions ?? false;
21+
});
22+
23+
const hasChanges = $derived.by(() => {
24+
const alertsChanged = authSessionAlerts !== (project?.authSessionAlerts ?? false);
25+
const invalidationChanged =
26+
sessionInvalidation !== (project?.authInvalidateSessions ?? false);
27+
return alertsChanged || invalidationChanged;
28+
});
29+
30+
async function updateSessionSecurity() {
31+
try {
32+
await sdk.forConsole.projects.updateSessionAlerts({
33+
projectId: project.$id,
34+
alerts: authSessionAlerts
35+
});
36+
await sdk.forConsole.projects.updateSessionInvalidation({
37+
projectId: project.$id,
38+
enabled: sessionInvalidation
39+
});
40+
41+
await invalidate(Dependencies.PROJECT);
42+
43+
addNotification({
44+
type: 'success',
45+
message: 'Updated session security settings.'
46+
});
47+
trackEvent(Submit.AuthSessionAlertsUpdate);
48+
trackEvent(Submit.AuthInvalidateSession);
49+
} catch (error) {
50+
addNotification({
51+
type: 'error',
52+
message: error.message
53+
});
54+
trackError(error, Submit.AuthSessionAlertsUpdate);
55+
}
56+
}
57+
</script>
58+
59+
<Form onSubmit={updateSessionSecurity}>
60+
<CardGrid gap="xxl">
61+
<svelte:fragment slot="title">Session security</svelte:fragment>
62+
<svelte:fragment slot="aside">
63+
<InputSwitch
64+
bind:value={authSessionAlerts}
65+
id="authSessionAlerts"
66+
label="Session alerts">
67+
<svelte:fragment slot="description">
68+
<Typography.Text>
69+
Enabling this option will send an email to the users when a new session is
70+
created.
71+
</Typography.Text>
72+
</svelte:fragment>
73+
</InputSwitch>
74+
75+
<InputSwitch
76+
bind:value={sessionInvalidation}
77+
id="invalidateSessions"
78+
label="Invalidate sessions">
79+
<svelte:fragment slot="description">
80+
<Typography.Text>
81+
Enabling this option will clear all existing sessions when the user changes
82+
their password.
83+
</Typography.Text>
84+
</svelte:fragment>
85+
</InputSwitch>
86+
</svelte:fragment>
87+
88+
<svelte:fragment slot="actions">
89+
<Button disabled={!hasChanges} submit>Update</Button>
90+
</svelte:fragment>
91+
</CardGrid>
92+
</Form>

src/routes/(console)/project-[region]-[project]/auth/security/updatePasswordDictionary.svelte

Lines changed: 0 additions & 61 deletions
This file was deleted.

0 commit comments

Comments
 (0)