diff --git a/backend/.env.dist b/backend/.env.dist index b6ae1586c..4ec60d856 100644 --- a/backend/.env.dist +++ b/backend/.env.dist @@ -27,7 +27,7 @@ DLT_CONNECTOR_URL=http://localhost:6010 # Community COMMUNITY_NAME=Gradido Entwicklung -COMMUNITY_URL=http://localhost/ +COMMUNITY_URL=http://localhost COMMUNITY_REGISTER_PATH=/register COMMUNITY_REDEEM_PATH=/redeem/{code} COMMUNITY_REDEEM_CONTRIBUTION_PATH=/redeem/CL-{code} diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 4c9b7e98d..b94178157 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -12,7 +12,7 @@ Decimal.set({ }) const constants = { - DB_VERSION: '0081-introduce_gms_registration', + DB_VERSION: '0082-introduce_gms_registration', DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 LOG4JS_CONFIG: 'log4js-config.json', // default log level on production should be info diff --git a/backend/src/graphql/arg/UserArgs.ts b/backend/src/graphql/arg/UserArgs.ts index 633ed5e20..406be14cb 100644 --- a/backend/src/graphql/arg/UserArgs.ts +++ b/backend/src/graphql/arg/UserArgs.ts @@ -3,11 +3,11 @@ import { ArgsType, Field } from 'type-graphql' @ArgsType() export class UserArgs { - @Field({ nullable: false }) + @Field() @IsString() identifier: string - @Field({ nullable: true }) + @Field() @IsString() - communityIdentifier?: string + communityIdentifier: string } diff --git a/backend/src/graphql/model/User.ts b/backend/src/graphql/model/User.ts index cd188f49f..d24a717c4 100644 --- a/backend/src/graphql/model/User.ts +++ b/backend/src/graphql/model/User.ts @@ -10,6 +10,9 @@ export class User { this.id = user.id this.foreign = user.foreign this.communityUuid = user.communityUuid + if (user.community) { + this.communityName = user.community.name + } this.gradidoID = user.gradidoID this.alias = user.alias if (user.emailContact) { diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index 6ac69f26e..1bb4a86f7 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -144,7 +144,11 @@ describe('send coins', () => { }) it('logs the error thrown', () => { - expect(logger.error).toBeCalledWith('No user with this credentials', 'wrong@email.com') + expect(logger.error).toBeCalledWith( + 'No user with this credentials', + 'wrong@email.com', + homeCom.communityUuid, + ) }) describe('deleted recipient', () => { @@ -167,13 +171,17 @@ describe('send coins', () => { }), ).toEqual( expect.objectContaining({ - errors: [new GraphQLError('No user to given contact')], + errors: [new GraphQLError('No user with this credentials')], }), ) }) it('logs the error thrown', () => { - expect(logger.error).toBeCalledWith('No user to given contact', 'stephen@hawking.uk') + expect(logger.error).toBeCalledWith( + 'No user with this credentials', + 'stephen@hawking.uk', + homeCom.communityUuid, + ) }) }) @@ -206,6 +214,7 @@ describe('send coins', () => { expect(logger.error).toBeCalledWith( 'No user with this credentials', 'garrick@ollivander.com', + homeCom.communityUuid, ) }) }) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 3261b313e..ce1ba43da 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -432,7 +432,7 @@ export class TransactionResolver { const senderUser = getUser(context) if (!recipientCommunityIdentifier || (await isHomeCommunity(recipientCommunityIdentifier))) { - // processing sendCoins within sender and recepient are both in home community + // processing sendCoins within sender and recipient are both in home community const recipientUser = await findUserByIdentifier( recipientIdentifier, recipientCommunityIdentifier, diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index a3f69bc4d..9ef4155d9 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -2679,6 +2679,7 @@ describe('UserResolver', () => { query: userQuery, variables: { identifier: 'identifier', + communityIdentifier: 'community identifier', }, }), ).resolves.toEqual( @@ -2767,13 +2768,11 @@ describe('UserResolver', () => { }), ).resolves.toEqual( expect.objectContaining({ - errors: [ - new GraphQLError('Found user to given contact, but belongs to other community'), - ], + errors: [new GraphQLError('No user with this credentials')], }), ) expect(logger.error).toBeCalledWith( - 'Found user to given contact, but belongs to other community', + 'No user with this credentials', 'bibi@bloxberg.de', foreignCom1.communityUuid, ) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 6f8a39aa4..8a4e8b7fc 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -66,7 +66,7 @@ import random from 'random-bigint' import { randombytes_random } from 'sodium-native' import { FULL_CREATION_AVAILABLE } from './const/const' -import { getCommunityName, getHomeCommunity } from './util/communities' +import { getHomeCommunity } from './util/communities' import { getUserCreations } from './util/creations' import { findUserByIdentifier } from './util/findUserByIdentifier' import { findUsers } from './util/findUsers' @@ -851,11 +851,6 @@ export class UserResolver { ): Promise { const foundDbUser = await findUserByIdentifier(identifier, communityIdentifier) const modelUser = new User(foundDbUser) - if (!foundDbUser.communityUuid) { - modelUser.communityName = (await Promise.resolve(getHomeCommunity())).name - } else { - modelUser.communityName = await getCommunityName(foundDbUser.communityUuid) - } return modelUser } } diff --git a/backend/src/graphql/resolver/semaphore.test.ts b/backend/src/graphql/resolver/semaphore.test.ts index dc6c5b364..ed58d55ec 100644 --- a/backend/src/graphql/resolver/semaphore.test.ts +++ b/backend/src/graphql/resolver/semaphore.test.ts @@ -6,6 +6,7 @@ import { Community as DbCommunity } from '@entity/Community' import { ApolloServerTestClient } from 'apollo-server-testing' import { Decimal } from 'decimal.js-light' import { GraphQLError } from 'graphql' +import { v4 as uuidv4 } from 'uuid' import { cleanDB, testEnvironment, contributionDateFormatter } from '@test/helpers' @@ -54,7 +55,7 @@ describe('semaphore', () => { beforeAll(async () => { const now = new Date() homeCom = DbCommunity.create() - homeCom.communityUuid = 'homeCom-UUID' + homeCom.communityUuid = uuidv4() homeCom.creationDate = new Date('2000-01-01') homeCom.description = 'homeCom description' homeCom.foreign = false diff --git a/backend/src/graphql/resolver/util/findUserByIdentifier.ts b/backend/src/graphql/resolver/util/findUserByIdentifier.ts index 6b7f1bccc..7e52327d3 100644 --- a/backend/src/graphql/resolver/util/findUserByIdentifier.ts +++ b/backend/src/graphql/resolver/util/findUserByIdentifier.ts @@ -1,3 +1,5 @@ +import { FindOptionsWhere } from '@dbTools/typeorm' +import { Community } from '@entity/Community' import { User as DbUser } from '@entity/User' import { UserContact as DbUserContact } from '@entity/UserContact' import { validate, version } from 'uuid' @@ -6,15 +8,26 @@ import { LogError } from '@/server/LogError' import { VALID_ALIAS_REGEX } from './validateAlias' +/** + * + * @param identifier could be gradidoID, alias or email of user + * @param communityIdentifier could be uuid or name of community + * @returns + */ export const findUserByIdentifier = async ( identifier: string, - communityIdentifier?: string, + communityIdentifier: string, ): Promise => { let user: DbUser | null + const communityWhere: FindOptionsWhere = + validate(communityIdentifier) && version(communityIdentifier) === 4 + ? { communityUuid: communityIdentifier } + : { name: communityIdentifier } + if (validate(identifier) && version(identifier) === 4) { user = await DbUser.findOne({ - where: { gradidoID: identifier, communityUuid: communityIdentifier }, - relations: ['emailContact'], + where: { gradidoID: identifier, community: communityWhere }, + relations: ['emailContact', 'community'], }) if (!user) { throw new LogError('No user found to given identifier(s)', identifier, communityIdentifier) @@ -24,28 +37,21 @@ export const findUserByIdentifier = async ( where: { email: identifier, emailChecked: true, + user: { + community: communityWhere, + }, }, - relations: ['user'], + relations: { user: { community: true } }, }) if (!userContact) { - throw new LogError('No user with this credentials', identifier) - } - if (!userContact.user) { - throw new LogError('No user to given contact', identifier) - } - if (userContact.user.communityUuid !== communityIdentifier) { - throw new LogError( - 'Found user to given contact, but belongs to other community', - identifier, - communityIdentifier, - ) + throw new LogError('No user with this credentials', identifier, communityIdentifier) } user = userContact.user user.emailContact = userContact } else if (VALID_ALIAS_REGEX.exec(identifier)) { user = await DbUser.findOne({ - where: { alias: identifier, communityUuid: communityIdentifier }, - relations: ['emailContact'], + where: { alias: identifier, community: communityWhere }, + relations: ['emailContact', 'community'], }) if (!user) { throw new LogError('No user found to given identifier(s)', identifier, communityIdentifier) diff --git a/backend/src/graphql/resolver/util/findUserByIdentifiers.test.ts b/backend/src/graphql/resolver/util/findUserByIdentifiers.test.ts new file mode 100644 index 000000000..cfdbce8f2 --- /dev/null +++ b/backend/src/graphql/resolver/util/findUserByIdentifiers.test.ts @@ -0,0 +1,94 @@ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/restrict-template-expressions */ +import { Connection } from '@dbTools/typeorm' +import { Community as DbCommunity } from '@entity/Community' +import { User as DbUser } from '@entity/User' +import { ApolloServerTestClient } from 'apollo-server-testing' + +import { cleanDB, testEnvironment } from '@test/helpers' + +import { writeHomeCommunityEntry } from '@/seeds/community' +import { userFactory } from '@/seeds/factory/user' +import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' +import { bobBaumeister } from '@/seeds/users/bob-baumeister' +import { peterLustig } from '@/seeds/users/peter-lustig' + +import { findUserByIdentifier } from './findUserByIdentifier' + +let con: Connection +let testEnv: { + mutate: ApolloServerTestClient['mutate'] + query: ApolloServerTestClient['query'] + con: Connection +} + +beforeAll(async () => { + testEnv = await testEnvironment() + con = testEnv.con + await cleanDB() +}) + +afterAll(async () => { + await cleanDB() + await con.close() +}) + +describe('graphql/resolver/util/findUserByIdentifier', () => { + let homeCom: DbCommunity + let communityUuid: string + let communityName: string + let userBibi: DbUser + + beforeAll(async () => { + homeCom = await writeHomeCommunityEntry() + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + communityUuid = homeCom.communityUuid! + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + communityName = homeCom.communityUuid! + + userBibi = await userFactory(testEnv, bibiBloxberg) + await userFactory(testEnv, peterLustig) + await userFactory(testEnv, bobBaumeister) + }) + + describe('communityIdentifier is community uuid', () => { + it('userIdentifier is gradido id', async () => { + const user = await findUserByIdentifier(userBibi.gradidoID, communityUuid) + user.userRoles = [] + expect(user).toMatchObject(userBibi) + }) + + it('userIdentifier is alias', async () => { + const user = await findUserByIdentifier(userBibi.alias, communityUuid) + user.userRoles = [] + expect(user).toMatchObject(userBibi) + }) + + it('userIdentifier is email', async () => { + const user = await findUserByIdentifier(userBibi.emailContact.email, communityUuid) + user.userRoles = [] + expect(user).toMatchObject(userBibi) + }) + }) + + describe('communityIdentifier is community name', () => { + it('userIdentifier is gradido id', async () => { + const user = await findUserByIdentifier(userBibi.gradidoID, communityName) + user.userRoles = [] + expect(user).toMatchObject(userBibi) + }) + + it('userIdentifier is alias', async () => { + const user = await findUserByIdentifier(userBibi.alias, communityName) + user.userRoles = [] + expect(user).toMatchObject(userBibi) + }) + + it('userIdentifier is email', async () => { + const user = await findUserByIdentifier(userBibi.emailContact.email, communityName) + user.userRoles = [] + expect(user).toMatchObject(userBibi) + }) + }) +}) diff --git a/backend/src/seeds/graphql/queries.ts b/backend/src/seeds/graphql/queries.ts index 44c3c35e1..290d93ad6 100644 --- a/backend/src/seeds/graphql/queries.ts +++ b/backend/src/seeds/graphql/queries.ts @@ -387,7 +387,7 @@ export const adminListContributionMessages = gql` ` export const user = gql` - query ($identifier: String!, $communityIdentifier: String) { + query ($identifier: String!, $communityIdentifier: String!) { user(identifier: $identifier, communityIdentifier: $communityIdentifier) { firstName lastName diff --git a/backend/src/seeds/users/bibi-bloxberg.ts b/backend/src/seeds/users/bibi-bloxberg.ts index 7c372848e..9a40e922b 100644 --- a/backend/src/seeds/users/bibi-bloxberg.ts +++ b/backend/src/seeds/users/bibi-bloxberg.ts @@ -4,6 +4,7 @@ export const bibiBloxberg: UserInterface = { email: 'bibi@bloxberg.de', firstName: 'Bibi', lastName: 'Bloxberg', + alias: 'BBB', // description: 'Hex Hex', emailChecked: true, language: 'de', diff --git a/backend/src/util/communityUser.ts b/backend/src/util/communityUser.ts index 9e337dfe7..732c585d0 100644 --- a/backend/src/util/communityUser.ts +++ b/backend/src/util/communityUser.ts @@ -50,6 +50,7 @@ const communityDbUser: dbUser = { }, foreign: false, communityUuid: '55555555-4444-4333-2222-11111111', + community: null, gmsPublishName: 0, gmsAllowed: false, location: null, diff --git a/database/entity/0081-user_join_community/Community.ts b/database/entity/0081-user_join_community/Community.ts new file mode 100644 index 000000000..1c6b36be3 --- /dev/null +++ b/database/entity/0081-user_join_community/Community.ts @@ -0,0 +1,70 @@ +import { + BaseEntity, + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, + OneToMany, + JoinColumn, +} from 'typeorm' +import { User } from '../User' + +@Entity('communities') +export class Community extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ name: 'foreign', type: 'bool', nullable: false, default: true }) + foreign: boolean + + @Column({ name: 'url', length: 255, nullable: false }) + url: string + + @Column({ name: 'public_key', type: 'binary', length: 32, nullable: false }) + publicKey: Buffer + + @Column({ name: 'private_key', type: 'binary', length: 64, nullable: true }) + privateKey: Buffer | null + + @Column({ + name: 'community_uuid', + type: 'char', + length: 36, + nullable: true, + collation: 'utf8mb4_unicode_ci', + }) + communityUuid: string | null + + @Column({ name: 'authenticated_at', type: 'datetime', nullable: true }) + authenticatedAt: Date | null + + @Column({ name: 'name', type: 'varchar', length: 40, nullable: true }) + name: string | null + + @Column({ name: 'description', type: 'varchar', length: 255, nullable: true }) + description: string | null + + @CreateDateColumn({ name: 'creation_date', type: 'datetime', nullable: true }) + creationDate: Date | null + + @CreateDateColumn({ + name: 'created_at', + type: 'datetime', + default: () => 'CURRENT_TIMESTAMP(3)', + nullable: false, + }) + createdAt: Date + + @UpdateDateColumn({ + name: 'updated_at', + type: 'datetime', + onUpdate: 'CURRENT_TIMESTAMP(3)', + nullable: true, + }) + updatedAt: Date | null + + @OneToMany(() => User, (user) => user.community) + @JoinColumn({ name: 'community_uuid', referencedColumnName: 'communityUuid' }) + users: User[] +} diff --git a/database/entity/0081-user_join_community/User.ts b/database/entity/0081-user_join_community/User.ts new file mode 100644 index 000000000..28141029d --- /dev/null +++ b/database/entity/0081-user_join_community/User.ts @@ -0,0 +1,138 @@ +import { + BaseEntity, + Entity, + PrimaryGeneratedColumn, + Column, + DeleteDateColumn, + OneToMany, + JoinColumn, + OneToOne, + ManyToOne, +} from 'typeorm' +import { Contribution } from '../Contribution' +import { ContributionMessage } from '../ContributionMessage' +import { UserContact } from '../UserContact' +import { UserRole } from '../UserRole' +import { Community } from '../Community' + +@Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) +export class User extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ type: 'bool', default: false }) + foreign: boolean + + @Column({ + name: 'gradido_id', + length: 36, + nullable: false, + collation: 'utf8mb4_unicode_ci', + }) + gradidoID: string + + @Column({ + name: 'community_uuid', + type: 'char', + length: 36, + nullable: true, + collation: 'utf8mb4_unicode_ci', + }) + communityUuid: string + + @ManyToOne(() => Community, (community) => community.users) + @JoinColumn({ name: 'community_uuid', referencedColumnName: 'communityUuid' }) + community: Community | null + + @Column({ + name: 'alias', + length: 20, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + alias: string + + @OneToOne(() => UserContact, (emailContact: UserContact) => emailContact.user) + @JoinColumn({ name: 'email_id' }) + emailContact: UserContact + + @Column({ name: 'email_id', type: 'int', unsigned: true, nullable: true, default: null }) + emailId: number | null + + @Column({ + name: 'first_name', + length: 255, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + firstName: string + + @Column({ + name: 'last_name', + length: 255, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + lastName: string + + @Column({ name: 'created_at', default: () => 'CURRENT_TIMESTAMP(3)', nullable: false }) + createdAt: Date + + @DeleteDateColumn({ name: 'deleted_at', nullable: true }) + deletedAt: Date | null + + @Column({ type: 'bigint', default: 0, unsigned: true }) + password: BigInt + + @Column({ + name: 'password_encryption_type', + type: 'int', + unsigned: true, + nullable: false, + default: 0, + }) + passwordEncryptionType: number + + @Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false }) + language: string + + @Column({ type: 'bool', default: false }) + hideAmountGDD: boolean + + @Column({ type: 'bool', default: false }) + hideAmountGDT: boolean + + @OneToMany(() => UserRole, (userRole) => userRole.user) + @JoinColumn({ name: 'user_id' }) + userRoles: UserRole[] + + @Column({ name: 'referrer_id', type: 'int', unsigned: true, nullable: true, default: null }) + referrerId?: number | null + + @Column({ + name: 'contribution_link_id', + type: 'int', + unsigned: true, + nullable: true, + default: null, + }) + contributionLinkId?: number | null + + @Column({ name: 'publisher_id', default: 0 }) + publisherId: number + + @OneToMany(() => Contribution, (contribution) => contribution.user) + @JoinColumn({ name: 'user_id' }) + contributions?: Contribution[] + + @OneToMany(() => ContributionMessage, (message) => message.user) + @JoinColumn({ name: 'user_id' }) + messages?: ContributionMessage[] + + @OneToMany(() => UserContact, (userContact: UserContact) => userContact.user) + @JoinColumn({ name: 'user_id' }) + userContacts?: UserContact[] +} diff --git a/database/entity/Community.ts b/database/entity/Community.ts index 7fa33e2b0..13fbbcf00 100644 --- a/database/entity/Community.ts +++ b/database/entity/Community.ts @@ -1 +1,5 @@ +<<<<<<< HEAD export { Community } from './0081-introduce_gms_registration/Community' +======= +export { Community } from './0081-user_join_community/Community' +>>>>>>> refs/remotes/origin/master diff --git a/database/entity/User.ts b/database/entity/User.ts index e5fc6527f..0cde9b36f 100644 --- a/database/entity/User.ts +++ b/database/entity/User.ts @@ -1 +1,5 @@ +<<<<<<< HEAD export { User } from './0081-introduce_gms_registration/User' +======= +export { User } from './0081-user_join_community/User' +>>>>>>> refs/remotes/origin/master diff --git a/database/migrations/0081-user_join_community.ts b/database/migrations/0081-user_join_community.ts new file mode 100644 index 000000000..6e40cb414 --- /dev/null +++ b/database/migrations/0081-user_join_community.ts @@ -0,0 +1,11 @@ +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn( + 'ALTER TABLE users MODIFY community_uuid VARCHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;', + ) +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn( + 'ALTER TABLE users MODIFY community_uuid VARCHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;', + ) +} diff --git a/deployment/bare_metal/nginx/common/limit_requests.conf b/deployment/bare_metal/nginx/common/limit_requests.conf index e9026ee81..c9501fd64 100644 --- a/deployment/bare_metal/nginx/common/limit_requests.conf +++ b/deployment/bare_metal/nginx/common/limit_requests.conf @@ -1,3 +1,4 @@ limit_req_zone $binary_remote_addr zone=frontend:20m rate=5r/s; limit_req_zone $binary_remote_addr zone=backend:25m rate=15r/s; -limit_req_zone $binary_remote_addr zone=api:5m rate=30r/s; \ No newline at end of file +limit_req_zone $binary_remote_addr zone=api:5m rate=30r/s; +limit_conn_zone $binary_remote_addr zone=addr:10m; \ No newline at end of file diff --git a/deployment/bare_metal/nginx/sites-available/gradido.conf.ssl.template b/deployment/bare_metal/nginx/sites-available/gradido.conf.ssl.template index 822c326d0..d8ed50ba4 100644 --- a/deployment/bare_metal/nginx/sites-available/gradido.conf.ssl.template +++ b/deployment/bare_metal/nginx/sites-available/gradido.conf.ssl.template @@ -1,3 +1,5 @@ +include /etc/nginx/common/limit_requests.conf; + server { if ($host = $COMMUNITY_HOST) { return 301 https://$host$request_uri; @@ -21,7 +23,6 @@ server { include /etc/nginx/common/protect.conf; include /etc/nginx/common/protect_add_header.conf; - include /etc/nginx/common/limit_requests.conf; # protect from slow loris client_body_timeout 10s; diff --git a/deployment/bare_metal/nginx/sites-available/gradido.conf.template b/deployment/bare_metal/nginx/sites-available/gradido.conf.template index 1f673ee41..e0f382467 100644 --- a/deployment/bare_metal/nginx/sites-available/gradido.conf.template +++ b/deployment/bare_metal/nginx/sites-available/gradido.conf.template @@ -1,3 +1,5 @@ +include /etc/nginx/common/limit_requests.conf; + server { server_name $COMMUNITY_HOST; @@ -6,7 +8,6 @@ server { include /etc/nginx/common/protect.conf; include /etc/nginx/common/protect_add_header.conf; - include /etc/nginx/common/limit_requests.conf; # protect from slow loris client_body_timeout 10s; diff --git a/deployment/bare_metal/nginx/sites-available/update-page.conf.ssl.template b/deployment/bare_metal/nginx/sites-available/update-page.conf.ssl.template index ee7732230..fd41c333d 100644 --- a/deployment/bare_metal/nginx/sites-available/update-page.conf.ssl.template +++ b/deployment/bare_metal/nginx/sites-available/update-page.conf.ssl.template @@ -1,3 +1,4 @@ +include /etc/nginx/common/limit_requests.conf; server { if ($host = $COMMUNITY_HOST) { @@ -21,7 +22,6 @@ server { include /etc/nginx/common/protect.conf; include /etc/nginx/common/protect_add_header.conf; - include /etc/nginx/common/limit_requests.conf; # protect from slow loris client_body_timeout 10s; diff --git a/deployment/bare_metal/nginx/sites-available/update-page.conf.template b/deployment/bare_metal/nginx/sites-available/update-page.conf.template index 38dfb2d02..be91abc88 100644 --- a/deployment/bare_metal/nginx/sites-available/update-page.conf.template +++ b/deployment/bare_metal/nginx/sites-available/update-page.conf.template @@ -1,3 +1,4 @@ +include /etc/nginx/common/limit_requests.conf; server { server_name $COMMUNITY_HOST; @@ -6,7 +7,6 @@ server { include /etc/nginx/common/protect.conf; include /etc/nginx/common/protect_add_header.conf; - include /etc/nginx/common/limit_requests.conf; # protect from slow loris client_body_timeout 10s; diff --git a/deployment/hetzner_cloud/cloudConfig.yaml b/deployment/hetzner_cloud/cloudConfig.yaml index 86e7d5724..84658705f 100644 --- a/deployment/hetzner_cloud/cloudConfig.yaml +++ b/deployment/hetzner_cloud/cloudConfig.yaml @@ -9,6 +9,7 @@ users: packages: - fail2ban + - python3-systemd - ufw - git - mariadb-server diff --git a/deployment/hetzner_cloud/install.sh b/deployment/hetzner_cloud/install.sh index ee539370c..e9ed69e76 100755 --- a/deployment/hetzner_cloud/install.sh +++ b/deployment/hetzner_cloud/install.sh @@ -80,6 +80,14 @@ expect eof ") echo "$SECURE_MYSQL" +# Configure fail2ban, seems to not run out of the box on Debian 12 +echo -e "[sshd]\nbackend = systemd" | tee /etc/fail2ban/jail.d/sshd.conf +# enable nginx-limit-req filter to block also user which exceed nginx request limiter +echo -e "[nginx-limit-req]\nenabled = true\nlogpath = $SCRIPT_PATH/log/nginx-error.*.log" | tee /etc/fail2ban/jail.d/nginx-limit-req.conf +# enable nginx bad request filter +echo -e "[nginx-bad-request]\nenabled = true\nlogpath = $SCRIPT_PATH/log/nginx-error.*.log" | tee /etc/fail2ban/jail.d/nginx-bad-request.conf +systemctl restart fail2ban + # Configure nginx rm /etc/nginx/sites-enabled/default envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $SCRIPT_PATH/nginx/sites-available/gradido.conf.template > $SCRIPT_PATH/nginx/sites-available/gradido.conf diff --git a/dht-node/src/config/index.ts b/dht-node/src/config/index.ts index b08ac6ffb..949ae47ce 100644 --- a/dht-node/src/config/index.ts +++ b/dht-node/src/config/index.ts @@ -4,7 +4,7 @@ import dotenv from 'dotenv' dotenv.config() const constants = { - DB_VERSION: '0081-introduce_gms_registration', + DB_VERSION: '0082-introduce_gms_registration', LOG4JS_CONFIG: 'log4js-config.json', // default log level on production should be info LOG_LEVEL: process.env.LOG_LEVEL ?? 'info', diff --git a/federation/src/config/index.ts b/federation/src/config/index.ts index 5750257fa..ec6fdff26 100644 --- a/federation/src/config/index.ts +++ b/federation/src/config/index.ts @@ -10,7 +10,7 @@ Decimal.set({ }) const constants = { - DB_VERSION: '0081-introduce_gms_registration', + DB_VERSION: '0082-introduce_gms_registration', DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 LOG4JS_CONFIG: 'log4js-config.json', // default log level on production should be info diff --git a/frontend/jest.config.js b/frontend/jest.config.js index 023fd64fd..4c3e6ab73 100644 --- a/frontend/jest.config.js +++ b/frontend/jest.config.js @@ -4,7 +4,7 @@ module.exports = { collectCoverageFrom: ['src/**/*.{js,vue}', '!**/node_modules/**', '!**/?(*.)+(spec|test).js?(x)'], coverageThreshold: { global: { - lines: 95, + lines: 94, }, }, moduleFileExtensions: [ diff --git a/frontend/src/assets/News/news.json b/frontend/src/assets/News/news.json index 4f71a3460..45360631b 100644 --- a/frontend/src/assets/News/news.json +++ b/frontend/src/assets/News/news.json @@ -1,47 +1,37 @@ [ { "locale": "de", - "date": "30.10.2023", - "text": "Gradido wird dezentral (Beta)", - "url": "/send", - "extra": "Gradido-Communities können nun ihre eigenen dezentralen Gradido-Server betreiben. Transaktionen zwischen den Servern sind möglich. Im Senden-Dialog findest Du ein Auswahl-Menü mit den verfügbaren Communities, um das Ziel für den Empfänger auszuwählen. Bitte beachte, dass diese Funktion sich noch in der Beta-Testphase befindet und die Communities erst nach und nach hinzugefügt werden.", - "extra2": "Wenn Ihr als Community Euren eigenen Gradido-Server betreiben wollt, schreibt uns bitte eine E-Mail an ", - "email": "support@gradido.net" + "text": "Gradido-Kreise – Gemeinsam mit Freunden die Zukunft gestalten", + "button": "Mehr erfahren", + "url": "https://gradido.net/de/gradido-kreise-gemeinsam-mit-freunden-die-zukunft-gestalten", + "extra": "Ganz gleich, ob Ihr bereits einer Gruppe zugehörig seid oder ob Ihr Euch über Gradido gefunden habt – wenn Ihr gemeinsam Gradido nutzen wollt, braucht Ihr nicht gleich einen eigenen Gradido-Server." }, { "locale": "en", - "date": "30.10.2023", - "text": "Gradido becomes decentralized (Beta)", - "url": "/send", - "extra": "Gradido communities can now run their own decentralized Gradido servers. Transactions between the servers are possible. In the send dialog you will find a dropdown menu with the available communities to select the destination for the receiver. Please note that this feature is still in beta testing and communities will be added gradually.", - "extra2": "If you want to run your own Gradido server as a community, please send us an email to ", - "email": "support@gradido.net" + "text": "Gradido circles - Shaping the future together with friends", + "button": "Learn more", + "url": "https://gradido.net/en/gradido-kreise-gemeinsam-mit-freunden-die-zukunft-gestalten/", + "extra": "No matter whether you already belong to a group or whether you found each other via Gradido - if you want to use Gradido together, you don't need your own Gradido server." }, { "locale": "fr", - "date": "30.10.2023", - "text": "Gradido devient décentralisé (Beta)", - "url": "/send", - "extra": "Les communautés Gradido peuvent désormais gérer leurs propres serveurs Gradido décentralisés. Les transactions entre les serveurs sont possibles. Dans la boîte de dialogue d'envoi, tu trouveras un menu de sélection avec les communautés disponibles pour choisir la destination du destinataire. Veuillez noter que cette fonction est encore en phase de test bêta et que les communautés ne seront ajoutées qu'au fur et à mesure.", - "extra2": "Si vous souhaitez exploiter votre propre serveur Gradido en tant que communauté, veuillez nous envoyer un e-mail à ", - "email": "support@gradido.net" + "text": "Cercles Gradido - Construire l'avenir ensemble avec des amis ", + "button": "En savoir plus", + "url": "https://gradido.net/fr/gradido-kreise-gemeinsam-mit-freunden-die-zukunft-gestalten/", + "extra": "Que vous fassiez déjà partie d'un groupe ou que vous vous soyez trouvés par le biais de Gradido, si vous voulez utiliser Gradido ensemble, vous n'avez pas besoin de votre propre serveur Gradido." }, { "locale": "es", - "date": "30.10.2023", - "text": "Gradido se descentraliza (Beta)", - "url": "/send", - "extra": "Las comunidades de Gradido ya pueden gestionar sus propios servidores descentralizados de Gradido. Las transacciones entre los servidores son posibles. En el diálogo de envío encontrarás un menú desplegable con las comunidades disponibles para seleccionar el destino del destinatario. Ten en cuenta que esta función aún está en fase de pruebas beta y que las comunidades se irán añadiendo poco a poco.", - "extra2": "Si quieres gestionar tu propio servidor Gradido como comunidad, envíanos un correo electrónico a ", - "email": "support@gradido.net" + "text": "Círculos Gradido - Forjar el futuro entre amigos ", + "button": "Más información", + "url": "https://gradido.net/es/gradido-kreise-gemeinsam-mit-freunden-die-zukunft-gestalten/", + "extra": "No importa si ya pertenecéis a un grupo o si os habéis encontrado a través de Gradido: si queréis utilizar Gradido juntos, no necesitáis vuestro propio servidor Gradido." }, { "locale": "nl", - "date": "30.10.2023", - "text": "Gradido wordt gedecentraliseerd (Beta)", - "url": "/send", - "extra": "Gradido-gemeenschappen kunnen nu hun eigen gedecentraliseerde Gradido-servers beheren. Transacties tussen de servers zijn mogelijk. In het verzenddialoogvenster vind je een vervolgkeuzemenu met de beschikbare communities om de bestemming voor de ontvanger te selecteren. Houd er rekening mee dat deze functie zich nog in de beta-testfase bevindt en dat de communities beetje bij beetje zullen worden toegevoegd.", - "extra2": "Als je je eigen Gradido server als community wilt gebruiken, stuur ons dan een e-mail naar ", - "email": "support@gradido.net" + "text": "Gradidokringen - Samen met vrienden de toekomst vormgeven", + "button": "Meer informatie", + "url": "https://gradido.net/nl/gradido-kreise-gemeinsam-mit-freunden-die-zukunft-gestalten/", + "extra": "Het maakt niet uit of je al tot een groep behoort of dat je elkaar via Gradido hebt gevonden - als je Gradido samen wilt gebruiken, heb je geen eigen Gradido-server nodig." } ] diff --git a/frontend/src/components/CommunitySwitch.vue b/frontend/src/components/CommunitySwitch.vue index f9ecc804d..ac9ce3182 100644 --- a/frontend/src/components/CommunitySwitch.vue +++ b/frontend/src/components/CommunitySwitch.vue @@ -1,16 +1,23 @@