All files passwordReset.ts

98.31% Statements 58/59
87.5% Branches 14/16
100% Functions 6/6
97.92% Lines 47/48

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161          20x                                                     20x               20x   6x     6x   6x 1x       5x 5x   5x 1x       4x 4x 4x   4x 1x       3x           3x             3x 6x   3x       3x                 20x 3x 3x 3x   3x                 20x 4x 4x 4x   4x               20x 3x 3x 6x                   20x       3x   3x 1x     2x   2x 1x     2x 2x   1x   20x  
/**
 * Services for the password reset table.
 * @packageDocumentation
 */
 
import {
  BaseService,
  getTime,
  newUniqueID,
  prunePasswordResetRecord,
  passwordResetIDLength,
} from "./util";
 
/**
 * Password reset architecture.
 */
export interface PasswordReset {
  id: string;
  email: string;
  createTime: number;
}
 
/**
 * Password reset with only ID architecture.
 */
interface PasswordResetID {
  id: string;
}
 
/**
 * Password reset services.
 */
export class PasswordResetService extends BaseService {
  /**
   * Request a password reset.
   *
   * @param email The email address associated with the user's account.
   * @param prune Whether or not to prune the record when the time comes.
   * @returns The new password reset record's ID.
   */
  public async requestPasswordReset(
    email: string,
    Iprune: boolean = true
  ): Promise<string> {
    // Confirm that the email address exists
    const emailUnused = await this.dbm.userService.uniqueEmail(email);
 
    if (emailUnused) {
      return null;
    }
 
    // Confirm account is verified
    const user = await this.dbm.userService.getUserByEmail(email);
    const verified = await this.dbm.userService.isVerified(user.id);
 
    if (!verified) {
      return null;
    }
 
    // Check that no password reset has already been requested
    let sql = `SELECT id FROM PasswordReset WHERE email = ?;`;
    let params: any[] = [email];
    let rows: PasswordResetID[] = await this.dbm.execute(sql, params);
 
    if (rows.length > 0) {
      return null;
    }
 
    // Create the password reset record
    const newPasswordResetID = await newUniqueID(
      this.dbm,
      "PasswordReset",
      passwordResetIDLength
    );
 
    sql = `
      INSERT INTO PasswordReset (
        id, email, createTime
      ) VALUES (
        ?, ?, ?
      );
    `;
    params = [newPasswordResetID, email, getTime()];
    await this.dbm.execute(sql, params);
 
    Iif (prune) {
      prunePasswordResetRecord(this.dbm, newPasswordResetID);
    }
 
    return newPasswordResetID;
  }
 
  /**
   * Check if a password reset record exists.
   *
   * @param resetID A password reset record's ID.
   * @returns Whether or not the password reset record exists.
   */
  public async resetRecordExists(resetID: string): Promise<boolean> {
    const sql = `SELECT id FROM PasswordReset WHERE id = ?;`;
    const params = [resetID];
    const rows: PasswordResetID[] = await this.dbm.execute(sql, params);
 
    return rows.length > 0;
  }
 
  /**
   * Get a password reset record.
   *
   * @param resetID A password reset record's ID.
   * @returns The password reset record.
   */
  public async getResetRecord(resetID: string): Promise<PasswordReset> {
    const sql = `SELECT * FROM PasswordReset WHERE id = ?;`;
    const params = [resetID];
    const rows: PasswordReset[] = await this.dbm.execute(sql, params);
 
    return rows[0];
  }
 
  /**
   * Delete a password reset record.
   *
   * @param resetID A password reset record's ID.
   */
  public async deleteResetRecord(resetID: string): Promise<void> {
    const sql = `DELETE FROM PasswordReset WHERE id = ?;`;
    const params = [resetID];
    await this.dbm.execute(sql, params);
  }
 
  /**
   * Reset a user's password.
   *
   * @param resetID A password reset record's ID.
   * @param newPassword The user's new password.
   * @returns Whether or not the reset was successful.
   */
  public async resetPassword(
    resetID: string,
    newPassword: string
  ): Promise<boolean> {
    const resetRecord = await this.getResetRecord(resetID);
 
    if (!resetRecord) {
      return false;
    }
 
    const user = await this.dbm.userService.getUserByEmail(resetRecord.email);
 
    if (!user) {
      return false;
    }
 
    await this.dbm.userService.setUserPassword(user.id, newPassword);
    await this.deleteResetRecord(resetID);
 
    return true;
  }
}