diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js index 9dcf35476..dc54d5a29 100644 --- a/backend/src/middleware/permissionsMiddleware.js +++ b/backend/src/middleware/permissionsMiddleware.js @@ -52,11 +52,13 @@ const isMySocialMedia = rule({ return socialMedia.ownedBy.node.id === user.id }) -const isAllowSeeingMembersOfGroup = rule({ +const isAllowedSeeingMembersOfGroup = rule({ cache: 'no_cache', })(async (_parent, args, { user, driver }) => { if (!user) return false const { id: groupId } = args + // Wolle: console.log('groupId: ', groupId) + // console.log('user.id: ', user.id) const session = driver.session() const readTxPromise = session.readTransaction(async (transaction) => { const transactionResponse = await transaction.run( @@ -64,19 +66,22 @@ const isAllowSeeingMembersOfGroup = rule({ MATCH (group:Group {id: $groupId}) OPTIONAL MATCH (admin:User {id: $userId})-[membership:MEMBER_OF]->(group) WHERE membership.role IN ['admin', 'owner'] - RETURN group, admin {.*, myRoleInGroup: membership.role} + RETURN group {.*}, admin {.*, myRoleInGroup: membership.role} `, { groupId, userId: user.id }, ) return { - admin: transactionResponse.records.map((record) => record.get('admin')), - group: transactionResponse.records.map((record) => record.get('group')), + admin: transactionResponse.records.map((record) => record.get('admin'))[0], + group: transactionResponse.records.map((record) => record.get('group'))[0], } }) try { - const [{ admin, group }] = await readTxPromise + const { admin, group } = await readTxPromise + // Wolle: console.log('admin: ', admin) + // console.log('group: ', group) return group.groupType === 'public' || !!admin } catch (error) { + // Wolle: console.log('error: ', error) throw new Error(error) } finally { session.close() @@ -146,7 +151,7 @@ export default shield( statistics: allow, currentUser: allow, Group: isAuthenticated, - GroupMember: isAllowSeeingMembersOfGroup, + GroupMember: isAllowedSeeingMembersOfGroup, Post: allow, profilePagePosts: allow, Comment: allow, diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js index b64484d37..91135a1db 100644 --- a/backend/src/schema/resolvers/groups.js +++ b/backend/src/schema/resolvers/groups.js @@ -48,15 +48,15 @@ export default { }, GroupMember: async (_object, params, context, _resolveInfo) => { const { id: groupId } = params + // Wolle: console.log('groupId: ', groupId) const session = context.driver.session() const readTxResultPromise = session.readTransaction(async (txc) => { const groupMemberCypher = ` - MATCH (user:User {id: $userId})-[membership:MEMBER_OF]->(:Group {id: $groupId}) + MATCH (user:User)-[membership:MEMBER_OF]->(:Group {id: $groupId}) RETURN user {.*, myRoleInGroup: membership.role} ` const result = await txc.run(groupMemberCypher, { groupId, - userId: context.user.id, }) return result.records.map((record) => record.get('user')) }) diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js index 81223a584..33aeaf2bd 100644 --- a/backend/src/schema/resolvers/groups.spec.js +++ b/backend/src/schema/resolvers/groups.spec.js @@ -208,127 +208,309 @@ describe('Group', () => { }) }) -// describe('GroupMember', () => { -// describe('unauthenticated', () => { -// it('throws authorization error', async () => { -// const { errors } = await query({ query: groupMemberQuery, variables: {} }) -// expect(errors[0]).toHaveProperty('message', 'Not Authorised!') -// }) -// }) +describe('GroupMember', () => { + describe('unauthenticated', () => { + it('throws authorization error', async () => { + variables = { + id: 'not-existing-group', + } + const { errors } = await query({ query: groupMemberQuery, variables }) + expect(errors[0]).toHaveProperty('message', 'Not Authorised!') + }) + }) -// describe('authenticated', () => { -// beforeEach(async () => { -// authenticatedUser = await user.toJson() -// }) + describe('authenticated', () => { + let otherUser + let ownerOfClosedGroupUser + let ownerOfHiddenGroupUser -// let otherUser + beforeEach(async () => { + // create users + otherUser = await Factory.build( + 'user', + { + id: 'other-user', + name: 'Other TestUser', + }, + { + email: 'test2@example.org', + password: '1234', + }, + ) + ownerOfClosedGroupUser = await Factory.build( + 'user', + { + id: 'owner-of-closed-group', + name: 'Owner Of Closed Group', + }, + { + email: 'owner-of-closed-group@example.org', + password: '1234', + }, + ) + ownerOfHiddenGroupUser = await Factory.build( + 'user', + { + id: 'owner-of-hidden-group', + name: 'Owner Of Hidden Group', + }, + { + email: 'owner-of-hidden-group@example.org', + password: '1234', + }, + ) + // create groups + authenticatedUser = await ownerOfClosedGroupUser.toJson() + await mutate({ + mutation: createGroupMutation, + variables: { + id: 'closed-group', + name: 'Uninteresting Group', + about: 'We will change nothing!', + description: 'We love it like it is!?' + descriptionAdditional100, + groupType: 'closed', + actionRadius: 'national', + categoryIds, + }, + }) + authenticatedUser = await ownerOfHiddenGroupUser.toJson() + await mutate({ + mutation: createGroupMutation, + variables: { + id: 'hidden-group', + name: 'Investigative Journalism Group', + about: 'We will change all.', + description: 'We research …' + descriptionAdditional100, + groupType: 'hidden', + actionRadius: 'global', + categoryIds, + }, + }) + authenticatedUser = await user.toJson() + await mutate({ + mutation: createGroupMutation, + variables: { + id: 'public-group', + name: 'The Best Group', + about: 'We will change the world!', + description: 'Some description' + descriptionAdditional100, + groupType: 'public', + actionRadius: 'regional', + categoryIds, + }, + }) + // create additional memberships + await mutate({ + mutation: enterGroupMutation, + variables: { + id: 'public-group', + userId: 'owner-of-closed-group', + }, + }) + await mutate({ + mutation: enterGroupMutation, + variables: { + id: 'public-group', + userId: 'owner-of-hidden-group', + }, + }) + await mutate({ + mutation: enterGroupMutation, + variables: { + id: 'closed-group', + userId: 'current-user', + }, + }) + await mutate({ + mutation: enterGroupMutation, + variables: { + id: 'hidden-group', + userId: 'owner-of-closed-group', + }, + }) + }) -// beforeEach(async () => { -// otherUser = await Factory.build( -// 'user', -// { -// id: 'other-user', -// name: 'Other TestUser', -// }, -// { -// email: 'test2@example.org', -// password: '1234', -// }, -// ) -// authenticatedUser = await otherUser.toJson() -// await mutate({ -// mutation: createGroupMutation, -// variables: { -// id: 'others-group', -// name: 'Uninteresting Group', -// about: 'We will change nothing!', -// description: 'We love it like it is!?' + descriptionAdditional100, -// groupType: 'closed', -// actionRadius: 'global', -// categoryIds, -// }, -// }) -// authenticatedUser = await user.toJson() -// await mutate({ -// mutation: createGroupMutation, -// variables: { -// id: 'my-group', -// name: 'The Best Group', -// about: 'We will change the world!', -// description: 'Some description' + descriptionAdditional100, -// groupType: 'public', -// actionRadius: 'regional', -// categoryIds, -// }, -// }) -// }) + describe('public group', () => { + beforeEach(async () => { + variables = { + id: 'public-group', + } + }) -// describe('query group members', () => { -// describe('by owner', () => { -// it.only('finds all members', async () => { -// const expected = { -// data: { -// GroupMember: expect.arrayContaining([ -// expect.objectContaining({ -// id: 'my-group', -// slug: 'the-best-group', -// myRole: 'owner', -// }), -// // Wolle: expect.objectContaining({ -// // id: 'others-group', -// // slug: 'uninteresting-group', -// // myRole: null, -// // }), -// ]), -// }, -// errors: undefined, -// } -// await expect(query({ query: groupQuery, variables: {} })).resolves.toMatchObject(expected) -// }) -// }) + describe('query group members', () => { + describe('by owner', () => { + it('finds all members', async () => { + const expected = { + data: { + GroupMember: expect.arrayContaining([ + expect.objectContaining({ + id: 'current-user', + myRoleInGroup: 'owner', + }), + expect.objectContaining({ + id: 'owner-of-closed-group', + myRoleInGroup: 'usual', + }), + expect.objectContaining({ + id: 'owner-of-hidden-group', + myRoleInGroup: 'usual', + }), + ]), + }, + errors: undefined, + } + const result = await mutate({ + mutation: groupMemberQuery, + variables, + }) + expect(result).toMatchObject(expected) + expect(result.data.GroupMember.length).toBe(3) + }) + }) -// describe('isMember = true', () => { -// it('finds only groups where user is member', async () => { -// const expected = { -// data: { -// Group: [ -// { -// id: 'my-group', -// slug: 'the-best-group', -// myRole: 'owner', -// }, -// ], -// }, -// errors: undefined, -// } -// await expect( -// query({ query: groupQuery, variables: { isMember: true } }), -// ).resolves.toMatchObject(expected) -// }) -// }) + describe('by "other-user"', () => { + it.only('throws authorization error', async () => { + authenticatedUser = await otherUser.toJson() + const result = await query({ query: groupMemberQuery, variables }) + console.log('result: ', result) + // Wolle: const { errors } = await query({ query: groupMemberQuery, variables }) + // expect(errors[0]).toHaveProperty('message', 'Not Authorised!') + }) + }) + }) -// describe('isMember = false', () => { -// it('finds only groups where user is not(!) member', async () => { -// const expected = { -// data: { -// Group: expect.arrayContaining([ -// expect.objectContaining({ -// id: 'others-group', -// slug: 'uninteresting-group', -// myRole: null, -// }), -// ]), -// }, -// errors: undefined, -// } -// await expect( -// query({ query: groupQuery, variables: { isMember: false } }), -// ).resolves.toMatchObject(expected) -// }) -// }) -// }) -// }) -// }) + describe('entered by its owner', () => { + describe('does not create additional "MEMBER_OF" relation and therefore', () => { + it('has still "owner" as membership role', async () => { + variables = { + id: 'public-group', + userId: 'current-user', + } + const expected = { + data: { + EnterGroup: { + id: 'current-user', + myRoleInGroup: 'owner', + }, + }, + errors: undefined, + } + await expect( + mutate({ + mutation: enterGroupMutation, + variables, + }), + ).resolves.toMatchObject(expected) + }) + }) + }) + }) + + describe('closed group', () => { + describe('entered by "current-user"', () => { + it('has "pending" as membership role', async () => { + variables = { + id: 'closed-group', + userId: 'current-user', + } + const expected = { + data: { + EnterGroup: { + id: 'current-user', + myRoleInGroup: 'pending', + }, + }, + errors: undefined, + } + await expect( + mutate({ + mutation: enterGroupMutation, + variables, + }), + ).resolves.toMatchObject(expected) + }) + }) + + describe('entered by its owner', () => { + describe('does not create additional "MEMBER_OF" relation and therefore', () => { + it('has still "owner" as membership role', async () => { + variables = { + id: 'closed-group', + userId: 'owner-of-closed-group', + } + const expected = { + data: { + EnterGroup: { + id: 'owner-of-closed-group', + myRoleInGroup: 'owner', + }, + }, + errors: undefined, + } + await expect( + mutate({ + mutation: enterGroupMutation, + variables, + }), + ).resolves.toMatchObject(expected) + }) + }) + }) + }) + + describe('hidden group', () => { + describe('entered by "owner-of-closed-group"', () => { + it('has "pending" as membership role', async () => { + variables = { + id: 'hidden-group', + userId: 'owner-of-closed-group', + } + const expected = { + data: { + EnterGroup: { + id: 'owner-of-closed-group', + myRoleInGroup: 'pending', + }, + }, + errors: undefined, + } + await expect( + mutate({ + mutation: enterGroupMutation, + variables, + }), + ).resolves.toMatchObject(expected) + }) + }) + + describe('entered by its owner', () => { + describe('does not create additional "MEMBER_OF" relation and therefore', () => { + it('has still "owner" as membership role', async () => { + variables = { + id: 'hidden-group', + userId: 'owner-of-hidden-group', + } + const expected = { + data: { + EnterGroup: { + id: 'owner-of-hidden-group', + myRoleInGroup: 'owner', + }, + }, + errors: undefined, + } + await expect( + mutate({ + mutation: enterGroupMutation, + variables, + }), + ).resolves.toMatchObject(expected) + }) + }) + }) + }) + }) +}) describe('CreateGroup', () => { beforeEach(() => {