diff --git a/.gitignore b/.gitignore index 02347691f..a5dadd281 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ messages.pot .skeema nbproject .metadata +/.env 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/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/auth/auth.ts b/backend/src/auth/auth.ts new file mode 100644 index 000000000..ff7a07735 --- /dev/null +++ b/backend/src/auth/auth.ts @@ -0,0 +1,23 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +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) => { + 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.setHeaders.push({ key: 'token', value: encode(decoded.sessionId) }) + return result.success + } + } + return false +} diff --git a/backend/src/graphql/inputs/GdtInputs.ts b/backend/src/graphql/inputs/GdtInputs.ts index 1404fbf14..e11ce24c1 100644 --- a/backend/src/graphql/inputs/GdtInputs.ts +++ b/backend/src/graphql/inputs/GdtInputs.ts @@ -17,9 +17,6 @@ export class GdtTransactionInput { @ArgsType() export class GdtTransactionSessionIdInput { - @Field(() => Number) - sessionId: number - @Field(() => Int, { nullable: true }) currentPage?: number diff --git a/backend/src/graphql/inputs/LoginUserInput.ts b/backend/src/graphql/inputs/LoginUserInput.ts index 19326689e..f92cf5510 100644 --- a/backend/src/graphql/inputs/LoginUserInput.ts +++ b/backend/src/graphql/inputs/LoginUserInput.ts @@ -38,9 +38,6 @@ export class ChangePasswordArgs { @ArgsType() export class UpdateUserInfosArgs { - @Field(() => Number) - sessionId!: number - @Field(() => String) email!: string diff --git a/backend/src/graphql/inputs/TransactionInput.ts b/backend/src/graphql/inputs/TransactionInput.ts index 7f83cd82d..a62d86a65 100644 --- a/backend/src/graphql/inputs/TransactionInput.ts +++ b/backend/src/graphql/inputs/TransactionInput.ts @@ -2,9 +2,6 @@ import { ArgsType, Field, Int } from 'type-graphql' @ArgsType() export class TransactionListInput { - @Field(() => Number) - sessionId: number - @Field(() => Int) firstPage: number @@ -17,9 +14,6 @@ export class TransactionListInput { @ArgsType() export class TransactionSendArgs { - @Field(() => Number) - sessionId: number - @Field(() => String) email: string diff --git a/backend/src/graphql/models/GdtEntry.ts b/backend/src/graphql/models/GdtEntry.ts index 09ee35a70..7935d4181 100644 --- a/backend/src/graphql/models/GdtEntry.ts +++ b/backend/src/graphql/models/GdtEntry.ts @@ -15,6 +15,7 @@ export enum GdtEntryType { @ObjectType() export class GdtEntry { constructor(json: any) { + this.id = json.id this.amount = json.amount this.date = json.date this.email = json.email @@ -27,6 +28,9 @@ export class GdtEntry { this.gdt = json.gdt } + @Field(() => Number) + id: number + @Field(() => Number) amount: number diff --git a/backend/src/graphql/models/LoginResponse.ts b/backend/src/graphql/models/LoginResponse.ts deleted file mode 100644 index 258784b75..000000000 --- a/backend/src/graphql/models/LoginResponse.ts +++ /dev/null @@ -1,20 +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 './User' - -// temporaray solution until we have JWT implemented - -@ObjectType() -export class LoginResponse { - constructor(json: any) { - this.sessionId = json.session_id - this.user = new User(json.user) - } - - @Field(() => Number) - sessionId: number - - @Field(() => User) - user: User -} diff --git a/backend/src/graphql/resolvers/BalanceResolver.ts b/backend/src/graphql/resolvers/BalanceResolver.ts index c483ad5c9..27e0d4890 100644 --- a/backend/src/graphql/resolvers/BalanceResolver.ts +++ b/backend/src/graphql/resolvers/BalanceResolver.ts @@ -1,13 +1,17 @@ -import { Resolver, Query, /* Mutation, */ Arg } from 'type-graphql' +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + +import { Resolver, Query, Ctx, Authorized } from 'type-graphql' import CONFIG from '../../config' import { Balance } from '../models/Balance' import { apiGet } from '../../apis/loginAPI' @Resolver() export class BalanceResolver { + @Authorized() @Query(() => Balance) - async balance(@Arg('sessionId') sessionId: number): Promise { - const result = await apiGet(CONFIG.COMMUNITY_API_URL + 'getBalance/' + sessionId) + async balance(@Ctx() context: any): Promise { + const result = await apiGet(CONFIG.COMMUNITY_API_URL + 'getBalance/' + context.sessionId) if (!result.success) throw new Error(result.data) return new Balance(result.data) } diff --git a/backend/src/graphql/resolvers/GdtResolver.ts b/backend/src/graphql/resolvers/GdtResolver.ts index 0ff187bd1..b69f6ad7e 100644 --- a/backend/src/graphql/resolvers/GdtResolver.ts +++ b/backend/src/graphql/resolvers/GdtResolver.ts @@ -1,5 +1,7 @@ -// import jwt from 'jsonwebtoken' -import { Resolver, Query, /* Mutation, */ Args } from 'type-graphql' +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + +import { Resolver, Query, Args, Ctx, Authorized } from 'type-graphql' import CONFIG from '../../config' import { GdtEntryList } from '../models/GdtEntryList' import { GdtTransactionSessionIdInput } from '../inputs/GdtInputs' @@ -7,14 +9,16 @@ import { apiGet } from '../../apis/loginAPI' @Resolver() export class GdtResolver { + @Authorized() @Query(() => GdtEntryList) // eslint-disable-next-line @typescript-eslint/no-explicit-any async listGDTEntries( @Args() - { currentPage = 1, pageSize = 5, order = 'DESC', sessionId }: GdtTransactionSessionIdInput, + { currentPage = 1, pageSize = 5, order = 'DESC' }: GdtTransactionSessionIdInput, + @Ctx() context: any, ): Promise { const result = await apiGet( - `${CONFIG.COMMUNITY_API_URL}listGDTTransactions/${currentPage}/${pageSize}/${order}/${sessionId}`, + `${CONFIG.COMMUNITY_API_URL}listGDTTransactions/${currentPage}/${pageSize}/${order}/${context.sessionId}`, ) if (!result.success) { throw new Error(result.data) diff --git a/backend/src/graphql/resolvers/TransactionResolver.ts b/backend/src/graphql/resolvers/TransactionResolver.ts index 607b09691..2f1c2bf16 100644 --- a/backend/src/graphql/resolvers/TransactionResolver.ts +++ b/backend/src/graphql/resolvers/TransactionResolver.ts @@ -1,4 +1,7 @@ -import { Resolver, Query, Args } from 'type-graphql' +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + +import { Resolver, Query, Args, Authorized, Ctx } from 'type-graphql' import CONFIG from '../../config' import { TransactionList } from '../models/Transaction' import { TransactionListInput, TransactionSendArgs } from '../inputs/TransactionInput' @@ -6,23 +9,27 @@ import { apiGet, apiPost } from '../../apis/loginAPI' @Resolver() export class TransactionResolver { + @Authorized() @Query(() => TransactionList) async transactionList( - @Args() { sessionId, firstPage = 1, items = 25, order = 'DESC' }: TransactionListInput, + @Args() { firstPage = 1, items = 25, order = 'DESC' }: TransactionListInput, + @Ctx() context: any, ): Promise { const result = await apiGet( - `${CONFIG.COMMUNITY_API_URL}listTransactions/${firstPage}/${items}/${order}/${sessionId}`, + `${CONFIG.COMMUNITY_API_URL}listTransactions/${firstPage}/${items}/${order}/${context.sessionId}`, ) if (!result.success) throw new Error(result.data) return new TransactionList(result.data) } + @Authorized() @Query(() => String) async sendCoins( - @Args() { sessionId, email, amount, memo }: TransactionSendArgs, + @Args() { email, amount, memo }: TransactionSendArgs, + @Ctx() context: any, ): Promise { const payload = { - session_id: sessionId, + session_id: context.sessionId, target_email: email, amount: amount * 10000, memo, diff --git a/backend/src/graphql/resolvers/UserResolver.ts b/backend/src/graphql/resolvers/UserResolver.ts index cad1642f1..e23ff5976 100644 --- a/backend/src/graphql/resolvers/UserResolver.ts +++ b/backend/src/graphql/resolvers/UserResolver.ts @@ -1,11 +1,14 @@ -// import jwt from 'jsonwebtoken' -import { Resolver, Query, Args, Arg } from 'type-graphql' +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + +import { Resolver, Query, Args, Arg, Authorized, Ctx } from 'type-graphql' import CONFIG from '../../config' import { CheckUsernameResponse } from '../models/CheckUsernameResponse' -import { LoginResponse } from '../models/LoginResponse' import { LoginViaVerificationCode } from '../models/LoginViaVerificationCode' import { SendPasswordResetEmailResponse } from '../models/SendPasswordResetEmailResponse' import { UpdateUserInfosResponse } from '../models/UpdateUserInfosResponse' +import { User } from '../models/User' +import encode from '../../jwt/encode' import { ChangePasswordArgs, CheckUsernameArgs, @@ -17,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 }) @@ -27,21 +30,9 @@ export class UserResolver { throw new Error(result.data) } - // temporary solution until we have JWT implemented - return new LoginResponse(result.data) + context.setHeaders.push({ key: 'token', value: encode(result.data.session_id) }) - // create and return the json web token - // The expire doesn't help us here. The client needs to track when the token expires on its own, - // since every action prolongs the time the session is valid. - /* - return jwt.sign( - { result, role: 'todo' }, - CONFIG.JWT_SECRET, // * , { expiresIn: CONFIG.JWT_EXPIRES_IN } , - ) - */ - // return (await apiPost(CONFIG.LOGIN_API_URL + 'unsecureLogin', login)).result.data - // const loginResult: LoginResult = await loginAPI.login(data) - // return loginResult.user ? loginResult.user : new User() + return new User(result.data.user) } @Query(() => LoginViaVerificationCode) @@ -59,9 +50,10 @@ export class UserResolver { return new LoginViaVerificationCode(result.data) } + @Authorized() @Query(() => String) - async logout(@Arg('sessionId') sessionId: number): Promise { - const payload = { session_id: sessionId } + async logout(@Ctx() context: any): Promise { + const payload = { session_id: context.sessionId } const result = await apiPost(CONFIG.LOGIN_API_URL + 'logout', payload) if (!result.success) { throw new Error(result.data) @@ -115,11 +107,11 @@ export class UserResolver { return 'sucess' } + @Authorized() @Query(() => UpdateUserInfosResponse) async updateUserInfos( @Args() { - sessionId, email, firstName, lastName, @@ -129,9 +121,10 @@ export class UserResolver { password, passwordNew, }: UpdateUserInfosArgs, + @Ctx() context: any, ): Promise { const payload = { - session_id: sessionId, + session_id: context.sessionId, email, update: { 'User.first_name': firstName || undefined, diff --git a/backend/src/index.ts b/backend/src/index.ts index 6e51ed2f4..b99e5bee4 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -1,5 +1,8 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + 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' @@ -8,17 +11,31 @@ import connection from './database/connection' import CONFIG from './config' // TODO move to extern -// import { BookResolver } from './graphql/resolvers/BookResolver' import { UserResolver } from './graphql/resolvers/UserResolver' import { BalanceResolver } from './graphql/resolvers/BalanceResolver' import { GdtResolver } from './graphql/resolvers/GdtResolver' import { TransactionResolver } from './graphql/resolvers/TransactionResolver' +import { isAuthorized } from './auth/auth' + // TODO implement // import queryComplexity, { simpleEstimator, fieldConfigEstimator } from "graphql-query-complexity"; const DB_VERSION = '0001-init_db' +const context = (args: any) => { + const authorization = args.req.headers.authorization + let token = null + if (authorization) { + token = authorization.replace(/^Bearer /, '') + } + const context = { + token, + setHeaders: [], + } + return context +} + async function main() { // check for correct database version const con = await connection() @@ -34,6 +51,7 @@ async function main() { // const connection = await createConnection() const schema = await buildSchema({ resolvers: [UserResolver, BalanceResolver, TransactionResolver, GdtResolver], + authChecker: isAuthorized, }) // Graphiql interface @@ -45,8 +63,31 @@ async function main() { // Express Server const server = express() + const corsOptions = { + origin: '*', + exposedHeaders: ['token'], + } + + server.use(cors(corsOptions)) + + 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 }) + const apollo = new ApolloServer({ schema, playground, context, plugins }) apollo.applyMiddleware({ app: server }) // Start Server diff --git a/backend/src/jwt/decode.ts b/backend/src/jwt/decode.ts new file mode 100644 index 000000000..47cf62154 --- /dev/null +++ b/backend/src/jwt/decode.ts @@ -0,0 +1,20 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + +import jwt from 'jsonwebtoken' +import CONFIG from '../config/' + +export default (token: string): any => { + if (!token) return null + let sessionId = null + try { + const decoded = jwt.verify(token, CONFIG.JWT_SECRET) + sessionId = decoded.sub + return { + token, + sessionId, + } + } catch (err) { + return null + } +} diff --git a/backend/src/jwt/encode.ts b/backend/src/jwt/encode.ts new file mode 100644 index 000000000..9c5145e6d --- /dev/null +++ b/backend/src/jwt/encode.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + +import jwt from 'jsonwebtoken' +import CONFIG from '../config/' + +// Generate an Access Token +export default function encode(sessionId: string): string { + const token = jwt.sign({ sessionId }, CONFIG.JWT_SECRET, { + expiresIn: CONFIG.JWT_EXPIRES_IN, + subject: sessionId.toString(), + }) + return token +} diff --git a/community_server/src/Template/StateBalances/overview_gdt.ctp b/community_server/src/Template/StateBalances/overview_gdt.ctp index eb67626b0..f293b93be 100644 --- a/community_server/src/Template/StateBalances/overview_gdt.ctp +++ b/community_server/src/Template/StateBalances/overview_gdt.ctp @@ -21,7 +21,7 @@ $this->assign('title', __('GDT Kontoübersicht')); $header = '

' . __('Zur Verfügung: ') . '

'; if($gdtSum > 0){ - $header .= '

'.$this->element('printGDT', ['number' => $gdtSum]).'

'; + $header .= '

'.$this->element('printGDT', ['number' => $gdtSum*100.0]).'

'; } if($moreEntrysAsShown) { $header .= ''. __('Nur die letzten 100 Einträge werden angezeigt!') . ''; @@ -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']*100.0]); ?> + element('printEuro', ['number' => $entry['amount2']*100.0]) ?> +
Number->format($entry['factor']) ?> @@ -56,7 +60,7 @@ $this->assign('header', $header); Number->format($entry['factor2']) ?>
-
element('printGDT', ['number' => $entry['gdt']]) ?>
+
element('printGDT', ['number' => $entry['gdt']*100.0]) ?>
@@ -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']*100.0]); ?> + element('printEuro', ['number' => $gdtEntry['amount2']*100.0]) ?> +
Number->format($gdtEntry['factor']) ?> @@ -98,7 +106,7 @@ $this->assign('header', $header); Number->format($gdtEntry['factor2']) ?>
-
element('printGDT', ['number' => $gdtEntry['gdt']]) ?>
+
element('printGDT', ['number' => $gdtEntry['gdt'] * 100.0]) ?>
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/deployment/bare_metal/build_frontend.sh b/deployment/bare_metal/build_frontend.sh index 2e968179d..2e7e59381 100755 --- a/deployment/bare_metal/build_frontend.sh +++ b/deployment/bare_metal/build_frontend.sh @@ -3,8 +3,7 @@ # or NPM_BIN Path and NVM_DIR must be adjusted cd /var/www/html/gradido -eval "echo \"$(cat .env.local)\"" > .env -eval "echo \"$(cat .env.shell)\"" >> .env +eval "echo \"$(cat .env.shell)\"" > .env cd frontend NPM_BIN=/root/.nvm/versions/node/v12.19.0/bin/npm diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 5eb808b99..84906a5a9 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -13,7 +13,6 @@ services: environment: - NODE_ENV="development" # - DEBUG=true - - NUXT_BUILD=/tmp/nuxt # avoid file permission issues when `rm -rf .nuxt/` volumes: # This makes sure the docker container has its own node modules. # Therefore it is possible to have a different node version on the host machine diff --git a/docker-compose.yml b/docker-compose.yml index 70d36521e..8fdab9f17 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,14 +22,13 @@ services: # Envs used in Dockerfile # - DOCKER_WORKDIR="/app" # - PORT=3000 - - BUILD_DATE - - BUILD_VERSION - - BUILD_COMMIT + # - BUILD_DATE="1970-01-01T00:00:00.00Z" + # - BUILD_VERSION="0.0.0.0" + # - BUILD_COMMIT="0000000" - NODE_ENV="production" - # Application only envs - #- HOST=0.0.0.0 # This is nuxt specific, alternative value is HOST=webapp - #env_file: - # - ./frontend/.env + env_file: + - ./.env + # - ./frontend/.env ######################################################### ## MARIADB ############################################## diff --git a/docu/Concepts/Snippets/1-Blockchain/Roles.md b/docu/Concepts/Snippets/1-Blockchain/Roles.md new file mode 100644 index 000000000..d21b3814a --- /dev/null +++ b/docu/Concepts/Snippets/1-Blockchain/Roles.md @@ -0,0 +1,53 @@ +# Roles +User Roles also handled by blockchain and node servers + +My Goal is to save not only the gradido transactions in blockchain but also +who is allowed to create new gradidos, +who allow joining new user to the group, +who allow connect to another group and +how the community decide which one is allowed to do this things. + +## Why? +If this would be handled only by community-server everyone could be easly +overwrite this rules by using a modified client to send his transactions direct over +hedera or iota bypassing the community-server rules. +With hedera it is possible to only allow sending messages to a topic with the correct admin key, +but then is the admin the single point of failure. Also must the key saved on server to allow everyone +sending gradidos or the transactions will only be proccessed, when admin is logging in. +If we don't use blockchain technologie at all, we have a big single point of failure. +The Community-Server and everyone who has direct access to server and the admins of course. +But it would be much much simpler of course :) + +In summary it is to make sure that the community is in power and no one can take over. + +## How? +There is a special type of transactions with which users determine who can determine what. +This transaction control which signatures are neccessary for things like creation and so one. +For this I think different types are needed. +- *one*: The founder of group (or someone other choosen) decide everything, this is default from start +- *some*: a number of user must sign, set absolute count or relative count +- *most*: more than 1/2 or 3/4 must sign +- *all*: all member must sign, default for choose which mode will be used +- *one-council*: one member of council +- *some-council*: absolute or relative number of council members must sign +- *most-council*: more than 1/2 or 3/4 from council members must sign +- *all-council*: all members of council must sign + +this configuration can be done for different types of action, +so the voting-mode for creation may differ from voting mode for +add new members to community. Also the council members for different actions +may differ. +Also how to vote for council members is an extra type of action. + +## Veto +Especially for *some* and *some-council* maybe also for other types. +The users there could vote but haven't yet and not all need to vote, +can make a Veto with Explanation which reset all existing signs of current vote, +and is needed to sign by all which again vote for the case. + +## Summary +With that setup all community consense models should be possible except Democracy. +Democracy needs a secret ballot. The votes on blockchain are open (at least if someone knows which +public-key belongs to which user). A secret ballot on blockchain is really hard. By my last +recherche I haven't found on. But maybe this can do the trick: https://secure.vote/ + diff --git a/docu/Concepts/Snippets/Iota/ColoredCoins.md b/docu/Concepts/Snippets/Iota/ColoredCoins.md new file mode 100644 index 000000000..33d3a891e --- /dev/null +++ b/docu/Concepts/Snippets/Iota/ColoredCoins.md @@ -0,0 +1,42 @@ +# Wie Colored Coins in Iota funktionieren +## Schöpfung +- Colored Coins werden bei Iota mit einer speziellen Transaktion erzeugt +- Die Farbe des neuen Coins wird durch den Transaktionshash beschrieben +- Die einmal erzeugte Menge von Colored Coins ist fest +- Um die Menge zu erhöhen müssten neue Colored Coin erzeugt werden und die alten damit ausgetauscht werden (z.B. mittels SmartContract) + +## Geldmenge +- Colored Coins basierend auf den normalen Iota-Coins somit werden soviele Iota-Coins benötigt wie man Colored-Coins braucht +- 2.779.530.283.000.000 Iota maximale Coinmenge +- Weltbevölkerung 2021: 7.915.559.780 +- Pro Kopf Geldmenge Gradido: 53.476 +- Benötigte Iota Coins für Gradido mit 4 Nachkommastellen mit 25% Puffer: +- 7.915.559.780 * 534.760.000 * 1.25 = 5.291.155.935.000.000.000 +- Vorhandene Iota coins: 2.779.530.283.000.000 +- Es sind nicht genügend Iota Coins vorhanden im die ganze Welt mit Gradido auf Basis von Iota versorgen zu können + +## Kosten +- Kurs am 30.08.2021: 1 Miota = 0,84 +- Bei Verwendung von 4 Nachkommastellen braucht es 10 Miota für 1.000 Colored Coins (Gradido) also Miota im Wert von 8,40€ +- Aktuell (30.08.2021) geschöpfte Gradido Cent: 17.001.990.500 +- Notwendige Miota: 17.002.0, Wert: 14.286,73 € +- Solange die Benutzer Kontrolle über ihre Keys haben können sie mit einem regulärem Iota Wallet die Gradidos wieder in Iota umwandeln (um z.B. der Vergänglichkeit zu entgehen) +- Mit 2 Nachkommastellen wird die Vergänglichkeit schon bei 100 Gradido und 1 Stunde ungenau +- 1 Stunde, 100 Gradido, Vergänglichkeit: 0,00576, Gradidos nach einer Stunde: 99,99424 GDD +- 1 Minute, 100 Gradido, Vergänglichkeit: 0,000096, Gradidos nach einer Minute: 99,999904 GDD + +## Dust-Protection +- Iota erlaubt bei leeren Adressen nur Transaktionen von mindestens 1 Miota +- Nachdem 1 Miota da ist sind bis zu 10 Transaktione < 1 Miota erlaubt, bevor ein weitere Transaktion mit mindestens 1 Miota eingehen muss +- Bei Verwendung von 4 Nachkommastellen entspricht das 100 GDD, bei 2 Nachkommastellen 10.000 GDD + +## Lösung +Wir können nur 3 Nachkommastellen verwenden. +### Kosten +- 0,84 € für 1.000 GDD +- 1.428 Euro für alle bisherigen geschöpften Gradidos +### Dust-Protection +- 1.000 GDD entspricht 1 Miota, die erste Schöpfung muss also zwangsläufig 1k Gradido betragen +- Jeder kann nur maximal 10 Transaktionen < 1k GDD empfangen bevor er die GDD weitergesendet haben muss um neue erhalten zu können oder eine neue Schöpfung von 1k GDD bekommen hat + + diff --git a/docu/Concepts/Snippets/images/classes.drawio b/docu/Concepts/Snippets/images/classes.drawio new file mode 100644 index 000000000..ad4f88aa2 --- /dev/null +++ b/docu/Concepts/Snippets/images/classes.drawio @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/docu/Concepts/Snippets/images/classes.png b/docu/Concepts/Snippets/images/classes.png new file mode 100644 index 000000000..1cf30c437 Binary files /dev/null and b/docu/Concepts/Snippets/images/classes.png differ diff --git a/docu/Concepts/Snippets/images/example_blockchain.drawio b/docu/Concepts/Snippets/images/example_blockchain.drawio new file mode 100644 index 000000000..ac043b21b --- /dev/null +++ b/docu/Concepts/Snippets/images/example_blockchain.drawio @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/docu/Concepts/Snippets/images/example_blockchain.png b/docu/Concepts/Snippets/images/example_blockchain.png new file mode 100644 index 000000000..f2771b388 Binary files /dev/null and b/docu/Concepts/Snippets/images/example_blockchain.png differ diff --git a/docu/Concepts/Snippets/images/iota.drawio b/docu/Concepts/Snippets/images/iota.drawio new file mode 100644 index 000000000..5e491aefa --- /dev/null +++ b/docu/Concepts/Snippets/images/iota.drawio @@ -0,0 +1 @@ +zVlLc+MoEP41Os6U9bRz9GMym9qaSmqTqp3sjUjYYoOEF6HYmV+/DQJLCHnieJRkLjK0oNV099cP7IXLYv+Vo23+jWWYesEk23vhyguCaRLAUxKeNWEaN4QNJ1lD8lvCLfmBNXGiqTXJcGUtFIxRQbY2MWVliVNh0RDnbGcvWzNqf3WLNtgh3KaIutS/SSbyhjoLpi39D0w2ufmyn1w0bwpkFuuTVDnK2K5DCr944ZIzJppRsV9iKnVn9NLsuzzy9iAYx6U4aYNW/BOitT7c1fXdXAsnns2JYRcoFyYLkHgriSllNTBZ7HIi8O0WpZK4A3MDLRcFhZkPQ1cgLeMT5gLvOyQt4FfMCiz4MyzRb8MLrSztLWa6a1XvG33mHbXPNA1pa28OnFuFwEDr5Ih+Ekc/4NIZydgdR2WFUkFY6WiLs7rMsGQxeRcNzWwF+ZMTNZSMoaGpoyHXe8psLmGn3AZVFUltFeA9Ed8743upuc+xnq32WpFq8mwmJQj6XS0EI+m53hibebtVzay9N5gTOC3mmnjUDhWreYptfxCIb7BZpjGEMytquNbqmCMesIahcUyRIE92rBkykf7CDSMg8TFnCKKekZvj6E3d0NDj44c9r7roMWp04DBSDnM49Wk+NHNR9td8dbW6/uwFCSokYihoaYEoga8b0kYczNZxNsCMsN2L44r8QA9qgbT0VoqsDhEvvHgl+daCVU2a8ZvPbEoYU7yWrCQQCUT/uSYLJr9eAaJJubmTk9WnaBwkB7OoB+WZC2V/wHmCEaBseHTMQEklcKnyKzxeYZREvXrgMFIUi5FeWj5U8qcgFFeClZDRg0tI2WvCCwiefbuaxLPlLMVV9XJgfUDp40aF4utaUFIa646RknrYCEI34gZDZhoj4poA+ysR97Rg91sHtrCf308NbFEQfp5Mkwswmnr6w6YcP8wFoWO4GzCUDCJtMVEddXwQAPMS0VvBuCpQPxAA8cv+7ydvVJMF0Xv5/0CyNzb8TTDhJOnpudm+Xza8IQzcnsPki9w3+eKfGlY8Um8ZevOIy8zRrADm7SJD3BrCn6woVJbJCIbnN8gTgJRKfkyW6ZMrJpDsHykmaQ6DEqmfDBfS9GoPYKAC78FSN7uaZ4odQEgeAJhzxYsfNh2SlxcsFZEfOD3Ai0Z+QTaSXY7kkzVrHo2ozWJUK0kKpBplpAQLLzun3raHfrHgqQRnj3jJKJPFbSmlCxdrQmmPpEsYIMROQRMca10YaGhNFbRykkn9QIVlNTxjxJfIdsckdPPrJHZh5ffd9qwA45ajxhCgo9LSfvJfLXv1RlOfqkZVc+lajCtjmgXGfGvpEouldIF/cSZNrxtKGFk95WSHaZpj5XfSl1tHcz1LOWrHWRoxj/jLGd2pY7gB8x4vluLYTtzRicnCH6OoDd2i9tXZwvSnbU9633kz3J8OpokXU445sZVyZh+ZYXqZPj67nZzYjKK3aydNRfczk787CCIbBLE/AILZgBnH6BjCETqGzn3L4Xrl3uvergzftZyJg5mLg/BDK60eDpJ+bDoXB3G/YhsRB0P9BmSlkqm8s8w5K0kqHeByxKLCXJ6kuFSXa871SQFVAz3WvgxVEr8EvCjqV8ixC7zJT/zmFcCDaXt13xis/f8j/PI/ \ No newline at end of file diff --git a/docu/Concepts/Snippets/images/iota.png b/docu/Concepts/Snippets/images/iota.png new file mode 100644 index 000000000..b8f7fb7e5 Binary files /dev/null and b/docu/Concepts/Snippets/images/iota.png differ diff --git a/frontend/.env.dist b/frontend/.env.dist index a22b31307..1252faf5b 100644 --- a/frontend/.env.dist +++ b/frontend/.env.dist @@ -2,3 +2,4 @@ LOGIN_API_URL=http://localhost/login_api/ COMMUNITY_API_URL=http://localhost/api/ ALLOW_REGISTER=true GRAPHQL_URI=http://localhost:4000/graphql +//BUILD_COMMIT=0000000 \ No newline at end of file diff --git a/frontend/src/components/LanguageSwitch.spec.js b/frontend/src/components/LanguageSwitch.spec.js index 72771bd37..afe98b357 100644 --- a/frontend/src/components/LanguageSwitch.spec.js +++ b/frontend/src/components/LanguageSwitch.spec.js @@ -15,7 +15,6 @@ describe('LanguageSwitch', () => { let wrapper const state = { - sessionId: 1234, email: 'he@ho.he', language: null, } @@ -123,7 +122,6 @@ describe('LanguageSwitch', () => { expect(updateUserInfosQueryMock).toBeCalledWith( expect.objectContaining({ variables: { - sessionId: 1234, email: 'he@ho.he', locale: 'en', }, @@ -136,7 +134,6 @@ describe('LanguageSwitch', () => { expect(updateUserInfosQueryMock).toBeCalledWith( expect.objectContaining({ variables: { - sessionId: 1234, email: 'he@ho.he', locale: 'de', }, diff --git a/frontend/src/components/LanguageSwitch.vue b/frontend/src/components/LanguageSwitch.vue index e6f9c86c3..f0528a935 100644 --- a/frontend/src/components/LanguageSwitch.vue +++ b/frontend/src/components/LanguageSwitch.vue @@ -32,13 +32,13 @@ export default { localeChanged(locale) }, async saveLocale(locale) { + // if (this.$i18n.locale === locale) return this.setLocale(locale) - if (this.$store.state.sessionId && this.$store.state.email) { + if (this.$store.state.email) { this.$apollo .query({ query: updateUserInfos, variables: { - sessionId: this.$store.state.sessionId, email: this.$store.state.email, locale: locale, }, diff --git a/frontend/src/components/Transaction.spec.js b/frontend/src/components/Transaction.spec.js new file mode 100644 index 000000000..43dc44782 --- /dev/null +++ b/frontend/src/components/Transaction.spec.js @@ -0,0 +1,31 @@ +import { mount } from '@vue/test-utils' +import Transaction from './Transaction' + +const localVue = global.localVue + +describe('Transaction', () => { + let wrapper + + const mocks = { + $i18n: { + locale: 'en', + }, + $t: jest.fn((t) => t), + $n: jest.fn((n) => n), + $d: jest.fn((d) => d), + } + + const Wrapper = () => { + return mount(Transaction, { localVue, mocks }) + } + + describe('mount', () => { + beforeEach(() => { + wrapper = Wrapper() + }) + + it('renders the component', () => { + expect(wrapper.find('div.gdt-transaction-list-item').exists()).toBeTruthy() + }) + }) +}) diff --git a/frontend/src/components/Transaction.vue b/frontend/src/components/Transaction.vue new file mode 100644 index 000000000..03b559463 --- /dev/null +++ b/frontend/src/components/Transaction.vue @@ -0,0 +1,124 @@ + + diff --git a/frontend/src/components/TransactionCollapse.spec.js b/frontend/src/components/TransactionCollapse.spec.js new file mode 100644 index 000000000..f27c3ae66 --- /dev/null +++ b/frontend/src/components/TransactionCollapse.spec.js @@ -0,0 +1,152 @@ +import { mount } from '@vue/test-utils' +import TransactionCollapse from './TransactionCollapse' + +const localVue = global.localVue + +describe('TransactionCollapse', () => { + let wrapper + + const mocks = { + $t: jest.fn((t) => t), + $n: jest.fn((n) => n), + } + + const Wrapper = (propsData) => { + return mount(TransactionCollapse, { localVue, mocks, propsData }) + } + + describe('mount with gdtEntryType: 1', () => { + beforeEach(() => { + const propsData = { + amount: 100, + gdt: 110, + factor: 22, + gdtEntryType: 1, + } + + wrapper = Wrapper(propsData) + }) + + it('renders the component', () => { + expect(wrapper.find('div.gdt-transaction-collapse').exists()).toBeTruthy() + }) + + it('checks the prop gdtEntryType ', () => { + expect(wrapper.props().gdtEntryType).toBe(1) + }) + + it('renders the component collapse-header', () => { + expect(wrapper.find('.gdt-list-collapse-header-text')).toBeTruthy() + }) + + it('renders the component collapse-headline', () => { + expect(wrapper.find('#collapse-headline').text()).toBe('gdt.calculation') + }) + + it('renders the component collapse-first', () => { + expect(wrapper.find('#collapse-first').text()).toBe('gdt.factor') + }) + + it('renders the component collapse-second', () => { + expect(wrapper.find('#collapse-second').text()).toBe('gdt.formula') + }) + + it('renders the component collapse-firstMath', () => { + expect(wrapper.find('#collapse-firstMath').text()).toBe('22 GDT pro €') + }) + + it('renders the component collapse-secondMath', () => { + expect(wrapper.find('#collapse-secondMath').text()).toBe('100 € * 22 GDT / € = 110 GDT') + }) + }) + + describe('mount with gdtEntryType: 7', () => { + beforeEach(() => { + const propsData = { + amount: 100, + gdt: 2200, + factor: 22, + gdtEntryType: 7, + } + + wrapper = Wrapper(propsData) + }) + + it('renders the component', () => { + expect(wrapper.find('div.gdt-transaction-collapse').exists()).toBeTruthy() + }) + + it('checks the prop gdtEntryType ', () => { + expect(wrapper.props().gdtEntryType).toBe(7) + }) + + it('renders the component collapse-header', () => { + expect(wrapper.find('.gdt-list-collapse-header-text')).toBeTruthy() + }) + + it('renders the component collapse-headline', () => { + expect(wrapper.find('#collapse-headline').text()).toBe('gdt.conversion-gdt-euro') + }) + + it('renders the component collapse-first', () => { + expect(wrapper.find('#collapse-first').text()).toBe('gdt.raise') + }) + + it('renders the component collapse-second', () => { + expect(wrapper.find('#collapse-second').text()).toBe('gdt.conversion') + }) + + it('renders the component collapse-firstMath', () => { + expect(wrapper.find('#collapse-firstMath').text()).toBe('2200 %') + }) + + it('renders the component collapse-secondMath', () => { + expect(wrapper.find('#collapse-secondMath').text()).toBe('100 GDT * 2200 % = 2200 GDT') + }) + }) + + describe('mount with gdtEntryType: 4', () => { + beforeEach(() => { + const propsData = { + amount: 100, + gdt: 2200, + factor: 22, + gdtEntryType: 4, + } + + wrapper = Wrapper(propsData) + }) + + it('renders the component', () => { + expect(wrapper.find('div.gdt-transaction-collapse').exists()).toBeTruthy() + }) + + it('checks the prop gdtEntryType ', () => { + expect(wrapper.props().gdtEntryType).toBe(4) + }) + + it('renders the component collapse-header', () => { + expect(wrapper.find('.gdt-list-collapse-header-text')).toBeTruthy() + }) + + it('renders the component collapse-headline', () => { + expect(wrapper.find('#collapse-headline').text()).toBe('gdt.publisher') + }) + + it('renders the component collapse-first', () => { + expect(wrapper.find('#collapse-first').text()).toBe('') + }) + + it('renders the component collapse-second', () => { + expect(wrapper.find('#collapse-second').text()).toBe('') + }) + + it('renders the component collapse-firstMath', () => { + expect(wrapper.find('#collapse-firstMath').text()).toBe('') + }) + + it('renders the component collapse-secondMath', () => { + expect(wrapper.find('#collapse-secondMath').text()).toBe('') + }) + }) +}) diff --git a/frontend/src/components/TransactionCollapse.vue b/frontend/src/components/TransactionCollapse.vue new file mode 100644 index 000000000..854282a43 --- /dev/null +++ b/frontend/src/components/TransactionCollapse.vue @@ -0,0 +1,76 @@ + + diff --git a/frontend/src/config/index.js b/frontend/src/config/index.js index 878ed3c55..e372a033f 100644 --- a/frontend/src/config/index.js +++ b/frontend/src/config/index.js @@ -1,13 +1,20 @@ -// ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env) +// ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env). +// The whole contents is exposed to the client // Load Package Details for some default values const pkg = require('../../package') +const version = { + APP_VERSION: pkg.version, + BUILD_COMMIT: process.env.BUILD_COMMIT || null, + // self reference of `version.BUILD_COMMIT` is not possible at this point, hence the duplicate code + BUILD_COMMIT_SHORT: (process.env.BUILD_COMMIT || '0000000').substr(0, 7), +} + const environment = { NODE_ENV: process.env.NODE_ENV, DEBUG: process.env.NODE_ENV !== 'production' || false, PRODUCTION: process.env.NODE_ENV === 'production' || false, - ALLOW_REGISTER: process.env.ALLOW_REGISTER !== 'false', } const server = { @@ -16,13 +23,15 @@ const server = { GRAPHQL_URI: process.env.GRAPHQL_URI || 'http://localhost:4000/graphql', } -// eslint-disable-next-line no-console -console.log('hash: %o', process.env.VUE_APP_BUILD_COMMIT) +const options = { + ALLOW_REGISTER: process.env.ALLOW_REGISTER !== 'false', +} const CONFIG = { + ...version, ...environment, ...server, - APP_VERSION: pkg.version, + ...options, COMMIT_HASH: process.env.VUE_APP_BUILD_COMMIT === 'undefined' ? '00000000' diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js index 3afe807f4..7414414b2 100644 --- a/frontend/src/graphql/queries.js +++ b/frontend/src/graphql/queries.js @@ -3,22 +3,19 @@ import gql from 'graphql-tag' export const login = gql` query($email: String!, $password: String!) { login(email: $email, password: $password) { - sessionId - user { - email - firstName - lastName - language - username - description - } + email + username + firstName + lastName + language + description } } ` export const logout = gql` - query($sessionId: Float!) { - logout(sessionId: $sessionId) + query { + logout } ` @@ -39,7 +36,6 @@ export const loginViaEmailVerificationCode = gql` export const updateUserInfos = gql` query( - $sessionId: Float! $email: String! $firstName: String $lastName: String @@ -50,7 +46,6 @@ export const updateUserInfos = gql` $locale: String ) { updateUserInfos( - sessionId: $sessionId email: $email firstName: $firstName lastName: $lastName @@ -66,8 +61,8 @@ export const updateUserInfos = gql` ` export const transactionsQuery = gql` - query($sessionId: Float!, $firstPage: Int = 1, $items: Int = 25, $order: String = "DESC") { - transactionList(sessionId: $sessionId, firstPage: $firstPage, items: $items, order: $order) { + query($firstPage: Int = 1, $items: Int = 25, $order: String = "DESC") { + transactionList(firstPage: $firstPage, items: $items, order: $order) { gdtSum count balance @@ -103,8 +98,8 @@ export const resgisterUserQuery = gql` ` export const sendCoins = gql` - query($sessionId: Float!, $email: String!, $amount: Float!, $memo: String!) { - sendCoins(sessionId: $sessionId, email: $email, amount: $amount, memo: $memo) + query($email: String!, $amount: Float!, $memo: String!) { + sendCoins(email: $email, amount: $amount, memo: $memo) } ` @@ -125,10 +120,11 @@ export const checkUsername = gql` ` export const listGDTEntriesQuery = gql` - query($currentPage: Int!, $pageSize: Int!, $sessionId: Float!) { - listGDTEntries(currentPage: $currentPage, pageSize: $pageSize, sessionId: $sessionId) { + query($currentPage: Int!, $pageSize: Int!) { + listGDTEntries(currentPage: $currentPage, pageSize: $pageSize) { count gdtEntries { + id amount date comment diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index 5db4bc337..bc46aeac8 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -18,6 +18,7 @@ "de": "Deutsch", "en": "English" }, + "select_language": "Bitte wähle eine Sprache für die App und Newsletter", "decay": { "decay": "Vergänglichkeit", "decay_since_last_transaction":"Vergänglichkeit seit der letzten Transaktion", @@ -136,15 +137,6 @@ "send_gradido":"Gradido versenden", "add_work":"neuer Gemeinschaftsbeitrag" }, - "profil": { - "activity": { - "new":"Neue Gemeinschaftsstunden eintragen", - "list":"Meine Gemeinschaftsstunden Liste" - }, - "user-data": { - "change-success": "Deine Daten wurden gespeichert." - } - }, "navbar" : { "my-profil":"Mein Profil", "settings":"Einstellungen", @@ -182,8 +174,8 @@ "formula":"Berechungsformel", "no-transactions":"Du hast zur Zeit keine Transaktionen", "publisher":"Dein geworbenes Mitglied hat einen Beitrag bezahlt", - "gdt-receive":"Aktion", - "your-share":"Geworbenes Mitglied", + "action":"Aktion", + "recruited-member":"Geworbenes Mitglied", "contribution":"Beitrag" } } diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 323f96b4d..7dcfd5dec 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -18,6 +18,7 @@ "de": "Deutsch", "en": "English" }, + "select_language": "Please choose a language for the app and newsletter", "decay": { "decay": "Decay", "decay_since_last_transaction":"Decay since the last transaction", @@ -136,16 +137,6 @@ "send_gradido":"Send Gradido", "add_work":"New Community Contribution" }, - "profil": { - "transactions":"transactions", - "activity": { - "new":"Register new community hours", - "list":"My Community Hours List" - }, - "user-data": { - "change-success": "Your data has been saved." - } - }, "navbar" : { "my-profil":"My profile", "settings":"Settings", @@ -183,8 +174,8 @@ "formula": "Calculation formula", "no-transactions":"You currently have no transactions", "publisher":"A member you referred has paid a contribution", - "gdt-receive":"GDT receive", - "your-share":"Your share", + "action":"Action", + "recruited-member":"Recruited Member", "contribution":"Contribution" } } diff --git a/frontend/src/main.js b/frontend/src/main.js index 31ccd8de7..0b0e98e2d 100755 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -3,7 +3,7 @@ import DashboardPlugin from './plugins/dashboard-plugin' import App from './App.vue' import i18n from './i18n.js' import { loadAllRules } from './validation-rules' -import ApolloClient from 'apollo-boost' +import { ApolloClient, ApolloLink, InMemoryCache, HttpLink } from 'apollo-boost' import VueApollo from 'vue-apollo' import CONFIG from './config' @@ -11,7 +11,25 @@ import { store } from './store/store' import router from './routes/router' +const httpLink = new HttpLink({ uri: CONFIG.GRAPHQL_URI }) + +const authLink = new ApolloLink((operation, forward) => { + const token = store.state.token + operation.setContext({ + headers: { + Authorization: token && token.length > 0 ? `Bearer ${token}` : '', + }, + }) + 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({ + link: authLink.concat(httpLink), + cache: new InMemoryCache(), uri: CONFIG.GRAPHQL_URI, }) @@ -26,7 +44,7 @@ Vue.config.productionTip = false loadAllRules(i18n) router.beforeEach((to, from, next) => { - if (to.meta.requiresAuth && !store.state.sessionId) { + if (to.meta.requiresAuth && !store.state.token) { next({ path: '/login' }) } else { next() diff --git a/frontend/src/store/store.js b/frontend/src/store/store.js index 03be5e75c..00f8369d2 100644 --- a/frontend/src/store/store.js +++ b/frontend/src/store/store.js @@ -1,6 +1,7 @@ import Vue from 'vue' import Vuex from 'vuex' import createPersistedState from 'vuex-persistedstate' + Vue.use(Vuex) export const mutations = { @@ -10,9 +11,6 @@ export const mutations = { email: (state, email) => { state.email = email }, - sessionId: (state, sessionId) => { - state.sessionId = sessionId - }, username: (state, username) => { state.username = username }, @@ -25,43 +23,45 @@ export const mutations = { description: (state, description) => { state.description = description }, + token: (state, token) => { + state.token = token + }, } export const actions = { login: ({ dispatch, commit }, data) => { - commit('sessionId', data.sessionId) - commit('email', data.user.email) - commit('language', data.user.language) - commit('username', data.user.username) - commit('firstName', data.user.firstName) - commit('lastName', data.user.lastName) - commit('description', data.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('sessionId', null) + commit('token', null) commit('email', null) commit('username', '') commit('firstName', '') commit('lastName', '') commit('description', '') - sessionStorage.clear() + localStorage.clear() }, } export const store = new Vuex.Store({ plugins: [ createPersistedState({ - storage: window.sessionStorage, + storage: window.localStorage, }), ], state: { - sessionId: null, email: '', language: null, firstName: '', lastName: '', username: '', description: '', + token: null, }, getters: {}, // Syncronous mutation of the state diff --git a/frontend/src/store/store.test.js b/frontend/src/store/store.test.js index a67dff5ce..c067a6e49 100644 --- a/frontend/src/store/store.test.js +++ b/frontend/src/store/store.test.js @@ -1,6 +1,6 @@ import { mutations, actions } from './store' -const { language, email, sessionId, username, firstName, lastName, description } = mutations +const { language, email, token, username, firstName, lastName, description } = mutations const { login, logout } = actions describe('Vuex store', () => { @@ -21,11 +21,11 @@ describe('Vuex store', () => { }) }) - describe('sessionId', () => { - it('sets the state of sessionId', () => { - const state = { sessionId: null } - sessionId(state, '1234') - expect(state.sessionId).toEqual('1234') + describe('token', () => { + it('sets the state of token', () => { + const state = { token: null } + token(state, '1234') + expect(state.token).toEqual('1234') }) }) @@ -67,55 +67,47 @@ describe('Vuex store', () => { const commit = jest.fn() const state = {} const commitedData = { - sessionId: 1234, - user: { - email: 'someone@there.is', - language: 'en', - username: 'user', - 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 sessionId', () => { - login({ commit, state }, commitedData) - expect(commit).toHaveBeenNthCalledWith(1, 'sessionId', 1234) + expect(commit).toHaveBeenCalledTimes(6) }) it('commits email', () => { login({ commit, state }, commitedData) - expect(commit).toHaveBeenNthCalledWith(2, 'email', 'someone@there.is') + expect(commit).toHaveBeenNthCalledWith(1, 'email', 'user@example.org') }) it('commits language', () => { login({ commit, state }, commitedData) - expect(commit).toHaveBeenNthCalledWith(3, 'language', 'en') + expect(commit).toHaveBeenNthCalledWith(2, 'language', 'de') }) it('commits username', () => { login({ commit, state }, commitedData) - expect(commit).toHaveBeenNthCalledWith(4, 'username', 'user') + 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') }) }) @@ -128,9 +120,9 @@ describe('Vuex store', () => { expect(commit).toHaveBeenCalledTimes(6) }) - it('commits sessionId', () => { + it('commits token', () => { logout({ commit, state }) - expect(commit).toHaveBeenNthCalledWith(1, 'sessionId', null) + expect(commit).toHaveBeenNthCalledWith(1, 'token', null) }) it('commits email', () => { @@ -159,7 +151,7 @@ describe('Vuex store', () => { }) // how to get this working? - it.skip('calls sessionStorage.clear()', () => { + it.skip('calls localStorage.clear()', () => { const clearStorageMock = jest.fn() global.sessionStorage = jest.fn(() => { return { diff --git a/frontend/src/views/Layout/ContentFooter.vue b/frontend/src/views/Layout/ContentFooter.vue index c18608545..4350e3f55 100755 --- a/frontend/src/views/Layout/ContentFooter.vue +++ b/frontend/src/views/Layout/ContentFooter.vue @@ -16,11 +16,11 @@ App version {{ version }} - {{ shortHash }} + ({{ shortHash }}) @@ -66,8 +66,8 @@ export default { return { year: new Date().getFullYear(), version: CONFIG.APP_VERSION, - hash: CONFIG.COMMIT_HASH, - shortHash: CONFIG.COMMIT_HASH.substr(0, 8), + hash: CONFIG.BUILD_COMMIT, + shortHash: CONFIG.BUILD_COMMIT_SHORT, } }, } diff --git a/frontend/src/views/Layout/DashboardLayout_gdd.spec.js b/frontend/src/views/Layout/DashboardLayout_gdd.spec.js index f0f08f6a2..614ddf9c4 100644 --- a/frontend/src/views/Layout/DashboardLayout_gdd.spec.js +++ b/frontend/src/views/Layout/DashboardLayout_gdd.spec.js @@ -41,7 +41,6 @@ describe('DashboardLayoutGdd', () => { }, $store: { state: { - sessionId: 1, email: 'user@example.org', }, dispatch: storeDispatchMock, @@ -128,16 +127,17 @@ describe('DashboardLayoutGdd', () => { describe('logout', () => { beforeEach(async () => { + await apolloMock.mockResolvedValue({ + data: { + logout: 'success', + }, + }) await wrapper.findComponent({ name: 'sidebar' }).vm.$emit('logout') await flushPromises() }) it('calls the API', async () => { - expect(apolloMock).toBeCalledWith( - expect.objectContaining({ - variables: { sessionId: 1 }, - }), - ) + await expect(apolloMock).toBeCalled() }) it('dispatches logout to store', () => { @@ -196,7 +196,6 @@ describe('DashboardLayoutGdd', () => { expect(apolloMock).toBeCalledWith( expect.objectContaining({ variables: { - sessionId: 1, firstPage: 2, items: 5, }, diff --git a/frontend/src/views/Layout/DashboardLayout_gdd.vue b/frontend/src/views/Layout/DashboardLayout_gdd.vue index 4081f4ceb..35e7bb8af 100755 --- a/frontend/src/views/Layout/DashboardLayout_gdd.vue +++ b/frontend/src/views/Layout/DashboardLayout_gdd.vue @@ -92,7 +92,6 @@ export default { this.$apollo .query({ query: logout, - variables: { sessionId: this.$store.state.sessionId }, }) .then(() => { this.$sidebar.displaySidebar(false) @@ -111,7 +110,6 @@ export default { .query({ query: transactionsQuery, variables: { - sessionId: this.$store.state.sessionId, firstPage: pagination.firstPage, items: pagination.items, }, diff --git a/frontend/src/views/Pages/AccountOverview.spec.js b/frontend/src/views/Pages/AccountOverview.spec.js index b890f1471..df964ed2c 100644 --- a/frontend/src/views/Pages/AccountOverview.spec.js +++ b/frontend/src/views/Pages/AccountOverview.spec.js @@ -16,12 +16,12 @@ describe('AccountOverview', () => { const mocks = { $t: jest.fn((t) => t), + $n: jest.fn((n) => String(n)), $store: { state: { - sessionId: 1, + email: 'sender@example.org', }, }, - $n: jest.fn((n) => String(n)), $apollo: { query: sendMock, }, @@ -93,7 +93,6 @@ describe('AccountOverview', () => { expect(sendMock).toBeCalledWith( expect.objectContaining({ variables: { - sessionId: 1, email: 'user@example.org', amount: 23.45, memo: 'Make the best of it!', diff --git a/frontend/src/views/Pages/AccountOverview.vue b/frontend/src/views/Pages/AccountOverview.vue index b89955a6a..8e5362d8c 100644 --- a/frontend/src/views/Pages/AccountOverview.vue +++ b/frontend/src/views/Pages/AccountOverview.vue @@ -107,10 +107,7 @@ export default { this.$apollo .query({ query: sendCoins, - variables: { - sessionId: this.$store.state.sessionId, - ...this.transactionData, - }, + variables: this.transactionData, }) .then(() => { this.error = false diff --git a/frontend/src/views/Pages/AccountOverview/GddSend.spec.js b/frontend/src/views/Pages/AccountOverview/GddSend.spec.js index c7005ddf5..15beac511 100644 --- a/frontend/src/views/Pages/AccountOverview/GddSend.spec.js +++ b/frontend/src/views/Pages/AccountOverview/GddSend.spec.js @@ -8,11 +8,6 @@ describe('GddSend', () => { const mocks = { $t: jest.fn((t) => t), - $store: { - state: { - sessionId: 1234, - }, - }, $i18n: { locale: jest.fn(() => 'en'), }, diff --git a/frontend/src/views/Pages/AccountOverview/GdtTransactionList.spec.js b/frontend/src/views/Pages/AccountOverview/GdtTransactionList.spec.js index 5ad12be97..a3c737d10 100644 --- a/frontend/src/views/Pages/AccountOverview/GdtTransactionList.spec.js +++ b/frontend/src/views/Pages/AccountOverview/GdtTransactionList.spec.js @@ -57,11 +57,6 @@ describe('GdtTransactionList', () => { $t: jest.fn((t) => t), $n: jest.fn((n) => n), $d: jest.fn((d) => d), - $store: { - state: { - sessionId: 1, - }, - }, $toasted: { error: toastErrorMock, }, @@ -89,7 +84,6 @@ describe('GdtTransactionList', () => { expect(apolloMock).toBeCalledWith( expect.objectContaining({ variables: { - sessionId: 1, currentPage: 1, pageSize: 25, }, diff --git a/frontend/src/views/Pages/AccountOverview/GdtTransactionList.vue b/frontend/src/views/Pages/AccountOverview/GdtTransactionList.vue index 8193717cf..1ad4ba13e 100644 --- a/frontend/src/views/Pages/AccountOverview/GdtTransactionList.vue +++ b/frontend/src/views/Pages/AccountOverview/GdtTransactionList.vue @@ -17,134 +17,14 @@ } in transactionsGdt" :key="transactionId" > -
- -
- -
- - -
- - i - -
- - - - - -
-
{{ $t('gdt.gdt-receive') }}
-
{{ $t('gdt.credit') }}
-
-
-
{{ comment }}
-
{{ $n(gdt, 'decimal') }} GDT
-
-
- - -
-
{{ $t('gdt.your-share') }}
-
{{ $t('gdt.credit') }}
-
-
-
5%
-
{{ $n(amount, 'decimal') }} GDT
-
-
- - -
-
{{ $t('gdt.contribution') }}
-
{{ $t('gdt.credit') }}
-
-
-
{{ $n(amount, 'decimal') }} €
-
{{ $n(gdt, 'decimal') }} GDT
-
-
- - - - - -
- {{ $t('form.memo') }} -
-
- {{ comment }} -
-
- - - -
- {{ $t('form.date') }} -
-
- {{ $d($moment(date), 'long') }} {{ $i18n.locale === 'de' ? 'Uhr' : '' }} -
-
-
- - - - -
- - -
- {{ $t('gdt.conversion-gdt-euro') }} -
-
- {{ $t('gdt.publisher') }} -
-
{{ $t('gdt.calculation') }}
-
- - - -
-
{{ $t('gdt.raise') }}
-
{{ $t('gdt.conversion') }}
-
-
-
{{ factor * 100 }} %
-
- {{ $n(amount, 'decimal') }} GDT * {{ factor * 100 }} % = - {{ $n(gdt, 'decimal') }} GDT -
-
-
- - -
-
-
- - - -
-
{{ $t('gdt.factor') }}
-
{{ $t('gdt.formula') }}
-
-
-
{{ factor }} GDT pro €
-
- {{ $n(amount, 'decimal') }} € * {{ factor }} GDT / € = - {{ $n(gdt, 'decimal') }} GDT -
-
-
-
-
- + import { listGDTEntriesQuery } from '../../../graphql/queries' import PaginationButtons from '../../../components/PaginationButtons' - -const iconsByType = { - 1: { icon: 'heart', classes: 'gradido-global-color-accent' }, - 4: { icon: 'person-check', classes: 'gradido-global-color-accent' }, - 7: { icon: 'gift', classes: 'gradido-global-color-accent' }, -} +import Transaction from '../../../components/Transaction.vue' export default { name: 'gdt-transaction-list', components: { PaginationButtons, + Transaction, }, data() { return { @@ -199,7 +75,6 @@ export default { .query({ query: listGDTEntriesQuery, variables: { - sessionId: this.$store.state.sessionId, currentPage: this.currentPage, pageSize: this.pageSize, }, @@ -215,18 +90,6 @@ export default { this.$toasted.error(error.message) }) }, - getIcon(givenType) { - const type = iconsByType[givenType] - if (type) - return { - icon: type.icon, - class: type.classes + ' m-mb-1 font2em', - } - this.throwError('no icon to given type: ' + givenType) - }, - throwError(msg) { - throw new Error(msg) - }, showNext() { this.currentPage++ this.updateGdt() diff --git a/frontend/src/views/Pages/Login.spec.js b/frontend/src/views/Pages/Login.spec.js index 7cbe5f003..20eead7a3 100644 --- a/frontend/src/views/Pages/Login.spec.js +++ b/frontend/src/views/Pages/Login.spec.js @@ -6,12 +6,7 @@ const localVue = global.localVue const loginQueryMock = jest.fn().mockResolvedValue({ data: { - login: { - sessionId: 1, - user: { - name: 'Peter Lustig', - }, - }, + login: 'token', }, }) @@ -159,10 +154,7 @@ describe('Login', () => { describe('login success', () => { it('dispatches server response to store', () => { - expect(mockStoreDispach).toBeCalledWith('login', { - sessionId: 1, - user: { name: 'Peter Lustig' }, - }) + expect(mockStoreDispach).toBeCalledWith('login', 'token') }) it('redirects to overview page', () => { diff --git a/frontend/src/views/Pages/Register.spec.js b/frontend/src/views/Pages/Register.spec.js index 34a7390c5..72042477c 100644 --- a/frontend/src/views/Pages/Register.spec.js +++ b/frontend/src/views/Pages/Register.spec.js @@ -80,6 +80,13 @@ describe('Register', () => { it('has password repeat input fields', () => { expect(wrapper.find('input[name="form.passwordRepeat"]').exists()).toBeTruthy() }) + it('has Language selected field', () => { + expect(wrapper.find('#selectedLanguage').exists()).toBeTruthy() + }) + it('selected Language value de', async () => { + wrapper.find('#selectedLanguage').findAll('option').at(1).setSelected() + expect(wrapper.find('#selectedLanguage').element.value).toBe('de') + }) it('has 1 checkbox input fields', () => { expect(wrapper.find('#registerCheckbox').exists()).toBeTruthy() @@ -121,9 +128,16 @@ describe('Register', () => { wrapper.find('#Email-input-field').setValue('max.mustermann@gradido.net') wrapper.find('input[name="form.password"]').setValue('Aa123456') wrapper.find('input[name="form.passwordRepeat"]').setValue('Aa123456') + wrapper.find('#selectedLanguage').findAll('option').at(1).setSelected() wrapper.find('input[name="site.signup.agree"]').setChecked(true) }) + it('reset selected value language', async () => { + await wrapper.find('button.ml-2').trigger('click') + await flushPromises() + expect(wrapper.find('#selectedLanguage').element.value).toBe('') + }) + it('resets the firstName field after clicking the reset button', async () => { await wrapper.find('button.ml-2').trigger('click') await flushPromises() @@ -168,6 +182,7 @@ describe('Register', () => { wrapper.find('#Email-input-field').setValue('max.mustermann@gradido.net') wrapper.find('input[name="form.password"]').setValue('Aa123456') wrapper.find('input[name="form.passwordRepeat"]').setValue('Aa123456') + wrapper.find('#selectedLanguage').findAll('option').at(1).setSelected() }) describe('server sends back error', () => { @@ -214,6 +229,7 @@ describe('Register', () => { firstName: 'Max', lastName: 'Mustermann', password: 'Aa123456', + language: 'de', }, }), ) diff --git a/frontend/src/views/Pages/Register.vue b/frontend/src/views/Pages/Register.vue index 933cae028..f8e7eefd0 100755 --- a/frontend/src/views/Pages/Register.vue +++ b/frontend/src/views/Pages/Register.vue @@ -84,6 +84,18 @@ :register="register" > + + + {{ $t('language') }} + + + + -
+
{{ $t('form.reset') }} {{ $t('signup') }} @@ -147,6 +162,12 @@ export default { passwordRepeat: '', }, }, + selected: null, + options: [ + { value: null, text: this.$t('select_language') }, + { value: 'de', text: this.$t('languages.de') }, + { value: 'en', text: this.$t('languages.en') }, + ], submitted: false, showError: false, messageError: '', @@ -168,8 +189,7 @@ export default { }, agree: false, } - this.form.password.password = '' - this.form.password.passwordRepeat = '' + this.selected = null this.$nextTick(() => { this.$refs.observer.reset() }) @@ -183,6 +203,7 @@ export default { firstName: this.form.firstname, lastName: this.form.lastname, password: this.form.password.password, + language: this.selected, }, }) .then(() => { @@ -191,6 +212,7 @@ export default { this.form.lastname = '' this.form.password.password = '' this.form.password.passwordRepeat = '' + this.selected = null this.$router.push('/thx/register') }) .catch((error) => { @@ -206,6 +228,7 @@ export default { this.form.lastname = '' this.form.password.password = '' this.form.password.passwordRepeat = '' + this.selected = null }, }, computed: { @@ -220,6 +243,9 @@ export default { emailFilled() { return this.form.email !== '' }, + languageFilled() { + return this.selected !== null + }, }, } diff --git a/frontend/src/views/Pages/UserProfile/UserCard_FormUserData.spec.js b/frontend/src/views/Pages/UserProfile/UserCard_FormUserData.spec.js index 847185741..b3e014e57 100644 --- a/frontend/src/views/Pages/UserProfile/UserCard_FormUserData.spec.js +++ b/frontend/src/views/Pages/UserProfile/UserCard_FormUserData.spec.js @@ -17,7 +17,6 @@ describe('UserCard_FormUserData', () => { $t: jest.fn((t) => t), $store: { state: { - sessionId: 1, email: 'user@example.org', firstName: 'Peter', lastName: 'Lustig', @@ -118,7 +117,6 @@ describe('UserCard_FormUserData', () => { expect(mockAPIcall).toBeCalledWith( expect.objectContaining({ variables: { - sessionId: 1, email: 'user@example.org', firstName: 'Petra', lastName: 'Lustiger', @@ -167,7 +165,6 @@ describe('UserCard_FormUserData', () => { expect(mockAPIcall).toBeCalledWith( expect.objectContaining({ variables: { - sessionId: 1, email: 'user@example.org', firstName: 'Petra', lastName: 'Lustiger', diff --git a/frontend/src/views/Pages/UserProfile/UserCard_FormUserData.vue b/frontend/src/views/Pages/UserProfile/UserCard_FormUserData.vue index 369d30074..3726b5298 100644 --- a/frontend/src/views/Pages/UserProfile/UserCard_FormUserData.vue +++ b/frontend/src/views/Pages/UserProfile/UserCard_FormUserData.vue @@ -85,7 +85,6 @@ export default { data() { return { showUserData: true, - sessionId: this.$store.state.sessionId, form: { firstName: this.$store.state.firstName, lastName: this.$store.state.lastName, @@ -118,7 +117,6 @@ export default { .query({ query: updateUserInfos, variables: { - sessionId: this.$store.state.sessionId, email: this.$store.state.email, firstName: this.form.firstName, lastName: this.form.lastName, diff --git a/frontend/src/views/Pages/UserProfile/UserCard_FormUserMail.spec.js b/frontend/src/views/Pages/UserProfile/UserCard_FormUserMail.spec.js index 93477bdfa..06f26877a 100644 --- a/frontend/src/views/Pages/UserProfile/UserCard_FormUserMail.spec.js +++ b/frontend/src/views/Pages/UserProfile/UserCard_FormUserMail.spec.js @@ -14,7 +14,6 @@ describe('UserCard_FormUserMail', () => { $t: jest.fn((t) => t), $store: { state: { - sessionId: 1, email: 'user@example.org', firstName: 'Peter', lastName: 'Lustig', @@ -76,7 +75,6 @@ describe('UserCard_FormUserMail', () => { expect(mockAPIcall).toHaveBeenCalledWith( expect.objectContaining({ variables: { - sessionId: 1, email: 'user@example.org', newEmail: 'test@example.org', }, @@ -106,7 +104,6 @@ describe('UserCard_FormUserMail', () => { expect(mockAPIcall).toHaveBeenCalledWith( expect.objectContaining({ variables: { - sessionId: 1, email: 'user@example.org', newEmail: 'test@example.org', }, diff --git a/frontend/src/views/Pages/UserProfile/UserCard_FormUserMail.vue b/frontend/src/views/Pages/UserProfile/UserCard_FormUserMail.vue index b5c4f30bb..ed88425c7 100644 --- a/frontend/src/views/Pages/UserProfile/UserCard_FormUserMail.vue +++ b/frontend/src/views/Pages/UserProfile/UserCard_FormUserMail.vue @@ -48,7 +48,6 @@ export default { .query({ query: updateUserInfos, variables: { - sessionId: this.$store.state.sessionId, email: this.$store.state.email, newEmail: this.newEmail, }, diff --git a/frontend/src/views/Pages/UserProfile/UserCard_FormUserPasswort.spec.js b/frontend/src/views/Pages/UserProfile/UserCard_FormUserPasswort.spec.js index c44f7d300..fa4aceb0c 100644 --- a/frontend/src/views/Pages/UserProfile/UserCard_FormUserPasswort.spec.js +++ b/frontend/src/views/Pages/UserProfile/UserCard_FormUserPasswort.spec.js @@ -17,7 +17,6 @@ describe('UserCard_FormUserPasswort', () => { $t: jest.fn((t) => t), $store: { state: { - sessionId: 1, email: 'user@example.org', }, }, @@ -175,7 +174,6 @@ describe('UserCard_FormUserPasswort', () => { expect(changePasswordProfileMock).toHaveBeenCalledWith( expect.objectContaining({ variables: { - sessionId: 1, email: 'user@example.org', password: '1234', passwordNew: 'Aa123456', diff --git a/frontend/src/views/Pages/UserProfile/UserCard_FormUserPasswort.vue b/frontend/src/views/Pages/UserProfile/UserCard_FormUserPasswort.vue index a9be983c1..4678defaf 100644 --- a/frontend/src/views/Pages/UserProfile/UserCard_FormUserPasswort.vue +++ b/frontend/src/views/Pages/UserProfile/UserCard_FormUserPasswort.vue @@ -81,7 +81,6 @@ export default { .query({ query: updateUserInfos, variables: { - sessionId: this.$store.state.sessionId, email: this.$store.state.email, password: this.form.password, passwordNew: this.form.newPassword.password, diff --git a/frontend/src/views/Pages/UserProfile/UserCard_FormUsername.spec.js b/frontend/src/views/Pages/UserProfile/UserCard_FormUsername.spec.js index b6ab05902..8a69ee7eb 100644 --- a/frontend/src/views/Pages/UserProfile/UserCard_FormUsername.spec.js +++ b/frontend/src/views/Pages/UserProfile/UserCard_FormUsername.spec.js @@ -25,7 +25,6 @@ describe('UserCard_FormUsername', () => { $t: jest.fn((t) => t), $store: { state: { - sessionId: 1, email: 'user@example.org', username: '', }, @@ -111,7 +110,6 @@ describe('UserCard_FormUsername', () => { expect.objectContaining({ variables: { email: 'user@example.org', - sessionId: 1, username: 'username', }, }), @@ -151,7 +149,6 @@ describe('UserCard_FormUsername', () => { expect.objectContaining({ variables: { email: 'user@example.org', - sessionId: 1, username: 'username', }, }), diff --git a/frontend/src/views/Pages/UserProfile/UserCard_FormUsername.vue b/frontend/src/views/Pages/UserProfile/UserCard_FormUsername.vue index 1c269ba53..f6587b968 100644 --- a/frontend/src/views/Pages/UserProfile/UserCard_FormUsername.vue +++ b/frontend/src/views/Pages/UserProfile/UserCard_FormUsername.vue @@ -90,7 +90,6 @@ export default { .query({ query: updateUserInfos, variables: { - sessionId: this.$store.state.sessionId, email: this.$store.state.email, username: this.form.username, }, diff --git a/frontend/src/views/Pages/UserProfileTransactionList.spec.js b/frontend/src/views/Pages/UserProfileTransactionList.spec.js index 484dd10f1..8e4235fbe 100644 --- a/frontend/src/views/Pages/UserProfileTransactionList.spec.js +++ b/frontend/src/views/Pages/UserProfileTransactionList.spec.js @@ -13,11 +13,6 @@ describe('UserProfileTransactionList', () => { $i18n: { locale: jest.fn(() => 'en'), }, - $store: { - state: { - sessionId: 1, - }, - }, } const stubs = { diff --git a/frontend/vue.config.js b/frontend/vue.config.js index 698aca1de..fa12f8200 100644 --- a/frontend/vue.config.js +++ b/frontend/vue.config.js @@ -1,5 +1,6 @@ const path = require('path') -const dotenv = require('dotenv-webpack') +const webpack = require('webpack') +const Dotenv = require('dotenv-webpack') process.env.VUE_APP_BUILD_COMMIT = process.env.BUILD_COMMIT @@ -25,8 +26,17 @@ module.exports = { assets: path.join(__dirname, 'src/assets'), }, }, - // eslint-disable-next-line new-cap - plugins: [new dotenv()], + plugins: [ + new Dotenv(), + new webpack.DefinePlugin({ + // Those are Environment Variables transmitted via Docker + // 'process.env.DOCKER_WORKDIR': JSON.stringify(process.env.DOCKER_WORKDIR), + // 'process.env.BUILD_DATE': JSON.stringify(process.env.BUILD_DATE), + // 'process.env.BUILD_VERSION': JSON.stringify(process.env.BUILD_VERSION), + 'process.env.BUILD_COMMIT': JSON.stringify(process.env.BUILD_COMMIT), + // 'process.env.PORT': JSON.stringify(process.env.PORT), + }), + ], }, css: { // Enable CSS source maps. 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..73f5deea0 --- /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 +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. + +```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 + + 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;