diff --git a/backend/src/middleware/permissionsMiddleware.ts b/backend/src/middleware/permissionsMiddleware.ts index b8c728f4c..0f2b71678 100644 --- a/backend/src/middleware/permissionsMiddleware.ts +++ b/backend/src/middleware/permissionsMiddleware.ts @@ -384,7 +384,7 @@ export default shield( Tag: allow, reports: isModerator, statistics: allow, - currentUser: allow, + currentUser: isAuthenticated, Group: isAuthenticated, GroupMembers: isAllowedSeeingGroupMembers, GroupCount: isAuthenticated, @@ -392,7 +392,6 @@ export default shield( profilePagePosts: allow, Comment: allow, User: or(noEmailFilter, isAdmin), - isLoggedIn: allow, Badge: allow, PostsEmotionsCountByEmotion: allow, PostsEmotionsByCurrentUser: isAuthenticated, diff --git a/backend/src/schema/resolvers/user_management.spec.ts b/backend/src/schema/resolvers/user_management.spec.ts index d9905fd71..527821856 100644 --- a/backend/src/schema/resolvers/user_management.spec.ts +++ b/backend/src/schema/resolvers/user_management.spec.ts @@ -64,55 +64,6 @@ afterEach(async () => { await cleanDatabase() }) -describe('isLoggedIn', () => { - const isLoggedInQuery = gql` - { - isLoggedIn - } - ` - const respondsWith = async (expected) => { - await expect(query({ query: isLoggedInQuery })).resolves.toMatchObject(expected) - } - - describe('unauthenticated', () => { - it('returns false', async () => { - await respondsWith({ data: { isLoggedIn: false } }) - }) - }) - - describe('authenticated', () => { - beforeEach(async () => { - user = await Factory.build('user', { id: 'u3' }) - const userBearerToken = encode({ id: 'u3' }) - req = { headers: { authorization: `Bearer ${userBearerToken}` } } - }) - - it('returns true', async () => { - await respondsWith({ data: { isLoggedIn: true } }) - }) - - describe('but user is disabled', () => { - beforeEach(async () => { - await disable('u3') - }) - - it('returns false', async () => { - await respondsWith({ data: { isLoggedIn: false } }) - }) - }) - - describe('but user is deleted', () => { - beforeEach(async () => { - await user.update({ updatedAt: new Date().toISOString(), deleted: true }) - }) - - it('returns false', async () => { - await respondsWith({ data: { isLoggedIn: false } }) - }) - }) - }) -}) - describe('currentUser', () => { const currentUserQuery = gql` { @@ -135,8 +86,8 @@ describe('currentUser', () => { } describe('unauthenticated', () => { - it('returns null', async () => { - await respondsWith({ data: { currentUser: null } }) + it('throws "Not Authorized!"', async () => { + await respondsWith({ errors: [{ message: 'Not Authorized!' }] }) }) }) @@ -200,10 +151,32 @@ describe('currentUser', () => { ) }) - it('returns empty array for all categories', async () => { + it('returns all categories by default', async () => { await respondsWith({ data: { - currentUser: expect.objectContaining({ activeCategories: [] }), + currentUser: expect.objectContaining({ + activeCategories: [ + 'cat1', + 'cat10', + 'cat11', + 'cat12', + 'cat13', + 'cat14', + 'cat15', + 'cat16', + 'cat17', + 'cat18', + 'cat19', + 'cat2', + 'cat3', + 'cat4', + 'cat5', + 'cat6', + 'cat7', + 'cat8', + 'cat9', + ], + }), }, }) }) diff --git a/backend/src/schema/resolvers/user_management.ts b/backend/src/schema/resolvers/user_management.ts index dfc33b6ae..e9376f940 100644 --- a/backend/src/schema/resolvers/user_management.ts +++ b/backend/src/schema/resolvers/user_management.ts @@ -1,5 +1,6 @@ import { AuthenticationError } from 'apollo-server' import bcrypt from 'bcryptjs' +import { neo4jgraphql } from 'neo4j-graphql-js' import { getNeode } from '@db/neo4j' import encode from '@jwt/encode' @@ -11,38 +12,8 @@ const neode = getNeode() export default { Query: { - isLoggedIn: (_, args, { driver, user }) => { - return Boolean(user && user.id) - }, - currentUser: async (object, params, context, resolveInfo) => { - const { user, driver } = context - if (!user) return null - const session = driver.session() - const currentUserTransactionPromise = session.readTransaction(async (transaction) => { - const result = await transaction.run( - ` - MATCH (user:User {id: $id}) - OPTIONAL MATCH (category:Category) WHERE NOT ((user)-[:NOT_INTERESTED_IN]->(category)) - OPTIONAL MATCH (cats:Category) - WITH user, [(user)<-[:OWNED_BY]-(medium:SocialMedia) | properties(medium) ] AS media, category, toString(COUNT(cats)) AS categoryCount - RETURN user {.*, socialMedia: media, activeCategories: collect(category.id) } AS user, categoryCount - `, - { id: user.id }, - ) - const [categoryCount] = result.records.map((record) => record.get('categoryCount')) - const [currentUser] = result.records.map((record) => record.get('user')) - // frontend expects empty array when all categories are selected - if (currentUser.activeCategories.length === parseInt(categoryCount)) - currentUser.activeCategories = [] - return currentUser - }) - try { - const currentUser = await currentUserTransactionPromise - return currentUser - } finally { - session.close() - } - }, + currentUser: async (object, params, context, resolveInfo) => + neo4jgraphql(object, { id: context.user.id }, context, resolveInfo), }, Mutation: { login: async (_, { email, password }, { driver, req, user }) => { diff --git a/backend/src/schema/resolvers/users.ts b/backend/src/schema/resolvers/users.ts index afbdd42a6..e93dffbd0 100644 --- a/backend/src/schema/resolvers/users.ts +++ b/backend/src/schema/resolvers/users.ts @@ -66,12 +66,12 @@ export default { const result = txc.run( ` MATCH (user:User)-[:PRIMARY_EMAIL]->(e:EmailAddress {email: $args.email}) - RETURN user`, + RETURN user {.*, email: e.email}`, { args }, ) return result }) - return readTxResult.records.map((r) => r.get('user').properties) + return readTxResult.records.map((r) => r.get('user')) } finally { session.close() } @@ -291,14 +291,14 @@ export default { const switchUserRoleResponse = await transaction.run( ` MATCH (user:User {id: $id}) + OPTIONAL MATCH (user)-[:PRIMARY_EMAIL]->(e:EmailAddress) SET user.role = $role SET user.updatedAt = toString(datetime()) - RETURN user {.*} + RETURN user {.*, email: e.email} `, { id, role }, ) - const [user] = switchUserRoleResponse.records.map((record) => record.get('user')) - return user + return switchUserRoleResponse.records.map((record) => record.get('user'))[0] }) try { const user = await writeTxResultPromise @@ -383,14 +383,6 @@ export default { }, }, User: { - email: async (parent, params, context, resolveInfo) => { - if (typeof parent.email !== 'undefined') return parent.email - const { id } = parent - const statement = `MATCH(u:User {id: $id})-[:PRIMARY_EMAIL]->(e:EmailAddress) RETURN e` - const result = await neode.cypher(statement, { id }) - const [{ email }] = result.records.map((r) => r.get('e').properties) - return email - }, emailNotificationSettings: async (parent, params, context, resolveInfo) => { return [ { diff --git a/backend/src/schema/types/type/User.gql b/backend/src/schema/types/type/User.gql index 37281d6bb..cac622316 100644 --- a/backend/src/schema/types/type/User.gql +++ b/backend/src/schema/types/type/User.gql @@ -199,8 +199,7 @@ type Query { availableRoles: [UserRole]! mutedUsers: [User] blockedUsers: [User] - isLoggedIn: Boolean! - currentUser: User + currentUser: User! } enum Deletable { diff --git a/backend/src/server.spec.ts b/backend/src/server.spec.ts deleted file mode 100644 index 1d5c5aca8..000000000 --- a/backend/src/server.spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { createTestClient } from 'apollo-server-testing' - -import createServer from './server' - -/** - * This file is for demonstration purposes. It does not really test the - * `isLoggedIn` query but demonstrates how we can use `apollo-server-testing`. - * All we need to do is to get an instance of `ApolloServer` and maybe we want - * stub out `context` as shown below. - * - */ - -let user -let action -describe('isLoggedIn', () => { - beforeEach(() => { - action = async () => { - const { server } = createServer({ - context: () => { - return { - user, - } - }, - }) - const { query } = createTestClient(server) - - const isLoggedIn = `{ isLoggedIn }` - return query({ query: isLoggedIn }) - } - }) - - it('returns false', async () => { - const expected = expect.objectContaining({ data: { isLoggedIn: false } }) - await expect(action()).resolves.toEqual(expected) - }) - - describe('when authenticated', () => { - it('returns true', async () => { - user = { id: '123' } - const expected = expect.objectContaining({ data: { isLoggedIn: true } }) - await expect(action()).resolves.toEqual(expected) - }) - }) -}) diff --git a/webapp/components/LoginForm/LoginForm.vue b/webapp/components/LoginForm/LoginForm.vue index 59a6d7a24..5bdf356fe 100644 --- a/webapp/components/LoginForm/LoginForm.vue +++ b/webapp/components/LoginForm/LoginForm.vue @@ -100,7 +100,7 @@ export default { await this.$store.dispatch('auth/login', { email, password }) if (this.currentUser && this.currentUser.activeCategories) { this.resetCategories() - if (this.currentUser.activeCategories.length > 0) { + if (this.currentUser.activeCategories.length < 19) { this.currentUser.activeCategories.forEach((categoryId) => { this.toggleCategory(categoryId) })