Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src-tauri/proto
Submodule proto updated 1 files
+7 −0 core/proxy.proto
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,12 @@ export const AddInstanceInitForm = ({ nextStep }: Props) => {
);
}
debug('Response received with status OK');
const r = (await res.json()) as EnrollmentStartResponse;
const startResponse = (await res.json()) as EnrollmentStartResponse;
// get client registered instances
const clientInstances = await clientApi.getInstances();
const instance = clientInstances.find((i) => i.uuid === r.instance.id);
const instance = clientInstances.find(
(i) => i.uuid === startResponse.instance.id,
);
let proxy_api_url = values.url;
if (proxy_api_url[proxy_api_url.length - 1] === '/') {
proxy_api_url = proxy_api_url.slice(0, -1);
Expand Down Expand Up @@ -191,24 +193,29 @@ export const AddInstanceInitForm = ({ nextStep }: Props) => {
}
// register new instance
// is user in need of full enrollment ?
if (r.user.enrolled) {
if (startResponse.user.enrolled) {
//no, only create new device for desktop client
debug('User already active, adding device only.');
nextStep({
url: proxy_api_url,
cookie: authCookie,
device_names: r.user.device_names,
device_names: startResponse.user.device_names,
});
} else {
// yes, enroll the user
debug('User is not active. Starting enrollment.');
const sessionEnd = dayjs.unix(r.deadline_timestamp).utc().local().format();
const sessionEnd = dayjs
.unix(startResponse.deadline_timestamp)
.utc()
.local()
.format();
const sessionStart = dayjs().local().format();
initEnrollment({
userInfo: r.user,
adminInfo: r.admin,
endContent: r.final_page_content,
userInfo: startResponse.user,
adminInfo: startResponse.admin,
endContent: startResponse.final_page_content,
proxy_url: proxy_api_url,
enrollmentSettings: startResponse.settings,
sessionEnd,
sessionStart,
cookie: authCookie,
Expand Down
4 changes: 0 additions & 4 deletions src/pages/enrollment/EnrollmentPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,6 @@ export const EnrollmentPage = () => {
enrollmentFinished.current = currentStep === EnrollmentStepKey.FINISH;
}, [currentStep]);

useEffect(() => {
console.log(currentStepConfig);
}, [currentStepConfig]);

return (
<PageContainer id="enrollment">
<EnrollmentSideBar />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ type SideBarItem = {
export const EnrollmentSideBar = () => {
const { LL } = useI18nContext();

const enrollmentSettings = useEnrollmentStore((s) => s.enrollmentSettings);

const vpnOptional = useEnrollmentStore((state) => state.vpnOptional);

const currentStep = useEnrollmentStore((state) => state.step);
Expand All @@ -41,7 +43,7 @@ export const EnrollmentSideBar = () => {
case EnrollmentStepKey.DEVICE:
return `${stepsLL.vpn()}${vpnOptional ? '*' : ''}`;
case EnrollmentStepKey.MFA:
return stepsLL.mfa();
return `${stepsLL.mfa()}${enrollmentSettings.mfa_required ? '' : '*'}`;
case EnrollmentStepKey.MFA_CHOICE:
return stepsLL.mfaChoice();
case EnrollmentStepKey.MFA_SETUP:
Expand All @@ -56,7 +58,7 @@ export const EnrollmentSideBar = () => {
return '';
}
},
[LL.pages.enrollment.sideBar.steps, vpnOptional],
[LL.pages.enrollment.sideBar.steps, vpnOptional, enrollmentSettings.mfa_required],
);

const stepsData = useMemo(
Expand Down
14 changes: 13 additions & 1 deletion src/pages/enrollment/hooks/store/useEnrollmentStore.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import type { Dayjs } from 'dayjs';
import { pick } from 'lodash-es';
import { Subject } from 'rxjs';
import { createJSONStorage, persist } from 'zustand/middleware';
import { createWithEqualityFn } from 'zustand/traditional';
import type {
AdminInfo,
CreateDeviceResponse,
EnrollmentSettings,
UserInfo,
} from '../../../../shared/hooks/api/types';
import { MfaMethod } from '../../../../shared/types';
Expand All @@ -15,6 +17,14 @@ const defaultValues: StoreValues = {
// assume default dev
proxy_url: '/api/v1/',
loading: false,
enrollmentSettings: {
admin_device_management: false,
mfa_required: false,
only_client_activation: false,
smtp_configured: false,
vpn_setup_optional: true,
},
emailResendTimestamp: undefined,
step: EnrollmentStepKey.WELCOME,
mfaMethod: MfaMethod.TOTP,
recoveryCodes: [],
Expand Down Expand Up @@ -46,6 +56,7 @@ const persistKeys: Array<keyof StoreValues> = [
'deviceKeys',
'deviceResponse',
'cookie',
'enrollmentSettings',
];

export const useEnrollmentStore = createWithEqualityFn<Store>()(
Expand Down Expand Up @@ -75,10 +86,11 @@ export const useEnrollmentStore = createWithEqualityFn<Store>()(
type Store = StoreValues & StoreMethods;

type StoreValues = {
// next and back are disabled
loading: boolean;
enrollmentSettings: EnrollmentSettings;
step: EnrollmentStepKey;
mfaMethod: MfaMethod;
emailResendTimestamp?: Dayjs;
nextSubject: Subject<EnrollmentNavDirection>;
// Date
proxy_url: string;
Expand Down
26 changes: 15 additions & 11 deletions src/pages/enrollment/steps/ChooseMfaStep/ChooseMfaStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { EnrollmentNavDirection } from '../../hooks/types';

export const ChooseMfaStep = () => {
const selectedMethod = useEnrollmentStore((s) => s.mfaMethod);
const enrollmentSettings = useEnrollmentStore((s) => s.enrollmentSettings);
const [setStore, navSubject] = useEnrollmentStore(
(s) => [s.setState, s.nextSubject],
shallow,
Expand Down Expand Up @@ -55,6 +56,7 @@ export const ChooseMfaStep = () => {
}}
/>
<LabeledRadio
disabled={!enrollmentSettings.smtp_configured}
active={selectedMethod === MfaMethod.EMAIL}
label="Email"
onClick={() => {
Expand All @@ -63,17 +65,19 @@ export const ChooseMfaStep = () => {
/>
</div>
<div className="controls">
<Button
size={ButtonSize.SMALL}
styleVariant={ButtonStyleVariant.LINK}
text="Skip MFA Setup"
onClick={() => {
setStore({
loading: false,
step: EnrollmentStepKey.ACTIVATE_USER,
});
}}
/>
{!enrollmentSettings.mfa_required && (
<Button
size={ButtonSize.SMALL}
styleVariant={ButtonStyleVariant.LINK}
text="Skip MFA Setup"
onClick={() => {
setStore({
loading: false,
step: EnrollmentStepKey.ACTIVATE_USER,
});
}}
/>
)}
<Button
text="Setup selected MFA"
size={ButtonSize.SMALL}
Expand Down
71 changes: 71 additions & 0 deletions src/pages/enrollment/steps/MfaSetupStep/MfaSetupEmail.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import dayjs, { type Dayjs } from 'dayjs';
import { type ReactNode, useCallback, useEffect, useState } from 'react';
import { interval } from 'rxjs';
import { Button } from '../../../../shared/defguard-ui/components/Layout/Button/Button';
import {
ButtonSize,
ButtonStyleVariant,
} from '../../../../shared/defguard-ui/components/Layout/Button/types';
import { MessageBox } from '../../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox';
import { useToaster } from '../../../../shared/defguard-ui/hooks/toasts/useToaster';
import { useEnrollmentStore } from '../../hooks/store/useEnrollmentStore';

type Props = {
children: ReactNode;
userEmail: string;
refetch: () => void;
};

export const MfaSetupEmail = ({ children, userEmail, refetch }: Props) => {
const toaster = useToaster();
const checkResend = useCallback((resend: Dayjs | undefined) => {
if (resend) {
const cd = resend.add(1, 'minute');
return cd.isBefore(dayjs());
}
return true;
}, []);

const resendTimestamp = useEnrollmentStore((s) => s.emailResendTimestamp);
const setStore = useEnrollmentStore((s) => s.setState);

const [canResent, setCanResend] = useState(checkResend(resendTimestamp));

useEffect(() => {
const sub = interval(1000).subscribe(() => {
if (resendTimestamp) {
setCanResend(checkResend(resendTimestamp));
}
});
return () => {
sub.unsubscribe();
};
}, [resendTimestamp, checkResend]);

return (
<div className="email-setup setup-container">
<MessageBox>
<p>
To setup your MFA, enter the code that was sent to your account email:
<br />
<strong>{userEmail}</strong>
</p>
</MessageBox>
{children}
<Button
className="resend"
disabled={!canResent}
styleVariant={ButtonStyleVariant.STANDARD}
size={ButtonSize.LARGE}
text="Resend Email"
onClick={() => {
setStore({
emailResendTimestamp: dayjs(),
});
refetch();
toaster.success('Email resent');
}}
/>
</div>
);
};
Loading