mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 01:46:07 +00:00
userRoles as OneToMany relation
This commit is contained in:
parent
dcea6871ef
commit
9c9a05e64f
@ -33,11 +33,12 @@ export const isAuthorized: AuthChecker<Context> = async ({ context }, rights) =>
|
||||
try {
|
||||
const user = await User.findOneOrFail({
|
||||
where: { gradidoID: decoded.gradidoID },
|
||||
relations: ['emailContact', 'userRole'],
|
||||
relations: ['emailContact', 'userRoles'],
|
||||
})
|
||||
console.log('isAuthorized user=', user)
|
||||
context.user = user
|
||||
context.role = user.userRole
|
||||
? user.userRole.role === ROLE_NAMES.ROLE_NAME_ADMIN
|
||||
context.role = user.userRoles
|
||||
? user.userRoles[0].role === ROLE_NAMES.ROLE_NAME_ADMIN
|
||||
? ROLE_ADMIN
|
||||
: ROLE_MODERATOR
|
||||
: ROLE_USER
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { ROLE_NAMES } from '@/auth/ROLES'
|
||||
import { User as dbUser } from '@entity/User'
|
||||
import { ObjectType, Field, Int } from 'type-graphql'
|
||||
|
||||
import { ROLE_NAMES } from '@/auth/ROLES'
|
||||
|
||||
import { KlickTipp } from './KlickTipp'
|
||||
|
||||
@ObjectType()
|
||||
@ -19,18 +20,11 @@ export class User {
|
||||
this.createdAt = user.createdAt
|
||||
this.language = user.language
|
||||
this.publisherId = user.publisherId
|
||||
if (user.userRole) {
|
||||
switch (user.userRole.role) {
|
||||
case ROLE_NAMES.ROLE_NAME_ADMIN:
|
||||
this.isAdmin = user.userRole.createdAt
|
||||
break
|
||||
case ROLE_NAMES.ROLE_NAME_MODERATOR:
|
||||
this.isModerator = user.userRole.createdAt
|
||||
break
|
||||
default:
|
||||
this.isAdmin = null
|
||||
this.isModerator = null
|
||||
}
|
||||
if (user.userRoles) {
|
||||
this.roles = [] as string[]
|
||||
user.userRoles.forEach((userRole) => {
|
||||
this.roles?.push(userRole.role)
|
||||
})
|
||||
}
|
||||
this.klickTipp = null
|
||||
this.hasElopage = null
|
||||
@ -75,15 +69,34 @@ export class User {
|
||||
@Field(() => Int, { nullable: true })
|
||||
publisherId: number | null
|
||||
|
||||
@Field(() => Date, { nullable: true })
|
||||
isAdmin: Date | null
|
||||
|
||||
@Field(() => Date, { nullable: true })
|
||||
isModerator: Date | null
|
||||
|
||||
@Field(() => KlickTipp, { nullable: true })
|
||||
klickTipp: KlickTipp | null
|
||||
|
||||
@Field(() => Boolean, { nullable: true })
|
||||
hasElopage: boolean | null
|
||||
|
||||
@Field(() => [String], { nullable: true })
|
||||
roles: string[] | null
|
||||
}
|
||||
|
||||
export function isAdmin(user: User): boolean {
|
||||
if (user.roles) {
|
||||
for (const role of user.roles) {
|
||||
if (role === ROLE_NAMES.ROLE_NAME_ADMIN) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export function isModerator(user: User): boolean {
|
||||
if (user.roles) {
|
||||
for (const role of user.roles) {
|
||||
if (role === ROLE_NAMES.ROLE_NAME_MODERATOR) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@ -2,6 +2,8 @@ import { User } from '@entity/User'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { ObjectType, Field, Int } from 'type-graphql'
|
||||
|
||||
import { ROLE_NAMES } from '@/auth/ROLES'
|
||||
|
||||
@ObjectType()
|
||||
export class UserAdmin {
|
||||
constructor(user: User, creation: Decimal[], hasElopage: boolean, emailConfirmationSend: string) {
|
||||
@ -14,8 +16,18 @@ export class UserAdmin {
|
||||
this.hasElopage = hasElopage
|
||||
this.deletedAt = user.deletedAt
|
||||
this.emailConfirmationSend = emailConfirmationSend
|
||||
if (user.userRole) {
|
||||
this.isAdmin = user.userRole?.createdAt
|
||||
if (user.userRoles) {
|
||||
switch (user.userRoles[0].role) {
|
||||
case ROLE_NAMES.ROLE_NAME_ADMIN:
|
||||
this.isAdmin = user.userRoles[0].createdAt
|
||||
break
|
||||
case ROLE_NAMES.ROLE_NAME_MODERATOR:
|
||||
this.isModerator = user.userRoles[0].createdAt
|
||||
break
|
||||
default:
|
||||
this.isAdmin = null
|
||||
this.isModerator = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,6 +60,9 @@ export class UserAdmin {
|
||||
|
||||
@Field(() => Date, { nullable: true })
|
||||
isAdmin: Date | null
|
||||
|
||||
@Field(() => Date, { nullable: true })
|
||||
isModerator: Date | null
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
|
||||
@ -9,6 +9,7 @@ import { Event as DbEvent } from '@entity/Event'
|
||||
import { TransactionLink } from '@entity/TransactionLink'
|
||||
import { User } from '@entity/User'
|
||||
import { UserContact } from '@entity/UserContact'
|
||||
import { UserRole } from '@entity/UserRole'
|
||||
import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { GraphQLError } from 'graphql'
|
||||
import { v4 as uuidv4, validate as validateUUID, version as versionUUID } from 'uuid'
|
||||
@ -21,6 +22,7 @@ import { testEnvironment, headerPushMock, resetToken, cleanDB } from '@test/help
|
||||
import { logger, i18n as localization } from '@test/testSetup'
|
||||
|
||||
import { subscribe } from '@/apis/KlicktippController'
|
||||
import { ROLE_NAMES } from '@/auth/ROLES'
|
||||
import { CONFIG } from '@/config'
|
||||
import {
|
||||
sendAccountActivationEmail,
|
||||
@ -62,8 +64,6 @@ import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||
import { stephenHawking } from '@/seeds/users/stephen-hawking'
|
||||
import { printTimeDuration } from '@/util/time'
|
||||
import { objectValuesToArray } from '@/util/utilities'
|
||||
import { UserRole } from '@entity/UserRole'
|
||||
import { ROLE_NAMES } from '@/auth/ROLES'
|
||||
|
||||
jest.mock('@/emails/sendEmailVariants', () => {
|
||||
const originalModule = jest.requireActual('@/emails/sendEmailVariants')
|
||||
@ -142,7 +142,7 @@ describe('UserResolver', () => {
|
||||
describe('valid input data', () => {
|
||||
// let loginEmailOptIn: LoginEmailOptIn[]
|
||||
beforeAll(async () => {
|
||||
user = await User.find({ relations: ['emailContact', 'userRole'] })
|
||||
user = await User.find({ relations: ['emailContact', 'userRoles'] })
|
||||
// loginEmailOptIn = await LoginEmailOptIn.find()
|
||||
emailVerificationCode = user[0].emailContact.emailVerificationCode.toString()
|
||||
})
|
||||
@ -164,7 +164,7 @@ describe('UserResolver', () => {
|
||||
createdAt: expect.any(Date),
|
||||
// emailChecked: false,
|
||||
language: 'de',
|
||||
userRole: null,
|
||||
userRoles: null,
|
||||
deletedAt: null,
|
||||
publisherId: 1234,
|
||||
referrerId: null,
|
||||
@ -338,16 +338,16 @@ describe('UserResolver', () => {
|
||||
// make Peter Lustig Admin
|
||||
let peter = await User.findOneOrFail({
|
||||
where: { id: user[0].id },
|
||||
relations: ['userRole'],
|
||||
relations: ['userRoles'],
|
||||
})
|
||||
console.log('vorher peter=', peter)
|
||||
await mutate({
|
||||
mutation: setUserRole,
|
||||
variables: { userId: user[0].id, isAdmin: true },
|
||||
variables: { userId: user[0].id, role: ROLE_NAMES.ROLE_NAME_ADMIN },
|
||||
})
|
||||
peter = await User.findOneOrFail({
|
||||
where: { id: user[0].id },
|
||||
relations: ['userRole'],
|
||||
relations: ['userRoles'],
|
||||
})
|
||||
console.log('nachher peter=', peter)
|
||||
|
||||
@ -704,7 +704,7 @@ describe('UserResolver', () => {
|
||||
firstName: 'Bibi',
|
||||
hasElopage: false,
|
||||
id: expect.any(Number),
|
||||
userRole: null,
|
||||
userRoles: null,
|
||||
klickTipp: {
|
||||
newsletterState: false,
|
||||
},
|
||||
@ -961,7 +961,7 @@ describe('UserResolver', () => {
|
||||
|
||||
beforeAll(async () => {
|
||||
await mutate({ mutation: login, variables })
|
||||
user = await User.find({ relations: ['userRole'] })
|
||||
user = await User.find({ relations: ['userRoles'] })
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
@ -981,7 +981,7 @@ describe('UserResolver', () => {
|
||||
},
|
||||
hasElopage: false,
|
||||
publisherId: 1234,
|
||||
userRole: null,
|
||||
userRoles: null,
|
||||
},
|
||||
},
|
||||
}),
|
||||
@ -1501,7 +1501,7 @@ describe('UserResolver', () => {
|
||||
firstName: 'Bibi',
|
||||
hasElopage: false,
|
||||
id: expect.any(Number),
|
||||
userRole: null,
|
||||
userRoles: null,
|
||||
klickTipp: {
|
||||
newsletterState: false,
|
||||
},
|
||||
@ -1526,7 +1526,10 @@ describe('UserResolver', () => {
|
||||
describe('unauthenticated', () => {
|
||||
it('returns an error', async () => {
|
||||
await expect(
|
||||
mutate({ mutation: setUserRole, variables: { userId: 1, isAdmin: true } }),
|
||||
mutate({
|
||||
mutation: setUserRole,
|
||||
variables: { userId: 1, role: ROLE_NAMES.ROLE_NAME_ADMIN },
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('401 Unauthorized')],
|
||||
@ -1552,7 +1555,10 @@ describe('UserResolver', () => {
|
||||
|
||||
it('returns an error', async () => {
|
||||
await expect(
|
||||
mutate({ mutation: setUserRole, variables: { userId: user.id + 1, isAdmin: true } }),
|
||||
mutate({
|
||||
mutation: setUserRole,
|
||||
variables: { userId: user.id + 1, role: ROLE_NAMES.ROLE_NAME_ADMIN },
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('401 Unauthorized')],
|
||||
@ -1579,7 +1585,10 @@ describe('UserResolver', () => {
|
||||
it('throws an error', async () => {
|
||||
jest.clearAllMocks()
|
||||
await expect(
|
||||
mutate({ mutation: setUserRole, variables: { userId: admin.id + 1, isAdmin: true } }),
|
||||
mutate({
|
||||
mutation: setUserRole,
|
||||
variables: { userId: admin.id + 1, role: ROLE_NAMES.ROLE_NAME_ADMIN },
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('Could not find user with given ID')],
|
||||
@ -1602,7 +1611,7 @@ describe('UserResolver', () => {
|
||||
it('returns date string', async () => {
|
||||
const result = await mutate({
|
||||
mutation: setUserRole,
|
||||
variables: { userId: user.id, isAdmin: true },
|
||||
variables: { userId: user.id, role: ROLE_NAMES.ROLE_NAME_ADMIN },
|
||||
})
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
@ -1636,7 +1645,7 @@ describe('UserResolver', () => {
|
||||
describe('to usual user', () => {
|
||||
it('returns null', async () => {
|
||||
await expect(
|
||||
mutate({ mutation: setUserRole, variables: { userId: user.id, isAdmin: false } }),
|
||||
mutate({ mutation: setUserRole, variables: { userId: user.id, role: null } }),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
data: {
|
||||
@ -1654,7 +1663,7 @@ describe('UserResolver', () => {
|
||||
it('throws an error', async () => {
|
||||
jest.clearAllMocks()
|
||||
await expect(
|
||||
mutate({ mutation: setUserRole, variables: { userId: admin.id, isAdmin: false } }),
|
||||
mutate({ mutation: setUserRole, variables: { userId: admin.id, role: null } }),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('Administrator can not change his own role')],
|
||||
@ -1672,10 +1681,13 @@ describe('UserResolver', () => {
|
||||
jest.clearAllMocks()
|
||||
await mutate({
|
||||
mutation: setUserRole,
|
||||
variables: { userId: user.id, isAdmin: true },
|
||||
variables: { userId: user.id, role: ROLE_NAMES.ROLE_NAME_ADMIN },
|
||||
})
|
||||
await expect(
|
||||
mutate({ mutation: setUserRole, variables: { userId: user.id, isAdmin: true } }),
|
||||
mutate({
|
||||
mutation: setUserRole,
|
||||
variables: { userId: user.id, role: ROLE_NAMES.ROLE_NAME_ADMIN },
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('User is already admin')],
|
||||
@ -1693,10 +1705,10 @@ describe('UserResolver', () => {
|
||||
jest.clearAllMocks()
|
||||
await mutate({
|
||||
mutation: setUserRole,
|
||||
variables: { userId: user.id, isAdmin: false },
|
||||
variables: { userId: user.id, role: null },
|
||||
})
|
||||
await expect(
|
||||
mutate({ mutation: setUserRole, variables: { userId: user.id, isAdmin: false } }),
|
||||
mutate({ mutation: setUserRole, variables: { userId: user.id, role: null } }),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('User is already an usual user')],
|
||||
|
||||
@ -706,18 +706,27 @@ export class UserResolver {
|
||||
}
|
||||
|
||||
@Authorized([RIGHTS.SET_USER_ROLE])
|
||||
@Mutation(() => Date, { nullable: true })
|
||||
@Mutation(() => String, { nullable: true })
|
||||
async setUserRole(
|
||||
@Arg('userId', () => Int)
|
||||
userId: number,
|
||||
@Arg('isAdmin', () => Boolean)
|
||||
isAdmin: boolean,
|
||||
@Arg('role', () => String)
|
||||
role: string,
|
||||
@Ctx()
|
||||
context: Context,
|
||||
): Promise<Date | null> {
|
||||
): Promise<string | null> {
|
||||
switch (role) {
|
||||
case null:
|
||||
case ROLE_NAMES.ROLE_NAME_ADMIN:
|
||||
case ROLE_NAMES.ROLE_NAME_MODERATOR:
|
||||
logger.debug('setUserRole=', role)
|
||||
break
|
||||
default:
|
||||
throw new LogError('Not allowed to set user role=', role)
|
||||
}
|
||||
const user = await DbUser.findOne({
|
||||
where: { id: userId },
|
||||
relations: ['userRole'],
|
||||
relations: ['userRoles'],
|
||||
})
|
||||
// user exists ?
|
||||
if (!user) {
|
||||
@ -728,37 +737,32 @@ export class UserResolver {
|
||||
if (moderator.id === userId) {
|
||||
throw new LogError('Administrator can not change his own role')
|
||||
}
|
||||
// change userRole
|
||||
switch (user.userRole) {
|
||||
case null:
|
||||
if (isAdmin) {
|
||||
user.userRole = UserRole.create()
|
||||
user.userRole.createdAt = new Date()
|
||||
user.userRole.role = ROLE_NAMES.ROLE_NAME_ADMIN
|
||||
user.userRole.userId = user.id
|
||||
} else {
|
||||
throw new LogError('User is already an usual user')
|
||||
if (isUserInRole(user, role)) {
|
||||
throw new LogError('User already has role=', role)
|
||||
}
|
||||
// if user role should be deleted by role=null as parameter
|
||||
if (role === null && user.userRoles) {
|
||||
for (const usrRole of user.userRoles) {
|
||||
await UserRole.delete(usrRole)
|
||||
}
|
||||
user.userRoles = undefined
|
||||
} else {
|
||||
if (!isUserInRole(user, role)) {
|
||||
if (user.userRoles === undefined) {
|
||||
user.userRoles = [] as UserRole[]
|
||||
user.userRoles[0] = UserRole.create()
|
||||
}
|
||||
break
|
||||
default:
|
||||
if (!isAdmin) {
|
||||
if (user.userRole) {
|
||||
await UserRole.delete(user.userRole)
|
||||
}
|
||||
user.userRole = undefined
|
||||
} else {
|
||||
throw new LogError('User is already admin')
|
||||
}
|
||||
break
|
||||
user.userRoles[0].createdAt = new Date()
|
||||
user.userRoles[0].role = role
|
||||
user.userRoles[0].userId = user.id
|
||||
} else {
|
||||
throw new LogError('User already is in role=', role)
|
||||
}
|
||||
}
|
||||
await user.save()
|
||||
await EVENT_ADMIN_USER_ROLE_SET(user, moderator)
|
||||
const newUser = await DbUser.findOne({ id: userId })
|
||||
return newUser
|
||||
? newUser.userRole && newUser.userRole.role === ROLE_NAMES.ROLE_NAME_ADMIN
|
||||
? newUser.userRole.createdAt
|
||||
: null
|
||||
: null
|
||||
const newUser = await DbUser.findOne({ id: userId }, { relations: ['userRoles'] })
|
||||
return newUser?.userRoles ? newUser.userRoles[0].role : null
|
||||
}
|
||||
|
||||
@Authorized([RIGHTS.DELETE_USER])
|
||||
@ -850,7 +854,7 @@ export async function findUserByEmail(email: string): Promise<DbUser> {
|
||||
})
|
||||
const dbUser = dbUserContact.user
|
||||
dbUser.emailContact = dbUserContact
|
||||
dbUser.userRole = await UserRole.findOne({ userId: dbUser.id })
|
||||
dbUser.userRoles = await UserRole.find({ userId: dbUser.id })
|
||||
return dbUser
|
||||
}
|
||||
|
||||
@ -875,3 +879,14 @@ const isEmailVerificationCodeValid = (updatedAt: Date): boolean => {
|
||||
const canEmailResend = (updatedAt: Date): boolean => {
|
||||
return !isTimeExpired(updatedAt, CONFIG.EMAIL_CODE_REQUEST_TIME)
|
||||
}
|
||||
|
||||
export function isUserInRole(user: DbUser, role: string): boolean {
|
||||
if (user?.userRoles) {
|
||||
for (const usrRole of user.userRoles) {
|
||||
if (usrRole.role === role) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ export const contributionLinkFactory = async (
|
||||
mutation: login,
|
||||
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||
})
|
||||
|
||||
console.log('contributionlinkfactory user=', user)
|
||||
const variables = {
|
||||
amount: contributionLink.amount,
|
||||
memo: contributionLink.memo,
|
||||
|
||||
@ -37,15 +37,20 @@ export const userFactory = async (
|
||||
// get last changes of user from database
|
||||
dbUser = await User.findOneOrFail({ id }, { relations: ['emailContact', 'userRole'] })
|
||||
|
||||
if (user.createdAt || user.deletedAt || user.isAdmin) {
|
||||
if (user.createdAt || user.deletedAt || user.role) {
|
||||
if (user.createdAt) dbUser.createdAt = user.createdAt
|
||||
if (user.deletedAt) dbUser.deletedAt = user.deletedAt
|
||||
if (user.isAdmin) {
|
||||
dbUser.userRole = UserRole.create()
|
||||
dbUser.userRole.createdAt = new Date()
|
||||
dbUser.userRole.role = ROLE_NAMES.ROLE_NAME_ADMIN
|
||||
dbUser.userRole.userId = dbUser.id
|
||||
await dbUser.userRole.save()
|
||||
if (user.role) {
|
||||
dbUser.userRoles = [] as UserRole[]
|
||||
dbUser.userRoles[0] = UserRole.create()
|
||||
dbUser.userRoles[0].createdAt = new Date()
|
||||
if (user.role === ROLE_NAMES.ROLE_NAME_ADMIN) {
|
||||
dbUser.userRoles[0].role = ROLE_NAMES.ROLE_NAME_ADMIN
|
||||
} else if (user.role === ROLE_NAMES.ROLE_NAME_MODERATOR) {
|
||||
dbUser.userRoles[0].role = ROLE_NAMES.ROLE_NAME_MODERATOR
|
||||
}
|
||||
dbUser.userRoles[0].userId = dbUser.id
|
||||
await dbUser.userRoles[0].save()
|
||||
}
|
||||
await dbUser.save()
|
||||
}
|
||||
|
||||
@ -8,5 +8,5 @@ export interface UserInterface {
|
||||
language?: string
|
||||
deletedAt?: Date
|
||||
publisherId?: number
|
||||
isAdmin?: boolean
|
||||
role?: string
|
||||
}
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { ROLE_NAMES } from '@/auth/ROLES'
|
||||
|
||||
import { UserInterface } from './UserInterface'
|
||||
|
||||
export const peterLustig: UserInterface = {
|
||||
@ -8,5 +10,5 @@ export const peterLustig: UserInterface = {
|
||||
createdAt: new Date('2020-11-25T10:48:43'),
|
||||
emailChecked: true,
|
||||
language: 'de',
|
||||
isAdmin: true,
|
||||
role: ROLE_NAMES.ROLE_NAME_ADMIN,
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ const communityDbUser: dbUser = {
|
||||
createdAt: new Date(),
|
||||
// emailChecked: false,
|
||||
language: '',
|
||||
userRole: undefined,
|
||||
userRoles: undefined,
|
||||
publisherId: 0,
|
||||
// default password encryption type
|
||||
passwordEncryptionType: PasswordEncryptionType.NO_PASSWORD,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user