From 9d17e31d8fd8e0e90e366b1c8993222980a585fc Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 26 Oct 2021 11:35:25 +0200 Subject: [PATCH 001/178] Change the dropbox functionality so that the language is set when changing the language in the register form. --- frontend/src/views/Pages/Register.vue | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontend/src/views/Pages/Register.vue b/frontend/src/views/Pages/Register.vue index ff83f84fc..e4e9650fc 100755 --- a/frontend/src/views/Pages/Register.vue +++ b/frontend/src/views/Pages/Register.vue @@ -161,6 +161,7 @@ import InputEmail from '../../components/Inputs/InputEmail.vue' import InputPasswordConfirmation from '../../components/Inputs/InputPasswordConfirmation.vue' import LanguageSwitchSelect from '../../components/LanguageSwitchSelect.vue' import { registerUser } from '../../graphql/mutations' +import { localeChanged } from 'vee-validate' export default { components: { InputPasswordConfirmation, InputEmail, LanguageSwitchSelect }, @@ -187,6 +188,10 @@ export default { methods: { updateLanguage(e) { this.language = e + console.log('language', this.language) + this.$store.commit('language', this.language) + this.$i18n.locale = this.language + localeChanged(this.language) }, getValidationState({ dirty, validated, valid = null }) { return dirty || validated ? valid : null From 489743b6d6750b8e23fe063f39a863fcf7a4975b Mon Sep 17 00:00:00 2001 From: ogerly Date: Fri, 5 Nov 2021 13:50:10 +0100 Subject: [PATCH 002/178] add navbar and routes --- admin/src/App.vue | 9 ++++++++- admin/src/components/NavBar.vue | 19 +++++++++++++++++++ admin/src/components/UserTable.vue | 24 ++++++++++++++++++++++++ admin/src/main.js | 2 ++ admin/src/router/routes.js | 29 ++++++++++++++++++++++++++--- admin/src/views/Creation.vue | 9 +++++++++ admin/src/views/Overview.vue | 21 +++++++++++++++++++++ admin/src/views/UserSearch.vue | 9 +++++++++ 8 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 admin/src/components/NavBar.vue create mode 100644 admin/src/components/UserTable.vue create mode 100644 admin/src/views/Creation.vue create mode 100644 admin/src/views/Overview.vue create mode 100644 admin/src/views/UserSearch.vue diff --git a/admin/src/App.vue b/admin/src/App.vue index 9267cc82b..1a15d5d7c 100644 --- a/admin/src/App.vue +++ b/admin/src/App.vue @@ -1,9 +1,16 @@ diff --git a/admin/src/components/NavBar.vue b/admin/src/components/NavBar.vue new file mode 100644 index 000000000..42bc2161c --- /dev/null +++ b/admin/src/components/NavBar.vue @@ -0,0 +1,19 @@ + diff --git a/admin/src/components/UserTable.vue b/admin/src/components/UserTable.vue new file mode 100644 index 000000000..55e9b5b3c --- /dev/null +++ b/admin/src/components/UserTable.vue @@ -0,0 +1,24 @@ + + + diff --git a/admin/src/main.js b/admin/src/main.js index 61f65129e..089e02352 100644 --- a/admin/src/main.js +++ b/admin/src/main.js @@ -14,6 +14,8 @@ import VueApollo from 'vue-apollo' import CONFIG from './config' import { BootstrapVue } from 'bootstrap-vue' +import 'bootstrap-vue/dist/bootstrap-vue.css' +import 'bootstrap/dist/css/bootstrap.css' const httpLink = new HttpLink({ uri: CONFIG.GRAPHQL_URI }) diff --git a/admin/src/router/routes.js b/admin/src/router/routes.js index 40f3ce325..71500eece 100644 --- a/admin/src/router/routes.js +++ b/admin/src/router/routes.js @@ -1,13 +1,36 @@ -import NotFound from '@/components/NotFoundPage.vue' - const routes = [ { path: '/', + component: () => import('@/views/Overview.vue'), meta: { requiresAuth: true, }, }, - { path: '*', component: NotFound }, + { + path: '/overview', + component: () => import('@/views/Overview.vue'), + meta: { + requiresAuth: true, + }, + }, + { + path: '/user', + component: () => import('@/views/UserSearch.vue'), + meta: { + requiresAuth: true, + }, + }, + { + path: '/creation', + component: () => import('@/views/Creation.vue'), + meta: { + requiresAuth: true, + }, + }, + { + path: '*', + component: () => import('@/components/NotFoundPage.vue'), + }, ] export default routes diff --git a/admin/src/views/Creation.vue b/admin/src/views/Creation.vue new file mode 100644 index 000000000..794218163 --- /dev/null +++ b/admin/src/views/Creation.vue @@ -0,0 +1,9 @@ + diff --git a/admin/src/views/Overview.vue b/admin/src/views/Overview.vue new file mode 100644 index 000000000..0cb8a106d --- /dev/null +++ b/admin/src/views/Overview.vue @@ -0,0 +1,21 @@ + + diff --git a/admin/src/views/UserSearch.vue b/admin/src/views/UserSearch.vue new file mode 100644 index 000000000..be6ce38c9 --- /dev/null +++ b/admin/src/views/UserSearch.vue @@ -0,0 +1,9 @@ + From 01707674d2cc7e765edae5adcab7eb4507b32533 Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 8 Nov 2021 13:27:03 +0100 Subject: [PATCH 003/178] Added LoginUserRepository to get login_users. --- backend/src/typeorm/repository/LoginUser.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 backend/src/typeorm/repository/LoginUser.ts diff --git a/backend/src/typeorm/repository/LoginUser.ts b/backend/src/typeorm/repository/LoginUser.ts new file mode 100644 index 000000000..65ac6f67b --- /dev/null +++ b/backend/src/typeorm/repository/LoginUser.ts @@ -0,0 +1,11 @@ +import { EntityRepository, Repository } from 'typeorm' +import { LoginUser } from '@entity/LoginUser' + +@EntityRepository(LoginUser) +export class LoginUserRepository extends Repository { + async findByEmail(email: string): Promise { + return this.createQueryBuilder('loginUser') + .where('loginUser.email = :email', { email }) + .getOneOrFail() + } +} From a375e6ecc9d9982ad7ea569b3f186a562107df30 Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 8 Nov 2021 13:27:32 +0100 Subject: [PATCH 004/178] WIP: Change the login so that the unsecureLogin logic is implemented. --- backend/src/graphql/resolver/UserResolver.ts | 41 +++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index d05640e5e..bfb8b41b4 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -26,9 +26,13 @@ import { UserSettingRepository } from '../../typeorm/repository/UserSettingRepos import { Setting } from '../enum/Setting' import { UserRepository } from '../../typeorm/repository/User' import { LoginUser } from '@entity/LoginUser' +import { LoginUserRepository } from '../../typeorm/repository/LoginUser' @Resolver() export class UserResolver { + private userRepository = getCustomRepository(UserRepository) + private userSettingRepository = getCustomRepository(UserSettingRepository) + @Query(() => User) @UseMiddleware(klicktippNewsletterStateMiddleware) async login( @@ -36,27 +40,34 @@ export class UserResolver { @Ctx() context: any, ): Promise { email = email.trim().toLowerCase() - const result = await apiPost(CONFIG.LOGIN_API_URL + 'unsecureLogin', { email, password }) - - // if there is no user, throw an authentication error - if (!result.success) { - throw new Error(result.data) + // const result = await apiPost(CONFIG.LOGIN_API_URL + 'unsecureLogin', { email, password }) + // UnsecureLogin + const userCount = await LoginUser.count({ email }) + if (userCount === 0) { + throw new Error('No user with this credentials') } + if (!isPassword(password)) { + throw new Error('No user with this credentials') + } + + const loginUserRepository = getCustomRepository(LoginUserRepository) + const loginUser = await loginUserRepository.findByEmail(email) + if (loginUser.password) context.setHeaders.push({ key: 'token', value: encode(result.data.session_id, result.data.user.public_hex), }) - const user = new User(result.data.user) + // const user = new User(result.data.user) // Hack: Database Field is not validated properly and not nullable if (user.publisherId === 0) { user.publisherId = undefined } user.hasElopage = result.data.hasElopage // read additional settings from settings table - const userRepository = getCustomRepository(UserRepository) + // const userRepository = getCustomRepository(UserRepository) let userEntity: void | DbUser - userEntity = await userRepository.findByPubkeyHex(user.pubkey).catch(() => { + userEntity = await this.userRepository.findByPubkeyHex(user.pubkey).catch(() => { userEntity = new DbUser() userEntity.firstName = user.firstName userEntity.lastName = user.lastName @@ -64,7 +75,7 @@ export class UserResolver { userEntity.email = user.email userEntity.pubkey = Buffer.from(fromHex(user.pubkey)) - userRepository.save(userEntity).catch(() => { + this.userRepository.save(userEntity).catch(() => { throw new Error('error by save userEntity') }) }) @@ -216,7 +227,7 @@ export class UserResolver { }, } let response: UpdateUserInfosResponse | undefined - const userRepository = getCustomRepository(UserRepository) + // const userRepository = getCustomRepository(UserRepository) if ( firstName || @@ -232,7 +243,7 @@ export class UserResolver { if (!result.success) throw new Error(result.data) response = new UpdateUserInfosResponse(result.data) - const userEntity = await userRepository.findByPubkeyHex(context.pubKey) + const userEntity = await this.userRepository.findByPubkeyHex(context.pubKey) let userEntityChanged = false if (firstName) { userEntity.firstName = firstName @@ -247,7 +258,7 @@ export class UserResolver { userEntityChanged = true } if (userEntityChanged) { - userRepository.save(userEntity).catch((error) => { + this.userRepository.save(userEntity).catch((error) => { throw new Error(error) }) } @@ -255,10 +266,10 @@ export class UserResolver { if (coinanimation !== undefined) { // load user and balance - const userEntity = await userRepository.findByPubkeyHex(context.pubKey) + const userEntity = await this.userRepository.findByPubkeyHex(context.pubKey) - const userSettingRepository = getCustomRepository(UserSettingRepository) - userSettingRepository + // const userSettingRepository = getCustomRepository(UserSettingRepository) + this.userSettingRepository .setOrUpdate(userEntity.id, Setting.COIN_ANIMATION, coinanimation.toString()) .catch((error) => { throw new Error(error) From f2d8d400ac39c48c8534b6a1d5d4702f83920b87 Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 8 Nov 2021 14:26:32 +0100 Subject: [PATCH 005/178] Add libsodium. --- backend/package-lock.json | 19 ++++++++++++++++--- backend/package.json | 1 + backend/yarn.lock | 12 ++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index bb0d16316..f18213264 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -4903,6 +4903,19 @@ "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.9.34.tgz", "integrity": "sha512-gHTNU9xTtVgSp30IDX/57W4pETMXDIYXFfwEOJVXiYosiY7Hc7ogJwlBjOqlCcU04X0aA8DT57hdwUC1sJBJnA==" }, + "libsodium": { + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.9.tgz", + "integrity": "sha512-gfeADtR4D/CM0oRUviKBViMGXZDgnFdMKMzHsvBdqLBHd9ySi6EtYnmuhHVDDYgYpAO8eU8hEY+F8vIUAPh08A==" + }, + "libsodium-wrappers": { + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.9.tgz", + "integrity": "sha512-9HaAeBGk1nKTRFRHkt7nzxqCvnkWTjn1pdjKgcUnZxj0FyOP4CnhgFhMdrFfgNsukijBGyBLpP2m2uKT1vuWhQ==", + "requires": { + "libsodium": "^0.7.0" + } + }, "load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -6934,9 +6947,9 @@ } }, "validator": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.6.0.tgz", - "integrity": "sha512-gVgKbdbHgtxpRyR8K0O6oFZPhhB5tT1jeEHZR0Znr9Svg03U0+r9DXWMrnRAB+HtCStDQKlaIZm42tVsVjqtjg==" + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==" }, "vary": { "version": "1.1.2", diff --git a/backend/package.json b/backend/package.json index e47939b41..c0d244ecd 100644 --- a/backend/package.json +++ b/backend/package.json @@ -27,6 +27,7 @@ "graphql": "^15.5.1", "jest": "^27.2.4", "jsonwebtoken": "^8.5.1", + "libsodium-wrappers": "^0.7.9", "module-alias": "^2.2.2", "mysql2": "^2.3.0", "nodemailer": "^6.6.5", diff --git a/backend/yarn.lock b/backend/yarn.lock index 915766619..3cb8e9b43 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -3918,6 +3918,18 @@ libphonenumber-js@^1.9.7: resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.37.tgz#944f59a3618a8f85d9b619767a0b6fb87523f285" integrity sha512-RnUR4XwiVhMLnT7uFSdnmLeprspquuDtaShAgKTA+g/ms9/S4hQU3/QpFdh3iXPHtxD52QscXLm2W2+QBmvYAg== +libsodium-wrappers@^0.7.9: + version "0.7.9" + resolved "https://registry.yarnpkg.com/libsodium-wrappers/-/libsodium-wrappers-0.7.9.tgz#4ffc2b69b8f7c7c7c5594a93a4803f80f6d0f346" + integrity sha512-9HaAeBGk1nKTRFRHkt7nzxqCvnkWTjn1pdjKgcUnZxj0FyOP4CnhgFhMdrFfgNsukijBGyBLpP2m2uKT1vuWhQ== + dependencies: + libsodium "^0.7.0" + +libsodium@^0.7.0: + version "0.7.9" + resolved "https://registry.yarnpkg.com/libsodium/-/libsodium-0.7.9.tgz#4bb7bcbf662ddd920d8795c227ae25bbbfa3821b" + integrity sha512-gfeADtR4D/CM0oRUviKBViMGXZDgnFdMKMzHsvBdqLBHd9ySi6EtYnmuhHVDDYgYpAO8eU8hEY+F8vIUAPh08A== + load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" From 5400967142619d975ff74efb0aa802b1f7e6ce1f Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 8 Nov 2021 14:27:07 +0100 Subject: [PATCH 006/178] Get LoginUser from the DB, checkPassword. --- backend/src/graphql/resolver/UserResolver.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index be1e06305..4768b4659 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -196,9 +196,6 @@ const SecretKeyCryptographyEncrypt = (message: Buffer, encryptionKey: Buffer): B @Resolver() export class UserResolver { - - private userSettingRepository = getCustomRepository(UserSettingRepository) - @Query(() => User) @UseMiddleware(klicktippNewsletterStateMiddleware) async login( @@ -208,19 +205,18 @@ export class UserResolver { email = email.trim().toLowerCase() // const result = await apiPost(CONFIG.LOGIN_API_URL + 'unsecureLogin', { email, password }) // UnsecureLogin - const userCount = await LoginUser.count({ email }) - if (userCount === 0) { + const loginUserRepository = getCustomRepository(LoginUserRepository) + const loginUser = await loginUserRepository.findByEmail(email) + if (!loginUser) { throw new Error('No user with this credentials') } if (!isPassword(password)) { throw new Error('No user with this credentials') } - const loginUserRepository = getCustomRepository(LoginUserRepository) - const loginUser = await loginUserRepository.findByEmail(email) const passwordHash = SecretKeyCryptographyCreateKey(email, password) // return short and long hash - // loginUser.password = passwordHash[0].readBigUInt64LE() - if (loginUser.password !== passwordHash[0].readBigUInt64LE()) { + const loginUserPassword = BigInt(loginUser.password.toString()) + if (loginUserPassword !== passwordHash[0].readBigUInt64LE()) { throw new Error('No user with this credentials') } From 8a61f73c56ecf122f74978dc87545841c94f307e Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 10 Nov 2021 06:01:25 +0100 Subject: [PATCH 007/178] Added the load or store of the state_users. --- backend/src/graphql/resolver/UserResolver.ts | 42 +++++++++++--------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 4768b4659..50f2cfe48 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -224,6 +224,29 @@ export class UserResolver { key: 'token', value: encode(loginUser.pubKey), }) + + const userRepository = getCustomRepository(UserRepository) + let userEntity: void | DbUser + userEntity = await userRepository + .findByPubkeyHex(loginUser.pubKey.toString('utf8')) + .catch(() => { + // User not stored in state_users + userEntity = new DbUser() + userEntity.firstName = loginUser.firstName + userEntity.lastName = loginUser.lastName + userEntity.username = loginUser.username + userEntity.email = loginUser.email + userEntity.pubkey = Buffer.from(loginUser.pubKey.toString('utf8'), 'hex') + + userRepository.save(userEntity).catch(() => { + throw new Error('error by save userEntity') + }) + }) + if (!userEntity) { + throw new Error('error with cannot happen') + } + // TODO: Check and/or store hasElopage + // TODO: If user has no pubKey Create it again and update user. throw new Error('WIP') // const user = new User(result.data.user) // Hack: Database Field is not validated properly and not nullable @@ -232,25 +255,8 @@ export class UserResolver { // } // user.hasElopage = result.data.hasElopage // // read additional settings from settings table - // const userRepository = getCustomRepository(UserRepository) - // let userEntity: void | DbUser - // userEntity = await userRepository.findByPubkeyHex(user.pubkey).catch(() => { - // userEntity = new DbUser() - // userEntity.firstName = user.firstName - // userEntity.lastName = user.lastName - // userEntity.username = user.username - // userEntity.email = user.email - // userEntity.pubkey = Buffer.from(user.pubkey, 'hex') - // userRepository.save(userEntity).catch(() => { - // throw new Error('error by save userEntity') - // }) - // }) - // if (!userEntity) { - // throw new Error('error with cannot happen') - // } - - // // Save publisherId if Elopage is not yet registered + // Save publisherId if Elopage is not yet registered // if (!user.hasElopage && publisherId) { // user.publisherId = publisherId // await this.updateUserInfos( From e8b9765a34c588c909eb4581e4f96c35dec61cf4 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 10 Nov 2021 06:13:29 +0100 Subject: [PATCH 008/178] Withdrew the check on the sessionId when is authorized is called. --- backend/src/graphql/directive/isAuthorized.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/src/graphql/directive/isAuthorized.ts b/backend/src/graphql/directive/isAuthorized.ts index c553539bc..23971162d 100644 --- a/backend/src/graphql/directive/isAuthorized.ts +++ b/backend/src/graphql/directive/isAuthorized.ts @@ -14,13 +14,13 @@ const isAuthorized: AuthChecker = async ( if (context.token) { const decoded = decode(context.token) // if (decoded.sessionId && decoded.sessionId !== 0) { - // const result = await apiGet( - // `${CONFIG.LOGIN_API_URL}checkSessionState?session_id=${decoded.sessionId}`, - // ) - // context.sessionId = decoded.sessionId - context.pubKey = decoded.pubKey - context.setHeaders.push({ key: 'token', value: encode(decoded.pubKey) }) - return true + // const result = await apiGet( + // `${CONFIG.LOGIN_API_URL}checkSessionState?session_id=${decoded.sessionId}`, + // ) + // context.sessionId = decoded.sessionId + context.pubKey = decoded.pubKey + context.setHeaders.push({ key: 'token', value: encode(decoded.pubKey) }) + return true // } } throw new Error('401 Unauthorized') From 8730bc898c86f0300f3a589783e9b1f3b728e864 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 10 Nov 2021 06:14:02 +0100 Subject: [PATCH 009/178] We don't use the login_server to fill this model anymore. --- backend/src/graphql/model/User.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/backend/src/graphql/model/User.ts b/backend/src/graphql/model/User.ts index 08651ae17..5b7682e01 100644 --- a/backend/src/graphql/model/User.ts +++ b/backend/src/graphql/model/User.ts @@ -10,15 +10,17 @@ export class User { @PrimaryGeneratedColumn() id: number */ - constructor(json: any) { - 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 + constructor(json?: any) { + if (json) { + 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 + } } @Field(() => String) From 4af3c6ed8c1e422991ad73d7e24b6abc18b46b78 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 10 Nov 2021 06:14:38 +0100 Subject: [PATCH 010/178] Filling the returned model so that we get the same result as before. --- backend/src/graphql/resolver/UserResolver.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 50f2cfe48..b37c568a4 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -219,6 +219,7 @@ export class UserResolver { if (loginUserPassword !== passwordHash[0].readBigUInt64LE()) { throw new Error('No user with this credentials') } + // TODO: If user has no pubKey Create it again and update user. context.setHeaders.push({ key: 'token', @@ -245,8 +246,22 @@ export class UserResolver { if (!userEntity) { throw new Error('error with cannot happen') } - // TODO: Check and/or store hasElopage - // TODO: If user has no pubKey Create it again and update user. + + const user = new User() + user.email = email + user.firstName = loginUser.firstName + user.lastName = loginUser.lastName + user.username = loginUser.username + user.description = loginUser.description + user.pubkey = loginUser.pubKey.toString('utf8') + user.language = loginUser.language + // TODO: hasElopage + // auto elopage_buy = Poco::AutoPtr(new model::table::ElopageBuy); + // mHasElopage = elopage_buy->isExistInDB("payer_email", mEmail); + // else undefined + + // TODO: coinAnimation + return user throw new Error('WIP') // const user = new User(result.data.user) // Hack: Database Field is not validated properly and not nullable From 75d7ff3905f38b39777ce1934cc8f029255032a1 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 10 Nov 2021 06:15:58 +0100 Subject: [PATCH 011/178] Withdrew the comments. --- backend/src/graphql/directive/isAuthorized.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/backend/src/graphql/directive/isAuthorized.ts b/backend/src/graphql/directive/isAuthorized.ts index 23971162d..079e8e88c 100644 --- a/backend/src/graphql/directive/isAuthorized.ts +++ b/backend/src/graphql/directive/isAuthorized.ts @@ -2,9 +2,6 @@ import { AuthChecker } from 'type-graphql' -import CONFIG from '../../config' -import { apiGet } from '../../apis/HttpRequest' - import decode from '../../jwt/decode' import encode from '../../jwt/encode' @@ -13,15 +10,9 @@ const isAuthorized: AuthChecker = async ( ) => { if (context.token) { const decoded = decode(context.token) - // if (decoded.sessionId && decoded.sessionId !== 0) { - // const result = await apiGet( - // `${CONFIG.LOGIN_API_URL}checkSessionState?session_id=${decoded.sessionId}`, - // ) - // context.sessionId = decoded.sessionId context.pubKey = decoded.pubKey context.setHeaders.push({ key: 'token', value: encode(decoded.pubKey) }) return true - // } } throw new Error('401 Unauthorized') } From 30ef78dddea4214c5d65fc11dca19b5ce7d7e4d8 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 10 Nov 2021 10:25:41 +0100 Subject: [PATCH 012/178] Withdrew sessionId from the jwt token. --- backend/src/jwt/decode.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/backend/src/jwt/decode.ts b/backend/src/jwt/decode.ts index 34b3ed836..6f09276b0 100644 --- a/backend/src/jwt/decode.ts +++ b/backend/src/jwt/decode.ts @@ -2,27 +2,22 @@ import jwt, { JwtPayload } from 'jsonwebtoken' import CONFIG from '../config/' interface CustomJwtPayload extends JwtPayload { - sessionId: number pubKey: Buffer } type DecodedJwt = { token: string - sessionId: number pubKey: Buffer } export default (token: string): DecodedJwt => { if (!token) throw new Error('401 Unauthorized') - let sessionId = null let pubKey = null try { const decoded = jwt.verify(token, CONFIG.JWT_SECRET) - sessionId = decoded.sessionId pubKey = decoded.pubKey return { token, - sessionId, pubKey, } } catch (err) { From 74cc9522c21e53eb57f2a18347d01247eaedb637 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 10 Nov 2021 10:27:05 +0100 Subject: [PATCH 013/178] Added method to find User with their email & find User with a PubKey Buffer. --- backend/src/typeorm/repository/User.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/backend/src/typeorm/repository/User.ts b/backend/src/typeorm/repository/User.ts index 441c1b2c8..e127c179c 100644 --- a/backend/src/typeorm/repository/User.ts +++ b/backend/src/typeorm/repository/User.ts @@ -9,6 +9,15 @@ export class UserRepository extends Repository { .getOneOrFail() } + async findByPubkeyHexBuffer(pubkeyHexBuffer: Buffer): Promise { + const pubKeyString = pubkeyHexBuffer.toString('hex') + return await this.findByPubkeyHex(pubKeyString) + } + + async findByEmail(email: string): Promise { + return this.createQueryBuilder('user').where('user.email = :email', { email }).getOneOrFail() + } + async getUsersIndiced(userIds: number[]): Promise { if (!userIds.length) return [] const users = await this.createQueryBuilder('user') From a16b7ad47a7ff176760b808a4b3c297f148c486f Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 10 Nov 2021 10:28:31 +0100 Subject: [PATCH 014/178] Changed the decode of context.pubKey so that it is stored in a buffer and transformed after in a Hex string. --- backend/src/graphql/resolver/BalanceResolver.ts | 3 ++- backend/src/graphql/resolver/GdtResolver.ts | 3 ++- backend/src/graphql/resolver/TransactionResolver.ts | 6 ++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/backend/src/graphql/resolver/BalanceResolver.ts b/backend/src/graphql/resolver/BalanceResolver.ts index e067b4d68..902e50b8b 100644 --- a/backend/src/graphql/resolver/BalanceResolver.ts +++ b/backend/src/graphql/resolver/BalanceResolver.ts @@ -18,7 +18,8 @@ export class BalanceResolver { const balanceRepository = getCustomRepository(BalanceRepository) const userRepository = getCustomRepository(UserRepository) - const userEntity = await userRepository.findByPubkeyHex(context.pubKey) + const pubKeyString = Buffer.from(context.pubKey).toString('hex') + const userEntity = await userRepository.findByPubkeyHex(pubKeyString) const balanceEntity = await balanceRepository.findByUser(userEntity.id) const now = new Date() diff --git a/backend/src/graphql/resolver/GdtResolver.ts b/backend/src/graphql/resolver/GdtResolver.ts index b4f9a512b..ba10d45bf 100644 --- a/backend/src/graphql/resolver/GdtResolver.ts +++ b/backend/src/graphql/resolver/GdtResolver.ts @@ -22,7 +22,8 @@ export class GdtResolver { ): Promise { // load user const userRepository = getCustomRepository(UserRepository) - const userEntity = await userRepository.findByPubkeyHex(context.pubKey) + const pubKeyString = Buffer.from(context.pubKey).toString('hex') + const userEntity = await userRepository.findByPubkeyHex(pubKeyString) const resultGDT = await apiGet( `${CONFIG.GDT_API_URL}/GdtEntries/listPerEmailApi/${userEntity.email}/${currentPage}/${pageSize}/${order}`, diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 70dfdc505..b804c1af9 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -496,7 +496,8 @@ export class TransactionResolver { ): Promise { // load user const userRepository = getCustomRepository(UserRepository) - const userEntity = await userRepository.findByPubkeyHex(context.pubKey) + const pubKeyString = Buffer.from(context.pubKey).toString('hex') + const userEntity = await userRepository.findByPubkeyHex(pubKeyString) const transactions = await listTransactions(currentPage, pageSize, order, userEntity) @@ -531,7 +532,8 @@ export class TransactionResolver { // TODO this is subject to replay attacks // validate sender user (logged in) const userRepository = getCustomRepository(UserRepository) - const senderUser = await userRepository.findByPubkeyHex(context.pubKey) + const pubKeyString = Buffer.from(context.pubKey).toString('hex') + const senderUser = await userRepository.findByPubkeyHex(pubKeyString) if (senderUser.pubkey.length !== 32) { throw new Error('invalid sender public key') } From 6a85017406a315c86f8d84215ed5c524c639adfc Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 10 Nov 2021 10:30:35 +0100 Subject: [PATCH 015/178] PubKey decoded has to be cast to buffer and back to hex string, changed the coinanimation. --- backend/src/graphql/resolver/UserResolver.ts | 63 ++++++++++---------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index b37c568a4..a2d4ee0e8 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -228,21 +228,21 @@ export class UserResolver { const userRepository = getCustomRepository(UserRepository) let userEntity: void | DbUser - userEntity = await userRepository - .findByPubkeyHex(loginUser.pubKey.toString('utf8')) - .catch(() => { - // User not stored in state_users - userEntity = new DbUser() - userEntity.firstName = loginUser.firstName - userEntity.lastName = loginUser.lastName - userEntity.username = loginUser.username - userEntity.email = loginUser.email - userEntity.pubkey = Buffer.from(loginUser.pubKey.toString('utf8'), 'hex') + const loginUserPubKey = loginUser.pubKey + const loginUserPubKeyString = loginUserPubKey.toString('hex') + userEntity = await userRepository.findByPubkeyHex(loginUserPubKeyString).catch(() => { + // User not stored in state_users + userEntity = new DbUser() + userEntity.firstName = loginUser.firstName + userEntity.lastName = loginUser.lastName + userEntity.username = loginUser.username + userEntity.email = loginUser.email + userEntity.pubkey = loginUser.pubKey - userRepository.save(userEntity).catch(() => { - throw new Error('error by save userEntity') - }) + userRepository.save(userEntity).catch(() => { + throw new Error('error by save userEntity') }) + }) if (!userEntity) { throw new Error('error with cannot happen') } @@ -253,24 +253,19 @@ export class UserResolver { user.lastName = loginUser.lastName user.username = loginUser.username user.description = loginUser.description - user.pubkey = loginUser.pubKey.toString('utf8') + user.pubkey = loginUserPubKeyString user.language = loginUser.language - // TODO: hasElopage + // TODO: Get Method from PR (hasElopage) // auto elopage_buy = Poco::AutoPtr(new model::table::ElopageBuy); // mHasElopage = elopage_buy->isExistInDB("payer_email", mEmail); // else undefined + // user.hasElopage = result.data.hasElopage - // TODO: coinAnimation - return user - throw new Error('WIP') - // const user = new User(result.data.user) + // TODO: Get Method from PR (publisherId) // Hack: Database Field is not validated properly and not nullable // if (user.publisherId === 0) { // user.publisherId = undefined // } - // user.hasElopage = result.data.hasElopage - // // read additional settings from settings table - // Save publisherId if Elopage is not yet registered // if (!user.hasElopage && publisherId) { // user.publisherId = publisherId @@ -280,14 +275,15 @@ export class UserResolver { // ) // } - // const userSettingRepository = getCustomRepository(UserSettingRepository) - // const coinanimation = await userSettingRepository - // .readBoolean(userEntity.id, Setting.COIN_ANIMATION) - // .catch((error) => { - // throw new Error(error) - // }) - // user.coinanimation = coinanimation - // return user + // coinAnimation + const userSettingRepository = getCustomRepository(UserSettingRepository) + const coinanimation = await userSettingRepository + .readBoolean(userEntity.id, Setting.COIN_ANIMATION) + .catch((error) => { + throw new Error(error) + }) + user.coinanimation = coinanimation + return user } @Query(() => LoginViaVerificationCode) @@ -521,7 +517,8 @@ export class UserResolver { if (!result.success) throw new Error(result.data) response = new UpdateUserInfosResponse(result.data) - const userEntity = await userRepository.findByPubkeyHex(context.pubKey) + const pubKeyString = Buffer.from(context.pubKey).toString('hex') + const userEntity = await userRepository.findByPubkeyHex(pubKeyString) let userEntityChanged = false if (firstName) { userEntity.firstName = firstName @@ -543,8 +540,8 @@ export class UserResolver { } if (coinanimation !== undefined) { // load user and balance - - const userEntity = await userRepository.findByPubkeyHex(context.pubKey) + const pubKeyString = Buffer.from(context.pubKey).toString('hex') + const userEntity = await userRepository.findByPubkeyHex(pubKeyString) const userSettingRepository = getCustomRepository(UserSettingRepository) userSettingRepository From 5d886a93607a2af03b3c0d4cea51e9daf66e210e Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 10 Nov 2021 11:28:35 +0100 Subject: [PATCH 016/178] LoginUserRepository import has droped out put it in again. --- backend/src/graphql/resolver/UserResolver.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index ec28f964e..3aa7e7fd5 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -23,6 +23,7 @@ import { } from '../../middleware/klicktippMiddleware' import { CheckEmailResponse } from '../model/CheckEmailResponse' import { UserSettingRepository } from '../../typeorm/repository/UserSettingRepository' +import { LoginUserRepository } from '../../typeorm/repository/LoginUser' import { Setting } from '../enum/Setting' import { UserRepository } from '../../typeorm/repository/User' import { LoginUser } from '@entity/LoginUser' From 7655a647fe30e3ce7c129cfc1533419e372285ef Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 11 Nov 2021 06:15:21 +0100 Subject: [PATCH 017/178] Implementation of the LoginElopageBuys repository. --- backend/src/typeorm/repository/LoginElopageBuys.ts | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 backend/src/typeorm/repository/LoginElopageBuys.ts diff --git a/backend/src/typeorm/repository/LoginElopageBuys.ts b/backend/src/typeorm/repository/LoginElopageBuys.ts new file mode 100644 index 000000000..15f2a8492 --- /dev/null +++ b/backend/src/typeorm/repository/LoginElopageBuys.ts @@ -0,0 +1,5 @@ +import { EntityRepository, Repository } from 'typeorm' +import { LoginElopageBuys } from '@entity/LoginElopageBuys' + +@EntityRepository(LoginElopageBuys) +export class LoginElopageBuysRepository extends Repository {} From 1baf756c08953784111e28cd29e9a72f95e9e42a Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 11 Nov 2021 06:31:00 +0100 Subject: [PATCH 018/178] HasElopage has been called, search loginUser catch instead of if no user, context get's the pubKey at the end of the login call instead of the start. --- backend/src/graphql/resolver/UserResolver.ts | 27 ++++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 437d7072e..bee35d676 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -31,6 +31,7 @@ import { LoginElopageBuys } from '@entity/LoginElopageBuys' import { LoginUserBackup } from '@entity/LoginUserBackup' import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' import { sendEMail } from '../../util/sendEMail' +import { LoginElopageBuysRepository } from '../../typeorm/repository/LoginElopageBuys' // eslint-disable-next-line @typescript-eslint/no-var-requires const sodium = require('sodium-native') @@ -195,14 +196,9 @@ export class UserResolver { // const result = await apiPost(CONFIG.LOGIN_API_URL + 'unsecureLogin', { email, password }) // UnsecureLogin const loginUserRepository = getCustomRepository(LoginUserRepository) - const loginUser = await loginUserRepository.findByEmail(email) - if (!loginUser) { + const loginUser = await loginUserRepository.findByEmail(email).catch(() => { throw new Error('No user with this credentials') - } - if (!isPassword(password)) { - throw new Error('No user with this credentials') - } - + }) const passwordHash = SecretKeyCryptographyCreateKey(email, password) // return short and long hash const loginUserPassword = BigInt(loginUser.password.toString()) if (loginUserPassword !== passwordHash[0].readBigUInt64LE()) { @@ -210,11 +206,6 @@ export class UserResolver { } // TODO: If user has no pubKey Create it again and update user. - context.setHeaders.push({ - key: 'token', - value: encode(loginUser.pubKey), - }) - const userRepository = getCustomRepository(UserRepository) let userEntity: void | DbUser const loginUserPubKey = loginUser.pubKey @@ -244,11 +235,7 @@ export class UserResolver { user.description = loginUser.description user.pubkey = loginUserPubKeyString user.language = loginUser.language - // TODO: Get Method from PR (hasElopage) - // auto elopage_buy = Poco::AutoPtr(new model::table::ElopageBuy); - // mHasElopage = elopage_buy->isExistInDB("payer_email", mEmail); - // else undefined - // user.hasElopage = result.data.hasElopage + user.hasElopage = await this.hasElopage({ pubkey: loginUser.pubKey }) // TODO: Get Method from PR (publisherId) // Hack: Database Field is not validated properly and not nullable @@ -272,6 +259,12 @@ export class UserResolver { throw new Error(error) }) user.coinanimation = coinanimation + + context.setHeaders.push({ + key: 'token', + value: encode(loginUser.pubKey), + }) + return user } From e976c6854d88d2b4586eb6959c5af77df308cd93 Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 11 Nov 2021 06:41:56 +0100 Subject: [PATCH 019/178] Adding hasElopage and publisherId logic, hasElopage is called and checks if in ElopageBuys their is a user with this email, if hasElopage and publisherId is filled store it in the user table. --- backend/src/graphql/resolver/UserResolver.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index bee35d676..8462df819 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -236,7 +236,10 @@ export class UserResolver { user.pubkey = loginUserPubKeyString user.language = loginUser.language user.hasElopage = await this.hasElopage({ pubkey: loginUser.pubKey }) - + if (!user.hasElopage && publisherId) { + user.publisherId = publisherId + await this.updateUserInfos({ publisherId }, { pubKey: loginUser.pubKey }) + } // TODO: Get Method from PR (publisherId) // Hack: Database Field is not validated properly and not nullable // if (user.publisherId === 0) { From e97798d73c780f6a7c16870e1f04b64f20c255e4 Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 11 Nov 2021 06:42:27 +0100 Subject: [PATCH 020/178] Withdrew comments. --- backend/src/graphql/resolver/UserResolver.ts | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 8462df819..ee44346e7 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -31,7 +31,6 @@ import { LoginElopageBuys } from '@entity/LoginElopageBuys' import { LoginUserBackup } from '@entity/LoginUserBackup' import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' import { sendEMail } from '../../util/sendEMail' -import { LoginElopageBuysRepository } from '../../typeorm/repository/LoginElopageBuys' // eslint-disable-next-line @typescript-eslint/no-var-requires const sodium = require('sodium-native') @@ -235,24 +234,13 @@ export class UserResolver { user.description = loginUser.description user.pubkey = loginUserPubKeyString user.language = loginUser.language + + // Elopage Status & Stored PublisherId user.hasElopage = await this.hasElopage({ pubkey: loginUser.pubKey }) if (!user.hasElopage && publisherId) { user.publisherId = publisherId await this.updateUserInfos({ publisherId }, { pubKey: loginUser.pubKey }) } - // TODO: Get Method from PR (publisherId) - // Hack: Database Field is not validated properly and not nullable - // if (user.publisherId === 0) { - // user.publisherId = undefined - // } - // Save publisherId if Elopage is not yet registered - // if (!user.hasElopage && publisherId) { - // user.publisherId = publisherId - // await this.updateUserInfos( - // { publisherId }, - // { sessionId: result.data.session_id, pubKey: result.data.user.public_hex }, - // ) - // } // coinAnimation const userSettingRepository = getCustomRepository(UserSettingRepository) @@ -600,7 +588,6 @@ export class UserResolver { @Authorized() @Query(() => Boolean) async hasElopage(@Ctx() context: any): Promise { - // const result = await apiGet(CONFIG.LOGIN_API_URL + 'hasElopage?session_id=' + context.sessionId) const userRepository = getCustomRepository(UserRepository) const userEntity = await userRepository.findByPubkeyHex(context.pubKey).catch() if (!userEntity) { From 4cb7abdc9bfca2d601a66b417ef301c531d4cb59 Mon Sep 17 00:00:00 2001 From: ogerly Date: Thu, 11 Nov 2021 06:51:21 +0100 Subject: [PATCH 021/178] add momentjs, add component CreateFormular.vue formular for creating gdd --- admin/package.json | 2 + admin/src/components/CreateFormular.vue | 66 +++++++++++++++++++++ admin/src/components/UserTable.vue | 78 ++++++++++++++++++++++--- admin/src/locales/en.json | 17 +++++- admin/src/main.js | 4 ++ admin/src/views/Overview.vue | 7 ++- admin/yarn.lock | 12 ++++ 7 files changed, 177 insertions(+), 9 deletions(-) create mode 100644 admin/src/components/CreateFormular.vue diff --git a/admin/package.json b/admin/package.json index 2b4e33c91..5dff9145e 100644 --- a/admin/package.json +++ b/admin/package.json @@ -33,11 +33,13 @@ "dotenv-webpack": "^7.0.3", "graphql": "^15.6.1", "jest": "26.6.3", + "moment": "^2.29.1", "stats-webpack-plugin": "^0.7.0", "vue": "^2.6.11", "vue-apollo": "^3.0.8", "vue-i18n": "^8.26.5", "vue-jest": "^3.0.7", + "vue-moment": "^4.1.0", "vue-router": "^3.5.3", "vuex": "^3.6.2" }, diff --git a/admin/src/components/CreateFormular.vue b/admin/src/components/CreateFormular.vue new file mode 100644 index 000000000..5dd07a4c1 --- /dev/null +++ b/admin/src/components/CreateFormular.vue @@ -0,0 +1,66 @@ + + diff --git a/admin/src/components/UserTable.vue b/admin/src/components/UserTable.vue index 55e9b5b3c..2e92c9a7f 100644 --- a/admin/src/components/UserTable.vue +++ b/admin/src/components/UserTable.vue @@ -1,22 +1,86 @@ + diff --git a/frontend/src/views/Pages/Login.vue b/frontend/src/views/Pages/Login.vue index 3f5c840d4..4ef7f7fb6 100755 --- a/frontend/src/views/Pages/Login.vue +++ b/frontend/src/views/Pages/Login.vue @@ -103,11 +103,13 @@ export default { loader.hide() }) .catch((error) => { + if (!error.message.includes('user email not validated')) { + this.$toasted.error(this.$t('error.no-account')) + } else { + // : this.$t('error.no-email-verify') + this.$router.push('/thx/login') + } loader.hide() - const toastedError = !error.message.includes('user email not validated') - ? this.$t('error.no-account') - : this.$t('error.no-email-verify') - this.$toasted.error(toastedError) }) }, async onCreated() { diff --git a/frontend/src/views/Pages/thx.vue b/frontend/src/views/Pages/thx.vue index 9d9143456..5dc2bda19 100644 --- a/frontend/src/views/Pages/thx.vue +++ b/frontend/src/views/Pages/thx.vue @@ -36,6 +36,16 @@ const textFields = { button: 'login', linkTo: '/login', }, + login: { + subtitle: 'site.thx.activateEmail', + button: 'Send Activation Link', + linkTo: '/activateEmail', + }, + activateEmail: { + subtitle: 'site.thx.emailActivated', + button: 'login', + linkTo: '/login', + }, } export default { From 1bf8e8451da37e91db7233e45c83134430f47953 Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 12 Nov 2021 10:06:51 +0100 Subject: [PATCH 028/178] Textes for the new pages added. --- frontend/src/locales/de.json | 6 ++++++ frontend/src/locales/en.json | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index 1c298b416..97f49b5e8 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -153,6 +153,10 @@ "ooops": "Ooops!", "text": "Seite nicht gefunden. Aber keine Sorge, wir haben noch viele andere Seiten zum Erkunden" }, + "activateEmail": { + "headline": "", + "subtitle": "" + }, "checkEmail": { "errorText": "Email konnte nicht verifiziert werden.", "title": "Email wird verifiziert" @@ -179,8 +183,10 @@ "uppercase": "Ein Großbuchstabe erforderlich." }, "thx": { + "activateEmail": "Deine Email wurde noch nicht aktiviert, email erneut senden?", "checkEmail": "Deine Email würde erfolgreich verifiziert.", "email": "Wir haben dir eine eMail gesendet.", + "emailActivated": "Danke dass Du deine Email bestätigt hast.", "register": "Du bist jetzt registriert, bitte überprüfe deine Emails und klicke auf den Aktivierungslink.", "reset": "Dein Passwort wurde geändert.", "title": "Danke!" diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index fd57798c4..236d607cf 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -153,6 +153,10 @@ "ooops": "Ooops!", "text": "Page not found. Do not worry though, we have plenty of other pages to explore" }, + "activateEmail": { + "headline": "", + "subtitle": "" + }, "checkEmail": { "errorText": "Could not verify the email.", "title": "Verifing email" @@ -179,8 +183,10 @@ "uppercase": "One uppercase letter required." }, "thx": { + "activateEmail": "Your email has not been activated yet, send again the activation email?", "checkEmail": "Your email has been successfully verified.", "email": "We have sent you an email.", + "emailActivated": "Thank you your email has been activated.", "register": "You are registered now, please check your emails and click the activation link.", "reset": "Your password has been changed.", "title": "Thank you!" From 9a75604a7a6f00c0e7350963ba5e01afec450601 Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 12 Nov 2021 10:55:43 +0100 Subject: [PATCH 029/178] Change prozess so that a user that has not activate his account gets a warning that he needs to click the activation link in the email. Withdrew sendActivationMail again. --- frontend/src/locales/de.json | 3 +- frontend/src/locales/en.json | 3 +- frontend/src/routes/routes.js | 6 +- frontend/src/views/Pages/ActivateEmail.vue | 76 ---------------------- frontend/src/views/Pages/thx.vue | 17 ++--- 5 files changed, 14 insertions(+), 91 deletions(-) delete mode 100644 frontend/src/views/Pages/ActivateEmail.vue diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index 97f49b5e8..95504407d 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -183,10 +183,11 @@ "uppercase": "Ein Großbuchstabe erforderlich." }, "thx": { - "activateEmail": "Deine Email wurde noch nicht aktiviert, email erneut senden?", + "activateEmail": "Deine Email wurde noch nicht aktiviert, bitte überprüfe deine Email und Klicke den Aktivierungslink!", "checkEmail": "Deine Email würde erfolgreich verifiziert.", "email": "Wir haben dir eine eMail gesendet.", "emailActivated": "Danke dass Du deine Email bestätigt hast.", + "errorTitle": "Achtung!", "register": "Du bist jetzt registriert, bitte überprüfe deine Emails und klicke auf den Aktivierungslink.", "reset": "Dein Passwort wurde geändert.", "title": "Danke!" diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 236d607cf..ac00b0d20 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -183,10 +183,11 @@ "uppercase": "One uppercase letter required." }, "thx": { - "activateEmail": "Your email has not been activated yet, send again the activation email?", + "activateEmail": "Your email has not been activated yet, please check your emails and click the activation link!", "checkEmail": "Your email has been successfully verified.", "email": "We have sent you an email.", "emailActivated": "Thank you your email has been activated.", + "errorTitle": "Attention!", "register": "You are registered now, please check your emails and click the activation link.", "reset": "Your password has been changed.", "title": "Thank you!" diff --git a/frontend/src/routes/routes.js b/frontend/src/routes/routes.js index 3d16ef45e..9c51bc688 100755 --- a/frontend/src/routes/routes.js +++ b/frontend/src/routes/routes.js @@ -40,7 +40,7 @@ const routes = [ path: '/thx/:comingFrom', component: () => import('../views/Pages/thx.vue'), beforeEnter: (to, from, next) => { - const validFrom = ['password', 'reset', 'register', 'login', 'activateEmail'] + const validFrom = ['password', 'reset', 'register', 'login'] if (!validFrom.includes(from.path.split('/')[1])) { next({ path: '/login' }) } else { @@ -68,10 +68,6 @@ const routes = [ path: '/checkEmail/:optin', component: () => import('../views/Pages/CheckEmail.vue'), }, - { - path: '/activateEmail', - component: () => import('../views/Pages/ActivateEmail.vue'), - }, { path: '*', component: NotFound }, ] diff --git a/frontend/src/views/Pages/ActivateEmail.vue b/frontend/src/views/Pages/ActivateEmail.vue deleted file mode 100644 index 203065b3c..000000000 --- a/frontend/src/views/Pages/ActivateEmail.vue +++ /dev/null @@ -1,76 +0,0 @@ - - - diff --git a/frontend/src/views/Pages/thx.vue b/frontend/src/views/Pages/thx.vue index 5dc2bda19..d28b129e1 100644 --- a/frontend/src/views/Pages/thx.vue +++ b/frontend/src/views/Pages/thx.vue @@ -4,10 +4,12 @@
-

{{ $t('site.thx.title') }}

+

{{ $t(displaySetup.headline) }}

{{ $t(displaySetup.subtitle) }}


- {{ $t(displaySetup.button) }} + + {{ $t(displaySetup.button) }} +
@@ -17,34 +19,33 @@ diff --git a/admin/src/components/CreationFormular.spec.js b/admin/src/components/CreationFormular.spec.js new file mode 100644 index 000000000..c2098768c --- /dev/null +++ b/admin/src/components/CreationFormular.spec.js @@ -0,0 +1,22 @@ +import { mount } from '@vue/test-utils' +import CreationFormular from './CreationFormular.vue' + +const localVue = global.localVue + +describe('CreationFormular', () => { + let wrapper + + const Wrapper = () => { + return mount(CreationFormular, { localVue }) + } + + describe('mount', () => { + beforeEach(() => { + wrapper = Wrapper() + }) + + it('have a DIV element with the class.componente-creation-formular', () => { + expect(wrapper.find('.componente-creation-formular').exists()).toBeTruthy() + }) + }) +}) diff --git a/admin/src/components/CreationFormular.vue b/admin/src/components/CreationFormular.vue new file mode 100644 index 000000000..dbc40c13c --- /dev/null +++ b/admin/src/components/CreationFormular.vue @@ -0,0 +1,114 @@ + + diff --git a/admin/src/components/NavBar.spec.js b/admin/src/components/NavBar.spec.js new file mode 100644 index 000000000..ab9be26e5 --- /dev/null +++ b/admin/src/components/NavBar.spec.js @@ -0,0 +1,22 @@ +import { mount } from '@vue/test-utils' +import NavBar from './NavBar.vue' + +const localVue = global.localVue + +describe('NavBar', () => { + let wrapper + + const Wrapper = () => { + return mount(NavBar, { localVue }) + } + + describe('mount', () => { + beforeEach(() => { + wrapper = Wrapper() + }) + + it('have a DIV element with the class.componente-nabvar', () => { + expect(wrapper.find('.componente-nabvar').exists()).toBeTruthy() + }) + }) +}) diff --git a/admin/src/components/NavBar.vue b/admin/src/components/NavBar.vue index 42bc2161c..1efdffc04 100644 --- a/admin/src/components/NavBar.vue +++ b/admin/src/components/NavBar.vue @@ -1,19 +1,18 @@ diff --git a/admin/src/components/UserTable.spec.js b/admin/src/components/UserTable.spec.js new file mode 100644 index 000000000..935bd177c --- /dev/null +++ b/admin/src/components/UserTable.spec.js @@ -0,0 +1,22 @@ +import { mount } from '@vue/test-utils' +import UserTable from './UserTable.vue' + +const localVue = global.localVue + +describe('UserTable', () => { + let wrapper + + const Wrapper = () => { + return mount(UserTable, { localVue }) + } + + describe('mount', () => { + beforeEach(() => { + wrapper = Wrapper() + }) + + it('have a DIV element with the class.componente-user-table', () => { + expect(wrapper.find('.componente-user-table').exists()).toBeTruthy() + }) + }) +}) diff --git a/admin/src/components/UserTable.vue b/admin/src/components/UserTable.vue index 2e92c9a7f..a376e0f67 100644 --- a/admin/src/components/UserTable.vue +++ b/admin/src/components/UserTable.vue @@ -1,24 +1,8 @@ diff --git a/admin/src/locales/en.json b/admin/src/locales/en.json index ac7159ece..0967ef424 100644 --- a/admin/src/locales/en.json +++ b/admin/src/locales/en.json @@ -1,16 +1 @@ -{ - "monthNames": { - "January": "", - "February": "", - "March": "", - "April": "", - "May": "", - "June": "", - "July": "", - "August": "", - "September": "", - "October": "", - "November": "", - "December": "" - } -} +{} diff --git a/admin/src/main.js b/admin/src/main.js index 197b82888..b3925c9fe 100644 --- a/admin/src/main.js +++ b/admin/src/main.js @@ -13,9 +13,9 @@ import VueApollo from 'vue-apollo' import CONFIG from './config' -import { BootstrapVue } from 'bootstrap-vue' -import 'bootstrap-vue/dist/bootstrap-vue.css' +import { BootstrapVue, IconsPlugin } from 'bootstrap-vue' import 'bootstrap/dist/css/bootstrap.css' +import 'bootstrap-vue/dist/bootstrap-vue.css' import moment from 'vue-moment' @@ -52,6 +52,8 @@ const apolloProvider = new VueApollo({ }) Vue.use(BootstrapVue) +Vue.use(IconsPlugin) + Vue.use(moment) addNavigationGuards(router, store) diff --git a/admin/src/main.test.js b/admin/src/main.test.js index 27c8898ab..b2d7fadc8 100644 --- a/admin/src/main.test.js +++ b/admin/src/main.test.js @@ -5,10 +5,13 @@ import CONFIG from './config' import Vue from 'vue' import Vuex from 'vuex' import VueI18n from 'vue-i18n' +import { BootstrapVue, IconsPlugin } from 'bootstrap-vue' +import moment from 'vue-moment' jest.mock('vue') jest.mock('vuex') jest.mock('vue-i18n') +jest.mock('moment') const storeMock = jest.fn() Vuex.Store = storeMock @@ -25,6 +28,16 @@ jest.mock('apollo-boost', () => { } }) +jest.mock('bootstrap-vue', () => { + return { + __esModule: true, + BootstrapVue: jest.fn(), + IconsPlugin: jest.fn(() => { + return { concat: jest.fn() } + }), + } +}) + describe('main', () => { it('calls the HttpLink', () => { expect(HttpLink).toBeCalledWith({ uri: CONFIG.GRAPHQL_URI }) @@ -50,6 +63,18 @@ describe('main', () => { expect(VueI18n).toBeCalled() }) + it('calls BootstrapVue', () => { + expect(BootstrapVue).toBeCalled() + }) + + it('calls IconsPlugin', () => { + expect(IconsPlugin).toBeCalled() + }) + + it('calls Moment', () => { + expect(moment).toBeCalled() + }) + it.skip('creates a store', () => { expect(storeMock).toBeCalled() }) diff --git a/admin/src/router/routes.js b/admin/src/router/routes.js index 71500eece..a13463e08 100644 --- a/admin/src/router/routes.js +++ b/admin/src/router/routes.js @@ -27,6 +27,13 @@ const routes = [ requiresAuth: true, }, }, + { + path: '/creation-confirm', + component: () => import('@/views/CreationConfirm.vue'), + meta: { + requiresAuth: true, + }, + }, { path: '*', component: () => import('@/components/NotFoundPage.vue'), diff --git a/admin/src/views/Creation.vue b/admin/src/views/Creation.vue index 794218163..02a060f74 100644 --- a/admin/src/views/Creation.vue +++ b/admin/src/views/Creation.vue @@ -1,9 +1,178 @@ + diff --git a/admin/src/views/CreationConfirm.vue b/admin/src/views/CreationConfirm.vue new file mode 100644 index 000000000..2d416e08f --- /dev/null +++ b/admin/src/views/CreationConfirm.vue @@ -0,0 +1,108 @@ + + diff --git a/admin/src/views/Overview.vue b/admin/src/views/Overview.vue index 55eca4aea..d529701e2 100644 --- a/admin/src/views/Overview.vue +++ b/admin/src/views/Overview.vue @@ -1,26 +1,71 @@ diff --git a/admin/src/views/UserSearch.vue b/admin/src/views/UserSearch.vue index be6ce38c9..c7ed5ffef 100644 --- a/admin/src/views/UserSearch.vue +++ b/admin/src/views/UserSearch.vue @@ -1,9 +1,73 @@ + From f362032b388373d372bf71102632fc66e5c1d0b3 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 17 Nov 2021 16:56:13 +0100 Subject: [PATCH 031/178] add body parser --- backend/package.json | 1 + backend/yarn.lock | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/package.json b/backend/package.json index bc098958f..375046363 100644 --- a/backend/package.json +++ b/backend/package.json @@ -20,6 +20,7 @@ "apollo-server-express": "^2.25.2", "apollo-server-testing": "^2.25.2", "axios": "^0.21.1", + "body-parser": "^1.19.0", "class-validator": "^0.13.1", "cors": "^2.8.5", "dotenv": "^10.0.0", diff --git a/backend/yarn.lock b/backend/yarn.lock index b411bcf60..5b74ba7c3 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1552,7 +1552,7 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -body-parser@1.19.0, body-parser@^1.18.3: +body-parser@1.19.0, body-parser@^1.18.3, body-parser@^1.19.0: version "1.19.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== From 33670f1575565f37dcf2f980257dfd1a900ee670 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 17 Nov 2021 16:56:32 +0100 Subject: [PATCH 032/178] WEBHOOK_ELOPAGE_SECRET --- backend/src/config/index.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index f21082d1d..22e37eeb9 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -56,9 +56,21 @@ const email = { process.env.EMAIL_LINK_VERIFICATION || 'http://localhost/vue/checkEmail/$1', } +const webhook = { + WEBHOOK_ELOPAGE_SECRET: process.env.WEBHOOK_ELOPAGE_SECRET || 'secret', +} + // This is needed by graphql-directive-auth process.env.APP_SECRET = server.JWT_SECRET -const CONFIG = { ...server, ...database, ...klicktipp, ...community, ...email, ...loginServer } +const CONFIG = { + ...server, + ...database, + ...klicktipp, + ...community, + ...email, + ...loginServer, + ...webhook, +} export default CONFIG From e68f22ade02861cb5fdb83921de910374e6cec8a Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 17 Nov 2021 16:57:04 +0100 Subject: [PATCH 033/178] implement the webhook --- backend/src/server/createServer.ts | 10 + backend/src/webhook/elopage.ts | 365 +++++++++++++++++++++++++++++ 2 files changed, 375 insertions(+) create mode 100644 backend/src/webhook/elopage.ts diff --git a/backend/src/server/createServer.ts b/backend/src/server/createServer.ts index 4350483ff..28e0e1ce4 100644 --- a/backend/src/server/createServer.ts +++ b/backend/src/server/createServer.ts @@ -6,6 +6,7 @@ import 'module-alias/register' import { ApolloServer } from 'apollo-server-express' import express from 'express' +import bodyParser from 'body-parser' // database import connection from '../typeorm/connection' @@ -22,6 +23,9 @@ import CONFIG from '../config' // graphql import schema from '../graphql/schema' +// webhooks +import { elopageWebhook } from '../webhook/elopage' + // TODO implement // import queryComplexity, { simpleEstimator, fieldConfigEstimator } from "graphql-query-complexity"; @@ -50,6 +54,12 @@ const createServer = async (context: any = serverContext): Promise => { // cors app.use(cors) + // bodyparser + app.use(bodyParser.json()) + + // Elopage Webhook + app.post('/hook/elopage/' + CONFIG.WEBHOOK_ELOPAGE_SECRET, elopageWebhook) + // Apollo Server const apollo = new ApolloServer({ schema: await schema(), diff --git a/backend/src/webhook/elopage.ts b/backend/src/webhook/elopage.ts new file mode 100644 index 000000000..0db26bf9c --- /dev/null +++ b/backend/src/webhook/elopage.ts @@ -0,0 +1,365 @@ +/* + Elopage Webhook + + Those are all available infos: + HandleElopageRequestTask: order_id 849951 + + Es gibt 5 elopage request mit dieser order_id + Alle von der gleichen Person, aber unterschiedliche Events: + 2019-12-03: chargeback.successful + 29.10.2019: order.subscription.paused + 2019-12-06: payment.successful + 29.10.2019: order.subscription.paying + 2091-12-07: payment.pending + + + order_id=849951&order_token=y22MJxHr9XzzPiaaH9GU&payment_session_id=849951&payment_session_token=y22MJxHr9XzzPiaaH9GU&action=payment_processed&initiator&payer[email]=theodora.mis%40gmx.ch&payer[first_name]=Theodora&payer[last_name]=Mis&payer[country]=Schweiz&payer[country_code]=CH&payer[city]=St.+Gallen&payer[street]=Vonwilstrasse+23&payer[street_number]&payer[zip]=9000&payer[company]&payer[vat_no]&payer[phone]&gift_receiver&publisher[id]=691&publisher[email]=joytopia%40gmail.com&publisher[first_name]=Bernd&publisher[last_name]=H%C3%BCckst%C3%A4dt&publisher[street]=Pfarrweg+2&publisher[zip]=74653&publisher[city]=K%C3%BCnzelsau&publisher[country]=Deutschland&publisher[phone]=%2B4979405460810&team_members&product_id=43944&product[id]=43944&product[slug]=gold-de&product[name]=Gold-Mitgliedschaft&product[type]=membership&product[price]=40.0&product[affiliate_program_id]=111&upsell&membership[id]=43944&membership[name]=Gold-Mitgliedschaft&membership[membership_product_1]=Werkzeuge+%26+Ressourcen+%28Gold%29&membership[membership_product_1_id]=44982&membership[membership_product_2]=Zertifizierung+zum%2Fr+Gradido-Botschafter%2Fin&membership[membership_product_2_id]=43970&membership[membership_product_3]=Seminar+3+Nat%C3%BCrliche+%C3%96konomie+des+Lebens&membership[membership_product_3_id]=43969&membership[membership_product_4]=Potential-Entfaltungs-Techniken&membership[membership_product_4_id]=43954&membership[membership_product_5]=Seminar+2+Nat%C3%BCrliche+%C3%96konomie+des+Lebens&membership[membership_product_5_id]=43896&membership[membership_product_6]=Kongresspaket%3A+Gesundes+Geld+f%C3%BCr+eine+gesunde+Welt&membership[membership_product_6_id]=14590&membership[membership_product_7]=Deine+Gold-Mitgliedschaft+bei+Gradido&membership[membership_product_7_id]=43951&membership[membership_product_8]=Gradido+E-Book%2C+H%C3%B6rspiel+%22Joytopia%22++%E2%80%93+und+100+Vorteile&membership[membership_product_8_id]=7312&membership[membership_product_9]=Danke%2C+dass+Du+hilfst+Gradido+in+die+Welt+zu+bringen%21&membership[membership_product_9_id]=43744&membership[membership_product_10]=Basis-Informationen+zu+Gradido&membership[membership_product_10_id]=42600&membership[membership_product_11]=Seminar+1+Nat%C3%BCrliche+%C3%96konomie+des+Lebens&membership[membership_product_11_id]=43882&membership[membership_product_12]=Musical+%22Gradido+%E2%80%93+gemeinsam+retten+wir+die+Welt%22&membership[membership_product_12_id]=43886&membership[membership_product_13]=Premium+Community+%26+Markt&membership[membership_product_13_id]=43885&membership[membership_product_14]=Gradido+Buch+ungek%C3%BCrzte+Version+%26+%C3%9Cbersetzungen&membership[membership_product_14_id]=43887&membership[membership_product_15]=Online-Konferenzen&membership[membership_product_15_id]=43919&membership[membership_product_16]=Gradido+H%C3%B6rbuch&membership[membership_product_16_id]=43920&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&pricing_plan[name]=Monatlich&add_id_1&add_id_2&campaign_id¤cy=EUR&coupon_code&recurring=yes&recurring_form=subscription&payment_state=payment_paused&payment_method=sepa&opt_ins&payments_schedule[][rate]=1&payments_schedule[][state]=debt&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.10.2019&payments_schedule[][rate]=2&payments_schedule[][state]=pending&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.11.2019&payments_schedule[][rate]=3&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.12.2019&payments_schedule[][rate]=4&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.01.2020&payments_schedule[][rate]=5&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.02.2020&payments_schedule[][rate]=6&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.03.2020&payments_schedule[][rate]=7&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.04.2020&payments_schedule[][rate]=8&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.05.2020&payments_schedule[][rate]=9&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.06.2020&payments_schedule[][rate]=10&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.07.2020&payments_schedule[][rate]=11&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.08.2020&payments_schedule[][rate]=12&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.09.2020&payments_schedule[][rate]=13&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.10.2020&payments_schedule[][rate]=14&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=0.0&payments_schedule[][date]=29.11.2020&payments_count=0&payments_count_expected&with_test_period=false&with_custom_start=false&created=29.10.2019+13%3A17&id=58268076&invoice_number&revenue=-40.0&amount=-23.72&fee=-16.28&vat_rate=0.0&vat_amount=0.0&state=successful&refunded_transfer_id=52876337&invoice_link&credit_memo_link=http%3A%2F%2Felopage.com%2Fcommon%2Fcredit_memos%2F12410%3Ftoken%3D6dyBsddt6gsJpX8Fq-M2&success_link=http%3A%2F%2Felopage.com%2Fs%2Fgradido%2Fpayment%2Fy22MJxHr9XzzPiaaH9GU&error_msg&created_date=2019-12-03T22%3A15Z&success_date=2019-12-03T22%3A15Z&success_date_short=2019-12-03&created_date_utc=03.12.2019+22%3A15&success_date_utc=03.12.2019+22%3A15&team_member_commissions&event=chargeback.successful + order_id=849951&order_token=y22MJxHr9XzzPiaaH9GU&payment_session_id=849951&payment_session_token=y22MJxHr9XzzPiaaH9GU&action=subscription_state_changed&initiator&payer[email]=theodora.mis%40gmx.ch&payer[first_name]=Theodora&payer[last_name]=Mis&payer[country]=Schweiz&payer[country_code]=CH&payer[city]=St.+Gallen&payer[street]=Vonwilstrasse+23&payer[street_number]&payer[zip]=9000&payer[company]&payer[vat_no]&payer[phone]&gift_receiver&publisher[id]=691&publisher[email]=joytopia%40gmail.com&publisher[first_name]=Bernd&publisher[last_name]=H%C3%BCckst%C3%A4dt&publisher[street]=Pfarrweg+2&publisher[zip]=74653&publisher[city]=K%C3%BCnzelsau&publisher[country]=Deutschland&publisher[phone]=%2B4979405460810&team_members&product_id=43944&product[id]=43944&product[slug]=gold-de&product[name]=Gold-Mitgliedschaft&product[type]=membership&product[price]=40.0&product[affiliate_program_id]=111&upsell&membership[id]=43944&membership[name]=Gold-Mitgliedschaft&membership[membership_product_1]=Werkzeuge+%26+Ressourcen+%28Gold%29&membership[membership_product_1_id]=44982&membership[membership_product_2]=Zertifizierung+zum%2Fr+Gradido-Botschafter%2Fin&membership[membership_product_2_id]=43970&membership[membership_product_3]=Seminar+3+Nat%C3%BCrliche+%C3%96konomie+des+Lebens&membership[membership_product_3_id]=43969&membership[membership_product_4]=Potential-Entfaltungs-Techniken&membership[membership_product_4_id]=43954&membership[membership_product_5]=Seminar+2+Nat%C3%BCrliche+%C3%96konomie+des+Lebens&membership[membership_product_5_id]=43896&membership[membership_product_6]=Kongresspaket%3A+Gesundes+Geld+f%C3%BCr+eine+gesunde+Welt&membership[membership_product_6_id]=14590&membership[membership_product_7]=Deine+Gold-Mitgliedschaft+bei+Gradido&membership[membership_product_7_id]=43951&membership[membership_product_8]=Gradido+E-Book%2C+H%C3%B6rspiel+%22Joytopia%22++%E2%80%93+und+100+Vorteile&membership[membership_product_8_id]=7312&membership[membership_product_9]=Danke%2C+dass+Du+hilfst+Gradido+in+die+Welt+zu+bringen%21&membership[membership_product_9_id]=43744&membership[membership_product_10]=Basis-Informationen+zu+Gradido&membership[membership_product_10_id]=42600&membership[membership_product_11]=Seminar+1+Nat%C3%BCrliche+%C3%96konomie+des+Lebens&membership[membership_product_11_id]=43882&membership[membership_product_12]=Musical+%22Gradido+%E2%80%93+gemeinsam+retten+wir+die+Welt%22&membership[membership_product_12_id]=43886&membership[membership_product_13]=Premium+Community+%26+Markt&membership[membership_product_13_id]=43885&membership[membership_product_14]=Gradido+Buch+ungek%C3%BCrzte+Version+%26+%C3%9Cbersetzungen&membership[membership_product_14_id]=43887&membership[membership_product_15]=Online-Konferenzen&membership[membership_product_15_id]=43919&membership[membership_product_16]=Gradido+H%C3%B6rbuch&membership[membership_product_16_id]=43920&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&pricing_plan[name]=Monatlich&add_id_1&add_id_2&campaign_id¤cy=EUR&coupon_code&recurring=yes&recurring_form=subscription&payment_state=payment_paused&payment_method=sepa&opt_ins&payments_schedule[][rate]=1&payments_schedule[][state]=debt&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.10.2019&payments_schedule[][rate]=2&payments_schedule[][state]=pending&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.11.2019&payments_schedule[][rate]=3&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.12.2019&payments_schedule[][rate]=4&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.01.2020&payments_schedule[][rate]=5&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.02.2020&payments_schedule[][rate]=6&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.03.2020&payments_schedule[][rate]=7&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.04.2020&payments_schedule[][rate]=8&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.05.2020&payments_schedule[][rate]=9&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.06.2020&payments_schedule[][rate]=10&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.07.2020&payments_schedule[][rate]=11&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.08.2020&payments_schedule[][rate]=12&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.09.2020&payments_schedule[][rate]=13&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.10.2020&payments_schedule[][rate]=14&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=0.0&payments_schedule[][date]=29.11.2020&payments_count=0&payments_count_expected&with_test_period=false&with_custom_start=false&created=29.10.2019+13%3A17&event=order.subscription.paused + order_id=849951&order_token=y22MJxHr9XzzPiaaH9GU&payment_session_id=849951&payment_session_token=y22MJxHr9XzzPiaaH9GU&action=payment_processed&initiator&payer[email]=theodora.mis%40gmx.ch&payer[first_name]=Theodora&payer[last_name]=Mis&payer[country]=Schweiz&payer[country_code]=CH&payer[city]=St.+Gallen&payer[street]=Vonwilstrasse+23&payer[street_number]&payer[zip]=9000&payer[company]&payer[vat_no]&payer[phone]&gift_receiver&publisher[id]=691&publisher[email]=joytopia%40gmail.com&publisher[first_name]=Bernd&publisher[last_name]=H%C3%BCckst%C3%A4dt&publisher[street]=Pfarrweg+2&publisher[zip]=74653&publisher[city]=K%C3%BCnzelsau&publisher[country]=Deutschland&publisher[phone]=%2B4979405460810&team_members&product_id=43944&product[id]=43944&product[slug]=gold-de&product[name]=Gold-Mitgliedschaft&product[type]=membership&product[price]=40.0&product[affiliate_program_id]=111&upsell&membership[id]=43944&membership[name]=Gold-Mitgliedschaft&membership[membership_product_1]=Werkzeuge+%26+Ressourcen+%28Gold%29&membership[membership_product_1_id]=44982&membership[membership_product_2]=Zertifizierung+zum%2Fr+Gradido-Botschafter%2Fin&membership[membership_product_2_id]=43970&membership[membership_product_3]=Seminar+3+Nat%C3%BCrliche+%C3%96konomie+des+Lebens&membership[membership_product_3_id]=43969&membership[membership_product_4]=Potential-Entfaltungs-Techniken&membership[membership_product_4_id]=43954&membership[membership_product_5]=Seminar+2+Nat%C3%BCrliche+%C3%96konomie+des+Lebens&membership[membership_product_5_id]=43896&membership[membership_product_6]=Kongresspaket%3A+Gesundes+Geld+f%C3%BCr+eine+gesunde+Welt&membership[membership_product_6_id]=14590&membership[membership_product_7]=Deine+Gold-Mitgliedschaft+bei+Gradido&membership[membership_product_7_id]=43951&membership[membership_product_8]=Gradido+E-Book%2C+H%C3%B6rspiel+%22Joytopia%22++%E2%80%93+und+100+Vorteile&membership[membership_product_8_id]=7312&membership[membership_product_9]=Danke%2C+dass+Du+hilfst+Gradido+in+die+Welt+zu+bringen%21&membership[membership_product_9_id]=43744&membership[membership_product_10]=Basis-Informationen+zu+Gradido&membership[membership_product_10_id]=42600&membership[membership_product_11]=Seminar+1+Nat%C3%BCrliche+%C3%96konomie+des+Lebens&membership[membership_product_11_id]=43882&membership[membership_product_12]=Musical+%22Gradido+%E2%80%93+gemeinsam+retten+wir+die+Welt%22&membership[membership_product_12_id]=43886&membership[membership_product_13]=Premium+Community+%26+Markt&membership[membership_product_13_id]=43885&membership[membership_product_14]=Gradido+Buch+ungek%C3%BCrzte+Version+%26+%C3%9Cbersetzungen&membership[membership_product_14_id]=43887&membership[membership_product_15]=Online-Konferenzen&membership[membership_product_15_id]=43919&membership[membership_product_16]=Gradido+H%C3%B6rbuch&membership[membership_product_16_id]=43920&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&pricing_plan[name]=Monatlich&add_id_1&add_id_2&campaign_id¤cy=EUR&coupon_code&recurring=yes&recurring_form=subscription&payment_state=active_subscription&payment_method=sepa&opt_ins&payments_schedule[][rate]=1&payments_schedule[][state]=debt&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.10.2019&payments_schedule[][rate]=2&payments_schedule[][state]=paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.11.2019&payments_schedule[][rate]=3&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.12.2019&payments_schedule[][rate]=4&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.01.2020&payments_schedule[][rate]=5&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.02.2020&payments_schedule[][rate]=6&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.03.2020&payments_schedule[][rate]=7&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.04.2020&payments_schedule[][rate]=8&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.05.2020&payments_schedule[][rate]=9&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.06.2020&payments_schedule[][rate]=10&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.07.2020&payments_schedule[][rate]=11&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.08.2020&payments_schedule[][rate]=12&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.09.2020&payments_schedule[][rate]=13&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.10.2020&payments_schedule[][rate]=14&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=0.0&payments_schedule[][date]=29.11.2020&payments_schedule[][rate]=15&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=0.0&payments_schedule[][date]=29.12.2020&payments_count=1&payments_count_expected&with_test_period=false&with_custom_start=false&created=29.10.2019+13%3A17&id=57354055&invoice_number=111-1839-000000677&revenue=40.0&amount=23.72&fee=16.28&vat_rate=0.0&vat_amount=0.0&state=successful&refunded_transfer_id&invoice_link=http%3A%2F%2Felopage.com%2Fcommon%2Finvoices%2F450856%2Fdownload.pdf%3Ftoken%3DGR7bG7zcbgCzNJEPLDss&credit_memo_link&success_link=http%3A%2F%2Felopage.com%2Fs%2Fgradido%2Fpayment%2Fy22MJxHr9XzzPiaaH9GU&error_msg&created_date=2019-11-29T07%3A19Z&success_date=2019-12-06T13%3A12Z&success_date_short=2019-12-06&created_date_utc=29.11.2019+07%3A19&success_date_utc=06.12.2019+13%3A12&team_member_commissions&event=payment.successful + order_id=849951&order_token=y22MJxHr9XzzPiaaH9GU&payment_session_id=849951&payment_session_token=y22MJxHr9XzzPiaaH9GU&action=subscription_state_changed&initiator&payer[email]=theodora.mis%40gmx.ch&payer[first_name]=Theodora&payer[last_name]=Mis&payer[country]=Schweiz&payer[country_code]=CH&payer[city]=St.+Gallen&payer[street]=Vonwilstrasse+23&payer[street_number]&payer[zip]=9000&payer[company]&payer[vat_no]&payer[phone]&gift_receiver&publisher[id]=691&publisher[email]=joytopia%40gmail.com&publisher[first_name]=Bernd&publisher[last_name]=H%C3%BCckst%C3%A4dt&publisher[street]=Pfarrweg+2&publisher[zip]=74653&publisher[city]=K%C3%BCnzelsau&publisher[country]=Deutschland&publisher[phone]=%2B4979405460810&team_members&product_id=43944&product[id]=43944&product[slug]=gold-de&product[name]=Gold-Mitgliedschaft&product[type]=membership&product[price]=40.0&product[affiliate_program_id]=111&upsell&membership[id]=43944&membership[name]=Gold-Mitgliedschaft&membership[membership_product_1]=Werkzeuge+%26+Ressourcen+%28Gold%29&membership[membership_product_1_id]=44982&membership[membership_product_2]=Zertifizierung+zum%2Fr+Gradido-Botschafter%2Fin&membership[membership_product_2_id]=43970&membership[membership_product_3]=Seminar+3+Nat%C3%BCrliche+%C3%96konomie+des+Lebens&membership[membership_product_3_id]=43969&membership[membership_product_4]=Potential-Entfaltungs-Techniken&membership[membership_product_4_id]=43954&membership[membership_product_5]=Seminar+2+Nat%C3%BCrliche+%C3%96konomie+des+Lebens&membership[membership_product_5_id]=43896&membership[membership_product_6]=Kongresspaket%3A+Gesundes+Geld+f%C3%BCr+eine+gesunde+Welt&membership[membership_product_6_id]=14590&membership[membership_product_7]=Deine+Gold-Mitgliedschaft+bei+Gradido&membership[membership_product_7_id]=43951&membership[membership_product_8]=Gradido+E-Book%2C+H%C3%B6rspiel+%22Joytopia%22++%E2%80%93+und+100+Vorteile&membership[membership_product_8_id]=7312&membership[membership_product_9]=Danke%2C+dass+Du+hilfst+Gradido+in+die+Welt+zu+bringen%21&membership[membership_product_9_id]=43744&membership[membership_product_10]=Basis-Informationen+zu+Gradido&membership[membership_product_10_id]=42600&membership[membership_product_11]=Seminar+1+Nat%C3%BCrliche+%C3%96konomie+des+Lebens&membership[membership_product_11_id]=43882&membership[membership_product_12]=Musical+%22Gradido+%E2%80%93+gemeinsam+retten+wir+die+Welt%22&membership[membership_product_12_id]=43886&membership[membership_product_13]=Premium+Community+%26+Markt&membership[membership_product_13_id]=43885&membership[membership_product_14]=Gradido+Buch+ungek%C3%BCrzte+Version+%26+%C3%9Cbersetzungen&membership[membership_product_14_id]=43887&membership[membership_product_15]=Online-Konferenzen&membership[membership_product_15_id]=43919&membership[membership_product_16]=Gradido+H%C3%B6rbuch&membership[membership_product_16_id]=43920&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&pricing_plan[name]=Monatlich&add_id_1&add_id_2&campaign_id¤cy=EUR&coupon_code&recurring=yes&recurring_form=subscription&payment_state=active_subscription&payment_method=sepa&opt_ins&payments_schedule[][rate]=1&payments_schedule[][state]=debt&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.10.2019&payments_schedule[][rate]=2&payments_schedule[][state]=paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.11.2019&payments_schedule[][rate]=3&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.12.2019&payments_schedule[][rate]=4&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.01.2020&payments_schedule[][rate]=5&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.02.2020&payments_schedule[][rate]=6&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.03.2020&payments_schedule[][rate]=7&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.04.2020&payments_schedule[][rate]=8&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.05.2020&payments_schedule[][rate]=9&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.06.2020&payments_schedule[][rate]=10&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.07.2020&payments_schedule[][rate]=11&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.08.2020&payments_schedule[][rate]=12&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.09.2020&payments_schedule[][rate]=13&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.10.2020&payments_schedule[][rate]=14&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=0.0&payments_schedule[][date]=29.11.2020&payments_schedule[][rate]=15&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=0.0&payments_schedule[][date]=29.12.2020&payments_count=1&payments_count_expected&with_test_period=false&with_custom_start=false&created=29.10.2019+13%3A17&event=order.subscription.paying + order_id=849951&order_token=y22MJxHr9XzzPiaaH9GU&payment_session_id=849951&payment_session_token=y22MJxHr9XzzPiaaH9GU&action=payment_processed&initiator&payer[email]=theodora.mis%40gmx.ch&payer[first_name]=Theodora&payer[last_name]=Mis&payer[country]=Schweiz&payer[country_code]=CH&payer[city]=St.+Gallen&payer[street]=Vonwilstrasse+23&payer[street_number]&payer[zip]=9000&payer[company]&payer[vat_no]&payer[phone]&gift_receiver&publisher[id]=691&publisher[email]=joytopia%40gmail.com&publisher[first_name]=Bernd&publisher[last_name]=H%C3%BCckst%C3%A4dt&publisher[street]=Pfarrweg+2&publisher[zip]=74653&publisher[city]=K%C3%BCnzelsau&publisher[country]=Deutschland&publisher[phone]=%2B4979405460810&team_members&product_id=43944&product[id]=43944&product[slug]=gold-de&product[name]=Gold-Mitgliedschaft&product[type]=membership&product[price]=40.0&product[affiliate_program_id]=111&upsell&membership[id]=43944&membership[name]=Gold-Mitgliedschaft&membership[membership_product_1]=Werkzeuge+%26+Ressourcen+%28Gold%29&membership[membership_product_1_id]=44982&membership[membership_product_2]=Zertifizierung+zum%2Fr+Gradido-Botschafter%2Fin&membership[membership_product_2_id]=43970&membership[membership_product_3]=Seminar+3+Nat%C3%BCrliche+%C3%96konomie+des+Lebens&membership[membership_product_3_id]=43969&membership[membership_product_4]=Potential-Entfaltungs-Techniken&membership[membership_product_4_id]=43954&membership[membership_product_5]=Seminar+2+Nat%C3%BCrliche+%C3%96konomie+des+Lebens&membership[membership_product_5_id]=43896&membership[membership_product_6]=Kongresspaket%3A+Gesundes+Geld+f%C3%BCr+eine+gesunde+Welt&membership[membership_product_6_id]=14590&membership[membership_product_7]=Deine+Gold-Mitgliedschaft+bei+Gradido&membership[membership_product_7_id]=43951&membership[membership_product_8]=Gradido+E-Book%2C+H%C3%B6rspiel+%22Joytopia%22++%E2%80%93+und+100+Vorteile&membership[membership_product_8_id]=7312&membership[membership_product_9]=Danke%2C+dass+Du+hilfst+Gradido+in+die+Welt+zu+bringen%21&membership[membership_product_9_id]=43744&membership[membership_product_10]=Basis-Informationen+zu+Gradido&membership[membership_product_10_id]=42600&membership[membership_product_11]=Seminar+1+Nat%C3%BCrliche+%C3%96konomie+des+Lebens&membership[membership_product_11_id]=43882&membership[membership_product_12]=Musical+%22Gradido+%E2%80%93+gemeinsam+retten+wir+die+Welt%22&membership[membership_product_12_id]=43886&membership[membership_product_13]=Premium+Community+%26+Markt&membership[membership_product_13_id]=43885&membership[membership_product_14]=Gradido+Buch+ungek%C3%BCrzte+Version+%26+%C3%9Cbersetzungen&membership[membership_product_14_id]=43887&membership[membership_product_15]=Online-Konferenzen&membership[membership_product_15_id]=43919&membership[membership_product_16]=Gradido+H%C3%B6rbuch&membership[membership_product_16_id]=43920&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&events[]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&tickets[][codes]&tickets[][ticket_attendees]&pricing_plan[name]=Monatlich&add_id_1&add_id_2&campaign_id¤cy=EUR&coupon_code&recurring=yes&recurring_form=subscription&payment_state=active_subscription&payment_method=sepa&opt_ins&payments_schedule[][rate]=1&payments_schedule[][state]=pending&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.10.2019&payments_schedule[][rate]=2&payments_schedule[][state]=paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.11.2019&payments_schedule[][rate]=3&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.12.2019&payments_schedule[][rate]=4&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.01.2020&payments_schedule[][rate]=5&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.02.2020&payments_schedule[][rate]=6&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.03.2020&payments_schedule[][rate]=7&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.04.2020&payments_schedule[][rate]=8&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.05.2020&payments_schedule[][rate]=9&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.06.2020&payments_schedule[][rate]=10&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.07.2020&payments_schedule[][rate]=11&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.08.2020&payments_schedule[][rate]=12&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.09.2020&payments_schedule[][rate]=13&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=40.0&payments_schedule[][date]=29.10.2020&payments_schedule[][rate]=14&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=0.0&payments_schedule[][date]=29.11.2020&payments_schedule[][rate]=15&payments_schedule[][state]=to_be_paid&payments_schedule[][amount]=0.0&payments_schedule[][date]=29.12.2020&payments_count=1&payments_count_expected&with_test_period=false&with_custom_start=false&created=29.10.2019+13%3A17&id=58838098&invoice_number=111-1839-000000689&revenue=40.0&amount=23.72&fee=16.28&vat_rate=0.0&vat_amount=0.0&state=pending&refunded_transfer_id&invoice_link=http%3A%2F%2Felopage.com%2Fcommon%2Finvoices%2F470009%2Fdownload.pdf%3Ftoken%3DZ_gogUf8tpKxcHhB-7Cz&credit_memo_link&success_link=http%3A%2F%2Felopage.com%2Fs%2Fgradido%2Fpayment%2Fy22MJxHr9XzzPiaaH9GU&error_msg&created_date=2019-12-07T07%3A19Z&success_date&success_date_short&created_date_utc=07.12.2019+07%3A19&success_date_utc&team_member_commissions&event=payment.pending + + Additional we have the Elopage API docu: + https://apidoc.elopage.com/#webhooks + + I assume that the webhook arrives via POST and transmits a string as shown above +*/ + +import { LoginElopageBuys } from '@entity/LoginElopageBuys' +import { LoginUser } from '@entity/LoginUser' +import { randomBytes } from 'crypto' +import { UserResolver } from '../graphql/resolver/UserResolver' + +export const elopageWebhook = async (req: any, res: any): Promise => { + res.status(200).end() // Responding is important + + const loginElopgaeBuy = new LoginElopageBuys() + let firstName = '' + let lastName = '' + const entries = req.body.split('&') + entries.map((entry: string) => { + const keyVal = entry.split('=') + if (keyVal.length !== 2) { + throw new Error(`Error parsing entry '${entry}'`) + } + const key = keyVal[0] + const val = decodeURIComponent(keyVal[1]).replace('+', ' ').trim() + switch (key) { + case 'product[affiliate_program_id]': + loginElopgaeBuy.affiliateProgramId = parseInt(val) + break + case 'publisher[id]': + loginElopgaeBuy.publisherId = parseInt(val) + break + case 'order_id': + loginElopgaeBuy.orderId = parseInt(val) + break + case 'product_id': + loginElopgaeBuy.productId = parseInt(val) + break + case 'product[price]': + // TODO: static_cast(round(stof(temp) * 100.0f)); + loginElopgaeBuy.productPrice = parseFloat(val) + break + case 'payer[email]': + loginElopgaeBuy.payerEmail = val + break + case 'publisher[email]': + loginElopgaeBuy.publisherEmail = val + break + case 'payment_state': + loginElopgaeBuy.payed = val === 'paid' + break + case 'success_date': + loginElopgaeBuy.successDate = new Date(val) + break + case 'event': + loginElopgaeBuy.event = val + break + case 'membership[id]': + // TODO this was never set on login_server - its unclear if this is the correct value + loginElopgaeBuy.elopageUserId = parseInt(val) + break + case 'payer[first_name]': + firstName = val + break + case 'payer[last_name]': + lastName = val + break + default: + // eslint-disable-next-line no-console + console.log(`Unknown Elopage Value '${entry}'`) + } + return null // we write things into the loginElopgaeBuy object, no return value needed + }) + + // Do not process certain events + if (['lesson.viewed', 'lesson.completed', 'lesson.commented'].includes(loginElopgaeBuy.event)) { + // eslint-disable-next-line no-console + console.log('User viewed, completed or commented - not saving hook') + return + } + + // Save the hook data + await loginElopgaeBuy.save() + + // create user for certain products + /* + Registrierung - Schritt 1 von 3, 36001 + Gradido-Basis, 43741 + Premium-Mitgliedschaft, 43870 + Gold-Mitgliedschaft, 43944 + Business-Mitgliedschaft, 43960 + Förderbeitrag: 49106 + */ + if ([36001, 43741, 43870, 43944, 43960, 49106].includes(loginElopgaeBuy.productId)) { + const email = loginElopgaeBuy.payerEmail + + const VALIDATE_EMAIL = /^[a-zA-Z0-9.!#$%&?*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/ + const VALIDATE_NAME = /^<>&;]{2,}$/ + + // Validate inputs + if ( + email === '' || + !email.match(VALIDATE_EMAIL) || + firstName === '' || + firstName.match(VALIDATE_NAME) || + lastName === '' || + lastName.match(VALIDATE_NAME) + ) { + // eslint-disable-next-line no-console + console.log(`Could not create User ${firstName} ${lastName} with email: ${email}`) + return + } + + // Do we already have such a user? + if ((await LoginUser.count({ email })) !== 0) { + // eslint-disable-next-line no-console + console.log(`Did not create User - already exists with email: ${email}`) + return + } + + // generate a random password - 8 random bytes, the email, special char, capital & small letter, number and another set of 8 random bytes + // TODO: The user will be forced to reset his password - how was this done before? + const password = + randomBytes(8).toString('hex') + email + '!aA1' + randomBytes(8).toString('hex') + + const userResolver = new UserResolver() + userResolver.createUser({ + email, + firstName, + lastName, + password, + language: 'default', + publisherId: loginElopgaeBuy.publisherId, + }) + } +} + +/* +Known unused fields: + + order_token=y22MJxHr9XzzPiaaH9GU + payment_session_id=849951 + payment_session_token=y22MJxHr9XzzPiaaH9GU + action=payment_processed + initiator + payer[first_name]=Theodora + payer[last_name]=Mis + payer[country]=Schweiz + payer[country_code]=CH + payer[city]=St.+Gallen + payer[street]=Vonwilstrasse+23 + payer[street_number] + payer[zip]=9000 + payer[company] + payer[vat_no] + payer[phone] + gift_receiver + publisher[first_name]=Bernd + publisher[last_name]=H%C3%BCckst%C3%A4dt + publisher[street]=Pfarrweg+2 + publisher[zip]=74653 + publisher[city]=K%C3%BCnzelsau + publisher[country]=Deutschland + publisher[phone]=%2B4979405460810 + team_members + product[id]=43944 + product[slug]=gold-de + product[name]=Gold-Mitgliedschaft + product[type]=membership + upsell + membership[name]=Gold-Mitgliedschaft + membership[membership_product_1]=Werkzeuge+%26+Ressourcen+%28Gold%29 + membership[membership_product_1_id]=44982 + membership[membership_product_2]=Zertifizierung+zum%2Fr+Gradido-Botschafter%2Fin + membership[membership_product_2_id]=43970 + membership[membership_product_3]=Seminar+3+Nat%C3%BCrliche+%C3%96konomie+des+Lebens + membership[membership_product_3_id]=43969 + membership[membership_product_4]=Potential-Entfaltungs-Techniken + membership[membership_product_4_id]=43954 + membership[membership_product_5]=Seminar+2+Nat%C3%BCrliche+%C3%96konomie+des+Lebens + membership[membership_product_5_id]=43896 + membership[membership_product_6]=Kongresspaket%3A+Gesundes+Geld+f%C3%BCr+eine+gesunde+Welt + membership[membership_product_6_id]=14590 + membership[membership_product_7]=Deine+Gold-Mitgliedschaft+bei+Gradido + membership[membership_product_7_id]=43951 + membership[membership_product_8]=Gradido+E-Book%2C+H%C3%B6rspiel+%22Joytopia%22++%E2%80%93+und+100+Vorteile + membership[membership_product_8_id]=7312 + membership[membership_product_9]=Danke%2C+dass+Du+hilfst+Gradido+in+die+Welt+zu+bringen%21 + membership[membership_product_9_id]=43744 + membership[membership_product_10]=Basis-Informationen+zu+Gradido + membership[membership_product_10_id]=42600 + membership[membership_product_11]=Seminar+1+Nat%C3%BCrliche+%C3%96konomie+des+Lebens + membership[membership_product_11_id]=43882 + membership[membership_product_12]=Musical+%22Gradido+%E2%80%93+gemeinsam+retten+wir+die+Welt%22 + membership[membership_product_12_id]=43886 + membership[membership_product_13]=Premium+Community+%26+Markt + membership[membership_product_13_id]=43885 + membership[membership_product_14]=Gradido+Buch+ungek%C3%BCrzte+Version+%26+%C3%9Cbersetzungen + membership[membership_product_14_id]=43887 + membership[membership_product_15]=Online-Konferenzen + membership[membership_product_15_id]=43919 + membership[membership_product_16]=Gradido+H%C3%B6rbuch + membership[membership_product_16_id]=43920 + events[] + events[] + events[] + events[] + events[] + events[] + events[] + events[] + events[] + events[] + events[] + events[] + events[] + events[] + events[] + events[] + tickets[][codes] + tickets[][ticket_attendees] + tickets[][codes] + tickets[][ticket_attendees] + tickets[][codes] + tickets[][ticket_attendees] + tickets[][codes] + tickets[][ticket_attendees] + tickets[][codes] + tickets[][ticket_attendees] + tickets[][codes] + tickets[][ticket_attendees] + tickets[][codes] + tickets[][ticket_attendees] + tickets[][codes] + tickets[][ticket_attendees] + tickets[][codes] + tickets[][ticket_attendees] + tickets[][codes] + tickets[][ticket_attendees] + tickets[][codes] + tickets[][ticket_attendees] + tickets[][codes] + tickets[][ticket_attendees] + tickets[][codes] + tickets[][ticket_attendees] + tickets[][codes] + tickets[][ticket_attendees] + tickets[][codes] + tickets[][ticket_attendees] + tickets[][codes] + tickets[][ticket_attendees] + pricing_plan[name]=Monatlich + add_id_1 + add_id_2 + campaign_id + currency=EUR + coupon_code + recurring=yes + recurring_form=subscription + payment_method=sepa + opt_ins + payments_schedule[][rate]=1 + payments_schedule[][state]=debt + payments_schedule[][amount]=40.0 + payments_schedule[][date]=29.10.2019 + payments_schedule[][rate]=2 + payments_schedule[][state]=paid + payments_schedule[][amount]=40.0 + payments_schedule[][date]=29.11.2019 + payments_schedule[][rate]=3 + payments_schedule[][state]=to_be_paid + payments_schedule[][amount]=40.0 + payments_schedule[][date]=29.12.2019 + payments_schedule[][rate]=4 + payments_schedule[][state]=to_be_paid + payments_schedule[][amount]=40.0 + payments_schedule[][date]=29.01.2020 + payments_schedule[][rate]=5 + payments_schedule[][state]=to_be_paid + payments_schedule[][amount]=40.0 + payments_schedule[][date]=29.02.2020 + payments_schedule[][rate]=6 + payments_schedule[][state]=to_be_paid + payments_schedule[][amount]=40.0 + payments_schedule[][date]=29.03.2020 + payments_schedule[][rate]=7 + payments_schedule[][state]=to_be_paid + payments_schedule[][amount]=40.0 + payments_schedule[][date]=29.04.2020 + payments_schedule[][rate]=8 + payments_schedule[][state]=to_be_paid + payments_schedule[][amount]=40.0 + payments_schedule[][date]=29.05.2020 + payments_schedule[][rate]=9 + payments_schedule[][state]=to_be_paid + payments_schedule[][amount]=40.0 + payments_schedule[][date]=29.06.2020 + payments_schedule[][rate]=10 + payments_schedule[][state]=to_be_paid + payments_schedule[][amount]=40.0 + payments_schedule[][date]=29.07.2020 + payments_schedule[][rate]=11 + payments_schedule[][state]=to_be_paid + payments_schedule[][amount]=40.0 + payments_schedule[][date]=29.08.2020 + payments_schedule[][rate]=12 + payments_schedule[][state]=to_be_paid + payments_schedule[][amount]=40.0 + payments_schedule[][date]=29.09.2020 + payments_schedule[][rate]=13 + payments_schedule[][state]=to_be_paid + payments_schedule[][amount]=40.0 + payments_schedule[][date]=29.10.2020 + payments_schedule[][rate]=14 + payments_schedule[][state]=to_be_paid + payments_schedule[][amount]=0.0 + payments_schedule[][date]=29.11.2020 + payments_schedule[][rate]=15 + payments_schedule[][state]=to_be_paid + payments_schedule[][amount]=0.0 + payments_schedule[][date]=29.12.2020 + payments_count=1&payments_count_expected + with_test_period=false + with_custom_start=false + created=29.10.2019+13%3A17 + id=57354055 + invoice_number=111-1839-000000677 + revenue=40.0 + amount=23.72 + fee=16.28 + vat_rate=0.0 + vat_amount=0.0 + state=successful + refunded_transfer_id + invoice_link=http%3A%2F%2Felopage.com%2Fcommon%2Finvoices%2F450856%2Fdownload.pdf%3Ftoken%3DGR7bG7zcbgCzNJEPLDss + credit_memo_link + success_link=http%3A%2F%2Felopage.com%2Fs%2Fgradido%2Fpayment%2Fy22MJxHr9XzzPiaaH9GU + error_msg + created_date=2019-11-29T07%3A19Z + success_date_short=2019-12-06 + created_date_utc=29.11.2019+07%3A19 + success_date_utc=06.12.2019+13%3A12 + team_member_commissions +*/ From 52b6026ed836ce3f07599399d4798315f166c09c Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 17 Nov 2021 16:58:04 +0100 Subject: [PATCH 034/178] missing .env.dist value --- backend/.env.dist | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/.env.dist b/backend/.env.dist index b4a91026a..1b485b8e4 100644 --- a/backend/.env.dist +++ b/backend/.env.dist @@ -30,4 +30,6 @@ COMMUNITY_URL= COMMUNITY_REGISTER_URL= COMMUNITY_DESCRIPTION= LOGIN_APP_SECRET=21ffbbc616fe -LOGIN_SERVER_KEY=a51ef8ac7ef1abf162fb7a65261acd7a \ No newline at end of file +LOGIN_SERVER_KEY=a51ef8ac7ef1abf162fb7a65261acd7a + +WEBHOOK_ELOPAGE_SECRET=secret \ No newline at end of file From c2a3866ae53c034e171406945dd0edfd26ba8f34 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 17 Nov 2021 19:32:11 +0100 Subject: [PATCH 035/178] reduce coverage --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0e89057cf..f4d48c5c4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -394,7 +394,7 @@ jobs: report_name: Coverage Backend type: lcov result_path: ./backend/coverage/lcov.info - min_coverage: 39 + min_coverage: 38 token: ${{ github.token }} ############################################################################## From 5335d128021d54eb4a90efd0bd90d425d5487ae7 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 17 Nov 2021 19:37:35 +0100 Subject: [PATCH 036/178] catch error on createUser and log them --- backend/src/webhook/elopage.ts | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/backend/src/webhook/elopage.ts b/backend/src/webhook/elopage.ts index 0db26bf9c..945a2a24c 100644 --- a/backend/src/webhook/elopage.ts +++ b/backend/src/webhook/elopage.ts @@ -145,14 +145,19 @@ export const elopageWebhook = async (req: any, res: any): Promise => { randomBytes(8).toString('hex') + email + '!aA1' + randomBytes(8).toString('hex') const userResolver = new UserResolver() - userResolver.createUser({ - email, - firstName, - lastName, - password, - language: 'default', - publisherId: loginElopgaeBuy.publisherId, - }) + try { + await userResolver.createUser({ + email, + firstName, + lastName, + password, + language: 'default', + publisherId: loginElopgaeBuy.publisherId, + }) + } catch (error) { + // eslint-disable-next-line no-console + console.log(`Could not create User for ${email}. Following Error occured:`, error) + } } } From d8def24e0c2f87a6973e3298a184a9b8e7c82867 Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 18 Nov 2021 10:28:54 +0100 Subject: [PATCH 037/178] Their was an error in the UserResolver due to a check undefined and false would fall out of the update change it to !== undefined. --- backend/src/graphql/resolver/UserResolver.ts | 2 +- backend/src/typeorm/repository/UserSettingRepository.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 5c4625938..b10876d46 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -537,7 +537,7 @@ export class UserResolver { await queryRunner.startTransaction('READ UNCOMMITTED') try { - if (coinanimation) { + if (coinanimation !== undefined) { queryRunner.manager .getCustomRepository(UserSettingRepository) .setOrUpdate(userEntity.id, Setting.COIN_ANIMATION, coinanimation.toString()) diff --git a/backend/src/typeorm/repository/UserSettingRepository.ts b/backend/src/typeorm/repository/UserSettingRepository.ts index 80c44802b..7ec7ed3dd 100644 --- a/backend/src/typeorm/repository/UserSettingRepository.ts +++ b/backend/src/typeorm/repository/UserSettingRepository.ts @@ -18,7 +18,7 @@ export class UserSettingRepository extends Repository { let entity = await this.findOne({ userId: userId, key: key }) if (!entity) { - entity = new UserSetting() + entity = this.create() entity.userId = userId entity.key = key } From 2aba37e60c5a76d749a66bd62e94f2d2aa630b8a Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 19 Nov 2021 01:00:55 +0100 Subject: [PATCH 038/178] replace map with foreach since we don't need a return - thanks moriz --- backend/src/webhook/elopage.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/src/webhook/elopage.ts b/backend/src/webhook/elopage.ts index 945a2a24c..10dc35b61 100644 --- a/backend/src/webhook/elopage.ts +++ b/backend/src/webhook/elopage.ts @@ -37,7 +37,7 @@ export const elopageWebhook = async (req: any, res: any): Promise => { let firstName = '' let lastName = '' const entries = req.body.split('&') - entries.map((entry: string) => { + entries.foreach((entry: string) => { const keyVal = entry.split('=') if (keyVal.length !== 2) { throw new Error(`Error parsing entry '${entry}'`) @@ -90,7 +90,6 @@ export const elopageWebhook = async (req: any, res: any): Promise => { // eslint-disable-next-line no-console console.log(`Unknown Elopage Value '${entry}'`) } - return null // we write things into the loginElopgaeBuy object, no return value needed }) // Do not process certain events From a44a3ee6d06df5bf6dc465680791e71a0d2df553 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 19 Nov 2021 01:08:53 +0100 Subject: [PATCH 039/178] make language an optional parameter for createUser and do not set it on the elopage webhook --- backend/src/graphql/arg/CreateUserArgs.ts | 2 +- backend/src/graphql/resolver/UserResolver.ts | 2 +- backend/src/webhook/elopage.ts | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/backend/src/graphql/arg/CreateUserArgs.ts b/backend/src/graphql/arg/CreateUserArgs.ts index 3d09e56eb..906e14aed 100644 --- a/backend/src/graphql/arg/CreateUserArgs.ts +++ b/backend/src/graphql/arg/CreateUserArgs.ts @@ -15,7 +15,7 @@ export default class CreateUserArgs { password: string @Field(() => String) - language: string + language?: string @Field(() => Int, { nullable: true }) publisherId: number diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 888a1aa00..ba71fef7a 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -280,7 +280,7 @@ export class UserResolver { // default int publisher_id = 0; // Validate Language (no throw) - if (!isLanguage(language)) { + if (!language || !isLanguage(language)) { language = DEFAULT_LANGUAGE } diff --git a/backend/src/webhook/elopage.ts b/backend/src/webhook/elopage.ts index 10dc35b61..fb25e9068 100644 --- a/backend/src/webhook/elopage.ts +++ b/backend/src/webhook/elopage.ts @@ -150,7 +150,6 @@ export const elopageWebhook = async (req: any, res: any): Promise => { firstName, lastName, password, - language: 'default', publisherId: loginElopgaeBuy.publisherId, }) } catch (error) { From ad82cda48933929c7b13188ac998acbe6cd1cbf6 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 19 Nov 2021 01:12:01 +0100 Subject: [PATCH 040/178] remove unused fields comments to reduce file lengrh --- backend/src/webhook/elopage.ts | 208 --------------------------------- 1 file changed, 208 deletions(-) diff --git a/backend/src/webhook/elopage.ts b/backend/src/webhook/elopage.ts index fb25e9068..62af89f2f 100644 --- a/backend/src/webhook/elopage.ts +++ b/backend/src/webhook/elopage.ts @@ -158,211 +158,3 @@ export const elopageWebhook = async (req: any, res: any): Promise => { } } } - -/* -Known unused fields: - - order_token=y22MJxHr9XzzPiaaH9GU - payment_session_id=849951 - payment_session_token=y22MJxHr9XzzPiaaH9GU - action=payment_processed - initiator - payer[first_name]=Theodora - payer[last_name]=Mis - payer[country]=Schweiz - payer[country_code]=CH - payer[city]=St.+Gallen - payer[street]=Vonwilstrasse+23 - payer[street_number] - payer[zip]=9000 - payer[company] - payer[vat_no] - payer[phone] - gift_receiver - publisher[first_name]=Bernd - publisher[last_name]=H%C3%BCckst%C3%A4dt - publisher[street]=Pfarrweg+2 - publisher[zip]=74653 - publisher[city]=K%C3%BCnzelsau - publisher[country]=Deutschland - publisher[phone]=%2B4979405460810 - team_members - product[id]=43944 - product[slug]=gold-de - product[name]=Gold-Mitgliedschaft - product[type]=membership - upsell - membership[name]=Gold-Mitgliedschaft - membership[membership_product_1]=Werkzeuge+%26+Ressourcen+%28Gold%29 - membership[membership_product_1_id]=44982 - membership[membership_product_2]=Zertifizierung+zum%2Fr+Gradido-Botschafter%2Fin - membership[membership_product_2_id]=43970 - membership[membership_product_3]=Seminar+3+Nat%C3%BCrliche+%C3%96konomie+des+Lebens - membership[membership_product_3_id]=43969 - membership[membership_product_4]=Potential-Entfaltungs-Techniken - membership[membership_product_4_id]=43954 - membership[membership_product_5]=Seminar+2+Nat%C3%BCrliche+%C3%96konomie+des+Lebens - membership[membership_product_5_id]=43896 - membership[membership_product_6]=Kongresspaket%3A+Gesundes+Geld+f%C3%BCr+eine+gesunde+Welt - membership[membership_product_6_id]=14590 - membership[membership_product_7]=Deine+Gold-Mitgliedschaft+bei+Gradido - membership[membership_product_7_id]=43951 - membership[membership_product_8]=Gradido+E-Book%2C+H%C3%B6rspiel+%22Joytopia%22++%E2%80%93+und+100+Vorteile - membership[membership_product_8_id]=7312 - membership[membership_product_9]=Danke%2C+dass+Du+hilfst+Gradido+in+die+Welt+zu+bringen%21 - membership[membership_product_9_id]=43744 - membership[membership_product_10]=Basis-Informationen+zu+Gradido - membership[membership_product_10_id]=42600 - membership[membership_product_11]=Seminar+1+Nat%C3%BCrliche+%C3%96konomie+des+Lebens - membership[membership_product_11_id]=43882 - membership[membership_product_12]=Musical+%22Gradido+%E2%80%93+gemeinsam+retten+wir+die+Welt%22 - membership[membership_product_12_id]=43886 - membership[membership_product_13]=Premium+Community+%26+Markt - membership[membership_product_13_id]=43885 - membership[membership_product_14]=Gradido+Buch+ungek%C3%BCrzte+Version+%26+%C3%9Cbersetzungen - membership[membership_product_14_id]=43887 - membership[membership_product_15]=Online-Konferenzen - membership[membership_product_15_id]=43919 - membership[membership_product_16]=Gradido+H%C3%B6rbuch - membership[membership_product_16_id]=43920 - events[] - events[] - events[] - events[] - events[] - events[] - events[] - events[] - events[] - events[] - events[] - events[] - events[] - events[] - events[] - events[] - tickets[][codes] - tickets[][ticket_attendees] - tickets[][codes] - tickets[][ticket_attendees] - tickets[][codes] - tickets[][ticket_attendees] - tickets[][codes] - tickets[][ticket_attendees] - tickets[][codes] - tickets[][ticket_attendees] - tickets[][codes] - tickets[][ticket_attendees] - tickets[][codes] - tickets[][ticket_attendees] - tickets[][codes] - tickets[][ticket_attendees] - tickets[][codes] - tickets[][ticket_attendees] - tickets[][codes] - tickets[][ticket_attendees] - tickets[][codes] - tickets[][ticket_attendees] - tickets[][codes] - tickets[][ticket_attendees] - tickets[][codes] - tickets[][ticket_attendees] - tickets[][codes] - tickets[][ticket_attendees] - tickets[][codes] - tickets[][ticket_attendees] - tickets[][codes] - tickets[][ticket_attendees] - pricing_plan[name]=Monatlich - add_id_1 - add_id_2 - campaign_id - currency=EUR - coupon_code - recurring=yes - recurring_form=subscription - payment_method=sepa - opt_ins - payments_schedule[][rate]=1 - payments_schedule[][state]=debt - payments_schedule[][amount]=40.0 - payments_schedule[][date]=29.10.2019 - payments_schedule[][rate]=2 - payments_schedule[][state]=paid - payments_schedule[][amount]=40.0 - payments_schedule[][date]=29.11.2019 - payments_schedule[][rate]=3 - payments_schedule[][state]=to_be_paid - payments_schedule[][amount]=40.0 - payments_schedule[][date]=29.12.2019 - payments_schedule[][rate]=4 - payments_schedule[][state]=to_be_paid - payments_schedule[][amount]=40.0 - payments_schedule[][date]=29.01.2020 - payments_schedule[][rate]=5 - payments_schedule[][state]=to_be_paid - payments_schedule[][amount]=40.0 - payments_schedule[][date]=29.02.2020 - payments_schedule[][rate]=6 - payments_schedule[][state]=to_be_paid - payments_schedule[][amount]=40.0 - payments_schedule[][date]=29.03.2020 - payments_schedule[][rate]=7 - payments_schedule[][state]=to_be_paid - payments_schedule[][amount]=40.0 - payments_schedule[][date]=29.04.2020 - payments_schedule[][rate]=8 - payments_schedule[][state]=to_be_paid - payments_schedule[][amount]=40.0 - payments_schedule[][date]=29.05.2020 - payments_schedule[][rate]=9 - payments_schedule[][state]=to_be_paid - payments_schedule[][amount]=40.0 - payments_schedule[][date]=29.06.2020 - payments_schedule[][rate]=10 - payments_schedule[][state]=to_be_paid - payments_schedule[][amount]=40.0 - payments_schedule[][date]=29.07.2020 - payments_schedule[][rate]=11 - payments_schedule[][state]=to_be_paid - payments_schedule[][amount]=40.0 - payments_schedule[][date]=29.08.2020 - payments_schedule[][rate]=12 - payments_schedule[][state]=to_be_paid - payments_schedule[][amount]=40.0 - payments_schedule[][date]=29.09.2020 - payments_schedule[][rate]=13 - payments_schedule[][state]=to_be_paid - payments_schedule[][amount]=40.0 - payments_schedule[][date]=29.10.2020 - payments_schedule[][rate]=14 - payments_schedule[][state]=to_be_paid - payments_schedule[][amount]=0.0 - payments_schedule[][date]=29.11.2020 - payments_schedule[][rate]=15 - payments_schedule[][state]=to_be_paid - payments_schedule[][amount]=0.0 - payments_schedule[][date]=29.12.2020 - payments_count=1&payments_count_expected - with_test_period=false - with_custom_start=false - created=29.10.2019+13%3A17 - id=57354055 - invoice_number=111-1839-000000677 - revenue=40.0 - amount=23.72 - fee=16.28 - vat_rate=0.0 - vat_amount=0.0 - state=successful - refunded_transfer_id - invoice_link=http%3A%2F%2Felopage.com%2Fcommon%2Finvoices%2F450856%2Fdownload.pdf%3Ftoken%3DGR7bG7zcbgCzNJEPLDss - credit_memo_link - success_link=http%3A%2F%2Felopage.com%2Fs%2Fgradido%2Fpayment%2Fy22MJxHr9XzzPiaaH9GU - error_msg - created_date=2019-11-29T07%3A19Z - success_date_short=2019-12-06 - created_date_utc=29.11.2019+07%3A19 - success_date_utc=06.12.2019+13%3A12 - team_member_commissions -*/ From f6f6314eb170358c23b2a6f41bf5239c19edb062 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 19 Nov 2021 01:22:24 +0100 Subject: [PATCH 041/178] save price as integer --- backend/src/webhook/elopage.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/webhook/elopage.ts b/backend/src/webhook/elopage.ts index 62af89f2f..da9d1a171 100644 --- a/backend/src/webhook/elopage.ts +++ b/backend/src/webhook/elopage.ts @@ -58,8 +58,8 @@ export const elopageWebhook = async (req: any, res: any): Promise => { loginElopgaeBuy.productId = parseInt(val) break case 'product[price]': - // TODO: static_cast(round(stof(temp) * 100.0f)); - loginElopgaeBuy.productPrice = parseFloat(val) + // TODO: WHAT THE ACTUAL FUK? Please save this as float in the future directly in the database + loginElopgaeBuy.productPrice = Math.trunc(parseFloat(val) * 100) break case 'payer[email]': loginElopgaeBuy.payerEmail = val From a6e4e84c9bc484bccc3dbc2fcfcb0310a3da1aab Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 19 Nov 2021 01:51:13 +0100 Subject: [PATCH 042/178] do not generate a password for a user, but change the process to set the password later. --- backend/src/graphql/arg/CreateUserArgs.ts | 5 +- backend/src/graphql/resolver/UserResolver.ts | 54 +++++++++++--------- backend/src/webhook/elopage.ts | 6 --- 3 files changed, 32 insertions(+), 33 deletions(-) diff --git a/backend/src/graphql/arg/CreateUserArgs.ts b/backend/src/graphql/arg/CreateUserArgs.ts index 906e14aed..0d63e76bb 100644 --- a/backend/src/graphql/arg/CreateUserArgs.ts +++ b/backend/src/graphql/arg/CreateUserArgs.ts @@ -12,10 +12,7 @@ export default class CreateUserArgs { lastName: string @Field(() => String) - password: string - - @Field(() => String) - language?: string + language?: string // Will default to DEFAULT_LANGUAGE @Field(() => Int, { nullable: true }) publisherId: number diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index ba71fef7a..471ca7384 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -274,7 +274,7 @@ export class UserResolver { @Mutation(() => String) async createUser( - @Args() { email, firstName, lastName, password, language, publisherId }: CreateUserArgs, + @Args() { email, firstName, lastName, language, publisherId }: CreateUserArgs, ): Promise { // TODO: wrong default value (should be null), how does graphql work here? Is it an required field? // default int publisher_id = 0; @@ -284,12 +284,13 @@ export class UserResolver { language = DEFAULT_LANGUAGE } + // TODO: Register process // Validate Password - if (!isPassword(password)) { - throw new Error( - 'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!', - ) - } + // if (!isPassword(password)) { + // throw new Error( + // 'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!', + // ) + // } // Validate username // TODO: never true @@ -307,11 +308,13 @@ export class UserResolver { throw new Error(`User already exists.`) } - const passphrase = PassphraseGenerate() - const keyPair = KeyPairEd25519Create(passphrase) // return pub, priv Key - const passwordHash = SecretKeyCryptographyCreateKey(email, password) // return short and long hash + // TODO: Register process + // const passphrase = PassphraseGenerate() + // const keyPair = KeyPairEd25519Create(passphrase) // return pub, priv Key + // const passwordHash = SecretKeyCryptographyCreateKey(email, password) // return short and long hash + // const encryptedPrivkey = SecretKeyCryptographyEncrypt(keyPair[1], passwordHash[1]) + const emailHash = getEmailHash(email) - const encryptedPrivkey = SecretKeyCryptographyEncrypt(keyPair[1], passwordHash[1]) // Table: login_users const loginUser = new LoginUser() @@ -320,13 +323,15 @@ export class UserResolver { loginUser.lastName = lastName loginUser.username = username loginUser.description = '' - loginUser.password = passwordHash[0].readBigUInt64LE() // using the shorthash + // TODO: Register process + // loginUser.password = passwordHash[0].readBigUInt64LE() // using the shorthash loginUser.emailHash = emailHash loginUser.language = language loginUser.groupId = 1 loginUser.publisherId = publisherId - loginUser.pubKey = keyPair[0] - loginUser.privKey = encryptedPrivkey + // TODO: Register process + // loginUser.pubKey = keyPair[0] + // loginUser.privKey = encryptedPrivkey const queryRunner = getConnection().createQueryRunner() await queryRunner.connect() @@ -338,21 +343,24 @@ export class UserResolver { throw new Error('insert user failed') }) + // TODO: Register process // Table: login_user_backups - const loginUserBackup = new LoginUserBackup() - loginUserBackup.userId = loginUserId - loginUserBackup.passphrase = passphrase.join(' ') + ' ' // login server saves trailing space - loginUserBackup.mnemonicType = 2 // ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER; + // const loginUserBackup = new LoginUserBackup() + // loginUserBackup.userId = loginUserId + // loginUserBackup.passphrase = passphrase.join(' ') + ' ' // login server saves trailing space + // loginUserBackup.mnemonicType = 2 // ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER; - await queryRunner.manager.save(loginUserBackup).catch((error) => { - // eslint-disable-next-line no-console - console.log('insert LoginUserBackup failed', error) - throw new Error('insert user backup failed') - }) + // TODO: Register process + // await queryRunner.manager.save(loginUserBackup).catch((error) => { + // // eslint-disable-next-line no-console + // console.log('insert LoginUserBackup failed', error) + // throw new Error('insert user backup failed') + // }) // Table: state_users const dbUser = new DbUser() - dbUser.pubkey = keyPair[0] + // TODO: Register process + // dbUser.pubkey = keyPair[0] dbUser.email = email dbUser.firstName = firstName dbUser.lastName = lastName diff --git a/backend/src/webhook/elopage.ts b/backend/src/webhook/elopage.ts index da9d1a171..90cdb159f 100644 --- a/backend/src/webhook/elopage.ts +++ b/backend/src/webhook/elopage.ts @@ -138,18 +138,12 @@ export const elopageWebhook = async (req: any, res: any): Promise => { return } - // generate a random password - 8 random bytes, the email, special char, capital & small letter, number and another set of 8 random bytes - // TODO: The user will be forced to reset his password - how was this done before? - const password = - randomBytes(8).toString('hex') + email + '!aA1' + randomBytes(8).toString('hex') - const userResolver = new UserResolver() try { await userResolver.createUser({ email, firstName, lastName, - password, publisherId: loginElopgaeBuy.publisherId, }) } catch (error) { From 686f9bfd4e6aeaaad9be451ddcdcf04f54e3cbe5 Mon Sep 17 00:00:00 2001 From: ogerly Date: Fri, 19 Nov 2021 11:14:16 +0100 Subject: [PATCH 043/178] admin area extended according to concept and functions added and improved. workflow stands for the mass creation and for the confirmation of creations. all changes made which were indicated by moritz. all translations will be done in one step at a later date. --- admin/package.json | 3 +- admin/src/App.vue | 3 + admin/src/components/CreationFormular.spec.js | 8 +- admin/src/components/CreationFormular.vue | 201 +++++++++++++---- admin/src/components/Footer.vue | 10 + admin/src/components/NavBar.spec.js | 4 +- admin/src/components/NavBar.vue | 18 +- admin/src/components/UserTable.spec.js | 4 +- admin/src/components/UserTable.vue | 208 ++++++++++++++---- admin/src/store/store.js | 22 ++ admin/src/views/Creation.vue | 109 ++++----- admin/src/views/CreationConfirm.vue | 48 ++-- admin/src/views/Overview.vue | 31 ++- admin/yarn.lock | 13 ++ 14 files changed, 497 insertions(+), 185 deletions(-) create mode 100644 admin/src/components/Footer.vue diff --git a/admin/package.json b/admin/package.json index 5dff9145e..f3ba786bc 100644 --- a/admin/package.json +++ b/admin/package.json @@ -41,7 +41,8 @@ "vue-jest": "^3.0.7", "vue-moment": "^4.1.0", "vue-router": "^3.5.3", - "vuex": "^3.6.2" + "vuex": "^3.6.2", + "vuex-persistedstate": "^4.1.0" }, "devDependencies": { "@babel/eslint-parser": "^7.15.8", diff --git a/admin/src/App.vue b/admin/src/App.vue index 1a15d5d7c..70bc2978a 100644 --- a/admin/src/App.vue +++ b/admin/src/App.vue @@ -2,15 +2,18 @@
+
diff --git a/admin/src/components/CreationFormular.spec.js b/admin/src/components/CreationFormular.spec.js index c2098768c..9f4fc3f1b 100644 --- a/admin/src/components/CreationFormular.spec.js +++ b/admin/src/components/CreationFormular.spec.js @@ -3,11 +3,13 @@ import CreationFormular from './CreationFormular.vue' const localVue = global.localVue +const mocks = { $moment: jest.fn() } + describe('CreationFormular', () => { let wrapper const Wrapper = () => { - return mount(CreationFormular, { localVue }) + return mount(CreationFormular, { localVue, mocks }) } describe('mount', () => { @@ -15,8 +17,8 @@ describe('CreationFormular', () => { wrapper = Wrapper() }) - it('have a DIV element with the class.componente-creation-formular', () => { - expect(wrapper.find('.componente-creation-formular').exists()).toBeTruthy() + it('has a DIV element with the class.component-creation-formular', () => { + expect(wrapper.find('.component-creation-formular').exists()).toBeTruthy() }) }) }) diff --git a/admin/src/components/CreationFormular.vue b/admin/src/components/CreationFormular.vue index dbc40c13c..995640c72 100644 --- a/admin/src/components/CreationFormular.vue +++ b/admin/src/components/CreationFormular.vue @@ -1,72 +1,108 @@ From 03e5df5ece7dd4984b062644f59e853621d876bf Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 19 Nov 2021 21:26:12 +0100 Subject: [PATCH 105/178] convenience mode to disable the auth mode for development --- admin/.env.dist | 3 ++- admin/src/App.vue | 8 +++++++- admin/src/config/index.js | 5 +++++ admin/src/router/guards.js | 14 +++++++++----- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/admin/.env.dist b/admin/.env.dist index a056837ab..6d78e6782 100644 --- a/admin/.env.dist +++ b/admin/.env.dist @@ -1,2 +1,3 @@ GRAPHQL_URI=http://localhost:4000/graphql -WALLET_AUTH_URL=http://localhost/vue/authenticate?token=$1 \ No newline at end of file +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.vue b/admin/src/App.vue index 40460eda4..7c3dd8514 100644 --- a/admin/src/App.vue +++ b/admin/src/App.vue @@ -1,15 +1,21 @@ diff --git a/admin/src/config/index.js b/admin/src/config/index.js index 1ef44fe60..236274d1f 100644 --- a/admin/src/config/index.js +++ b/admin/src/config/index.js @@ -23,6 +23,10 @@ const endpoints = { WALLET_AUTH_URL: process.env.WALLET_AUTH_URL || 'http://localhost:3000/vue/authenticate?token=$1' } +const debug = { + DEBUG_DISABLE_AUTH: process.env.DEBUG_DISABLE_AUTH === 'true' || false +} + const options = {} const CONFIG = { @@ -30,6 +34,7 @@ const CONFIG = { ...environment, ...endpoints, ...options, + ...debug, } export default CONFIG diff --git a/admin/src/router/guards.js b/admin/src/router/guards.js index 6207f443e..a8bdaa2e2 100644 --- a/admin/src/router/guards.js +++ b/admin/src/router/guards.js @@ -1,5 +1,7 @@ +import CONFIG from "../config" + const addNavigationGuards = (router, store) => { - // store token on authenticate + // store token on `authenticate` router.beforeEach((to, from, next) => { if (to.path === '/authenticate' && to.query.token) { store.commit('token',to.query.token) @@ -9,11 +11,13 @@ const addNavigationGuards = (router, store) => { } }) - // protect all routes but not-found + // protect all routes but `not-found` router.beforeEach((to, from, next) => { - console.log('protect', to.path, from.path) - // handle authentication - if (!store.state.token && to.path !== '/not-found' && to.path !== '/logout') { + 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` + console.log(!CONFIG.DEBUG_DISABLE_AUTH,!store.state.token,to.path !== '/not-found',to.path !== '/logout') next({ path: '/not-found' }) } else { next() From c3801f37ab00a144bb2c82195510566287b00ca3 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 20 Nov 2021 16:30:05 +0100 Subject: [PATCH 106/178] corrected ports --- admin/src/config/index.js | 2 +- frontend/src/config/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/admin/src/config/index.js b/admin/src/config/index.js index 236274d1f..9f33b59cb 100644 --- a/admin/src/config/index.js +++ b/admin/src/config/index.js @@ -20,7 +20,7 @@ const environment = { const endpoints = { GRAPHQL_URI: process.env.GRAPHQL_URI || 'http://localhost:4000/graphql', // TODO port - WALLET_AUTH_URL: process.env.WALLET_AUTH_URL || 'http://localhost:3000/vue/authenticate?token=$1' + WALLET_AUTH_URL: process.env.WALLET_AUTH_URL || 'http://localhost/vue/authenticate?token=$1' } const debug = { diff --git a/frontend/src/config/index.js b/frontend/src/config/index.js index 01c6c009a..0f8e58c1c 100644 --- a/frontend/src/config/index.js +++ b/frontend/src/config/index.js @@ -21,7 +21,7 @@ const environment = { const endpoints = { GRAPHQL_URI: process.env.GRAPHQL_URI || 'http://localhost:4000/graphql', // TODO port - ADMIN_AUTH_URL: process.env.ADMIN_AUTH_URL || 'http://localhost:8080/admin/authenticate?token=$1' + ADMIN_AUTH_URL: process.env.ADMIN_AUTH_URL || 'http://localhost/admin/authenticate?token=$1' } const options = {} From 41d6a7ba45659f5f129ea0a31a30d927f71f37a2 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 20 Nov 2021 16:30:22 +0100 Subject: [PATCH 107/178] implemented `isAdmin` from backend --- backend/src/graphql/model/User.ts | 4 +++ backend/src/graphql/resolver/UserResolver.ts | 31 ++++++++++++++++++++ frontend/src/graphql/queries.js | 1 + frontend/src/store/store.js | 7 ++++- 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/model/User.ts b/backend/src/graphql/model/User.ts index 5b7682e01..33dce434b 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 } } @@ -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 25f83bb09..1fd441935 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -194,6 +194,36 @@ 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 + } + */ + @Query(() => User) @UseMiddleware(klicktippNewsletterStateMiddleware) async login( @@ -265,6 +295,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/src/graphql/queries.js b/frontend/src/graphql/queries.js index 01021f601..9cd364ed7 100644 --- a/frontend/src/graphql/queries.js +++ b/frontend/src/graphql/queries.js @@ -15,6 +15,7 @@ export const login = gql` } hasElopage publisherId + isAdmin } } ` diff --git a/frontend/src/store/store.js b/frontend/src/store/store.js index 1173a8685..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,7 +92,7 @@ export const store = new Vuex.Store({ username: '', description: '', token: null, - isAdmin: true, // TODO implement this properly + isAdmin: false, coinanimation: true, newsletterState: null, community: { From f21587ae3abb1ed8125d4de277bfbef990047176 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 20 Nov 2021 16:37:25 +0100 Subject: [PATCH 108/178] frontend lint --- frontend/src/components/SidebarPlugin/SideBar.vue | 2 +- frontend/src/config/index.js | 3 +-- frontend/src/routes/guards.js | 9 ++++----- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/frontend/src/components/SidebarPlugin/SideBar.vue b/frontend/src/components/SidebarPlugin/SideBar.vue index d8b26318f..8277b17eb 100755 --- a/frontend/src/components/SidebarPlugin/SideBar.vue +++ b/frontend/src/components/SidebarPlugin/SideBar.vue @@ -122,7 +122,7 @@ export default { this.$emit('logout') }, admin() { - window.location = CONFIG.ADMIN_AUTH_URL.replace('$1',this.$store.state.token) + window.location = CONFIG.ADMIN_AUTH_URL.replace('$1', this.$store.state.token) this.$store.dispatch('logout') // logout without redirect }, getElopageLink() { diff --git a/frontend/src/config/index.js b/frontend/src/config/index.js index 0f8e58c1c..b3a9366b7 100644 --- a/frontend/src/config/index.js +++ b/frontend/src/config/index.js @@ -20,8 +20,7 @@ const environment = { const endpoints = { GRAPHQL_URI: process.env.GRAPHQL_URI || 'http://localhost:4000/graphql', - // TODO port - ADMIN_AUTH_URL: process.env.ADMIN_AUTH_URL || 'http://localhost/admin/authenticate?token=$1' + ADMIN_AUTH_URL: process.env.ADMIN_AUTH_URL || 'http://localhost/admin/authenticate?token=$1', } const options = {} diff --git a/frontend/src/routes/guards.js b/frontend/src/routes/guards.js index bb55ba0cf..005ed720f 100644 --- a/frontend/src/routes/guards.js +++ b/frontend/src/routes/guards.js @@ -11,16 +11,15 @@ const addNavigationGuards = (router, store) => { // store token on authenticate router.beforeEach((to, from, next) => { - console.log('token', to.path, from.path, to.query) if (to.path === '/authenticate' && to.query.token) { - console.log('token', to.query.token, to) - store.commit('token',to.query.token) - next({path: '/overview'}) + // TODO verify user in order to get user data + store.commit('token', to.query.token) + next({ path: '/overview' }) } else { next() } }) - + // handle authentication router.beforeEach((to, from, next) => { if (to.meta.requiresAuth && !store.state.token) { From 84684fe51f3cae31c3d3bb7384c500de54037276 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 20 Nov 2021 16:40:38 +0100 Subject: [PATCH 109/178] lint admin --- admin/src/App.vue | 4 ++-- admin/src/components/NavBar.vue | 4 ++-- admin/src/config/index.js | 5 ++--- admin/src/layouts/defaultLayout.vue | 2 +- admin/src/router/guards.js | 18 ++++++++++-------- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/admin/src/App.vue b/admin/src/App.vue index 7c3dd8514..359b63b3a 100644 --- a/admin/src/App.vue +++ b/admin/src/App.vue @@ -14,8 +14,8 @@ export default { components: { defaultLayout }, data() { return { - showLayout: CONFIG.DEBUG_DISABLE_AUTH || this.$store.state.token + showLayout: CONFIG.DEBUG_DISABLE_AUTH || this.$store.state.token, } - } + }, } diff --git a/admin/src/components/NavBar.vue b/admin/src/components/NavBar.vue index 260c066b3..6fb0cb31f 100644 --- a/admin/src/components/NavBar.vue +++ b/admin/src/components/NavBar.vue @@ -50,9 +50,9 @@ export default { this.$router.push('/logout') }, wallet() { - window.location = CONFIG.WALLET_AUTH_URL.replace('$1',this.$store.state.token) + window.location = CONFIG.WALLET_AUTH_URL.replace('$1', this.$store.state.token) this.$store.dispatch('logout') // logout without redirect }, - } + }, } diff --git a/admin/src/config/index.js b/admin/src/config/index.js index 9f33b59cb..69d30a66a 100644 --- a/admin/src/config/index.js +++ b/admin/src/config/index.js @@ -19,12 +19,11 @@ const environment = { const endpoints = { GRAPHQL_URI: process.env.GRAPHQL_URI || 'http://localhost:4000/graphql', - // TODO port - WALLET_AUTH_URL: process.env.WALLET_AUTH_URL || 'http://localhost/vue/authenticate?token=$1' + 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 + DEBUG_DISABLE_AUTH: process.env.DEBUG_DISABLE_AUTH === 'true' || false, } const options = {} diff --git a/admin/src/layouts/defaultLayout.vue b/admin/src/layouts/defaultLayout.vue index 71d2f894b..4f6cbbe4d 100644 --- a/admin/src/layouts/defaultLayout.vue +++ b/admin/src/layouts/defaultLayout.vue @@ -16,4 +16,4 @@ export default { FooTer, }, } - \ No newline at end of file + diff --git a/admin/src/router/guards.js b/admin/src/router/guards.js index a8bdaa2e2..f6d8058aa 100644 --- a/admin/src/router/guards.js +++ b/admin/src/router/guards.js @@ -1,11 +1,12 @@ -import CONFIG from "../config" +import CONFIG from '../config' const addNavigationGuards = (router, store) => { // store token on `authenticate` router.beforeEach((to, from, next) => { if (to.path === '/authenticate' && to.query.token) { - store.commit('token',to.query.token) - next({path: '/'}) + // TODO verify user to get user data + store.commit('token', to.query.token) + next({ path: '/' }) } else { next() } @@ -13,11 +14,12 @@ const addNavigationGuards = (router, store) => { // 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` - console.log(!CONFIG.DEBUG_DISABLE_AUTH,!store.state.token,to.path !== '/not-found',to.path !== '/logout') + 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() From 47bce06f95d74a851e1e69e22eda90466f77a1ce Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 20 Nov 2021 16:48:10 +0100 Subject: [PATCH 110/178] properly handle auth shortcut in oder to not break layout on logout --- admin/src/App.vue | 8 +------- admin/src/store/store.js | 3 ++- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/admin/src/App.vue b/admin/src/App.vue index 359b63b3a..40460eda4 100644 --- a/admin/src/App.vue +++ b/admin/src/App.vue @@ -1,21 +1,15 @@ diff --git a/admin/src/store/store.js b/admin/src/store/store.js index 4df9f6f39..7820296c6 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) @@ -33,7 +34,7 @@ const store = new Vuex.Store({ }), ], state: { - token: null, + token: CONFIG.DEBUG_DISABLE_AUTH ? 'validToken' : null, moderator: 'Dertest Moderator', openCreations: 0, }, From c8d188279fe0995671cb96bdf6d2f0cc6a9f6518 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 20 Nov 2021 16:51:21 +0100 Subject: [PATCH 111/178] add infos about jq software requirement --- frontend/README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) 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 From 024eb306127b6c8efebacef9bccdd1363e56c3b1 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 20 Nov 2021 16:51:45 +0100 Subject: [PATCH 112/178] fixed locales --- frontend/src/locales/de.json | 2 +- frontend/src/locales/en.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index 3f981450d..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", @@ -110,7 +111,6 @@ "login": "Anmeldung", "logout": "Abmelden", "members_area": "Mitgliederbereich", - "admin_area": "Adminbereich", "message": "hallo gradido !!", "overview": "Übersicht", "privacy_policy": "Datenschutzerklärung", diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 08c29d646..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", @@ -110,7 +111,6 @@ "login": "Login", "logout": "Logout", "members_area": "Member's area", - "admin_area": "Admin's area", "message": "hello gradido !!", "overview": "Overview", "privacy_policy": "Privacy policy", From b2bb67ab9c04a570cc780f94fd7fa4cf45d4c4a2 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 20 Nov 2021 17:01:28 +0100 Subject: [PATCH 113/178] fix tests --- frontend/src/routes/guards.test.js | 2 +- frontend/src/routes/router.test.js | 4 ++-- frontend/src/store/store.test.js | 19 +++++++++++++++---- 3 files changed, 18 insertions(+), 7 deletions(-) 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/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() From c1381da2407d43f05b38daabe245a268d7b49aa4 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 22 Nov 2021 17:51:09 +0100 Subject: [PATCH 114/178] test admin link, correct pointer class --- .../components/SidebarPlugin/SideBar.spec.js | 28 +++++++++++++++++++ .../src/components/SidebarPlugin/SideBar.vue | 6 ++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/SidebarPlugin/SideBar.spec.js b/frontend/src/components/SidebarPlugin/SideBar.spec.js index 8204eb604..0a3b0ec1a 100644 --- a/frontend/src/components/SidebarPlugin/SideBar.spec.js +++ b/frontend/src/components/SidebarPlugin/SideBar.spec.js @@ -24,6 +24,7 @@ describe('SideBar', () => { hasElopage: false, }, commit: jest.fn(), + dispatch: jest.fn(), }, $i18n: { locale: 'en', @@ -154,6 +155,33 @@ 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(() => { + mocks.$store.state.isAdmin = true + mocks.$store.state.token = 'valid token' + // const { location } = window; + delete window.location + window.location = {} + Object.defineProperty(window, 'location', assignLocationSpy) + wrapper = Wrapper() + }) + + it('is visible', () => { + expect(wrapper.findAll('li').at(1).text()).toBe('admin_area') + }) + it.skip('opens a new window when clicked', async () => { + wrapper.findAll('li').at(1).find('a').trigger('click') + await wrapper.vm.$nextTick() + expect(assignLocationSpy).toHaveBeenCalledWith('peter') + }) + }) + }) }) }) }) diff --git a/frontend/src/components/SidebarPlugin/SideBar.vue b/frontend/src/components/SidebarPlugin/SideBar.vue index 8277b17eb..9cf6b6185 100755 --- a/frontend/src/components/SidebarPlugin/SideBar.vue +++ b/frontend/src/components/SidebarPlugin/SideBar.vue @@ -50,14 +50,14 @@ -