-
Notifications
You must be signed in to change notification settings - Fork 205
feat: New Grouped security cards #2747
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
1fa6722
44c693b
23529ff
dd9eab7
2d2b102
8d75905
7a595b1
0c65c0d
c687c00
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,26 +1,23 @@ | ||
| <script lang="ts"> | ||
| import { Container } from '$lib/layout'; | ||
| import type { PageProps } from './$types'; | ||
| 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'; | ||
|
|
||
| let { data }: PageProps = $props(); | ||
| </script> | ||
|
|
||
| <Container> | ||
| <UpdateUsersLimit /> | ||
| <UpdateSessionLength /> | ||
| <UpdateSessionsLimit /> | ||
| <UpdatePasswordHistory /> | ||
| <UpdatePasswordDictionary /> | ||
| <UpdatePersonalDataCheck /> | ||
| <UpdateSessionAlerts /> | ||
| <UpdateSessionInvalidation /> | ||
| <PasswordPolicies project={data.project} /> | ||
| <SessionSecurity project={data.project} /> | ||
| <UpdateMockNumbers /> | ||
| <UpdateMembershipPrivacy /> | ||
| </Container> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,159 @@ | ||
| <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 type { Models } from '@appwrite.io/console'; | ||
| import { onMount } from 'svelte'; | ||
|
|
||
| let { | ||
| project | ||
| }: { | ||
| project: Models.Project; | ||
| } = $props(); | ||
|
|
||
| let lastValidLimit = $state(5); | ||
| let passwordHistory = $state(5); | ||
| let passwordDictionary = $state(false); | ||
| let passwordHistoryEnabled = $state(false); | ||
| let authPersonalDataCheck = $state(false); | ||
|
|
||
| onMount(() => { | ||
| // update initial states here in onMount. | ||
| const historyValue = project.authPasswordHistory; | ||
| if (historyValue && historyValue > 0) { | ||
| passwordHistory = historyValue; | ||
| lastValidLimit = historyValue; | ||
| } | ||
|
|
||
| passwordHistoryEnabled = (historyValue ?? 0) !== 0; | ||
| passwordDictionary = project.authPasswordDictionary ?? false; | ||
| authPersonalDataCheck = project.authPersonalDataCheck ?? false; | ||
| }); | ||
|
|
||
| $effect(() => { | ||
| // restore last valid limit when enabling | ||
| if (passwordHistoryEnabled && passwordHistory < 1) { | ||
| passwordHistory = lastValidLimit; | ||
| } | ||
| }); | ||
|
|
||
| const hasChanges = $derived.by(() => { | ||
| const dictChanged = passwordDictionary !== (project.authPasswordDictionary ?? false); | ||
| const dataCheckChanged = authPersonalDataCheck !== (project.authPersonalDataCheck ?? false); | ||
| const historyChanged = | ||
| passwordHistoryEnabled !== ((project.authPasswordHistory ?? 0) !== 0); | ||
| const limitChanged = | ||
| passwordHistoryEnabled && | ||
| Number(passwordHistory) !== (project.authPasswordHistory ?? 0); | ||
|
|
||
| return historyChanged || dictChanged || dataCheckChanged || limitChanged; | ||
| }); | ||
|
|
||
| async function updatePasswordPolicies() { | ||
| try { | ||
| const projectSdk = sdk.forConsole.projects; | ||
|
|
||
| await projectSdk.updateAuthPasswordHistory({ | ||
| projectId: project.$id, | ||
| limit: passwordHistoryEnabled ? passwordHistory : 0 | ||
| }); | ||
|
|
||
| await projectSdk.updateAuthPasswordDictionary({ | ||
| projectId: project.$id, | ||
| enabled: passwordDictionary | ||
| }); | ||
|
|
||
| await projectSdk.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} | ||
| autofocus | ||
| label="Limit" | ||
| id="password-history" | ||
| bind:value={passwordHistory} | ||
| 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 prevents 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> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| <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 type { Models } from '@appwrite.io/console'; | ||
| import { onMount } from 'svelte'; | ||
| let { project }: { project: Models.Project } = $props(); | ||
| let authSessionAlerts = $state(false); | ||
| let sessionInvalidation = $state(false); | ||
| onMount(() => { | ||
| authSessionAlerts = project?.authSessionAlerts ?? false; | ||
| sessionInvalidation = project?.authInvalidateSessions ?? false; | ||
| }); | ||
|
Comment on lines
+18
to
+21
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replace The 🔎 Recommended fix using $effect- onMount(() => {
+ $effect(() => {
authSessionAlerts = project?.authSessionAlerts ?? false;
sessionInvalidation = project?.authInvalidateSessions ?? false;
});Remove the - import { onMount } from 'svelte';🤖 Prompt for AI Agents |
||
| const hasChanges = $derived.by(() => { | ||
| const alertsChanged = authSessionAlerts !== (project?.authSessionAlerts ?? false); | ||
| const invalidationChanged = | ||
| sessionInvalidation !== (project?.authInvalidateSessions ?? false); | ||
| return alertsChanged || invalidationChanged; | ||
| }); | ||
| async function updateSessionSecurity() { | ||
| try { | ||
| await sdk.forConsole.projects.updateSessionAlerts({ | ||
| projectId: project.$id, | ||
| alerts: authSessionAlerts | ||
| }); | ||
| 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.AuthInvalidateSession); | ||
| } catch (error) { | ||
| addNotification({ | ||
| type: 'error', | ||
| message: error.message | ||
| }); | ||
| trackError(error, Submit.AuthSessionAlertsUpdate); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Error tracking doesn't distinguish between API call failures. Line 54 always tracks 🔎 Suggested approachIf you parallelize the API calls as suggested above, you could track errors individually: try {
- await sdk.forConsole.projects.updateSessionAlerts({
- projectId: project.$id,
- alerts: authSessionAlerts
- });
- await sdk.forConsole.projects.updateSessionInvalidation({
- projectId: project.$id,
- enabled: sessionInvalidation
- });
+ await Promise.all([
+ sdk.forConsole.projects.updateSessionAlerts({
+ projectId: project.$id,
+ alerts: authSessionAlerts
+ }),
+ 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.AuthInvalidateSession);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
- trackError(error, Submit.AuthSessionAlertsUpdate);
+ // Track both events since we can't distinguish which failed
+ trackError(error, Submit.AuthSessionAlertsUpdate);
+ trackError(error, Submit.AuthInvalidateSession);
}
🤖 Prompt for AI Agents |
||
| } | ||
| } | ||
| </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> | ||
This file was deleted.
Uh oh!
There was an error while loading. Please reload this page.