From 0936602cb6133f6e9ba4ec96f8fd9a59d378984b Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 14 Mar 2023 12:17:10 +0100 Subject: [PATCH 01/16] feat(frontend): send coins via gradidoID --- frontend/src/components/GddSend/TransactionForm.vue | 13 ++++++++++++- frontend/src/components/TransactionRows/Name.vue | 5 +++-- frontend/src/graphql/mutations.js | 1 + frontend/src/graphql/queries.js | 1 + 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/GddSend/TransactionForm.vue b/frontend/src/components/GddSend/TransactionForm.vue index b560e0767..51e5c5ece 100644 --- a/frontend/src/components/GddSend/TransactionForm.vue +++ b/frontend/src/components/GddSend/TransactionForm.vue @@ -50,7 +50,7 @@ -
+
+
+ + {{ $t('form.recipient') }} + + + {{ gradidoID }} + +
-
+
{{ itemText }} @@ -36,7 +36,8 @@ export default { methods: { tunnelEmail() { this.$emit('set-tunneled-email', this.linkedUser.email) - if (this.$router.history.current.fullPath !== '/send') this.$router.push({ path: '/send' }) + if (this.$router.history.current.fullPath !== '/send') + this.$router.push({ path: '/send', query: { gradidoID: this.linkedUser.gradidoID } }) }, }, computed: { diff --git a/frontend/src/graphql/mutations.js b/frontend/src/graphql/mutations.js index 55858b09b..0f173d3b9 100644 --- a/frontend/src/graphql/mutations.js +++ b/frontend/src/graphql/mutations.js @@ -144,6 +144,7 @@ export const createContributionMessage = gql` export const login = gql` mutation($email: String!, $password: String!, $publisherId: Int) { login(email: $email, password: $password, publisherId: $publisherId) { + gradidoID email firstName lastName diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js index 7193eded0..45f360610 100644 --- a/frontend/src/graphql/queries.js +++ b/frontend/src/graphql/queries.js @@ -38,6 +38,7 @@ export const transactionsQuery = gql` linkedUser { firstName lastName + gradidoID email } decay { From 8db5c055e5ae39777ef164349c35b4a03b32a58c Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 14 Mar 2023 13:08:51 +0100 Subject: [PATCH 02/16] simple user query and test --- backend/src/auth/RIGHTS.ts | 1 + backend/src/auth/ROLES.ts | 1 + .../src/graphql/resolver/UserResolver.test.ts | 104 +++++++++++++++++- backend/src/graphql/resolver/UserResolver.ts | 15 +++ backend/src/seeds/graphql/queries.ts | 9 ++ 5 files changed, 129 insertions(+), 1 deletion(-) diff --git a/backend/src/auth/RIGHTS.ts b/backend/src/auth/RIGHTS.ts index 22be48e40..3f39f1424 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -36,6 +36,7 @@ export enum RIGHTS { CREATE_CONTRIBUTION_MESSAGE = 'CREATE_CONTRIBUTION_MESSAGE', LIST_ALL_CONTRIBUTION_MESSAGES = 'LIST_ALL_CONTRIBUTION_MESSAGES', OPEN_CREATIONS = 'OPEN_CREATIONS', + USER = 'USER', // Admin SEARCH_USERS = 'SEARCH_USERS', SET_USER_ROLE = 'SET_USER_ROLE', diff --git a/backend/src/auth/ROLES.ts b/backend/src/auth/ROLES.ts index 2f3b4e081..df1ee0271 100644 --- a/backend/src/auth/ROLES.ts +++ b/backend/src/auth/ROLES.ts @@ -34,6 +34,7 @@ export const ROLE_USER = new Role('user', [ RIGHTS.CREATE_CONTRIBUTION_MESSAGE, RIGHTS.LIST_ALL_CONTRIBUTION_MESSAGES, RIGHTS.OPEN_CREATIONS, + RIGHTS.USER, ]) export const ROLE_ADMIN = new Role('admin', Object.values(RIGHTS)) // all rights diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index a57346583..c75678439 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -26,7 +26,13 @@ import { unDeleteUser, sendActivationEmail, } from '@/seeds/graphql/mutations' -import { verifyLogin, queryOptIn, searchAdminUsers, searchUsers } from '@/seeds/graphql/queries' +import { + verifyLogin, + queryOptIn, + searchAdminUsers, + searchUsers, + user as userQuery, +} from '@/seeds/graphql/queries' import { GraphQLError } from 'graphql' import { User } from '@entity/User' import CONFIG from '@/config' @@ -2189,6 +2195,102 @@ describe('UserResolver', () => { }) }) }) + + describe('user', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + describe('unauthenticated', () => { + it('throws and logs "401 Unauthorized" error', async () => { + await expect( + query({ + query: userQuery, + variables: { + identifier: 'identifier', + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + expect(logger.error).toBeCalledWith('401 Unauthorized') + }) + }) + + describe('authenticated', () => { + beforeAll(async () => { + user = await userFactory(testEnv, bibiBloxberg) + await mutate({ + mutation: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + }) + + describe('identifier is no gradido ID', () => { + it('throws and logs "No valid gradido ID" error', async () => { + await expect( + query({ + query: userQuery, + variables: { + identifier: 'identifier', + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('No valid gradido ID')], + }), + ) + expect(logger.error).toBeCalledWith('No valid gradido ID', 'identifier') + }) + }) + + describe('identifier is not found', () => { + it('throws and logs "No user found to given identifier" error', async () => { + await expect( + query({ + query: userQuery, + variables: { + identifier: '00000000-0000-0000-0000-000000000000', + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('No user found to given identifier')], + }), + ) + expect(logger.error).toBeCalledWith( + 'No user found to given identifier', + '00000000-0000-0000-0000-000000000000', + ) + }) + }) + + describe('identifier is found', () => { + it('returns user', async () => { + await expect( + query({ + query: userQuery, + variables: { + identifier: user.gradidoID, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + user: { + firstName: 'Bibi', + lastName: 'Bloxberg', + }, + }, + errors: undefined, + }), + ) + }) + }) + }) + }) }) describe('printTimeDuration', () => { diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 078a29a8e..c07946f0b 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -811,6 +811,21 @@ export class UserResolver { return true } + + @Authorized([RIGHTS.USER]) + @Query(() => User, { nullable: true }) + async user(@Arg('identifier') identifier: string): Promise { + const isGradidoID = + /^[0-9a-f]{8,8}-[0-9a-f]{4,4}-[0-9a-f]{4,4}-[0-9a-f]{4,4}-[0-9a-f]{12,12}$/.exec(identifier) + if (!isGradidoID) { + throw new LogError('No valid gradido ID', identifier) + } + const user = await DbUser.findOne({ where: { gradidoID: identifier } }) + if (!user) { + throw new LogError('No user found to given identifier', identifier) + } + return new User(user) + } } export async function findUserByEmail(email: string): Promise { diff --git a/backend/src/seeds/graphql/queries.ts b/backend/src/seeds/graphql/queries.ts index 299a0103d..7c893586a 100644 --- a/backend/src/seeds/graphql/queries.ts +++ b/backend/src/seeds/graphql/queries.ts @@ -322,3 +322,12 @@ export const listContributionMessages = gql` } } ` + +export const user = gql` + query ($identifier: String!) { + user(identifier: $identifier) { + firstName + lastName + } + } +` From 7232780baed4d76bfcf02012386b6c0b1bea4fe9 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 14 Mar 2023 19:25:06 +0100 Subject: [PATCH 03/16] send form with gradido id via url query --- .../components/GddSend/TransactionForm.vue | 25 ++++++++++++++++++- .../src/components/TransactionRows/Name.vue | 4 +-- frontend/src/graphql/queries.js | 9 +++++++ 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/GddSend/TransactionForm.vue b/frontend/src/components/GddSend/TransactionForm.vue index 51e5c5ece..8620337d6 100644 --- a/frontend/src/components/GddSend/TransactionForm.vue +++ b/frontend/src/components/GddSend/TransactionForm.vue @@ -65,7 +65,7 @@ {{ $t('form.recipient') }} - {{ gradidoID }} + {{ userName }}
@@ -129,6 +129,7 @@ import { SEND_TYPES } from '@/pages/Send' import InputEmail from '@/components/Inputs/InputEmail' import InputAmount from '@/components/Inputs/InputAmount' import InputTextarea from '@/components/Inputs/InputTextarea' +import { user as userQuery } from '@/graphql/queries' export default { name: 'TransactionForm', @@ -153,6 +154,7 @@ export default { memo: this.memo, }, radioSelected: this.selected, + userName: '', } }, methods: { @@ -173,11 +175,32 @@ export default { this.form.amount = '' this.form.memo = '' this.$refs.formValidator.validate() + if (this.$route.query && !this.$route.query === {}) this.$router.replace({ query: undefined }) }, setNewRecipientEmail() { this.form.email = this.recipientEmail ? this.recipientEmail : this.form.email }, }, + apollo: { + UserName: { + query() { + return userQuery + }, + fetchPolicy: 'network-only', + variables() { + return { identifier: this.gradidoID } + }, + skip() { + return !this.gradidoID + }, + update({ user }) { + this.userName = `${user.firstName} ${user.lastName}` + }, + error({ message }) { + this.toastError(message) + }, + }, + }, watch: { recipientEmail() { this.setNewRecipientEmail() diff --git a/frontend/src/components/TransactionRows/Name.vue b/frontend/src/components/TransactionRows/Name.vue index 84b235cb3..de87c0ad1 100644 --- a/frontend/src/components/TransactionRows/Name.vue +++ b/frontend/src/components/TransactionRows/Name.vue @@ -36,8 +36,8 @@ export default { methods: { tunnelEmail() { this.$emit('set-tunneled-email', this.linkedUser.email) - if (this.$router.history.current.fullPath !== '/send') - this.$router.push({ path: '/send', query: { gradidoID: this.linkedUser.gradidoID } }) + if (this.$router.history.current.fullPath !== '/send') this.$router.push({ path: '/send' }) + this.$router.push({ query: { gradidoID: this.linkedUser.gradidoID } }) }, }, computed: { diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js index 45f360610..ceaa6043b 100644 --- a/frontend/src/graphql/queries.js +++ b/frontend/src/graphql/queries.js @@ -268,3 +268,12 @@ export const openCreations = gql` } } ` + +export const user = gql` + query($identifier: String!) { + user(identifier: $identifier) { + firstName + lastName + } + } +` From 4c15669c4b38041cc77a250bc85c1323385d8610 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 14 Mar 2023 19:48:16 +0100 Subject: [PATCH 04/16] remove unused listeners, remove tunneled email and its methods --- .../src/components/GddSend/TransactionForm.vue | 15 --------------- frontend/src/components/GddTransactionList.vue | 3 --- .../Template/RightSide/LastTransactions.vue | 6 +----- frontend/src/components/TransactionRows/Name.vue | 1 - .../Transactions/TransactionReceive.vue | 1 - .../components/Transactions/TransactionSend.vue | 1 - frontend/src/layouts/DashboardLayout.vue | 12 ------------ frontend/src/pages/Transactions.vue | 1 - 8 files changed, 1 insertion(+), 39 deletions(-) diff --git a/frontend/src/components/GddSend/TransactionForm.vue b/frontend/src/components/GddSend/TransactionForm.vue index 8620337d6..a4e527d95 100644 --- a/frontend/src/components/GddSend/TransactionForm.vue +++ b/frontend/src/components/GddSend/TransactionForm.vue @@ -145,7 +145,6 @@ export default { memo: { type: String, default: '' }, selected: { type: String, default: 'send' }, }, - inject: ['getTunneledEmail'], data() { return { form: { @@ -177,9 +176,6 @@ export default { this.$refs.formValidator.validate() if (this.$route.query && !this.$route.query === {}) this.$router.replace({ query: undefined }) }, - setNewRecipientEmail() { - this.form.email = this.recipientEmail ? this.recipientEmail : this.form.email - }, }, apollo: { UserName: { @@ -201,11 +197,6 @@ export default { }, }, }, - watch: { - recipientEmail() { - this.setNewRecipientEmail() - }, - }, computed: { disabled() { if ( @@ -224,16 +215,10 @@ export default { sendTypes() { return SEND_TYPES }, - recipientEmail() { - return this.getTunneledEmail() - }, gradidoID() { return this.$route.query && this.$route.query.gradidoID }, }, - created() { - this.setNewRecipientEmail() - }, mounted() { if (this.form.email !== '') this.$refs.formValidator.validate() }, diff --git a/frontend/src/components/GddTransactionList.vue b/frontend/src/components/GddTransactionList.vue index deed0dedb..63e203f31 100644 --- a/frontend/src/components/GddTransactionList.vue +++ b/frontend/src/components/GddTransactionList.vue @@ -37,7 +37,6 @@ @@ -45,7 +44,6 @@ @@ -53,7 +51,6 @@ diff --git a/frontend/src/components/Template/RightSide/LastTransactions.vue b/frontend/src/components/Template/RightSide/LastTransactions.vue index 54e959436..b08fbf89a 100644 --- a/frontend/src/components/Template/RightSide/LastTransactions.vue +++ b/frontend/src/components/Template/RightSide/LastTransactions.vue @@ -32,11 +32,7 @@
- +
diff --git a/frontend/src/components/TransactionRows/Name.vue b/frontend/src/components/TransactionRows/Name.vue index de87c0ad1..8695645d8 100644 --- a/frontend/src/components/TransactionRows/Name.vue +++ b/frontend/src/components/TransactionRows/Name.vue @@ -35,7 +35,6 @@ export default { }, methods: { tunnelEmail() { - this.$emit('set-tunneled-email', this.linkedUser.email) if (this.$router.history.current.fullPath !== '/send') this.$router.push({ path: '/send' }) this.$router.push({ query: { gradidoID: this.linkedUser.gradidoID } }) }, diff --git a/frontend/src/components/Transactions/TransactionReceive.vue b/frontend/src/components/Transactions/TransactionReceive.vue index 9d6f0207a..1d7c99ff3 100644 --- a/frontend/src/components/Transactions/TransactionReceive.vue +++ b/frontend/src/components/Transactions/TransactionReceive.vue @@ -14,7 +14,6 @@