mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
0f4f0d3dad
Summary: Moves some auth-related UI components, like MFAConfig, out of core, and adds a new ChangePasswordDialog component for allowing direct password changes, replacing the old reset password link to hosted Cognito. Updates all MFA endpoints to use SRP for authentication. Also refactors MFAConfig into smaller files, and polishes up some parts of the UI to be more consistent with the login pages. Test Plan: New server and deployment tests. Updated existing tests. Reviewers: paulfitz Reviewed By: paulfitz Differential Revision: https://phab.getgrist.com/D3311
73 lines
2.4 KiB
TypeScript
73 lines
2.4 KiB
TypeScript
import {reportError} from 'app/client/models/errors';
|
|
import {ApiError} from 'app/common/ApiError';
|
|
import {BaseAPI} from 'app/common/BaseAPI';
|
|
import {dom, Observable} from 'grainjs';
|
|
|
|
/**
|
|
* Handles submission of an HTML form element.
|
|
*
|
|
* When the form is submitted, `onSubmit` will be called, followed by
|
|
* either `onSuccess` or `onError`, depending on whether `onSubmit` threw any
|
|
* unhandled errors. The `pending` observable is set to true until `onSubmit`
|
|
* resolves.
|
|
*/
|
|
export function handleSubmit<T>(
|
|
pending: Observable<boolean>,
|
|
onSubmit: (fields: { [key: string]: string }, form: HTMLFormElement) => Promise<T> = submitForm,
|
|
onSuccess: (v: T) => void = () => { /* noop */ },
|
|
onError: (e: unknown) => void = (e) => reportError(e as string | Error)
|
|
): (elem: HTMLFormElement) => void {
|
|
return dom.on('submit', async (e, form) => {
|
|
e.preventDefault();
|
|
try {
|
|
if (pending.get()) { return; }
|
|
|
|
pending.set(true);
|
|
const result = await onSubmit(formDataToObj(form), form).finally(() => pending.set(false));
|
|
onSuccess(result);
|
|
} catch (err) {
|
|
onError(err);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Convert a form to a JSON-stringifiable object, ignoring any File fields.
|
|
*/
|
|
export function formDataToObj(formElem: HTMLFormElement): { [key: string]: string } {
|
|
// Use FormData to collect values (rather than e.g. finding <input> elements) to ensure we get
|
|
// values from all form items correctly (e.g. checkboxes and textareas).
|
|
const formData = new FormData(formElem);
|
|
const data: { [key: string]: string } = {};
|
|
for (const [name, value] of formData.entries()) {
|
|
if (typeof value === 'string') {
|
|
data[name] = value;
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* Submit a form using BaseAPI. Send inputs as JSON, and interpret any reply as JSON.
|
|
*/
|
|
export async function submitForm(fields: { [key: string]: string }, form: HTMLFormElement): Promise<any> {
|
|
return BaseAPI.requestJson(form.action, {method: 'POST', body: JSON.stringify(fields)});
|
|
}
|
|
|
|
/**
|
|
* Sets the error details on `errObs` if `err` is a 4XX error (except 401). Otherwise, reports the
|
|
* error via the Notifier instance.
|
|
*/
|
|
export function handleFormError(err: unknown, errObs: Observable<string|null>) {
|
|
if (
|
|
err instanceof ApiError &&
|
|
err.status !== 401 &&
|
|
err.status >= 400 &&
|
|
err.status < 500
|
|
) {
|
|
errObs.set(err.details?.userError ?? err.message);
|
|
} else {
|
|
reportError(err as Error|string);
|
|
}
|
|
}
|