-
- Die anzahl der offene Schöpfungen stimmen nicht! Diese wird bei absenden im $store
- hochgezählt. Die Liste die hier angezeigt wird ist SIMULIERT!
-
diff --git a/admin/src/pages/Overview.vue b/admin/src/pages/Overview.vue
index 056bb14a6..1c58751bc 100644
--- a/admin/src/pages/Overview.vue
+++ b/admin/src/pages/Overview.vue
@@ -76,7 +76,24 @@
diff --git a/admin/src/router/guards.js b/admin/src/router/guards.js
index d59234a25..4ed6c8516 100644
--- a/admin/src/router/guards.js
+++ b/admin/src/router/guards.js
@@ -1,12 +1,28 @@
+import { verifyLogin } from '../graphql/verifyLogin'
import CONFIG from '../config'
-const addNavigationGuards = (router, store) => {
+const addNavigationGuards = (router, store, apollo) => {
// store token on `authenticate`
- router.beforeEach((to, from, next) => {
+ router.beforeEach(async (to, from, next) => {
if (to.path === '/authenticate' && to.query && to.query.token) {
- // TODO verify user to get user data
store.commit('token', to.query.token)
- next({ path: '/' })
+ await apollo
+ .query({
+ query: verifyLogin,
+ fetchPolicy: 'network-only',
+ })
+ .then((result) => {
+ const moderator = result.data.verifyLogin
+ if (moderator.isAdmin) {
+ store.commit('moderator', moderator)
+ next({ path: '/' })
+ } else {
+ next({ path: '/not-found' })
+ }
+ })
+ .catch(() => {
+ next({ path: '/not-found' })
+ })
} else {
next()
}
@@ -16,7 +32,9 @@ const addNavigationGuards = (router, store) => {
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
+ (!store.state.token || // we do not have a token
+ !store.state.moderator || // no moderator set in store
+ !store.state.moderator.isAdmin) && // user is no admin
to.path !== '/not-found' && // we are not on `not-found`
to.path !== '/logout' // we are not on `logout`
) {
diff --git a/admin/src/router/guards.test.js b/admin/src/router/guards.test.js
index e69846aab..cd5b33e68 100644
--- a/admin/src/router/guards.test.js
+++ b/admin/src/router/guards.test.js
@@ -2,6 +2,13 @@ import addNavigationGuards from './guards'
import router from './router'
const storeCommitMock = jest.fn()
+const apolloQueryMock = jest.fn().mockResolvedValue({
+ data: {
+ verifyLogin: {
+ isAdmin: true,
+ },
+ },
+})
const store = {
commit: storeCommitMock,
@@ -10,7 +17,11 @@ const store = {
},
}
-addNavigationGuards(router, store)
+const apollo = {
+ query: apolloQueryMock,
+}
+
+addNavigationGuards(router, store, apollo)
describe('navigation guards', () => {
beforeEach(() => {
@@ -21,18 +32,70 @@ describe('navigation guards', () => {
const navGuard = router.beforeHooks[0]
const next = jest.fn()
- describe('with valid token', () => {
- it('commits the token to the store', async () => {
+ describe('with valid token and as admin', () => {
+ beforeEach(() => {
navGuard({ path: '/authenticate', query: { token: 'valid-token' } }, {}, next)
+ })
+
+ it('commits the token to the store', async () => {
expect(storeCommitMock).toBeCalledWith('token', 'valid-token')
})
+ it('commits the moderator to the store', () => {
+ expect(storeCommitMock).toBeCalledWith('moderator', { isAdmin: true })
+ })
+
it('redirects to /', async () => {
- navGuard({ path: '/authenticate', query: { token: 'valid-token' } }, {}, next)
expect(next).toBeCalledWith({ path: '/' })
})
})
+ describe('with valid token and not as admin', () => {
+ beforeEach(() => {
+ apolloQueryMock.mockResolvedValue({
+ data: {
+ verifyLogin: {
+ isAdmin: false,
+ },
+ },
+ })
+ navGuard({ path: '/authenticate', query: { token: 'valid-token' } }, {}, next)
+ })
+
+ it('commits the token to the store', async () => {
+ expect(storeCommitMock).toBeCalledWith('token', 'valid-token')
+ })
+
+ it('does not commit the moderator to the store', () => {
+ expect(storeCommitMock).not.toBeCalledWith('moderator', { isAdmin: false })
+ })
+
+ it('redirects to /not-found', async () => {
+ expect(next).toBeCalledWith({ path: '/not-found' })
+ })
+ })
+
+ describe('with valid token and server error on verification', () => {
+ beforeEach(() => {
+ apolloQueryMock.mockRejectedValue({
+ message: 'Ouch!',
+ })
+ navGuard({ path: '/authenticate', query: { token: 'valid-token' } }, {}, next)
+ })
+
+ it('commits the token to the store', async () => {
+ expect(storeCommitMock).toBeCalledWith('token', 'valid-token')
+ })
+
+ it('does not commit the moderator to the store', () => {
+ expect(storeCommitMock).not.toBeCalledWith('moderator', { isAdmin: false })
+ })
+
+ it('redirects to /not-found', async () => {
+ expect(next).toBeCalledWith({ path: '/not-found' })
+ })
+ })
+
describe('without valid token', () => {
it('does not commit the token to the store', async () => {
navGuard({ path: '/authenticate' }, {}, next)
@@ -55,9 +118,16 @@ describe('navigation guards', () => {
expect(next).toBeCalledWith({ path: '/not-found' })
})
- it('does not redirect when token in store', () => {
+ it('redirects to not found with token in store and not moderator', () => {
store.state.token = 'valid token'
navGuard({ path: '/' }, {}, next)
+ expect(next).toBeCalledWith({ path: '/not-found' })
+ })
+
+ it('does not redirect with token in store and as moderator', () => {
+ store.state.token = 'valid token'
+ store.state.moderator = { isAdmin: true }
+ navGuard({ path: '/' }, {}, next)
expect(next).toBeCalledWith()
})
})
diff --git a/admin/src/store/store.js b/admin/src/store/store.js
index d67537499..fe5629e19 100644
--- a/admin/src/store/store.js
+++ b/admin/src/store/store.js
@@ -18,6 +18,9 @@ export const mutations = {
token: (state, token) => {
state.token = token
},
+ setOpenCreations: (state, openCreations) => {
+ state.openCreations = openCreations
+ },
moderator: (state, moderator) => {
state.moderator = moderator
},
@@ -26,6 +29,7 @@ export const mutations = {
export const actions = {
logout: ({ commit, state }) => {
commit('token', null)
+ commit('moderator', null)
window.localStorage.clear()
},
}
@@ -38,7 +42,7 @@ const store = new Vuex.Store({
],
state: {
token: CONFIG.DEBUG_DISABLE_AUTH ? 'validToken' : null,
- moderator: { name: 'Dertest Moderator', id: 0 },
+ moderator: null,
openCreations: 0,
},
// Syncronous mutation of the state
diff --git a/admin/src/store/store.test.js b/admin/src/store/store.test.js
index 4482a46bf..e027ebf1a 100644
--- a/admin/src/store/store.test.js
+++ b/admin/src/store/store.test.js
@@ -1,11 +1,19 @@
import store, { mutations, actions } from './store'
+import CONFIG from '../config'
-const { token, openCreationsPlus, openCreationsMinus, resetOpenCreations } = mutations
+jest.mock('../config')
+
+const {
+ token,
+ openCreationsPlus,
+ openCreationsMinus,
+ resetOpenCreations,
+ setOpenCreations,
+ moderator,
+} = mutations
const { logout } = actions
-const CONFIG = {
- DEBUG_DISABLE_AUTH: true,
-}
+CONFIG.DEBUG_DISABLE_AUTH = true
describe('Vuex store', () => {
describe('mutations', () => {
@@ -40,6 +48,22 @@ describe('Vuex store', () => {
expect(state.openCreations).toEqual(0)
})
})
+
+ describe('moderator', () => {
+ it('sets the moderator object in state', () => {
+ const state = { moderator: null }
+ moderator(state, { id: 1 })
+ expect(state.moderator).toEqual({ id: 1 })
+ })
+ })
+
+ describe('setOpenCreations', () => {
+ it('sets the open creations to given value', () => {
+ const state = { openCreations: 24 }
+ setOpenCreations(state, 12)
+ expect(state.openCreations).toEqual(12)
+ })
+ })
})
describe('actions', () => {
@@ -57,6 +81,11 @@ describe('Vuex store', () => {
expect(commit).toBeCalledWith('token', null)
})
+ it('deletes the moderator in store', () => {
+ logout({ commit, state })
+ expect(commit).toBeCalledWith('moderator', null)
+ })
+
it.skip('clears the window local storage', () => {
expect(windowStorageMock).toBeCalled()
})
diff --git a/backend/src/graphql/model/PendingCreation.ts b/backend/src/graphql/model/PendingCreation.ts
new file mode 100644
index 000000000..f1087ea0b
--- /dev/null
+++ b/backend/src/graphql/model/PendingCreation.ts
@@ -0,0 +1,34 @@
+import { ObjectType, Field, Int } from 'type-graphql'
+
+@ObjectType()
+export class PendingCreation {
+ @Field(() => String)
+ firstName: string
+
+ @Field(() => Int)
+ id?: number
+
+ @Field(() => String)
+ lastName: string
+
+ @Field(() => Number)
+ userId: number
+
+ @Field(() => String)
+ email: string
+
+ @Field(() => Date)
+ date: Date
+
+ @Field(() => String)
+ memo: string
+
+ @Field(() => Number)
+ amount: BigInt
+
+ @Field(() => Number)
+ moderator: number
+
+ @Field(() => [Number])
+ creation: number[]
+}
diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts
index f3c9d1516..169b79250 100644
--- a/backend/src/graphql/resolver/AdminResolver.ts
+++ b/backend/src/graphql/resolver/AdminResolver.ts
@@ -1,7 +1,7 @@
import { Resolver, Query, Arg, Args, Authorized, Mutation } from 'type-graphql'
import { getCustomRepository, Raw } from 'typeorm'
import { UserAdmin } from '../model/UserAdmin'
-import { LoginUserRepository } from '../../typeorm/repository/LoginUser'
+import { PendingCreation } from '../model/PendingCreation'
import { RIGHTS } from '../../auth/RIGHTS'
import { TransactionCreationRepository } from '../../typeorm/repository/TransactionCreation'
import { PendingCreationRepository } from '../../typeorm/repository/PendingCreation'
@@ -14,19 +14,19 @@ export class AdminResolver {
@Authorized([RIGHTS.SEARCH_USERS])
@Query(() => [UserAdmin])
async searchUsers(@Arg('searchText') searchText: string): Promise