mirror of
https://github.com/goauthentik/authentik.git
synced 2026-06-17 19:09:11 +03:00
web: fix file upload form (#18808)
* web: fix file upload form name mismatch and modal submit promise handling Fixes the following error: FileUploadForm.ts:74 POST http://authentik.localhost:9000/api/v3/admin/file/ 405 (Method Not Allowed) (anonymous) @ fetch.ts:81 fetchApi @ runtime.ts:206 await in fetchApi request @ runtime.ts:136 await in request adminFileCreateRaw @ AdminApi.ts:191 adminFileCreate @ AdminApi.ts:206 send @ FileUploadForm.ts:74 submit @ Form.ts:363 (anonymous) @ ModalForm.ts:54 handleEvent @ lit-html.ts:2109 n @ helpers.ts:117Understand this error Form.ts:403 authentik/forms: API rejected the form submission due to an invalid field that doesn't appear to be in the form. This is likely a bug in authentik. {detail: 'Response returned an error code'} (anonymous) @ console.ts:39 (anonymous) @ Form.ts:403 Promise.catch submit @ Form.ts:376 (anonymous) @ ModalForm.ts:54 handleEvent @ lit-html.ts:2109 n @ helpers.ts:117Understand this error runtime.ts:140 Uncaught (in promise) ResponseError: Response returned an error code at mR.request (runtime.ts:140:15) at async mR.adminFileCreateRaw (AdminApi.ts:191:26) at async mR.adminFileCreate (AdminApi.ts:206:9) - align file upload rename field with api name so validation errors map correctly -improve custom filename extension logic to avoid double or incorrect extensions - prevent unhandled promise rejections from modal submit click handler and show missing-form errors to users * rev * wip * Update ModalForm.ts Signed-off-by: Dominic R <dominic@sdko.org> * scope better * fix what it validates against --------- Signed-off-by: Dominic R <dominic@sdko.org>
This commit is contained in:
@@ -16,18 +16,33 @@ import { createRef, ref } from "lit/directives/ref.js";
|
||||
|
||||
// Same regex is used in the backend as well
|
||||
const VALID_FILE_NAME_PATTERN = /^[a-zA-Z0-9._/-]+$/;
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/source
|
||||
// This is perfect for the "pattern" attribute
|
||||
const VALID_FILE_NAME_PATTERN_STRING = VALID_FILE_NAME_PATTERN.source;
|
||||
|
||||
// Note: browsers compile `pattern` using the new `v` RegExp flag (Unicode sets). Under `/v`,
|
||||
// both `/` and `-` must be escaped inside character classes.
|
||||
const VALID_FILE_NAME_PATTERN_STRING = "^[a-zA-Z0-9._\\/\\-]+$";
|
||||
|
||||
function assertValidFileName(fileName: string): void {
|
||||
if (!VALID_FILE_NAME_PATTERN.test(fileName)) {
|
||||
throw new Error(
|
||||
msg("Filename can only contain letters, numbers, dots, hyphens, and underscores"),
|
||||
msg(
|
||||
"Filename can only contain letters, numbers, dots, hyphens, underscores, and slashes",
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function getFileExtension(fileName: string): string {
|
||||
const lastDot = fileName.lastIndexOf(".");
|
||||
if (lastDot <= 0) return "";
|
||||
return fileName.slice(lastDot);
|
||||
}
|
||||
|
||||
function hasBasenameExtension(fileName: string): boolean {
|
||||
const baseName = fileName.split("/").pop() ?? fileName;
|
||||
const lastDot = baseName.lastIndexOf(".");
|
||||
return lastDot > 0;
|
||||
}
|
||||
|
||||
@customElement("ak-file-upload-form")
|
||||
export class FileUploadForm extends Form<Record<string, unknown>> {
|
||||
@property({ type: String, useDefault: true })
|
||||
@@ -57,36 +72,36 @@ export class FileUploadForm extends Form<Record<string, unknown>> {
|
||||
throw new PreventFormSubmit("Selected file not provided", this);
|
||||
}
|
||||
|
||||
assertValidFileName(this.selectedFile.name);
|
||||
|
||||
const api = new AdminApi(DEFAULT_CONFIG);
|
||||
const customName = typeof data.fileName === "string" ? data.fileName.trim() : "";
|
||||
const customName = typeof data.name === "string" ? data.name.trim() : "";
|
||||
|
||||
// If custom name provided, validate and append original extension
|
||||
// Only validate the original filename if no custom name is provided
|
||||
let finalName = this.selectedFile.name;
|
||||
if (customName) {
|
||||
assertValidFileName(customName);
|
||||
const ext = this.selectedFile.name.substring(this.selectedFile.name.lastIndexOf("."));
|
||||
finalName = customName + ext;
|
||||
const ext = getFileExtension(this.selectedFile.name);
|
||||
finalName =
|
||||
ext && !hasBasenameExtension(customName) ? `${customName}${ext}` : customName;
|
||||
} else {
|
||||
assertValidFileName(this.selectedFile.name);
|
||||
}
|
||||
|
||||
return api
|
||||
.adminFileCreate({
|
||||
file: this.selectedFile,
|
||||
name: finalName,
|
||||
usage: this.usage,
|
||||
})
|
||||
.then(() => {
|
||||
showMessage({
|
||||
level: MessageLevel.success,
|
||||
message: msg("File uploaded successfully"),
|
||||
});
|
||||
assertValidFileName(finalName);
|
||||
|
||||
this.reset();
|
||||
})
|
||||
.finally(() => {
|
||||
this.clearFileInput();
|
||||
});
|
||||
await api.adminFileCreate({
|
||||
file: this.selectedFile,
|
||||
name: finalName,
|
||||
usage: this.usage,
|
||||
});
|
||||
|
||||
showMessage({
|
||||
level: MessageLevel.success,
|
||||
message: msg("File uploaded successfully"),
|
||||
});
|
||||
|
||||
this.reset();
|
||||
this.clearFileInput();
|
||||
}
|
||||
|
||||
renderForm() {
|
||||
@@ -101,7 +116,7 @@ export class FileUploadForm extends Form<Record<string, unknown>> {
|
||||
@change=${this.#fileChangeListener}
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("File Name")} name="fileName">
|
||||
<ak-form-element-horizontal label=${msg("File Name")} name="name">
|
||||
<input
|
||||
type="text"
|
||||
class="pf-c-form-control"
|
||||
|
||||
Reference in New Issue
Block a user