瀏覽代碼

Move from using params on User instance for permissions to a dedicated Ban model, move methods in permissions.js file to class methods on Ban model

sbkwgh 8 年之前
父節點
當前提交
a614022e88
共有 10 個文件被更改,包括 402 次插入233 次删除
  1. 0 44
      lib/permissions.js
  2. 78 0
      models/ban.js
  3. 0 8
      models/user.js
  4. 92 0
      routes/ban.js
  5. 4 5
      routes/post.js
  6. 4 5
      routes/thread.js
  7. 0 51
      routes/user.js
  8. 1 0
      server.js
  9. 223 0
      test/ban.js
  10. 0 120
      test/user.js

+ 0 - 44
lib/permissions.js

@@ -1,44 +0,0 @@
-let { User } = require('../models')
-
-//`req` is the request object
-//`permissions` is an array of strings:
-//('loggedIn', 'admin', 'canCreatePosts', 'canCreateThreads',)
-module.exports = async function (req, permissions) {
-	try {
-		//The return value - i.e. does the user have the
-		//appropriate permissions
-		let ret = true
-
-		if(permissions.includes('loggedIn')) {
-			ret = !!req.session.loggedIn
-		}
-		if(permissions.includes('admin')) {
-			ret = !!req.session.admin
-		}
-
-		//`ret` to check if they are loggedIn/admin and so their account does exist
-		if(
-			( permissions.includes('canCreatePosts') || permissions.includes('canCreateThreads') )
-			&& ret
-		) {
-			let user = await User.findOne({
-				where: { username: req.session.username }
-			})
-
-			if(!user) return false
-
-			if(permissions.includes('canCreatePosts')) {
-				ret = user.canCreatePosts
-			}
-			if(permissions.includes('canCreateThreads')) {
-				ret = user.canCreateThreads
-			}
-		}
-
-		return ret
-	} catch(e) {
-		console.log(e)
-
-		return false
-	}
-}

+ 78 - 0
models/ban.js

@@ -0,0 +1,78 @@
+const Errors = require('../lib/errors')
+
+module.exports = (sequelize, DataTypes) => {
+	let Ban = sequelize.define('Ban', {
+		canCreatePosts: {
+			type: DataTypes.BOOLEAN,
+			defaultValue: true,
+			validate: {
+				isBoolean (val) {
+					if(typeof val !== 'boolean') {
+						throw new sequelize.ValidationError('canCreateThreads must be a string')
+					}
+				}
+			}
+		},
+		canCreateThreads: {
+			type: DataTypes.BOOLEAN,
+			defaultValue: true,
+			validate: {
+				isBoolean (val) {
+					if(typeof val !== 'boolean') {
+						throw new sequelize.ValidationError('canCreateThreads must be a string')
+					}
+				}
+			}
+		},
+		message: {
+			type: DataTypes.TEXT,
+			validate: {
+				isString (val) {
+					if(typeof val !== 'string') {
+						throw new sequelize.ValidationError('description must be a string')
+					}
+				},
+				len: {
+					args: [0, 1024],
+					msg: 'message must be less than 1024 characters'
+				}
+			}
+		}
+	}, {
+		classMethods: {
+			associate (models) {
+				Ban.belongsTo(models.User)
+			},
+			async getBanInstance (username) {
+				let user = await sequelize.models.User.findOne({ where: { username } })
+				let ban = await Ban.findOne({ where: { UserId: user.id } })
+
+				return ban
+			},
+			async canCreatePosts (username) {
+				let ban = await this.getBanInstance(username)
+
+				if(ban && !ban.canCreatePosts) {
+					throw Errors.sequelizeValidation(sequelize.Sequelize, {
+						error: ban.message || 'You have been banned from posting'
+					})
+				} else {
+					false
+				}
+			},
+			async canCreateThreads (username) {
+				let ban = await this.getBanInstance(username)
+
+				if(ban && !ban.canCreateThreads) {
+					throw Errors.sequelizeValidation(sequelize.Sequelize, {
+						error: ban.message || 'You have been banned from creating threads'
+					})
+				} else {
+					false
+				}
+			}
+		}
+	})
+
+	return Ban
+}

