codimd/lib/models/user.js
Rafal Proszowski e1977a1da7
Fix GitHub's avatar URL
At the moment, the URL is being composed and modified with the use of
string composition.

This causes issues, if the URL returned by GitHub slightly differs from
the time developer initially had a look into it.

In our case, the URL from GitHub has two query parameters in it, whilst
the codebase only expected one.

This change will take all of these parameters and only set the one we
care about, whilst leaving others intact and carry on with the full URL.

Fixes #1489

Signed-off-by: Rafal Proszowski <paroxp@gmail.com>
2020-04-20 12:25:32 +01:00

173 lines
4.7 KiB
JavaScript

'use strict'
// external modules
var Sequelize = require('sequelize')
var Scrypt = require('scrypt-kdf')
// core
var logger = require('../logger')
var { generateAvatarURL } = require('../letter-avatars')
module.exports = function (sequelize, DataTypes) {
var User = sequelize.define('User', {
id: {
type: DataTypes.UUID,
primaryKey: true,
defaultValue: Sequelize.UUIDV4
},
profileid: {
type: DataTypes.STRING,
unique: true
},
profile: {
type: DataTypes.TEXT
},
history: {
type: DataTypes.TEXT
},
accessToken: {
type: DataTypes.TEXT
},
refreshToken: {
type: DataTypes.TEXT
},
deleteToken: {
type: DataTypes.UUID,
defaultValue: Sequelize.UUIDV4
},
email: {
type: Sequelize.TEXT,
validate: {
isEmail: true
}
},
password: {
type: Sequelize.TEXT
}
})
User.hashPassword = async function (plain) {
return (await Scrypt.kdf(plain, await Scrypt.pickParams(0.1))).toString('hex')
}
User.prototype.verifyPassword = async function (attempt) {
if (await Scrypt.verify(Buffer.from(this.password, 'hex'), attempt)) {
return this
}
return false
}
User.addHook('beforeCreate', async function (user) {
// only do hash when password is presented
if (user.password) {
user.password = await User.hashPassword(user.password)
}
})
User.addHook('beforeUpdate', async function (user) {
if (user.changed('password')) {
user.password = await User.hashPassword(user.password)
}
})
User.associate = function (models) {
User.hasMany(models.Note, {
foreignKey: 'ownerId',
constraints: false
})
User.hasMany(models.Note, {
foreignKey: 'lastchangeuserId',
constraints: false
})
}
User.getProfile = function (user) {
if (!user) {
return null
}
return user.profile ? User.parseProfile(user.profile) : (user.email ? User.parseProfileByEmail(user.email) : null)
}
User.parseProfile = function (profile) {
try {
profile = JSON.parse(profile)
} catch (err) {
logger.error(err)
profile = null
}
if (profile) {
profile = {
name: profile.displayName || profile.username,
photo: User.parsePhotoByProfile(profile),
biggerphoto: User.parsePhotoByProfile(profile, true)
}
}
return profile
}
User.parsePhotoByProfile = function (profile, bigger) {
var photo = null
switch (profile.provider) {
case 'facebook':
photo = 'https://graph.facebook.com/' + profile.id + '/picture'
if (bigger) photo += '?width=400'
else photo += '?width=96'
break
case 'twitter':
photo = 'https://twitter.com/' + profile.username + '/profile_image'
if (bigger) photo += '?size=original'
else photo += '?size=bigger'
break
case 'github':
const photoURL = new URL(profile.photos && profile.photos[0]
? profile.photos[0].value
: `https://avatars.githubusercontent.com/u/${profile.id}`)
photoURL.searchParams.set('s', bigger ? 400 : 96)
photo = photoURL.toString()
break
case 'gitlab':
photo = profile.avatarUrl
if (photo) {
if (bigger) photo = photo.replace(/(\?s=)\d*$/i, '$1400')
else photo = photo.replace(/(\?s=)\d*$/i, '$196')
} else {
photo = generateAvatarURL(profile.username)
}
break
case 'mattermost':
photo = profile.avatarUrl
if (photo) {
if (bigger) photo = photo.replace(/(\?s=)\d*$/i, '$1400')
else photo = photo.replace(/(\?s=)\d*$/i, '$196')
} else {
photo = generateAvatarURL(profile.username)
}
break
case 'dropbox':
photo = generateAvatarURL('', profile.emails[0].value, bigger)
break
case 'google':
photo = profile.photos[0].value
if (bigger) photo = photo.replace(/(\?sz=)\d*$/i, '$1400')
else photo = photo.replace(/(\?sz=)\d*$/i, '$196')
break
case 'ldap':
photo = generateAvatarURL(profile.username, profile.emails[0], bigger)
break
case 'saml':
photo = generateAvatarURL(profile.username, profile.emails[0], bigger)
break
case 'oauth2':
photo = profile.photo
if (!photo) photo = generateAvatarURL(profile.username, profile.email, bigger)
break
}
return photo
}
User.parseProfileByEmail = function (email) {
return {
name: email.substring(0, email.lastIndexOf('@')),
photo: generateAvatarURL('', email, false),
biggerphoto: generateAvatarURL('', email, true)
}
}
return User
}