From 349f4e5730d9b97b642fb17f27917ac6120482a4 Mon Sep 17 00:00:00 2001 From: NGPixel Date: Thu, 3 Aug 2023 09:32:56 +0000 Subject: [PATCH] feat: admin auth UI + refs --- server/db/migrations/3.0.0.mjs | 23 + server/graph/resolvers/authentication.mjs | 11 +- server/graph/schemas/authentication.graphql | 1 + server/helpers/common.mjs | 3 +- server/locales/en.json | 14 +- .../authentication/auth0/definition.yml | 7 + .../authentication/azure/definition.yml | 2 +- .../authentication/discord/definition.yml | 7 + .../authentication/dropbox/definition.yml | 7 + .../authentication/facebook/definition.yml | 7 + .../authentication/github/definition.yml | 11 + .../authentication/gitlab/definition.yml | 7 + .../authentication/google/definition.yml | 7 + .../authentication/keycloak/definition.yml | 8 +- .../authentication/ldap/definition.yml | 1 + .../authentication/microsoft/definition.yml | 1 + .../authentication/oauth2/definition.yml | 7 + .../authentication/oidc/definition.yml | 7 + .../authentication/okta/definition.yml | 7 + .../authentication/rocketchat/definition.yml | 7 + .../authentication/slack/definition.yml | 7 + ux/public/_assets/icons/ultraviolet-back.svg | 1 + .../_assets/icons/ultraviolet-private.svg | 1 + .../_assets/icons/ultraviolet-register.svg | 1 + .../_assets/icons/ultraviolet-shutdown.svg | 1 + ux/src/pages/AdminAuth.vue | 474 +++++++++--------- 26 files changed, 386 insertions(+), 244 deletions(-) create mode 100644 ux/public/_assets/icons/ultraviolet-back.svg create mode 100644 ux/public/_assets/icons/ultraviolet-private.svg create mode 100644 ux/public/_assets/icons/ultraviolet-register.svg create mode 100644 ux/public/_assets/icons/ultraviolet-shutdown.svg diff --git a/server/db/migrations/3.0.0.mjs b/server/db/migrations/3.0.0.mjs index 96afaec1..bed044f3 100644 --- a/server/db/migrations/3.0.0.mjs +++ b/server/db/migrations/3.0.0.mjs @@ -414,6 +414,7 @@ export async function up (knex) { // -> GENERATE IDS const groupAdminId = uuid() + const groupUserId = uuid() const groupGuestId = '10000000-0000-4000-8000-000000000001' const siteId = uuid() const authModuleId = uuid() @@ -658,6 +659,24 @@ export async function up (knex) { rules: JSON.stringify([]), isSystem: true }, + { + id: groupUserId, + name: 'Users', + permissions: JSON.stringify(['read:pages', 'read:assets', 'read:comments']), + rules: JSON.stringify([ + { + id: uuid(), + name: 'Default Rule', + roles: ['read:pages', 'read:assets', 'read:comments'], + match: 'START', + mode: 'ALLOW', + path: '', + locales: [], + sites: [] + } + ]), + isSystem: true + }, { id: groupGuestId, name: 'Guests', @@ -744,6 +763,10 @@ export async function up (knex) { userId: userAdminId, groupId: groupAdminId }, + { + userId: userAdminId, + groupId: groupUserId + }, { userId: userGuestId, groupId: groupGuestId diff --git a/server/graph/resolvers/authentication.mjs b/server/graph/resolvers/authentication.mjs index b5e70a7c..3a5fcfca 100644 --- a/server/graph/resolvers/authentication.mjs +++ b/server/graph/resolvers/authentication.mjs @@ -40,7 +40,16 @@ export default { * Fetch active authentication strategies */ async authActiveStrategies (obj, args, context) { - return WIKI.db.authentication.getStrategies({ enabledOnly: args.enabledOnly }) + const strategies = await WIKI.db.authentication.getStrategies({ enabledOnly: args.enabledOnly }) + return strategies.map(a => { + const str = _.find(WIKI.data.authentication, ['key', a.module]) || {} + return { + ...a, + config: _.transform(str.props, (r, v, k) => { + r[k] = v.sensitive ? a.config[k] : '********' + }, {}) + } + }) }, /** * Fetch site authentication strategies diff --git a/server/graph/schemas/authentication.graphql b/server/graph/schemas/authentication.graphql index 5da7dbd7..54cc5dbc 100644 --- a/server/graph/schemas/authentication.graphql +++ b/server/graph/schemas/authentication.graphql @@ -86,6 +86,7 @@ extend type Mutation { type AuthenticationStrategy { key: String props: JSON + refs: JSON title: String description: String isAvailable: Boolean diff --git a/server/helpers/common.mjs b/server/helpers/common.mjs index 51f5996a..29e64441 100644 --- a/server/helpers/common.mjs +++ b/server/helpers/common.mjs @@ -115,7 +115,8 @@ export function parseModuleProps (props) { multiline: value.multiline || false, sensitive: value.sensitive || false, icon: value.icon || 'rename', - order: value.order || 100 + order: value.order || 100, + if: value.if ?? [] }) return result }, {}) diff --git a/server/locales/en.json b/server/locales/en.json index f9847297..9569eee7 100644 --- a/server/locales/en.json +++ b/server/locales/en.json @@ -67,28 +67,32 @@ "admin.auth.activeStrategies": "Active Strategies", "admin.auth.addStrategy": "Add Strategy", "admin.auth.allowedWebOrigins": "Allowed Web Origins", - "admin.auth.autoEnrollGroups": "Assign to group", - "admin.auth.autoEnrollGroupsHint": "Automatically assign new users to these groups.", + "admin.auth.autoEnrollGroups": "Assign to group(s)", + "admin.auth.autoEnrollGroupsHint": "Automatically assign new users to these groups. New users are always added to the Users group regardless of this setting.", "admin.auth.callbackUrl": "Callback URL / Redirect URI", "admin.auth.configReference": "Configuration Reference", "admin.auth.configReferenceSubtitle": "Some strategies may require some configuration values to be set on your provider. These are provided for reference only and may not be needed by the current strategy.", "admin.auth.displayName": "Display Name", "admin.auth.displayNameHint": "The title shown to the end user for this authentication strategy.", - "admin.auth.domainsWhitelist": "Limit to specific email domains", - "admin.auth.domainsWhitelistHint": "A list of domains authorized to register. The user email address domain must match one of these to gain access.", + "admin.auth.domainsWhitelist": "Email Address Allowlist", + "admin.auth.domainsWhitelistHint": "Only allow users to register with an email address that matches the regex expression.", "admin.auth.enabled": "Enabled", "admin.auth.enabledForced": "This strategy cannot be disabled.", "admin.auth.enabledHint": "Should this strategy be available to sites for login.", "admin.auth.force2fa": "Force all users to use Two-Factor Authentication (2FA)", "admin.auth.force2faHint": "Users will be required to setup 2FA the first time they login and cannot be disabled by the user.", "admin.auth.globalAdvSettings": "Global Advanced Settings", + "admin.auth.info": "Info", + "admin.auth.infoName": "Name", + "admin.auth.infoNameHint": "Display name for this strategy.", "admin.auth.loginUrl": "Login URL", "admin.auth.logoutUrl": "Logout URL", + "admin.auth.noConfigOption": "This strategy has no configuration options you can modify.", "admin.auth.refreshSuccess": "List of strategies has been refreshed.", "admin.auth.registration": "Registration", "admin.auth.saveSuccess": "Authentication configuration saved successfully.", "admin.auth.security": "Security", - "admin.auth.selfRegistration": "Allow self-registration", + "admin.auth.selfRegistration": "Allow Self-Registration", "admin.auth.selfRegistrationHint": "Allow any user successfully authorized by the strategy to access the wiki.", "admin.auth.siteUrlNotSetup": "You must set a valid {siteUrl} first! Click on {general} in the left sidebar.", "admin.auth.status": "Status", diff --git a/server/modules/authentication/auth0/definition.yml b/server/modules/authentication/auth0/definition.yml index 54a701cb..d228dd44 100644 --- a/server/modules/authentication/auth0/definition.yml +++ b/server/modules/authentication/auth0/definition.yml @@ -27,4 +27,11 @@ props: type: String title: Client Secret hint: Application Client Secret + sensitive: true order: 3 +refs: + callbackUrl: + title: Authorization Callback URL + hint: The callback endpoint to input on Auth0. + icon: back + value: '{host}/login/{id}/callback' diff --git a/server/modules/authentication/azure/definition.yml b/server/modules/authentication/azure/definition.yml index f213b3c7..e1db7692 100644 --- a/server/modules/authentication/azure/definition.yml +++ b/server/modules/authentication/azure/definition.yml @@ -26,5 +26,5 @@ props: cookieEncryptionKeyString: type: String title: Cookie Encryption Key String - hint: Random string with 44-character length. Setting this enables workaround for Chrome's SameSite cookies. + hint: Random string with 44-character length. Setting this enables workaround for Chrome's SameSite cookies. order: 3 diff --git a/server/modules/authentication/discord/definition.yml b/server/modules/authentication/discord/definition.yml index 1d0e517d..7c186dfd 100644 --- a/server/modules/authentication/discord/definition.yml +++ b/server/modules/authentication/discord/definition.yml @@ -18,9 +18,16 @@ props: type: String title: Client Secret hint: Application Client Secret + sensitive: true order: 2 guildId: type: String title: Server ID hint: Optional - Your unique server identifier, such that only members are authorized order: 3 +refs: + callbackUrl: + title: Authorization Callback URL + hint: The callback endpoint to input on Discord. + icon: back + value: '{host}/login/{id}/callback' diff --git a/server/modules/authentication/dropbox/definition.yml b/server/modules/authentication/dropbox/definition.yml index 11ea43ce..bdb14a96 100644 --- a/server/modules/authentication/dropbox/definition.yml +++ b/server/modules/authentication/dropbox/definition.yml @@ -18,4 +18,11 @@ props: type: String title: App Secret hint: Application Client Secret + sensitive: true order: 2 +refs: + callbackUrl: + title: Authorization Callback URL + hint: The callback endpoint to input on Dropbox. + icon: back + value: '{host}/login/{id}/callback' diff --git a/server/modules/authentication/facebook/definition.yml b/server/modules/authentication/facebook/definition.yml index fc0e668c..493d53db 100644 --- a/server/modules/authentication/facebook/definition.yml +++ b/server/modules/authentication/facebook/definition.yml @@ -20,4 +20,11 @@ props: type: String title: App Secret hint: Application Secret + sensitive: true order: 2 +refs: + callbackUrl: + title: Authorization Callback URL + hint: The callback endpoint to input on Facebook. + icon: back + value: '{host}/login/{id}/callback' diff --git a/server/modules/authentication/github/definition.yml b/server/modules/authentication/github/definition.yml index 93022b5d..417bea9d 100644 --- a/server/modules/authentication/github/definition.yml +++ b/server/modules/authentication/github/definition.yml @@ -18,6 +18,7 @@ props: type: String title: Client Secret hint: Application Client Secret + sensitive: true order: 2 useEnterprise: type: Boolean @@ -31,10 +32,20 @@ props: hint: GitHub Enterprise Only - Domain of your installation (e.g. github.company.com). Leave blank otherwise. default: '' order: 4 + if: + - { key: 'useEnterprise', eq: true } enterpriseUserEndpoint: type: String title: GitHub Enterprise User Endpoint hint: GitHub Enterprise Only - Endpoint to fetch user details (e.g. https://api.github.com/user). Leave blank otherwise. default: 'https://api.github.com/user' order: 5 + if: + - { key: 'useEnterprise', eq: true } +refs: + callbackUrl: + title: Authorization Callback URL + hint: The callback endpoint to input on GitHub. + icon: back + value: '{host}/login/{id}/callback' diff --git a/server/modules/authentication/gitlab/definition.yml b/server/modules/authentication/gitlab/definition.yml index ce3407c2..c5de2dea 100644 --- a/server/modules/authentication/gitlab/definition.yml +++ b/server/modules/authentication/gitlab/definition.yml @@ -18,6 +18,7 @@ props: type: String title: Client Secret hint: Application Client Secret + sensitive: true order: 2 baseUrl: type: String @@ -25,3 +26,9 @@ props: hint: For self-managed GitLab instances, define the base URL (e.g. https://gitlab.example.com). Leave default for GitLab.com SaaS (https://gitlab.com). default: https://gitlab.com order: 3 +refs: + callbackUrl: + title: Authorization Callback URL + hint: The callback endpoint to input on GitLab. + icon: back + value: '{host}/login/{id}/callback' diff --git a/server/modules/authentication/google/definition.yml b/server/modules/authentication/google/definition.yml index 435c6828..488d42d8 100644 --- a/server/modules/authentication/google/definition.yml +++ b/server/modules/authentication/google/definition.yml @@ -22,9 +22,16 @@ props: type: String title: Client Secret hint: Application Client Secret + sensitive: true order: 2 hostedDomain: type: String title: Hosted Domain hint: (optional) Only for G Suite hosted domain. Leave empty otherwise. order: 3 +refs: + callbackUrl: + title: Authorization Callback URL + hint: The callback endpoint to input on Google. + icon: back + value: '{host}/login/{id}/callback' diff --git a/server/modules/authentication/keycloak/definition.yml b/server/modules/authentication/keycloak/definition.yml index 59aaf38b..1caa249e 100644 --- a/server/modules/authentication/keycloak/definition.yml +++ b/server/modules/authentication/keycloak/definition.yml @@ -28,6 +28,7 @@ props: type: String title: Client Secret hint: Application Client Secret + sensitive: true order: 4 authorizationURL: type: String @@ -54,4 +55,9 @@ props: title: Logout Endpoint URL hint: e.g. https://KEYCLOAK-HOST/auth/realms/YOUR-REALM/protocol/openid-connect/logout order: 9 - +refs: + callbackUrl: + title: Authorization Callback URL + hint: The callback endpoint to input on Keycloak. + icon: back + value: '{host}/login/{id}/callback' diff --git a/server/modules/authentication/ldap/definition.yml b/server/modules/authentication/ldap/definition.yml index af982089..51572fb7 100644 --- a/server/modules/authentication/ldap/definition.yml +++ b/server/modules/authentication/ldap/definition.yml @@ -29,6 +29,7 @@ props: type: String hint: The password of the account used above for binding. maxWidth: 600 + sensitive: true order: 3 searchBase: title: Search Base diff --git a/server/modules/authentication/microsoft/definition.yml b/server/modules/authentication/microsoft/definition.yml index bf6f6b06..0b18c2e7 100644 --- a/server/modules/authentication/microsoft/definition.yml +++ b/server/modules/authentication/microsoft/definition.yml @@ -22,4 +22,5 @@ props: type: String title: Client Secret hint: Application Client Secret + sensitive: true order: 2 diff --git a/server/modules/authentication/oauth2/definition.yml b/server/modules/authentication/oauth2/definition.yml index 203b1a7c..f606475a 100644 --- a/server/modules/authentication/oauth2/definition.yml +++ b/server/modules/authentication/oauth2/definition.yml @@ -18,6 +18,7 @@ props: type: String title: Client Secret hint: Application Client Secret + sensitive: true order: 2 authorizationURL: type: String @@ -71,3 +72,9 @@ props: title: Pass access token via GET query string to User Info Endpoint hint: (optional) Pass the access token in an `access_token` parameter attached to the GET query string of the User Info Endpoint URL. Otherwise the access token will be passed in the Authorization header. order: 11 +refs: + callbackUrl: + title: Authorization Callback URL + hint: The callback endpoint to input on the oauth2 server. + icon: back + value: '{host}/login/{id}/callback' diff --git a/server/modules/authentication/oidc/definition.yml b/server/modules/authentication/oidc/definition.yml index 68a6ffc1..afe8dae9 100644 --- a/server/modules/authentication/oidc/definition.yml +++ b/server/modules/authentication/oidc/definition.yml @@ -22,6 +22,7 @@ props: type: String title: Client Secret hint: Application Client Secret + sensitive: true order: 2 authorizationURL: type: String @@ -55,3 +56,9 @@ props: title: Logout URL hint: (optional) Logout URL on the OAuth2 provider where the user will be redirected to complete the logout process. order: 8 +refs: + callbackUrl: + title: Authorization Callback URL + hint: The callback endpoint to input on OIDC server. + icon: back + value: '{host}/login/{id}/callback' diff --git a/server/modules/authentication/okta/definition.yml b/server/modules/authentication/okta/definition.yml index 74a16823..1d8b0476 100644 --- a/server/modules/authentication/okta/definition.yml +++ b/server/modules/authentication/okta/definition.yml @@ -29,6 +29,7 @@ props: type: String hint: 40 chars alphanumeric string with a hyphen(s) maxWidth: 600 + sensitive: true order: 3 idp: title: Identity Provider ID (idp) @@ -36,3 +37,9 @@ props: hint: (Optional) - 20 chars alphanumeric string maxWidth: 400 order: 4 +refs: + callbackUrl: + title: Authorization Callback URL + hint: The callback endpoint to input on Okta. + icon: back + value: '{host}/login/{id}/callback' diff --git a/server/modules/authentication/rocketchat/definition.yml b/server/modules/authentication/rocketchat/definition.yml index e04a1724..f0155af8 100644 --- a/server/modules/authentication/rocketchat/definition.yml +++ b/server/modules/authentication/rocketchat/definition.yml @@ -22,9 +22,16 @@ props: type: String title: Client Secret hint: Application Client Secret + sensitive: true order: 2 siteURL: type: String title: Rocket.chat Site URL hint: The base URL of your Rocket.chat site (e.g. https://example.rocket.chat) order: 3 +refs: + callbackUrl: + title: Authorization Callback URL + hint: The callback endpoint to input on Rocket.chat. + icon: back + value: '{host}/login/{id}/callback' diff --git a/server/modules/authentication/slack/definition.yml b/server/modules/authentication/slack/definition.yml index 6c57d951..3b0f51ef 100644 --- a/server/modules/authentication/slack/definition.yml +++ b/server/modules/authentication/slack/definition.yml @@ -22,9 +22,16 @@ props: type: String title: Client Secret hint: Application Client Secret + sensitive: true order: 2 team: type: String title: Team / Workspace ID hint: Optional - Your unique team (workspace) identifier order: 3 +refs: + callbackUrl: + title: Authorization Callback URL + hint: The callback endpoint to input on Slack. + icon: back + value: '{host}/login/{id}/callback' diff --git a/ux/public/_assets/icons/ultraviolet-back.svg b/ux/public/_assets/icons/ultraviolet-back.svg new file mode 100644 index 00000000..1a6c816b --- /dev/null +++ b/ux/public/_assets/icons/ultraviolet-back.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ux/public/_assets/icons/ultraviolet-private.svg b/ux/public/_assets/icons/ultraviolet-private.svg new file mode 100644 index 00000000..87849d72 --- /dev/null +++ b/ux/public/_assets/icons/ultraviolet-private.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ux/public/_assets/icons/ultraviolet-register.svg b/ux/public/_assets/icons/ultraviolet-register.svg new file mode 100644 index 00000000..c8cdf3f9 --- /dev/null +++ b/ux/public/_assets/icons/ultraviolet-register.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ux/public/_assets/icons/ultraviolet-shutdown.svg b/ux/public/_assets/icons/ultraviolet-shutdown.svg new file mode 100644 index 00000000..2c6cbdaa --- /dev/null +++ b/ux/public/_assets/icons/ultraviolet-shutdown.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ux/src/pages/AdminAuth.vue b/ux/src/pages/AdminAuth.vue index be9e4b75..23ec0ea1 100644 --- a/ux/src/pages/AdminAuth.vue +++ b/ux/src/pages/AdminAuth.vue @@ -27,7 +27,7 @@ q-page.admin-mail ) q-separator(inset) .row.q-pa-md.q-col-gutter-md - .col-auto + .col-12.col-lg-auto q-card.rounded-borders.bg-dark q-list( style='min-width: 350px;' @@ -35,8 +35,8 @@ q-page.admin-mail dark ) q-item( - v-for='str of combinedActiveStrategies' - :key='str.key' + v-for='str of state.activeStrategies' + :key='str.id' active-class='bg-primary text-white' :active='state.selectedStrategy === str.id' @click='state.selectedStrategy = str.id' @@ -77,20 +77,124 @@ q-page.admin-mail .col q-card.q-pb-sm q-card-section - .text-subtitle1 {{t('admin.storage.contentTypes')}} - .text-body2.text-grey {{ t('admin.storage.contentTypesHint') }} + .text-subtitle1 {{t('admin.auth.info')}} + q-item + blueprint-icon(icon='information') + q-item-section + q-item-label {{t(`admin.auth.infoName`)}} + q-item-label(caption) {{t(`admin.auth.infoNameHint`)}} + q-item-section + q-input( + outlined + v-model='state.strategy.displayName' + dense + hide-bottom-space + :aria-label='t(`admin.auth.infoName`)' + ) + q-separator.q-my-sm(inset) + q-item(tag='label') + blueprint-icon(icon='shutdown') + q-item-section + q-item-label {{t(`admin.auth.enabled`)}} + q-item-label(caption) {{t(`admin.auth.enabledHint`)}} + q-item-label.text-deep-orange(v-if='state.strategy.strategy.key === `local`', caption) {{t(`admin.auth.enabledForced`)}} + q-item-section(avatar) + q-toggle( + v-model='state.strategy.isEnabled' + :disable='state.strategy.strategy.key === `local`' + color='primary' + checked-icon='las la-check' + unchecked-icon='las la-times' + :aria-label='t(`admin.auth.enabled`)' + ) + q-separator.q-my-sm(inset) + q-item(tag='label') + blueprint-icon(icon='register') + q-item-section + q-item-label {{t(`admin.auth.selfRegistration`)}} + q-item-label(caption) {{t(`admin.auth.selfRegistrationHint`)}} + q-item-section(avatar) + q-toggle( + v-model='state.strategy.selfRegistration' + color='primary' + checked-icon='las la-check' + unchecked-icon='las la-times' + :aria-label='t(`admin.auth.selfRegistration`)' + ) + template(v-if='state.strategy.selfRegistration') + q-separator.q-my-sm(inset) + q-item + blueprint-icon(icon='team') + q-item-section + q-item-label {{t(`admin.auth.autoEnrollGroups`)}} + q-item-label(caption) {{t(`admin.auth.autoEnrollGroupsHint`)}} + q-item-section + q-select( + outlined + :options='state.groups' + v-model='state.strategy.autoEnrollGroups' + multiple + map-options + emit-value + option-value='id' + option-label='name' + options-dense + dense + hide-bottom-space + :aria-label='t(`admin.users.groups`)' + :loading='state.loadingGroups' + ) + template(v-slot:selected) + .text-caption(v-if='state.strategy.autoEnrollGroups?.length > 1') + i18n-t(keypath='admin.users.groupsSelected') + template(#count) + strong {{ state.strategy.autoEnrollGroups?.length }} + .text-caption(v-else-if='state.strategy.autoEnrollGroups?.length === 1') + i18n-t(keypath='admin.users.groupSelected') + template(#group) + strong {{ selectedGroupName }} + span(v-else) + template(v-slot:option='{ itemProps, opt, selected, toggleOption }') + q-item( + v-bind='itemProps' + ) + q-item-section(side) + q-checkbox( + size='sm' + :model-value='selected' + @update:model-value='toggleOption(opt)' + ) + q-item-section + q-item-label {{opt.name}} + + q-separator.q-my-sm(inset) + q-item + blueprint-icon(icon='private') + q-item-section + q-item-label {{t(`admin.auth.domainsWhitelist`)}} + q-item-label(caption) {{t(`admin.auth.domainsWhitelistHint`)}} + q-item-section + q-input( + outlined + v-model='state.strategy.domainWhitelist' + dense + hide-bottom-space + :aria-label='t(`admin.auth.domainsWhitelist`)' + prefix='/' + suffix='/' + ) //- ----------------------- //- Configuration //- ----------------------- q-card.q-pb-sm.q-mt-md q-card-section - .text-subtitle1 {{t('admin.storage.config')}} + .text-subtitle1 {{t('admin.auth.strategyConfiguration')}} q-banner.q-mt-md( v-if='!state.strategy.config || Object.keys(state.strategy.config).length < 1' rounded :class='$q.dark.isActive ? `bg-negative text-white` : `bg-grey-2 text-grey-7`' - ) {{t('admin.storage.noConfigOption')}} + ) {{t('admin.auth.noConfigOption')}} template( v-for='(cfg, cfgKey, idx) in state.strategy.config' ) @@ -148,228 +252,62 @@ q-page.admin-mail outlined v-model='cfg.value' dense - :type='cfg.multiline ? `textarea` : `input`' + :type='cfg.multiline ? `textarea` : (cfg.sensitive ? `password` : `input`)' :aria-label='cfg.title' :disable='cfg.readOnly' ) - .col-auto(v-if='state.selectedStrategy && state.strategy') //- ----------------------- - //- Infobox + //- References //- ----------------------- - q-card.rounded-borders.q-pb-md(style='width: 350px;') + q-card.q-pb-sm.q-mt-md(v-if='strategyRefs.length > 0') q-card-section - .text-subtitle1 {{state.strategy.strategy.title}} - q-img.q-mt-sm.rounded-borders( - :src='state.strategy.strategy.logo' - fit='contain' - no-spinner - style='height: 100px;' - ) - .text-body2.q-mt-md {{state.strategy.strategy.description}} - q-separator.q-mb-sm(inset) - q-item + .text-subtitle1 {{t('admin.auth.configReference')}} + q-item(v-for='strRef of strategyRefs', :key='strRef.key') + blueprint-icon(:icon='strRef.icon', :hue-rotate='-45') q-item-section - q-item-label.text-grey {{t(`admin.auth.vendor`)}} - q-item-label {{state.strategy.strategy.vendor}} - q-separator.q-my-sm(inset) - q-item + q-item-label {{strRef.title}} + q-item-label(caption) {{strRef.hint}} q-item-section - q-item-label.text-grey {{t(`admin.auth.vendorWebsite`)}} - q-item-label: a(:href='state.strategy.strategy.website', target='_blank', rel='noreferrer') {{state.strategy.strategy.website}} - + q-input( + outlined + v-model='strRef.value' + dense + :aria-label='strRef.title' + readonly + ) //- ----------------------- - //- Status + //- Infobox //- ----------------------- - q-card.rounded-borders.q-pb-md.q-mt-md(style='width: 350px;') - q-card-section - .text-subtitle1 {{t(`admin.auth.status`)}} - q-item(tag='label') - q-item-section - q-item-label {{t(`admin.auth.enabled`)}} - q-item-label(caption) {{t(`admin.auth.enabledHint`)}} - q-item-label.text-deep-orange(v-if='state.strategy.strategy.key === `local`', caption) {{t(`admin.auth.enabledForced`)}} - q-item-section(avatar) - q-toggle( - v-model='state.strategy.isEnabled' - :disable='state.strategy.strategy.key === `local`' - color='primary' - checked-icon='las la-check' - unchecked-icon='las la-times' - :aria-label='t(`admin.auth.enabled`)' - ) - q-separator.q-my-sm(inset) - q-item - q-item-section - q-btn.acrylic-btn( - icon='las la-trash-alt' - flat - color='negative' - :disable='state.strategy.strategy.key === `local`' - label='Delete Strategy' - ) - - //- v-flex(xs12, lg9) - //- v-card.animated.fadeInUp.wait-p2s - //- v-toolbar(color='primary', dense, flat, dark) - //- .subtitle-1 {{strategy.displayName}} #[em ({{strategy.strategy.title}})] - //- v-spacer - //- v-btn(small, outlined, dark, color='white', :disabled='strategy.key === `local`', @click='deleteStrategy()') - //- v-icon(left) mdi-close - //- span {{$t('common.actions.delete')}} - //- v-card-info(color='blue') - //- div - //- span {{strategy.strategy.description}} - //- .caption: a(:href='strategy.strategy.website') {{strategy.strategy.website}} - //- v-spacer - //- .admin-providerlogo - //- img(:src='strategy.strategy.logo', :alt='strategy.strategy.title') - //- v-card-text - //- .row - //- .col-8 - //- v-text-field( - //- outlined - //- :label='$t(`admin.auth.displayName`)' - //- v-model='strategy.displayName' - //- prepend-icon='mdi-format-title' - //- :hint='$t(`admin.auth.displayNameHint`)' - //- persistent-hint - //- ) - //- .col-4 - //- v-switch.mt-1( - //- :label='$t(`admin.auth.strategyIsEnabled`)' - //- v-model='strategy.isEnabled' - //- color='primary' - //- prepend-icon='mdi-power' - //- :hint='$t(`admin.auth.strategyIsEnabledHint`)' - //- persistent-hint - //- inset - //- :disabled='strategy.key === `local`' - //- ) - //- template(v-if='strategy.config && Object.keys(strategy.config).length > 0') - //- v-divider - //- .overline.my-5 {{$t('admin.auth.strategyConfiguration')}} - //- .pr-3 - //- template(v-for='cfg in strategy.config') - //- v-select.mb-3( - //- v-if='cfg.value.type === "string" && cfg.value.enum' - //- outlined - //- :items='cfg.value.enum' - //- :key='cfg.key' - //- :label='cfg.value.title' - //- v-model='cfg.value.value' - //- prepend-icon='mdi-cog-box' - //- :hint='cfg.value.hint ? cfg.value.hint : ""' - //- persistent-hint - //- :class='cfg.value.hint ? "mb-2" : ""' - //- :style='cfg.value.maxWidth > 0 ? `max-width:` + cfg.value.maxWidth + `px;` : ``' - //- ) - //- v-switch.mb-6( - //- v-else-if='cfg.value.type === "boolean"' - //- :key='cfg.key' - //- :label='cfg.value.title' - //- v-model='cfg.value.value' - //- color='primary' - //- prepend-icon='mdi-cog-box' - //- :hint='cfg.value.hint ? cfg.value.hint : ""' - //- persistent-hint - //- inset - //- ) - //- v-textarea.mb-3( - //- v-else-if='cfg.value.type === "string" && cfg.value.multiline' - //- outlined - //- :key='cfg.key' - //- :label='cfg.value.title' - //- v-model='cfg.value.value' - //- prepend-icon='mdi-cog-box' - //- :hint='cfg.value.hint ? cfg.value.hint : ""' - //- persistent-hint - //- :class='cfg.value.hint ? "mb-2" : ""' - //- ) - //- v-text-field.mb-3( - //- v-else - //- outlined - //- :key='cfg.key' - //- :label='cfg.value.title' - //- v-model='cfg.value.value' - //- prepend-icon='mdi-cog-box' - //- :hint='cfg.value.hint ? cfg.value.hint : ""' - //- persistent-hint - //- :class='cfg.value.hint ? "mb-2" : ""' - //- :style='cfg.value.maxWidth > 0 ? `max-width:` + cfg.value.maxWidth + `px;` : ``' - //- ) - //- v-divider - //- .overline.my-5 {{$t('admin.auth.registration')}} - //- .pr-3 - //- v-switch.ml-3( - //- v-model='strategy.selfRegistration' - //- :label='$t(`admin.auth.selfRegistration`)' - //- color='primary' - //- :hint='$t(`admin.auth.selfRegistrationHint`)' - //- persistent-hint - //- inset - //- ) - //- v-combobox.ml-3.mt-5( - //- :label='$t(`admin.auth.domainsWhitelist`)' - //- v-model='strategy.domainWhitelist' - //- prepend-icon='mdi-email-check-outline' - //- outlined - //- :disabled='!strategy.selfRegistration' - //- :hint='$t(`admin.auth.domainsWhitelistHint`)' - //- persistent-hint - //- small-chips - //- deletable-chips - //- clearable - //- multiple - //- chips - //- ) - //- v-autocomplete.mt-3.ml-3( - //- outlined - //- :disabled='!strategy.selfRegistration' - //- :items='groups' - //- item-text='name' - //- item-value='id' - //- :label='$t(`admin.auth.autoEnrollGroups`)' - //- v-model='strategy.autoEnrollGroups' - //- prepend-icon='mdi-account-group' - //- :hint='$t(`admin.auth.autoEnrollGroupsHint`)' - //- small-chips - //- persistent-hint - //- deletable-chips - //- clearable - //- multiple - //- chips - //- ) + q-card.q-mt-md + q-card-section.text-center + q-img.rounded-borders( + :src='state.strategy.strategy.logo' + fit='contain' + no-spinner + style='height: 100px; max-width: 300px;' + ) + .text-subtitle2.q-mt-sm {{state.strategy.strategy.title}} + .text-caption.q-mt-sm {{state.strategy.strategy.description}} + .text-caption.q-mt-sm: strong {{state.strategy.strategy.vendor}} + .text-caption: a(:href='state.strategy.strategy.website', target='_blank', rel='noreferrer') {{state.strategy.strategy.website}} - //- v-card.mt-4.wiki-form.animated.fadeInUp.wait-p4s(v-if='selectedStrategy !== `local`') - //- v-toolbar(color='primary', dense, flat, dark) - //- .subtitle-1 {{$t('admin.auth.configReference')}} - //- v-card-text - //- .body-2 {{$t('admin.auth.configReferenceSubtitle')}} - //- v-alert.mt-3.radius-7(v-if='host.length < 8', color='red', outlined, :value='true', icon='mdi-alert') - //- i18next(path='admin.auth.siteUrlNotSetup', tag='span') - //- strong(place='siteUrl') {{$t('admin.general.siteUrl')}} - //- strong(place='general') {{$t('admin.general.title')}} - //- .pa-3.mt-3.radius-7.grey(v-else, :class='$vuetify.theme.dark ? `darken-3-d5` : `lighten-3`') - //- .body-2: strong {{$t('admin.auth.allowedWebOrigins')}} - //- .body-2 {{host}} - //- v-divider.my-3 - //- .body-2: strong {{$t('admin.auth.callbackUrl')}} - //- .body-2 {{host}}/login/{{strategy.key}}/callback - //- v-divider.my-3 - //- .body-2: strong {{$t('admin.auth.loginUrl')}} - //- .body-2 {{host}}/login - //- v-divider.my-3 - //- .body-2: strong {{$t('admin.auth.logoutUrl')}} - //- .body-2 {{host}} - //- v-divider.my-3 - //- .body-2: strong {{$t('admin.auth.tokenEndpointAuthMethod')}} - //- .body-2 HTTP-POST + .flex.q-mt-md + .text-caption.text-grey ID: {{ state.strategy.id }} + q-space + q-btn.acrylic-btn( + icon='las la-trash-alt' + flat + color='negative' + :disable='state.strategy.strategy.key === `local`' + label='Delete Strategy' + @click='deleteStrategy(state.strategy.id)' + )