diff --git a/burstmake.json b/burstmake.json index 8e097ef..70c2757 100644 --- a/burstmake.json +++ b/burstmake.json @@ -5,7 +5,7 @@ "moduleResolution" : "node", "target": "es5", "declaration": false, - "noImplicitAny": true, + "noImplicitAny": false, "noEmitOnError": true, "sourceMap": true }, diff --git a/package.json b/package.json index 3ea4baa..b87b775 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,11 @@ "license": "MIT", "dependencies": { "express": "^4.13.1", - "mongoose": "^4.4.14" + "mongoose": "^4.4.14", + "promise": "^7.1.1", + "body-parser": "^1.15.1", + "helmet": "^2.0.0" + }, "devDependencies": { "apidoc": "^0.15.1", @@ -51,8 +55,8 @@ "mocha": "^2.4.5", "rimraf": "^2.5.2", "tslint": "^3.5.0", - "typedoc": "^0.3.12", + "typedoc": "latest", "typescript": "^1.8.7", - "typings": "^0.7.9" + "typings": "latest" } } diff --git a/src/apidoc.json b/src/apidoc.json index 62707a6..34270c2 100644 --- a/src/apidoc.json +++ b/src/apidoc.json @@ -1,7 +1,7 @@ { - "name": "example", + "name": "MaaS", "version": "0.1.0", - "description": "apiDoc basic example", - "title": "Custom apiDoc browser title", - "url" : "https://api.github.com/v1" + "description": "API for MaaS", + "title": "MaaS: MongoDB as a Service API documentation", + "url" : "http://bugbusterswe.github.io/MaaS/" } diff --git a/src/config/devConfiguration.ts b/src/config/devConfiguration.ts index 18ae2eb..0fe0bdd 100644 --- a/src/config/devConfiguration.ts +++ b/src/config/devConfiguration.ts @@ -1,7 +1,7 @@ import Configuration from "./configuration"; import MongoConnection from "./mongoConnection"; /** - * @description Development configuration. + * Development configuration. * @history * | Author | Action Performed | Data | * | --- | --- | --- | diff --git a/src/config/index.ts b/src/config/index.ts index 5b914cf..843f05d 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -3,9 +3,10 @@ import Configuration from "./configuration"; import DevConfiguration from "./devConfiguration"; import TestConfiguration from "./testConfiguration"; import ProdConfiguration from "./prodConfiguration"; +import {readFileSync} from "fs"; /** - * @description Class used for get the necessary configuration. + * Class used for get the necessary configuration. * @history * | Author | Action Performed | Data | * | --- | --- | --- | @@ -20,16 +21,21 @@ class ChooseConfiguration { * @description Return the right configuration according to the Node.js * environment variable. It may be: 'development', 'test' or 'production'. * The default configuration is the 'production' one. + * The connection's parameters are read fro an external json file named + * mongoParameters.json. * @returns {Configuration} The configuration. */ public static getConfig() : Configuration { - /** @todo parameters */ + let params : MongoConnection = JSON.parse(readFileSync( + "src/config/mongoParameters.json", + "utf-8" + )); let connection : MongoConnection = new MongoConnection( - "admin", - "admin", - "ds013250.mlab.com", - 13250, - "mongocbtest" + params["user"], + params["password"], + params["host"], + params["port"], + params["dbName"] ); let serverSecret : string = "serverSecret"; let config : Configuration; diff --git a/src/config/mongoConnection.ts b/src/config/mongoConnection.ts index 8a2508c..ec9a0d0 100644 --- a/src/config/mongoConnection.ts +++ b/src/config/mongoConnection.ts @@ -1,5 +1,5 @@ /** - * @description This class stores the parameters for the MondoDB connection. + * This class stores the parameters for the MondoDB connection. * @history * | Author | Action Performed | Data | * | --- | --- | --- | diff --git a/src/config/mongoParameters.json b/src/config/mongoParameters.json new file mode 100644 index 0000000..e025176 --- /dev/null +++ b/src/config/mongoParameters.json @@ -0,0 +1,7 @@ +{ + "user" : "admin", + "password" : "admin", + "host" : "ds013250.mlab.com", + "port" : 13250, + "dbName" : "mongocbtest" +} diff --git a/src/config/prodConfiguration.ts b/src/config/prodConfiguration.ts index c1fb907..afe23dd 100644 --- a/src/config/prodConfiguration.ts +++ b/src/config/prodConfiguration.ts @@ -2,7 +2,7 @@ import Configuration from "./configuration"; import MongoConnection from "./mongoConnection"; /** - * @description Production configuration. + * Production configuration. * @history * | Author | Action Performed | Data | * | --- | --- | --- | diff --git a/src/lib/authenticationChecker.ts b/src/lib/authenticationChecker.ts new file mode 100644 index 0000000..f410e5d --- /dev/null +++ b/src/lib/authenticationChecker.ts @@ -0,0 +1,169 @@ +import * as jwt from "jsonwebtoken"; +import UserModel from "../models/userModel"; +import UserDocument from "../models/userModel"; +import * as express from "express"; +import ConfigurationChooser from "../config/index"; +import * as mongoose from "mongoose"; + +/** + * This class is used to check if the current user is correctly authenticate + * to the MaaS application. + * + * @history + * | Author | Action Performed | Data | + * | --- | --- | --- | + * | Luca Bianco | Create class | 07/05/2016 | + * + * @author Luca Bianco + * @license MIT + */ + +class AuthenticationChecker { + + /** + * @description Server's secret string, used for encode the JWT tokens. + */ + private secret : string; + + /** + * @description Request's expire time. By default it is 60*24*7. + */ + private DEFAULT_EXPIRE_TIME : number = 60 * 24 * 7; + + /** + * @description Default name of the 'username' field in every request. + */ + private USERNAME_BODY_FIELD : string = "username"; + + /** + * @description Default name of the 'password' field in every request. + */ + private PASSWORD_BODY_FIELD : string = "password"; + + /** + * @description Login the user. + * @param request The express request. + * See the official + * documentation for more details. + * @param response The express response object. + * See the official + * documentation for more details. + */ + public login(request : express.Request, + response : express.Response) : void { + let username : string = request.body[this.USERNAME_BODY_FIELD]; + let password : string = request.body[this.PASSWORD_BODY_FIELD]; + + // TODO: sistemare inclusione del modello utente + let userModel : UserModel = new UserModel(); + userModel + .login(username, password) // Call the login method... + .then(function (user : UserDocument) : + void { // ...when done, let's say it to the client + if (!user) { + this.loginFailed(response); + } else { + let userToken : string = this.createToken(user); + response.status(200); + response.json({ + done: true, + message: "Authentication done", + token: userToken, + user_id: user["_id"] + }); + } + }) + } + + /** + * @description Authenticate the user. + * @param request The express request. + * See the official + * documentation for more details. + * @param response The express response object. + * See the official + * documentation for more details. + * @param next Function which invokes the next route handler in framework. + */ + public authenticate( + request : express.Request, + response : express.Response, + next : express.NextFunction) : void { + let token : string = request.body.token || + request.query.token || + request.headers["x-access-token"]; + + if (!token) { // Token not found + this.responseTokenNotFound(response); + } else { + jwt.verify(token, this.secret, + function (err : Error, decoded : Object) : void { + if (err) { // Authentication failed + this.responseAuthenticationFailed(response); + } else { // Success! + request.user = decoded; + next(); + } + }); + } + } + + /** + * @description Create the JWT token for the current user. + * @param data User's data. + * @returns {string} A string which represents the JWT token created. + */ + private createToken(data : Object) : string { + return jwt.sign( + { + data: data, + expireTime: this.DEFAULT_EXPIRE_TIME + }, + ConfigurationChooser.getConfig().getServerSecret() + ); + } + + /** + * @descripton + * Create a parametrized response for the token not found situation. + * @param response The generated response with an error message which + * represents the "token not found" situation. + */ + private responseTokenNotFound(response : express.Response) : void { + response.status(403); + response.json({ + done: false, + message: "Authentication failed. No Token Found" + }); + } + + /** + * @description + * Create a parametrized response for the authentication failed situation. + * @param response The generated response with an error message which + * represents the "authentication failed" situation. + */ + private responseAuthenticationFailed(response : express.Response) : void { + response.status(403); + response.json({ + done: false, + message: "Authentication failed. Token invalid" + }); + } + + /** + * @description + * Create a parametrized response for the login failed situation. + * @param response The generated response with an error message which + * represents the "login failed" situation. + */ + private loginFailed(response : express.Response) : void { + response.status(401); + response.json({ + done: false, + message: "Login Failed" + }); + } +} + +export default AuthenticationChecker; diff --git a/src/lib/dslChecker.ts b/src/lib/dslChecker.ts new file mode 100644 index 0000000..27dac24 --- /dev/null +++ b/src/lib/dslChecker.ts @@ -0,0 +1,384 @@ +import Map from "./map" + +/** + * Class to check if a json data has been structure as a DSLStructure. + * @history + * | Author | Action Performed | Data | + * | --- | --- | --- | + * | Andrea Mantovani | Completed class DSLChecker | 07/05/2016 | + * | Andrea Mantovani | Create class | 06/05/2016 | + * + * @author Andrea Mantovani + * @license MIT + */ +class DSLChecker { + private flowControl : Map<(struct : Object, id : string) => boolean>; + + /** + * @description + * Default constructor. + * @return {DSLChecker} + * This. + */ + constructor() { + this.flowControl = {}; // Initialize + + this.flowControl["cell"] = ( + struct : Object, + id : string + ) : boolean => { + return this.checkLeaf(struct, id); + }; + + this.flowControl["collection"] = ( + struct : Object, + id : string + ) : boolean => { + return this.checkCollection(struct, id); + }; + + this.flowControl["dashboard"] = ( + struct : Object, + id : string + ) : boolean => { + return this.checkDashboard(struct, id); + }; + + this.flowControl["document"] = ( + struct : Object, + id : string + ) : boolean => { + return this.checkDocument(struct, id); + }; + + this.flowControl["index"] = ( + struct : Object, + id : string + ) : boolean => { + return this.checkIndex(struct, id); + }; + + this.flowControl["leaf"] = ( + struct : Object, + id : string + ) : boolean => { + return this.checkLeaf(struct, id); + }; + } + + /** + * @description + * Check if data represents a DSLStructure. The data use the editor to be + * create. Therefore, is not necessary all control. + * @param data {Object} + * JSON valid data. + * @return {boolean} + * True if data is a savable structure into DB, false otherwise. + * @throws {Error} + */ + public check(data : Object) : boolean { + if (data === undefined) { + throw new Error("The param 'data' is undefined"); + } + + let root : string = data["root"]; + + // No root found + if (root === undefined) { + return false; + } + + let struct : Object = data[root]; + + if (struct === undefined) { + throw new Error("Unexpected finish of structure"); + } + + let type : string = struct["type"]; + + if (type === undefined) { + throw new Error( + `The attribute 'type' of the element ${root} is not defined` + ); + } + + let follow : (struct : Object, id : string) => boolean + = this.flowControl[type]; + + if (follow === undefined) { + throw new Error( + `The value ${type} as 'type', in the elemen ${root}, no ` + + `math with anything expected` + ); + } + + // Catch all exception throw from the innested calls. + try { + return follow(data, root); + + } catch (err) { + throw err; + } + } + + /** + * @description + * Return true if the collection created it is right. + * @param data {Object} + * JSON valid data + * @param id {string} + * Id of the collection structure into *data* + * @return {boolean} + * True if be start here is possible reach a leaf structure. + * @throws {Error} + */ + private checkCollection(data : Object, id : string) : boolean { + // No collection found + if (data[id] === undefined) { + return false; + } + + // Extract the model of the collection + let collection : Object = this.extractModel(data, id); + let actions : Array = collection["action"]; + let index : string = collection["index"]; + let document : string = collection["document"]; + + if (actions === undefined) { + throw new Error( + `The collection ${id} don't have a setted action attribute` + ); + } + + if (index === undefined) { + throw new Error( + `The collection ${id} don't have a setted index attribute` + ); + } + + return ( + this.checkLeafs(data, actions) && + this.flowControl["index"](data, index) && + this.flowControl["document"](data, document) + ); + } + + /** + * @description + * Return true if the dashboard created it is right. + * @param data {Object} + * JSON valid data + * @param id {string} + * Id of the dashboard structure into *data* + * @return {boolean} + * True if be start here is possible reach a leaf structure. + * @throws {Error} + */ + private checkDashboard(data : Object, id : string) : boolean { + // No collection found + if (data[id] === undefined) { + return false; + } + + // Extract the model of the dashboard + let dashboard : Object = this.extractModel(data, id); + let rows : Array = dashboard["rows"]; + + if (rows === undefined) { + throw new Error( + `The dashboard ${id} don't have a setted rows attribute` + ); + } + + // Is similar at what to happen in the function checkLeafs + let correct : boolean = true; + + rows.forEach((row : string) => { + // Get type for the current row + let type : string = this.extractType(data, row); + correct = correct && this.flowControl[type](data, row); + }); + + return correct; + } + + /** + * @description + * Return true if the Document created it is right. + * @param data {Object} + * JSON valid data + * @param id {string} + * Id of the document structure into *data* + * @return {boolean} + * True if be start here is possible reach a leaf structure. + * @throws {Error} + */ + private checkDocument(data : Object, id : string) : boolean { + // No collection found + if (data[id] === undefined) { + return false; + } + + // Extract the model of Document + let document : Object = this.extractModel(data, id); + let actions : Array = document["action"]; + let rows : Array = document["row"]; + + if (actions === undefined) { + throw new Error( + `The document ${id} don't have a setted action attribute` + ); + } + + if (rows === undefined) { + throw new Error( + `The document ${id} don't have a setted row attribute` + ); + } + + return (this.checkLeafs(data, actions) && this.checkLeafs(data, rows)); + } + + /** + * @description + * Return true if the Index created it is right. + * @param data {Object} + * JSON valid data + * @param id {string} + * Id of the index structure into *data* + * @return {boolean} + * True if be start here is possible reach a leaf structure. + * @throws {Error} + */ + private checkIndex(data : Object, id : string) : boolean { + // No collection found + if (data[id] === undefined) { + return false; + } + + // Extract the model of Index + let index : Object = this.extractModel(data, id); + let columns : Array = index["column"]; + + if (columns === undefined) { + throw new Error( + `The index ${id} don't have a setted column attribute` + ); + } + + return this.checkLeafs(data, columns); + } + + /** + * @description + * Return true if the general leaf created it is right. + * @param data {Object} + * JSON valid data + * @param id {string} + * Id of the leaf structure into *data* + * @return {boolean} + * True if is a really leaf, false otherwise. + * @throws {Error} + */ + private checkLeaf(data : Object, id : string) : boolean { + // No collection found + if (data[id] === undefined) { + return false; + } + + /* + Common model for any leaf. If no exception is throw, the leaf will + be correct. + */ + this.extractModel(data, id); + return true; + } + + /** + * @description + * Call the *flowControl* for the case 'leaf' for each leafs. + * @param data {Object} + * JSON valid data + * @param ids {Array} + * Ids of each leafs + * @return {boolean} + * True if each calls at the *flowControl* returns true. + * @throws {Error} + */ + private checkLeafs( + data : Object, + ids : Array + ) : boolean { + let correct : boolean = true; + + ids.forEach((id : string) => { + /* + Follow the control. If each calls at the flowControl returns true, + the structure will be correct. This because, also if only once + call of flowControl returns false, the operator && will keep + variable correct to the false. + */ + correct = correct && this.flowControl["leaf"](data, id); + }); + + return correct; + } + + /** + * @description + * Get the pModel inside any structure. + * @param data {Object} + * JSON valid data + * @param id {string} + * Id of the structure into *data* + * @return {Object} + * The content of **pModel** attribute. + * @throws {Error} + */ + private extractModel(data : Object, id : string) : Object { + let model : Object = data[id]["pModel"]; + + if (model === undefined) { + throw new Error( + `The pModel for the structure ${id} is expected` + ); + } + + return model; + } + + /** + * @description + * Extract type from object received with the id passed by argument. + * @param data {Object} + * JSON valid data + * @param id {string} + * Key to access to the content + * @return {string} + * The type of object + * @throws {Error} + */ + private extractType(data : Object, id : string) : string { + let struct : Object = data[id]; + + if (struct === undefined) { + throw new Error( + "Unexpected finish of structure" + ) + + } + + let type : string = struct["type"]; + + if (type === undefined) { + throw new Error( + `The attribute 'type' of the element ${id} ` + + "is not defined" + ) + } + + return type; + } +} + +export default DSLChecker; diff --git a/src/lib/levelChecker.ts b/src/lib/levelChecker.ts new file mode 100644 index 0000000..5492d29 --- /dev/null +++ b/src/lib/levelChecker.ts @@ -0,0 +1,99 @@ +import * as express from "express"; +/** + * This class checks the level of the user from the request + * + * @history + * | Author | Action Performed | Data | + * | --- | --- | --- | + * | Luca Bianco | Create class | 07/05/2016 | + * + * @author Luca Bianco + * @license MIT + * + */ + +class LevelChecker { + + /** + * @description Array which contains all the possible users' levels. + */ + private levelsAllowed : Array; + + /** + * @description Complete constructor. + * @param levelsAllowed An array which contains all the possible users' + * levels. + */ + constructor (levelsAllowed : Array) { + this.levelsAllowed = levelsAllowed; + } + + /** + * @description Method to check the level of the user. It allows to + * check if the current user is allowed to do the invoked operation. + * @param request The express request. + * See the official + * documentation for more details. + * @param response The express response object. + * See the official + * documentation for more details. + * @param next Function which invokes the next route handler in framework. + */ + public check( + request : express.Request, + response : express.Response, + next : express.NextFunction) : void { + + let user : Object = request.user || undefined; + + if ( !user ) { // There's no user to check + this.accessDenied(response); + } else { + if ( this.levelsAllowed.indexOf(user["level"]) ) { + // Level is inside of allowed so go to next middleware + next(); + } else { + this.accessDenied(response); + } + } + } + + /** + * @description This is a middleware to check the level of an user. This + * check is skipped if the id of the user is the same that is defined on the + * request. + * @param request The express request. + * See the official + * documentation for more details. + * @param response The express response object. + * See the official + * documentation for more details. + * @param next Function which invokes the next route handler in framework. + */ + public checkWithIDSkip( + request : express.Request, + response : express.Response, + next : express.NextFunction) : void { + if (request.params.user_id == request.user._id ) { + next(); + } else { + this.check(request, response, next); + } + } + + /** + * @description + * Create a parametrized response for the access denied situation. + * @param response The generated response with an error message which + * represents the "access denied" situation. + */ + private accessDenied(response : express.Response) : void { + response.status(400); + response.json({ + done: false, + message: "Unauthorized" + }); + } +} + +export default LevelChecker; diff --git a/src/lib/map.ts b/src/lib/map.ts new file mode 100644 index 0000000..267d379 --- /dev/null +++ b/src/lib/map.ts @@ -0,0 +1,15 @@ +/** + * Interface to expose the utility of map, it associates the key with the value. + * @history + * | Author | Action Performed | Data | + * | --- | --- | --- | + * | Andrea Mantovani | Create interface | 06/05/2016 | + * + * @author Andrea Mantovani + * @license MIT + */ +interface Map { + [k : string] : value; +} + +export default Map; diff --git a/src/maas.ts b/src/maas.ts index bb54bce..7b8d2ee 100644 --- a/src/maas.ts +++ b/src/maas.ts @@ -1,13 +1,37 @@ -/** - * Created by davide on 26/03/16. - */ - -import * as test from "./main"; import * as express from "express"; -/*import {Express} from "express";*/ - +import * as bodyParser from "body-parser"; +import * as http from "http"; +import * as helmet from "helmet"; +import ConfigurationChooser from "./config/index"; +import Configuration from "./config/configuration"; +import * as routes from "./routes/routerFacade"; +// Initializing app let app : express.Express = express(); +let configuration : Configuration = ConfigurationChooser.getConfig(); + +// Allow to get data from body in JSON format +app.use(bodyParser.urlencoded({extended: true})); +app.use(bodyParser.json()); + +// Set helmet for security checks +app.use(helmet()); + +app.use(function (req : express.Request, + res : express.Response, + next : express.NextFunction) : void { + res.header("Access-Control-Allow-Origin", "*"); + res.header("Access-Control-Allow-Headers", + "Origin, X-Requested-With, Content-Type, Accept"); + next(); +}); +// Routes' require +/* app.use("/api", routes); */ -test.HelloWorld("test"); +// Starting the server +app.set("port", process.env.PORT || 3000); +let server : http.Server = app.listen(app.get("port"), function () : void { + console.log("Express server is listening on port " + server.address().port + + " in " + configuration.getEnvName() + " environment."); +}); diff --git a/src/models/companyModel.ts b/src/models/companyModel.ts new file mode 100644 index 0000000..0b86140 --- /dev/null +++ b/src/models/companyModel.ts @@ -0,0 +1,68 @@ +import * as mongoose from "mongoose"; +import Model from "./model"; + +/** + * CompanyDocument defines an interface which stores the Company attributes. + * @history + * | Author | Action Performed | Data | + * | --- | --- | --- | + * | Andrea Mantovani | Insert comment above properties | 12/05/2016 | + * | Davide Rigoni | Create class | 10/05/2016 | + * + * @author Davide Rigoni + * @license MIT + */ +export interface CompanyDocument extends mongoose.Document { + /** + * @description Company's name + */ + name : string, + /** + * @description Id of company's owner + */ + idOwner : string; +} + +/** + * CompanyModel implements the company business logic. It contains model and + * schema defined by MongooseJS. + * + * @history + * | Author | Action Performed | Data | + * | --- | --- | --- | + * | Davide Rigoni | Create class | 03/05/2016 | + * + * @author Davide Rigoni + * @license MIT + * + */ +class CompanyModel extends Model { + /** + * @description Default constructor. + */ + constructor() { + super(); + } + + /** + * @description Get the schema of the company model. + * @returns {"mongoose".Schema} The schema of the Company. + * @override + */ + protected getSchema() : mongoose.Schema { + return new mongoose.Schema({name: String, idOwner: String}); + } + + /** + * @description + * Get the model of the company model instantiated by Company's schema + * @returns {Model} + * The model of the Company + * @override + */ + protected getModel() : mongoose.Model { + return mongoose.model("Company", this.getSchema()); + } +} + +export default CompanyModel; diff --git a/src/models/customModelInterface.ts b/src/models/customModelInterface.ts new file mode 100644 index 0000000..ffeabf2 --- /dev/null +++ b/src/models/customModelInterface.ts @@ -0,0 +1,16 @@ +import * as mongoose from "mongoose"; +/** + * This is the base models class and contains some useful methods to perform + * basic operations with MongoDB. + * @history + * | Author | Action Performed | Data | + * | --- | --- | --- | + * | Andrea Mantovani | Use export default | 12/05/2016 | + * | Luca Bianco | Created interface | 10/05/2016 | + * + * @author Luca Bianco + * @license MIT + */ +interface CustomModel extends mongoose.Document {} + +export default CustomModel; diff --git a/src/models/databaseModel.ts b/src/models/databaseModel.ts new file mode 100644 index 0000000..04cf780 --- /dev/null +++ b/src/models/databaseModel.ts @@ -0,0 +1,252 @@ +import * as mongoose from "mongoose"; +import Model from "./model"; +import CustomModel from "./customModelInterface"; + +/** + * DatabaseModel is a interface that represent the document on MongoDB. + * + * @history + * | Author | Action Performed | Data | + * | --- | --- | --- | + * | Davide Polonio | Create interface | 09/05/2016 | + * + * @author Davide Polonio + * @copyright MIT + */ +export interface DatabaseDocument extends CustomModel { + /** + * @description Represent the company name. + */ + name : string, + /** + * @description Represent the owner id ( the Company that owns the db). + */ + idOwner : string, + /** + * @description Represent the username to access to the database. + */ + username : string, + /** + * @description Represent the password to access to the database. + */ + password : string, + /** + * @description Represent the address host where is install the database. + */ + host : string, + /** + * @description Represent the name of database. + */ + dbName : string +} + + +/** + * DatabaseModel manage all connections to MongoDB companies databases. + * Implements model and schema of MongooseJS. + * + * This model represent a connection to a company. + * + * + * @history + * | Author | Action Performed | Data | + * | --- | --- | --- | + * | Davide Polonio | Create class | 06/05/2016 | + * + * @author Davide Polonio + * @copyright MIT + */ +class DatabaseModel extends Model { + /** + * @description + *

This constructor calls his super constructor. After it gets the + * database connection and puts it in model.

+ * @return {DatabaseModel} + * This. + */ + constructor() { + super(); + } + + /** + * @description Gets All the databases for a company + * @param company_id + * @returns {Promise|Promise} + */ + public getAllForCompany(company_id : string) : Promise { + return new Promise((resolve : (data : Object) => void, + reject : (error : Object) => void) => { + this.model.find({idOwner : company_id}, + (error : Object, data : Object) => { + if (error) { + reject(error); + } else { + resolve(data); + } + }) + }) + } + + /** + * @description Create a new database for the stated Company. + * @param jsonData data of the new database. + * @returns {Promise} + * Promise with the error or the saved data. + * @override + */ + public create(jsonData : Object) : Promise { + let self : DatabaseModel = this; + + return new Promise((resolve : (data : Object) => void, + reject : (error : Object) => void) => { + this + .getCollections( + jsonData["port"], + jsonData["host"], + jsonData["username"], + jsonData["password"], + jsonData["dbName"] + ) + .then(function (collections : Array) : void { + jsonData["collections"] = collections; + self + .superCreate(jsonData) + .then((data : Object) => { + resolve(data); + }, + (error : Object) => { + reject(error); + }); + }); + }); + + + } + + /** + * @description Update the stated Company databases. + * @param _id The id of the Company. + * @param jsonData Data of the new database. + * @returns {Promise} + * Promise with the error or the saved data. + */ + /* Luca Bianco: ho settato in Object il tipo di promise per rimanere + nella stessa linea di derivazione di model */ + public update(_id : string, jsonData : Object) : Promise { + let self : DatabaseModel = this; + + return new Promise((resolve : (data : Object) => void, + reject : (error : Object) => void) => { + + this + .getCollections( + jsonData["port"], + jsonData["host"], + jsonData["username"], + jsonData["password"], + jsonData["dbName"] + ) + .then(function (collections : Array) : void { + jsonData["collections"] = collections; + self.superUpdate(_id, jsonData) + .then((data : Object) => { + resolve(data); + }, (error : Object) => { + reject(error); + }); + }) + }); + } + + /** + * @description Get the database's schema. + * @returns {"mongoose".Schema} The schema. + * @override + */ + protected getSchema() : mongoose.Schema { + return new mongoose.Schema({ + name: String, + idOwner: String, + idDatabase: String, + collections: [String] + }); + } + + /** + * @description Return the database's model. + * @returns {Model} The model. + * @override + */ + protected getModel() : mongoose.Model { + return mongoose.model("Database", this.getSchema()); + } + + /** + * @description Return the collection's list of the stated database. + * @param port Database's port. + * @param host Database's host. + * @param username Database's user username. + * @param password Database's user password. + * @param dbName Database's name. + * @returns {Promise|Promise} Promise + * generated by a mongoose's query. + */ + private getCollections(port : string, host : string, + username : string, password : string, + dbName : string) : Promise { + + // Connect to the database: + // Create the string + let connectionString : string = + "mongodb://" + username + + ":" + password + + "@" + host + + ":" + port + + "/" + dbName; + + // And use it to connect + let mongooseTemporaryConnection : mongoose.Connection = + mongoose.createConnection(connectionString); + return new Promise((resolve : (data : Object) => void, + reject : (error : Object) => void) => { + + // Get connections' names + mongooseTemporaryConnection.db.collectionNames( + function (err : Object, names : Array) : void { + if (err) { + reject(err); + } else { + resolve(names); + } + }); + }); + } + + /** + * @description + *

Method used for invoke the base class create method from a callback + * function.

+ * @param jsonData Data of the database. + * @returns {Promise} + * Promise with the error or the saved data + */ + private superCreate(jsonData : Object) : Promise { + return super.create(jsonData) + } + + /** + * @description + *

Method used for invoke the base class update method from a callback + * function.

+ * @param _id The id of the company + * @param jsonData Data of the database. + * @returns {Promise} + * Promise with the error or the saved data + */ + private superUpdate(_id : string, + jsonData : Object) : Promise { + return super.update(_id, jsonData); + } +} + +export default DatabaseModel; diff --git a/src/models/dslModel.ts b/src/models/dslModel.ts new file mode 100644 index 0000000..e796ec3 --- /dev/null +++ b/src/models/dslModel.ts @@ -0,0 +1,159 @@ +import * as mongoose from "mongoose"; +import Model from "./model"; + +type DSLSchema = { + permission : [{user : String, read : Boolean, exec : Boolean}], + content : String +}; + +/** + * Define the form of a permission of a user on the DSL. + * + * @history + * | Author | Action Performed | Data | + * | --- | --- | --- | + * | Andrea Mantovani | Create interface | 07/05/2016 | + * + * @author Andrea Mantovani + * @license MIT + */ +export interface PermissionOnDSL { + /** + * @description + * User's ID + */ + user : string; + + /** + * @description + * Set the permission of read a DSL + */ + read : boolean; + + /** + * @description + * Set the permission of execute a DSL + */ + exec : boolean; +} + +/** + * Define the attribute of a dsl's document. + * + * @history + * | Author | Action Performed | Data | + * | --- | --- | --- | + * | Andrea Mantovani | Create interface | 08/05/2016 | + * + * @author Andrea Mantovani + * @license MIT + */ +export interface DSLDocument extends mongoose.Document { + /** + * @description + * Register of users with access to the DSL + */ + permission : Array, + /** + * @description + * Code of the DSL + */ + content : string; + + /** + * @description + * Id of the company that owns the DSL + */ + company_id : string; + + /** + * @description + * Boolean value to check if it's a dashboard + */ + isDashobard : boolean; +} + +/** + * DSLModel implements the dsl business logic. It contains model and scheme + * defined by MongooseJS. + * @history + * + * + * + * + * + * + * + * + * + *
AuthorAction PerformedData
Andrea Mantovani + * Add methods: + *
    + *
  • *get*
  • + *
  • *getAll*
  • + *
  • *add*
  • + *
  • *update*
  • + *
  • *delete*
  • + *
+ *
08/05/2016
Andrea MantovaniCreate class07/05/2016
+ * + * @author Andrea Mantovani + * @license MIT + */ +class DSLModel extends Model { + + /** + * Default constructor. + * @return {DSLModel} + * This. + */ + constructor() { + super(); + } + + /** + * @description Gets all the DSLs for a company + * @param company_id + * @returns {Promise|Promise} + */ + public getAllForCompany(company_id : string) : Promise { + return new Promise((resolve : (data : Object) => void, + reject : (error : Object) => void) => { + this.model.find({company_id: company_id}, + (error : Object, data : Object) => { + if (error) { + reject(error); + } else { + resolve(data); + } + }) + }); + } + + /** + * @description Get the dsl's schema. + * @returns {"mongoose".Schema} The schema. + * @override + */ + protected getSchema() : mongoose.Schema { + return new mongoose.Schema({ + permission: [{user: String, read: Boolean, exec: Boolean}], + content: String, + company_id: mongoose.Schema.Types.ObjectId, + isDashboard: Boolean + }); + } + + /** + * @description Get the dsl's model. + * @returns {"mongoose".Schema} The model. + * @override + */ + protected getModel() : mongoose.Model { + return mongoose.model("DSL", this.getSchema()); + } + + +} + +export default DSLModel; diff --git a/src/models/model.ts b/src/models/model.ts index 24ac96e..d00f466 100644 --- a/src/models/model.ts +++ b/src/models/model.ts @@ -1,7 +1,30 @@ import MongooseConnection from "./mongooseConnection"; -import MongoConnection from "../config/mongoConnection"; import * as mongoose from "mongoose"; -import {connection} from "mongoose"; +import CustomModel from "./customModelInterface"; + +/** + * Result document after any update. For more information about the meaning of + * any attributes refer at the official documentation: + * [Update#Output - MongoDB](http://bit.ly/1Ygx5UW) + */ +export type MongoDBUpdate = { + ok : Number, + n : Number, + nModified : Number, + upserted : [{ + index : Number, + _id : mongoose.Types.ObjectId + }], + writeErrors : [{ + index : Number, + code : Number, + errmsg : String + }], + writeConcernError : [{ + code : Number, + errmsg : String + }] +}; /** * This is the base models class and contains some useful methods to perform @@ -10,21 +33,34 @@ import {connection} from "mongoose"; * | Author | Action Performed | Data | * | --- | --- | --- | * | Matteo Di Pirro | Create class | 04/05/2016 | + * | Luca Bianco | Fixed methods | 10/05/2016 | * * @author Matteo Di Pirro * @license MIT */ abstract class Model { + /** + * @description Model representing the data in mongoose + */ + protected model : mongoose.Model; + /** * @description Mongoose connection */ private connection : MongooseConnection; + /** + * @description Schema of the model + */ + private schema : mongoose.Schema; + /** * @description Complete constructor. */ - constructor () { + constructor() { this.connection = MongooseConnection.getInstance(); + this.schema = this.getSchema(); + this.model = this.getModel(); } /** @@ -34,6 +70,136 @@ abstract class Model { public getConnection() : MongooseConnection { return this.connection; } + + /** + * @description Creates a new document into the database + * @param jsonData{Object} + * The object that contains the information to create the object + * @returns {Promise} + * Promise with the error or the saved data. + */ + public create(jsonData : Object) : Promise { + let concreteModel : CustomModel = new this.model(jsonData); + return new Promise((resolve : (data : Object) => void, + reject : (error : Object) => void) => { + concreteModel.save((error : Object, data : Object) => { + if (error) { + reject(error); + } else { + resolve(data); + } + }); + }); + } + + /** + * @description + * Update the document specified by id with the data, in json format, + * passed. + * @param _id {string} Id of the document + * @param jsonData {Object} + * The content of data. The classes derived from *Model* apply the override + * specify the structure of *jsonData* + * @returns {Promise} + * + */ + public update(_id : string, jsonData : Object) : Promise { + return new Promise(( + resolve : (data : MongoDBUpdate) => void, + reject : (error : Object) => void + ) => { + this.model.findOneAndUpdate( + {_id: _id}, + { + $set: jsonData, + $inc: {__v: 1} + }, + (error : Object, data : MongoDBUpdate) => { + if (error) { + reject(error); + } else { + resolve(data); + } + }); + }); + } + + /** + * @description Remove the document specified by the id + * @param _id Remove the element with the stated _id. + * @returns {Promise|Promise} Promise generated by the + * mongoose's query. + */ + public remove(_id : string) : Promise { + return new Promise((resolve : (data : Object) => void, + reject : (error : Object) => void) => { + this.model.findOneAndRemove( + {_id: _id}, + (error : Object, data : Object) => { + if (error) { + reject(error); + } else { + resolve(data); + } + }); + }); + } + + /** + * @description Get the document specified by the id. + * + * @param _id id of the document to return. + * + * @returns {Promise|Promise} Promise generated by the + * mongoose's query. + */ + public getOne(_id : string) : Promise { + return new Promise((resolve : (data : Object) => void, + reject : (error : Object) => void) => { + this.model.findOne({_id: _id}, + (error : Object, data : Object) => { + if (error) { + reject(error); + } else { + resolve(data); + } + }); + }) + } + + /** + * @description Get all the documents for the model of the data + * @returns {Promise|Promise} Promise generated by the + * mongoose's query. + */ + public getAll() : Promise { + return new Promise((resolve : (data : Object) => void, + reject : (error : Object) => void) => { + this.model.find({}, + (error : Object, data : Object) => { + if (error) { + reject(error); + } else { + resolve(data); + } + }) + }) + } + + /** + * @description Builds the schema of data using mongoose Schema. + * @returns {mongoose.Schema} The built schema. + */ + protected abstract getSchema() : mongoose.Schema; + + /** + * @description Returns the mongoose model representing the data into the + * database. + * @returns {mongoose.Model} the model to use to manage data into the + * database. + */ + protected abstract getModel() : mongoose.Model; + } export default Model; diff --git a/src/models/mongooseConnection.ts b/src/models/mongooseConnection.ts index 86fd35e..f408ca9 100644 --- a/src/models/mongooseConnection.ts +++ b/src/models/mongooseConnection.ts @@ -2,7 +2,7 @@ * This is a Singleton class used for perform a connection with a MongoDB * database. * - * * @history + * @history * | Author | Action Performed | Data | * | --- | --- | --- | * | Matteo Di Pirro | Create class | 04/05/2016 | @@ -16,7 +16,17 @@ import MongoConnection from "../config/mongoConnection"; import ConfigurationChooser from "../config/index"; import Configuration from "../config/configuration"; -export class MongooseConnection { +/** + * This is a class used for connect to the MongoDB database of the application. + * @history + * | Author | Action Performed | Data | + * | --- | --- | --- | + * | Matteo Di Pirro | Create class | 04/05/2016 | + * + * @author Matteo Di Pirro + * @license MIT + */ +class MongooseConnection { /** * @description Connection to the db */ diff --git a/src/models/userModel.ts b/src/models/userModel.ts new file mode 100644 index 0000000..30a1248 --- /dev/null +++ b/src/models/userModel.ts @@ -0,0 +1,298 @@ +import * as mongoose from "mongoose"; +import * as crypto from "crypto"; +import Model from "./model"; +import CustomModel from "./customModelInterface"; + +/** + * This is the model to represent users in MaaS. Extends model class. + * + * @history + * | Author | Action Performed | Data | + * | --- | --- | --- | + * | Luca Bianco | Create class | 07/05/2016 | + * + * @author Luca Bianco + * @license MIT + */ + +interface UserDocument extends CustomModel { + _id : string; + /** + * @description The user's username. + */ + username : string; + + + /** + * @description virtual field to store the password before hasing + */ + password : string; + + /** + * @description The user's hashed password. + */ + passwordHashed : string; + + /** + * @description The user's salt password. + */ + passwordSalt : string; + + /** + * @description The user's password iteration. + */ + passwordIterations : number; + + /** + * @description The user's level. + */ + level : string; + + /** + * Document method to authenticate the user + * @param password + */ + authenticate : (password : string) => boolean; +} + +/** + * description This class represents the user model. + */ +class UserModel extends Model { + /** + * @description Default number of password iterations. + */ + private static PWD_DEFAULT_ITERATIONS : number = 1000; + + /** + * Default password length. + */ + private static PWD_LENGTH : number = 50; + + /** + * Array which represents the user's levels. + * @type {string[]} + */ + private USER_TYPES : Array = [ + "GUEST", + "MEMBER", + "ADMIN", + "OWNER", + "SUPERADMIN" + ]; + + /** + * @description Default constructor. + */ + constructor() { + super(); + } + + /** + * @description Login method. + * @param username User's username. + * @param password USer's password. + * @returns {Promise|Promise} Promise generated by the mongoose's query. + */ + public login(username : string, password : string) : Promise { + return new Promise( + function (resolve : (data : Object) => void, + reject : (error : Object) => void) : void { + this.model.findOne( + {username: username}, + function (error : Object, + user : UserDocument) : void { + if (error) { + reject(error); + } else { + if (!user.authenticate(password)) { + reject(new Error("Invalid password!")); + } else { + delete user.passwordHashed; + delete user.passwordSalt; + delete user.passwordIterations; + resolve(user); + } + } + }) + }); + } + + + /** + * @description Create a superAdmin + * @param jsonData + * @returns {Promise} + */ + public addSuperAdmin(jsonData : Object) : Promise { + jsonData["type"] = "SUPERADMIN"; + return this.create(jsonData); + } + + /** + * @description Set the new Credentials only if old credentials are good + * @param username old user's username + * @param password old user's password + * @param newUsername new user's username + * @param newPassword new user's password + * @returns {Promise|Promise} + */ + public setCredentials(username : string, + password : string, + newUsername : string, + newPassword : string) : Promise { + return new Promise( + function (resolve : (data : Object) => void, + reject : (error : Object) => void) : void { + this + .login(username, password) + .then((user : UserDocument) => { + user.username = newUsername; + user.password = newPassword; + + user.save((error : Object, data : Object) => { + if (error) { + reject(error); + } else { + resolve(data); + } + }) + }) + }); + } + + /** + * @description Get the element represented by the _id ID. + * @param _id The ID of the element. + * @returns {Promise|Promise} Promise generated by the mongoose's + * query. + */ + public getOne(_id : string) : Promise < Object > { + return new Promise((resolve : (data : Object) => void, + reject : (error : Object) => void) => { + this.model.findOne({_id: _id}, + { + passwordHased: false, + passwordSalt: false, + passwordIterations: false + }, + (error : Object, data : Object) => { + if (error) { + reject(error); + } else { + resolve(data); + } + }); + }) + } + + /** + * @description Get all the element of a collection. + * @returns {Promise|Promise} Promise generated by the mongoose's + * query. + */ + public getAll() : Promise < Object > { + return new Promise((resolve : (data : Object) => void, + reject : (error : Object) => void) => { + this.model.find({}, + { + passwordHased: false, + passwordSalt: false, + passwordIterations: false + }, + (error : Object, data : Object) => { + if (error) { + reject(error); + } else { + resolve(data); + } + }) + }); + } + + /** + * @description Return the user's model. + * @returns {Model} The model. + * @override + */ + protected getModel() : mongoose.Model < UserDocument > { + return mongoose.model("User", this.getSchema()); + } + + /** + * @description Return the user's model. + * @returns {Schema} The model. + * @override + */ + protected getSchema() : mongoose.Schema { + let schema : mongoose.Schema = new mongoose.Schema({ + username: { + type: String, + required: true, + sparse: true + }, + passwordHashed: { + type: String, + required: true + }, + passwordSalt: { + type: String, + required: true + }, + passwordIterations: { + type: Number, + default: UserModel.PWD_DEFAULT_ITERATIONS + }, + level: { + type: String, + required: true, + enum: this.USER_TYPES, + default: this.USER_TYPES[0] + } + }); + + schema.virtual("password") + .set(function (password : string) : void { + this._password = password; + this.passwordSalt = this.generateSalt(); + this.passwordHashed = this.hashPassword(this._password); + }) + .get(function () : string { + return this._password; + }); + + this.setSchemaMethods(schema); + + return schema; + } + + /** + * @description Set the methods of the user's schema. + * @param schema The user's schema. + */ + private setSchemaMethods(schema : mongoose.Schema) : void { + schema.method( + "authenticate", + function (passwordText : string) : boolean { + return this.hashPassword(passwordText) === this.hashed_pwd; + }); + schema.method( + "generateSalt", + function () : string { + return crypto.randomBytes(16).toString("base64"); + }); + schema.method( + "hashPassword", + function (password : string) : string { + return crypto + .pbkdf2Sync(password, + this.passwordSalt, + this.passwordIterations, + UserModel.PWD_LENGTH) + .toString("base64"); + }); + } + +} + +export default UserModel; diff --git a/src/routes/companyRouter.ts b/src/routes/companyRouter.ts new file mode 100644 index 0000000..0ff187d --- /dev/null +++ b/src/routes/companyRouter.ts @@ -0,0 +1,385 @@ +import CompanyModel from "../models/companyModel"; +import CompanyDocument from "../models/companyModel"; +import * as express from "express"; +import * as promise from "es6-promise"; +import LevelChecker from "../lib/levelChecker"; + +/** + * This class contains endpoint definition about companies. + * + * @history + * | Author | Action Performed | Data | + * | --- | --- | --- | + * | Davide Rigoni | Create class | 03/05/2016 | + * + * @author Davide Rigoni + * @license MIT + * + */ +class CompanyRouter { + + /** + * @description Express router. + */ + private router : express.Router; + + /** + * @description Company model. + */ + private companyModel : CompanyModel; + + /** + * @description Minimum level: ADMIN + */ + private checkAdmin : LevelChecker; + + /** + * @description Minimum level: OWNER + */ + private checkOwner : LevelChecker; + + /** + * @description Level checker for super admin level. + */ + private checkSuperAdmin : LevelChecker; + + /** + * @description Complete constructor. Here we initialize the company routes. + */ + constructor() { + + // Init fields. + this.router = express.Router(); + this.companyModel = new CompanyModel(); + this.checkAdmin = new LevelChecker(["ADMIN", "OWNER", "SUPERADMIN"]); + this.checkOwner = new LevelChecker(["OWNER", "SUPERADMIN"]); + this.checkSuperAdmin = new LevelChecker(["SUPERADMIN"]); + + // Set the endpoints. + this.router.get( + "/admin/companies", + this.checkSuperAdmin.check, + this.getAllCompanies); + + this.router.get( + "/companies/:company_id", + this.getOneCompany); + + this.router.post( + "/companies", + this.createCompany); + + this.router.put( + "/companies/:company_id", + this.checkAdmin.check, + this.updateCompany); + + this.router.delete( + "/companies/:company_id", + this.checkOwner.check, + this.remove); + } + + /** + * @description Return the Express router. + * @returns {express.Router} The Express router. + */ + public getRouter() : express.Router { + return this.router; + } + + /** + * @description Get a specific Company. + * @param request The express request. + * See the official + * documentation for more details. + * @param result The express response object. + * See the official + * documentation for more details. + */ + /** + * @api {get} /api/companies/:company_id + * Get the data of a stated Company. + * @apiVersion 0.1.0 + * @apiName getAllCompanies + * @apiGroup Company + * @apiPermission GUEST + * + * @apiDescription Use this request to get the data of a specific Company. + * + * @apiParam {Number} company_id The ID of the Company. + * @apiParam {Number} user_id The ID of the logged user. + * + * @apiExample Example usage: + * curl -i http://maas.com/api/companies/1540 + * + * @apiSuccess {string} name The name of the company. + * @apiSuccess {string} owner The company owner's email address. + * + * @apiError CannotFindTheCompany It was impossible to find the requested + * Company. + * + * @apiErrorExample Response (example): + * HTTP/1.1 404 + * { + * "done": false, + * "error": "Cannot get the Company" + * } + */ + private getOneCompany(request : express.Request, + result : express.Response) : void { + this.companyModel + .getOne(request.params.company_id) + .then(function (data : Object) : void { + result + .status(200) + .json(data); + }, function (error : Object) : void { + result + .status(404) + .json({ + done: false, + message: "Cannot find the requested company" + }); + }) + } + + /** + * @description Get all the companies subscribed to MaaS. + * @param request The express request. + * See the official + * documentation for more details. + * @param result The express response object. + * See the official + * documentation for more details. + */ + /** + * @api {get} /api/admin/companies + * Get the data of all the companies subscribed to MaaS. + * @apiVersion 0.1.0 + * @apiName getAllCompanies + * @apiGroup Company + * @apiPermission SUPERADMIN + * + * @apiDescription Use this request to get the data of all the companies + * subscribed to MaaS. They are stored in an array. + * + * @apiParam {Number} user_id The ID of the logged user. + * + * @apiExample Example usage: + * curl -i http://maas.com/api/companies/company_id + * + * @apiSuccess {array} Please take note that the 'name' and 'owner' + * fields are + * stored in an array. Each element of this array represents a company + * subscribed to MaaS. + * @apiSuccess {string} name The name of the company. + * @apiSuccess {string} owner The company owner's email address. + * + * @apiError NoAccessRight Only authenticated Super admins can access + * the data. + * @apiError CannotFindAnyCompany It was impossible to find any Company. + * + * @apiErrorExample Response (example): + * HTTP/1.1 404 + * { + * "done": false, + * "error": "Cannot get companies data" + * } + */ + private getAllCompanies(request : express.Request, + result : express.Response) : void { + this.companyModel + .getAll() + .then(function (data : Object) : void { + result + .status(200) + .json(data); + }, function (error : Object) : void { + // Todo: che status? + result + .status(400) + .json({ + done: false, + message: "Cannot get companies data" + }) + }); + } + + /** + * @description Create a new Company. + * @param request The express request. + * See the official + * documentation for more details. + * @param result The express response object. + * See the official + * documentation for more details. + */ + /** + * @api {post} /api/companies + * Create a new Company. + * @apiVersion 0.1.0 + * @apiName updateCompany + * @apiGroup Company + * + * @apiDescription Use this request to create a new Company. + * + * @apiParam {string} name The name of the Company. + * @apiParam {string} owner The ID of the owner of the Company. + * + * @apiExample Example usage: + * curl -i http://maas.com/api/companies/1540 + * + * @apiSuccess {Number} id The id of the Company. + * + * @apiError CannotCreateTheCompany It was impossible to find the requested + * Company. + * + * @apiErrorExample Response (example): + * HTTP/1.1 404 + * { + * "done": false, + * "error": "Cannot save the Company" + * } + */ + private createCompany(request : express.Request, + result : express.Response) : void { + this.companyModel + .create(request.body) + .then(function (data : Object) : void { + result + .status(200) + .json(data); + }, function (error : Object) : void { + result + // Not acceptable + .status(406) + .json({ + done: false, + message: "Cannot save the company" + }) + }); + } + + /** + * @description Update a stated Company. + * @param request The express request. + * See the official + * documentation for more details. + * @param result The express response object. + * See the official + * documentation for more details. + */ + /** + * @api {put} /api/companies/:company_id + * Update the data of a specific Company. + * @apiVersion 0.1.0 + * @apiName updateCompany + * @apiGroup Company + * @apiPermission ADMIN + * + * @apiDescription Use this request to update the data of a specific + * Company. + * + * @apiParam {Number} company_id The ID of the Company. + * @apiParam {string} name The name of the Company. + * @apiParam {string} owner The ID of the owner of the Company. + * @apiParam {Number} user_id The ID of the logged user. + * + * @apiExample Example usage: + * curl -i http://maas.com/api/companies/1540 + * + * @apiSuccess {Number} id The id of the Company. + * @apiSuccess {string} name The new name of the company. + * @apiSuccess {string} owner The new company owner's email address. + * + * @apiError CannotFindTheCompany It was impossible to find the requested + * Company. + * @apiError NoAccessRight Only authenticated Admins can access + * the data. + * + * @apiErrorExample Response (example): + * HTTP/1.1 404 + * { + * "done": false, + * "error": "Cannot update the Company" + * } + */ + private updateCompany(request : express.Request, + result : express.Response) : void { + this.companyModel + .update(request.params.company_id, request.body) + .then(function (data : Object) : void { + result + .status(200) + .json(data); + }, function (error : Object) : void { + result + .status(406) + .json({ + done: false, + message: "Cannot modify the data" + }); + }) + } + + /** + * @description Remove a stated company. + * @param request The express request. + * See the official + * documentation for more details. + * @param result The express response object. + * See the official + * documentation for more details. + */ + /** + * @api {delete} /api/companies/:company_id + * Delete the data of a specific Company. + * @apiVersion 0.1.0 + * @apiName removeCompany + * @apiGroup Company + * @apiPermission OWNER + * + * @apiDescription Use this request to remove a specific Company. + * + * @apiParam {Number} company_id The ID of the Company. + * @apiParam {Number} user_id The ID of the logged user. + * + * @apiExample Example usage: + * curl -i http://maas.com/api/companies/1540 + * + * @apiSuccess {Number} id The id of the removed Company. + * + * @apiError CannotFindTheCompany It was impossible to find the requested + * Company. + * @apiError NoAccessRight Only authenticated Owners can access + * the data. + * + * @apiErrorExample Response (example): + * HTTP/1.1 404 + * { + * "done": false, + * "error": "Cannot remove the Company" + * } + */ + private remove(request : express.Request, + result : express.Response) : void { + this.companyModel + .remove(request.params) + .then(function (data : Object) : void { + result + .status(200) + .json(data); + }, function (error : Object) : void { + result + // Todo set the status + .status(400) + .json({ + done: false, + message: "Can't remove the Company" + }); + }); + } +} + +export default CompanyRouter; diff --git a/src/routes/databaseRouter.ts b/src/routes/databaseRouter.ts new file mode 100644 index 0000000..4b7e393 --- /dev/null +++ b/src/routes/databaseRouter.ts @@ -0,0 +1,438 @@ +/* +TODO: Missing reference to the DatabaseModel object +*/ +import * as express from "express"; +import DatabaseModel from "../models/databaseModel"; +import LevelChecker from "../lib/levelChecker"; +/* import authentication checker */ + +/** + * This class contains API definitions for companies database connections. + * + * @history + * | Author | Action Performed | Data | + * | --- | --- | --- | + * | Davide Polonio | Create class | 03/05/2016 | + * + * @author Davide Polonio + * @copyright MIT + */ +class DatabaseRouter { + + /** + * @description Express router. + */ + private router : express.Router; + + /** + * @description Level checker for member level. + */ + private checkMember : LevelChecker; + + /** + * @description Level checker for admin level. + */ + private checkAdmin : LevelChecker; + + /** + * @description Database model. + */ + private databaseModel : DatabaseModel; + + /** + * @description Complete constructor. Here we initialize databases routers. + */ + constructor() { + + this.router = express.Router(); + this.databaseModel = new DatabaseModel(); + this.checkMember = new LevelChecker( + ["MEMBER", "ADMIN", "OWNER", "SUPERADMIN"]); + this.checkAdmin = new LevelChecker( + ["ADMIN", "OWNER", "SUPERADMIN"]); + + + this.router.get( + "/companies/:company_id/databases", + this.checkMember.check, + this.getAllDatabasesForCompany); + this.router.get( + "/companies/:company_id/databases/:database_id", + this.checkMember.check, + this.getOneDatabase); + // TODO getCollections + this.router.post( + "/companies/:company_id/databases", + this.checkAdmin.check, + this.createDatabase); + this.router.put( + "/companies/:company_id/database/:database_id", + this.checkAdmin.check, + this.updateDatabase); + this.router.delete( + "/companies/:company_id/database/:database_id", + this.checkAdmin.check, + this.removeDatabase); + } + + /** + * @description Return the Express router. + * @returns {express.Router} The Express router. + */ + public getRouter() : express.Router { + return this.router; + } + + /** + * @description Get the database represented by the id contained in + * the request. + * @param request The express request. + * See the official + * documentation for more details. + * @param result The express response object. + * See the official + * documentation for more details. + */ + /** + * @api {get} /api/companies/:company_id/databases/:database_id + * Get a stated database. + * @apiVersion 0.1.0 + * @apiName getOneDatabase + * @apiGroup Database + * @apiPermission ADMIN + * + * @apiDescription Use this request to get the data of an existing + * database. + * + * @apiParam {Number} company_id The ID of the Company. + * @apiParam {Number} database_id The ID of the database to return. + * @apiParam {Number} user_id The ID of the logged user. + * + * @apiExample Example usage: + * curl -i http://maas.com/api/companies/1540/databases/5230 + * + * @apiSuccess {Number} company_id The ID of the Company. + * @apiSuccess {Number} database_id The ID of the database to update. + * @apiSuccess {string} username The username of the used used for read + * data from the database. + * @apiSuccess {string} password The password of the used used for read + * data from the database. + * @apiSuccess {string} host The host of the database. + * @apiSuccess {string} name The name of the database. + * @apiSuccess {Number} port The port of the database. + * + * @apiError CannotFindTheDatabase It was impossible to find the requested + * database. + * @apiError CannotFindTheCompany It was impossible to find the requested + * company. + * @apiError NoAccessRight Only authenticated admins can access + * the data. + * + * @apiErrorExample Response (example): + * HTTP/1.1 404 + * { + * "done": false, + * "error": "Cannot update the database" + * } + */ + private getOneDatabase(request : express.Request, + result : express.Response) : void { + this.databaseModel + .getOne(request.params.database_id) + .then(function (data : Object) : void { + result + .status(200) + .json(data); + }, function (error : Object) : void { + result + .status(404) + .json({ + done: false, + message: "Cannot find the requested database" + }); + }); + } + + /** + * @description Get all databases for the company. + * @param request The express request. + * See the official + * documentation for more details. + * @param result The express response object. + * See the official + * documentation for more details. + */ + /** + * @api {put} /api/companies/:company_id/databases + * Return a list of all the Company's databases + * @apiVersion 0.1.0 + * @apiName getAllDatabase + * @apiGroup Database + * @apiPermission MEMBER + * + * @apiDescription Use this request to get a list of the accessible + * database of the stated Company + * + * @apiParam {Number} company_id The ID of the Company. + * @apiParam {Number} database_id The ID of the database to update. + * @apiParam {Number} user_id The ID of the logged user. + * + * @apiExample Example usage: + * curl -i http://maas.com/api/companies/1540/databases/5230 + * + * @apiSuccess {JSON[]} databases Please take note that this request + * return an array which contains the requested data. + * @apiSuccess {Number} id Database's ID. + * @apiSuccess {string} Database's name. + * + * @apiError CannotFindTheDatabase It was impossible to find the requested + * database. + * @apiError CannotFindTheCompany It was impossible to find the requested + * company. + * @apiError NoAccessRight Only authenticated members can access + * the data. + * + * @apiErrorExample Response (example): + * HTTP/1.1 404 + * { + * "done": false, + * "error": "Cannot find the database" + * } + */ + private getAllDatabasesForCompany(request : express.Request, + result : express.Response) : void { + this.databaseModel + .getAllForCompany(request.params.company_id) + .then(function (data : Object) : void { + result + .status(200) + .json(data); + }, function (error : Object) : void { + result + .status(404) + .json({ + done: false, + message: "Cannot find the databases" + }); + }); + } + + /** + * @description Update the database represented by the id contained in + * the request. + * @param request The express request. + * See the official + * documentation for more details. + * @param result The express response object. + * See the official + * documentation for more details. + */ + /** + * @api {put} /api/companies/:company_id/databases/:database_id + * Update a stated database. + * @apiVersion 0.1.0 + * @apiName updateDatabase + * @apiGroup Database + * @apiPermission ADMIN + * + * @apiDescription Use this request to update an existing database. + * + * @apiParam {Number} company_id The ID of the Company. + * @apiParam {Number} database_id The ID of the database to update. + * @apiParam {Number} user_id The ID of the logged user. + * @apiParam {string} username The username of the used used for read + * data from the database. + * @apiParam {string} password The password of the used used for read + * data from the database. + * @apiParam {string} host The host of the database. + * @apiParam {string} name The name of the database. + * @apiParam {Number} port The port of the database. + * @apiParam {JSON[]} collections Array containing the collections of + * the database. + * + * @apiExample Example usage: + * curl -i http://maas.com/api/companies/1540/databases/5230 + * + * @apiSuccess {Number} company_id The ID of the Company. + * @apiSuccess {Number} database_id The ID of the database to update. + * @apiSuccess {string} username The username of the used used for read + * data from the database. + * @apiSuccess {string} password The password of the used used for read + * data from the database. + * @apiSuccess {string} host The host of the database. + * @apiSuccess {string} name The name of the database. + * @apiSuccess {Number} port The port of the database. + * + * @apiError CannotRemoveTheDatabase It was impossible to find the requested + * database. + * @apiError CannotFindTheCompany It was impossible to find the requested + * company. + * @apiError NoAccessRight Only authenticated admins can access + * the data. + * + * @apiErrorExample Response (example): + * HTTP/1.1 404 + * { + * "done": false, + * "error": "Cannot update the database" + * } + */ + private updateDatabase(request : express.Request, + result : express.Response) : void { + this.databaseModel + .update(request.params.database_id, request.body) + .then(function (data : Object) : void { + result + .status(200) + .json(data); + }, function (error : Object) : void { + result + // Todo : set the status + .status(404) + .json({ + done: false, + message: "Cannot modify the databases" + }); + }); + } + + /** + * @description Remove the database represented by the id contained in + * the request. + * @param request The express request. + * See the official + * documentation for more details. + * @param result The express response object. + * See the official + * documentation for more details. + */ + /** + * @api {delete} /api/companies/:company_id/databases/:database_id + * Update a stated database. + * @apiVersion 0.1.0 + * @apiName removeDatabase + * @apiGroup Database + * @apiPermission ADMIN + * + * @apiDescription Use this request to update an existing database. + * + * @apiParam {Number} company_id The ID of the Company. + * @apiParam {Number} database_id The ID of the database to remove. + * @apiParam {Number} user_id The ID of the logged user. + * + * @apiExample Example usage: + * curl -i http://maas.com/api/companies/1540/databases/5230 + * + * @apiSuccess {Number} company_id The ID of the Company. + * @apiSuccess {Number} database_id The ID of the database to update. + * + * @apiError CannotCreateTheDatabase It was impossible to find the requested + * database. + * @apiError CannotFindTheCompany It was impossible to find the requested + * company. + * @apiError NoAccessRight Only authenticated admins can access + * the data. + * + * @apiErrorExample Response (example): + * HTTP/1.1 404 + * { + * "done": false, + * "error": "Cannot remove the database" + * } + */ + private removeDatabase(request : express.Request, + result : express.Response) : void { + this.databaseModel + .remove(request.params.database_id) + .then(function (data : Object) : void { + result + .status(200) + .json(data); + }, function (error : Object) : void { + result + // Todo : set the status + .status(404) + .json({ + done: false, + message: "Cannot remove the database" + }); + }); + } + + /** + * @description Create a new database. + * @param request The express request. + * See the official + * documentation for more details. + * @param result The express response object. + * See the official + * documentation for more details. + */ + /** + * @api {post} /api/companies/:company_id/databases/ + * Create a new database. + * @apiVersion 0.1.0 + * @apiName createDatabase + * @apiGroup Database + * @apiPermission ADMIN + * + * @apiDescription Use this request to create a new database. + * + * @apiParam {Number} company_id The ID of the Company. + * @apiParam {Number} user_id The ID of the logged user. + * @apiParam {string} username The username of the used used for read + * data from the database. + * @apiParam {string} password The password of the used used for read + * data from the database. + * @apiParam {string} host The host of the database. + * @apiParam {string} name The name of the database. + * @apiParam {Number} port The port of the database. + * + * @apiExample Example usage: + * curl -i http://maas.com/api/companies/1540/databases/5230 + * + * @apiSuccess {Number} company_id The ID of the Company. + * @apiSuccess {Number} database_id The ID of the database to update. + * @apiSuccess {string} username The username of the used used for read + * data from the database. + * @apiSuccess {string} password The password of the used used for read + * data from the database. + * @apiSuccess {string} host The host of the database. + * @apiSuccess {string} name The name of the database. + * @apiSuccess {Number} port The port of the database. + * + * @apiError CannotUpdateTheDatabase It was impossible to find the requested + * database. + * @apiError CannotFindTheCompany It was impossible to find the requested + * company. + * @apiError NoAccessRight Only authenticated admins can access + * the data. + * + * @apiErrorExample Response (example): + * HTTP/1.1 404 + * { + * "done": false, + * "error": "Cannot create the database" + * } + */ + private createDatabase(request : express.Request, + result : express.Response) : void { + this.databaseModel + .create(request.body) + .then(function (data : Object) : void { + result + .status(200) + .json(data); + }, function (error : Object) : void { + result + // Todo : set the status + .status(404) + .json({ + done: false, + message: "Cannot create the database" + }); + }); + } +} + +export default DatabaseRouter; diff --git a/src/routes/dslRouter.ts b/src/routes/dslRouter.ts new file mode 100644 index 0000000..8acc35a --- /dev/null +++ b/src/routes/dslRouter.ts @@ -0,0 +1,386 @@ +import * as express from "express"; +import DSLModel from "../models/dslModel"; +import LevelChecker from "../lib/levelChecker"; +/** + * This class contains endpoint definition about DSLs. + * + * @history + * | Author | Action Performed | Data | + * | --- | --- | --- | + * | Emanuele Carraro | Create class DSLRouter | 10/05/2016 | + * + * @author Emanuele Carraro + * @license MIT + * + */ +class DSLRouter { + + /** + * @description Express router. + */ + private router : express.Router; + + /** + * @description Level checker. + */ + private checkMember : LevelChecker; + + + /** + * @description DSL' model. + */ + private dslModel : DSLModel; + + /** + * @description Complete constructor. + */ + constructor() { + + this.router = express.Router(); + this.dslModel = new DSLModel(); + this.checkMember = new LevelChecker( + ["MEMBER", "ADMIN", "OWNER", "SUPERADMIN"]); + + this.router.get( + "/companies/:company_id/DSLs", + this.getAllDSLForCompany); + this.router.get( + "/companies/:company_id/DSLs/:dsl_id", + this.checkMember.check, + this.getOneDSL); + this.router.post( + "/companies/:company_id/DSLs/", + this.checkMember.check, + this.createDSL); + this.router.put( + "/companies/:company_id/DSLs/:dsl_id", + this.checkMember.check, + this.updateDSL); + this.router.delete( + "/companies/:company_id/DSLs/:dsl_id", + this.checkMember.check, + this.removeDSL); + + // TODO DSL execution + // TODO dashboard + } + + /** + * @description Return the Express router. + * @returns {express.Router} The Express router. + */ + public getRouter() : express.Router { + return this.router; + } + + /** + * @description Get the DSL represented by the id contained in + * the request. + * @param request The express request. + * See the official + * documentation for more details. + * @param response The express response object. + * See the official + * documentation for more details. + */ + /** + * @api {get} /api/companies/:company_id/DSLs/:dsl_id + * Get all the DSL specifics accessible from the logged user. + * @apiVersion 0.1.0 + * @apiName getOneDSL + * @apiGroup DSL + * @apiPermission GUEST + * + * @apiDescription Use this request to get the code of a stated specific + * DSL. + * + * @apiParam {Number} company_id The ID of the Company. + * @apiParam {Number} user_id The ID of the logged user. + * + * @apiExample Example usage: + * curl -i http://maas.com/api/companies/1540/DSLs/6552/ + * + * @apiSuccess {Number} dsl_id The ID of the specific dsl. + * @apiSuccess {string} code The code of the specific dsl. + * + * @apiError CannotFindTheDSL It was impossible to find the requested + * specific dsl. + * @apiError CannotFindTheCompany It was impossible to find the requested + * company. + * @apiError NoAccessRight Only authenticated members can access the data. + * + * @apiErrorExample Response (example): + * HTTP/1.1 404 + * { + * "done": false, + * "error": "Cannot find the DSL" + * } + */ + private getOneDSL(request : express.Request, + response : express.Response) : void { + this.dslModel + .getOne(request.params.dsl_id) + .then(function (data : Object) : void { + response + .status(200) + .json(data); + }, function (error : Object) : void { + response + .status(404) + .json({ + done: false, + message: "Cannot find the requested DSL" + }); + }); + } + + /** + * @description Get the dsl for the company + * @param request The express request. + * See the official + * documentation for more details. + * @param result The express response object. + * See the official + * documentation for more details. + */ + /** + * @api {get} /api/companies/:company_id/DSLs + * Get all the DSL specifics accessible from the logged user. + * @apiVersion 0.1.0 + * @apiName getAllDSLForCompany + * @apiGroup DSL + * @apiPermission MEMBER + * + * @apiDescription Use this request to get all the specific dsls + * accessible from the logged user. + * + * @apiParam {Number} company_id The ID of the Company. + * @apiParam {Number} user_id The ID of the logged user. + * + * @apiExample Example usage: + * curl -i http://maas.com/api/companies/1540/DSLs/ + * + * @apiSuccess {JSON[]} dsls Please take note that all the data of the + * DSLs are returned in an array. + * @apiSuccess {Number} dsl_id The ID of the specific dsl. + * @apiSuccess {string} code The code of the specific dsl. + * + * @apiError CannotFindTheDSL It was impossible to find the requested + * specific dsl. + * @apiError CannotFindTheCompany It was impossible to find the requested + * company. + * + * @apiErrorExample Response (example): + * HTTP/1.1 404 + * { + * "done": false, + * "error": "Cannot find the DSLs" + * } + */ + private getAllDSLForCompany(request : express.Request, + response : express.Response) : void { + this.dslModel + .getAllForCompany(request.params.company_id) + .then(function (data : Object) : void { + response + .status(200) + .json(data); + }, function (error : Error) : void { + response + .status(404) + .json({ + done: false, + message: "Cannot find the DSLs" + }); + }); + } + + /** + * @description Update the dsl represented by the id contained in + * the request. + * @param request The express request. + * See the official + * documentation for more details. + * @param response The express response object. + * See the official + * documentation for more details. + */ + /** + * @api {put} /api/companies/:company_id/DSLs/:dsl_id + * Update a stated specific DSL. + * @apiVersion 0.1.0 + * @apiName updateDSL + * @apiGroup DSL + * @apiPermission MEMBER + * + * @apiDescription Use this request to update a stated specific DSL. + * + * @apiParam {Number} company_id The ID of the Company. + * @apiParam {Number} user_id The ID of the logged user. + * @apiParam {Number} dsl_id The ID of the specific DSL. + * @apiParam {string} code The code of the specific DSL. + * @apiParam {JSON[]} permits Array which contains all the access + * permissions to the specific DSL. In particular, each element have two + * boolean fields, 'write', for writing permits, and 'exec' for the + * execution permits. + * + * @apiExample Example usage: + * curl -i http://maas.com/api/companies/1540/DSLs/6558/ + * + * @apiSuccess {Number} id The ID of the specific dsl. + * + * @apiError CannotUpdateTheDSL It was impossible to create the specific + * DSL. + * @apiError CannotFindTheCompany It was impossible to find the requested + * company. + * @apiError NoAccessRight Only authenticated members can access the data. + * + * @apiErrorExample Response (example): + * HTTP/1.1 404 + * { + * "done": false, + * "error": "Cannot update the DSL" + * } + */ + private updateDSL(request : express.Request, + response : express.Response) : void { + this.dslModel + .update(request.params.database_id, request.body) + .then(function (data : Object) : void { + response + .status(200) + .json(data); + }, function (error : Object) : void { + response + // Todo : set the status + .status(404) + .json({ + done: false, + message: "Cannot modify the dsl" + }); + }); + } + + /** + * @description Remove the dsl represented by the id contained in + * the request. + * @param request The express request. + * See the official + * documentation for more details. + * @param response The express response object. + * See the official + * documentation for more details. + */ + /** + * @api {put} /api/companies/:company_id/DSLs/:dsl_id + * Remove a stated specific DSL. + * @apiVersion 0.1.0 + * @apiName removeDSL + * @apiGroup DSL + * @apiPermission MEMBER + * + * @apiDescription Use this request to remove a stated specific DSL. + * + * @apiParam {Number} company_id The ID of the Company. + * @apiParam {Number} user_id The ID of the logged user. + * @apiParam {Number} dsl_id The ID of the specific DSL. + * + * @apiExample Example usage: + * curl -i http://maas.com/api/companies/1540/DSLs/6558/ + * + * @apiSuccess {Number} id The ID of the specific DSL. + * + * @apiError CannotRemoveTheDSL It was impossible to create the specific + * DSL. + * @apiError CannotFindTheCompany It was impossible to find the requested + * company. + * @apiError NoAccessRight Only authenticated members can access the data. + * + * @apiErrorExample Response (example): + * HTTP/1.1 404 + * { + * "done": false, + * "error": "Cannot remove the DSL" + * } + */ + private removeDSL(request : express.Request, + response : express.Response) : void { + this.dslModel + .remove(request.params.database_id) + .then(function (data : Object) : void { + response + .status(200) + .json(data); + }, function (error : Object) : void { + response + // Todo : set the status + .status(404) + .json({ + done: false, + message: "Cannot remove the DSL" + }); + }); + } + + /** + * @description Create a new DSL. + * @param request The express request. + * See the official + * documentation for more details. + * @param response The express response object. + * See the official + * documentation for more details. + */ + /** + * @api {post} /api/companies/:company_id/DSLs + * Create a new specific DSL. + * @apiVersion 0.1.0 + * @apiName createDSL + * @apiGroup DSL + * @apiPermission MEMBER + * + * @apiDescription Use this request to create a new specific DSL. + * + * @apiParam {Number} company_id The ID of the Company. + * @apiParam {Number} user_id The ID of the logged user. + * @apiParam {string} code The code of the specific DSL. + * + * @apiExample Example usage: + * curl -i http://maas.com/api/companies/1540/DSLs/ + * + * @apiSuccess {Number} id The ID of the specific dsl. + * + * @apiError CannotCreateTheDSL It was impossible to create the specific + * DSL. + * @apiError CannotFindTheCompany It was impossible to find the requested + * company. + * @apiError NoAccessRight Only authenticated members can access the data. + * + * @apiErrorExample Response (example): + * HTTP/1.1 404 + * { + * "done": false, + * "error": "Cannot create the DSL" + * } + */ + private createDSL(request : express.Request, + response : express.Response) : void { + this.dslModel + .create(request.body) + .then(function (data : Object) : void { + response + .status(200) + .json(data); + }, function (error : Object) : void { + response + // Todo : set the status + .status(404) + .json({ + done: false, + message: "Cannot create the DSL" + }); + }); + } +} + +export default DSLRouter; diff --git a/src/routes/routerFacade.ts b/src/routes/routerFacade.ts new file mode 100644 index 0000000..1046dc3 --- /dev/null +++ b/src/routes/routerFacade.ts @@ -0,0 +1,27 @@ +import * as express from "express"; + +/** + * Here we export all route files. You need only to import this file. + * @history + * | Author | Action Performed | Data | + * | --- | --- | --- | + * | Matteo Di Pirro | Create module | 06/05/2016 | + * + * @author Matteo Di Pirro + * @license MIT + */ + +// Import the routes +import DSLRouter from "./dslRouter"; +import UserRouter from "./userRouter"; +import DatabaseRouter from "./databaseRouter"; +import CompanyRouter from "./companyRouter"; + +let RouterFacade : express.Router = express.Router(); + +RouterFacade.use(new DSLRouter().getRouter()); +RouterFacade.use(new UserRouter().getRouter()); +RouterFacade.use(new DatabaseRouter().getRouter()); +RouterFacade.use(new CompanyRouter().getRouter()); + +export default RouterFacade; diff --git a/src/routes/userRouter.ts b/src/routes/userRouter.ts new file mode 100644 index 0000000..638dc89 --- /dev/null +++ b/src/routes/userRouter.ts @@ -0,0 +1,505 @@ +import * as express from "express"; +import UserModel from "../models/userModel"; +import AuthenticationChecker from "../lib/authenticationChecker"; +import LevelChecker from "../lib/levelChecker"; + +/** + * This class contains endpoint definition about users. + * + * @history + * | Author | Action Performed | Data | + * | --- | --- | --- | + * | Luca Bianco | Create class UserRouter | 10/05/2016 | + * + * @author Emanuele Carraro + * @license MIT + * + */ +class UserRouter { + + /** + * @description Express router. + */ + private router : express.Router; + + /** + * @description User's model. + */ + private userModel : UserModel; + + /** + * @description Authentication checker. + */ + private authCheck : AuthenticationChecker; + + /** + * @description Level checker + * @type {LevelChecker} + */ + private checkOwner : LevelChecker; + + /** + * @description Level checker for SuperAdmin + * @type {LevelChecker} + */ + private checkSuperAdmin : LevelChecker; + + /** + * @description Complete constructor. Here we initialize user's routes. + */ + constructor() { + + this.router = express.Router(); + this.userModel = new UserModel(); + this.authCheck = new AuthenticationChecker(); + this.checkOwner = new LevelChecker(["OWNER", "SUPERADMIN"]); + this.checkSuperAdmin = new LevelChecker(["SUPERADMIN"]); + + this.router.get( + "/companies/:company_id/users", + this.checkOwner.check, + this.getAllUsers); + + this.router.get( + "/companies/:company_id/users/:user_id/", + this.getOneUser); + + this.router.post( + "/companies/:company_id/users", + this.checkOwner.check, + this.createUser); + + this.router.put( + "/companies/:company_id/users/:user_id/credentials", + this.checkOwner.checkWithIDSkip, + this.updateUser); + + this.router.delete( + "/companies/:company_id/users/:user_id", + this.checkOwner.checkWithIDSkip, + this.removeUser); + + this.router.post( + "/admin/superadmins", + this.checkSuperAdmin.check, + this.createSuperAdmin); + } + + /** + * @description Return the Express router. + * @returns {express.Router} The Express router. + */ + public getRouter() : express.Router { + return this.router; + } + + /** + * @description Perform the user's login. + * @param request The express request. + * See the official + * documentation for more details. + * @param response The express response object. + * See the official + * documentation for more details. + */ + /** + * @api {get} /api/login + * USer login. + * @apiVersion 0.1.0 + * @apiName updateUser + * @apiGroup User + * + * @apiDescription Use this request in order to login. + * + * @apiParam {string} username The new user's email address. + * @apiParam {string} password The new user's password. + * + * @apiExample Example usage: + * curl -i http://maas.com/api/companies/5741/users/12054/credentials + * + * @apiSuccess {Number} id The User's ID. + * @apiSuccess {jwt} token Access token. + * + * @apiError CannotFindTheUser It was impossible to find the user. + * + * @apiErrorExample Response (example): + * HTTP/1.1 404 + * { + * "done": false, + * "error": "Cannot find the user" + * } + */ + private login(request : express.Request, + response : express.Response) : void { + this.authCheck + .login(request, response); + /*.then(function (data : Object) : void { + response + .status(200) + .json(data); + }, function (error : Error) : void { + response + .status(404) + .json({ + done: false, + message: "Cannot login" + }); + });*/ + } + + // TODO: is this right? + /** + * @description Creates a new super admin + * @param request The express request. + * See the official + * documentation for more details. + * @param response The express response object. + * See the official + * documentation for more details. + */ + /** + * @api {put} /api/admin/superadmins + * Add a new superadmin + * @apiVersion 0.1.0 + * @apiName addSuperAdmin + * @apiGroup Admin + * @apiPermission SUPERADMIN + * + * @apiDescription Use this API o add a new SuperAdmin + * + * @apiParam {Number} company_id The Company's ID. + * @apiParam {Number} user_id The user's ID. + * @apiParam {Number} user_id The ID of the logged user. + * @apiParam {string} username The new user's email address. + * @apiParam {string} password The new user's password. + * + * @apiExample Example usage: + * curl -i http://maas.com/api/admin/superadmin + * + * + * @apiError CannotAddTheSuperAdmin It was impossible to add the new + * SuperAdmin + * + * @apiErrorExample Response (example): + * HTTP/1.1 400 + * { + * "done": false, + * "error": "Cannot add the new Super Admin" + * } + */ + private createSuperAdmin(request : express.Request, + response : express.Response) : void { + this.userModel + .addSuperAdmin(request.body) + .then(function (data : Object) : void { + response + .status(200) + .json(data); + }, function (error : Object) : void { + response + // Todo : set the status + .status(400) + .json({ + done: false, + message: "Cannot create the user" + }); + }); + } + + /** + * @description Method to modify the credentials of an user + * @param request The express request. + * See the official + * documentation for more details. + * @param response The express response object. + * See the official + * documentation for more details. + */ + /** + * @api {put} /api/companies/:company_id/users/:user_id/credentials + * Update credentials of an user. + * @apiVersion 0.1.0 + * @apiName updateUser + * @apiGroup User + * @apiPermission GUEST + * + * @apiDescription Use this request to update your access credentials. + * + * @apiParam {Number} company_id The Company's ID. + * @apiParam {Number} user_id The user's ID. + * @apiParam {Number} user_id The ID of the logged user. + * @apiParam {string} username The new user's email address. + * @apiParam {string} password The new user's password. + * + * @apiExample Example usage: + * curl -i http://maas.com/api/companies/5741/users/12054/credentials + * + * @apiSuccess {Number} id The User's ID. + * @apiSuccess {string} username The user's new username. + * @apiSuccess {string} password The user's new password. + * + * @apiError CannotModifyTheUser It was impossible to update the user's + * data. + * + * @apiErrorExample Response (example): + * HTTP/1.1 404 + * { + * "done": false, + * "error": "Cannot modify the credentials" + * } + */ + private changeCredentials(request : express.Request, + response : express.Response) : void { + this.userModel + .setCredentials(request.body.username, + request.body.password, + request.body.newUsername, + request.body.newPassword) + .then((data : Object) => { + response + .status(200) + .json(data); + }, (error : Object) => { + response + .status(400) + .json({ + done: false, + message: "Cannot modify the credentials" + }); + }); + } + + /** + * @description Get the user represented by the id contained in + * the request. + * @param request The express request. + * See the official + * documentation for more details. + * @param response The express response object. + * See the official + * documentation for more details. + */ + private getOneUser(request : express.Request, + response : express.Response) : void { + this.userModel + .getOne(request.params.user_id) + .then(function (data : Object) : void { + response + .status(200) + .json(data); + }, function (error : Error) : void { + response + .status(404) + .json({ + done: false, + message: "Cannot find the requested user" + }); + }); + } + + /** + * @description Get all the users. + * @param request The express request. + * See the official + * documentation for more details. + * @param response The express response object. + * See the official + * documentation for more details. + */ + private getAllUsers(request : express.Request, + response : express.Response) : void { + this.userModel + .getAll() + .then(function (data : Object) : void { + response + .status(200) + .json(data); + }, function (error : Object) : void { + response + .status(404) + .json({ + done: false, + message: "Cannot find the user" + }); + }); + } + + /** + * @description Update the user represented by the id contained in + * the request. + * @param request The express request. + * See the official + * documentation for more details. + * @param response The express response object. + * See the official + * documentation for more details. + */ + /** + * @api {put} /api/companies/:company_id/users/:user_id/credentials + * Update credentials of an user. + * @apiVersion 0.1.0 + * @apiName updateUser + * @apiGroup User + * @apiPermission GUEST + * + * @apiDescription Use this request to update your access credentials. + * + * @apiParam {Number} company_id The Company's ID. + * @apiParam {Number} user_id The user's ID. + * @apiParam {Number} user_id The ID of the logged user. + * @apiParam {string} username The new user's email address. + * @apiParam {string} password The new user's password. + * + * @apiExample Example usage: + * curl -i http://maas.com/api/companies/5741/users/12054/credentials + * + * @apiSuccess {Number} id The User's ID. + * @apiSuccess {string} username The user's new username. + * @apiSuccess {string} password The user's new password. + * + * @apiError CannotModifyTheUser It was impossible to update the user's + * data. + * + * @apiErrorExample Response (example): + * HTTP/1.1 404 + * { + * "done": false, + * "error": "Cannot modify the user" + * } + */ + private updateUser(request : express.Request, + response : express.Response) : void { + this.userModel + .update(request.params.user_id, request.body) + .then(function (data : Object) : void { + response + .status(200) + .json(data); + }, function (error : Object) : void { + response + // Todo : set the status + .status(404) + .json({ + done: false, + message: "Cannot modify the user" + }); + }); + } + + /** + * @description Remove the user represented by the id contained in + * the request. + * @param request The express request. + * See the official + * documentation for more details. + * @param response The express response object. + * See the official + * documentation for more details. + */ + /** + * @api {delete} /api/companies/:company_id/users/:user_id Remove an user. + * @apiVersion 0.1.0 + * @apiName removeUser + * @apiGroup User + * @apiPermission OWNER + * + * @apiDescription Use this request to remove an user from a stated Company. + * + * @apiParam {Number} company_id The Company's ID. + * @apiParam {Number} user_id The user's ID. + * @apiParam {Number} user_id The ID of the logged user. + * + * @apiExample Example usage: + * curl -i http://maas.com/api/companies/5741/users/12054/ + * + * @apiSuccess {Number} id The User's ID. + * + * @apiError NoAccessRight Only authenticated Owners can access the data. + * @apiError CannotRemoveTheUser It was impossible to remove the user. + * + * @apiErrorExample Response (example): + * HTTP/1.1 404 + * { + * "done": false, + * "error": "Cannot remove the user" + * } + */ + + private removeUser(request : express.Request, + response : express.Response) : void { + this.userModel + .remove(request.params.user_id) + .then(function (data : Object) : void { + response + .status(200) + .json(data); + }, function (error : Object) : void { + response + // Todo : set the status + .status(404) + .json({ + done: false, + message: "Cannot remove the user" + }); + }); + } + + /** + * @description Create a new user. + * @param request The express request. + * See the official + * documentation for more details. + * @param response The express response object. + * See the official + * documentation for more details. + */ + /** + * @api {post} /api/companies/:company_id/users Read data of an User + * @apiVersion 0.1.0 + * @apiName createUser + * @apiGroup User + * @apiPermission OWNER + * + * @apiDescription Use this request to insert a new user in a stated + * company. + * + * @apiParam {Number} company_id The Company's ID. + * @apiParam {Number} user_id The ID of the logged user. + * + * @apiExample Example usage: + * curl -i http://maas.com/api/companies/5741/users + * + * @apiSuccess {Number} id The User's ID. + * + * @apiError NoAccessRight Only authenticated Owners can access the data. + * @apiError CannotCreateTheUser It was impossible to create the user. + * + * @apiErrorExample Response (example): + * HTTP/1.1 404 + * { + * "done": false, + * "error": "Cannot create the user" + * } + */ + + private createUser(request : express.Request, + response : express.Response) : void { + this.userModel + .create(request.body) + .then(function (data : Object) : void { + response + .status(200) + .json(data); + }, function (error : Object) : void { + response + // Todo : set the status + .status(404) + .json({ + done: false, + message: "Cannot create the user" + }); + }); + } +} + +export default UserRouter; diff --git a/test/devConfigurationTest.ts b/test/config/devConfigurationTest.ts similarity index 91% rename from test/devConfigurationTest.ts rename to test/config/devConfigurationTest.ts index a001d16..228e0e1 100644 --- a/test/devConfigurationTest.ts +++ b/test/config/devConfigurationTest.ts @@ -4,8 +4,8 @@ */ import * as Chai from "chai"; -import DevConfiguration from "../src/config/devConfiguration"; -import MongoConnection from "../src/config/mongoConnection"; +import DevConfiguration from "../../src/config/devConfiguration"; +import MongoConnection from "../../src/config/mongoConnection"; describe("DevConfigurationTest", () => { diff --git a/test/indexTest.ts b/test/config/indexTest.ts similarity index 56% rename from test/indexTest.ts rename to test/config/indexTest.ts index d2c9def..a9fc787 100644 --- a/test/indexTest.ts +++ b/test/config/indexTest.ts @@ -2,8 +2,10 @@ * This is a test for the ChooseConfiguration class. */ -import ChooseConfiguration from "../src/config/index"; +import ChooseConfiguration from "../../src/config/index"; import * as Chai from "chai"; // You need assertions. Trust me. +import {readFileSync} from "fs"; +import MongoConnection from "../../src/config/mongoConnection"; describe("IndexTest", () => { @@ -38,4 +40,21 @@ describe("IndexTest", () => { equal("production"); }); }); + + describe("#correctParameters", () => { + it ("Should read the right parameters", () => { + let params : MongoConnection = JSON.parse(readFileSync( + "src/config/mongoParameters.json", + "utf-8" + )); + let connection : MongoConnection = ChooseConfiguration.getConfig(). + getMongoConnection(); + Chai.expect(connection.getUser()).to.equal(params["user"]); + Chai.expect(connection.getPassword()).to.equal(params["password"]); + Chai.expect(connection.getHost()).to.equal(params["host"]); + Chai.expect(connection.getDatabasePort()).to.equal(params["port"]); + Chai.expect(connection.getDatabaseName()).to. + equal(params["dbName"]); + }) + }) }); diff --git a/test/mongoConnectionTest.ts b/test/config/mongoConnectionTest.ts similarity index 95% rename from test/mongoConnectionTest.ts rename to test/config/mongoConnectionTest.ts index 4ecb88a..01ff57f 100644 --- a/test/mongoConnectionTest.ts +++ b/test/config/mongoConnectionTest.ts @@ -3,7 +3,7 @@ * getter methods work correctly. */ -import MongoConnection from "../src/config/mongoConnection"; +import MongoConnection from "../../src/config/mongoConnection"; import * as Chai from "chai"; // You need assertions. Trust me. describe("MongoConnectionTest", () => { diff --git a/test/prodConfigurationTest.ts b/test/config/prodConfigurationTest.ts similarity index 91% rename from test/prodConfigurationTest.ts rename to test/config/prodConfigurationTest.ts index 1460041..34d9667 100644 --- a/test/prodConfigurationTest.ts +++ b/test/config/prodConfigurationTest.ts @@ -4,8 +4,8 @@ */ import * as Chai from "chai"; -import ProdConfiguration from "../src/config/prodConfiguration"; -import MongoConnection from "../src/config/mongoConnection"; +import ProdConfiguration from "../../src/config/prodConfiguration"; +import MongoConnection from "../../src/config/mongoConnection"; describe("ProdConfigurationTest", () => { diff --git a/test/testConfigurationTest.ts b/test/config/testConfigurationTest.ts similarity index 91% rename from test/testConfigurationTest.ts rename to test/config/testConfigurationTest.ts index 2241d0c..76f861c 100644 --- a/test/testConfigurationTest.ts +++ b/test/config/testConfigurationTest.ts @@ -4,8 +4,8 @@ */ import * as Chai from "chai"; -import TestConfiguration from "../src/config/testConfiguration"; -import MongoConnection from "../src/config/mongoConnection"; +import TestConfiguration from "../../src/config/testConfiguration"; +import MongoConnection from "../../src/config/mongoConnection"; describe("TestConfigurationTest", () => { diff --git a/test/lib/dslCheckerTest.ts b/test/lib/dslCheckerTest.ts new file mode 100644 index 0000000..1aa5789 --- /dev/null +++ b/test/lib/dslCheckerTest.ts @@ -0,0 +1,77 @@ +import DSLChecker from "../../src/lib/dslChecker" +import * as Chai from "chai" + +describe("DSLCheckTest", () => { + + let toTest : DSLChecker; + + // Call one of the many function that are in the Mocha framework + beforeEach(() => { + toTest = new DSLChecker(); + }); + + /* + Test to show if DSLChecker check correctly a DSLStruct with inside + only the root elements (Cell,Collection,Dashboard,Document) + */ + describe("#workWithOnlyRoot", () => { + it("Cell", () => { + Chai.expect(toTest.check({ + root: "1", + "1": { + type: "cell", + pModel: {} + } + })).to.equal(true); + }); + + it("Collection", () => { + Chai.expect(toTest.check({ + root: "1", + "1": { + type: "collection", + pModel: { + action: ["2"], + index: "3", + document: "4" + } + }, + + "2": { + type: "action", + pModel: {} + }, + + "3": { + type: "index", + pModel: { + column: ["5"] + } + }, + + "4": { + type: "document", + pModel: { + action: ["6"], + row: ["7"] + } + }, + + "5": { + type: "column", + pModel: {} + }, + + "6": { + type: "action", + pModel: {} + }, + + "7": { + type: "row", + pModel: {} + } + })).to.equal(true); + }); + }); +}); diff --git a/test/models/companyModelTest.ts b/test/models/companyModelTest.ts new file mode 100644 index 0000000..181725c --- /dev/null +++ b/test/models/companyModelTest.ts @@ -0,0 +1,68 @@ +import CompanyModel from "../../src/models/companyModel"; +import CompanyDocument from "../../src/models/companyModel"; +import * as Chai from "chai"; +import * as mongoose from "mongoose"; + +/** + * CompanyModel implements the company business logic. It contains model and + * scheme defined by MongooseJS. + * + * + * @history + * | Author | Action Performed | Data | + * | --- | --- | --- | + * | Davide Rigoni | Create class | 06/05/2016 | + * + * @author Davide Rigoni + * @copyright MIT + * + * + */ + +describe("CompanyModel", () => { + let toTest : CompanyModel = new CompanyModel(); + let testID : string = ""; + let anotherTestID : string = ""; + + describe("#Add", () => { + it("Should create a company and the owner", () => { + // TODO + }); + }); + + describe("#Find", () => { + it("Should find a company", () => { + toTest.getOne(testID).then( + function(doc : CompanyDocument) : void { + Chai.expect(doc["_id"]).to.equal(testID); + }); + }); + + it("Should find all companies", () => { + toTest.getAll().then( + function(docs : CompanyDocument[]) : void { + Chai.expect(docs[0]["_id"]).to.equal(testID); + Chai.expect(docs[1]["_id"]).to.equal(anotherTestID); + }) + }) + }); + + describe("#Update", () => { + /*toTest.update(testID, new CompanyDocument("AnotherName", + "")). + + then(function );*/ + }); + + describe("#Remove", () => { + it("Should remove a company given its id", () => { + let promise : Promise = toTest.remove(testID); + promise.then(function(doc : CompanyDocument) : void { + toTest.getOne(doc["_id"]).then( + function(doc2 : CompanyDocument) : void { + Chai.expect(doc2).null; + }) + }); + }) + }); +}); diff --git a/test/models/databaseModelTest.ts b/test/models/databaseModelTest.ts new file mode 100644 index 0000000..b9f480e --- /dev/null +++ b/test/models/databaseModelTest.ts @@ -0,0 +1,56 @@ +import * as DatabaseModel from "../../src/models/databaseModel"; +import * as Chai from "chai"; + +/** + * DatabaseModel manage all connections to MongoDB companies databases. + * Implements model and schema of MongooseJS. + * + * This model represent a connection to a company. + * + * @history + * | Author | Action Performed | Data | + * | --- | --- | --- | + * | Davide Polonio | Create class | 06/05/2016 | + * + * @author Davide Polonio + * @copyright MIT + * + * + */ + +describe("DatabaseModel", () => { + /* + * //TODO + * Things to test: + * 1 - Return a list of databases of a company (getDatabase) + * 2 - Try to get a list of databases of a company da doesn't + * exists (getDatabase) + * 3 - Add a database to a company. + * 4 - Try to get a single database of a company + * 5 - Try to get a single database of a company that doesn't exists + * 6 - Try to update a database + * 7 - Try to update a database that doesn't exists + * 8 - Remove a database + * 9 - Remove a database that doesn't exists + */ + + /* + let toTest:someThing; // Name of the object to test + + // Call one of the many function that are in the Mocha framework + beforeEach(function ():void { + + toTest = new someThing(); // You can do something before every test + }); + + // This function starts a single test + describe("#nameOfWhatYouAreTesting", () => { + it("should ", () => { + + // Your code here + + Chai.expect(toTest.sayHello(name)).to.equal("Hello Derp"); + }); + + */ + }); diff --git a/test/models/dslModelTest.ts b/test/models/dslModelTest.ts new file mode 100644 index 0000000..f298a16 --- /dev/null +++ b/test/models/dslModelTest.ts @@ -0,0 +1,29 @@ +import * as Chai from "chai" +import * as mongoose from "mongoose" +import DSLModel from "../../src/models/dslModel" + +describe("DSLModel", () => { + let dsl : DSLModel; + + beforeEach(() => { + dsl = new DSLModel(); + }); + + describe("#loadADSLOnDatabase", () => { + it("should exec the callback", () => { + let promise : Promise = + dsl.create({ + permission: [{user: "bo", read: true, exec: true}], + content: "this is unsafe" + }); + + /* @todo + promise.onFulfill((data : Object) => { + console.log("Hello world!"); + + }).onReject((error : Object) => { + console.log("Errore"); + });*/ + }); + }); +}); diff --git a/test/routes/companyRouterTest.ts b/test/routes/companyRouterTest.ts new file mode 100644 index 0000000..83ff772 --- /dev/null +++ b/test/routes/companyRouterTest.ts @@ -0,0 +1,33 @@ +import * as CompanyRouter from "../../src/routes/companyRouter"; +import * as express from "express"; +import * as Chai from "chai"; + +/** + * This is the test for CompanyRouter class + * + * @history + * | Author | Action Performed | Data | + * | --- | --- | --- | + * | Davide Rigoni | Create class | 03/05/2016 | + * + * @author Davide Rigoni + * @copyright MIT + * + * Created by Davide Rigoni on 03/05/16. + */ +describe("DatabaseRouter", () => { + + /* nothing to test for now + let toTest : DatabaseRouter; + let dummyExpress : express.Express = express(); + beforeEach(function () : void { + toTest = new DatabaseRouter(dummyExpress); + }); + describe("#nameOfWhatYouAreTesting", () => { + it ("should ", () => { + // Your code here + }); + }); + */ +}); + diff --git a/test/routes/databaseRouterTest.ts b/test/routes/databaseRouterTest.ts new file mode 100644 index 0000000..38f1e95 --- /dev/null +++ b/test/routes/databaseRouterTest.ts @@ -0,0 +1,37 @@ +import * as DatabaseRouter from "../../src/routes/databaseRouter"; +import * as express from "express"; +import * as Chai from "chai"; + +/** + * This is the test for DatabaseRouter class + * + * @history + * | Author | Action Performed | Data | + * | --- | --- | --- | + * | Davide Polonio | Create class | 03/05/2016 | + * + * @author Davide Polonio + * @copyright MIT + * + * Created by Davide Polonio on 03/05/16. + */ +describe("DatabaseRouter", () => { + + /* nothing to test for now + let toTest : DatabaseRouter; + let dummyExpress : express.Express = express(); + + beforeEach(function () : void { + + toTest = new DatabaseRouter(dummyExpress); + }); + + describe("#nameOfWhatYouAreTesting", () => { + it ("should ", () => { + + // Your code here + }); + + }); + */ +}); diff --git a/test/routes/dslRouterTest.ts b/test/routes/dslRouterTest.ts new file mode 100644 index 0000000..adff1aa --- /dev/null +++ b/test/routes/dslRouterTest.ts @@ -0,0 +1,32 @@ +import * as DSLRouter from "../../src/routes/dslRouter"; +import * as express from "express"; +import * as Chai from "chai"; + +/** + * This is the test for DatabaseRouter class + * + * @history + * | Author | Action Performed | Data | + * | --- | --- | --- | + * | Emanuele Carraro | Create class DSLRouter | 10/05/2016 | + * + * @author Emanuele Carraro + * @copyright MIT + * + */ +describe("DSLRouter", () => { + + /* nothing to test for now + let toTest : DSLRouter; + let dummyExpress : express.Express = express(); + beforeEach(function () : void { + toTest = new DSLRouter(dummyExpress); + }); + describe("#nameOfWhatYouAreTesting", () => { + it ("should ", () => { + // Your code here + }); + }); + */ + +}); diff --git a/tslint.json b/tslint.json index 06aca6e..fee8e7d 100644 --- a/tslint.json +++ b/tslint.json @@ -44,10 +44,10 @@ "no-eval": true, "no-null-keyword": true, "no-shadowed-variable": false, - "no-string-literal": true, + "no-string-literal": false, "no-switch-case-fall-through": true, "no-unreachable": true, - "no-unused-expression": true, + //"no-unused-expression": true, "no-unused-variable": false, "no-use-before-declare": true, "no-var-keyword": true, diff --git a/typings.json b/typings.json index e02719f..05f5d58 100644 --- a/typings.json +++ b/typings.json @@ -1,8 +1,13 @@ { "ambientDependencies": { + "body-parser": "registry:dt/body-parser#0.0.0+20160317120654", "chai": "registry:dt/chai#3.4.0+20160317120654", + "dslEngine": "github:BugBusterSWE/DSLEngine/src/dslEngine.d.ts", + "es6-promise": "registry:dt/es6-promise#0.0.0+20160423074304", "express": "registry:dt/express#4.0.0+20160317120654", "express-serve-static-core": "registry:dt/express-serve-static-core#0.0.0+20160322035842", + "jsonwebtoken": "registry:dt/jsonwebtoken#0.0.0+20160505173617", + "helmet": "registry:dt/helmet#0.0.0+20160501161936", "mime": "registry:dt/mime#0.0.0+20160316155526", "mocha": "registry:dt/mocha#2.2.5+20160317120654", "mongoose": "registry:dt/mongoose#3.8.5+20160505185535",