+ 0 - 8
models/user.js

@@ -56,14 +56,6 @@ module.exports = (sequelize, DataTypes) => {
 				}
 			}
 		},
-		canCreatePosts: {
-			type: DataTypes.BOOLEAN,
-			defaultValue: true
-		},
-		canCreateThreads: {
-			type: DataTypes.BOOLEAN,
-			defaultValue: true
-		},
 		admin: {
 			type: DataTypes.BOOLEAN,
 			defaultValue: false

+ 92 - 0
routes/ban.js

@@ -0,0 +1,92 @@
+let express = require('express')
+let router = express.Router()
+
+let { User, Ban, Sequelize } = require('../models')
+const Errors = require('../lib/errors')
+
+router.all('*', (req, res, next) => {
+	if(req.session.admin) {
+		next()
+	} else {
+		res.status(400)
+		res.json({
+			errors: [Errors.requestNotAuthorized]
+		})
+	}
+})
+
+router.post('/', async (req, res) => {
+	try {
+		let user = await User.findById(req.body.userId)
+		if(!user) throw Errors.sequelizeValidation(Sequelize, {
+			error: 'user does not exist',
+			value: req.body.userId
+		})
+
+		let ban = await Ban.create({
+			message: req.body.message,
+			canCreateThreads: req.body.canCreateThreads,
+			canCreatePosts: req.body.canCreatePosts
+		})
+		await ban.setUser(user)
+
+		res.json(ban.toJSON())
+	} catch (e) {
+		if(e instanceof Sequelize.ValidationError) {
+			res.status(400)
+			res.json(e)
+		} else {
+			console.log(e)
+
+			res.status(500)
+			res.json({
+				errors: [Errors.unknown]
+			})
+		}
+	}
+})
+
+router.get('/', async (req, res) => {
+	try {
+		let bans = await Ban.findAll({
+			include: [User]
+		})
+
+		res.json(bans.map(b => b.toJSON()))
+	} catch (e) {
+		console.log(e)
+
+		res.status(500)
+		res.json({
+			errors: [Errors.unknown]
+		})
+	}
+})
+
+router.delete('/:ban_id', async (req, res) => {
+	try {
+		let ban = await Ban.findById(req.params.ban_id)
+		if(!ban) throw Errors.sequelizeValidation(Sequelize, {
+			error: 'ban does not exist',
+			value: req.body.userId
+		})
+
+		await ban.destroy()
+		res.json({ success: true })
+
+	} catch (e) {
+		if(e instanceof Sequelize.ValidationError) {
+			res.status(400)
+			res.json(e)
+		} else {
+			console.log(e)
+
+			res.status(500)
+			res.json({
+				errors: [Errors.unknown]
+			})
+		}
+	}
+})
+
+module.exports = router

+ 4 - 5
routes/post.js

@@ -2,7 +2,7 @@ let express = require('express')
 let router = express.Router()
 
 const Errors = require('../lib/errors')
-let { User, Thread, Post, Notification, Sequelize, sequelize } = require('../models')
+let { User, Thread, Post, Notification, Ban, Sequelize, sequelize } = require('../models')
 
 router.get('/:post_id', async (req, res) => {
 	try {
@@ -96,10 +96,12 @@ router.delete('/:post_id/like', async (req, res) => {
 })
 
 router.post('/', async (req, res) => {
-	let validationErrors = []
 	let thread, replyingToPost, post, uniqueMentions = []
 
 	try {
+		//Will throw an error if banned
+		await Ban.canCreatePosts(req.session.username)
+
 		if(req.body.mentions) {
 			uniqueMentions = Notification.filterMentions(req.body.mentions)
 		}
@@ -111,9 +113,6 @@ router.post('/', async (req, res) => {
 			username: req.session.username
 		}})
 
-		if(!user.canCreatePosts) throw Errors.sequelizeValidation(Sequelize, {
-			error: 'You have been banned from posting'
-		})
 		if(!thread) throw Errors.sequelizeValidation(Sequelize, {
 			error: 'thread does not exist',
 			path: 'id'

+ 4 - 5
routes/thread.js

@@ -2,7 +2,7 @@ let express = require('express')
 let router = express.Router()
 
 const Errors = require('../lib/errors.js')
-let { User, Thread, Category, Post, Sequelize } = require('../models')
+let { User, Thread, Category, Post, Ban, Sequelize } = require('../models')
 let pagination = require('../lib/pagination.js')
 
 router.get('/:thread_id', async (req, res) => {
@@ -49,6 +49,8 @@ router.post('/', async (req, res) => {
 	let validationErrors = []
 
 	try {
+		await Ban.canCreateThreads(req.session.username)
+
 		let category = await Category.findOne({ where: {
 			value: req.body.category
 		}})
@@ -57,10 +59,7 @@ router.post('/', async (req, res) => {
 		let user = await User.findOne({ where: {
 			username: req.session.username	
 		}})
-	
-		if(!user.canCreateThreads) throw Errors.sequelizeValidation(Sequelize, {
-			error: 'You have been banned from creating threads'
-		})
+
 
 		let thread = await Thread.create({
 			name: req.body.name

+ 0 - 51
routes/user.js

@@ -245,55 +245,4 @@ router.delete('/:username', async (req, res) => {
 	}
 })
 
-router.all('*', (req, res, next) => {
-	if(req.session.admin) {
-		next()
-	} else {
-		res.status(400)
-		res.json({
-			errors: [Errors.requestNotAuthorized]
-		})
-	}
-})
-
-router.put('/:username/permissions', async (req, res) => {
-	try {
-		let update = {}
-		if(typeof req.body.canCreatePosts === 'boolean') {
-			update.canCreatePosts = req.body.canCreatePosts
-		}
-		if(typeof req.body.canCreateThreads === 'boolean') {
-			update.canCreateThreads = req.body.canCreateThreads
-		}
-
-		let affectedRows = await User.update(
-			update,
-			{ where: { username: req.params.username } }
-		)
-
-		//If the number of affected rows is 0
-		//i.e. the username does not match any records
-		if(!affectedRows[0]) { 
-			throw Errors.sequelizeValidation(Sequelize, {
-				error: 'user does not exist',
-				value: req.params.username
-			})
-		} else {
-			res.json({ success: true })
-		}
-	} catch (e) {
-		if(e instanceof Sequelize.ValidationError) {
-			res.status(400)
-			res.json(e)
-		} else {
-			console.log(e)
-
-			res.status(500)
-			res.json({
-				errors: [Errors.unknown]
-			})
-		}
-	}
-})
-
 module.exports = router

+ 1 - 0
server.js

@@ -38,6 +38,7 @@ app.use('/api/v1/notification', require('./routes/notification'))
 app.use('/api/v1/post', require('./routes/post'))
 app.use('/api/v1/settings', require('./routes/settings'))
 app.use('/api/v1/report', require('./routes/report'))
+app.use('/api/v1/ban', require('./routes/ban'))
 
 sequelize
 	.sync({ force: true })

+ 223 - 0
test/ban.js

@@ -0,0 +1,223 @@
+process.env.NODE_ENV = 'test'
+process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"
+
+let chai = require('chai')
+let server = require('../server')
+let should = chai.should()
+let expect = chai.expect
+
+let { sequelize, User, Ban } = require('../models')
+const Errors = require('../lib/errors.js')
+
+chai.use(require('chai-http'))
+chai.use(require('chai-things'))
+
+let adminAccount = chai.request.agent(server)
+let userAccount = chai.request.agent(server)
+let userAccountId, anotherUserAccountId
+
+describe('Ban', () => {
+	//Wait for app to start before commencing
+	before((done) => {
+		function createAccounts () {
+			adminAccount
+				.post('/api/v1/user')
+				.set('content-type', 'application/json')
+				.send({
+					username: 'adminaccount',
+					password: 'password',
+					admin: true
+				})
+				.then(_ => {
+					return userAccount
+						.post('/api/v1/user')
+						.set('content-type', 'application/json')
+						.send({
+							username: 'anotheruseraccount',
+							password: 'password'
+						})
+				})
+				.then(res => {
+					anotherUserAccountId = res.body.id
+
+					return userAccount
+						.post('/api/v1/user')
+						.set('content-type', 'application/json')
+						.send({
+							username: 'useraccount',
+							password: 'password'
+						})
+				})
+				.then(res => {
+					userAccountId = res.body.id
+
+					return adminAccount
+						.post('/api/v1/category')
+						.set('content-type', 'application/json')
+						.send({ name: 'category_name' })
+				})
+				.then(_ => {
+					return userAccount
+						.post('/api/v1/thread')
+						.set('content-type', 'application/json')
+						.send({ name: 'thread', category: 'category_name' })
+
+				})
+				.then(_ => done())
+				.catch(done)
+		}
+
+
+		if(server.locals.appStarted) createAccounts()
+
+		server.on('appStarted', () => {
+			createAccounts()
+		})
+	})
+
+
+	//Delete all rows in table after
+	//tests completed
+	after(() => sequelize.sync({ force: true }) )
+
+	describe('/ban POST', () => {
+
+		it('should create a ban for the user', async () => {
+			let res = await adminAccount
+				.post('/api/v1/ban')
+				.set('content-type', 'application/json')
+				.send({
+					canCreatePosts: false,
+					canCreateThreads: false,
+					userId: userAccountId,
+					message: 'ban message'
+				})
+
+			res.should.be.json
+			res.should.have.status(200)
+
+			let ban = await Ban.findById(1)
+			ban.should.have.property('canCreatePosts', false)
+			ban.should.have.property('canCreateThreads', false)
+			ban.should.have.property('message', 'ban message')
+			ban.should.have.property('UserId', userAccountId)
+		})
+		it('should return an error if not an administrator', done => {
+			userAccount
+				.post('/api/v1/ban')
+				.set('content-type', 'application/json')
+				.send({
+					canCreatePosts: false,
+					canCreateThreads: false,
+					userId: userAccountId,
+					message: 'ban message'
+				})
+				.end((err, res) => {
+					res.should.be.json
+					res.should.have.status(400)
+					res.body.errors.should.contain.something.that.deep.equals(Errors.requestNotAuthorized)
+
+					done()
+				})
+		})
+		it('should return an error if user id is not valid', done => {
+			adminAccount
+				.post('/api/v1/ban')
+				.set('content-type', 'application/json')
+				.send({
+					canCreatePosts: false,
+					canCreateThreads: false,
+					userId: 'not an id',
+					message: 'ban message'
+				})
+				.end((err, res) => {
+					res.should.be.json
+					res.should.have.status(400)
+					res.body.errors.should.contain.something.that.has.property('message', 'user does not exist')
+
+					done()
+				})
+		})
+		it('should return an error if trying to post replies if permissions so set', done => {
+			userAccount
+				.post('/api/v1/post')
+				.set('content-type', 'application/json')
+				.send({
+					threadId: 1,
+					content: 'post'
+				})
+				.end((err, res) => {
+					res.should.be.json
+					res.should.have.status(400)
+					res.body.errors.should.contain.something.with.property('message', 'ban message')
+
+					done()
+				})
+		})
+		it('should return an error if trying to create thread if permissions so set', done => {
+			userAccount
+				.post('/api/v1/thread')
+				.set('content-type', 'application/json')
+				.send({
+					category: 'category',
+					name: 'thread name'
+				})
+				.end((err, res) => {
+					res.should.be.json
+					res.should.have.status(400)
+					res.body.errors.should.contain.something.with.property('message', 'ban message')
+
+					done()
+				})
+		})
+	})
+
+	describe('/ban GET', () => {
+		before(done => {
+			adminAccount
+				.post('/api/v1/ban')
+				.set('content-type', 'application/json')
+				.send({
+					canCreatePosts: false,
+					canCreateThreads: false,
+					userId: anotherUserAccountId,
+					message: 'ban message2'
+				})
+				.end((err, res) => {
+					done()
+				})
+		})
+
+		it('should get all bans', async () => {
+			let res = await adminAccount.get('/api/v1/ban')
+
+			res.should.be.json
+			res.should.have.status(200)
+			res.body.should.contain.something.with.deep.property('User.username', 'useraccount')
+			res.body.should.have.length(2)
+		})
+	})
+
+	describe('/ban/:ban_id DELETE', () => {
+		it('should remove a ban', async () => {
+			let res = await adminAccount.delete('/api/v1/ban/1')
+
+			res.should.be.json
+			res.should.have.status(200)
+
+			let ban = await Ban.findAll()
+			ban.should.have.length(1)
+			ban[0].should.have.property('id', 2)
+		})
+		it('should return an error if ban id is not valid', done => {
+			adminAccount.delete('/api/v1/ban/notarealid')
+				.end((err, res) => {
+					res.should.be.json
+					res.should.have.status(400)
+					res.body.errors.should.contain.something.that.has.property('message', 'ban does not exist')
+
+					done()
+				})
+		})
+	})
+})

+ 0 - 120
test/user.js

@@ -520,126 +520,6 @@ describe('User', () => {
 		})
 	})
 
-	describe('/:username/permissions PUT', () => {
-		let admin = chai.request.agent(server)
-		let user = chai.request.agent(server)
-
-		before(async () => {
-			await admin.post('/api/v1/user/adminaccount/login')
-				.set('content-type', 'application/json')
-				.send({
-					password: 'password'
-				})
-
-			await admin.post('/api/v1/category')
-				.set('content-type', 'application/json')
-				.send({
-					name: 'category'
-				})
-
-			await admin.post('/api/v1/thread')
-				.set('content-type', 'application/json')
-				.send({
-					category: 'category',
-					name: 'thread'
-				})
-			
-			await user.post('/api/v1/user')
-				.set('content-type', 'application/json')
-				.send({
-					username: 'user123',
-					password: 'password'
-				})
-		})
-
-		it('should update the permissions for the user', async () => {
-			let res = await admin
-				.put('/api/v1/user/user123/permissions')
-				.set('content-type', 'application/json')
-				.send({
-					canCreatePosts: false
-				})
-
-			res.should.be.json
-			res.should.have.status(200)
-
-			let user = await User.findOne({
-				where: { username: 'user123' }
-			})
-			user.should.have.property('canCreatePosts', false)
-		})
-		it('should return an error if not an administrator', done => {
-			user
-				.put('/api/v1/user/user123/permissions')
-				.set('content-type', 'application/json')
-				.send({
-					canCreatePosts: true
-				})
-				.end((err, res) => {
-					res.should.be.json
-					res.should.have.status(400)
-					res.body.errors.should.contain.something.that.deep.equals(Errors.requestNotAuthorized)
-
-					done()
-				})
-		})
-		it('should return an error if username is not valid', done => {
-			admin
-				.put('/api/v1/user/fakeuser123/permissions')
-				.set('content-type', 'application/json')
-				.send({
-					canCreatePosts: true
-				})
-				.end((err, res) => {
-					res.should.be.json
-					res.should.have.status(400)
-					res.body.errors.should.contain.something.that.has.property('message', 'user does not exist')
-
-					done()
-				})
-		})
-		it('should return an error if trying to post replies if permissions so set', done => {
-			user
-				.post('/api/v1/post')
-				.set('content-type', 'application/json')
-				.send({
-					threadId: 1,
-					content: 'post'
-				})
-				.end((err, res) => {
-					res.should.be.json
-					res.should.have.status(400)
-					res.body.errors.should.contain.something.with.property('message', 'You have been banned from posting')
-
-					done()
-				})
-		})
-		it('should return an error if trying to create thread if permissions so set', done => {
-			admin
-				.put('/api/v1/user/user123/permissions')
-				.set('content-type', 'application/json')
-				.send({
-					canCreateThreads: false	
-				})
-				.end((err, res) => {
-					user
-						.post('/api/v1/thread')
-						.set('content-type', 'application/json')
-						.send({
-							category: 'category',
-							name: 'thread name'
-						})
-						.end((err, res) => {
-							res.should.be.json
-							res.should.have.status(400)
-							res.body.errors.should.contain.something.with.property('message', 'You have been banned from creating threads')
-
-							done()
-						})
-				})
-		})
-	})
-
 	describe('/:username PUT user', () => {
 		let agent = chai.request.agent(server)