diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index ff6f4e831..3d58752e7 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -441,7 +441,7 @@ jobs:
report_name: Coverage Admin Interface
type: lcov
result_path: ./coverage/lcov.info
- min_coverage: 47
+ min_coverage: 51
token: ${{ github.token }}
##############################################################################
diff --git a/admin/.env.dist b/admin/.env.dist
new file mode 100644
index 000000000..6d78e6782
--- /dev/null
+++ b/admin/.env.dist
@@ -0,0 +1,3 @@
+GRAPHQL_URI=http://localhost:4000/graphql
+WALLET_AUTH_URL=http://localhost/vue/authenticate?token=$1
+DEBUG_DISABLE_AUTH=false
\ No newline at end of file
diff --git a/admin/src/App.spec.js b/admin/src/App.spec.js
index e77bc578b..6936394f1 100644
--- a/admin/src/App.spec.js
+++ b/admin/src/App.spec.js
@@ -7,11 +7,19 @@ const stubs = {
RouterView: true,
}
+const mocks = {
+ $store: {
+ state: {
+ token: null,
+ },
+ },
+}
+
describe('App', () => {
let wrapper
const Wrapper = () => {
- return shallowMount(App, { localVue, stubs })
+ return shallowMount(App, { localVue, stubs, mocks })
}
describe('shallowMount', () => {
diff --git a/admin/src/App.vue b/admin/src/App.vue
index a76b1dcab..40460eda4 100644
--- a/admin/src/App.vue
+++ b/admin/src/App.vue
@@ -1,19 +1,15 @@
-
-
-
+
+
diff --git a/admin/src/components/NavBar.vue b/admin/src/components/NavBar.vue
index de9cfe6b2..c52743857 100644
--- a/admin/src/components/NavBar.vue
+++ b/admin/src/components/NavBar.vue
@@ -16,15 +16,43 @@
>
| {{ $store.state.openCreations }} offene Schöpfungen
+ Wallet
+ Logout
- Profilbereich
diff --git a/admin/src/config/index.js b/admin/src/config/index.js
index eab63e903..69d30a66a 100644
--- a/admin/src/config/index.js
+++ b/admin/src/config/index.js
@@ -17,8 +17,13 @@ const environment = {
PRODUCTION: process.env.NODE_ENV === 'production' || false,
}
-const server = {
+const endpoints = {
GRAPHQL_URI: process.env.GRAPHQL_URI || 'http://localhost:4000/graphql',
+ WALLET_AUTH_URL: process.env.WALLET_AUTH_URL || 'http://localhost/vue/authenticate?token=$1',
+}
+
+const debug = {
+ DEBUG_DISABLE_AUTH: process.env.DEBUG_DISABLE_AUTH === 'true' || false,
}
const options = {}
@@ -26,8 +31,9 @@ const options = {}
const CONFIG = {
...version,
...environment,
- ...server,
+ ...endpoints,
...options,
+ ...debug,
}
export default CONFIG
diff --git a/admin/src/layouts/defaultLayout.vue b/admin/src/layouts/defaultLayout.vue
new file mode 100644
index 000000000..28babdd58
--- /dev/null
+++ b/admin/src/layouts/defaultLayout.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
diff --git a/admin/src/views/Creation.spec.js b/admin/src/pages/Creation.spec.js
similarity index 100%
rename from admin/src/views/Creation.spec.js
rename to admin/src/pages/Creation.spec.js
diff --git a/admin/src/views/Creation.vue b/admin/src/pages/Creation.vue
similarity index 100%
rename from admin/src/views/Creation.vue
rename to admin/src/pages/Creation.vue
diff --git a/admin/src/views/CreationConfirm.spec.js b/admin/src/pages/CreationConfirm.spec.js
similarity index 100%
rename from admin/src/views/CreationConfirm.spec.js
rename to admin/src/pages/CreationConfirm.spec.js
diff --git a/admin/src/views/CreationConfirm.vue b/admin/src/pages/CreationConfirm.vue
similarity index 100%
rename from admin/src/views/CreationConfirm.vue
rename to admin/src/pages/CreationConfirm.vue
diff --git a/admin/src/views/Overview.vue b/admin/src/pages/Overview.vue
similarity index 100%
rename from admin/src/views/Overview.vue
rename to admin/src/pages/Overview.vue
diff --git a/admin/src/views/UserSearch.spec.js b/admin/src/pages/UserSearch.spec.js
similarity index 100%
rename from admin/src/views/UserSearch.spec.js
rename to admin/src/pages/UserSearch.spec.js
diff --git a/admin/src/views/UserSearch.vue b/admin/src/pages/UserSearch.vue
similarity index 100%
rename from admin/src/views/UserSearch.vue
rename to admin/src/pages/UserSearch.vue
diff --git a/admin/src/router/guards.js b/admin/src/router/guards.js
index c9baf61cb..d59234a25 100644
--- a/admin/src/router/guards.js
+++ b/admin/src/router/guards.js
@@ -1,7 +1,25 @@
+import CONFIG from '../config'
+
const addNavigationGuards = (router, store) => {
+ // store token on `authenticate`
router.beforeEach((to, from, next) => {
- // handle authentication
- if (to.meta.requiresAuth && !store.state.token) {
+ if (to.path === '/authenticate' && to.query && to.query.token) {
+ // TODO verify user to get user data
+ store.commit('token', to.query.token)
+ next({ path: '/' })
+ } else {
+ next()
+ }
+ })
+
+ // protect all routes but `not-found`
+ router.beforeEach((to, from, next) => {
+ if (
+ !CONFIG.DEBUG_DISABLE_AUTH && // we did not disabled the auth module for debug purposes
+ !store.state.token && // we do not have a token
+ to.path !== '/not-found' && // we are not on `not-found`
+ to.path !== '/logout' // we are not on `logout`
+ ) {
next({ path: '/not-found' })
} else {
next()
diff --git a/admin/src/router/guards.test.js b/admin/src/router/guards.test.js
new file mode 100644
index 000000000..e69846aab
--- /dev/null
+++ b/admin/src/router/guards.test.js
@@ -0,0 +1,64 @@
+import addNavigationGuards from './guards'
+import router from './router'
+
+const storeCommitMock = jest.fn()
+
+const store = {
+ commit: storeCommitMock,
+ state: {
+ token: null,
+ },
+}
+
+addNavigationGuards(router, store)
+
+describe('navigation guards', () => {
+ beforeEach(() => {
+ jest.clearAllMocks()
+ })
+
+ describe('authenticate', () => {
+ const navGuard = router.beforeHooks[0]
+ const next = jest.fn()
+
+ describe('with valid token', () => {
+ it('commits the token to the store', async () => {
+ navGuard({ path: '/authenticate', query: { token: 'valid-token' } }, {}, next)
+ expect(storeCommitMock).toBeCalledWith('token', 'valid-token')
+ })
+
+ it('redirects to /', async () => {
+ navGuard({ path: '/authenticate', query: { token: 'valid-token' } }, {}, next)
+ expect(next).toBeCalledWith({ path: '/' })
+ })
+ })
+
+ describe('without valid token', () => {
+ it('does not commit the token to the store', async () => {
+ navGuard({ path: '/authenticate' }, {}, next)
+ expect(storeCommitMock).not.toBeCalledWith()
+ })
+
+ it('calls next withou arguments', async () => {
+ navGuard({ path: '/authenticate' }, {}, next)
+ expect(next).toBeCalledWith()
+ })
+ })
+ })
+
+ describe('protect all routes', () => {
+ const navGuard = router.beforeHooks[1]
+ const next = jest.fn()
+
+ it('redirects no not found with no token in store ', () => {
+ navGuard({ path: '/' }, {}, next)
+ expect(next).toBeCalledWith({ path: '/not-found' })
+ })
+
+ it('does not redirect when token in store', () => {
+ store.state.token = 'valid token'
+ navGuard({ path: '/' }, {}, next)
+ expect(next).toBeCalledWith()
+ })
+ })
+})
diff --git a/admin/src/router/router.test.js b/admin/src/router/router.test.js
index 8e2e70d4d..eb9b646cb 100644
--- a/admin/src/router/router.test.js
+++ b/admin/src/router/router.test.js
@@ -44,19 +44,19 @@ describe('router', () => {
})
describe('routes', () => {
+ it('has seven routes defined', () => {
+ expect(routes).toHaveLength(7)
+ })
+
it('has "/overview" as default', async () => {
const component = await routes.find((r) => r.path === '/').component()
expect(component.default.name).toBe('overview')
})
- it('has fourteen routes defined', () => {
- expect(routes).toHaveLength(6)
- })
-
- describe('overview', () => {
- it('loads the "Overview" component', async () => {
- const component = await routes.find((r) => r.path === '/overview').component()
- expect(component.default.name).toBe('overview')
+ describe('logout', () => {
+ it('loads the "NotFoundPage" component', async () => {
+ const component = await routes.find((r) => r.path === '/logout').component()
+ expect(component.default.name).toBe('not-found')
})
})
diff --git a/admin/src/router/routes.js b/admin/src/router/routes.js
index a13463e08..72e7b1ac5 100644
--- a/admin/src/router/routes.js
+++ b/admin/src/router/routes.js
@@ -1,38 +1,27 @@
const routes = [
{
- path: '/',
- component: () => import('@/views/Overview.vue'),
- meta: {
- requiresAuth: true,
- },
+ path: '/authenticate',
},
{
- path: '/overview',
- component: () => import('@/views/Overview.vue'),
- meta: {
- requiresAuth: true,
- },
+ path: '/',
+ component: () => import('@/pages/Overview.vue'),
+ },
+ {
+ // TODO: Implement a "You are logged out"-Page
+ path: '/logout',
+ component: () => import('@/components/NotFoundPage.vue'),
},
{
path: '/user',
- component: () => import('@/views/UserSearch.vue'),
- meta: {
- requiresAuth: true,
- },
+ component: () => import('@/pages/UserSearch.vue'),
},
{
path: '/creation',
- component: () => import('@/views/Creation.vue'),
- meta: {
- requiresAuth: true,
- },
+ component: () => import('@/pages/Creation.vue'),
},
{
path: '/creation-confirm',
- component: () => import('@/views/CreationConfirm.vue'),
- meta: {
- requiresAuth: true,
- },
+ component: () => import('@/pages/CreationConfirm.vue'),
},
{
path: '*',
diff --git a/admin/src/store/store.js b/admin/src/store/store.js
index d199368fb..754c559c8 100644
--- a/admin/src/store/store.js
+++ b/admin/src/store/store.js
@@ -1,6 +1,7 @@
import Vuex from 'vuex'
import Vue from 'vue'
import createPersistedState from 'vuex-persistedstate'
+import CONFIG from '../config'
Vue.use(Vuex)
@@ -19,6 +20,13 @@ export const mutations = {
},
}
+export const actions = {
+ logout: ({ commit, state }) => {
+ commit('token', null)
+ window.localStorage.clear()
+ },
+}
+
const store = new Vuex.Store({
plugins: [
createPersistedState({
@@ -26,12 +34,13 @@ const store = new Vuex.Store({
}),
],
state: {
- token: 'some-valid-token',
+ token: CONFIG.DEBUG_DISABLE_AUTH ? 'validToken' : null,
moderator: 'Dertest Moderator',
openCreations: 0,
},
// Syncronous mutation of the state
mutations,
+ actions,
})
export default store
diff --git a/admin/src/store/store.test.js b/admin/src/store/store.test.js
index 81d75f05f..4482a46bf 100644
--- a/admin/src/store/store.test.js
+++ b/admin/src/store/store.test.js
@@ -1,6 +1,11 @@
-import { mutations } from './store'
+import store, { mutations, actions } from './store'
const { token, openCreationsPlus, openCreationsMinus, resetOpenCreations } = mutations
+const { logout } = actions
+
+const CONFIG = {
+ DEBUG_DISABLE_AUTH: true,
+}
describe('Vuex store', () => {
describe('mutations', () => {
@@ -36,4 +41,43 @@ describe('Vuex store', () => {
})
})
})
+
+ describe('actions', () => {
+ describe('logout', () => {
+ const windowStorageMock = jest.fn()
+ const commit = jest.fn()
+ const state = {}
+ beforeEach(() => {
+ jest.clearAllMocks()
+ window.localStorage.clear = windowStorageMock
+ })
+
+ it('deletes the token in store', () => {
+ logout({ commit, state })
+ expect(commit).toBeCalledWith('token', null)
+ })
+
+ it.skip('clears the window local storage', () => {
+ expect(windowStorageMock).toBeCalled()
+ })
+ })
+ })
+
+ describe('state', () => {
+ describe('authentication enabled', () => {
+ it('has no token', () => {
+ expect(store.state.token).toBe(null)
+ })
+ })
+
+ describe('authentication enabled', () => {
+ beforeEach(() => {
+ CONFIG.DEBUG_DISABLE_AUTH = false
+ })
+
+ it.skip('has a token', () => {
+ expect(store.state.token).toBe('validToken')
+ })
+ })
+ })
})
diff --git a/backend/src/graphql/model/User.ts b/backend/src/graphql/model/User.ts
index 5b7682e01..cdb46c954 100644
--- a/backend/src/graphql/model/User.ts
+++ b/backend/src/graphql/model/User.ts
@@ -20,6 +20,7 @@ export class User {
this.pubkey = json.public_hex
this.language = json.language
this.publisherId = json.publisher_id
+ this.isAdmin = json.isAdmin
}
}
@@ -48,7 +49,7 @@ export class User {
@Field(() => number)
created: number
- @Field(() => Boolean)
+ @Field(() =>>> Boolean)
emailChecked: boolean
@Field(() => Boolean)
@@ -71,6 +72,9 @@ export class User {
@Field(() => Int, { nullable: true })
publisherId?: number
+ @Field(() => Boolean)
+ isAdmin: boolean
+
@Field(() => Boolean)
coinanimation: boolean
diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts
index bb769f377..235b6c90f 100644
--- a/backend/src/graphql/resolver/UserResolver.ts
+++ b/backend/src/graphql/resolver/UserResolver.ts
@@ -194,6 +194,69 @@ const SecretKeyCryptographyDecrypt = (encryptedMessage: Buffer, encryptionKey: B
@Resolver()
export class UserResolver {
+ /*
+ @Authorized()
+ @Query(() => User)
+ async verifyLogin(@Ctx() context: any): Promise {
+ const loginUserRepository = getCustomRepository(LoginUserRepository)
+ loginUser = loginUserRepository.findByPubkeyHex()
+ const user = new User(result.data.user)
+
+ this.email = json.email
+ this.firstName = json.first_name
+ this.lastName = json.last_name
+ this.username = json.username
+ this.description = json.description
+ this.pubkey = json.public_hex
+ this.language = json.language
+ this.publisherId = json.publisher_id
+ this.isAdmin = json.isAdmin
+
+ const userSettingRepository = getCustomRepository(UserSettingRepository)
+ const coinanimation = await userSettingRepository
+ .readBoolean(userEntity.id, Setting.COIN_ANIMATION)
+ .catch((error) => {
+ throw new Error(error)
+ })
+ user.coinanimation = coinanimation
+ user.isAdmin = true // TODO implement
+ return user
+ }
+ */
+
+ @Authorized()
+ @Query(() => User)
+ @UseMiddleware(klicktippNewsletterStateMiddleware)
+ async verifyLogin(@Ctx() context: any): Promise {
+ // TODO refactor and do not have duplicate code with login(see below)
+ const userRepository = getCustomRepository(UserRepository)
+ const userEntity = await userRepository.findByPubkeyHex(context.pubKey)
+ const loginUserRepository = getCustomRepository(LoginUserRepository)
+ const loginUser = await loginUserRepository.findByEmail(userEntity.email)
+ const user = new User()
+ user.email = userEntity.email
+ user.firstName = userEntity.firstName
+ user.lastName = userEntity.lastName
+ user.username = userEntity.username
+ user.description = loginUser.description
+ user.pubkey = userEntity.pubkey.toString('hex')
+ user.language = loginUser.language
+
+ // Elopage Status & Stored PublisherId
+ user.hasElopage = await this.hasElopage(context)
+
+ // coinAnimation
+ const userSettingRepository = getCustomRepository(UserSettingRepository)
+ const coinanimation = await userSettingRepository
+ .readBoolean(userEntity.id, Setting.COIN_ANIMATION)
+ .catch((error) => {
+ throw new Error(error)
+ })
+ user.coinanimation = coinanimation
+ user.isAdmin = true // TODO implement
+ return user
+ }
+
@Query(() => User)
@UseMiddleware(klicktippNewsletterStateMiddleware)
async login(
@@ -266,6 +329,7 @@ export class UserResolver {
throw new Error(error)
})
user.coinanimation = coinanimation
+ user.isAdmin = true // TODO implement
context.setHeaders.push({
key: 'token',
diff --git a/frontend/.env.dist b/frontend/.env.dist
index 8d4025a5d..80dafb7f9 100644
--- a/frontend/.env.dist
+++ b/frontend/.env.dist
@@ -1,3 +1,4 @@
GRAPHQL_URI=http://localhost:4000/graphql
DEFAULT_PUBLISHER_ID=2896
-//BUILD_COMMIT=0000000
\ No newline at end of file
+#BUILD_COMMIT=0000000
+ADMIN_AUTH_URL=http://localhost/admin/authenticate?token=$1
\ No newline at end of file
diff --git a/frontend/DEV-README.md b/frontend/DEV-README.md
deleted file mode 100644
index ab2b3e225..000000000
--- a/frontend/DEV-README.md
+++ /dev/null
@@ -1,21 +0,0 @@
-DEV README von Alex
-
-default Page:
-´´´
-
- default
-
-
-
-
-´´´
-
diff --git a/frontend/ISSUE_TEMPLATE.md b/frontend/ISSUE_TEMPLATE.md
deleted file mode 100644
index 8103f52e6..000000000
--- a/frontend/ISSUE_TEMPLATE.md
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/frontend/README.md b/frontend/README.md
index e9ac0b097..f7c60552c 100755
--- a/frontend/README.md
+++ b/frontend/README.md
@@ -385,4 +385,13 @@ TODO: Update GDT-Server um paging und Zugriff auf alle Einträge zu erhalten, op
GET https://staging.gradido.net/state-balances/ajaxGdtTransactions
Liefert wenn alles in Ordnung ist:
-wenn nicht type 7 dann "amount" in euro ansonsten in GDT
\ No newline at end of file
+wenn nicht type 7 dann "amount" in euro ansonsten in GDT
+
+## Additional Software
+
+For `yarn locales` you will need `jq` to use it.
+You can install it (on arch) via
+
+```
+sudo pacman -S jq
+```
\ No newline at end of file
diff --git a/frontend/src/components/SidebarPlugin/SideBar.spec.js b/frontend/src/components/SidebarPlugin/SideBar.spec.js
index 8204eb604..7b12b6473 100644
--- a/frontend/src/components/SidebarPlugin/SideBar.spec.js
+++ b/frontend/src/components/SidebarPlugin/SideBar.spec.js
@@ -3,6 +3,8 @@ import SideBar from './SideBar'
const localVue = global.localVue
+const storeDispatchMock = jest.fn()
+
describe('SideBar', () => {
let wrapper
@@ -23,7 +25,7 @@ describe('SideBar', () => {
lastName: 'example',
hasElopage: false,
},
- commit: jest.fn(),
+ dispatch: storeDispatchMock,
},
$i18n: {
locale: 'en',
@@ -154,6 +156,42 @@ describe('SideBar', () => {
expect(wrapper.emitted('logout')).toEqual([[]])
})
})
+
+ describe('admin-area', () => {
+ it('is not visible when not an admin', () => {
+ expect(wrapper.findAll('li').at(1).text()).not.toBe('admin_area')
+ })
+
+ describe('logged in as admin', () => {
+ const assignLocationSpy = jest.fn()
+ beforeEach(async () => {
+ mocks.$store.state.isAdmin = true
+ mocks.$store.state.token = 'valid-token'
+ window.location.assign = assignLocationSpy
+ wrapper = Wrapper()
+ })
+
+ it('is visible', () => {
+ expect(wrapper.findAll('li').at(1).text()).toBe('admin_area')
+ })
+
+ describe('click on admin area', () => {
+ beforeEach(async () => {
+ await wrapper.findAll('li').at(1).find('a').trigger('click')
+ })
+
+ it('opens a new window when clicked', () => {
+ expect(assignLocationSpy).toHaveBeenCalledWith(
+ 'http://localhost/admin/authenticate?token=valid-token',
+ )
+ })
+
+ it('dispatches logout to store', () => {
+ expect(storeDispatchMock).toHaveBeenCalledWith('logout')
+ })
+ })
+ })
+ })
})
})
})
diff --git a/frontend/src/components/SidebarPlugin/SideBar.vue b/frontend/src/components/SidebarPlugin/SideBar.vue
index c33c132b0..96882e816 100755
--- a/frontend/src/components/SidebarPlugin/SideBar.vue
+++ b/frontend/src/components/SidebarPlugin/SideBar.vue
@@ -45,11 +45,20 @@
+
+
+
@@ -112,6 +121,10 @@ export default {
logout() {
this.$emit('logout')
},
+ admin() {
+ window.location.assign(CONFIG.ADMIN_AUTH_URL.replace('$1', this.$store.state.token))
+ this.$store.dispatch('logout') // logout without redirect
+ },
getElopageLink() {
const pId = this.$store.state.publisherId
? this.$store.state.publisherId
diff --git a/frontend/src/config/index.js b/frontend/src/config/index.js
index 1f1819c62..b3a9366b7 100644
--- a/frontend/src/config/index.js
+++ b/frontend/src/config/index.js
@@ -18,8 +18,9 @@ const environment = {
DEFAULT_PUBLISHER_ID: process.env.DEFAULT_PUBLISHER_ID || 2896,
}
-const server = {
+const endpoints = {
GRAPHQL_URI: process.env.GRAPHQL_URI || 'http://localhost:4000/graphql',
+ ADMIN_AUTH_URL: process.env.ADMIN_AUTH_URL || 'http://localhost/admin/authenticate?token=$1',
}
const options = {}
@@ -27,7 +28,7 @@ const options = {}
const CONFIG = {
...version,
...environment,
- ...server,
+ ...endpoints,
...options,
}
diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js
index 01021f601..8b55f4098 100644
--- a/frontend/src/graphql/queries.js
+++ b/frontend/src/graphql/queries.js
@@ -15,6 +15,27 @@ export const login = gql`
}
hasElopage
publisherId
+ isAdmin
+ }
+ }
+`
+
+export const verifyLogin = gql`
+ query {
+ verifyLogin {
+ email
+ username
+ firstName
+ lastName
+ language
+ description
+ coinanimation
+ klickTipp {
+ newsletterState
+ }
+ hasElopage
+ publisherId
+ isAdmin
}
}
`
diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json
index faa61886d..b0dfe36d4 100644
--- a/frontend/src/locales/de.json
+++ b/frontend/src/locales/de.json
@@ -1,4 +1,5 @@
{
+ "admin_area": "Adminbereich",
"back": "Zurück",
"community": {
"choose-another-community": "Eine andere Gemeinschaft auswählen",
diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json
index 91e25f61d..135729ffa 100644
--- a/frontend/src/locales/en.json
+++ b/frontend/src/locales/en.json
@@ -1,4 +1,5 @@
{
+ "admin_area": "Admin's area",
"back": "Back",
"community": {
"choose-another-community": "Choose another community",
diff --git a/frontend/src/main.js b/frontend/src/main.js
index fd06bf9c0..1aa945608 100755
--- a/frontend/src/main.js
+++ b/frontend/src/main.js
@@ -51,7 +51,7 @@ Vue.config.productionTip = false
loadAllRules(i18n)
-addNavigationGuards(router, store)
+addNavigationGuards(router, store, apolloProvider.defaultClient)
/* eslint-disable no-new */
new Vue({
diff --git a/frontend/src/routes/guards.js b/frontend/src/routes/guards.js
index eebd6976e..dc8df4f13 100644
--- a/frontend/src/routes/guards.js
+++ b/frontend/src/routes/guards.js
@@ -1,12 +1,34 @@
-const addNavigationGuards = (router, store) => {
+import { verifyLogin } from '../graphql/queries'
+
+const addNavigationGuards = (router, store, apollo) => {
+ // handle publisherId
router.beforeEach((to, from, next) => {
- // handle publisherId
const publisherId = to.query.pid
if (publisherId) {
store.commit('publisherId', publisherId)
delete to.query.pid
}
- // handle authentication
+ next()
+ })
+
+ // store token on authenticate
+ router.beforeEach(async (to, from, next) => {
+ if (to.path === '/authenticate' && to.query.token) {
+ // TODO verify user in order to get user data
+ store.commit('token', to.query.token)
+ const result = await apollo.query({
+ query: verifyLogin,
+ fetchPolicy: 'network-only',
+ })
+ store.dispatch('login', result.data.verifyLogin)
+ next({ path: '/overview' })
+ } else {
+ next()
+ }
+ })
+
+ // handle authentication
+ router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !store.state.token) {
next({ path: '/login' })
} else {
diff --git a/frontend/src/routes/guards.test.js b/frontend/src/routes/guards.test.js
index cf366eac8..f271c5427 100644
--- a/frontend/src/routes/guards.test.js
+++ b/frontend/src/routes/guards.test.js
@@ -30,7 +30,7 @@ describe('navigation guards', () => {
})
describe('authorization', () => {
- const navGuard = router.beforeHooks[0]
+ const navGuard = router.beforeHooks[2]
const next = jest.fn()
it('redirects to login when not authorized', () => {
diff --git a/frontend/src/routes/router.test.js b/frontend/src/routes/router.test.js
index df4f9c229..cd26b6f6b 100644
--- a/frontend/src/routes/router.test.js
+++ b/frontend/src/routes/router.test.js
@@ -49,8 +49,8 @@ describe('router', () => {
expect(routes.find((r) => r.path === '/').redirect()).toEqual({ path: '/login' })
})
- it('has fourteen routes defined', () => {
- expect(routes).toHaveLength(14)
+ it('has fifteen routes defined', () => {
+ expect(routes).toHaveLength(15)
})
describe('overview', () => {
diff --git a/frontend/src/routes/routes.js b/frontend/src/routes/routes.js
index a3c1389ce..f6975d09d 100755
--- a/frontend/src/routes/routes.js
+++ b/frontend/src/routes/routes.js
@@ -1,6 +1,9 @@
import NotFound from '@/views/NotFoundPage.vue'
const routes = [
+ {
+ path: '/authenticate',
+ },
{
path: '/',
redirect: (to) => {
diff --git a/frontend/src/store/store.js b/frontend/src/store/store.js
index 6a229c161..c49197059 100644
--- a/frontend/src/store/store.js
+++ b/frontend/src/store/store.js
@@ -34,6 +34,9 @@ export const mutations = {
if (isNaN(pubId)) pubId = null
state.publisherId = pubId
},
+ isAdmin: (state, isAdmin) => {
+ state.isAdmin = !!isAdmin
+ },
community: (state, community) => {
state.community = community
},
@@ -57,6 +60,7 @@ export const actions = {
commit('newsletterState', data.klickTipp.newsletterState)
commit('hasElopage', data.hasElopage)
commit('publisherId', data.publisherId)
+ commit('isAdmin', data.isAdmin)
},
logout: ({ commit, state }) => {
commit('token', null)
@@ -69,6 +73,7 @@ export const actions = {
commit('newsletterState', null)
commit('hasElopage', false)
commit('publisherId', null)
+ commit('isAdmin', false)
localStorage.clear()
},
}
@@ -87,6 +92,7 @@ export const store = new Vuex.Store({
username: '',
description: '',
token: null,
+ isAdmin: false,
coinanimation: true,
newsletterState: null,
community: {
diff --git a/frontend/src/store/store.test.js b/frontend/src/store/store.test.js
index bdb98d03b..829678b44 100644
--- a/frontend/src/store/store.test.js
+++ b/frontend/src/store/store.test.js
@@ -148,11 +148,12 @@ describe('Vuex store', () => {
},
hasElopage: false,
publisherId: 1234,
+ isAdmin: true,
}
- it('calls ten commits', () => {
+ it('calls eleven commits', () => {
login({ commit, state }, commitedData)
- expect(commit).toHaveBeenCalledTimes(10)
+ expect(commit).toHaveBeenCalledTimes(11)
})
it('commits email', () => {
@@ -204,15 +205,20 @@ describe('Vuex store', () => {
login({ commit, state }, commitedData)
expect(commit).toHaveBeenNthCalledWith(10, 'publisherId', 1234)
})
+
+ it('commits isAdmin', () => {
+ login({ commit, state }, commitedData)
+ expect(commit).toHaveBeenNthCalledWith(11, 'isAdmin', true)
+ })
})
describe('logout', () => {
const commit = jest.fn()
const state = {}
- it('calls ten commits', () => {
+ it('calls eleven commits', () => {
logout({ commit, state })
- expect(commit).toHaveBeenCalledTimes(10)
+ expect(commit).toHaveBeenCalledTimes(11)
})
it('commits token', () => {
@@ -265,6 +271,11 @@ describe('Vuex store', () => {
expect(commit).toHaveBeenNthCalledWith(10, 'publisherId', null)
})
+ it('commits isAdmin', () => {
+ logout({ commit, state })
+ expect(commit).toHaveBeenNthCalledWith(11, 'isAdmin', false)
+ })
+
// how to get this working?
it.skip('calls localStorage.clear()', () => {
const clearStorageMock = jest.fn()