Fix new linting issues
This commit is contained in:
parent
9cc651ffc7
commit
2d8a6f49d7
@ -105,7 +105,7 @@ test('mapping private rooms', () => {
|
|||||||
|
|
||||||
test('mapping live chats', () => {
|
test('mapping live chats', () => {
|
||||||
expect(() =>
|
expect(() =>
|
||||||
mapRoom({ _id: 'liveChatId', t: RcRoomTypes.live })
|
mapRoom({ _id: 'liveChatId', t: RcRoomTypes.live }),
|
||||||
).toThrowError('Room type l is unknown')
|
).toThrowError('Room type l is unknown')
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -118,15 +118,15 @@ test('getting creator', () => {
|
|||||||
|
|
||||||
test('creating memberships for direct chats', async () => {
|
test('creating memberships for direct chats', async () => {
|
||||||
await expect(createDirectChatMemberships(rcDirectChat)).resolves.toBe(
|
await expect(createDirectChatMemberships(rcDirectChat)).resolves.toBe(
|
||||||
undefined
|
undefined,
|
||||||
)
|
)
|
||||||
expect(mockedStorage.createMembership).toHaveBeenCalledWith(
|
expect(mockedStorage.createMembership).toHaveBeenCalledWith(
|
||||||
rcDirectChat._id,
|
rcDirectChat._id,
|
||||||
rcDirectChat.uids[0]
|
rcDirectChat.uids[0],
|
||||||
)
|
)
|
||||||
expect(mockedStorage.createMembership).toHaveBeenCalledWith(
|
expect(mockedStorage.createMembership).toHaveBeenCalledWith(
|
||||||
rcDirectChat._id,
|
rcDirectChat._id,
|
||||||
rcDirectChat.uids[1]
|
rcDirectChat.uids[1],
|
||||||
)
|
)
|
||||||
expect(mockedStorage.createMembership).toHaveBeenCalledTimes(2)
|
expect(mockedStorage.createMembership).toHaveBeenCalledTimes(2)
|
||||||
|
|
||||||
@ -137,7 +137,7 @@ test('creating memberships for direct chats', async () => {
|
|||||||
...rcDirectChat,
|
...rcDirectChat,
|
||||||
_id: 'hoihoi',
|
_id: 'hoihoi',
|
||||||
uids: ['hoi', 'hoi'],
|
uids: ['hoi', 'hoi'],
|
||||||
})
|
}),
|
||||||
).resolves.toBe(undefined)
|
).resolves.toBe(undefined)
|
||||||
|
|
||||||
expect(mockedStorage.createMembership).toHaveBeenCalledWith('hoihoi', 'hoi')
|
expect(mockedStorage.createMembership).toHaveBeenCalledWith('hoihoi', 'hoi')
|
||||||
@ -152,7 +152,7 @@ test('registering room', async () => {
|
|||||||
expect(mockedAxios.post).toHaveBeenCalledWith(
|
expect(mockedAxios.post).toHaveBeenCalledWith(
|
||||||
'/_matrix/client/v3/createRoom',
|
'/_matrix/client/v3/createRoom',
|
||||||
rcPublicRoom,
|
rcPublicRoom,
|
||||||
sessionOption
|
sessionOption,
|
||||||
)
|
)
|
||||||
expect(mockedAxios.post).toHaveBeenCalledTimes(1)
|
expect(mockedAxios.post).toHaveBeenCalledTimes(1)
|
||||||
mockedAxios.post.mockClear()
|
mockedAxios.post.mockClear()
|
||||||
@ -160,12 +160,12 @@ test('registering room', async () => {
|
|||||||
|
|
||||||
test('inviting member', async () => {
|
test('inviting member', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
inviteMember('inviteme', room_id, sessionOption)
|
inviteMember('inviteme', room_id, sessionOption),
|
||||||
).resolves.not.toThrow()
|
).resolves.not.toThrow()
|
||||||
expect(mockedAxios.post).toHaveBeenCalledWith(
|
expect(mockedAxios.post).toHaveBeenCalledWith(
|
||||||
`/_matrix/client/v3/rooms/${room_id}/invite`,
|
`/_matrix/client/v3/rooms/${room_id}/invite`,
|
||||||
{ user_id: 'inviteme' },
|
{ user_id: 'inviteme' },
|
||||||
sessionOption
|
sessionOption,
|
||||||
)
|
)
|
||||||
expect(mockedAxios.post).toHaveBeenCalledTimes(1)
|
expect(mockedAxios.post).toHaveBeenCalledTimes(1)
|
||||||
mockedAxios.post.mockClear()
|
mockedAxios.post.mockClear()
|
||||||
@ -180,13 +180,13 @@ test('accepting invitation by joining the room', async () => {
|
|||||||
accessToken: 'secretAuthToken',
|
accessToken: 'secretAuthToken',
|
||||||
type: entities[Entity.Users].mappingType,
|
type: entities[Entity.Users].mappingType,
|
||||||
},
|
},
|
||||||
room_id
|
room_id,
|
||||||
)
|
),
|
||||||
).resolves.toBe(undefined)
|
).resolves.toBe(undefined)
|
||||||
expect(mockedAxios.post).toHaveBeenCalledWith(
|
expect(mockedAxios.post).toHaveBeenCalledWith(
|
||||||
`/_matrix/client/v3/join/${room_id}`,
|
`/_matrix/client/v3/join/${room_id}`,
|
||||||
{},
|
{},
|
||||||
{ headers: sessionOption.headers }
|
{ headers: sessionOption.headers },
|
||||||
)
|
)
|
||||||
expect(mockedAxios.post).toHaveBeenCalledTimes(1)
|
expect(mockedAxios.post).toHaveBeenCalledTimes(1)
|
||||||
mockedAxios.post.mockClear()
|
mockedAxios.post.mockClear()
|
||||||
@ -209,7 +209,7 @@ test('filtering members', async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mockedStorage.getMapping.mockImplementation(async (rcId, type) =>
|
mockedStorage.getMapping.mockImplementation(async (rcId, type) =>
|
||||||
rcId.includes('excluded') || !rcId ? null : mockMapping(rcId, type)
|
rcId.includes('excluded') || !rcId ? null : mockMapping(rcId, type),
|
||||||
)
|
)
|
||||||
|
|
||||||
await expect(getFilteredMembers(members, members[0])).resolves.toStrictEqual([
|
await expect(getFilteredMembers(members, members[0])).resolves.toStrictEqual([
|
||||||
@ -218,21 +218,21 @@ test('filtering members', async () => {
|
|||||||
])
|
])
|
||||||
expect(mockedStorage.getMapping).toBeCalledWith(
|
expect(mockedStorage.getMapping).toBeCalledWith(
|
||||||
'existingUser',
|
'existingUser',
|
||||||
entities[Entity.Users].mappingType
|
entities[Entity.Users].mappingType,
|
||||||
)
|
)
|
||||||
expect(mockedStorage.getMapping).toBeCalledWith(
|
expect(mockedStorage.getMapping).toBeCalledWith(
|
||||||
'otherExistingUser',
|
'otherExistingUser',
|
||||||
entities[Entity.Users].mappingType
|
entities[Entity.Users].mappingType,
|
||||||
)
|
)
|
||||||
expect(mockedStorage.getMapping).toBeCalledWith(
|
expect(mockedStorage.getMapping).toBeCalledWith(
|
||||||
'excludedUser',
|
'excludedUser',
|
||||||
entities[Entity.Users].mappingType
|
entities[Entity.Users].mappingType,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('creating mapping', async () => {
|
test('creating mapping', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
createMapping(rcPublicRoom._id, { ...mapRoom(rcPublicRoom), room_id })
|
createMapping(rcPublicRoom._id, { ...mapRoom(rcPublicRoom), room_id }),
|
||||||
).resolves.toBe(undefined)
|
).resolves.toBe(undefined)
|
||||||
expect(mockedStorage.save).toHaveBeenCalledWith({
|
expect(mockedStorage.save).toHaveBeenCalledWith({
|
||||||
rcId: rcPublicRoom._id,
|
rcId: rcPublicRoom._id,
|
||||||
|
|||||||
@ -98,14 +98,14 @@ export function getCreator(rcRoom: RcRoom): string {
|
|||||||
return rcRoom.uids[0]
|
return rcRoom.uids[0]
|
||||||
} else {
|
} else {
|
||||||
log.warn(
|
log.warn(
|
||||||
`Creator ID could not be determined for room ${rcRoom.name} of type ${rcRoom.t}. This is normal for the default room.`
|
`Creator ID could not be determined for room ${rcRoom.name} of type ${rcRoom.t}. This is normal for the default room.`,
|
||||||
)
|
)
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createDirectChatMemberships(
|
export async function createDirectChatMemberships(
|
||||||
rcRoom: RcRoom
|
rcRoom: RcRoom,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (rcRoom.t == RcRoomTypes.direct && rcRoom.uids) {
|
if (rcRoom.t == RcRoomTypes.direct && rcRoom.uids) {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
@ -113,13 +113,13 @@ export async function createDirectChatMemberships(
|
|||||||
.map(async (uid) => {
|
.map(async (uid) => {
|
||||||
await createMembership(rcRoom._id, uid)
|
await createMembership(rcRoom._id, uid)
|
||||||
log.debug(`${uid} membership in direct chat ${rcRoom._id} created`)
|
log.debug(`${uid} membership in direct chat ${rcRoom._id} created`)
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getCreatorSessionOptions(
|
export async function getCreatorSessionOptions(
|
||||||
creatorId: string
|
creatorId: string,
|
||||||
): Promise<SessionOptions | object> {
|
): Promise<SessionOptions | object> {
|
||||||
if (creatorId) {
|
if (creatorId) {
|
||||||
try {
|
try {
|
||||||
@ -135,13 +135,13 @@ export async function getCreatorSessionOptions(
|
|||||||
|
|
||||||
export async function registerRoom(
|
export async function registerRoom(
|
||||||
room: MatrixRoom,
|
room: MatrixRoom,
|
||||||
creatorSessionOptions: SessionOptions | object
|
creatorSessionOptions: SessionOptions | object,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
return (
|
return (
|
||||||
await axios.post(
|
await axios.post(
|
||||||
'/_matrix/client/v3/createRoom',
|
'/_matrix/client/v3/createRoom',
|
||||||
room,
|
room,
|
||||||
creatorSessionOptions
|
creatorSessionOptions,
|
||||||
)
|
)
|
||||||
).data.room_id
|
).data.room_id
|
||||||
}
|
}
|
||||||
@ -149,39 +149,39 @@ export async function registerRoom(
|
|||||||
export async function inviteMember(
|
export async function inviteMember(
|
||||||
inviteeId: string,
|
inviteeId: string,
|
||||||
roomId: string,
|
roomId: string,
|
||||||
creatorSessionOptions: SessionOptions | object
|
creatorSessionOptions: SessionOptions | object,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
log.http(`Invite member ${inviteeId}`)
|
log.http(`Invite member ${inviteeId}`)
|
||||||
await axios.post(
|
await axios.post(
|
||||||
`/_matrix/client/v3/rooms/${roomId}/invite`,
|
`/_matrix/client/v3/rooms/${roomId}/invite`,
|
||||||
{ user_id: inviteeId },
|
{ user_id: inviteeId },
|
||||||
creatorSessionOptions
|
creatorSessionOptions,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function acceptInvitation(
|
export async function acceptInvitation(
|
||||||
inviteeMapping: IdMapping,
|
inviteeMapping: IdMapping,
|
||||||
roomId: string
|
roomId: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
log.http(
|
log.http(
|
||||||
`Accepting invitation for member ${inviteeMapping.rcId} aka. ${inviteeMapping.matrixId}`
|
`Accepting invitation for member ${inviteeMapping.rcId} aka. ${inviteeMapping.matrixId}`,
|
||||||
)
|
)
|
||||||
await axios.post(
|
await axios.post(
|
||||||
`/_matrix/client/v3/join/${roomId}`,
|
`/_matrix/client/v3/join/${roomId}`,
|
||||||
{},
|
{},
|
||||||
formatUserSessionOptions(inviteeMapping.accessToken || '')
|
formatUserSessionOptions(inviteeMapping.accessToken || ''),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getFilteredMembers(
|
export async function getFilteredMembers(
|
||||||
rcMemberIds: string[],
|
rcMemberIds: string[],
|
||||||
creatorId: string
|
creatorId: string,
|
||||||
): Promise<IdMapping[]> {
|
): Promise<IdMapping[]> {
|
||||||
const memberMappings = (
|
const memberMappings = (
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
rcMemberIds
|
rcMemberIds
|
||||||
.filter((rcMemberId) => rcMemberId != creatorId)
|
.filter((rcMemberId) => rcMemberId != creatorId)
|
||||||
.map(async (rcMemberId) => await getMapping(rcMemberId, 0))
|
.map(async (rcMemberId) => await getMapping(rcMemberId, 0)),
|
||||||
)
|
)
|
||||||
).filter((memberMapping): memberMapping is IdMapping => memberMapping != null)
|
).filter((memberMapping): memberMapping is IdMapping => memberMapping != null)
|
||||||
return memberMappings
|
return memberMappings
|
||||||
@ -189,7 +189,7 @@ export async function getFilteredMembers(
|
|||||||
|
|
||||||
export async function createMapping(
|
export async function createMapping(
|
||||||
rcId: string,
|
rcId: string,
|
||||||
matrixRoom: MatrixRoom
|
matrixRoom: MatrixRoom,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const roomMapping = new IdMapping()
|
const roomMapping = new IdMapping()
|
||||||
roomMapping.rcId = rcId
|
roomMapping.rcId = rcId
|
||||||
@ -218,7 +218,7 @@ async function handleMemberships(
|
|||||||
rcRoomId: string,
|
rcRoomId: string,
|
||||||
room: MatrixRoom,
|
room: MatrixRoom,
|
||||||
creatorId: string,
|
creatorId: string,
|
||||||
creatorSessionOptions: object | SessionOptions
|
creatorSessionOptions: object | SessionOptions,
|
||||||
) {
|
) {
|
||||||
const rcMemberIds = await getMemberships(rcRoomId)
|
const rcMemberIds = await getMemberships(rcRoomId)
|
||||||
const memberMappings = await getFilteredMembers(rcMemberIds, creatorId)
|
const memberMappings = await getFilteredMembers(rcMemberIds, creatorId)
|
||||||
@ -226,13 +226,13 @@ async function handleMemberships(
|
|||||||
`Inviting members to room ${
|
`Inviting members to room ${
|
||||||
room.room_alias_name || room.name || room.room_id
|
room.room_alias_name || room.name || room.room_id
|
||||||
}:`,
|
}:`,
|
||||||
memberMappings.map((mapping) => mapping.matrixId)
|
memberMappings.map((mapping) => mapping.matrixId),
|
||||||
)
|
)
|
||||||
log.debug(
|
log.debug(
|
||||||
'Excluded members:',
|
'Excluded members:',
|
||||||
rcMemberIds.filter(
|
rcMemberIds.filter(
|
||||||
(x) => !memberMappings.map((mapping) => mapping.rcId).includes(x)
|
(x) => !memberMappings.map((mapping) => mapping.rcId).includes(x),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
@ -240,10 +240,10 @@ async function handleMemberships(
|
|||||||
await inviteMember(
|
await inviteMember(
|
||||||
memberMapping.matrixId || '',
|
memberMapping.matrixId || '',
|
||||||
room.room_id || '',
|
room.room_id || '',
|
||||||
creatorSessionOptions
|
creatorSessionOptions,
|
||||||
)
|
)
|
||||||
await acceptInvitation(memberMapping, room.room_id || '')
|
await acceptInvitation(memberMapping, room.room_id || '')
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -73,11 +73,11 @@ test('creating users', async () => {
|
|||||||
|
|
||||||
expect(mockedStorage.createMembership).toHaveBeenCalledWith(
|
expect(mockedStorage.createMembership).toHaveBeenCalledWith(
|
||||||
rcUser.__rooms[0],
|
rcUser.__rooms[0],
|
||||||
rcUser._id
|
rcUser._id,
|
||||||
)
|
)
|
||||||
expect(mockedStorage.createMembership).toHaveBeenCalledWith(
|
expect(mockedStorage.createMembership).toHaveBeenCalledWith(
|
||||||
rcUser.__rooms[1],
|
rcUser.__rooms[1],
|
||||||
rcUser._id
|
rcUser._id,
|
||||||
)
|
)
|
||||||
expect(mockedStorage.createMembership).toHaveBeenCalledTimes(2)
|
expect(mockedStorage.createMembership).toHaveBeenCalledTimes(2)
|
||||||
})
|
})
|
||||||
@ -88,7 +88,7 @@ test('users are excluded', () => {
|
|||||||
expect(userIsExcluded({ ...rcUser, username: 'excludedUser2' })).toBeTruthy()
|
expect(userIsExcluded({ ...rcUser, username: 'excludedUser2' })).toBeTruthy()
|
||||||
expect(userIsExcluded({ ...rcUser, roles: ['bot'] })).toBeTruthy()
|
expect(userIsExcluded({ ...rcUser, roles: ['bot'] })).toBeTruthy()
|
||||||
expect(
|
expect(
|
||||||
userIsExcluded({ ...rcUser, roles: [...rcUser.__rooms, 'app'] })
|
userIsExcluded({ ...rcUser, roles: [...rcUser.__rooms, 'app'] }),
|
||||||
).toBeTruthy()
|
).toBeTruthy()
|
||||||
expect(
|
expect(
|
||||||
userIsExcluded({
|
userIsExcluded({
|
||||||
@ -96,7 +96,7 @@ test('users are excluded', () => {
|
|||||||
_id: 'excludedUser2',
|
_id: 'excludedUser2',
|
||||||
username: 'excludedUser1',
|
username: 'excludedUser1',
|
||||||
roles: [...rcUser.__rooms, 'app', 'bot'],
|
roles: [...rcUser.__rooms, 'app', 'bot'],
|
||||||
})
|
}),
|
||||||
).toBeTruthy()
|
).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -53,7 +53,7 @@ export function generateHmac(user: MatrixUser): string {
|
|||||||
hmac.write(
|
hmac.write(
|
||||||
`${user.nonce}\0${user.username}\0${user.password}\0${
|
`${user.nonce}\0${user.username}\0${user.password}\0${
|
||||||
user.admin ? 'admin' : 'notadmin'
|
user.admin ? 'admin' : 'notadmin'
|
||||||
}`
|
}`,
|
||||||
)
|
)
|
||||||
hmac.end()
|
hmac.end()
|
||||||
return hmac.read().toString('hex')
|
return hmac.read().toString('hex')
|
||||||
@ -72,7 +72,7 @@ async function parseUserMemberships(rcUser: RcUser): Promise<void> {
|
|||||||
rcUser.__rooms.map(async (rcRoomId: string) => {
|
rcUser.__rooms.map(async (rcRoomId: string) => {
|
||||||
await createMembership(rcRoomId, rcUser._id)
|
await createMembership(rcRoomId, rcUser._id)
|
||||||
log.debug(`${rcUser.username} membership for ${rcRoomId} created`)
|
log.debug(`${rcUser.username} membership for ${rcRoomId} created`)
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ export function userIsExcluded(rcUser: RcUser): boolean {
|
|||||||
|
|
||||||
export async function createMapping(
|
export async function createMapping(
|
||||||
rcId: string,
|
rcId: string,
|
||||||
matrixUser: MatrixUser
|
matrixUser: MatrixUser,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const mapping = new IdMapping()
|
const mapping = new IdMapping()
|
||||||
mapping.rcId = rcId
|
mapping.rcId = rcId
|
||||||
|
|||||||
@ -5,6 +5,6 @@ export default winston.createLogger({
|
|||||||
transports: [new winston.transports.Console()],
|
transports: [new winston.transports.Console()],
|
||||||
format: winston.format.combine(
|
format: winston.format.combine(
|
||||||
winston.format.colorize({ all: true }),
|
winston.format.colorize({ all: true }),
|
||||||
winston.format.simple()
|
winston.format.simple(),
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
|
|||||||
@ -35,7 +35,7 @@ test('create mapping', async () => {
|
|||||||
|
|
||||||
test('get mapping', async () => {
|
test('get mapping', async () => {
|
||||||
await expect(getMapping(mapping.rcId, mapping.type)).resolves.toStrictEqual(
|
await expect(getMapping(mapping.rcId, mapping.type)).resolves.toStrictEqual(
|
||||||
mapping
|
mapping,
|
||||||
)
|
)
|
||||||
await expect(getMapping('inexistent', 0)).resolves.toBe(null)
|
await expect(getMapping('inexistent', 0)).resolves.toBe(null)
|
||||||
})
|
})
|
||||||
@ -47,7 +47,7 @@ test('get access token', async () => {
|
|||||||
|
|
||||||
test('create membership', async () => {
|
test('create membership', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
createMembership(membership.rcRoomId, membership.rcUserId)
|
createMembership(membership.rcRoomId, membership.rcUserId),
|
||||||
).resolves.toBe(undefined)
|
).resolves.toBe(undefined)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@ export async function initStorage(): Promise<void> {
|
|||||||
|
|
||||||
export function getMapping(
|
export function getMapping(
|
||||||
id: string,
|
id: string,
|
||||||
type: number
|
type: number,
|
||||||
): Promise<IdMapping | null> {
|
): Promise<IdMapping | null> {
|
||||||
return AppDataSource.manager.findOneBy(IdMapping, {
|
return AppDataSource.manager.findOneBy(IdMapping, {
|
||||||
rcId: id,
|
rcId: id,
|
||||||
@ -35,7 +35,7 @@ export async function getAccessToken(id: string): Promise<string | undefined> {
|
|||||||
|
|
||||||
export async function createMembership(
|
export async function createMembership(
|
||||||
rcRoomId: string,
|
rcRoomId: string,
|
||||||
rcUserId: string
|
rcUserId: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const membership = new Membership()
|
const membership = new Membership()
|
||||||
membership.rcRoomId = rcRoomId
|
membership.rcRoomId = rcRoomId
|
||||||
|
|||||||
@ -34,7 +34,7 @@ export function formatUserSessionOptions(accessToken: string): SessionOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getUserSessionOptions(
|
export async function getUserSessionOptions(
|
||||||
id: string
|
id: string,
|
||||||
): Promise<SessionOptions> {
|
): Promise<SessionOptions> {
|
||||||
const accessToken = await getAccessToken(id)
|
const accessToken = await getAccessToken(id)
|
||||||
if (!accessToken) {
|
if (!accessToken) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user