diff --git a/authentik/core/api/applications.py b/authentik/core/api/applications.py index d3963e37e9..8acf6f78bb 100644 --- a/authentik/core/api/applications.py +++ b/authentik/core/api/applications.py @@ -11,7 +11,13 @@ from drf_spectacular.utils import ( inline_serializer, ) from rest_framework.decorators import action -from rest_framework.fields import CharField, FileField, IntegerField, ReadOnlyField +from rest_framework.fields import ( + BooleanField, + CharField, + FileField, + IntegerField, + ReadOnlyField, +) from rest_framework.parsers import MultiPartParser from rest_framework.request import Request from rest_framework.response import Response @@ -173,7 +179,11 @@ class ApplicationViewSet(ModelViewSet): @extend_schema( request={ "multipart/form-data": inline_serializer( - "SetIcon", fields={"file": FileField()} + "SetIcon", + fields={ + "file": FileField(required=False), + "clear": BooleanField(default=False), + }, ) }, responses={ @@ -193,11 +203,16 @@ class ApplicationViewSet(ModelViewSet): """Set application icon""" app: Application = self.get_object() icon = request.FILES.get("file", None) - if not icon: - return HttpResponseBadRequest() - app.meta_icon = icon - app.save() - return Response({}) + clear = request.data.get("clear", False) + if clear: + # .delete() saves the model by default + app.meta_icon.delete() + return Response({}) + if icon: + app.meta_icon = icon + app.save() + return Response({}) + return HttpResponseBadRequest() @permission_required("authentik_core.change_application") @extend_schema( diff --git a/authentik/flows/api/flows.py b/authentik/flows/api/flows.py index 21717ac578..4515da0988 100644 --- a/authentik/flows/api/flows.py +++ b/authentik/flows/api/flows.py @@ -10,7 +10,7 @@ from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import OpenApiResponse, extend_schema, inline_serializer from guardian.shortcuts import get_objects_for_user from rest_framework.decorators import action -from rest_framework.fields import FileField, ReadOnlyField +from rest_framework.fields import BooleanField, FileField, ReadOnlyField from rest_framework.parsers import MultiPartParser from rest_framework.request import Request from rest_framework.response import Response @@ -269,7 +269,11 @@ class FlowViewSet(ModelViewSet): @extend_schema( request={ "multipart/form-data": inline_serializer( - "SetIcon", fields={"file": FileField()} + "SetIcon", + fields={ + "file": FileField(required=False), + "clear": BooleanField(default=False), + }, ) }, responses={ @@ -288,12 +292,17 @@ class FlowViewSet(ModelViewSet): def set_background(self, request: Request, slug: str): """Set Flow background""" flow: Flow = self.get_object() - icon = request.FILES.get("file", None) - if not icon: - return HttpResponseBadRequest() - flow.background = icon - flow.save() - return Response({}) + background = request.FILES.get("file", None) + clear = request.data.get("clear", False) + if clear: + # .delete() saves the model by default + flow.background.delete() + return Response({}) + if background: + flow.background = background + flow.save() + return Response({}) + return HttpResponseBadRequest() @permission_required("authentik_core.change_application") @extend_schema( diff --git a/schema.yml b/schema.yml index 6e3f09b6aa..bcf4bc18f6 100644 --- a/schema.yml +++ b/schema.yml @@ -1474,7 +1474,6 @@ paths: multipart/form-data: schema: $ref: '#/components/schemas/SetIconRequest' - required: true security: - authentik: [] - cookieAuth: [] @@ -4519,7 +4518,6 @@ paths: multipart/form-data: schema: $ref: '#/components/schemas/SetIconRequest' - required: true security: - authentik: [] - cookieAuth: [] @@ -4612,7 +4610,6 @@ paths: multipart/form-data: schema: $ref: '#/components/schemas/SetIconRequest' - required: true security: - authentik: [] - cookieAuth: [] @@ -24890,8 +24887,9 @@ components: file: type: string format: binary - required: - - file + clear: + type: boolean + default: false SetIconURLRequest: type: object properties: diff --git a/web/src/locales/en.po b/web/src/locales/en.po index 36254bb979..1d20e8d2f5 100644 --- a/web/src/locales/en.po +++ b/web/src/locales/en.po @@ -593,6 +593,10 @@ msgstr "Clear Flow cache" msgid "Clear Policy cache" msgstr "Clear Policy cache" +#: src/pages/flows/FlowForm.ts +msgid "Clear background image" +msgstr "Clear background image" + #: src/pages/flows/FlowListPage.ts #: src/pages/flows/FlowListPage.ts #: src/pages/policies/PolicyListPage.ts @@ -600,6 +604,10 @@ msgstr "Clear Policy cache" msgid "Clear cache" msgstr "Clear cache" +#: src/pages/applications/ApplicationForm.ts +msgid "Clear icon" +msgstr "Clear icon" + #: src/elements/forms/HorizontalFormElement.ts msgid "Click to change value" msgstr "Click to change value" @@ -948,6 +956,7 @@ msgstr "Created {0}" msgid "Creation Date" msgstr "Creation Date" +#: src/pages/applications/ApplicationForm.ts #: src/pages/flows/FlowForm.ts msgid "Currently set to:" msgstr "Currently set to:" @@ -1040,6 +1049,14 @@ msgstr "Delete Session" msgid "Delete account" msgstr "Delete account" +#: src/pages/flows/FlowForm.ts +msgid "Delete currently set background image." +msgstr "Delete currently set background image." + +#: src/pages/applications/ApplicationForm.ts +msgid "Delete currently set icon." +msgstr "Delete currently set icon." + #: src/pages/sources/saml/SAMLSourceForm.ts msgid "Delete temporary users after" msgstr "Delete temporary users after" @@ -1744,6 +1761,7 @@ msgstr "IP" msgid "IP Reputation" msgstr "IP Reputation" +#: src/pages/applications/ApplicationForm.ts #: src/pages/applications/ApplicationForm.ts msgid "Icon" msgstr "Icon" diff --git a/web/src/locales/pseudo-LOCALE.po b/web/src/locales/pseudo-LOCALE.po index aa45951cb5..8de96db050 100644 --- a/web/src/locales/pseudo-LOCALE.po +++ b/web/src/locales/pseudo-LOCALE.po @@ -587,6 +587,10 @@ msgstr "" msgid "Clear Policy cache" msgstr "" +#: +msgid "Clear background image" +msgstr "" + #: #: #: @@ -594,6 +598,10 @@ msgstr "" msgid "Clear cache" msgstr "" +#: +msgid "Clear icon" +msgstr "" + #: msgid "Click to change value" msgstr "" @@ -942,6 +950,7 @@ msgstr "" msgid "Creation Date" msgstr "" +#: #: msgid "Currently set to:" msgstr "" @@ -1034,6 +1043,14 @@ msgstr "" msgid "Delete account" msgstr "" +#: +msgid "Delete currently set background image." +msgstr "" + +#: +msgid "Delete currently set icon." +msgstr "" + #: msgid "Delete temporary users after" msgstr "" @@ -1736,6 +1753,7 @@ msgstr "" msgid "IP Reputation" msgstr "" +#: #: msgid "Icon" msgstr "" diff --git a/web/src/pages/applications/ApplicationForm.ts b/web/src/pages/applications/ApplicationForm.ts index 880546e4fe..c988bcbd45 100644 --- a/web/src/pages/applications/ApplicationForm.ts +++ b/web/src/pages/applications/ApplicationForm.ts @@ -27,6 +27,9 @@ export class ApplicationForm extends ModelForm { @property({ attribute: false }) provider?: number; + @property({ type: Boolean }) + clearIcon = false; + getSuccessMessage(): string { if (this.instance) { return t`Successfully updated application.`; @@ -54,11 +57,12 @@ export class ApplicationForm extends ModelForm { return config().then((c) => { if (c.capabilities.includes(CapabilitiesEnum.SaveMedia)) { const icon = this.getFormFile(); - if (icon) { + if (icon || this.clearIcon) { return writeOp.then(app => { return new CoreApi(DEFAULT_CONFIG).coreApplicationsSetIconCreate({ slug: app.slug, - file: icon + file: icon, + clear: this.clearIcon, }); }); } @@ -186,7 +190,21 @@ export class ApplicationForm extends ModelForm { ${this.instance?.metaIcon ? html`

${t`Currently set to:`} ${this.instance?.metaIcon}

`: html``} - `; + + ${this.instance?.metaIcon ? html` + +
+ { + const target = ev.target as HTMLInputElement; + this.clearIcon = target.checked; + }}> + +
+

${t`Delete currently set icon.`}

+
+ `: html``}`; } return html` { } } + @property({ type: Boolean }) + clearBackground = false; + send = (data: Flow): Promise => { let writeOp: Promise; if (this.instance) { @@ -41,11 +44,12 @@ export class FlowForm extends ModelForm { return config().then((c) => { if (c.capabilities.includes(CapabilitiesEnum.SaveMedia)) { const icon = this.getFormFile(); - if (icon) { + if (icon || this.clearBackground) { return writeOp.then(app => { return new FlowsApi(DEFAULT_CONFIG).flowsInstancesSetBackgroundCreate({ slug: app.slug, - file: icon + file: icon, + clear: this.clearBackground, }); }); } @@ -143,7 +147,21 @@ export class FlowForm extends ModelForm {

${t`Currently set to:`} ${this.instance?.background}

`: html``}

${t`Background shown during execution.`}

-
`; + + ${this.instance?.background ? html` + +
+ { + const target = ev.target as HTMLInputElement; + this.clearBackground = target.checked; + }}> + +
+

${t`Delete currently set background image.`}

+
+ `: html``}`; } return html`