Implement user blocking on server side
This commit is contained in:
parent
6b09aba90d
commit
e69219184b
|
@ -55,7 +55,11 @@ export class LoginComponent extends FormReactive implements OnInit {
|
||||||
.subscribe(
|
.subscribe(
|
||||||
() => this.redirectService.redirectToHomepage(),
|
() => this.redirectService.redirectToHomepage(),
|
||||||
|
|
||||||
err => this.error = err.message
|
err => {
|
||||||
|
if (err.message.indexOf('credentials are invalid') !== -1) this.error = this.i18n('Incorrect username or password.')
|
||||||
|
else if (err.message.indexOf('blocked') !== -1) this.error = this.i18n('You account is blocked.')
|
||||||
|
else this.error = err.message
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,7 @@
|
||||||
"@types/bluebird": "3.5.21"
|
"@types/bluebird": "3.5.21"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@types/oauth2-server": "^3.0.8",
|
||||||
"async": "^2.0.0",
|
"async": "^2.0.0",
|
||||||
"async-lock": "^1.1.2",
|
"async-lock": "^1.1.2",
|
||||||
"async-lru": "^1.1.1",
|
"async-lru": "^1.1.1",
|
||||||
|
@ -113,6 +114,7 @@
|
||||||
"morgan": "^1.5.3",
|
"morgan": "^1.5.3",
|
||||||
"multer": "^1.1.0",
|
"multer": "^1.1.0",
|
||||||
"nodemailer": "^4.4.2",
|
"nodemailer": "^4.4.2",
|
||||||
|
"oauth2-server": "^3.0.0",
|
||||||
"parse-torrent": "^6.0.0",
|
"parse-torrent": "^6.0.0",
|
||||||
"password-generator": "^2.0.2",
|
"password-generator": "^2.0.2",
|
||||||
"pem": "^1.12.3",
|
"pem": "^1.12.3",
|
||||||
|
|
|
@ -32,6 +32,7 @@ import {
|
||||||
import {
|
import {
|
||||||
deleteMeValidator,
|
deleteMeValidator,
|
||||||
usersAskResetPasswordValidator,
|
usersAskResetPasswordValidator,
|
||||||
|
usersBlockingValidator,
|
||||||
usersResetPasswordValidator,
|
usersResetPasswordValidator,
|
||||||
videoImportsSortValidator,
|
videoImportsSortValidator,
|
||||||
videosSortValidator
|
videosSortValidator
|
||||||
|
@ -108,6 +109,19 @@ usersRouter.get('/',
|
||||||
asyncMiddleware(listUsers)
|
asyncMiddleware(listUsers)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
usersRouter.post('/:id/block',
|
||||||
|
authenticate,
|
||||||
|
ensureUserHasRight(UserRight.MANAGE_USERS),
|
||||||
|
asyncMiddleware(usersBlockingValidator),
|
||||||
|
asyncMiddleware(blockUser)
|
||||||
|
)
|
||||||
|
usersRouter.post('/:id/unblock',
|
||||||
|
authenticate,
|
||||||
|
ensureUserHasRight(UserRight.MANAGE_USERS),
|
||||||
|
asyncMiddleware(usersBlockingValidator),
|
||||||
|
asyncMiddleware(unblockUser)
|
||||||
|
)
|
||||||
|
|
||||||
usersRouter.get('/:id',
|
usersRouter.get('/:id',
|
||||||
authenticate,
|
authenticate,
|
||||||
ensureUserHasRight(UserRight.MANAGE_USERS),
|
ensureUserHasRight(UserRight.MANAGE_USERS),
|
||||||
|
@ -278,6 +292,22 @@ async function getUserVideoQuotaUsed (req: express.Request, res: express.Respons
|
||||||
return res.json(data)
|
return res.json(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function unblockUser (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
|
const user: UserModel = res.locals.user
|
||||||
|
|
||||||
|
await changeUserBlock(res, user, false)
|
||||||
|
|
||||||
|
return res.status(204).end()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function blockUser (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
|
const user: UserModel = res.locals.user
|
||||||
|
|
||||||
|
await changeUserBlock(res, user, true)
|
||||||
|
|
||||||
|
return res.status(204).end()
|
||||||
|
}
|
||||||
|
|
||||||
function getUser (req: express.Request, res: express.Response, next: express.NextFunction) {
|
function getUser (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
return res.json((res.locals.user as UserModel).toFormattedJSON())
|
return res.json((res.locals.user as UserModel).toFormattedJSON())
|
||||||
}
|
}
|
||||||
|
@ -423,3 +453,21 @@ async function resetUserPassword (req: express.Request, res: express.Response, n
|
||||||
function success (req: express.Request, res: express.Response, next: express.NextFunction) {
|
function success (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
res.end()
|
res.end()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function changeUserBlock (res: express.Response, user: UserModel, block: boolean) {
|
||||||
|
const oldUserAuditView = new UserAuditView(user.toFormattedJSON())
|
||||||
|
|
||||||
|
user.blocked = block
|
||||||
|
|
||||||
|
await sequelizeTypescript.transaction(async t => {
|
||||||
|
await OAuthTokenModel.deleteUserToken(user.id, t)
|
||||||
|
|
||||||
|
await user.save({ transaction: t })
|
||||||
|
})
|
||||||
|
|
||||||
|
auditLogger.update(
|
||||||
|
res.locals.oauth.token.User.Account.Actor.getIdentifier(),
|
||||||
|
new UserAuditView(user.toFormattedJSON()),
|
||||||
|
oldUserAuditView
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import 'express-validator'
|
||||||
import * as validator from 'validator'
|
import * as validator from 'validator'
|
||||||
import { UserRole } from '../../../shared'
|
import { UserRole } from '../../../shared'
|
||||||
import { CONSTRAINTS_FIELDS, NSFW_POLICY_TYPES } from '../../initializers'
|
import { CONSTRAINTS_FIELDS, NSFW_POLICY_TYPES } from '../../initializers'
|
||||||
import { exists, isFileValid } from './misc'
|
import { exists, isFileValid, isBooleanValid } from './misc'
|
||||||
import { values } from 'lodash'
|
import { values } from 'lodash'
|
||||||
|
|
||||||
const USERS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.USERS
|
const USERS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.USERS
|
||||||
|
@ -29,17 +29,17 @@ function isUserDescriptionValid (value: string) {
|
||||||
return value === null || (exists(value) && validator.isLength(value, CONSTRAINTS_FIELDS.USERS.DESCRIPTION))
|
return value === null || (exists(value) && validator.isLength(value, CONSTRAINTS_FIELDS.USERS.DESCRIPTION))
|
||||||
}
|
}
|
||||||
|
|
||||||
function isBoolean (value: any) {
|
|
||||||
return typeof value === 'boolean' || (typeof value === 'string' && validator.isBoolean(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
const nsfwPolicies = values(NSFW_POLICY_TYPES)
|
const nsfwPolicies = values(NSFW_POLICY_TYPES)
|
||||||
function isUserNSFWPolicyValid (value: any) {
|
function isUserNSFWPolicyValid (value: any) {
|
||||||
return exists(value) && nsfwPolicies.indexOf(value) !== -1
|
return exists(value) && nsfwPolicies.indexOf(value) !== -1
|
||||||
}
|
}
|
||||||
|
|
||||||
function isUserAutoPlayVideoValid (value: any) {
|
function isUserAutoPlayVideoValid (value: any) {
|
||||||
return isBoolean(value)
|
return isBooleanValid(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isUserBlockedValid (value: any) {
|
||||||
|
return isBooleanValid(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
function isUserRoleValid (value: any) {
|
function isUserRoleValid (value: any) {
|
||||||
|
@ -57,6 +57,7 @@ function isAvatarFile (files: { [ fieldname: string ]: Express.Multer.File[] } |
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
isUserBlockedValid,
|
||||||
isUserPasswordValid,
|
isUserPasswordValid,
|
||||||
isUserRoleValid,
|
isUserRoleValid,
|
||||||
isUserVideoQuotaValid,
|
isUserVideoQuotaValid,
|
||||||
|
|
|
@ -15,7 +15,7 @@ let config: IConfig = require('config')
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
const LAST_MIGRATION_VERSION = 240
|
const LAST_MIGRATION_VERSION = 245
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
import * as Sequelize from 'sequelize'
|
||||||
|
import { createClient } from 'redis'
|
||||||
|
import { CONFIG } from '../constants'
|
||||||
|
import { JobQueue } from '../../lib/job-queue'
|
||||||
|
import { initDatabaseModels } from '../database'
|
||||||
|
|
||||||
|
async function up (utils: {
|
||||||
|
transaction: Sequelize.Transaction
|
||||||
|
queryInterface: Sequelize.QueryInterface
|
||||||
|
sequelize: Sequelize.Sequelize
|
||||||
|
}): Promise<any> {
|
||||||
|
{
|
||||||
|
const data = {
|
||||||
|
type: Sequelize.BOOLEAN,
|
||||||
|
allowNull: true,
|
||||||
|
defaultValue: null
|
||||||
|
}
|
||||||
|
await utils.queryInterface.addColumn('user', 'blocked', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const query = 'UPDATE "user" SET "blocked" = false'
|
||||||
|
await utils.sequelize.query(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const data = {
|
||||||
|
type: Sequelize.BOOLEAN,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: null
|
||||||
|
}
|
||||||
|
await utils.queryInterface.changeColumn('user', 'blocked', data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function down (options) {
|
||||||
|
throw new Error('Not implemented.')
|
||||||
|
}
|
||||||
|
|
||||||
|
export { up, down }
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { AccessDeniedError} from 'oauth2-server'
|
||||||
import { logger } from '../helpers/logger'
|
import { logger } from '../helpers/logger'
|
||||||
import { UserModel } from '../models/account/user'
|
import { UserModel } from '../models/account/user'
|
||||||
import { OAuthClientModel } from '../models/oauth/oauth-client'
|
import { OAuthClientModel } from '../models/oauth/oauth-client'
|
||||||
|
@ -34,6 +35,8 @@ async function getUser (usernameOrEmail: string, password: string) {
|
||||||
const passwordMatch = await user.isPasswordMatch(password)
|
const passwordMatch = await user.isPasswordMatch(password)
|
||||||
if (passwordMatch === false) return null
|
if (passwordMatch === false) return null
|
||||||
|
|
||||||
|
if (user.blocked) throw new AccessDeniedError('User is blocked.')
|
||||||
|
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,9 +70,7 @@ async function saveToken (token: TokenInfo, client: OAuthClientModel, user: User
|
||||||
}
|
}
|
||||||
|
|
||||||
const tokenCreated = await OAuthTokenModel.create(tokenToCreate)
|
const tokenCreated = await OAuthTokenModel.create(tokenToCreate)
|
||||||
const tokenToReturn = Object.assign(tokenCreated, { client, user })
|
return Object.assign(tokenCreated, { client, user })
|
||||||
|
|
||||||
return tokenToReturn
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -39,7 +39,7 @@ function token (req: express.Request, res: express.Response, next: express.NextF
|
||||||
if (err) {
|
if (err) {
|
||||||
return res.status(err.status)
|
return res.status(err.status)
|
||||||
.json({
|
.json({
|
||||||
error: 'Authentication failed.',
|
error: err.message,
|
||||||
code: err.name
|
code: err.name
|
||||||
})
|
})
|
||||||
.end()
|
.end()
|
||||||
|
|
|
@ -74,6 +74,26 @@ const usersRemoveValidator = [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const usersBlockingValidator = [
|
||||||
|
param('id').isInt().not().isEmpty().withMessage('Should have a valid id'),
|
||||||
|
|
||||||
|
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
|
logger.debug('Checking usersRemove parameters', { parameters: req.params })
|
||||||
|
|
||||||
|
if (areValidationErrors(req, res)) return
|
||||||
|
if (!await checkUserIdExist(req.params.id, res)) return
|
||||||
|
|
||||||
|
const user = res.locals.user
|
||||||
|
if (user.username === 'root') {
|
||||||
|
return res.status(400)
|
||||||
|
.send({ error: 'Cannot block the root user' })
|
||||||
|
.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
const deleteMeValidator = [
|
const deleteMeValidator = [
|
||||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
const user: UserModel = res.locals.oauth.token.User
|
const user: UserModel = res.locals.oauth.token.User
|
||||||
|
@ -230,6 +250,7 @@ export {
|
||||||
usersAddValidator,
|
usersAddValidator,
|
||||||
deleteMeValidator,
|
deleteMeValidator,
|
||||||
usersRegisterValidator,
|
usersRegisterValidator,
|
||||||
|
usersBlockingValidator,
|
||||||
usersRemoveValidator,
|
usersRemoveValidator,
|
||||||
usersUpdateValidator,
|
usersUpdateValidator,
|
||||||
usersUpdateMeValidator,
|
usersUpdateMeValidator,
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { hasUserRight, USER_ROLE_LABELS, UserRight } from '../../../shared'
|
||||||
import { User, UserRole } from '../../../shared/models/users'
|
import { User, UserRole } from '../../../shared/models/users'
|
||||||
import {
|
import {
|
||||||
isUserAutoPlayVideoValid,
|
isUserAutoPlayVideoValid,
|
||||||
|
isUserBlockedValid,
|
||||||
isUserNSFWPolicyValid,
|
isUserNSFWPolicyValid,
|
||||||
isUserPasswordValid,
|
isUserPasswordValid,
|
||||||
isUserRoleValid,
|
isUserRoleValid,
|
||||||
|
@ -100,6 +101,12 @@ export class UserModel extends Model<UserModel> {
|
||||||
@Column
|
@Column
|
||||||
autoPlayVideo: boolean
|
autoPlayVideo: boolean
|
||||||
|
|
||||||
|
@AllowNull(false)
|
||||||
|
@Default(false)
|
||||||
|
@Is('UserBlocked', value => throwIfNotValid(value, isUserBlockedValid, 'blocked boolean'))
|
||||||
|
@Column
|
||||||
|
blocked: boolean
|
||||||
|
|
||||||
@AllowNull(false)
|
@AllowNull(false)
|
||||||
@Is('UserRole', value => throwIfNotValid(value, isUserRoleValid, 'role'))
|
@Is('UserRole', value => throwIfNotValid(value, isUserRoleValid, 'role'))
|
||||||
@Column
|
@Column
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { logger } from '../../helpers/logger'
|
||||||
import { AccountModel } from '../account/account'
|
import { AccountModel } from '../account/account'
|
||||||
import { UserModel } from '../account/user'
|
import { UserModel } from '../account/user'
|
||||||
import { OAuthClientModel } from './oauth-client'
|
import { OAuthClientModel } from './oauth-client'
|
||||||
|
import { Transaction } from 'sequelize'
|
||||||
|
|
||||||
export type OAuthTokenInfo = {
|
export type OAuthTokenInfo = {
|
||||||
refreshToken: string
|
refreshToken: string
|
||||||
|
@ -125,7 +126,7 @@ export class OAuthTokenModel extends Model<OAuthTokenModel> {
|
||||||
} as OAuthTokenInfo
|
} as OAuthTokenInfo
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
logger.info('getRefreshToken error.', { err })
|
logger.error('getRefreshToken error.', { err })
|
||||||
throw err
|
throw err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -163,11 +164,12 @@ export class OAuthTokenModel extends Model<OAuthTokenModel> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static deleteUserToken (userId: number) {
|
static deleteUserToken (userId: number, t?: Transaction) {
|
||||||
const query = {
|
const query = {
|
||||||
where: {
|
where: {
|
||||||
userId
|
userId
|
||||||
}
|
},
|
||||||
|
transaction: t
|
||||||
}
|
}
|
||||||
|
|
||||||
return OAuthTokenModel.destroy(query)
|
return OAuthTokenModel.destroy(query)
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { UserRole, VideoImport, VideoImportState } from '../../../../shared'
|
||||||
import {
|
import {
|
||||||
createUser, flushTests, getMyUserInformation, getMyUserVideoRating, getUsersList, immutableAssign, killallServers, makeGetRequest,
|
createUser, flushTests, getMyUserInformation, getMyUserVideoRating, getUsersList, immutableAssign, killallServers, makeGetRequest,
|
||||||
makePostBodyRequest, makeUploadRequest, makePutBodyRequest, registerUser, removeUser, runServer, ServerInfo, setAccessTokensToServers,
|
makePostBodyRequest, makeUploadRequest, makePutBodyRequest, registerUser, removeUser, runServer, ServerInfo, setAccessTokensToServers,
|
||||||
updateUser, uploadVideo, userLogin, deleteMe
|
updateUser, uploadVideo, userLogin, deleteMe, unblockUser, blockUser
|
||||||
} from '../../utils'
|
} from '../../utils'
|
||||||
import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params'
|
import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params'
|
||||||
import { getMagnetURI, getMyVideoImports, getYoutubeVideoUrl, importVideo } from '../../utils/videos/video-imports'
|
import { getMagnetURI, getMyVideoImports, getYoutubeVideoUrl, importVideo } from '../../utils/videos/video-imports'
|
||||||
|
@ -455,17 +455,29 @@ describe('Test users API validators', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('When removing an user', function () {
|
describe('When blocking/unblocking/removing user', function () {
|
||||||
it('Should fail with an incorrect id', async function () {
|
it('Should fail with an incorrect id', async function () {
|
||||||
await removeUser(server.url, 'blabla', server.accessToken, 400)
|
await removeUser(server.url, 'blabla', server.accessToken, 400)
|
||||||
|
await blockUser(server.url, 'blabla', server.accessToken, 400)
|
||||||
|
await unblockUser(server.url, 'blabla', server.accessToken, 400)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should fail with the root user', async function () {
|
it('Should fail with the root user', async function () {
|
||||||
await removeUser(server.url, rootId, server.accessToken, 400)
|
await removeUser(server.url, rootId, server.accessToken, 400)
|
||||||
|
await blockUser(server.url, rootId, server.accessToken, 400)
|
||||||
|
await unblockUser(server.url, rootId, server.accessToken, 400)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should return 404 with a non existing id', async function () {
|
it('Should return 404 with a non existing id', async function () {
|
||||||
await removeUser(server.url, 4545454, server.accessToken, 404)
|
await removeUser(server.url, 4545454, server.accessToken, 404)
|
||||||
|
await blockUser(server.url, 4545454, server.accessToken, 404)
|
||||||
|
await unblockUser(server.url, 4545454, server.accessToken, 404)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should fail with a non admin user', async function () {
|
||||||
|
await removeUser(server.url, userId, userAccessToken, 403)
|
||||||
|
await blockUser(server.url, userId, userAccessToken, 403)
|
||||||
|
await unblockUser(server.url, userId, userAccessToken, 403)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
createUser, flushTests, getBlacklistedVideosList, getMyUserInformation, getMyUserVideoQuotaUsed, getMyUserVideoRating,
|
createUser, flushTests, getBlacklistedVideosList, getMyUserInformation, getMyUserVideoQuotaUsed, getMyUserVideoRating,
|
||||||
getUserInformation, getUsersList, getUsersListPaginationAndSort, getVideosList, killallServers, login, makePutBodyRequest, rateVideo,
|
getUserInformation, getUsersList, getUsersListPaginationAndSort, getVideosList, killallServers, login, makePutBodyRequest, rateVideo,
|
||||||
registerUser, removeUser, removeVideo, runServer, ServerInfo, testImage, updateMyAvatar, updateMyUser, updateUser, uploadVideo, userLogin,
|
registerUser, removeUser, removeVideo, runServer, ServerInfo, testImage, updateMyAvatar, updateMyUser, updateUser, uploadVideo, userLogin,
|
||||||
deleteMe
|
deleteMe, blockUser, unblockUser
|
||||||
} from '../../utils/index'
|
} from '../../utils/index'
|
||||||
import { follow } from '../../utils/server/follows'
|
import { follow } from '../../utils/server/follows'
|
||||||
import { setAccessTokensToServers } from '../../utils/users/login'
|
import { setAccessTokensToServers } from '../../utils/users/login'
|
||||||
|
@ -45,28 +45,28 @@ describe('Test users', function () {
|
||||||
const client = { id: 'client', secret: server.client.secret }
|
const client = { id: 'client', secret: server.client.secret }
|
||||||
const res = await login(server.url, client, server.user, 400)
|
const res = await login(server.url, client, server.user, 400)
|
||||||
|
|
||||||
expect(res.body.error).to.equal('Authentication failed.')
|
expect(res.body.error).to.contain('client is invalid')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should not login with an invalid client secret', async function () {
|
it('Should not login with an invalid client secret', async function () {
|
||||||
const client = { id: server.client.id, secret: 'coucou' }
|
const client = { id: server.client.id, secret: 'coucou' }
|
||||||
const res = await login(server.url, client, server.user, 400)
|
const res = await login(server.url, client, server.user, 400)
|
||||||
|
|
||||||
expect(res.body.error).to.equal('Authentication failed.')
|
expect(res.body.error).to.contain('client is invalid')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should not login with an invalid username', async function () {
|
it('Should not login with an invalid username', async function () {
|
||||||
const user = { username: 'captain crochet', password: server.user.password }
|
const user = { username: 'captain crochet', password: server.user.password }
|
||||||
const res = await login(server.url, server.client, user, 400)
|
const res = await login(server.url, server.client, user, 400)
|
||||||
|
|
||||||
expect(res.body.error).to.equal('Authentication failed.')
|
expect(res.body.error).to.contain('credentials are invalid')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should not login with an invalid password', async function () {
|
it('Should not login with an invalid password', async function () {
|
||||||
const user = { username: server.user.username, password: 'mew_three' }
|
const user = { username: server.user.username, password: 'mew_three' }
|
||||||
const res = await login(server.url, server.client, user, 400)
|
const res = await login(server.url, server.client, user, 400)
|
||||||
|
|
||||||
expect(res.body.error).to.equal('Authentication failed.')
|
expect(res.body.error).to.contain('credentials are invalid')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should not be able to upload a video', async function () {
|
it('Should not be able to upload a video', async function () {
|
||||||
|
@ -493,6 +493,27 @@ describe('Test users', function () {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Should block and unblock a user', async function () {
|
||||||
|
const user16 = {
|
||||||
|
username: 'user_16',
|
||||||
|
password: 'my super password'
|
||||||
|
}
|
||||||
|
const resUser = await createUser(server.url, server.accessToken, user16.username, user16.password)
|
||||||
|
const user16Id = resUser.body.user.id
|
||||||
|
|
||||||
|
accessToken = await userLogin(server, user16)
|
||||||
|
|
||||||
|
await getMyUserInformation(server.url, accessToken, 200)
|
||||||
|
await blockUser(server.url, user16Id, server.accessToken)
|
||||||
|
|
||||||
|
await getMyUserInformation(server.url, accessToken, 401)
|
||||||
|
await userLogin(server, user16, 400)
|
||||||
|
|
||||||
|
await unblockUser(server.url, user16Id, server.accessToken)
|
||||||
|
accessToken = await userLogin(server, user16)
|
||||||
|
await getMyUserInformation(server.url, accessToken, 200)
|
||||||
|
})
|
||||||
|
|
||||||
after(async function () {
|
after(async function () {
|
||||||
killallServers([ server ])
|
killallServers([ server ])
|
||||||
|
|
||||||
|
|
|
@ -134,6 +134,26 @@ function removeUser (url: string, userId: number | string, accessToken: string,
|
||||||
.expect(expectedStatus)
|
.expect(expectedStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function blockUser (url: string, userId: number | string, accessToken: string, expectedStatus = 204) {
|
||||||
|
const path = '/api/v1/users'
|
||||||
|
|
||||||
|
return request(url)
|
||||||
|
.post(path + '/' + userId + '/block')
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.set('Authorization', 'Bearer ' + accessToken)
|
||||||
|
.expect(expectedStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
function unblockUser (url: string, userId: number | string, accessToken: string, expectedStatus = 204) {
|
||||||
|
const path = '/api/v1/users'
|
||||||
|
|
||||||
|
return request(url)
|
||||||
|
.post(path + '/' + userId + '/unblock')
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.set('Authorization', 'Bearer ' + accessToken)
|
||||||
|
.expect(expectedStatus)
|
||||||
|
}
|
||||||
|
|
||||||
function updateMyUser (options: {
|
function updateMyUser (options: {
|
||||||
url: string
|
url: string
|
||||||
accessToken: string,
|
accessToken: string,
|
||||||
|
@ -234,6 +254,8 @@ export {
|
||||||
updateUser,
|
updateUser,
|
||||||
updateMyUser,
|
updateMyUser,
|
||||||
getUserInformation,
|
getUserInformation,
|
||||||
|
blockUser,
|
||||||
|
unblockUser,
|
||||||
askResetPassword,
|
askResetPassword,
|
||||||
resetPassword,
|
resetPassword,
|
||||||
updateMyAvatar
|
updateMyAvatar
|
||||||
|
|
121
yarn.lock
121
yarn.lock
|
@ -191,6 +191,12 @@
|
||||||
"@types/events" "*"
|
"@types/events" "*"
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/oauth2-server@^3.0.8":
|
||||||
|
version "3.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/oauth2-server/-/oauth2-server-3.0.8.tgz#0b7f5083790732ea00bf8c5e0b04b9fa1f22f22c"
|
||||||
|
dependencies:
|
||||||
|
"@types/express" "*"
|
||||||
|
|
||||||
"@types/parse-torrent-file@*":
|
"@types/parse-torrent-file@*":
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/parse-torrent-file/-/parse-torrent-file-4.0.1.tgz#056a6c18f3fac0cd7c6c74540f00496a3225976b"
|
resolved "https://registry.yarnpkg.com/@types/parse-torrent-file/-/parse-torrent-file-4.0.1.tgz#056a6c18f3fac0cd7c6c74540f00496a3225976b"
|
||||||
|
@ -2713,7 +2719,15 @@ fsevents@^1.2.2:
|
||||||
nan "^2.9.2"
|
nan "^2.9.2"
|
||||||
node-pre-gyp "^0.10.0"
|
node-pre-gyp "^0.10.0"
|
||||||
|
|
||||||
fstream@^1.0.0, fstream@^1.0.2:
|
fstream-ignore@^1.0.5:
|
||||||
|
version "1.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105"
|
||||||
|
dependencies:
|
||||||
|
fstream "^1.0.0"
|
||||||
|
inherits "2"
|
||||||
|
minimatch "^3.0.0"
|
||||||
|
|
||||||
|
fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2:
|
||||||
version "1.0.11"
|
version "1.0.11"
|
||||||
resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171"
|
resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -3227,7 +3241,7 @@ hashish@~0.0.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
traverse ">=0.2.4"
|
traverse ">=0.2.4"
|
||||||
|
|
||||||
hawk@~3.1.3:
|
hawk@3.1.3, hawk@~3.1.3:
|
||||||
version "3.1.3"
|
version "3.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4"
|
resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -4115,13 +4129,13 @@ levn@^0.3.0, levn@~0.3.0:
|
||||||
prelude-ls "~1.1.2"
|
prelude-ls "~1.1.2"
|
||||||
type-check "~0.3.2"
|
type-check "~0.3.2"
|
||||||
|
|
||||||
libxmljs@0.19.1:
|
libxmljs@0.19.0:
|
||||||
version "0.19.1"
|
version "0.19.0"
|
||||||
resolved "https://registry.yarnpkg.com/libxmljs/-/libxmljs-0.19.1.tgz#bc7a62822c4392363feaab49b116b4786b2d5ada"
|
resolved "https://registry.yarnpkg.com/libxmljs/-/libxmljs-0.19.0.tgz#dd0e635ce752af7701492ceb8c565ab74d494473"
|
||||||
dependencies:
|
dependencies:
|
||||||
bindings "~1.3.0"
|
bindings "~1.3.0"
|
||||||
nan "~2.10.0"
|
nan "~2.10.0"
|
||||||
node-pre-gyp "~0.10.2"
|
node-pre-gyp "~0.6.37"
|
||||||
|
|
||||||
lint-staged@^7.1.0:
|
lint-staged@^7.1.0:
|
||||||
version "7.2.0"
|
version "7.2.0"
|
||||||
|
@ -4701,7 +4715,7 @@ minimalistic-assert@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
|
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
|
||||||
|
|
||||||
"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2:
|
"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2:
|
||||||
version "3.0.4"
|
version "3.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -4959,7 +4973,7 @@ node-pre-gyp@0.10.2:
|
||||||
semver "^5.3.0"
|
semver "^5.3.0"
|
||||||
tar "^4"
|
tar "^4"
|
||||||
|
|
||||||
node-pre-gyp@^0.10.0, node-pre-gyp@~0.10.2:
|
node-pre-gyp@^0.10.0:
|
||||||
version "0.10.3"
|
version "0.10.3"
|
||||||
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc"
|
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -4974,6 +4988,22 @@ node-pre-gyp@^0.10.0, node-pre-gyp@~0.10.2:
|
||||||
semver "^5.3.0"
|
semver "^5.3.0"
|
||||||
tar "^4"
|
tar "^4"
|
||||||
|
|
||||||
|
node-pre-gyp@~0.6.37:
|
||||||
|
version "0.6.39"
|
||||||
|
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz#c00e96860b23c0e1420ac7befc5044e1d78d8649"
|
||||||
|
dependencies:
|
||||||
|
detect-libc "^1.0.2"
|
||||||
|
hawk "3.1.3"
|
||||||
|
mkdirp "^0.5.1"
|
||||||
|
nopt "^4.0.1"
|
||||||
|
npmlog "^4.0.2"
|
||||||
|
rc "^1.1.7"
|
||||||
|
request "2.81.0"
|
||||||
|
rimraf "^2.6.1"
|
||||||
|
semver "^5.3.0"
|
||||||
|
tar "^2.2.1"
|
||||||
|
tar-pack "^3.4.0"
|
||||||
|
|
||||||
node-sass@^4.9.0:
|
node-sass@^4.9.0:
|
||||||
version "4.9.2"
|
version "4.9.2"
|
||||||
resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.9.2.tgz#5e63fe6bd0f2ae3ac9d6c14ede8620e2b8bdb437"
|
resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.9.2.tgz#5e63fe6bd0f2ae3ac9d6c14ede8620e2b8bdb437"
|
||||||
|
@ -5133,7 +5163,7 @@ oauth-sign@~0.8.1, oauth-sign@~0.8.2:
|
||||||
version "0.8.2"
|
version "0.8.2"
|
||||||
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
|
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
|
||||||
|
|
||||||
oauth2-server@3.0.0:
|
oauth2-server@3.0.0, oauth2-server@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/oauth2-server/-/oauth2-server-3.0.0.tgz#c46276b74c3d28634d59ee981f76b58a6459cc28"
|
resolved "https://registry.yarnpkg.com/oauth2-server/-/oauth2-server-3.0.0.tgz#c46276b74c3d28634d59ee981f76b58a6459cc28"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -5838,7 +5868,7 @@ raw-body@~1.1.0:
|
||||||
bytes "1"
|
bytes "1"
|
||||||
string_decoder "0.10"
|
string_decoder "0.10"
|
||||||
|
|
||||||
rc@^1.0.1, rc@^1.1.6, rc@^1.2.7:
|
rc@^1.0.1, rc@^1.1.6, rc@^1.1.7, rc@^1.2.7:
|
||||||
version "1.2.8"
|
version "1.2.8"
|
||||||
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
|
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -6044,32 +6074,7 @@ repeating@^2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-finite "^1.0.0"
|
is-finite "^1.0.0"
|
||||||
|
|
||||||
request@2.87.0, request@^2.81.0, request@^2.83.0:
|
request@2.81.0, "request@>=2.9.0 <2.82.0":
|
||||||
version "2.87.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e"
|
|
||||||
dependencies:
|
|
||||||
aws-sign2 "~0.7.0"
|
|
||||||
aws4 "^1.6.0"
|
|
||||||
caseless "~0.12.0"
|
|
||||||
combined-stream "~1.0.5"
|
|
||||||
extend "~3.0.1"
|
|
||||||
forever-agent "~0.6.1"
|
|
||||||
form-data "~2.3.1"
|
|
||||||
har-validator "~5.0.3"
|
|
||||||
http-signature "~1.2.0"
|
|
||||||
is-typedarray "~1.0.0"
|
|
||||||
isstream "~0.1.2"
|
|
||||||
json-stringify-safe "~5.0.1"
|
|
||||||
mime-types "~2.1.17"
|
|
||||||
oauth-sign "~0.8.2"
|
|
||||||
performance-now "^2.1.0"
|
|
||||||
qs "~6.5.1"
|
|
||||||
safe-buffer "^5.1.1"
|
|
||||||
tough-cookie "~2.3.3"
|
|
||||||
tunnel-agent "^0.6.0"
|
|
||||||
uuid "^3.1.0"
|
|
||||||
|
|
||||||
"request@>=2.9.0 <2.82.0":
|
|
||||||
version "2.81.0"
|
version "2.81.0"
|
||||||
resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
|
resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -6096,6 +6101,31 @@ request@2.87.0, request@^2.81.0, request@^2.83.0:
|
||||||
tunnel-agent "^0.6.0"
|
tunnel-agent "^0.6.0"
|
||||||
uuid "^3.0.0"
|
uuid "^3.0.0"
|
||||||
|
|
||||||
|
request@2.87.0, request@^2.81.0, request@^2.83.0:
|
||||||
|
version "2.87.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e"
|
||||||
|
dependencies:
|
||||||
|
aws-sign2 "~0.7.0"
|
||||||
|
aws4 "^1.6.0"
|
||||||
|
caseless "~0.12.0"
|
||||||
|
combined-stream "~1.0.5"
|
||||||
|
extend "~3.0.1"
|
||||||
|
forever-agent "~0.6.1"
|
||||||
|
form-data "~2.3.1"
|
||||||
|
har-validator "~5.0.3"
|
||||||
|
http-signature "~1.2.0"
|
||||||
|
is-typedarray "~1.0.0"
|
||||||
|
isstream "~0.1.2"
|
||||||
|
json-stringify-safe "~5.0.1"
|
||||||
|
mime-types "~2.1.17"
|
||||||
|
oauth-sign "~0.8.2"
|
||||||
|
performance-now "^2.1.0"
|
||||||
|
qs "~6.5.1"
|
||||||
|
safe-buffer "^5.1.1"
|
||||||
|
tough-cookie "~2.3.3"
|
||||||
|
tunnel-agent "^0.6.0"
|
||||||
|
uuid "^3.1.0"
|
||||||
|
|
||||||
require-directory@^2.1.1:
|
require-directory@^2.1.1:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
|
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
|
||||||
|
@ -7081,6 +7111,19 @@ tar-fs@^1.13.0:
|
||||||
pump "^1.0.0"
|
pump "^1.0.0"
|
||||||
tar-stream "^1.1.2"
|
tar-stream "^1.1.2"
|
||||||
|
|
||||||
|
tar-pack@^3.4.0:
|
||||||
|
version "3.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.1.tgz#e1dbc03a9b9d3ba07e896ad027317eb679a10a1f"
|
||||||
|
dependencies:
|
||||||
|
debug "^2.2.0"
|
||||||
|
fstream "^1.0.10"
|
||||||
|
fstream-ignore "^1.0.5"
|
||||||
|
once "^1.3.3"
|
||||||
|
readable-stream "^2.1.4"
|
||||||
|
rimraf "^2.5.1"
|
||||||
|
tar "^2.2.1"
|
||||||
|
uid-number "^0.0.6"
|
||||||
|
|
||||||
tar-stream@^1.1.2:
|
tar-stream@^1.1.2:
|
||||||
version "1.6.1"
|
version "1.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.1.tgz#f84ef1696269d6223ca48f6e1eeede3f7e81f395"
|
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.1.tgz#f84ef1696269d6223ca48f6e1eeede3f7e81f395"
|
||||||
|
@ -7093,7 +7136,7 @@ tar-stream@^1.1.2:
|
||||||
to-buffer "^1.1.0"
|
to-buffer "^1.1.0"
|
||||||
xtend "^4.0.0"
|
xtend "^4.0.0"
|
||||||
|
|
||||||
tar@^2.0.0:
|
tar@^2.0.0, tar@^2.2.1:
|
||||||
version "2.2.1"
|
version "2.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1"
|
resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -7454,6 +7497,10 @@ uglify-to-browserify@~1.0.0:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7"
|
resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7"
|
||||||
|
|
||||||
|
uid-number@^0.0.6:
|
||||||
|
version "0.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
|
||||||
|
|
||||||
uint64be@^1.0.1:
|
uint64be@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/uint64be/-/uint64be-1.0.1.tgz#1f7154202f2a1b8af353871dda651bf34ce93e95"
|
resolved "https://registry.yarnpkg.com/uint64be/-/uint64be-1.0.1.tgz#1f7154202f2a1b8af353871dda651bf34ce93e95"
|
||||||
|
|
Loading…
Reference in New Issue