diff --git a/dbrepo-ui/bun.lockb b/dbrepo-ui/bun.lockb index 34e1f3dd06864bd64d099c2212a7b45cb5140d8f..35d4d918b6b74562ce445a20d60ebc12b72ae319 100755 Binary files a/dbrepo-ui/bun.lockb and b/dbrepo-ui/bun.lockb differ diff --git a/dbrepo-ui/components/user/UserToolbar.vue b/dbrepo-ui/components/user/UserToolbar.vue index b7452e52c77ca5b10fd4f7f8f070821551a71f60..3a16503673ac0599f827fb14b4e4c9517169c85d 100644 --- a/dbrepo-ui/components/user/UserToolbar.vue +++ b/dbrepo-ui/components/user/UserToolbar.vue @@ -11,32 +11,35 @@ <v-tab :text="$t('toolbars.user.authentication')" to="/user/authentication" /> - <v-tab - :text="$t('toolbars.user.developer')" - to="/user/developer" /> </v-tabs> </template> </v-toolbar> </div> </template> +<script setup> +definePageMeta({ + middleware: ['auth'] +}) +const { $oidc } = useNuxtApp() +</script> <script> -import { useUserStore } from '@/stores/user' - export default { data () { return { - tab: null, - userStore: useUserStore() + tab: null } }, computed: { user () { - return this.userStore.getUser + return this.$oidc.user }, roles () { - return this.userStore.getRoles - } + if (!this.user || !this.user.realm_access) { + return [] + } + return this.user.realm_access.roles + }, } } </script> diff --git a/dbrepo-ui/composables/axios-instance.ts b/dbrepo-ui/composables/axios-instance.ts index d4f274b717e938c3305bc20463c93d9e9474e079..55799aa741ca48fddca66830ee192daa1231ad36 100644 --- a/dbrepo-ui/composables/axios-instance.ts +++ b/dbrepo-ui/composables/axios-instance.ts @@ -1,11 +1,10 @@ import axios, {type AxiosInstance} from 'axios' -import {useUserStore} from '@/stores/user' let instance: AxiosInstance | null = null; export const useAxiosInstance = () => { - const config = useRuntimeConfig(); const userStore = useUserStore() + const config = useRuntimeConfig(); if (!instance) { instance = axios.create({ timeout: 90_000, @@ -19,37 +18,8 @@ export const useAxiosInstance = () => { }); instance.interceptors.request.use((config) => { const token = userStore.getToken - const refreshToken = userStore.getRefreshToken - if (!token || !refreshToken) { - return config - } - const authenticationService = useAuthenticationService() - if (authenticationService.isExpiredToken(refreshToken)) { - console.warn('Refresh token is expired: trigger logout of user') - userStore.logout() - return config - } - if (!authenticationService.isExpiredToken(token)) { - config.headers.Authorization = `Bearer ${token}` - return config - } - console.warn('Access token expired: request a new one') - const userService = useUserService() - return userService.refreshToken(refreshToken) - .then((response: KeycloakOpenIdTokenDto) => { - userStore.setToken(response.access_token) - userStore.setRefreshToken(response.refresh_token) - console.debug('new access token expires:', authenticationService.tokenToExpiryDate(response.access_token)) - config.headers.Authorization = `Bearer ${response.access_token}` - return config - }) - .catch((error: ApiErrorDto) => { - if (error.code === 'error.user.credentials') { - console.warn('User session expired.') - userStore.logout() - } - return config - }); + config.headers.Authorization = `Bearer ${token}` + return config }) } return instance; diff --git a/dbrepo-ui/layouts/default.vue b/dbrepo-ui/layouts/default.vue index fb4c40266b6dcc1f6f15a340cd400b8d4a0b3cd4..c3bd070422bb488f880d9a891acabfe22c230443 100644 --- a/dbrepo-ui/layouts/default.vue +++ b/dbrepo-ui/layouts/default.vue @@ -90,16 +90,16 @@ @click:append-inner="retrieve" /> <v-spacer /> <v-btn - v-if="!user" + v-if="!$oidc.isLoggedIn" class="mr-2" color="secondary" variant="flat" prepend-icon="mdi-login" - to="/login"> + @click="$oidc.login()"> {{ $t('navigation.login') }} </v-btn> <v-btn - v-if="!user" + v-if="!$oidc.isLoggedIn" color="primary" variant="flat" prepend-icon="mdi-account-plus" @@ -107,12 +107,12 @@ {{ $t('navigation.signup') }} </v-btn> <v-btn - v-if="user" + v-if="$oidc.user" to="/user" variant="plain" - :text="user.username" /> + :text="$oidc.user.preferred_username" /> <v-menu - v-if="user" + v-if="$oidc.isLoggedIn" location="bottom"> <template v-slot:activator="{ props }"> <v-btn @@ -121,20 +121,19 @@ </template> <v-list> <v-list-item - v-if="user" + v-if="$oidc.user" exact - :to="`/search?type=database&owner.username=${user.username}`"> + :to="`/search?type=database&owner.username=${$oidc.user.username}`"> {{ $t('navigation.databases') + ' ' + $t('navigation.mine')}} </v-list-item> <v-list-item - v-if="user" + v-if="$oidc.user" exact - :to="`/search?type=identifier&identifiers.creator.username=${user.username}`"> + :to="`/search?type=identifier&identifiers.creator.username=${$oidc.user.username}`"> {{ $t('navigation.identifiers') + ' ' + $t('navigation.mine') }} </v-list-item> <v-list-item - v-if="user" - @click="logout"> + @click="logout()"> {{ $t('navigation.logout') }} </v-list-item> </v-list> @@ -144,12 +143,17 @@ <v-main> <v-container> <slot /> + <pre>{{ accessToken }}</pre> + <pre>$oidc.isLoggedIn={{ $oidc.isLoggedIn }}</pre> + <pre v-if="$oidc.isLoggedIn" style="background:green;">$oidc.user={{$oidc.user}}</pre> </v-container> </v-main> </v-app> </template> <script setup> +const { $oidc } = useNuxtApp() +const accessToken = useCookie('oidc._access_token') const config = useRuntimeConfig() useServerHead({ title: config.public.title @@ -158,7 +162,6 @@ useServerHead({ <script> import { useUserStore } from '@/stores/user' import { useCacheStore } from '@/stores/cache' - export default { data () { return { @@ -172,31 +175,20 @@ export default { loadingSearch: false, loadingDatabases: false, search: null, - userStore: useUserStore(), - cacheStore: useCacheStore() } }, computed: { - token () { - return this.userStore.getToken - }, - user () { - return this.userStore.getUser - }, - locale () { - return this.userStore.getLocale - }, messages () { - return this.cacheStore.getMessages + const cacheStore = useCacheStore() + return cacheStore.getMessages }, table () { - return this.cacheStore.getTable + const cacheStore = useCacheStore() + return cacheStore.getTable }, database () { - return this.cacheStore.getDatabase - }, - roles () { - return this.userStore.getRoles + const cacheStore = useCacheStore() + return cacheStore.getDatabase }, version () { return this.$config.public.version @@ -231,22 +223,23 @@ export default { return } /* load database and optional access */ + const cacheStore = useCacheStore() this.cacheStore.setRouteDatabase(newObj.database_id) if (this.user) { - this.userStore.setRouteAccess(newObj.database_id) + const userStore = useUserStore() + userStore.setRouteAccess(newObj.database_id) } if (!newObj.table_id) { return } /* load table */ - this.cacheStore.setRouteTable(newObj.database_id, newObj.table_id) + cacheStore.setRouteTable(newObj.database_id, newObj.table_id) }, deep: true, immediate: true } }, mounted () { - this.initEnvironment() if (this.$route.query && this.$route.query.q) { this.search = this.$route.query.q } @@ -254,7 +247,8 @@ export default { return } this.setTheme() - this.cacheStore.reloadMessages() + const cacheStore = useCacheStore() + cacheStore.reloadMessages() }, methods: { login () { @@ -263,23 +257,12 @@ export default { }, logout () { this.$vuetify.theme.global.name = 'tuwThemeLight' - this.userStore.logout() - this.$router.push('/database') + this.$oidc.logout() }, retrieve () { console.debug('performing fuzzy search') this.$router.push({ path: '/search', query: { q: this.search } }) }, - initEnvironment () { - if (this.token && !this.user) { - console.error('Something went wrong with loading the user: reset user cache') - this.userStore.logout() - } - if (!this.locale) { - this.userStore.setLocale('en') - } - this.$i18n.locale = this.locale - }, setTheme () { switch (this.user.attributes.theme) { case 'dark': @@ -295,10 +278,6 @@ export default { this.$vuetify.theme.global.name = 'tuwThemeDarkContrast' break } - }, - setLocale (code) { - this.userStore.setLocale(code) - this.$i18n.locale = this.locale } } } diff --git a/dbrepo-ui/middleware/auth.ts b/dbrepo-ui/middleware/auth.ts new file mode 100644 index 0000000000000000000000000000000000000000..b16dfb5bbf48432aff6a191f65f962fe8d76920f --- /dev/null +++ b/dbrepo-ui/middleware/auth.ts @@ -0,0 +1,7 @@ +export default defineNuxtRouteMiddleware((to, from) => { + if (import.meta.server) { return } + const { $oidc } = useNuxtApp() + if (!$oidc.isLoggedIn) { + $oidc.login(to.fullPath) + } +}) diff --git a/dbrepo-ui/nuxt.config.ts b/dbrepo-ui/nuxt.config.ts index dc65c7ac3aca83cc1493bf69d20447273219c677..6b5d1a622b13f5b3cfd44a991a5d00be18c237d0 100644 --- a/dbrepo-ui/nuxt.config.ts +++ b/dbrepo-ui/nuxt.config.ts @@ -1,6 +1,7 @@ -import { transformAssetUrls } from 'vite-plugin-vuetify' +import {transformAssetUrls} from 'vite-plugin-vuetify' + +const proxy: any = {} -const proxy : any = {} /* proxies the backend calls, >>NOT<< the frontend calls (clicking) */ if (process.env.NODE_ENV === 'development') { @@ -19,153 +20,203 @@ if (process.env.NODE_ENV === 'development') { /** * https://nuxt.com/docs/guide/concepts/rendering#hybrid-rendering */ -const routeRules = { -} +const routeRules = {} export default defineNuxtConfig({ - app: { - head: { - charset: 'utf-8', - viewport: 'width=device-width, initial-scale=1', - meta: [ - { 'http-equiv': 'Content-Security-Policy', content: 'upgrade-insecure-requests' } - ], - htmlAttrs: { - lang: 'en-US' - } - } - }, - - build: { - transpile: ['vuetify'], - }, - - css: [ - 'vuetify/lib/styles/main.sass', - '@mdi/font/css/materialdesignicons.min.css', - '@/assets/globals.css', - '@/assets/overrides.css', - ], - - runtimeConfig: { - public: { - commit: '', - title: 'Database Repository', - logo: '/logo.svg', - icon: '/favicon.ico', - touch: '/apple-touch-icon.png', - version: 'bun-dev', - broker: { - host: 'localhost', - port: { - '5672': false - }, - extra: '' - }, - variant: { - input: { - normal: 'underlined', - contrast: 'outlined', - }, - button: { - normal: 'flat', - contrast: 'outlined', - }, - list: { - normal: '', - contrast: 'flat', - } - }, - api: { - client: 'http://localhost', - server: 'http://gateway-service', - }, - upload: { - client: 'http://localhost/api/upload/files' - }, - database: { - unsupported: '*,AVG,BIT_AND,BIT_OR,BIT_XOR,COUNT,COUNTDISTINCT,GROUP_CONCAT,JSON_ARRAYAGG,JSON_OBJECTAGG,MAX,MIN,STD,STDDEV,STDDEV_POP,STDDEV_SAMP,SUM,VARIANCE,VAR_POP,VAR_SAMP,--', - image: { - width: 200, - height: 200 - }, - extra: '' - }, - pid: { - default: { - publisher: 'Example University' - } - }, - doi: { - enabled: false, - endpoint: 'https://doi.org' - }, - links: { - rabbitmq: { - text: 'RabbitMQ Admin', - href: '/admin/broker/' - }, - keycloak: { - text: 'Keycloak Admin', - href: '/api/auth/' - } - } - } - }, - - routeRules, - - devServer: { - port: 3001 - }, - - modules: [ - '@pinia/nuxt', - '@pinia-plugin-persistedstate/nuxt', - '@nuxtjs/i18n' - ], - - pinia: { - storesDirs: ['./stores/**'], - }, - - piniaPersistedstate: { - storage: 'localStorage' - }, - - i18n: { - lazy: false, - langDir: 'locales', - strategy: 'no_prefix', - defaultLocale: 'de', - locales: [ - { - 'code': 'en', - 'file': 'en-US.json', - 'name': 'English (US)', - 'iso': 'en-US' - }, - { - 'code': 'de', - 'file': 'de-AT.json', - 'name': 'German (AT)', - 'iso': 'de-AT' - } - ] - - }, - - vite: { - server: { - proxy - }, - vue: { - template: { - transformAssetUrls, - }, - }, - }, - - devtools: { enabled: true }, - compatibilityDate: '2024-07-24' + app: { + head: { + charset: 'utf-8', + viewport: 'width=device-width, initial-scale=1', + meta: [ + {'http-equiv': 'Content-Security-Policy', content: 'upgrade-insecure-requests'} + ], + htmlAttrs: { + lang: 'en-US' + } + } + }, + + openidConnect: { + addPlugin: true, + op: { + issuer: 'http://localhost/api/auth/realms/dbrepo', + clientId: 'dbrepo-client', + clientSecret: 'MUwRc7yfXSJwX8AdRMWaQC3Nep1VjwgG', + scope: [ + 'openid', + 'roles', + 'attributes' + ] + }, + config: { + debug: false, + response_type: 'code', + secret: 'oidc._sessionid', + isCookieUserInfo: false, // whether save userinfo into cookie. + cookie: { loginName: '' }, + cookiePrefix: 'oidc._', + cookieEncrypt: true, + cookieEncryptKey: 'bfnuxt9c2470cb477d907b1e0917oidc', + cookieEncryptIV: 'ab83667c72eec9e4', + cookieEncryptALGO: 'aes-256-cbc', + cookieMaxAge: 24 * 60 * 60, // default one day + hasCookieRefreshExpireDate: false, // Set this to true if your provider has an refresh_expires_in date for the refresh token + cookieRefreshDefaultMaxAge: 24 * 60 * 60, // default one day if the hasCookieRefreshExpireDate is false + cookieFlags: { + access_token: { + httpOnly: true, + secure: false + } + } + } + }, + + build: { + transpile: ['vuetify'], + }, + + css: [ + 'vuetify/lib/styles/main.sass', + '@mdi/font/css/materialdesignicons.min.css', + '@/assets/globals.css', + '@/assets/overrides.css', + ], + + runtimeConfig: { + openidConnect: { + op: { + issuer: '', + clientId: '', + clientSecret: '', + }, + config: { + cookieFlags: { + access_token: { + httpOnly: true, + secure: false + } + } + } + }, + public: { + commit: '', + title: 'Database Repository', + logo: '/logo.svg', + icon: '/favicon.ico', + touch: '/apple-touch-icon.png', + version: 'bun-dev', + broker: { + host: 'localhost', + port: { + '5672': false + }, + extra: '' + }, + variant: { + input: { + normal: 'underlined', + contrast: 'outlined', + }, + button: { + normal: 'flat', + contrast: 'outlined', + }, + list: { + normal: '', + contrast: 'flat', + } + }, + api: { + client: 'http://localhost', + server: 'http://gateway-service', + }, + upload: { + client: 'http://localhost/api/upload/files' + }, + database: { + unsupported: '*,AVG,BIT_AND,BIT_OR,BIT_XOR,COUNT,COUNTDISTINCT,GROUP_CONCAT,JSON_ARRAYAGG,JSON_OBJECTAGG,MAX,MIN,STD,STDDEV,STDDEV_POP,STDDEV_SAMP,SUM,VARIANCE,VAR_POP,VAR_SAMP,--', + image: { + width: 200, + height: 200 + }, + extra: '' + }, + pid: { + default: { + publisher: 'Example University' + } + }, + doi: { + enabled: false, + endpoint: 'https://doi.org' + }, + links: { + rabbitmq: { + text: 'RabbitMQ Admin', + href: '/admin/broker/' + }, + keycloak: { + text: 'Keycloak Admin', + href: '/api/auth/' + } + } + } + }, + + routeRules, + + devServer: { + port: 3001 + }, + + modules: [ + '@pinia/nuxt', + '@pinia-plugin-persistedstate/nuxt', + '@nuxtjs/i18n', + 'nuxt-openid-connect' + ], + + pinia: { + storesDirs: ['./stores/**'], + }, + + piniaPersistedstate: { + storage: 'localStorage' + }, + + i18n: { + lazy: false, + langDir: 'locales', + strategy: 'no_prefix', + defaultLocale: 'de', + locales: [ + { + 'code': 'en', + 'file': 'en-US.json', + 'name': 'English (US)', + 'iso': 'en-US' + }, + { + 'code': 'de', + 'file': 'de-AT.json', + 'name': 'German (AT)', + 'iso': 'de-AT' + } + ] + + }, + + vite: { + server: { + proxy + }, + vue: { + template: { + transformAssetUrls, + }, + }, + }, + + devtools: {enabled: true}, + compatibilityDate: '2024-07-24' }) diff --git a/dbrepo-ui/package.json b/dbrepo-ui/package.json index 2bbe6696bc2e59845e4d1d30194a860923f2760d..5dfce95973ceb51b640f00439e202fec27ee3d70 100644 --- a/dbrepo-ui/package.json +++ b/dbrepo-ui/package.json @@ -25,6 +25,7 @@ "merkle-json": "^2.6.0", "moment": "^2.30.1", "nuxt": "^3.10.3", + "nuxt-openid-connect": "^0.8.1", "parse-md": "^3.0.3", "pinia": "^2.1.7", "qs": "^6.11.2", diff --git a/dbrepo-ui/pages/user/authentication.vue b/dbrepo-ui/pages/user/authentication.vue index 4cb3e11a02c34f38cf2a5a197e10c4d33762661f..3c346a8bcebad1326c856983b7e9008d7a2afc58 100644 --- a/dbrepo-ui/pages/user/authentication.vue +++ b/dbrepo-ui/pages/user/authentication.vue @@ -60,6 +60,11 @@ </div> </template> +<script setup> +definePageMeta({ + middleware: ['auth'] +}) +</script> <script> import UserToolbar from '@/components/user/UserToolbar.vue' import { useUserStore } from '@/stores/user' @@ -112,7 +117,7 @@ export default { changePassword () { this.loadingUpdate = true const userService = useUserService() - userService.updatePassword(this.user.id, {'password': this.password}) + userService.updatePassword(this.user.sub, {'password': this.password}) .then(() => { const toast = useToastInstance() toast.success(this.$t('success.user.password')) diff --git a/dbrepo-ui/pages/user/developer.vue b/dbrepo-ui/pages/user/developer.vue deleted file mode 100644 index 9b6f80a591a6a23b0059ba916c81a1bfdcc7737f..0000000000000000000000000000000000000000 --- a/dbrepo-ui/pages/user/developer.vue +++ /dev/null @@ -1,230 +0,0 @@ -<template> - <div> - <UserToolbar /> - <v-window - v-model="tab"> - <v-window-item> - <v-card - v-if="canHandleMessages" - :title="$t('pages.settings.subpages.developer.maintenance.title')" - rounded="0" - variant="flat"> - <v-data-table - :headers="headers" - :items="messages" - :loading="loadingMessages" - :items-per-page="10"> - <template v-slot:item.action="{ item }"> - <v-btn - size="x-small" - variant="flat" - :text="$t('pages.settings.subpages.developer.maintenance.modify.text')" - @click="modifyMessage(item)" /> - </template> - </v-data-table> - <v-card-text> - <v-btn - size="small" - variant="flat" - :text="$t('pages.settings.subpages.developer.maintenance.add.text')" - :disabled="!canCreateMessage" - @click="createMessage" /> - </v-card-text> - </v-card> - <v-divider - v-if="canHandleMessages" /> - <v-card - :title="$t('pages.settings.subpages.developer.token.title')" - :subtitle="$t('pages.settings.subpages.developer.token.subtitle')" - variant="flat" - rounded="0"> - <v-card-text> - <v-row dense> - <v-col xl="4"> - <v-text-field - v-model="accessTokenField" - disabled - :variant="inputVariant" - :label="$t('pages.settings.subpages.developer.token.access.label')" /> - </v-col> - <v-col xl="2"> - <v-text-field - v-model="tokenExpiry" - disabled - :variant="inputVariant" - :label="expiryLabel(token)" /> - </v-col> - </v-row> - <v-row dense> - <v-col xl="4"> - <v-text-field - v-model="refreshTokenField" - disabled - :variant="inputVariant" - :label="$t('pages.settings.subpages.developer.token.refresh.label')" /> - </v-col> - <v-col xl="2"> - <v-text-field - v-model="refreshTokenExpiry" - disabled - :variant="inputVariant" - :label="expiryLabel(refreshToken)" /> - </v-col> - </v-row> - </v-card-text> - </v-card> - </v-window-item> - </v-window> - <v-breadcrumbs :items="items" class="pa-0 mt-2" /> - <v-dialog - v-model="dialog" - persistent - max-width="640"> - <EditMaintenanceMessage - :id="messageId" - @close-dialog="closeDialog" /> - </v-dialog> - </div> -</template> - -<script> -import UserToolbar from '@/components/user/UserToolbar.vue' -import EditMaintenanceMessage from '@/components/dialogs/EditMaintenanceMessage.vue' -import { formatTimestampUTCLabel, isActiveMessage, timestampsToHumanDifference } from '@/utils' -import { useUserStore } from '@/stores/user' -import { useCacheStore } from '@/stores/cache' - -export default { - components: { - UserToolbar, - EditMaintenanceMessage - }, - data () { - return { - tab: 0, - accessTokenField: null, - refreshTokenField: null, - headers: [ - { title: this.$t('pages.settings.subpages.developer.maintenance.active'), value: 'active' }, - { title: this.$t('pages.settings.subpages.developer.maintenance.type'), value: 'type' }, - { title: this.$t('pages.settings.subpages.developer.maintenance.message'), value: 'message' }, - { title: this.$t('pages.settings.subpages.developer.maintenance.action'), value: 'action' } - ], - items: [ - { - title: this.$t('navigation.user'), - to: '/user' - }, - { - title: this.$t('toolbars.user.developer'), - to: `/user/developer`, - disabled: true - } - ], - messages: [], - loadingMessages: false, - dialog: false, - messageId: null, - userStore: useUserStore(), - cacheStore: useCacheStore() - } - }, - computed: { - token () { - return this.userStore.getToken - }, - tokenExpiry () { - if (!this.token) { - return null - } - const authenticationService = useAuthenticationService() - return formatTimestampUTCLabel(authenticationService.tokenToExpiryDate(this.token)) - }, - refreshToken () { - return this.userStore.getRefreshToken - }, - refreshTokenExpiry () { - if (!this.refreshToken) { - return null - } - const authenticationService = useAuthenticationService() - return formatTimestampUTCLabel(authenticationService.tokenToExpiryDate(this.refreshToken)) - }, - user () { - return this.userStore.getUser - }, - roles () { - return this.userStore.getRoles - }, - canCreateMessage () { - if (!this.roles) { - return false - } - return this.roles.includes('create-maintenance-message') - }, - canModifyMessage () { - if (!this.roles) { - return false - } - return this.roles.includes('modify-maintenance-message') - }, - canHandleMessages () { - return this.canCreateMessage || this.canModifyMessage - }, - inputVariant () { - const runtimeConfig = useRuntimeConfig() - return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.input.contrast : runtimeConfig.public.variant.input.normal - }, - buttonVariant () { - const runtimeConfig = useRuntimeConfig() - return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.button.contrast : runtimeConfig.public.variant.button.normal - } - }, - mounted () { - this.loadMessages() - if (!this.token || !this.refreshToken) { - return - } - this.accessTokenField = this.token - this.refreshTokenField = this.refreshToken - }, - methods: { - submit () { - }, - modifyMessage (message) { - this.messageId = message.id - this.dialog = true - }, - createMessage () { - this.messageId = null - this.dialog = true - }, - expiryLabel (token) { - const authenticationService = useAuthenticationService() - return this.$t('pages.settings.subpages.developer.token.expiry') + ' ' + timestampsToHumanDifference(Date.now(), authenticationService.tokenToExpiryDate(token)) - }, - loadMessages () { - const messageService = useMessageService() - messageService.findAll() - .then((messages) => { - this.messages = messages.map((message) => { - message.active = isActiveMessage(message) ? '● true' : 'false' - return message - }) - }) - .catch(() => { - this.loadingMessages = false - }) - .finally(() => { - this.loadingMessages = false - }) - }, - closeDialog (event) { - if (event.success) { - this.cacheStore.reloadMessages() - } - this.dialog = false - } - } -} -</script> diff --git a/dbrepo-ui/pages/user/index.vue b/dbrepo-ui/pages/user/index.vue index e729d9086f2be6604c2b9d0222c00acc79297f85..d6691d8302de92dcd23b060d79163df82b9c24e2 100644 --- a/dbrepo-ui/pages/user/index.vue +++ b/dbrepo-ui/pages/user/index.vue @@ -2,27 +2,14 @@ <div /> </template> +<script setup> +definePageMeta({ + middleware: ['auth'] +}) +</script> <script> -import { useUserStore } from '@/stores/user' - export default { - data () { - return { - userStore: useUserStore() - } - }, - computed: { - token () { - return this.userStore.getToken - }, - user () { - return this.userStore.getUser - } - }, mounted () { - if (!this.user) { - return - } this.$router.push('/user/info') } } diff --git a/dbrepo-ui/pages/user/info.vue b/dbrepo-ui/pages/user/info.vue index 3501818ca0408474e81a9038f15a84d61fb662ff..cb0e3fd9228477ade85e20f343a2a564a7c34701 100644 --- a/dbrepo-ui/pages/user/info.vue +++ b/dbrepo-ui/pages/user/info.vue @@ -13,18 +13,17 @@ <v-row dense> <v-col md="6"> <v-text-field - v-model="model.id" + v-model="user.sub" readonly + disabled :variant="inputVariant" - :label="$t('pages.user.subpages.info.id.label')" - append-inner-icon="mdi-content-copy" - @click:append-inner="copy" /> + :label="$t('pages.user.subpages.info.id.label')" /> </v-col> </v-row> <v-row dense> <v-col md="6"> <v-text-field - v-model="model.username" + v-model="user.preferred_username" disabled :variant="inputVariant" :label="$t('pages.user.subpages.info.username.label')" /> @@ -124,6 +123,12 @@ </div> </template> +<script setup> +definePageMeta({ + middleware: ['auth'] +}) +const { $oidc } = useNuxtApp() +</script> <script> import UserToolbar from '@/components/user/UserToolbar.vue' import { useUserStore } from '@/stores/user' @@ -143,7 +148,6 @@ export default { theme: null, orcidLoading: false, model: { - id: null, username: null, firstname: null, lastname: null, @@ -176,10 +180,13 @@ export default { }, computed: { user () { - return this.userStore.getUser + return this.$oidc.user }, roles () { - return this.userStore.getRoles + if (!this.user || !this.user.realm_access) { + return [] + } + return this.user.realm_access.roles }, locale () { return this.userStore.getLocale @@ -216,14 +223,12 @@ export default { language: this.model.language, } const userService = useUserService() - userService.update(this.user.id, payload) + userService.update(this.user.sub, payload) .then((user) => { console.info('Updated user information') const toast = useToastInstance() toast.success(this.$t('success.user.info')) - this.userStore.setUser(user) /* language */ - this.userStore.setLocale(this.model.language) this.$i18n.locale = this.locale /* theme */ switch (this.model.theme) { @@ -253,14 +258,12 @@ export default { return } this.model = { - id: this.user.id, - username: this.user.username, firstname: this.user.given_name, lastname: this.user.family_name, - orcid: this.user.attributes.orcid, - affiliation: this.user.attributes.affiliation, - theme: this.user.attributes.theme, - language: this.user.attributes.language + orcid: this.user.attributes?.orcid, + affiliation: this.user.attributes?.affiliation, + theme: this.user.attributes?.theme, + language: this.user.attributes?.language } }, retrieve () { @@ -284,11 +287,6 @@ export default { .finally(() => { this.orcidLoading = false }) - }, - copy () { - navigator.clipboard.writeText(this.model.id) - const toast = useToastInstance() - toast.success(this.$t('success.clipboard.user')) } } } diff --git a/dbrepo-ui/stores/user.js b/dbrepo-ui/stores/user.js index 522ce02a06ed1c695897d92a73c2682d791b499a..e857a0681ef999b2ff15bc9ef72d6b8b63091bcb 100644 --- a/dbrepo-ui/stores/user.js +++ b/dbrepo-ui/stores/user.js @@ -4,50 +4,22 @@ export const useUserStore = defineStore('user', { persist: true, state: () => { return { - /** @type String */ - token: null, - /** @type String */ - refreshToken: null, - roles: [], - user: null, access: null, - locale: null + /** @type String */ + token: null } }, getters: { getToken: (state) => state.token, - getRefreshToken: (state) => state.refreshToken, - getRoles: (state) => state.roles, - getUser: (state) => state.user, getAccess: (state) => state.access, - getLocale: (state) => state.locale }, actions: { setToken(token) { this.token = token }, - setRefreshToken(refreshToken) { - this.refreshToken = refreshToken - }, - setRoles(roles) { - this.roles = roles - }, - setUser(user) { - this.user = user - }, setAccess(access) { this.access = access }, - setLocale (locale) { - this.locale = locale - }, - logout() { - this.token = null - this.refreshToken = null - this.roles = [] - this.user = null - this.access = null - }, setRouteAccess(databaseId) { if (!databaseId || !this.user || !this.user.id) { return