We use the attribute `emails` (plural) for email addresses with other auth providers like LDAP or SAML. In case of OAuth2 we used the attribute `email` (singular) which resulted in problems. Furthermore the OAuth2 strategy fell into the default fallback of the provider switch statement. This statement did not check email addresses but did generate the letter-avatar instantly. Signed-off-by: Erik Michelson <github@erik.michelson.eu>
		
			
				
	
	
		
			171 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			171 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
'use strict'
 | 
						|
// external modules
 | 
						|
const Sequelize = require('sequelize')
 | 
						|
const scrypt = require('scrypt-kdf')
 | 
						|
const filterXSS = require('xss')
 | 
						|
 | 
						|
// core
 | 
						|
const logger = require('../logger')
 | 
						|
const { generateAvatarURL } = require('../letter-avatars')
 | 
						|
 | 
						|
module.exports = function (sequelize, DataTypes) {
 | 
						|
  const 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.prototype.verifyPassword = function (attempt) {
 | 
						|
    return scrypt.verify(Buffer.from(this.password, 'hex'), attempt)
 | 
						|
  }
 | 
						|
 | 
						|
  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: filterXSS(profile.displayName || profile.username),
 | 
						|
        photo: User.parsePhotoByProfile(profile),
 | 
						|
        biggerphoto: User.parsePhotoByProfile(profile, true)
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return profile
 | 
						|
  }
 | 
						|
  User.parsePhotoByProfile = function (profile, bigger) {
 | 
						|
    let 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':
 | 
						|
        photo = 'https://avatars.githubusercontent.com/u/' + profile.id
 | 
						|
        if (bigger) photo += '?s=400'
 | 
						|
        else photo += '?s=96'
 | 
						|
        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
 | 
						|
      default:
 | 
						|
        if (profile.emails && profile.emails.length > 0) {
 | 
						|
          photo = generateAvatarURL(profile.username, profile.emails[0])
 | 
						|
          break
 | 
						|
        }
 | 
						|
        photo = generateAvatarURL(profile.username)
 | 
						|
        break
 | 
						|
    }
 | 
						|
    return filterXSS(photo)
 | 
						|
  }
 | 
						|
  User.parseProfileByEmail = function (email) {
 | 
						|
    return {
 | 
						|
      name: email.substring(0, email.lastIndexOf('@')),
 | 
						|
      photo: generateAvatarURL('', email, false),
 | 
						|
      biggerphoto: generateAvatarURL('', email, true)
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  function updatePasswordHashHook (user, options) {
 | 
						|
    // suggested way to hash passwords to be able to do this asynchronously:
 | 
						|
    // @see https://github.com/sequelize/sequelize/issues/1821#issuecomment-44265819
 | 
						|
 | 
						|
    if (!user.changed('password')) {
 | 
						|
      return Promise.resolve()
 | 
						|
    }
 | 
						|
 | 
						|
    return scrypt.kdf(user.getDataValue('password'), { logN: 15 }).then(keyBuf => {
 | 
						|
      user.setDataValue('password', keyBuf.toString('hex'))
 | 
						|
    })
 | 
						|
  }
 | 
						|
 | 
						|
  User.beforeCreate(updatePasswordHashHook)
 | 
						|
  User.beforeUpdate(updatePasswordHashHook)
 | 
						|
 | 
						|
  return User
 | 
						|
}
 |