web-dev-qa-db-fra.com

Sequelize: ne retourne pas le mot de passe

J'utilise Sequelize pour effectuer une recherche de base de données pour un enregistrement utilisateur et je veux que le comportement par défaut du modèle ne renvoie pas le password champ pour cet enregistrement. Le champ password est un hachage mais je ne veux toujours pas le renvoyer.

J'ai plusieurs options qui fonctionneront, mais aucune ne semble particulièrement bonne:

  1. Créez une méthode de classe personnalisée findWithoutPassword pour le modèle User et dans cette méthode, effectuez une User.find avec le attributes défini comme indiqué dans le Sequelize docs

  2. Faites un User.find et filtrer les résultats dans le contrôleur (non préféré)

  3. Utilisez une autre bibliothèque pour supprimer les attributs indésirables

Y a-t-il une meilleure façon? Le mieux serait qu'il y ait un moyen de spécifier dans la définition du modèle Sequelize de ne jamais retourner le champ password, mais je n'ai pas trouvé de moyen de le faire.

29
Tyler

Je suggère de remplacer la fonction toJSON:

sequelize.define('user', attributes, {
  instanceMethods: {
    toJSON: function () {
      var values = Object.assign({}, this.get());

      delete values.password;
      return values;
    }
  }
});

Ou dans la suite v4

const User = sequelize.define('user', attributes, {});

User.prototype.toJSON =  function () {
  var values = Object.assign({}, this.get());

  delete values.password;
  return values;
}

toJSON est appelé lorsque les données sont retournées à l'utilisateur, donc les utilisateurs finaux ne verront pas le champ du mot de passe, mais il sera toujours disponible dans votre code.

Object.assign clone l'objet retourné - Sinon, vous supprimerez complètement la propriété de l'instance.

56
Jan Aagaard Meier

Une autre façon consiste à ajouter une étendue par défaut au modèle utilisateur.

Ajoutez ceci dans l'objet d'options du modèle

defaultScope: {
  attributes: { exclude: ['password'] },
}

Ou vous pouvez créer une portée distincte pour l'utiliser uniquement dans certaines requêtes.

Ajoutez ceci dans l'objet d'options du modèle

scopes: {
  withoutPassword: {
    attributes: { exclude: ['password'] },
  }
}

Ensuite, vous pouvez l'utiliser dans des requêtes

User.scope('withoutPassword').findAll();
48
Pawan Samdani

Peut-être que vous pouvez simplement ajouter exclude à votre attribut lorsque vous find, ressemble à ceci:

var User = sequelize.define('user', attributes);

User.findAll({
    attributes: {
        exclude: ['password']
    }
});

Lisez le docs pour plus de détails

23
Lai32290

J'aime utiliser une combinaison des deux réponses de Pawan et déclarer ce qui suit:

defaultScope: {
    attributes: { exclude: ['password'] },
},
scopes: {
    withPassword: {
        attributes: { },
    }
}

Cela me permet d'exclure le mot de passe par défaut et d'utiliser la portée withPassword pour renvoyer explicitement le mot de passe en cas de besoin, comme lors de l'exécution d'une méthode de connexion.

userModel.scope('withPassword').findAll()

Cela garantit que le mot de passe n'est pas retourné lors de l'inclusion de l'utilisateur via un champ référencé, par ex.

accountModel.findAll({
    include: [{
        model: userModel,
        as: 'user'
    }]
})
12
Matthew Cawley

J'ai pu faire fonctionner cela en ajoutant un getter au champ qui renvoie undefined

firstName: {
      type: DataTypes.STRING,
      get() {
        return undefined;
      }
    }

réponse acceptée ne fonctionne pas lorsque le modèle est inclus à partir d'un autre modèle.

J'avais un champ virtuel, disons fullName qui dépend des champs que je voulais cacher, disons firstName et lastName. Et la solution basée sur defaultScope ne fonctionne pas dans ce cas.

1
Ajmal Ahammed

il y a un plugin pour attributs de portée comme discuté ici .

je suis allé avec le remplacement comme mentionné dans la réponse acceptée, sauf que j'ai appelé l'original toJSON plutôt que get avec this.constructor.super_.prototype.toJSON.apply(this, arguments) comme décrit dans les documentation api

0
user2571090

Le code ci-dessous a fonctionné pour moi. Nous voulions accéder aux attributs d'instance au moment de l'exécution mais les supprimer avant d'envoyer les données au client.

const Sequelize = require('sequelize')

const sequelize = new Sequelize('postgres://user:[email protected]:5432/dbname')

const PROTECTED_ATTRIBUTES = ['password', 'token']

const Model = Sequelize.Model

class User extends Model {
  toJSON () {
    // hide protected fields
    let attributes = Object.assign({}, this.get())
    for (let a of PROTECTED_ATTRIBUTES) {
      delete attributes[a]
    }
    return attributes
  }
}

User.init({
  email: {
    type: Sequelize.STRING,
    unique: true,
    allowNull: false,
    validate: {
      isEmail: true
    }
  },
  password: {
    type: Sequelize.STRING,
    allowNull: false
  },
  token: {
    type: Sequelize.STRING(16),
    unique: true,
    allowNull: false
  },
},
{
  sequelize,
  modelName: 'user'
})

module.exports = User

Github Gist

0
Arivia