enum solution and graphql-schema-validation

This commit is contained in:
Claus-Peter Huebner 2023-07-14 20:08:06 +02:00
parent 03f60fa4d4
commit abc7128e5f
12 changed files with 49 additions and 139 deletions

View File

@ -6,17 +6,14 @@ import { MODERATOR_RIGHTS } from './MODERATOR_RIGHTS'
import { Role } from './Role'
import { USER_RIGHTS } from './USER_RIGHTS'
export const ROLE_UNAUTHORIZED = new Role(RoleNames.ROLE_NAME_UNAUTHORIZED, INALIENABLE_RIGHTS)
export const ROLE_USER = new Role(RoleNames.ROLE_NAME_USER, [
...INALIENABLE_RIGHTS,
...USER_RIGHTS,
])
export const ROLE_MODERATOR = new Role(RoleNames.ROLE_NAME_MODERATOR, [
export const ROLE_UNAUTHORIZED = new Role(RoleNames.MODERATOR, INALIENABLE_RIGHTS)
export const ROLE_USER = new Role(RoleNames.USER, [...INALIENABLE_RIGHTS, ...USER_RIGHTS])
export const ROLE_MODERATOR = new Role(RoleNames.MODERATOR, [
...INALIENABLE_RIGHTS,
...USER_RIGHTS,
...MODERATOR_RIGHTS,
])
export const ROLE_ADMIN = new Role(RoleNames.ROLE_NAME_ADMIN, [
export const ROLE_ADMIN = new Role(RoleNames.ADMIN, [
...INALIENABLE_RIGHTS,
...USER_RIGHTS,
...MODERATOR_RIGHTS,

View File

@ -8,6 +8,6 @@ export class SetUserRoleArgs {
@Field(() => Int)
userId: number
@Field(() => RoleNames, { nullable: true } )
role: RoleNames | null
@Field(() => RoleNames, { nullable: true })
role: RoleNames | null | undefined
}

View File

@ -1,13 +1,14 @@
import { User } from '@entity/User'
import { AuthChecker } from 'type-graphql'
import { RoleNames } from '@enum/RoleNames'
import { INALIENABLE_RIGHTS } from '@/auth/INALIENABLE_RIGHTS'
import { decode, encode } from '@/auth/JWT'
import { RIGHTS } from '@/auth/RIGHTS'
import { ROLE_UNAUTHORIZED, ROLE_USER, ROLE_ADMIN, ROLE_MODERATOR } from '@/auth/ROLES'
import { Context } from '@/server/context'
import { LogError } from '@/server/LogError'
import { RoleNames } from '@enum/RoleNames'
export const isAuthorized: AuthChecker<Context> = async ({ context }, rights) => {
context.role = ROLE_UNAUTHORIZED // unauthorized user
@ -41,10 +42,10 @@ export const isAuthorized: AuthChecker<Context> = async ({ context }, rights) =>
context.role = ROLE_USER
if (user.userRoles?.length > 0) {
switch (user.userRoles[0].role) {
case RoleNames.ROLE_NAME_ADMIN:
case RoleNames.ADMIN:
context.role = ROLE_ADMIN
break
case RoleNames.ROLE_NAME_MODERATOR:
case RoleNames.MODERATOR:
context.role = ROLE_MODERATOR
break
default:

View File

@ -1,10 +1,10 @@
import { registerEnumType } from 'type-graphql'
export enum RoleNames {
ROLE_NAME_ADMIN = 'admin',
ROLE_NAME_UNAUTHORIZED = 'unauthorized',
ROLE_NAME_USER = 'user',
ROLE_NAME_MODERATOR = 'moderator',
UNAUTHORIZED = 'UNAUTHORIZED',
USER = 'USER',
MODERATOR = 'MODERATOR',
ADMIN = 'ADMIN',
}
registerEnumType(RoleNames, {

View File

@ -1,56 +0,0 @@
import { UserContact as dbUserContact } from '@entity/UserContact'
import { ObjectType, Field, Int } from 'type-graphql'
@ObjectType()
export class UserContact {
constructor(userContact: dbUserContact) {
this.id = userContact.id
this.type = userContact.type
this.userId = userContact.userId
this.email = userContact.email
// this.emailVerificationCode = userContact.emailVerificationCode
this.emailOptInTypeId = userContact.emailOptInTypeId
this.emailResendCount = userContact.emailResendCount
this.emailChecked = userContact.emailChecked
this.phone = userContact.phone
this.createdAt = userContact.createdAt
this.updatedAt = userContact.updatedAt
this.deletedAt = userContact.deletedAt
}
@Field(() => Int)
id: number
@Field(() => String)
type: string
@Field(() => Int)
userId: number
@Field(() => String)
email: string
// @Field(() => BigInt, { nullable: true })
// emailVerificationCode: BigInt | null
@Field(() => Int, { nullable: true })
emailOptInTypeId: number | null
@Field(() => Int, { nullable: true })
emailResendCount: number | null
@Field(() => Boolean)
emailChecked: boolean
@Field(() => String, { nullable: true })
phone: string | null
@Field(() => Date)
createdAt: Date
@Field(() => Date, { nullable: true })
updatedAt: Date | null
@Field(() => Date, { nullable: true })
deletedAt: Date | null
}

View File

@ -10,19 +10,20 @@ import { TransactionLink } from '@entity/TransactionLink'
import { User } from '@entity/User'
import { UserContact } from '@entity/UserContact'
import { UserRole } from '@entity/UserRole'
import { UserInputError } from 'apollo-server-express'
import { ApolloServerTestClient } from 'apollo-server-testing'
import { GraphQLError } from 'graphql'
import { v4 as uuidv4, validate as validateUUID, version as versionUUID } from 'uuid'
import { OptInType } from '@enum/OptInType'
import { PasswordEncryptionType } from '@enum/PasswordEncryptionType'
import { RoleNames } from '@enum/RoleNames'
import { UserContactType } from '@enum/UserContactType'
import { ContributionLink } from '@model/ContributionLink'
import { testEnvironment, headerPushMock, resetToken, cleanDB } from '@test/helpers'
import { logger, i18n as localization } from '@test/testSetup'
import { subscribe } from '@/apis/KlicktippController'
import { RoleNames } from '@enum/RoleNames'
import { CONFIG } from '@/config'
import {
sendAccountActivationEmail,
@ -345,7 +346,7 @@ describe('UserResolver', () => {
peter.userRoles = [] as UserRole[]
peter.userRoles[0] = UserRole.create()
peter.userRoles[0].createdAt = new Date()
peter.userRoles[0].role = RoleNames.ROLE_NAME_ADMIN
peter.userRoles[0].role = RoleNames.ADMIN
peter.userRoles[0].userId = peter.id
await peter.userRoles[0].save()
@ -1411,7 +1412,7 @@ describe('UserResolver', () => {
expect.objectContaining({
firstName: 'Peter',
lastName: 'Lustig',
role: RoleNames.ROLE_NAME_ADMIN,
role: RoleNames.ADMIN,
}),
]),
},
@ -1520,7 +1521,7 @@ describe('UserResolver', () => {
await expect(
mutate({
mutation: setUserRole,
variables: { userId: 1, role: RoleNames.ROLE_NAME_ADMIN },
variables: { userId: 1, role: RoleNames.ADMIN },
}),
).resolves.toEqual(
expect.objectContaining({
@ -1549,7 +1550,7 @@ describe('UserResolver', () => {
await expect(
mutate({
mutation: setUserRole,
variables: { userId: user.id + 1, role: RoleNames.ROLE_NAME_ADMIN },
variables: { userId: user.id + 1, role: RoleNames.ADMIN },
}),
).resolves.toEqual(
expect.objectContaining({
@ -1566,7 +1567,7 @@ describe('UserResolver', () => {
// set Moderator-Role for Peter
const userRole = await UserRole.findOneOrFail({ where: { userId: admin.id } })
userRole.role = RoleNames.ROLE_NAME_MODERATOR
userRole.role = RoleNames.MODERATOR
userRole.userId = admin.id
await UserRole.save(userRole)
@ -1585,7 +1586,7 @@ describe('UserResolver', () => {
await expect(
mutate({
mutation: setUserRole,
variables: { userId: user.id, role: RoleNames.ROLE_NAME_ADMIN },
variables: { userId: user.id, role: RoleNames.ADMIN },
}),
).resolves.toEqual(
expect.objectContaining({
@ -1613,12 +1614,12 @@ describe('UserResolver', () => {
it('returns user with new moderator-role', async () => {
const result = await mutate({
mutation: setUserRole,
variables: { userId: user.id, role: RoleNames.ROLE_NAME_MODERATOR },
variables: { userId: user.id, role: RoleNames.MODERATOR },
})
expect(result).toEqual(
expect.objectContaining({
data: {
setUserRole: RoleNames.ROLE_NAME_MODERATOR,
setUserRole: RoleNames.MODERATOR,
},
}),
)
@ -1635,7 +1636,7 @@ describe('UserResolver', () => {
await expect(
mutate({
mutation: setUserRole,
variables: { userId: admin.id + 1, role: RoleNames.ROLE_NAME_ADMIN },
variables: { userId: admin.id + 1, role: RoleNames.ADMIN },
}),
).resolves.toEqual(
expect.objectContaining({
@ -1669,31 +1670,23 @@ describe('UserResolver', () => {
it('returns admin-rolename', async () => {
const result = await mutate({
mutation: setUserRole,
variables: { userId: user.id, role: RoleNames.ROLE_NAME_ADMIN },
variables: { userId: user.id, role: RoleNames.ADMIN },
})
expect(result).toEqual(
expect.objectContaining({
data: {
setUserRole: RoleNames.ROLE_NAME_ADMIN,
setUserRole: RoleNames.ADMIN,
},
}),
)
})
it('stores the ADMIN_USER_ROLE_SET event in the database', async () => {
const userContact = await UserContact.findOneOrFail({
where: { email: 'bibi@bloxberg.de' },
relations: ['user'],
})
const adminContact = await UserContact.findOneOrFail({
where: { email: 'peter@lustig.de' },
relations: ['user'],
})
await expect(DbEvent.find()).resolves.toContainEqual(
expect.objectContaining({
type: EventType.ADMIN_USER_ROLE_SET,
affectedUserId: userContact.user.id,
actingUserId: adminContact.user.id,
affectedUserId: user.id,
actingUserId: admin.id,
}),
)
})
@ -1703,12 +1696,12 @@ describe('UserResolver', () => {
it('returns date string', async () => {
const result = await mutate({
mutation: setUserRole,
variables: { userId: user.id, role: RoleNames.ROLE_NAME_MODERATOR },
variables: { userId: user.id, role: RoleNames.MODERATOR },
})
expect(result).toEqual(
expect.objectContaining({
data: {
setUserRole: RoleNames.ROLE_NAME_MODERATOR,
setUserRole: RoleNames.MODERATOR,
},
}),
)
@ -1716,19 +1709,11 @@ describe('UserResolver', () => {
})
it('stores the ADMIN_USER_ROLE_SET event in the database', async () => {
const userContact = await UserContact.findOneOrFail({
where: { email: 'bibi@bloxberg.de' },
relations: ['user'],
})
const adminContact = await UserContact.findOneOrFail({
where: { email: 'peter@lustig.de' },
relations: ['user'],
})
await expect(DbEvent.find()).resolves.toContainEqual(
expect.objectContaining({
type: EventType.ADMIN_USER_ROLE_SET,
affectedUserId: userContact.user.id,
actingUserId: adminContact.user.id,
affectedUserId: user.id,
actingUserId: admin.id,
}),
)
})
@ -1791,17 +1776,14 @@ describe('UserResolver', () => {
}),
).resolves.toEqual(
expect.objectContaining({
errors: [new GraphQLError('Not allowed to set user role=')],
errors: [
new UserInputError(
'Variable "$role" got invalid value "unknown rolename"; Value "unknown rolename" does not exist in "RoleNames" enum.',
),
],
}),
)
})
it('logs the error thrown', () => {
expect(logger.error).toBeCalledWith(
'Not allowed to set user role=',
'unknown rolename',
)
})
})
describe('user has already role to be set', () => {
@ -1810,12 +1792,12 @@ describe('UserResolver', () => {
jest.clearAllMocks()
await mutate({
mutation: setUserRole,
variables: { userId: user.id, role: RoleNames.ROLE_NAME_ADMIN },
variables: { userId: user.id, role: RoleNames.ADMIN },
})
await expect(
mutate({
mutation: setUserRole,
variables: { userId: user.id, role: RoleNames.ROLE_NAME_ADMIN },
variables: { userId: user.id, role: RoleNames.ADMIN },
}),
).resolves.toEqual(
expect.objectContaining({
@ -1825,10 +1807,7 @@ describe('UserResolver', () => {
})
it('logs the error thrown', () => {
expect(logger.error).toBeCalledWith(
'User already has role=',
RoleNames.ROLE_NAME_ADMIN,
)
expect(logger.error).toBeCalledWith('User already has role=', RoleNames.ADMIN)
})
})
@ -1837,12 +1816,12 @@ describe('UserResolver', () => {
jest.clearAllMocks()
await mutate({
mutation: setUserRole,
variables: { userId: user.id, role: RoleNames.ROLE_NAME_MODERATOR },
variables: { userId: user.id, role: RoleNames.MODERATOR },
})
await expect(
mutate({
mutation: setUserRole,
variables: { userId: user.id, role: RoleNames.ROLE_NAME_MODERATOR },
variables: { userId: user.id, role: RoleNames.MODERATOR },
}),
).resolves.toEqual(
expect.objectContaining({
@ -1852,10 +1831,7 @@ describe('UserResolver', () => {
})
it('logs the error thrown', () => {
expect(logger.error).toBeCalledWith(
'User already has role=',
RoleNames.ROLE_NAME_MODERATOR,
)
expect(logger.error).toBeCalledWith('User already has role=', RoleNames.MODERATOR)
})
})

View File

@ -15,6 +15,7 @@ import { v4 as uuidv4 } from 'uuid'
import { CreateUserArgs } from '@arg/CreateUserArgs'
import { Paginated } from '@arg/Paginated'
import { SearchUsersFilters } from '@arg/SearchUsersFilters'
import { SetUserRoleArgs } from '@arg/SetUserRoleArgs'
import { UnsecureLoginArgs } from '@arg/UnsecureLoginArgs'
import { UpdateUserInfosArgs } from '@arg/UpdateUserInfosArgs'
import { OptInType } from '@enum/OptInType'
@ -70,9 +71,6 @@ import { getKlicktippState } from './util/getKlicktippState'
import { setUserRole, deleteUserRole } from './util/modifyUserRole'
import { validateAlias } from './util/validateAlias'
import { RoleNames } from '@enum/RoleNames'
import { SetUserRoleArgs } from '@arg/SetUserRoleArgs'
const LANGUAGES = ['de', 'en', 'es', 'fr', 'nl']
const DEFAULT_LANGUAGE = 'de'
const isLanguage = (language: string): boolean => {

View File

@ -3,7 +3,7 @@ import { UserRole } from '@entity/UserRole'
import { LogError } from '@/server/LogError'
export async function setUserRole(user: DbUser, role: string | null): Promise<void> {
export async function setUserRole(user: DbUser, role: string | null | undefined): Promise<void> {
// if role should be set
if (role) {
// in case user has still no associated userRole

View File

@ -20,7 +20,6 @@ export const contributionLinkFactory = async (
mutation: login,
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
})
console.log('user=', user)
const variables = {
amount: contributionLink.amount,
memo: contributionLink.memo,
@ -33,6 +32,5 @@ export const contributionLinkFactory = async (
}
const result = await mutate({ mutation: createContributionLink, variables })
console.log('link...', result)
return result.data.createContributionLink
}

View File

@ -4,6 +4,7 @@ import { User } from '@entity/User'
import { ApolloServerTestClient } from 'apollo-server-testing'
import { RoleNames } from '@enum/RoleNames'
import { setUserRole } from '@/graphql/resolver/util/modifyUserRole'
import { createUser, setPassword } from '@/seeds/graphql/mutations'
import { UserInterface } from '@/seeds/users/UserInterface'
@ -37,10 +38,7 @@ export const userFactory = async (
if (user.createdAt || user.deletedAt || user.role) {
if (user.createdAt) dbUser.createdAt = user.createdAt
if (user.deletedAt) dbUser.deletedAt = user.deletedAt
if (
user.role &&
(user.role === RoleNames.ROLE_NAME_ADMIN || user.role === RoleNames.ROLE_NAME_MODERATOR)
) {
if (user.role && (user.role === RoleNames.ADMIN || user.role === RoleNames.MODERATOR)) {
await setUserRole(dbUser, user.role)
}
await dbUser.save()

View File

@ -95,8 +95,6 @@ export const searchUsers = gql`
emailConfirmationSend
deletedAt
roles
isAdmin
isModerator
}
}
}

View File

@ -10,5 +10,5 @@ export const peterLustig: UserInterface = {
createdAt: new Date('2020-11-25T10:48:43'),
emailChecked: true,
language: 'de',
role: RoleNames.ROLE_NAME_ADMIN,
role: RoleNames.ADMIN,
}