环回自定义密码验证

Loopback custom password validation

非常简单的问题:如果我尝试在用户模型中验证密码,似乎我只能验证已经加密的密码? 例如,如果我使用

 Customer.validatesLengthOf('password', { min: 8, message: 'Too short' })

然后检查加密密码(总是超过 8 个字符),所以不好...如果我尝试使用自定义验证,我如何才能访问原始密码(原始 req.body.password 基本上)?

好的,没有答案,所以我正在做的是使用远程挂钩来访问原始明文密码,现在就可以了。

var plainPwd
Customer.beforeRemote( 'create', function (ctx, inst, next) {
    plainPwd = ctx.req.body.password
    next()
})

然后我可以在自定义验证中使用它:

Customer.validate( 'password', function (err, res) {
    const pattern = new RegExp(/some-regex/)
    if (plainPwd && ! pattern.test( plainPwd )) err()
}, { message: 'Invalid format' }) 

好吧,我想上面的答案很新颖,显然是可以接受的,但是如果你想要一个真正简单的解决方案,只需要完成一些基本的验证而不需要太多代码,那么 loopback-mixin-complexity 是适合你的解决方案。

如果您不想创建另一个依赖项,那么您可以继续自定义 mixin,您可以将其添加到您的用户模型或任何其他需要某种验证的模型中,并且它会为您做验证。

这里有一个关于如何创建这样的 mixin 的示例代码

module.exports = function(Model, options) {
  'use strict';
  Model.observe('before save', function event(ctx, next) { //Observe any insert/update event on Model
    if (ctx.instance) {
      if(!yourValidatorFn(ctx.instance.password) )
         next('password not valid');
      else 
        next();
    } 
    else {
      if(!yourValidatorFn(ctx.data.password) )
         next('password not valid');
      else 
        next();
    }
  });
};

编辑(2019 年 8 月 20 日):我不确定这在最新的环回版本中是否仍然是一个问题。

其实这是一个known problem in loopback. The tacitly approved solution is to override the <UserModel>.validatePassword() method with your own。 YMMV.

akapaul commented on Jan 10, 2017 •

I've found another way to do this. In common model User there is a method called validatePassword. If we extend our UserModel from User, we can redefine this method in JS, like following:

var g = require('loopback/lib/globalize');

module.exports = function(UserModel) {
  UserModel.validatePassword = function(plain) {
    var err,
        passwordProperties = UserModel.definition.properties.password;

    if (plain.length > passwordProperties.max) {
      err = new Error (g.f('Password too long: %s (maximum %d symbols)', plain, passwordProperties.max));
      err.code = 'PASSWORD_TOO_LONG';
    } else if (plain.length < passwordProperties.min) {
      err = new Error(g.f('Password too short: %s (minimum %d symbols)', plain, passwordProperties.min));
      err.code = 'PASSWORD_TOO_SHORT';
    } else if(!(new RegExp(passwordProperties.pattern, 'g').test(plain))) {
      err =  new Error(g.f('Invalid password: %s (symbols and numbers are allowed)', plain));
      err.code = 'INVALID_PASSWORD';
    } else {
      return true;
    }
    err.statusCode = 422;
    throw err;
  };
};

This works for me. I don't think that g (globalize) object is required here, but I added this, just in case. Also, I've added my validator options in JSON definition of UserModel, because of Loopback docs

为了使用上面的代码,可以像这样将它们的验证规则放在模型的 .json 定义中(参见 maxminpattern properties.password):

{
  "name": "UserModel",
  "base": "User",
  ...
  "properties": {
    ...
    "password": {
      "type": "string",
      "required": true,
      ...
      "max": 50,
      "min": 8,
      "pattern": "(?=.*[A-Z])(?=.*[!@#$&*])(?=.*[0-9])(?=.*[a-z])^.*$"
    },
    ...
  },
  ...
}