Add support for personalized auth screen
This commit is contained in:
parent
f2469f9353
commit
29262afadf
@ -16,6 +16,7 @@
|
||||
v-if="!disabled"
|
||||
:endpoint="endpoint"
|
||||
:labels="labels"
|
||||
:accepted-files="['image/*']"
|
||||
@upload="$emit('upload', $event)"
|
||||
/>
|
||||
</b-form>
|
||||
|
||||
@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<b-card
|
||||
data-test-id="card-edit-authentication"
|
||||
header-bg-variant="white"
|
||||
footer-bg-variant="white"
|
||||
class="shadow-sm"
|
||||
>
|
||||
<template #header>
|
||||
<h3 class="m-0">
|
||||
{{ $t("title") }}
|
||||
</h3>
|
||||
</template>
|
||||
|
||||
<b-row cols="12">
|
||||
<b-col cols="6">
|
||||
<div class="shadow-sm">
|
||||
<div class="d-flex justify-content-between">
|
||||
<h5>
|
||||
{{ $t("image.uploader.title") }}
|
||||
</h5>
|
||||
|
||||
<b-button
|
||||
v-if="uploadedFile('auth.ui.background-image-src')"
|
||||
variant="link"
|
||||
class="d-flex align-items-top text-dark p-1"
|
||||
@click="$emit('resetAttachment', 'auth.ui.background-image-src')"
|
||||
>
|
||||
<font-awesome-icon :icon="['far', 'trash-alt']" />
|
||||
</b-button>
|
||||
</div>
|
||||
|
||||
<c-uploader-with-preview
|
||||
:value="uploadedFile('auth.ui.background-image-src')"
|
||||
:endpoint="'/settings/auth.ui.background-image-src'"
|
||||
:disabled="!canManage"
|
||||
:labels="$t('image.uploader', { returnObjects: true })"
|
||||
@upload="$emit('onUpload')"
|
||||
@clear="$emit('resetAttachment', 'auth.ui.background-image-src')"
|
||||
/>
|
||||
</div>
|
||||
</b-col>
|
||||
|
||||
<b-col cols="6">
|
||||
<div class="shadow-sm">
|
||||
<h5>
|
||||
{{ $t("image.editor.title") }}
|
||||
</h5>
|
||||
|
||||
<ace-editor
|
||||
data-test-id="auth-bg-image-styling-editor"
|
||||
:font-size="14"
|
||||
:show-print-margin="true"
|
||||
:show-gutter="true"
|
||||
:highlight-active-line="true"
|
||||
width="100%"
|
||||
height="200px"
|
||||
mode="css"
|
||||
theme="chrome"
|
||||
name="editor/css"
|
||||
:on-change="v => (settings['auth.ui.styles'] = v)"
|
||||
:value="settings['auth.ui.styles']"
|
||||
:editor-props="{
|
||||
$blockScrolling: false
|
||||
}"
|
||||
/>
|
||||
|
||||
<c-submit-button
|
||||
:disabled="!canManage"
|
||||
:processing="processing"
|
||||
:success="success"
|
||||
class="float-right mt-2"
|
||||
@submit="$emit('submit', settings['auth.ui.styles'])"
|
||||
/>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Ace as AceEditor } from 'vue2-brace-editor'
|
||||
import CUploaderWithPreview from 'corteza-webapp-admin/src/components/CUploaderWithPreview'
|
||||
import CSubmitButton from 'corteza-webapp-admin/src/components/CSubmitButton'
|
||||
|
||||
import 'brace/mode/css'
|
||||
import 'brace/theme/chrome'
|
||||
|
||||
export default {
|
||||
name: 'CSystemEditorAuthBgImage',
|
||||
|
||||
i18nOptions: {
|
||||
namespaces: 'system.settings',
|
||||
keyPrefix: 'editor.bgScreen',
|
||||
},
|
||||
|
||||
components: {
|
||||
CUploaderWithPreview,
|
||||
AceEditor,
|
||||
CSubmitButton,
|
||||
},
|
||||
|
||||
props: {
|
||||
settings: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
|
||||
canManage: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
|
||||
processing: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
|
||||
success: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
uploadedFile (name) {
|
||||
const localAttachment = /^attachment:(\d+)/
|
||||
|
||||
switch (true) {
|
||||
case this.settings[name] && localAttachment.test(this.settings[name]):
|
||||
const [, attachmentID] = localAttachment.exec(this.settings[name])
|
||||
|
||||
return (
|
||||
this.$SystemAPI.baseURL +
|
||||
this.$SystemAPI.attachmentOriginalEndpoint({
|
||||
attachmentID,
|
||||
kind: 'settings',
|
||||
name,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
return undefined
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -22,12 +22,22 @@
|
||||
:can-manage="canManage"
|
||||
@submit="onExternalSubmit"
|
||||
/>
|
||||
|
||||
<c-system-editor-auth-bg-screen
|
||||
:settings="getAuthBackground"
|
||||
:can-manage="canManage"
|
||||
class="mt-3"
|
||||
@onUpload="onBackgroundImageUpload"
|
||||
@resetAttachment="onResetBackgroundImage"
|
||||
@submit="onAuthBackgroundSubmit"
|
||||
/>
|
||||
</b-container>
|
||||
</template>
|
||||
<script>
|
||||
import editorHelpers from 'corteza-webapp-admin/src/mixins/editorHelpers'
|
||||
import CSystemEditorAuth from 'corteza-webapp-admin/src/components/Settings/System/CSystemEditorAuth'
|
||||
import CSystemEditorExternal from 'corteza-webapp-admin/src/components/Settings/System/CSystemEditorExternal'
|
||||
import CSystemEditorAuthBgScreen from 'corteza-webapp-admin/src/components/Settings/System/CSystemEditorAuthBgScreen'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
@ -39,6 +49,7 @@ export default {
|
||||
components: {
|
||||
CSystemEditorAuth,
|
||||
CSystemEditorExternal,
|
||||
CSystemEditorAuthBgScreen,
|
||||
},
|
||||
|
||||
mixins: [
|
||||
@ -58,6 +69,11 @@ export default {
|
||||
processing: false,
|
||||
success: false,
|
||||
},
|
||||
|
||||
authBackground: {
|
||||
processing: false,
|
||||
success: false,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@ -71,18 +87,13 @@ export default {
|
||||
},
|
||||
|
||||
getAuth () {
|
||||
if (this.settings.length > 0) {
|
||||
return this.settings.reduce((map, obj) => {
|
||||
const { name, value } = obj
|
||||
const split = name.split('.')
|
||||
if (split[0] === 'auth' && split[1] !== 'external') {
|
||||
map[name] = value
|
||||
}
|
||||
return map
|
||||
}, {})
|
||||
}
|
||||
return {}
|
||||
return this.filterSettings('auth')
|
||||
},
|
||||
|
||||
getAuthBackground () {
|
||||
return this.filterSettings('auth.ui')
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
created () {
|
||||
@ -106,6 +117,19 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
filterSettings (prefix) {
|
||||
if (this.settings.length > 0) {
|
||||
return this.settings.reduce((map, obj) => {
|
||||
const { name, value } = obj
|
||||
if (name.startsWith(prefix)) {
|
||||
map[name] = value
|
||||
}
|
||||
return map
|
||||
}, {})
|
||||
}
|
||||
return {}
|
||||
},
|
||||
|
||||
onAuthSubmit (auth) {
|
||||
this.auth.processing = true
|
||||
|
||||
@ -130,7 +154,9 @@ export default {
|
||||
this.$SystemAPI.settingsUpdate({ values: external })
|
||||
.then(() => {
|
||||
this.animateSuccess('external')
|
||||
this.toastSuccess(this.$t('notification:settings.system.external.success'))
|
||||
this.toastSuccess(
|
||||
this.$t('notification:settings.system.external.success')
|
||||
)
|
||||
})
|
||||
.catch(this.toastErrorHandler(this.$t('notification:settings.system.external.error')))
|
||||
.finally(() => {
|
||||
@ -138,6 +164,39 @@ export default {
|
||||
this.fetchSettings()
|
||||
})
|
||||
},
|
||||
|
||||
onBackgroundImageUpload () {
|
||||
this.fetchSettings()
|
||||
},
|
||||
|
||||
onResetBackgroundImage (name) {
|
||||
this.$SystemAPI.settingsUpdate({ values: [{ name, value: undefined }], upload: {} })
|
||||
.then(() => {
|
||||
this.fetchSettings()
|
||||
})
|
||||
},
|
||||
|
||||
onAuthBackgroundSubmit (authBackground) {
|
||||
this.authBackground.processing = true
|
||||
|
||||
const values = ([
|
||||
{
|
||||
name: 'auth.ui.styles',
|
||||
value: authBackground,
|
||||
},
|
||||
])
|
||||
|
||||
this.$SystemAPI.settingsUpdate({ values })
|
||||
.then(() => {
|
||||
this.animateSuccess('authBackground')
|
||||
this.toastSuccess(this.$t('notification:settings.system.bgScreen.style.success'))
|
||||
this.$Settings.fetch()
|
||||
})
|
||||
.catch(this.toastErrorHandler(this.$t('notification:settings.system.bgScreen.style.error')))
|
||||
.finally(() => {
|
||||
this.authBackground.processing = false
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -231,6 +231,10 @@ settings:
|
||||
external:
|
||||
success: External settings updated
|
||||
error: External settings update failed
|
||||
bgScreen:
|
||||
style:
|
||||
success: Auth background styles updated
|
||||
error: Auth background styles update failed
|
||||
compose:
|
||||
fetch:
|
||||
error: Compose settings fetch failed
|
||||
|
||||
@ -122,3 +122,15 @@ editor:
|
||||
permitted-roles:
|
||||
description: Only roles in this list will be added into security context when authenticates with this provider
|
||||
label: Permitted roles
|
||||
|
||||
bgScreen:
|
||||
title: Auth Background Screen
|
||||
|
||||
image:
|
||||
uploader:
|
||||
title: Background Image
|
||||
instructions: Click or drop background image here to upload
|
||||
uploading: Uploading auth background image
|
||||
|
||||
editor:
|
||||
title: Styles editor
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -611,6 +612,10 @@ func updateAuthSettings(svc authServicer, current *types.AppSettings) {
|
||||
Enforced: current.Auth.MultiFactor.EmailOTP.Enforced,
|
||||
},
|
||||
},
|
||||
BackgroundUI: authSettings.BackgroundUI{
|
||||
BackgroundImageSrcUrl: setAuthBgImageSrcUrl(current.Auth.UI.BackgroundImageSrc),
|
||||
Styles: setAuthBgStyles(current.Auth.UI.Styles),
|
||||
},
|
||||
}
|
||||
|
||||
for _, p := range current.Auth.External.Providers {
|
||||
@ -880,3 +885,25 @@ func setupSmtpDialer(log *zap.Logger, servers ...types.SmtpServers) {
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
func setAuthBgImageSrcUrl(imgAttachment string) string {
|
||||
if imgAttachment == "" {
|
||||
return ""
|
||||
}
|
||||
imgAttachmentValues := strings.Split(imgAttachment, ":")
|
||||
imgSrcUrl := fmt.Sprintf("/api/system/%s/settings/%s/original/auth.ui.background-image-src", imgAttachmentValues[0], imgAttachmentValues[1])
|
||||
return imgSrcUrl
|
||||
}
|
||||
|
||||
func setAuthBgStyles(styles string) string {
|
||||
re := regexp.MustCompile(`\{(.*?)\}`)
|
||||
|
||||
styles = strings.Replace(styles, "\n", "", -1)
|
||||
matches := re.FindAllStringSubmatch(styles, -1)
|
||||
|
||||
if matches != nil {
|
||||
return matches[0][1]
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
@ -1,32 +1,44 @@
|
||||
<!doctype html>
|
||||
<html lang="{{ language }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- Bootstrap icons -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.3.0/font/bootstrap-icons.css">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@500;700&display=swap" rel="stylesheet">
|
||||
<!-- Custom CSS -->
|
||||
<link href="{{ links.AuthAssets }}/style.css?{{ buildtime }}" rel="stylesheet">
|
||||
<title>Corteza</title>
|
||||
</head>
|
||||
<body style="background: url({{ links.Assets }}/release-background.png) no-repeat top;background-size: cover;background-attachment: fixed;">
|
||||
<header>
|
||||
{{ if .user }}
|
||||
<div class="float-right text-white m-2">
|
||||
<a class="font-weight-bold text-white" href="{{ links.Base }}"><i class="bi bi-grid-3x2-gap-fill text-white mr-1 align-middle" style="font-size: 1.4rem;"></i></a>
|
||||
{{ tr "inc_header.logged-in-as" }}
|
||||
<a data-test-id="link-redirect-to-profile" class="font-weight-bold text-white" href="{{ links.Profile }}">{{ coalesce .user.Name .user.Handle .user.Email }}</a>
|
||||
|
|
||||
<a data-test-id="link-logout" class="font-weight-bold text-white" href="{{ links.Logout }}">{{ tr "inc_header.logout" }}</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
</header>
|
||||
|
||||
<main class="auth mt-sm-5">
|
||||
<div class="card">
|
||||
{{ template "inc_nav.html.tpl" . }}
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- Bootstrap icons -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.3.0/font/bootstrap-icons.css">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@500;700&display=swap" rel="stylesheet">
|
||||
<!-- Custom CSS -->
|
||||
<link href="{{ links.AuthAssets }}/style.css?{{ buildtime }}" rel="stylesheet">
|
||||
<title>Corteza</title>
|
||||
<style>
|
||||
body {
|
||||
{{ safeCSS .authBg }}
|
||||
background-size: cover;
|
||||
background-attachment: fixed;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
{{ if .user }}
|
||||
<div class="float-right text-white m-2">
|
||||
<a class="font-weight-bold text-white" href="{{ links.Base }}"><i
|
||||
class="bi bi-grid-3x2-gap-fill text-white mr-1 align-middle" style="font-size: 1.4rem;"></i></a>
|
||||
{{ tr "inc_header.logged-in-as" }}
|
||||
<a data-test-id="link-redirect-to-profile" class="font-weight-bold text-white"
|
||||
href="{{ links.Profile }}">{{ coalesce .user.Name .user.Handle .user.Email }}</a>
|
||||
|
|
||||
<a data-test-id="link-logout" class="font-weight-bold text-white"
|
||||
href="{{ links.Logout }}">{{ tr "inc_header.logout" }}</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
</header>
|
||||
|
||||
<main class="auth mt-sm-5">
|
||||
<div class="card">
|
||||
{{ template "inc_nav.html.tpl" . }}
|
||||
@ -143,6 +143,7 @@ func New(ctx context.Context, log *zap.Logger, oa2m oauth2def.Manager, s store.S
|
||||
// temp, will be replaced
|
||||
"language": func() string { return language.Tag{}.String() },
|
||||
"tr": func(key string, pp ...string) string { return key },
|
||||
"safeCSS": func(styles string) template.CSS { return template.CSS(styles) },
|
||||
})
|
||||
|
||||
useEmbedded = len(opt.AssetsPath) == 0
|
||||
|
||||
@ -3,6 +3,7 @@ package handlers
|
||||
import (
|
||||
"context"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"net/http"
|
||||
@ -305,7 +306,7 @@ func (h *AuthHandlers) handle(fn handlerFn) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// Add alerts, settings, providers, csrf token
|
||||
// Add alerts, settings, providers, csrf token, Bg
|
||||
func (h *AuthHandlers) enrichTmplData(req *request.AuthReq) interface{} {
|
||||
d := req.Data
|
||||
if req.AuthUser != nil {
|
||||
@ -362,9 +363,21 @@ func (h *AuthHandlers) enrichTmplData(req *request.AuthReq) interface{} {
|
||||
dSettings.Providers = nil
|
||||
d["settings"] = dSettings
|
||||
|
||||
d["authBg"] = h.bgStylesData()
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
func (h *AuthHandlers) bgStylesData() string {
|
||||
if h.Settings.BackgroundUI.BackgroundImageSrcUrl == "" {
|
||||
return fmt.Sprintf("background: url(%s/release-background.png) no-repeat top; %s",
|
||||
GetLinks().Assets, h.Settings.BackgroundUI.Styles)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("background: url('%s') no-repeat top; %s",
|
||||
h.Settings.BackgroundUI.BackgroundImageSrcUrl, h.Settings.BackgroundUI.Styles)
|
||||
}
|
||||
|
||||
// Handle successful auth (on any factor)
|
||||
func handleSuccessfulAuth(req *request.AuthReq) {
|
||||
switch {
|
||||
|
||||
@ -12,6 +12,7 @@ type (
|
||||
Providers []Provider
|
||||
Saml SAML
|
||||
MultiFactor MultiFactor
|
||||
BackgroundUI BackgroundUI
|
||||
}
|
||||
|
||||
SAML struct {
|
||||
@ -79,4 +80,9 @@ type (
|
||||
Secret string
|
||||
Scope string
|
||||
}
|
||||
|
||||
BackgroundUI struct {
|
||||
BackgroundImageSrcUrl string
|
||||
Styles string
|
||||
}
|
||||
)
|
||||
|
||||
@ -148,6 +148,12 @@ type (
|
||||
FromAddress string `kv:"from-address"`
|
||||
FromName string `kv:"from-name"`
|
||||
} `json:"-"`
|
||||
|
||||
//Auth Background Image settings
|
||||
UI struct {
|
||||
BackgroundImageSrc string `kv:"background-image-src" json:"backgroundImageSrc"`
|
||||
Styles string `kv:"styles" json:"styles"`
|
||||
} `kv:"ui" json:"ui"`
|
||||
} `json:"auth"`
|
||||
|
||||
Compose struct {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user