denali-js/denali-auth

View on GitHub
lib/facets/resetable.ts

Summary

Maintainability
A
0 mins
Test Coverage
import {
  defaults
} from 'lodash';
import { createMixin, hasMany, Model } from 'denali';
import * as moment from 'moment';
import { map } from 'bluebird';
import * as createDebug from 'debug';

const debug = createDebug('denali-auth:resetable');

export default createMixin((BaseModel: typeof Model, options = {}) => {
  options = defaults(options, {
    expiresAfter: moment.duration(2, 'days')
  });

  // TODO: ensure that the mixin base has passwordable applied (and do the same
  // elsewhere to ensure proper super overriding order)

  return class ResetableMixin extends BaseModel {

    static isResetable = true;

    static passwordResetTokens = hasMany('password-reset-token');

    async sendResetPasswordEmail() {
      let ResetToken = this.modelFor('password-reset-token');
      debug(`password reset requested for ${ this.email }, clearing out existing reset tokens first`);
      let tokens = <Model[]>await this.getPasswordResetTokens();
      await map(tokens, (t) => t.delete());
      let token = await ResetToken.create({
        userId: this.id,
        userType: this.type,
        expiresAt: moment() + (options.expireAfter)
      });
      await token.save();
      debug(`sending password reset email to ${ this.email }`);
      return (<any>this.service('mailer')).send('reset-password', { user: this, token });
    }

    async resetPassword(token: Model, password: string) {
      debug(`resetting password for ${ this.type } ${ this.id }`);
      this.password = password;
      this.passwordResetAt = new Date();
      await this.save();
      debug(`clearing any outstanding reset tokens`);
      let tokens = <Model[]>await this.getPasswordResetTokens();
      map(tokens, (t) => t.delete());
    }

  };
});