/**
* User data management module.
* @module ./roboCat_UserData
*/
/** required modules */
const base64url = require('base64url'); // to encode ID
const mongoose = require('mongoose'); // general user data will be stored
const redis = require('redis'); // session will be stored
const crypto = require('crypto'); // to use sha256
// Connect Mongoose according to the given env values
mongoose.connect('mongodb://' + process.env.MONGO_SERVER_IP + ':' + process.env.MONGO_SERVER_PORT + '/' + process.env.MONGO_DATADASE_NAME, {
useNewUrlParser : true,
useCreateIndex: true,
useUnifiedTopology : true,
useFindAndModify : false,
});
// Create Redis according to the given env values
let redisClient = redis.createClient(process.env.REDIS_SERVER_PORT, process.env.REDIS_SERVER_IP);
// Mongoose Schema
let userDataSchema = new mongoose.Schema({
id : String,
username : String,
password: String,
avatar: String,
token : String
});
let UserData = mongoose.model('userData', userDataSchema);
// Error check for Redis
redisClient.on("error", function (err) {
console.log("Error " + err);
});
/** functions for data management */
module.exports = {
/** Initialize any data for execute any startup-logic */
InitUserData:function() {
},
BuildToken:function(message) {
let hash = crypto.createHash('sha256');
hash.update(message);
let token = hash.digest('base64');
return token;
},
/** Create a new user on the data */
CreateNewUser:function(req, res) {
var username = req.body.username;
var password = req.body.password;
var avatar = req.body.avatar;
UserData.findOne(
{username: username},
(err, model) => {
if (err != null) {
console.log(err);
res.sendStatus(500);
return;
}
// found the same username, can't create
if(model != null) {
res.status(409).send({error: "duplicated username"});
return;
}
// the given username is unique, need to create
else {
let id = base64url(username);
UserData.findOneAndUpdate(
{username: username},
{$set :
{
password: password,
avatar: avatar,
id: id
}
},
{
new : true,
upsert : true
},
(err, model) => {
//
if (err != null) {
console.log(err);
res.sendStatus(500);
return;
}
else{
res.json({
"id": id,
"username": username,
"password": password,
"avatar": avatar
});
}
});
}
});
},
LoginUser:function(req, res) {
var username = req.body.username;
var password = req.body.password;
var query = UserData.findOne(
{username: username},
(err, model) => {
if (err != null) {
console.log(err);
res.sendStatus(500);
return;
}
// no username in the data
if(model == null) {
res.status(400).send({error: "bad username"});
return;
}
// found the username, need to check password
else {
// wrong password
if(model.password != password) {
res.status(403).send({error: "bad password"});
return;
}
// password is matched
else {
var session = base64url(username);
var date = new Date(); // to generate a random number based on the time
var token = this.BuildToken((Math.random() * date.getTime()).toString());
this.UpdateAuthentication(username, session, token);
res.json({
"session": session,
"token": token
});
}
}
});
},
/** Retrieves the specified user by ID (the generated value). */
async RetrieveUserData(req, res) {
var didSendData = false;
var redisDataBySession = undefined;
var session = req.body.session;
var token = req.body.token;
// 401: no session
if(session == undefined) {
res.status(401).send({error: "no session"});
return;
}
try {
// 401: bad session
await redisClient.hgetall(session, (err, redisData) => {
if(didSendData) return;
if (err != null) {
console.log(err);
res.sendStatus(500);
didSendData = true;
return;
}
if(redisData == null) {
res.status(401).send({error: "bad session"});
didSendData = true;
return;
}
redisDataBySession = redisData;
});
} catch(e) {
console.log('// 401: bad session: ' + e);
}
try {
// 401: bad token
await UserData.findOne({token: token}, (err, model) => {
if(didSendData) return;
if (err != null) {
console.log(err);
res.sendStatus(500);
didSendData = true;
return;
}
if(model == null) {
res.status(401).send({error: "bad token"});
didSendData = true;
return;
}
// 404: bad ID
UserData.findOne({id: req.params.id}, (err, model) => {
if(didSendData) return;
if (err != null) {
console.log(err);
res.sendStatus(500);
didSendData = true;
return;
}
if(model == null) {
res.status(404).send({error: "bad ID"});
didSendData = true;
return;
}
// session is verified, gives with password info
if(model.username == redisDataBySession.username) {
res.json({
"id": model.id,
"username": model.username,
"password": model.password,
"avatar": model.avatar,
});
}
// session is different, no password info
else {
res.json({
"id": model.id,
"username": model.username,
"avatar": model.avatar,
});
}
});
});
} catch(e) {
console.log('// 401: bad token' + e);
}
},
/** Retrieves the specified user, searching by username. */
async SearchUserData(req, res) {
var didSendData = false;
var redisDataBySession = undefined;
var session = req.body.session;
var token = req.body.token;
// 401: no session
if(session == undefined) {
res.status(401).send({error: "no session"});
return;
}
try {
// 401: bad session
await redisClient.hgetall(session, (err, redisData) => {
if(didSendData) return;
if (err != null) {
console.log(err);
res.sendStatus(500);
didSendData = true;
return;
}
if(redisData == null) {
res.status(401).send({error: "bad session"});
didSendData = true;
return;
}
redisDataBySession = redisData;
});
} catch(e) {
console.log('// 401: bad session: ' + e);
}
try {
// 401: bad token
await UserData.findOne({token: token}, (err, model) => {
if(didSendData) return;
if (err != null) {
console.log(err);
res.sendStatus(500);
didSendData = true;
return;
}
if(model == null) {
res.status(401).send({error: "bad token"});
didSendData = true;
return;
}
// 404: bad Username
UserData.findOne({username: req.query.username}, (err, model) => {
if(didSendData) return;
// 400: no Username
if(req.query.username == undefined) {
res.status(400).send({error: "no Username"});
didSendData = true;
return;
}
if (err != null) {
console.log(err);
res.sendStatus(500);
didSendData = true;
return;
}
if(model == null) {
res.status(404).send({error: "bad Username"});
didSendData = true;
return;
}
// session is verified, gives with password info
if(model.username == redisDataBySession.username) {
res.json({
"id": model.id,
"username": model.username,
"password": model.password,
"avatar": model.avatar,
});
}
// session is different, no password info
else {
res.json({
"id": model.id,
"username": model.username,
"avatar": model.avatar,
});
}
});
});
} catch(e) {
console.log('// 401: bad token :' + e);
}
},
/** Update the given user to the given info */
async UpdateUserData(req, res) {
var didSendData = false;
var redisDataBySession = undefined;
var session = req.body.session;
var token = req.body.token;
// 401: no session
if(session == undefined) {
res.status(401).send({error: "no session"});
return;
}
try {
// 401: bad session
await redisClient.hgetall(session, (err, redisData) => {
if(didSendData) return;
if (err != null) {
console.log(err);
res.sendStatus(500);
didSendData = true;
return;
}
if(redisData == null) {
res.status(401).send({error: "bad session"});
didSendData = true;
return;
}
redisDataBySession = redisData;
});
} catch(e) {
console.log('// 401: bad session: ' + e);
}
try {
// 401: bad token
await UserData.findOne({token: token}, (err, model) => {
if(didSendData) return;
if (err != null) {
console.log(err);
res.sendStatus(500);
didSendData = true;
return;
}
if(model == null) {
res.status(401).send({error: "bad token"});
didSendData = true;
return;
}
});
} catch(e) {
console.log('// 401: bad token :' + e);
}
try {
await UserData.findOne({id: req.params.id}, (err, model) => {
if(didSendData) return;
if(model == null) {
res.status(404).send({error: "No ID"});
didSendData = true;
return;
}
if(model.username != redisDataBySession.username) {
res.status(403).send({error: "Bad user"});
didSendData = true;
return;
}
UserData.findOneAndUpdate(
{id: req.params.id},
{$set :
{
username: req.body.username,
password: req.body.password,
avatar: req.body.avatar
}
},
{
new : true,
upsert : true
},
(err, model) => {
if(didSendData) return;
if (err != null) {
console.log(err);
res.sendStatus(500);
didSendData = true;
return;
}
// 404: Bad ID
if(model == null) {
res.status(404).send({error: "Bad ID"});
didSendData = true;
return;
}
// 200: Success
else {
res.json({
"id": req.params.id,
"username": req.body.username,
"password": req.body.password,
"avatar": req.body.avatar,
});
redisClient.hmset(session, {username:req.body.username});
redisClient.expire(session, process.env.EXPIRATION_DURATION);
didSendData = true;
return;
}
}
);
});
} catch(e) {
console.log('// 403: Bad user :' + e);
}
},
async ConnectToGameServer(req, res)
{
var session = req.body.session;
var token = req.body.token;
var didSendData = false;
// 401: no session
if(session == undefined) {
res.status(401).send({error: "no session"});
return;
}
try {
// 401: bad session
await redisClient.hgetall(session, (err, redisData) => {
if(didSendData) return;
if (err != null) {
console.log(err);
res.sendStatus(500);
didSendData = true;
return;
}
if(redisData == null) {
res.status(401).send({error: "bad session"});
didSendData = true;
return;
}
});
} catch(e) {
console.log('// 401: bad session: ' + e);
}
try {
// 401: bad token
await UserData.findOne({token: token}, (err, model) => {
if(didSendData) return;
if (err != null) {
console.log(err);
res.sendStatus(500);
didSendData = true;
return;
}
if(model == null) {
res.status(401).send({error: "bad token"});
didSendData = true;
return;
}
var server = process.env.GAME_SERVER;
// to make the Connect token unique and disposable
var tokenConnect = this.BuildToken(model.username + model.avatar + process.env.SHARED_SECRET);
res.json({
"id": model.id,
"username": model.username,
"avatar": model.avatar,
"server": server,
"token": tokenConnect
});
didSendData = true;
return;
});
} catch(e) {
console.log('// 401: bad token :' + e);
}
},
/** Update session and token and assign to the given user */
UpdateAuthentication:function(username, session, token) {
UserData.findOneAndUpdate(
{username: username},
{$set :
{
token: token
}
},
{
new : true,
upsert : true
},
(err, model) => {
if (err != null) {
console.log(err);
res.sendStatus(500);
return;
}
});
redisClient.hmset(session, {username:username});
redisClient.expire(session, process.env.EXPIRATION_DURATION);
},
};