From df5a910368e571371ba81084986aef4734ef4965 Mon Sep 17 00:00:00 2001 From: einhorn_b Date: Thu, 26 Aug 2021 20:43:26 +0200 Subject: [PATCH 1/9] update scripts and doc for setup without docker --- .gitmodules | 3 + login_server/README | 31 ----- login_server/README.md | 147 ++++++++++++++++++++++ login_server/dependencies/protobuf | 1 + login_server/scripts/build_debug.sh | 5 +- login_server/scripts/prepare_build.sh | 4 +- login_server/scripts/unittest_coverage.sh | 8 ++ 7 files changed, 162 insertions(+), 37 deletions(-) delete mode 100644 login_server/README create mode 100644 login_server/README.md create mode 160000 login_server/dependencies/protobuf create mode 100644 login_server/scripts/unittest_coverage.sh diff --git a/.gitmodules b/.gitmodules index 70fda6a17..22790ccc7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -31,3 +31,6 @@ [submodule "login_server/src/proto"] path = login_server/src/proto url = https://github.com/gradido/gradido_protocol.git +[submodule "login_server/dependencies/protobuf"] + path = login_server/dependencies/protobuf + url = https://github.com/protocolbuffers/protobuf.git diff --git a/login_server/README b/login_server/README deleted file mode 100644 index 400aee30a..000000000 --- a/login_server/README +++ /dev/null @@ -1,31 +0,0 @@ -sudo apt install libsodium-dev - -# get dependencies -git submodule update --init --recursive - - -cd dependencies/mariadb-connector-c -mkdir build -cd build -cmake -DWITH_SSL=OFF .. -cd ../../../ - - -# get more dependencies with conan (need conan from https://conan.io/) -mkdir build && cd build -# // not used anymore -# conan remote add inexor https://api.bintray.com/conan/inexorgame/inexor-conan -# not needed, but bincrafter -# conan install .. -s build_type=Debug -conan install .. - -# build Makefile with cmake -cmake .. - -make grpc -# under windows build at least release for protoc.exe and grpc c++ plugin -cd ../ -./unix_parse_proto.sh -cd build -make - diff --git a/login_server/README.md b/login_server/README.md new file mode 100644 index 000000000..80b951118 --- /dev/null +++ b/login_server/README.md @@ -0,0 +1,147 @@ +# Build Login-Server yourself +## Linux (Ubuntu) Packets +install build essentials + +```bash +sudo apt install -y gcovr build-essential gettext libcurl4-openssl-dev libssl-dev libsodium-dev libboost-dev +``` + +## CMake +I use CMake for build file generation and the Login-Server needs at least version v3.18.2 +You can build and install it from source. +The Version in apt is sadly to old. + +```bash +git clone https://github.com/Kitware/CMake.git --branch v3.18.2 +cd CMake +./bootstrap --parallel=$(nproc) && make -j$(nproc) && sudo make install +``` + +## dependencies +load git submodules if you haven't done it yet + +```bash +git submodule update --init --recursive +``` + +## build tools +build protoc and page compiler needed for generating some additional code + +```bash +cd scripts +./prepare_build.sh +``` + +## build +build login-server in debug mode + +```bash +cd scripts +./build_debug.sh +``` + +## multilanguage text +Login-Server uses gettext translations found after build in src/LOCALE +On Linux Login-Server expect the *.po files in folder /etc/grd_login/LOCALE +on windows next to Binary in Folder LOCALE. +So please copy them over by yourself on first run or after change. + +If you like to update some translations your find a messages.pot in src/LOCALE. +Use it together with poedit and don't forget to copy over *.po files after change to /etc/grd_login/LOCALE +To update messages.pot run + +```bash +./scripts/compile_pot.sh +``` +This will be also called by ./scripts/build_debug.sh + +## database +Login-Server needs a db to run, it is tested with mariadb +table definitions are found in folder ./skeema/gradido_login +Currently at least one group must be present in table groups. +For example: +```sql +INSERT INTO `groups` (`id`, `alias`, `name`, `url`, `host`, `home`, `description`) VALUES +(1, 'docker', 'docker gradido group', 'localhost', 'localhost', '/', 'gradido test group for docker with blockchain db'); +``` + +## configuration +Login-Server needs a configuration file to able to run. +On Linux it expect it to find the file /etc/grd_login/grd_login.properties +and /etc/grd_login/grd_login_test.properties for unittest + +Example configuration (ini-format) +```ini +# Port for Web-Interface +HTTPServer.port = 1200 +# Port for json-Interface (used by new backend) +JSONServer.port = 1201 +# default group id for new users, if no group was choosen +Gradido.group_id = 1 + +# currently not used +crypto.server_admin_public = f909a866baec97c5460b8d7a93b72d3d4d20cc45d9f15d78bd83944eb9286b7f +# Server admin Passphrase +# nerve execute merit pool talk hockey basic win cargo spin disagree ethics swear price purchase say clutch decrease slow half forest reform cheese able +# + +# TODO: auto-generate in docker build step +# expect valid hex 32 character long (16 Byte) +# salt for hashing user password, should be moved into db generated and saved per user, used for hardening against hash-tables +crypto.server_key = a51ef8ac7ef1abf162fb7a65261acd7a + +# TODO: auto-generate in docker build step +# salt for hashing user encryption key, expect valid hex, as long as you like, used in sha512 +crypto.app_secret = 21ffbbc616fe + +# for url forwarding to old frontend, path of community server +phpServer.url = http://localhost/ +# host for community server api calls +phpServer.host = localhost +# port for community server api calls +phpServer.port = 80 + +# Path for Login-Server Web-Interface used for link-generation +loginServer.path = http://localhost/account +# default language for new users and if no one is logged in +loginServer.default_locale = de + +# db setup tested with mariadb, should also work with mysql +loginServer.db.host = localhost +loginServer.db.name = gradido_login +loginServer.db.user = root +loginServer.db.password = +loginServer.db.port = 3306 + +# check email path for new frontend for link generation in emails +frontend.checkEmailPath = http://localhost/vue/reset + +# disable email all together +email.disable = true + +# setup email smtp server for sending emails +#email.username = +#email.sender = +#email.admin_receiver = +#email.password = +#email.smtp.url = +#email.smtp.port = + +# server setup types: test, staging or production +# used mainly to decide if using http or https for links +# test use http and staging and production uses https +ServerSetupType=test +dev.default_group = docker + +# Session timeout in minutes +session.timeout = 15 + +# Disabling security features for faster develop and testing +unsecure.allow_passwort_via_json_request = 1 +unsecure.allow_auto_sign_transactions = 1 +unsecure.allow_cors_all = 1 + +# default disable, passwords must contain a number, a lower character, a high character, special character, and be at least 8 characters long +unsecure.allow_all_passwords = 1 + +``` diff --git a/login_server/dependencies/protobuf b/login_server/dependencies/protobuf new file mode 160000 index 000000000..0b8d13a1d --- /dev/null +++ b/login_server/dependencies/protobuf @@ -0,0 +1 @@ +Subproject commit 0b8d13a1d4cd9be16ed8a2230577aa9c296aa1ca diff --git a/login_server/scripts/build_debug.sh b/login_server/scripts/build_debug.sh index 99a0d6d1a..27e5740dd 100755 --- a/login_server/scripts/build_debug.sh +++ b/login_server/scripts/build_debug.sh @@ -1,12 +1,9 @@ #!/bin/sh - -cd ../scripts - chmod +x compile_pot.sh +./compile_pot.sh cd ../build cmake -DCMAKE_BUILD_TYPE=Debug .. -./compile_pot.sh make -j$(nproc) Gradido_LoginServer chmod +x ./bin/Gradido_LoginServer diff --git a/login_server/scripts/prepare_build.sh b/login_server/scripts/prepare_build.sh index fe9c70e46..5598e0318 100755 --- a/login_server/scripts/prepare_build.sh +++ b/login_server/scripts/prepare_build.sh @@ -9,9 +9,9 @@ fi mkdir build cd build cmake -DWITH_SSL=OFF .. -cd ../../ +cd ../../../ -if [! -d "./build" ] ; then +if [ ! -d "./build" ] ; then mkdir build fi cd build diff --git a/login_server/scripts/unittest_coverage.sh b/login_server/scripts/unittest_coverage.sh new file mode 100644 index 000000000..b0bd1a4c9 --- /dev/null +++ b/login_server/scripts/unittest_coverage.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +cd ../build +cmake -DCMAKE_BUILD_TYPE=Debug -DCOLLECT_COVERAGE_DATA=ON -DCOVERAGE_TOOL=gcovr .. && \ +make -j$(nproc) Gradido_LoginServer_Test +make coverage + + From 81242a83a1fa7caca940e772ab8abfde2fe7363b Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 2 Sep 2021 13:49:12 +0200 Subject: [PATCH 2/9] Update login_server/README.md Co-authored-by: Ulf Gebhardt --- login_server/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/login_server/README.md b/login_server/README.md index 80b951118..73f5deea0 100644 --- a/login_server/README.md +++ b/login_server/README.md @@ -7,7 +7,7 @@ sudo apt install -y gcovr build-essential gettext libcurl4-openssl-dev libssl-de ``` ## CMake -I use CMake for build file generation and the Login-Server needs at least version v3.18.2 +CMake is used for build file generation and the Login-Server needs at least version v3.18.2 You can build and install it from source. The Version in apt is sadly to old. From 19e4e95e2c27226ca0470ed15772f9b4504fc705 Mon Sep 17 00:00:00 2001 From: Dario Rekowski on RockPI Date: Tue, 7 Sep 2021 14:49:26 +0000 Subject: [PATCH 3/9] show event gdt as gdt instead of euro --- .../src/Template/StateBalances/overview_gdt.ctp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/community_server/src/Template/StateBalances/overview_gdt.ctp b/community_server/src/Template/StateBalances/overview_gdt.ctp index f076e26c8..198256e23 100644 --- a/community_server/src/Template/StateBalances/overview_gdt.ctp +++ b/community_server/src/Template/StateBalances/overview_gdt.ctp @@ -47,8 +47,12 @@ $this->assign('header', $header);
- element('printEuro', ['number' => $entry['amount']]); ?> - element('printEuro', ['number' => $entry['amount2']]) ?> + + element('printGDT', ['number' => $entry['amount']*100.0]); ?> + + element('printEuro', ['number' => $entry['amount']]); ?> + element('printEuro', ['number' => $entry['amount2']]) ?> +
Number->format($entry['factor']) ?> @@ -89,8 +93,12 @@ $this->assign('header', $header);
- element('printEuro', ['number' => $gdtEntry['amount']]) ?> - element('printEuro', ['number' => $gdtEntry['amount2']]) ?> + + element('printGDT', ['number' => $gdtEntry['amount']*100.0]); ?> + + element('printEuro', ['number' => $gdtEntry['amount']]); ?> + element('printEuro', ['number' => $gdtEntry['amount2']]) ?> +
Number->format($gdtEntry['factor']) ?> From b41966ee903f70cfcce9ecc34cc1802f05978010 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 8 Sep 2021 15:06:52 +0200 Subject: [PATCH 4/9] add path for reset password request from new frontend --- configs/login_server/grd_login.properties | 3 ++- login_server/src/cpp/JSONInterface/JsonSendEmail.cpp | 10 ++++++---- login_server/src/cpp/ServerConfig.cpp | 4 +++- login_server/src/cpp/ServerConfig.h | 1 + 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/configs/login_server/grd_login.properties b/configs/login_server/grd_login.properties index f14568a16..96b0ea928 100644 --- a/configs/login_server/grd_login.properties +++ b/configs/login_server/grd_login.properties @@ -22,7 +22,8 @@ loginServer.db.user = root loginServer.db.password = loginServer.db.port = 3306 -frontend.checkEmailPath = http://localhost/reset +frontend.checkEmailPath = vue/checkEmail +frontend.resetPasswordPath = vue/reset email.disable = true diff --git a/login_server/src/cpp/JSONInterface/JsonSendEmail.cpp b/login_server/src/cpp/JSONInterface/JsonSendEmail.cpp index f278c393f..281c55c51 100644 --- a/login_server/src/cpp/JSONInterface/JsonSendEmail.cpp +++ b/login_server/src/cpp/JSONInterface/JsonSendEmail.cpp @@ -105,12 +105,13 @@ Poco::JSON::Object* JsonSendEmail::handle(Poco::Dynamic::Var params) return stateSuccess(); } auto receiver_user_id = receiver_user->getModel()->getID(); - std::string checkEmailUrl = receiver_user->getGroupBaseUrl() + ServerConfig::g_frontend_checkEmailPath; + std::string linkInEmail = ""; if (emailVerificationCodeType == model::table::EMAIL_OPT_IN_RESET_PASSWORD) { + linkInEmail = receiver_user->getGroupBaseUrl() + ServerConfig::g_frontend_resetPasswordPath; session = sm->getNewSession(); if (emailType == model::EMAIL_USER_RESET_PASSWORD) { - auto r = session->sendResetPasswordEmail(receiver_user, true, checkEmailUrl); + auto r = session->sendResetPasswordEmail(receiver_user, true, linkInEmail); if (1 == r) { return stateWarning("email already sended"); } @@ -120,7 +121,7 @@ Poco::JSON::Object* JsonSendEmail::handle(Poco::Dynamic::Var params) } else if (emailType == model::EMAIL_CUSTOM_TEXT) { auto email_verification_code_object = controller::EmailVerificationCode::loadOrCreate(receiver_user_id, model::table::EMAIL_OPT_IN_RESET_PASSWORD); - email_verification_code_object->setBaseUrl(checkEmailUrl); + email_verification_code_object->setBaseUrl(linkInEmail); auto email = new model::Email(email_verification_code_object, receiver_user, emailCustomText, emailCustomSubject); em->addEmail(email); } @@ -131,12 +132,13 @@ Poco::JSON::Object* JsonSendEmail::handle(Poco::Dynamic::Var params) } else { + linkInEmail = receiver_user->getGroupBaseUrl() + ServerConfig::g_frontend_checkEmailPath; if (session->getNewUser()->getModel()->getRole() != model::table::ROLE_ADMIN) { return stateError("admin needed"); } auto email_verification_code_object = controller::EmailVerificationCode::loadOrCreate(receiver_user_id, emailVerificationCodeType); - email_verification_code_object->setBaseUrl(checkEmailUrl); + email_verification_code_object->setBaseUrl(linkInEmail); model::Email* email = nullptr; if (emailType == model::EMAIL_CUSTOM_TEXT) { email = new model::Email(email_verification_code_object, receiver_user, emailCustomText, emailCustomSubject); diff --git a/login_server/src/cpp/ServerConfig.cpp b/login_server/src/cpp/ServerConfig.cpp index a453cc956..81a7b2511 100644 --- a/login_server/src/cpp/ServerConfig.cpp +++ b/login_server/src/cpp/ServerConfig.cpp @@ -51,6 +51,7 @@ namespace ServerConfig { std::string g_php_serverPath; std::string g_php_serverHost; std::string g_frontend_checkEmailPath; + std::string g_frontend_resetPasswordPath; int g_phpServerPort; Poco::Mutex g_TimeMutex; int g_FakeLoginSleepTime = 820; @@ -238,8 +239,9 @@ namespace ServerConfig { if ("" != app_secret_string) { g_CryptoAppSecret = DataTypeConverter::hexToBin(app_secret_string); } - std::string defaultCheckEmailPath = g_serverPath + "/checkEmail"; + std::string defaultCheckEmailPath = "/account/checkEmail"; g_frontend_checkEmailPath = cfg.getString("frontend.checkEmailPath", defaultCheckEmailPath); + g_frontend_resetPasswordPath = cfg.getString("frontend.resetPasswordPath", defaultCheckEmailPath); //g_CryptoAppSecret // unsecure flags diff --git a/login_server/src/cpp/ServerConfig.h b/login_server/src/cpp/ServerConfig.h index 5608a638e..a5d4a7a38 100644 --- a/login_server/src/cpp/ServerConfig.h +++ b/login_server/src/cpp/ServerConfig.h @@ -67,6 +67,7 @@ namespace ServerConfig { extern std::string g_php_serverPath; extern std::string g_php_serverHost; extern std::string g_frontend_checkEmailPath; + extern std::string g_frontend_resetPasswordPath; extern int g_phpServerPort; extern Poco::Mutex g_TimeMutex; extern int g_FakeLoginSleepTime; From 6811f45a1ce8937247a95abe0d122748c97fad87 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 9 Sep 2021 11:43:14 +0200 Subject: [PATCH 5/9] feat: New JWT in Every Authenticated Response --- backend/src/auth/auth.ts | 2 ++ backend/src/graphql/models/LoginResponse.ts | 19 ++++++++++++++ backend/src/graphql/resolvers/UserResolver.ts | 12 +++------ backend/src/index.ts | 25 ++++++++++++++++--- backend/src/jwt/encode.ts | 8 ++---- frontend/src/graphql/queries.js | 12 ++++++++- frontend/src/store/store.js | 21 ++++++++-------- frontend/src/store/store.test.js | 12 ++++++++- 8 files changed, 81 insertions(+), 30 deletions(-) create mode 100644 backend/src/graphql/models/LoginResponse.ts diff --git a/backend/src/auth/auth.ts b/backend/src/auth/auth.ts index 0d9014b15..ff7a07735 100644 --- a/backend/src/auth/auth.ts +++ b/backend/src/auth/auth.ts @@ -4,6 +4,7 @@ import { AuthChecker } from 'type-graphql' import decode from '../jwt/decode' import { apiGet } from '../apis/loginAPI' import CONFIG from '../config' +import encode from '../jwt/encode' /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ export const isAuthorized: AuthChecker = async ({ root, args, context, info }, roles) => { @@ -14,6 +15,7 @@ export const isAuthorized: AuthChecker = async ({ root, args, context, info `${CONFIG.LOGIN_API_URL}checkSessionState?session_id=${decoded.sessionId}`, ) context.sessionId = decoded.sessionId + context.setHeaders.push({ key: 'token', value: encode(decoded.sessionId) }) return result.success } } diff --git a/backend/src/graphql/models/LoginResponse.ts b/backend/src/graphql/models/LoginResponse.ts new file mode 100644 index 000000000..e40dce259 --- /dev/null +++ b/backend/src/graphql/models/LoginResponse.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +import { ObjectType, Field } from 'type-graphql' +import { User } from '../models/User' +import encode from '../../jwt/encode' + +@ObjectType() +export class LoginResponse { + constructor(json: any) { + this.token = encode(json.sessionId) + this.user = new User(json.user) + } + + @Field(() => String) + token: string + + @Field(() => User) + user: User +} diff --git a/backend/src/graphql/resolvers/UserResolver.ts b/backend/src/graphql/resolvers/UserResolver.ts index dd8240e02..c6f3ea0b0 100644 --- a/backend/src/graphql/resolvers/UserResolver.ts +++ b/backend/src/graphql/resolvers/UserResolver.ts @@ -4,10 +4,10 @@ import { Resolver, Query, Args, Arg, Authorized, Ctx } from 'type-graphql' import CONFIG from '../../config' import { CheckUsernameResponse } from '../models/CheckUsernameResponse' -import { User } from '../models/User' import { LoginViaVerificationCode } from '../models/LoginViaVerificationCode' import { SendPasswordResetEmailResponse } from '../models/SendPasswordResetEmailResponse' import { UpdateUserInfosResponse } from '../models/UpdateUserInfosResponse' +import { LoginResponse } from '../models/LoginResponse' import { ChangePasswordArgs, CheckUsernameArgs, @@ -16,12 +16,11 @@ import { UpdateUserInfosArgs, } from '../inputs/LoginUserInput' import { apiPost, apiGet } from '../../apis/loginAPI' -import encode from '../../jwt/encode' @Resolver() export class UserResolver { - @Query(() => String) - async login(@Args() { email, password }: UnsecureLoginArgs): Promise { + @Query(() => LoginResponse) + async login(@Args() { email, password }: UnsecureLoginArgs): Promise { email = email.trim().toLowerCase() const result = await apiPost(CONFIG.LOGIN_API_URL + 'unsecureLogin', { email, password }) @@ -30,10 +29,7 @@ export class UserResolver { throw new Error(result.data) } - const data = result.data - const sessionId = data.session_id - delete data.session_id - return encode({ sessionId, user: new User(data.user) }) + return new LoginResponse({ sessionId: result.data.session_id, user: result.data.user }) } @Query(() => LoginViaVerificationCode) diff --git a/backend/src/index.ts b/backend/src/index.ts index 067403508..50e2c0a60 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -22,14 +22,15 @@ import { isAuthorized } from './auth/auth' const DB_VERSION = '0001-init_db' -const context = (req: any) => { - const authorization = req.req.headers.authorization +const context = (args: any) => { + const authorization = args.req.headers.authorization let token = null if (authorization) { - token = req.req.headers.authorization.replace(/^Bearer /, '') + token = authorization.replace(/^Bearer /, '') } const context = { token, + setHeaders: [], } return context } @@ -61,8 +62,24 @@ async function main() { // Express Server const server = express() + const plugins = [ + { + requestDidStart() { + return { + willSendResponse(requestContext: any) { + const { setHeaders = [] } = requestContext.context + setHeaders.forEach(({ key, value }: { [key: string]: string }) => { + requestContext.response.http.headers.append(key, value) + }) + return requestContext + }, + } + }, + }, + ] + // Apollo Server - const apollo = new ApolloServer({ schema, playground, context }) + const apollo = new ApolloServer({ schema, playground, context, plugins }) apollo.applyMiddleware({ app: server }) // Start Server diff --git a/backend/src/jwt/encode.ts b/backend/src/jwt/encode.ts index 477644dc7..9c5145e6d 100644 --- a/backend/src/jwt/encode.ts +++ b/backend/src/jwt/encode.ts @@ -5,13 +5,9 @@ import jwt from 'jsonwebtoken' import CONFIG from '../config/' // Generate an Access Token -export default function encode(data: any): string { - const { user, sessionId } = data - const { email, language, firstName, lastName } = user - const token = jwt.sign({ email, language, firstName, lastName, sessionId }, CONFIG.JWT_SECRET, { +export default function encode(sessionId: string): string { + const token = jwt.sign({ sessionId }, CONFIG.JWT_SECRET, { expiresIn: CONFIG.JWT_EXPIRES_IN, - // issuer: CONFIG.GRAPHQL_URI, - // audience: CONFIG.CLIENT_URI, subject: sessionId.toString(), }) return token diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js index 0aa11ce13..8c05c1ec3 100644 --- a/frontend/src/graphql/queries.js +++ b/frontend/src/graphql/queries.js @@ -2,7 +2,17 @@ import gql from 'graphql-tag' export const login = gql` query($email: String!, $password: String!) { - login(email: $email, password: $password) + login(email: $email, password: $password) { + token + user { + email + username + firstName + lastName + language + description + } + } } ` diff --git a/frontend/src/store/store.js b/frontend/src/store/store.js index 77c7096ad..8e7abcb08 100644 --- a/frontend/src/store/store.js +++ b/frontend/src/store/store.js @@ -1,7 +1,7 @@ import Vue from 'vue' import Vuex from 'vuex' import createPersistedState from 'vuex-persistedstate' -import VueJwtDecode from 'vue-jwt-decode' +// import VueJwtDecode from 'vue-jwt-decode' Vue.use(Vuex) @@ -30,15 +30,16 @@ export const mutations = { } export const actions = { - login: ({ dispatch, commit }, token) => { - const decoded = VueJwtDecode.decode(token) - commit('token', token) - commit('email', decoded.email) - commit('language', decoded.language) - commit('username', decoded.username) - commit('firstName', decoded.firstName) - commit('lastName', decoded.lastName) - commit('description', decoded.description) + login: ({ dispatch, commit }, data) => { + // const decoded = VueJwtDecode.decode(data.token) + const { user } = data + commit('token', data.token) + commit('email', user.email) + commit('language', user.language) + commit('username', user.username) + commit('firstName', user.firstName) + commit('lastName', user.lastName) + commit('description', user.description) }, logout: ({ commit, state }) => { commit('token', null) diff --git a/frontend/src/store/store.test.js b/frontend/src/store/store.test.js index 99a37451e..e935b40f1 100644 --- a/frontend/src/store/store.test.js +++ b/frontend/src/store/store.test.js @@ -77,7 +77,17 @@ describe('Vuex store', () => { describe('login', () => { const commit = jest.fn() const state = {} - const commitedData = 'token' + const commitedData = { + token: 'token', + user: { + email: 'user@example.org', + language: 'de', + username: 'peter', + firstName: 'Peter', + lastName: 'Lustig', + description: 'Nickelbrille', + }, + } it('calls seven commits', () => { login({ commit, state }, commitedData) From 02a962c83b10c37648de043eac7593eb64540f77 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 13 Sep 2021 13:04:09 +0200 Subject: [PATCH 6/9] JWT is updated in frontend from response header --- backend/package.json | 1 + backend/src/index.ts | 8 ++++++++ frontend/src/main.js | 6 +++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/backend/package.json b/backend/package.json index c337b8e35..e62085b2d 100644 --- a/backend/package.json +++ b/backend/package.json @@ -18,6 +18,7 @@ "apollo-server-express": "^2.25.2", "axios": "^0.21.1", "class-validator": "^0.13.1", + "cors": "^2.8.5", "dotenv": "^10.0.0", "express": "^4.17.1", "graphql": "^15.5.1", diff --git a/backend/src/index.ts b/backend/src/index.ts index 50e2c0a60..b99e5bee4 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -2,6 +2,7 @@ import 'reflect-metadata' import express from 'express' +import cors from 'cors' import { buildSchema } from 'type-graphql' import { ApolloServer } from 'apollo-server-express' import { RowDataPacket } from 'mysql2/promise' @@ -62,6 +63,13 @@ async function main() { // Express Server const server = express() + const corsOptions = { + origin: '*', + exposedHeaders: ['token'], + } + + server.use(cors(corsOptions)) + const plugins = [ { requestDidStart() { diff --git a/frontend/src/main.js b/frontend/src/main.js index 823df516c..0b0e98e2d 100755 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -20,7 +20,11 @@ const authLink = new ApolloLink((operation, forward) => { Authorization: token && token.length > 0 ? `Bearer ${token}` : '', }, }) - return forward(operation) + return forward(operation).map((response) => { + const newToken = operation.getContext().response.headers.get('token') + if (newToken) store.commit('token', newToken) + return response + }) }) const apolloClient = new ApolloClient({ From 12a5f58376b5c9cabcc6d1f1eb915eb5e9d1b98e Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 13 Sep 2021 13:30:53 +0200 Subject: [PATCH 7/9] no token in login response. token is inside response header --- backend/src/graphql/models/LoginResponse.ts | 19 -------- backend/src/graphql/resolvers/UserResolver.ts | 11 +++-- frontend/package.json | 1 - frontend/src/graphql/queries.js | 15 +++---- frontend/src/store/store.js | 16 +++---- frontend/src/store/store.test.js | 45 ++++++------------- 6 files changed, 32 insertions(+), 75 deletions(-) delete mode 100644 backend/src/graphql/models/LoginResponse.ts diff --git a/backend/src/graphql/models/LoginResponse.ts b/backend/src/graphql/models/LoginResponse.ts deleted file mode 100644 index e40dce259..000000000 --- a/backend/src/graphql/models/LoginResponse.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { ObjectType, Field } from 'type-graphql' -import { User } from '../models/User' -import encode from '../../jwt/encode' - -@ObjectType() -export class LoginResponse { - constructor(json: any) { - this.token = encode(json.sessionId) - this.user = new User(json.user) - } - - @Field(() => String) - token: string - - @Field(() => User) - user: User -} diff --git a/backend/src/graphql/resolvers/UserResolver.ts b/backend/src/graphql/resolvers/UserResolver.ts index c6f3ea0b0..e23ff5976 100644 --- a/backend/src/graphql/resolvers/UserResolver.ts +++ b/backend/src/graphql/resolvers/UserResolver.ts @@ -7,7 +7,8 @@ import { CheckUsernameResponse } from '../models/CheckUsernameResponse' import { LoginViaVerificationCode } from '../models/LoginViaVerificationCode' import { SendPasswordResetEmailResponse } from '../models/SendPasswordResetEmailResponse' import { UpdateUserInfosResponse } from '../models/UpdateUserInfosResponse' -import { LoginResponse } from '../models/LoginResponse' +import { User } from '../models/User' +import encode from '../../jwt/encode' import { ChangePasswordArgs, CheckUsernameArgs, @@ -19,8 +20,8 @@ import { apiPost, apiGet } from '../../apis/loginAPI' @Resolver() export class UserResolver { - @Query(() => LoginResponse) - async login(@Args() { email, password }: UnsecureLoginArgs): Promise { + @Query(() => User) + async login(@Args() { email, password }: UnsecureLoginArgs, @Ctx() context: any): Promise { email = email.trim().toLowerCase() const result = await apiPost(CONFIG.LOGIN_API_URL + 'unsecureLogin', { email, password }) @@ -29,7 +30,9 @@ export class UserResolver { throw new Error(result.data) } - return new LoginResponse({ sessionId: result.data.session_id, user: result.data.user }) + context.setHeaders.push({ key: 'token', value: encode(result.data.session_id) }) + + return new User(result.data.user) } @Query(() => LoginViaVerificationCode) diff --git a/frontend/package.json b/frontend/package.json index 59b34cc9d..4bc621916 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -72,7 +72,6 @@ "vue-good-table": "^2.21.3", "vue-i18n": "^8.22.4", "vue-jest": "^3.0.7", - "vue-jwt-decode": "^0.1.0", "vue-loading-overlay": "^3.4.2", "vue-moment": "^4.1.0", "vue-qrcode": "^0.3.5", diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js index 8c05c1ec3..7414414b2 100644 --- a/frontend/src/graphql/queries.js +++ b/frontend/src/graphql/queries.js @@ -3,15 +3,12 @@ import gql from 'graphql-tag' export const login = gql` query($email: String!, $password: String!) { login(email: $email, password: $password) { - token - user { - email - username - firstName - lastName - language - description - } + email + username + firstName + lastName + language + description } } ` diff --git a/frontend/src/store/store.js b/frontend/src/store/store.js index 8e7abcb08..00f8369d2 100644 --- a/frontend/src/store/store.js +++ b/frontend/src/store/store.js @@ -1,7 +1,6 @@ import Vue from 'vue' import Vuex from 'vuex' import createPersistedState from 'vuex-persistedstate' -// import VueJwtDecode from 'vue-jwt-decode' Vue.use(Vuex) @@ -31,15 +30,12 @@ export const mutations = { export const actions = { login: ({ dispatch, commit }, data) => { - // const decoded = VueJwtDecode.decode(data.token) - const { user } = data - commit('token', data.token) - commit('email', user.email) - commit('language', user.language) - commit('username', user.username) - commit('firstName', user.firstName) - commit('lastName', user.lastName) - commit('description', user.description) + commit('email', data.email) + commit('language', data.language) + commit('username', data.username) + commit('firstName', data.firstName) + commit('lastName', data.lastName) + commit('description', data.description) }, logout: ({ commit, state }) => { commit('token', null) diff --git a/frontend/src/store/store.test.js b/frontend/src/store/store.test.js index e935b40f1..c067a6e49 100644 --- a/frontend/src/store/store.test.js +++ b/frontend/src/store/store.test.js @@ -1,15 +1,4 @@ import { mutations, actions } from './store' -import VueJwtDecode from 'vue-jwt-decode' - -jest.mock('vue-jwt-decode') -VueJwtDecode.decode.mockReturnValue({ - email: 'user@example.org', - language: 'de', - username: 'peter', - firstName: 'Peter', - lastName: 'Lustig', - description: 'Nickelbrille', -}) const { language, email, token, username, firstName, lastName, description } = mutations const { login, logout } = actions @@ -78,55 +67,47 @@ describe('Vuex store', () => { const commit = jest.fn() const state = {} const commitedData = { - token: 'token', - user: { - email: 'user@example.org', - language: 'de', - username: 'peter', - firstName: 'Peter', - lastName: 'Lustig', - description: 'Nickelbrille', - }, + email: 'user@example.org', + language: 'de', + username: 'peter', + firstName: 'Peter', + lastName: 'Lustig', + description: 'Nickelbrille', } it('calls seven commits', () => { login({ commit, state }, commitedData) - expect(commit).toHaveBeenCalledTimes(7) - }) - - it('commits token', () => { - login({ commit, state }, commitedData) - expect(commit).toHaveBeenNthCalledWith(1, 'token', 'token') + expect(commit).toHaveBeenCalledTimes(6) }) it('commits email', () => { login({ commit, state }, commitedData) - expect(commit).toHaveBeenNthCalledWith(2, 'email', 'user@example.org') + expect(commit).toHaveBeenNthCalledWith(1, 'email', 'user@example.org') }) it('commits language', () => { login({ commit, state }, commitedData) - expect(commit).toHaveBeenNthCalledWith(3, 'language', 'de') + expect(commit).toHaveBeenNthCalledWith(2, 'language', 'de') }) it('commits username', () => { login({ commit, state }, commitedData) - expect(commit).toHaveBeenNthCalledWith(4, 'username', 'peter') + expect(commit).toHaveBeenNthCalledWith(3, 'username', 'peter') }) it('commits firstName', () => { login({ commit, state }, commitedData) - expect(commit).toHaveBeenNthCalledWith(5, 'firstName', 'Peter') + expect(commit).toHaveBeenNthCalledWith(4, 'firstName', 'Peter') }) it('commits lastName', () => { login({ commit, state }, commitedData) - expect(commit).toHaveBeenNthCalledWith(6, 'lastName', 'Lustig') + expect(commit).toHaveBeenNthCalledWith(5, 'lastName', 'Lustig') }) it('commits description', () => { login({ commit, state }, commitedData) - expect(commit).toHaveBeenNthCalledWith(7, 'description', 'Nickelbrille') + expect(commit).toHaveBeenNthCalledWith(6, 'description', 'Nickelbrille') }) }) From 8ff23b3dbaf9ae1e972cac597480e0dee28b6123 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 13 Sep 2021 13:38:18 +0200 Subject: [PATCH 8/9] fix view of euro now caming as float from gdt server --- .../src/Template/StateBalances/overview_gdt.ctp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/community_server/src/Template/StateBalances/overview_gdt.ctp b/community_server/src/Template/StateBalances/overview_gdt.ctp index 198256e23..f293b93be 100644 --- a/community_server/src/Template/StateBalances/overview_gdt.ctp +++ b/community_server/src/Template/StateBalances/overview_gdt.ctp @@ -50,8 +50,8 @@ $this->assign('header', $header); element('printGDT', ['number' => $entry['amount']*100.0]); ?> - element('printEuro', ['number' => $entry['amount']]); ?> - element('printEuro', ['number' => $entry['amount2']]) ?> + element('printEuro', ['number' => $entry['amount']*100.0]); ?> + element('printEuro', ['number' => $entry['amount2']*100.0]) ?>
@@ -96,8 +96,8 @@ $this->assign('header', $header); element('printGDT', ['number' => $gdtEntry['amount']*100.0]); ?> - element('printEuro', ['number' => $gdtEntry['amount']]); ?> - element('printEuro', ['number' => $gdtEntry['amount2']]) ?> + element('printEuro', ['number' => $gdtEntry['amount']*100.0]); ?> + element('printEuro', ['number' => $gdtEntry['amount2']*100.0]) ?>
From 4f4cdb6fe583650766caf3ee61d93f5407810674 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 13 Sep 2021 13:39:55 +0200 Subject: [PATCH 9/9] remove email from decoding --- backend/src/jwt/decode.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/backend/src/jwt/decode.ts b/backend/src/jwt/decode.ts index a414e0c41..47cf62154 100644 --- a/backend/src/jwt/decode.ts +++ b/backend/src/jwt/decode.ts @@ -7,15 +7,12 @@ import CONFIG from '../config/' export default (token: string): any => { if (!token) return null let sessionId = null - const email = null try { const decoded = jwt.verify(token, CONFIG.JWT_SECRET) sessionId = decoded.sub - // email = decoded.email return { token, sessionId, - email, } } catch (err) { return null