Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions sequelize/data/user-role.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
"path": "/entity",
"method": "GET"
},
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you're wildcarding, do you need the rule above this one?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that Casbin is touchy. It is very literal, so anything leading up to a wildcard isn't allowed because of the wildcard, but I'll test this.

"role": "user",
"path": "/entity/*",
"method": "GET"
},
{
"role": "user",
"path": "/entity",
Expand All @@ -40,6 +45,11 @@
"path": "/contact",
"method": "GET"
},
{
"role": "user",
"path": "/contact/*",
"method": "GET"
},
{
"role": "user",
"path": "/contact",
Expand Down
206 changes: 105 additions & 101 deletions src/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,40 @@ import jwt from 'jsonwebtoken'
import utils from '../utils'

const user = (sequelize, DataTypes) => {
// Defining our user table and setting User object.
const User = sequelize.define('User', {
id: {
type: DataTypes.UUID,
primaryKey: true,
defaultValue: DataTypes.UUIDV4,
allowNull: false,
autoIncrement: false,
},
email: {
type: DataTypes.STRING,
unique: true,
required: true
},
password: {
type: DataTypes.STRING,
required: true
},
salt: {
type: DataTypes.STRING
},
displayName: {
type: DataTypes.STRING
},
phone: {
type: DataTypes.STRING
},
attributes: {
type: DataTypes.JSON,
}
},
{
schema: process.env.DATABASE_SCHEMA
})
// Defining our user table and setting User object.
const User = sequelize.define('User', {
id: {
type: DataTypes.UUID,
primaryKey: true,
defaultValue: DataTypes.UUIDV4,
allowNull: false,
autoIncrement: false,
},
email: {
type: DataTypes.STRING,
unique: true,
required: true
},
password: {
type: DataTypes.STRING,
required: true
},
salt: {
type: DataTypes.STRING
},
displayName: {
type: DataTypes.STRING
},
phone: {
type: DataTypes.STRING
},
attributes: {
type: DataTypes.JSON,
}
},
{
schema: process.env.DATABASE_SCHEMA
})

/**
* Looks up and validates a user by email and password.
Expand All @@ -46,40 +46,44 @@ const user = (sequelize, DataTypes) => {
*
* @return {String} returns either the valid login token or an error message.
*/
User.findByLogin = async (email, password) => {
const user = await User.findOne({
where: {email}
})
if (user) {
const pw = User.encryptPassword(password, user.salt)
if (pw === user.password) {
return await User.getToken(user.id, user.email)
}
}
}

User.decodeToken = async token => {
return jwt.verify(token, process.env.JWT_KEY)
}

/** @todo deprecate this */
User.getToken = async (userId, email, expiresIn = '1d') => {
const token = jwt.sign(
{userId, email, type: 'user'},
process.env.JWT_KEY,
{expiresIn}
)
return token
}
User.findByLogin = async (email, password) => {
const user = await User.findOne({
where: { email }
})
if (user) {
const pw = User.encryptPassword(password, user.salt)
if (pw === user.password) {

const e = await utils.loadCasbin()
const roles = await e.getRolesForUser(user.email)

return await User.getToken(user.id, user.email, roles[0])
}
}
}

User.decodeToken = async token => {
return jwt.verify(token, process.env.JWT_KEY)
}

/** @todo deprecate this */
User.getToken = async (userId, email, type, expiresIn = '1d') => {
const token = jwt.sign(
{ userId, email, type },
process.env.JWT_KEY,
{ expiresIn }
)
return token
}

/**
* Generates a random salt for password security.
*
* @return {String} The password salt.
*/
User.generateSalt = () => {
return crypto.randomBytes(16).toString('base64')
}
User.generateSalt = () => {
return crypto.randomBytes(16).toString('base64')
}

/**
* Salts and hashes a password.
Expand All @@ -89,45 +93,45 @@ const user = (sequelize, DataTypes) => {
*
* @return {String} The secured password.
*/
User.encryptPassword = (plainText, salt) => {
return crypto
.createHash('RSA-SHA256')
.update(plainText)
.update(salt)
.digest('hex')
}

// Setters
const setSaltAndPassword = user => {
if (user.changed('password')) {
user.salt = User.generateSalt()
// User.password = User.encryptPassword(user.password, user.salt)
user.password = utils.encryptPassword(user.password, user.salt)
}
}

// Other Helpers
// const validateContactInfo = user => {
// let valid = true

// if (!validator.isEmail(validator.normalizeEmail(user.email))) {
// valid = false
// }

// /** @todo Add more validations for all contact info. */

// return valid
// }

// Create prep actions
// User.beforeCreate(validateContactInfo)
User.beforeCreate(setSaltAndPassword)

// Update prep actions
// User.beforeUpdate(validateContactInfo)
User.beforeUpdate(setSaltAndPassword)

return User
User.encryptPassword = (plainText, salt) => {
return crypto
.createHash('RSA-SHA256')
.update(plainText)
.update(salt)
.digest('hex')
}

// Setters
const setSaltAndPassword = user => {
if (user.changed('password')) {
user.salt = User.generateSalt()
// User.password = User.encryptPassword(user.password, user.salt)
user.password = utils.encryptPassword(user.password, user.salt)
}
}

// Other Helpers
// const validateContactInfo = user => {
// let valid = true

// if (!validator.isEmail(validator.normalizeEmail(user.email))) {
// valid = false
// }

// /** @todo Add more validations for all contact info. */

// return valid
// }

// Create prep actions
// User.beforeCreate(validateContactInfo)
User.beforeCreate(setSaltAndPassword)

// Update prep actions
// User.beforeUpdate(validateContactInfo)
User.beforeUpdate(setSaltAndPassword)

return User
}

export default user
5 changes: 3 additions & 2 deletions src/routes/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ router.put('/', async (req, res) => {
const response = new utils.Response()
try {
if (validator.isUUID(req.body.id)) {
let { id, name, type, address, phone, email, checkIn, contacts } = req.body
let { id, name, type, address, phone, email, checkIn, contacts, attributes } = req.body

/** @todo validate emails */
// Validating emails
Expand All @@ -140,7 +140,8 @@ router.put('/', async (req, res) => {
entity.type = (type) ? type : entity.type
entity.address = (address) ? address : entity.address
entity.phone = (phone) ? phone : entity.phone
entity.email = (email) ? email : entity.email
entity.email = (email) ? email : entity.email
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you intend to tab these back?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't realize I did. Probably linter.

entity.attributes = (attributes) ? attributes : entity.attributes
/** @todo validate checkIn JSON */
if (entity.checkIn === null && checkIn) {
const checkIns = {
Expand Down
2 changes: 1 addition & 1 deletion src/routes/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import utils from '../utils'
import email from '../email'

const router = new Router()
const max = (process.env.NODE_ENV !== 'production') ? 50000 : 5
const max = (process.env.NODE_ENV !== 'production') ? 50000 : 50
const loginLimiter = rateLimit({
windowMs: 60 * 60 * 1000,
max,
Expand Down
5 changes: 4 additions & 1 deletion src/tests/entity.routes.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ const { expect } = chai
const entity = {
name: randomWords(),
type: 'Test',
contacts: []
contacts: [],
attributes: {
notes: 'test'
}
}
const contact = {
name: randomWords(),
Expand Down
Loading