diff --git a/backend/middlewares/AuthenticationMWs.ts b/backend/middlewares/AuthenticationMWs.ts new file mode 100644 index 00000000..18002990 --- /dev/null +++ b/backend/middlewares/AuthenticationMWs.ts @@ -0,0 +1,70 @@ + +import {UserManager} from "../model/UserManager"; +import {NextFunction, Request, Response} from "express"; +import {BaseMWs} from "./BaseMWs"; +import {Error, ErrorCodes} from "../../common/entities/Error"; +import {UserRoles} from "../../common/entities/User"; +import {Utils} from "../../common/Utils"; + +export class AuthenticationMWs extends BaseMWs{ + + + public static authenticate(req:Request, res:Response, next:NextFunction){ + if (typeof req.session.user === 'undefined') { + return super.renderError(res,new Error(ErrorCodes.NOT_AUTHENTICATED)); + } + return next(); + } + + public static authorise(role:UserRoles){ + return (req:Request, res:Response, next:NextFunction) => { + if (req.session.user.role < role) { + return super.renderError(res, new Error(ErrorCodes.NOT_AUTHORISED)); + } + return next(); + }; + } + + public static inverseAuthenticate(req:Request, res:Response, next:NextFunction){ + if (typeof req.session.user !== 'undefined') { + return super.renderError(res,new Error(ErrorCodes.ALREADY_AUTHENTICATED)); + } + return next(); + } + + public static login(req:Request, res:Response, next:NextFunction){ + //not enough parameter + if ((typeof req.body === 'undefined') || (typeof req.body.logincredential === 'undefined') || (typeof req.body.logincredential.username === 'undefined') || + (typeof req.body.logincredential.password === 'undefined')) { + return next(); + } + + //lets find the user + UserManager.findOne({ + username: req.body.logincredential.username + }, (err, result) => { + if ((err) || (!result)) { + return super.renderError(res,new Error(ErrorCodes.CREDENTIAL_NOT_FOUND)); + } + + //check password + if (result.password !== req.body.logincredential.password) { + return super.renderError(res,new Error(ErrorCodes.CREDENTIAL_NOT_FOUND)); + } + + req.session.user = result; + + return next(); + }); + } + + + + public static renderUser(req:Request, res:Response, next:NextFunction){ + let user = Utils.clone(req.session.user); + delete user.password; + super.renderMessage(res,user); + } + + +} \ No newline at end of file diff --git a/backend/middlewares/BaseMWs.ts b/backend/middlewares/BaseMWs.ts index 164e7262..362b33eb 100644 --- a/backend/middlewares/BaseMWs.ts +++ b/backend/middlewares/BaseMWs.ts @@ -6,11 +6,16 @@ import {Error} from "../../common/entities/Error"; export class BaseMWs { protected static renderMessage(res:Response, content:T){ - let message = new Message ([],content); + let message = new Message (null,content); res.json(message); } protected static renderError(res:Response, error:Error){ - let message = new Message ([],null); + let message = new Message (error,null); res.json(message); } + + public static renderOK(req:Request, res:Response, next:NextFunction){ + let message = new Message (null,"ok"); + res.json(message); + } } \ No newline at end of file diff --git a/backend/middlewares/UserMWs.ts b/backend/middlewares/UserMWs.ts index 65467149..14fad145 100644 --- a/backend/middlewares/UserMWs.ts +++ b/backend/middlewares/UserMWs.ts @@ -3,61 +3,90 @@ import {UserManager} from "../model/UserManager"; import {NextFunction, Request, Response} from "express"; import {BaseMWs} from "./BaseMWs"; import {Error, ErrorCodes} from "../../common/entities/Error"; +import Util = jasmine.Util; export class UserMWs extends BaseMWs{ - - public static authenticate(req:Request, res:Response, next:NextFunction){ - if (typeof req.session.user === 'undefined') { - return super.renderError(res,new Error(ErrorCodes.NOT_AUTHENTICATED)); - } - return next(); - } - - public static inverseAuthenticate(req:Request, res:Response, next:NextFunction){ - if (typeof req.session.user !== 'undefined') { - return super.renderError(res,new Error(ErrorCodes.ALREADY_AUTHENTICATED)); - } - return next(); - } - - public static login(req:Request, res:Response, next:NextFunction){ - //not enough parameter - /* if ((typeof req.body === 'undefined') || (typeof req.body.email === 'undefined') || - (typeof req.body.password === 'undefined')) { + public static changePassword(req:Request, res:Response, next:NextFunction){ + if ((typeof req.body === 'undefined') || (typeof req.body.userModReq === 'undefined') + || (typeof req.body.userModReq.id === 'undefined') + || (typeof req.body.userModReq.oldPassword === 'undefined') + || (typeof req.body.userModReq.newPassword === 'undefined')) { return next(); - }*/ - - //lets find the user - UserManager.findOne({ - // email: req.body.email - }, function (err, result) { + } + + UserManager.changePassword(req.body.userModReq, (err, result) =>{ if ((err) || (!result)) { - // res.tpl.error.push('Your email address is not registered!'); - console.log(err); - return next(); + return super.renderError(res,new Error(ErrorCodes.GENERAL_ERROR)); } - /* //check password - if (result.password !== req.body.password) { - // res.tpl.error.push('Wrong password!'); - return next(); - } -*/ - //login is ok, save id to session - req.session.user = result; + return next(); + }); + } + - //redirect to / so the app can decide where to go next - // return res.redirect('/'); + public static createUser(req:Request, res:Response, next:NextFunction){ + if ((typeof req.body === 'undefined') || (typeof req.body.newUser === 'undefined')) { + return next(); + } + + UserManager.createUser(req.body.newUser, (err, result) =>{ + if ((err) || (!result)) { + return super.renderError(res,new Error(ErrorCodes.USER_CREATION_ERROR)); + } + + + return next(); + }); + + } + + public static deleteUser(req:Request, res:Response, next:NextFunction){ + if ((typeof req.body === 'undefined') || (typeof req.body.newUser === 'undefined') + || (typeof req.body.userModReq.id === 'undefined')) { + return next(); + } + + UserManager.deleteUser(req.body.userModReq.id, (err, result) =>{ + if ((err) || (!result)) { + return super.renderError(res,new Error(ErrorCodes.GENERAL_ERROR)); + } + + + return next(); + }); + + } + + public static changeRole(req:Request, res:Response, next:NextFunction){ + if ((typeof req.body === 'undefined') || (typeof req.body.userModReq === 'undefined') + || (typeof req.body.userModReq.id === 'undefined') + || (typeof req.body.userModReq.newRole === 'undefined')) { + return next(); + } + + UserManager.changeRole(req.body.userModReq, (err, result) =>{ + if ((err) || (!result)) { + return super.renderError(res,new Error(ErrorCodes.GENERAL_ERROR)); + } return next(); }); } - public static renderUser(req:Request, res:Response, next:NextFunction){ - super.renderMessage(res,req.session.user); + public static listUsers(req:Request, res:Response, next:NextFunction){ + UserManager.find({}, (err, result) =>{ + if ((err) || (!result)) { + return super.renderError(res,new Error(ErrorCodes.GENERAL_ERROR)); + } + + + super.renderMessage(res,result); + }); } + + } \ No newline at end of file diff --git a/backend/middlewares/UserRequestConstrainsMWs.ts b/backend/middlewares/UserRequestConstrainsMWs.ts new file mode 100644 index 00000000..fcbd288a --- /dev/null +++ b/backend/middlewares/UserRequestConstrainsMWs.ts @@ -0,0 +1,59 @@ + +import {UserManager} from "../model/UserManager"; +import {NextFunction, Request, Response} from "express"; +import {BaseMWs} from "./BaseMWs"; +import {Error, ErrorCodes} from "../../common/entities/Error"; +import {UserRoles} from "../../common/entities/User"; + +export class UserRequestConstrainsMWs extends BaseMWs{ + + + public static forceSelfRequest(req:Request, res:Response, next:NextFunction){ + if ((typeof req.params === 'undefined') || (typeof req.params.id === 'undefined')) { + return next(); + } + if(req.session.user.id !== req.params.id){ + return super.renderError(res,new Error(ErrorCodes.NOT_AUTHORISED)); + } + + return next(); + } + + + public static notSelfRequest(req:Request, res:Response, next:NextFunction){ + if ((typeof req.params === 'undefined') || (typeof req.params.id === 'undefined')) { + return next(); + } + + if(req.session.user.id === req.params.id){ + return super.renderError(res,new Error(ErrorCodes.NOT_AUTHORISED)); + } + + return next(); + } + + public static notSelfRequestOr2Admins(req:Request, res:Response, next:NextFunction){ + if ((typeof req.params === 'undefined') || (typeof req.params.id === 'undefined')) { + return next(); + } + + if(req.session.user.id !== req.params.id){ + return next(); + } + + UserManager.find({minRole:UserRoles.Admin}, (err, result) =>{ + if ((err) || (!result)) { + return super.renderError(res,new Error(ErrorCodes.GENERAL_ERROR)); + } + if(result.length <= 1) { + return super.renderError(res, new Error(ErrorCodes.GENERAL_ERROR)); + } + + }); + + return next(); + } + + + +} \ No newline at end of file diff --git a/backend/model/UserManager.ts b/backend/model/UserManager.ts index 2330453a..0ef077bd 100644 --- a/backend/model/UserManager.ts +++ b/backend/model/UserManager.ts @@ -1,10 +1,31 @@ import {User} from "../../common/entities/User"; export class UserManager { - private static DummyUser = new User("TestUser","test@test.hu","122345"); + private static users = [new User(1,"TestUser","test@test.hu","122345")]; public static findOne(filter,cb:(error: any,result:User) => void){ - return cb(null, UserManager.DummyUser); + return cb(null, UserManager.users[0]); + } + + public static find(filter,cb:(error: any,result:Array) => void){ + return cb(null, UserManager.users); + } + + public static createUser(user,cb:(error: any,result:User) => void){ + UserManager.users.push(user); + return cb(null, user); + } + + public static deleteUser(id:number,cb:(error: any,result:string) => void){ + UserManager.users = UserManager.users.filter(u => u.id != id); + return cb(null, "ok"); + } + + public static changeRole(request:any,cb:(error: any,result:string) => void){ + return cb(null,"ok"); + } + public static changePassword(request:any,cb:(error: any,result:string) => void){ + return cb(null,"ok"); } } \ No newline at end of file diff --git a/backend/routes/AdminRouter.ts b/backend/routes/AdminRouter.ts new file mode 100644 index 00000000..7c20135f --- /dev/null +++ b/backend/routes/AdminRouter.ts @@ -0,0 +1,33 @@ +/// + +import {AuthenticationMWs} from "../middlewares/AuthenticationMWs"; +import {UserRoles} from "../../common/entities/User"; + +export class AdminRouter{ + constructor(private app) { + + this.addResetDB(); + this.addIndexGalery(); + } + + private addResetDB() { + this.app.post("/api/admin/db/reset", + AuthenticationMWs.authenticate, + AuthenticationMWs.authorise(UserRoles.Admin) + //TODO: implement + ); + }; + + private addIndexGalery() { + this.app.post("/api/admin/gallery/index", + AuthenticationMWs.authenticate, + AuthenticationMWs.authorise(UserRoles.Admin) + //TODO: implement + ); + }; + + + + + +} \ No newline at end of file diff --git a/backend/routes/GalleryRouter.ts b/backend/routes/GalleryRouter.ts new file mode 100644 index 00000000..bd0310d6 --- /dev/null +++ b/backend/routes/GalleryRouter.ts @@ -0,0 +1,54 @@ +/// + +import {AuthenticationMWs} from "../middlewares/AuthenticationMWs"; + +export class GalleryRouter{ + constructor(private app){ + + this.addDirectoryList(); + this.addGetImageThumbnail(); + this.addGetImage(); + + this.addSearch(); + this.addAutoComplete(); + } + + private addDirectoryList() { + this.app.get("/api/gallery/:directory", + AuthenticationMWs.authenticate + //TODO: implement + ); + }; + + + private addGetImage() { + this.app.get("/api/gallery/:directory/:image", + AuthenticationMWs.authenticate + //TODO: implement + ); + }; + + private addGetImageThumbnail() { + this.app.get("/api/gallery/:directory/:image/thumbnail", + AuthenticationMWs.authenticate + //TODO: implement + ); + }; + + private addSearch() { + this.app.get("/api/gallery/search", + AuthenticationMWs.authenticate + //TODO: implement + ); + }; + + private addAutoComplete() { + this.app.get("/api/gallery/autocomplete", + AuthenticationMWs.authenticate + //TODO: implement + ); + }; + + + +} \ No newline at end of file diff --git a/backend/routes/PublicRouter.ts b/backend/routes/PublicRouter.ts index d84847e5..74b4ec17 100644 --- a/backend/routes/PublicRouter.ts +++ b/backend/routes/PublicRouter.ts @@ -2,12 +2,18 @@ import * as _express from 'express'; -import * as path from 'path'; +import * as _path from 'path'; export class PublicRouter{ constructor(private app){ - this.app.use(_express.static(path.resolve(__dirname, './../../frontend'))); - this.app.use('/node_modules',_express.static(path.resolve(__dirname, './../../node_modules'))); + this.app.use(_express.static(_path.resolve(__dirname, './../../frontend'))); + this.app.use('/node_modules',_express.static(_path.resolve(__dirname, './../../node_modules'))); + + var renderIndex = (req: _express.Request, res: _express.Response) => { + res.sendFile(_path.resolve(__dirname, './../frontend/index.html')); + }; + this.app.get(['/login',"/gallery"], renderIndex); + } diff --git a/backend/routes/SharingRouter.ts b/backend/routes/SharingRouter.ts new file mode 100644 index 00000000..cf774d0c --- /dev/null +++ b/backend/routes/SharingRouter.ts @@ -0,0 +1,33 @@ +/// + +import {AuthenticationMWs} from "../middlewares/AuthenticationMWs"; +import {UserRoles} from "../../common/entities/User"; + +export class AdminRouter{ + constructor(private app) { + + this.addGetSharing(); + this.addUpdateSharing(); + } + + private addGetSharing() { + this.app.get("/api/share/:directory", + AuthenticationMWs.authenticate, + AuthenticationMWs.authorise(UserRoles.User) + //TODO: implement + ); + }; + + private addUpdateSharing() { + this.app.update("/api/share/:directory", + AuthenticationMWs.authenticate, + AuthenticationMWs.authorise(UserRoles.User) + //TODO: implement + ); + }; + + + + + +} \ No newline at end of file diff --git a/backend/routes/UserRouter.ts b/backend/routes/UserRouter.ts index 2f01a093..82cadbad 100644 --- a/backend/routes/UserRouter.ts +++ b/backend/routes/UserRouter.ts @@ -1,19 +1,78 @@ /// - - import {UserMWs} from "../middlewares/UserMWs"; +import {UserRoles} from "../../common/entities/User"; +import {AuthenticationMWs} from "../middlewares/AuthenticationMWs"; +import {UserRequestConstrainsMWs} from "../middlewares/UserRequestConstrainsMWs"; + export class UserRouter{ constructor(private app){ - this.initLogin(); + this.addLogin(); + this.addChangePassword(); + + + this.addCreateUser(); + this.addDeleteUser(); + this.addListUsers(); + this.addChangeRole(); } - private initLogin() { - this.app.post("/api/login", - UserMWs.inverseAuthenticate, - UserMWs.login, - UserMWs.renderUser + private addLogin() { + this.app.post("/api/user/login", + AuthenticationMWs.inverseAuthenticate, + AuthenticationMWs.login, + AuthenticationMWs.renderUser ); }; - + + + private addChangePassword() { + this.app.update("/api/user/:id/password", + AuthenticationMWs.authenticate, + UserRequestConstrainsMWs.forceSelfRequest, + UserMWs.changePassword, + UserMWs.renderOK + ); + }; + + + private addCreateUser() { + this.app.put("/api/user", + AuthenticationMWs.authenticate, + AuthenticationMWs.authorise(UserRoles.Admin), + UserMWs.createUser, + UserMWs.renderOK + ); + }; + + private addDeleteUser() { + this.app.delete("/api/user/:id", + AuthenticationMWs.authenticate, + AuthenticationMWs.authorise(UserRoles.Admin), + UserRequestConstrainsMWs.notSelfRequest, + UserMWs.deleteUser, + UserMWs.renderOK + ); + }; + + + private addListUsers() { + this.app.post("/api/user/list", + AuthenticationMWs.authenticate, + AuthenticationMWs.authorise(UserRoles.Admin), + UserMWs.listUsers + ); + }; + + private addChangeRole() { + this.app.update("/api/user/:id/role", + AuthenticationMWs.authenticate, + AuthenticationMWs.authorise(UserRoles.Admin), + UserRequestConstrainsMWs.notSelfRequestOr2Admins, + UserMWs.changeRole, + UserMWs.renderOK + ); + }; + + } \ No newline at end of file diff --git a/backend/server.ts b/backend/server.ts index bdccc042..f96ec1aa 100644 --- a/backend/server.ts +++ b/backend/server.ts @@ -4,9 +4,10 @@ import * as _express from 'express'; import * as _session from 'express-session'; import * as _debug from 'debug'; import * as _http from 'http'; -import * as _path from 'path'; import {PublicRouter} from "./routes/PublicRouter"; import {UserRouter} from "./routes/UserRouter"; +import {GalleryRouter} from "./routes/GalleryRouter"; +import {AdminRouter} from "./routes/AdminRouter"; export class Server { @@ -42,12 +43,10 @@ export class Server { new PublicRouter(this.app); new UserRouter(this.app); + new GalleryRouter(this.app); + new AdminRouter(this.app); - var renderIndex = (req: _express.Request, res: _express.Response) => { - res.sendFile(_path.resolve(__dirname, './../frontend/index.html')); - }; - this.app.get(['/login',"/gallery"], renderIndex); - + // Get port from environment and store in Express. diff --git a/common/entities/Error.ts b/common/entities/Error.ts index ed348760..fef54229 100644 --- a/common/entities/Error.ts +++ b/common/entities/Error.ts @@ -1,7 +1,16 @@ export enum ErrorCodes{ NOT_AUTHENTICATED, - ALREADY_AUTHENTICATED + ALREADY_AUTHENTICATED, + NOT_AUTHORISED, + CREDENTIAL_NOT_FOUND, + + + USER_CREATION_ERROR, + + + GENERAL_ERROR + } export class Error{ diff --git a/common/entities/Message.ts b/common/entities/Message.ts index a1ecc184..b8743bc4 100644 --- a/common/entities/Message.ts +++ b/common/entities/Message.ts @@ -1,5 +1,5 @@ import {Error} from "./Error"; export class Message{ - constructor(public errors:Array,public result:T){} + constructor(public errors:Error,public result:T){} } \ No newline at end of file diff --git a/common/entities/PasswordChangeRequest.ts b/common/entities/PasswordChangeRequest.ts new file mode 100644 index 00000000..dfaa0bf7 --- /dev/null +++ b/common/entities/PasswordChangeRequest.ts @@ -0,0 +1,8 @@ +import {UserModificationRequest} from "./UserModificationRequest"; + +export class PasswordChangeRequest extends UserModificationRequest { + + constructor(id:number, public oldPassword:string, public newPassword:string) { + super(id); + } +} \ No newline at end of file diff --git a/common/entities/User.ts b/common/entities/User.ts index 996e33a5..dc93c4e0 100644 --- a/common/entities/User.ts +++ b/common/entities/User.ts @@ -1,3 +1,11 @@ + +export enum UserRoles{ + Guest = 1, + User = 2, + Admin = 3, + Developer = 4 +} + export class User { - constructor(public name?:string,public email?:string, public password?:string){} + constructor(public id?:number,public name?:string,public email?:string, public password?:string, public role?:UserRoles){} } \ No newline at end of file diff --git a/common/entities/UserModificationRequest.ts b/common/entities/UserModificationRequest.ts new file mode 100644 index 00000000..5582b69f --- /dev/null +++ b/common/entities/UserModificationRequest.ts @@ -0,0 +1,3 @@ +export class UserModificationRequest{ + constructor(public id:number){} +} diff --git a/frontend/app/model/network.service.ts b/frontend/app/model/network.service.ts index 996fa2c4..acd29bcf 100644 --- a/frontend/app/model/network.service.ts +++ b/frontend/app/model/network.service.ts @@ -11,19 +11,36 @@ export class NetworkService{ constructor(protected _http:Http){ } - - - protected postJson(url:string, data:any = {}){ + private callJson(method:string, url:string, data:any = {}){ let body = JSON.stringify({ data }); let headers = new Headers({ 'Content-Type': 'application/json' }); let options = new RequestOptions({ headers: headers }); console.log(this._http.post(this._baseUrl+url, body, options)); - return this._http.post(this._baseUrl+url, body, options) + return this._http['method'](this._baseUrl+url, body, options) .toPromise() .then(res => > res.json()) .catch(NetworkService.handleError); } + protected postJson(url:string, data:any = {}){ + return this.callJson("post",url,data); + } + + protected putJson(url:string, data:any = {}){ + return this.callJson("put",url,data); + } + protected getJson(url:string, data:any = {}){ + return this.callJson("get",url,data); + } + + protected updateJson(url:string, data:any = {}){ + return this.callJson("update",url,data); + } + + protected deleteJson(url:string, data:any = {}){ + return this.callJson("delete",url,data); + } + private static handleError (error: any) { // in a real world app, we may send the error to some remote logging infrastructure // instead of just logging it to the console diff --git a/frontend/app/model/user.service.ts b/frontend/app/model/user.service.ts index 49f660dc..164e1e2b 100644 --- a/frontend/app/model/user.service.ts +++ b/frontend/app/model/user.service.ts @@ -16,7 +16,7 @@ export class UserService extends NetworkService{ public login(credential:LoginCredential){ - return this.postJson("/login",credential); + return this.postJson("/user/login",credential); }