<commit_before>var crypto = require('crypto');
var errors = require('./error');

function hash(msg, key) {
  return crypto.createHmac('sha256', key).update(msg).digest('hex');
}

UserProvider = function(redisClient) {
  this.rclient = redisClient;
};

UserProvider.prototype = {
  createNew: function(login, name, password, admin, acl, next) {
    var provider = this;
    this.findByLogin(login, function(error, user) {
      if (!user) {
        var newUser = new User();
        newUser.login = login;
        newUser.name = name;
        newUser.setPassword(password);
        newUser.admin = admin;
        newUser.acl = acl;
        provider.rclient.incr('seq:nextUserId', function(error, nuid) {
          if (error) { return next(error); }
          newUser.uid = nuid;

          provider.rclient.set("uid:" + newUser.uid + ":user", JSON.stringify(newUser));
          provider.rclient.set("login:" + newUser.login + ":uid", newUser.uid);
          provider.rclient.sadd("uids", newUser.uid);

          next(null, newUser);
        });
      } else {
        next(new Error('Login already used'));
      }
    });
  },

  updateUser: function(user, next) {
    var provider = this;

    this.findByUid(user.uid, function(error, fuser) {
      if (error) { return next(error); }
      if (!fuser) { return next(new errors.NotFound("User not found")); }
      if (user.login != fuser.login) {
        return next(new Error("You can not change login!"));
      }

      provider.rclient.set("uid:" + fuser.uid + ":user", JSON.stringify(user));

      return next(null, user);
    });
  },

  deleteUser: function(uid, next) {
    var provider = this;

    this.findByUid(uid, function(error, fuser) {
      if (error) { return next(error); }
      if (!fuser) { return next(new errors.NotFound("User not found")); }

      // TODO: unlink all devices

      provider.rclient.del("uid:" + fuser.uid + ":user");
      provider.rclient.del("login:" + fuser.login + ":uid");
      provider.rclient.srem("uids", fuser.uid);

      next();
    });
  },

  findByUid: function(uid, next) {
    this.rclient.get("uid:" + uid + ":user", function(error, data) {
      if (error) { return next(error); }
      if (!data) { return next(); }
      next(null, new User(JSON.parse(data)));
    });
  },

  findByLogin: function(login, next) {
    var provider = this;
    provider.rclient.get("login:" + login + ":uid", function(error, uid) {
      if (error) { return next(error); }
      if (!uid) { next(); return null; }
      if (next) {
        provider.findByUid(uid, next);
      }

      return uid;
    });
  },<commit_msg>delete user's devices when deleting him<commit_after>var crypto = require('crypto');
var errors = require('./error');

function hash(msg, key) {
  return crypto.createHmac('sha256', key).update(msg).digest('hex');
}

UserProvider = function(redisClient, deviceProvider) {
  this.rclient = redisClient;
  this.deviceProvider = deviceProvider;
};

UserProvider.prototype = {
  createNew: function(login, name, password, admin, acl, next) {
    var provider = this;
    this.findByLogin(login, function(error, user) {
      if (!user) {
        var newUser = new User();
        newUser.login = login;
        newUser.name = name;
        newUser.setPassword(password);
        newUser.admin = admin;
        newUser.acl = acl;
        provider.rclient.incr('seq:nextUserId', function(error, nuid) {
          if (error) { return next(error); }
          newUser.uid = nuid;

          provider.rclient.set("uid:" + newUser.uid + ":user", JSON.stringify(newUser));
          provider.rclient.set("login:" + newUser.login + ":uid", newUser.uid);
          provider.rclient.sadd("uids", newUser.uid);

          next(null, newUser);
        });
      } else {
        next(new Error('Login already used'));
      }
    });
  },

  updateUser: function(user, next) {
    var provider = this;

    this.findByUid(user.uid, function(error, fuser) {
      if (error) { return next(error); }
      if (!fuser) { return next(new errors.NotFound("User not found")); }
      if (user.login != fuser.login) {
        return next(new Error("You can not change login!"));
      }

      provider.rclient.set("uid:" + fuser.uid + ":user", JSON.stringify(user));

      return next(null, user);
    });
  },

  deleteUser: function(uid, next) {
    var provider = this;

    this.findByUid(uid, function(error, fuser) {
      if (error) { return next(error); }
      if (!fuser) { return next(new errors.NotFound("User not found")); }

      var delUser = function() {
        provider.rclient.del("uid:" + fuser.uid + ":user");
        provider.rclient.del("uid:" + fuser.uid + ":devices");
        provider.rclient.del("login:" + fuser.login + ":uid");
        provider.rclient.srem("uids", fuser.uid);
      };

      // unlink all devices owned by user
      provider.deviceProvider.findByUserId(fuser.uid, function(error, devices) {
        if (error) { return next(error); }

        var count = devices.length;
        if (count === 0) {
          delUser();
        }
        devices.forEach(function(device) {
          provider.deviceProvider.unlinkDevice(device.id, function(error) {
            if (error) { return next(error); }
            if (--count === 0) {
              delUser();
            }
          });
        });
      });

      next();
    });
  },

  findByUid: function(uid, next) {
    this.rclient.get("uid:" + uid + ":user", function(error, data) {
      if (error) { return next(error); }
      if (!data) { return next(); }
      next(null, new User(JSON.parse(data)));
    });
  },

  findByLogin: function(login, next) {
    var provider = this;
    provider.rclient.get("login:" + login + ":uid", function(error, uid) {
      if (error) { return next(error); }
      if (!uid) { next(); return null; }
      if (next) {
        provider.findByUid(uid, next);
      }

      return uid;
    });
  },
{'fraction_non_alphanumeric': 0.1641991924629879, 'fraction_numerical': 0.0013458950201884253, 'mean_word_length': 2.551373954599761, 'pattern_counts': {'":': 15, '<': 3, '<?xml version=': 0, '>': 3, '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': '6294883', 'n_tokens_mistral': 1893, 'n_tokens_neox': 1856, 'n_words': 413}