<commit_before>import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import mergeConfig from './config';

const SALT_ROUNDS = 10;

// Thank you http://stackoverflow.com/a/46181
function isEmail(email) {
  // eslint-disable-next-line
  const re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
  return re.test(email);
}

/**
 * Base class for handling Account functionality.
 * This class should not be used directly and instead extended by database specific implementations.
 *
 * The child class must implement the following methods:
 *
 * createUser({ username, email, provider, identifier, profile, hash }) : Promise(userId)
 * findByUsername(username) : Promise(userId)
 * findByEmail(email) : Promise(userId)
 * findByProvider(provider, identifier) : Promise(userId)
 * findHashById(userId) : Promise(hash)
 */
class Accounts {
  constructor(config) {
    this.config = mergeConfig(config);
  }
  /**
   * Logs a user in through a specific provider.
   *
   * @param {string} provider Provider to login with. Must be defined in the config.
   * @param {Object} response The results of Grant authenticating with the provider.
   * @return {Object} tokens An object containing `userId`, 'accessToken' and 'refreshToken'.
   */
  async loginWithProvider(provider, response) {
    let userId;
    try {
      // Extract a unique identifier from the user's account under the provider
      const extraction = await this.config[provider].extractor(response, this.config[provider]);
      // Find the user id given the provider and identifier
      userId = await this.findByProvider(provider, extraction.identifier);
      // If no id was found that means this is the user's first time signing into our system.
      // We create a new account for them.
      if (!userId) {
        userId = await this.createUser({ provider, ...extraction });
      }
    } catch (e) {
      // TODO handle exception
    }

    return userId && this.generateTokens(userId);
  }
  /**
   * Registers a user.
   *
   * @param {Object} user The user's attributes.
   * @param {string} user.user Email or a username.
   * @param {string} user.username User's username.
   * @param {string} user.email User's email.
   * @param {string} user.password <PASSWORD>
   * @return {Promise} promise A promise resolving to the newly created user's id.
   */
  registerUser({ user, username, email, password }) {
   // TODO Validation needed
    const hash = Accounts.hashPassword(password);
    return this.createUser({ hash, ...Accounts.toUsernameAndEmail({ user, username, email }) });
  }
  /**
   * Log in a user.
   *
   * @param {Object} user The user's attributes.
   * @param {string} user.user Email or a username.
   * @param {string} user.username User's username.
   * @param {string} user.email User's email.
   * @param {string} user.password <PASSWORD>
   */
  async loginUser({ user, username, email, password }) {
    // TODO Validation needed
    const credentials = Accounts.toUsernameAndEmail({
      user, username, email,
    });

    let userId;

    try {
      if (credentials.username) {
        userId = await this.findByUsername(credentials.username);
      } else if (credentials.email) {
        userId = await this.findByEmail(credentials.email);
      }
    } catch (e) {
      throw new Error('User not found');
    }

    const hash = await this.findHashById(userId);

    return new Promise((resolve, reject) => {
      if (Accounts.comparePassword(password, hash)) {
        resolve(this.generateTokens(userId));
      } else {
        reject('Incorrect password');
      }
    });
  }
  /**
   * Generates JWT tokens for the user
   *
   * @param {string} userId The user id to encode in the token.
   * @return {Object} tokens An object containig `userId`, `accessToken` and `refreshToken`.
   */
  generateTokens(userId) {
    const accessToken = jwt.sign({ userId }, this.config.server.secret, { expiresIn: '1h' });
    const refreshToken = jwt.sign({}, this.config.server.secret, { expiresIn: '1h' });
    return { userId, accessToken, refreshToken };
  }
  /**
   * Given a username, user and/or email figure out the username and/or email.
   *
   * @param {Object} object An object containing at least `username`, `user` and/or `email`.<commit_msg>Throw exception in async method instead of using a promise<commit_after>import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import mergeConfig from './config';

const SALT_ROUNDS = 10;

// Thank you http://stackoverflow.com/a/46181
function isEmail(email) {
  // eslint-disable-next-line
  const re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
  return re.test(email);
}

/**
 * Base class for handling Account functionality.
 * This class should not be used directly and instead extended by database specific implementations.
 *
 * The child class must implement the following methods:
 *
 * createUser({ username, email, provider, identifier, profile, hash }) : Promise(userId)
 * findByUsername(username) : Promise(userId)
 * findByEmail(email) : Promise(userId)
 * findByProvider(provider, identifier) : Promise(userId)
 * findHashById(userId) : Promise(hash)
 */
class Accounts {
  constructor(config) {
    this.config = mergeConfig(config);
  }
  /**
   * Logs a user in through a specific provider.
   *
   * @param {string} provider Provider to login with. Must be defined in the config.
   * @param {Object} response The results of Grant authenticating with the provider.
   * @return {Object} tokens An object containing `userId`, 'accessToken' and 'refreshToken'.
   */
  async loginWithProvider(provider, response) {
    let userId;
    try {
      // Extract a unique identifier from the user's account under the provider
      const extraction = await this.config[provider].extractor(response, this.config[provider]);
      // Find the user id given the provider and identifier
      userId = await this.findByProvider(provider, extraction.identifier);
      // If no id was found that means this is the user's first time signing into our system.
      // We create a new account for them.
      if (!userId) {
        userId = await this.createUser({ provider, ...extraction });
      }
    } catch (e) {
      // TODO handle exception
    }

    return userId && this.generateTokens(userId);
  }
  /**
   * Registers a user.
   *
   * @param {Object} user The user's attributes.
   * @param {string} user.user Email or a username.
   * @param {string} user.username User's username.
   * @param {string} user.email User's email.
   * @param {string} user.password <PASSWORD>
   * @return {Promise} promise A promise resolving to the newly created user's id.
   */
  registerUser({ user, username, email, password }) {
   // TODO Validation needed
    const hash = Accounts.hashPassword(password);
    return this.createUser({ hash, ...Accounts.toUsernameAndEmail({ user, username, email }) });
  }
  /**
   * Log in a user.
   *
   * @param {Object} user The user's attributes.
   * @param {string} user.user Email or a username.
   * @param {string} user.username User's username.
   * @param {string} user.email User's email.
   * @param {string} user.password <PASSWORD>
   */
  async loginUser({ user, username, email, password }) {
    // TODO Validation needed
    const credentials = Accounts.toUsernameAndEmail({
      user, username, email,
    });

    let userId;

    try {
      if (credentials.username) {
        userId = await this.findByUsername(credentials.username);
      } else if (credentials.email) {
        userId = await this.findByEmail(credentials.email);
      }
    } catch (e) {
      throw new Error('User not found');
    }

    const hash = await this.findHashById(userId);

    let tokens;
    if (Accounts.comparePassword(password, hash)) {
      tokens = this.generateTokens(userId);
    } else {
      throw new Error('Incorrect password');
    }

    return tokens;
  }
  /**
   * Generates JWT tokens for the user
   *
   * @param {string} userId The user id to encode in the token.
   * @return {Object} tokens An object containig `userId`, `accessToken` and `refreshToken`.
   */
  generateTokens(userId) {
    const accessToken = jwt.sign({ userId }, this.config.server.secret, { expiresIn: '1h' });
    const refreshToken = jwt.sign({}, this.config.server.secret, { expiresIn: '1h' });
    return { userId, accessToken, refreshToken };
  }
  /**
   * Given a username, user and/or email figure out the username and/or email.
   *
   * @param {Object} object An object containing at least `username`, `user` and/or `email`.
{'fraction_non_alphanumeric': 0.14118316268486916, 'fraction_numerical': 0.006370875995449375, 'mean_word_length': 3.170303605313093, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 12, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2078234', 'n_tokens_mistral': 2656, 'n_tokens_neox': 2634, 'n_words': 817}