/**
* User related CRUD operations
* @module UserController
* @see User
*/
const db = require("../models"),
User = db.users,
Item = db.items,
{
PUBLIC_API_KEY,
MASTER_API_KEY,
API_KEY_LOGIN_RADIUS,
API_SECRET_LOGIN_RADIUS,
SITE_NAME
} = process.env,
config = {
apiDomain: 'api.loginradius.com',
apiKey: API_KEY_LOGIN_RADIUS,
apiSecret: API_SECRET_LOGIN_RADIUS,
siteName: SITE_NAME,
apiRequestSigning: false,
proxy: {
host: '',
port: '',
user: '',
password: ''
}
},
lrv2 = require('loginradius-sdk')(config);
/**
* **Create a new User**
*
* with the auth ID token and name(optional) from the request's body
* @function create
* @param {Object} req POST request
* @param {Object} req.body request's body
* @param {Number} req.body.idToken user's auth ID token
* @param {string|undefined} [req.body.name] user's name
*
* @param {Object} res response
* @param {User} res.user created User
*/
exports.create = (req, res) => {
// Validate authentication and authorization
const apiKey = req.header('x-api-key')
if (!apiKey) {
res
.status(401)
.send({ message: "Missing authentication header" });
return
}
if (apiKey !== MASTER_API_KEY) {
res
.status(403)
.send({ message: "You have no authorization to complete this operation" });
return
}
// Validate request parameters
if (!req.body.idToken) {
res
.status(400)
.send({ message: "Request is missing required parameters" });
return;
}
// Create a User
const name = req.body.name,
token = req.body.idToken,
user = new User({
name: name,
idToken: token
})
// Save User in the database
user.save(user)
.then(data => {
res.send(data);
})
.catch(err => {
res
.status(500)
.send({ message: `Error creating User` });
});
};
/**
* **Retrieve all Users**
* @function findAll
* @param {Object} req GET request
*
* @param {Object} res response
* @param {User[]} res.data list of users
*/
exports.findAll = (req, res) => {
// Validate authentication and authorization
const apiKey = req.header('x-api-key')
if (!apiKey) {
res
.status(401)
.send({ message: "Missing authentication header" });
return;
}
if (apiKey !== MASTER_API_KEY) {
res
.status(403)
.send({ message: "You have no authorization to complete this operation" });
return
}
// Get query string from the Request and consider it as condition for findAll() method.
const title = req.query.title,
condition = title ? { title: { $regex: new RegExp(title), $options: "i" } } : {}
User.find(condition, { __v: 0 })
.then(data => {
res.send(data);
})
.catch(err => {
res
.status(500)
.send({ message: `Error retrieving all Users` });
});
};
/**
* **Delete all Users**
* @function deleteAll
* @param {Object} req DELETE request
*
* @param {Object} res response
*/
exports.deleteAll = (req, res) => {
// Validate authentication and authorization
const apiKey = req.header('x-api-key')
if (!apiKey) {
res
.status(401)
.send({ message: "Missing authentication header" });
return;
}
if (apiKey !== MASTER_API_KEY) {
res
.status(403)
.send({ message: "You have no authorization to complete this operation" });
return
}
User.deleteMany({})
.then(data => {
res.send({
message: `${data.deletedCount} Users were deleted successfully`
});
})
.catch(err => {
res
.status(500)
.send({ message: `Error deleting all Users` });
});
};
/**
* **Find User by auth ID token**
*
* with the auth ID token in the request's path
* @function findByIdToken
* @param {Object} req POST request
* @param {Object} req.params request's path parameters
* @param {Number} req.params.idToken user's auth ID token
*
* @param {Object} res response
* @param {User} res.data found user
*/
exports.findByIdToken = async(req, res) => {
// Validate authentication and authorization
const apiKey = req.header('x-api-key')
if (!apiKey) {
res
.status(401)
.send({ message: "Missing authentication header" });
return;
}
if (apiKey !== PUBLIC_API_KEY && apiKey !== MASTER_API_KEY) {
res
.status(403)
.send({ message: "You have no authorization to complete this operation" });
return
}
const token = req.params.token,
profile = await getLoginRadiusProfile(token)
if (profile) {
const uid = profile["ID"],
name = profile["FullName"];
User.findOne({ idToken: uid }, { __v: 0 })
.then(async data => {
if (!data) {
req.body.name = name;
req.body.idToken = uid;
this.create(req, res)
} else
res.send(data);
})
.catch(err => {
res
.status(404)
.send({ message: `User with ID token '${token}' was not found` });
});
} else
res
.status(500)
.send({ message: `Error retrieving User with ID token '${token}'` });
};
/**
* **Find a single User by ID**
*
* with the ID in the request's path
* @function findOne
* @param {{params: {id: string}}} req GET request
* @param {Object} req.params request's path parameters
* @param {string} req.params.id user's ID
*
* @param {Object} res response
* @param {User} res.data found user
*/
exports.findOne = (req, res) => {
// Validate authentication and authorization
const apiKey = req.header('x-api-key')
if (!apiKey) {
res
.status(401)
.send({ message: "Missing authentication header" });
return;
}
if (apiKey !== PUBLIC_API_KEY && apiKey !== MASTER_API_KEY) {
res
.status(403)
.send({ message: "You have no authorization to complete this operation" });
return
}
const id = req.params.id;
User.findById(id, { __v: 0 })
.then(data => {
if (!data)
res
.status(404)
.send({ message: `User with ID '${id}' was not found` });
else
res.send(data);
})
.catch(err => {
res
.status(500)
.send({ message: `Error retrieving User with ID '${id}'` });
});
};
/**
* **Update a User by ID**
*
* with the ID in the request's path and the user's details from the request's body
* @function update
* @param {Object} req PUT request
* @param {Object} req.params request's path parameters
* @param {string} req.params.id user's ID
* @param {Object} req.body request's body
* @param {Number|undefined} [req.body.idToken] user's auth ID token
* @param {string|undefined} [req.body.name] user's name
*
* @param {Object} res response
* @param {string} res.message message
*/
exports.update = (req, res) => {
// Validate authentication and authorization
const apiKey = req.header('x-api-key')
if (!apiKey) {
res
.status(401)
.send({ message: "Missing authentication header" });
return;
}
if (apiKey !== PUBLIC_API_KEY && apiKey !== MASTER_API_KEY) {
res
.status(403)
.send({ message: "You have no authorization to complete this operation" });
return
}
// Validate request parameters
if (!req.body) {
res.status(400).send({
message: "Request is missing required parameters"
});
return
}
const id = req.params.id;
User.findByIdAndUpdate(id, req.body, { useFindAndModify: false }, { __v: 0 })
.then(data => {
if (!data) {
res.status(404).send({
message: `Can't update User with ID '${id}'. User may not exist`
});
} else res.send({ message: "User was updated successfully" });
})
.catch(err => {
res
.status(500)
.send({ message: `Error updating User with ID '${id}'` });
});
};
/**
* **Delete a User by ID**
*
* with the ID in the request's path
* @function delete
* @param {Object} req DELETE request
* @param {Object} req.params request's path parameters
* @param {string} req.params.id user's ID
*
* @param {Object} res response
* @param {string} res.message message
*/
exports.delete = (req, res) => {
// Validate authentication and authorization
const apiKey = req.header('x-api-key')
if (!apiKey) {
res
.status(401)
.send({ message: "Missing authentication header" });
return;
}
if (apiKey !== PUBLIC_API_KEY && apiKey !== MASTER_API_KEY) {
res
.status(403)
.send({ message: "You have no authorization to complete this operation" });
return
}
const id = req.params.id;
User.findByIdAndRemove(id)
.then(data => {
if (!data) {
res.status(404).send({
message: `Can't delete User with ID '${id}'. User may not exist`
});
} else {
res.send({
message: "User was deleted successfully"
});
}
})
.catch(err => {
res.status(500).send({
message: `Error deleting User with ID '${id}'`
});
});
};
/**
* **Add an Item to a User's bucket list**
*
* with the user's auth token from the request's path
*
* and the item's name and checked status from the request's body
* @function addItemToUser
* @param {Object} req POST request
* @param {Object} req.params request's path parameters
* @param {string} req.params.user user's auth token
* @param {Object} req.body request's body
* @param {string} req.body.item item's name
* @param {boolean|undefined} [req.body.checked] item's checked status
*
* @param {Object} res response
* @param {string} res.message message
*/
exports.addItemToUser = async(req, res) => {
// Validate authentication and authorization
const apiKey = req.header('x-api-key')
if (!apiKey) {
res
.status(401)
.send({ message: "Missing authentication header" });
return;
}
if (apiKey !== PUBLIC_API_KEY && apiKey !== MASTER_API_KEY) {
res
.status(403)
.send({ message: "You have no authorization to complete this operation" });
return
}
// Validate request parameters
if (!req.body.item) {
res
.status(400)
.send({ message: "Request is missing required parameters" });
return
}
const user = req.params.user,
itemName = req.body.item,
itemChecked = req.body.checked,
profile = await getLoginRadiusProfile(user),
uid = profile["ID"]
// Verify if item already exists in DB
let item = await Item.findOne({ "name": itemName }),
userHasItem;
if (!item) {
// Item doesn't exist yet so we create it
const newItem = new Item({
name: itemName
});
await newItem.save(newItem);
// Retrieve the Item we just created to get its details
// And add it to the User's bucket list
item = await Item.findOne({ "name": itemName });
} else {
// Item already exists so we check if the User already has is on their bucket list
userHasItem = await User.findOne({ "idToken": uid, "items.item": item._id });
if (userHasItem) {
res
.status(403)
.send({
message: `User already has Item '${itemName}'`
});
}
}
// Add Item to the User's bucket list
if (!userHasItem) {
User.updateOne({ "idToken": uid }, { $push: { items: { item: item._id, checked: itemChecked } } }, { new: true, useFindAndModify: false })
.then(data => {
if (!data) {
res.status(404).send({
message: `Can't add Item '${itemName}' to the User. User may not exist`
});
} else res.send({ message: `Item '${itemName}' was added to the User successfully` });
})
.catch(err => {
res.status(500).send({
message: `Error adding Item '${itemName}' to the User`
});
});
}
};
/**
* **Update an item from a User with token**
*
* with the user's auth ID token and the item's name on the request's path
*
* and the item's checked status on the request's body
* @function updateItemFromUser
* @param {Object} req PUT request
* @param {Object} req.params request's path parameters
* @param {string} req.params.user user's auth token
* @param {string} req.params.item item's name
* @param {Object} req.body request's body
* @param {boolean} req.body.checked item's checked status
*
* @param {Object} res response
* @param {string} res.message message
*/
exports.updateItemFromUser = async(req, res) => {
// Validate authentication and authorization
const apiKey = req.header('x-api-key')
if (!apiKey) {
res
.status(401)
.send({ message: "Missing authentication header" });
return;
}
if (apiKey !== PUBLIC_API_KEY && apiKey !== MASTER_API_KEY) {
res
.status(403)
.send({ message: "You have no authorization to complete this operation" });
return
}
// Validate request parameters
if (!req.body) {
res
.status(400)
.send({ message: "Request is missing required parameters" });
return
}
const user = req.params.user,
itemName = req.params.item,
itemChecked = req.body.checked,
profile = await getLoginRadiusProfile(user),
uid = profile["ID"],
item = await Item.findOne({ "name": itemName })
if (item) {
User.updateOne({ "idToken": uid, "items.item": item._id }, { "$set": { "items.$.checked": itemChecked } })
.then(data => {
if (!data) {
res.status(404).send({
message: `Can't update Item '${itemName}' from the User. User may not exist`
});
} else res.send({ message: `Item '${itemName}' from the User was updated successfully` });
})
.catch(err => {
res.status(500)
.send({ message: `Error updating Item '${item}' from the User` });
});
}
}
/**
* **Delete an item from a User with token**
*
* with the user's auth ID token and the item's name on the request's path
* @function deleteItemFromUser
* @param {Object} req DELETE request
* @param {Object} req.params request's path parameters
* @param {string} req.params.user user's auth token
* @param {string} req.params.item item's name
*
* @param {Object} res response
* @param {string} res.message message
*/
exports.deleteItemFromUser = async(req, res) => {
// Validate authentication and authorization
const apiKey = req.header('x-api-key')
if (!apiKey) {
res
.status(401)
.send({ message: "Missing authentication header" });
return;
}
if (apiKey !== PUBLIC_API_KEY && apiKey !== MASTER_API_KEY) {
res
.status(403)
.send({ message: "You have no authorization to complete this operation" });
return
}
const user = req.params.user,
itemName = req.params.item,
profile = await getLoginRadiusProfile(user),
uid = profile["ID"],
item = await Item.findOne({ "name": itemName })
if (item) {
User.updateOne({ "idToken": uid }, { $pull: { items: { item: item._id } } })
.then(data => {
if (!data) {
res.status(404).send({
message: `Can't delete Item ${itemName} from the User. User may not exist`
});
} else res.send({ message: `Item '${itemName}' from the User was deleted successfully` });
})
.catch(err => {
res.status(500).send({
message: `Error deleting Item '${itemName}' from the User`
});
});
}
};
/**
* **Get User profile from Login Radius**
* @function getLoginRadiusProfile
* @param {string} token User's token
* @returns {Object|null} User's profile
*/
async function getLoginRadiusProfile(token) {
return await lrv2.authenticationApi.getProfileByAccessToken(token)
.then(data =>
(!data) ? null : data["Identities"][0]
)
.catch((error) => null);
}