diff --git a/client/app/app.component.html b/client/app/app.component.html index 049a7757..eecc43d7 100644 --- a/client/app/app.component.html +++ b/client/app/app.component.html @@ -5,22 +5,22 @@ Home - + Cats - + Login - + Register - + Account ({{auth.currentUser.username}}) - + Admin - + Logout diff --git a/client/app/app.module.ts b/client/app/app.module.ts index a0ccfb9f..9384aa69 100644 --- a/client/app/app.module.ts +++ b/client/app/app.module.ts @@ -1,12 +1,10 @@ import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { RoutingModule } from './routing.module'; +import { AuthModule } from './auth.module'; import { SharedModule } from './shared/shared.module'; import { CatService } from './services/cat.service'; import { UserService } from './services/user.service'; -import { AuthService } from './services/auth.service'; -import { AuthGuardLogin } from './services/auth-guard-login.service'; -import { AuthGuardAdmin } from './services/auth-guard-admin.service'; import { AppComponent } from './app.component'; import { CatsComponent } from './cats/cats.component'; import { AboutComponent } from './about/about.component'; @@ -30,13 +28,11 @@ import { NotFoundComponent } from './not-found/not-found.component'; NotFoundComponent ], imports: [ + AuthModule, RoutingModule, SharedModule ], providers: [ - AuthService, - AuthGuardLogin, - AuthGuardAdmin, CatService, UserService ], diff --git a/client/app/auth.module.ts b/client/app/auth.module.ts new file mode 100644 index 00000000..c4cccfd2 --- /dev/null +++ b/client/app/auth.module.ts @@ -0,0 +1,30 @@ +import { NgModule } from '@angular/core'; +import { Http, RequestOptions } from '@angular/http'; +import { AuthHttp, AuthConfig } from 'angular2-jwt'; + +import { AuthService } from './services/auth.service'; +import { AuthGuardLogin } from './services/auth-guard-login.service'; +import { AuthGuardAdmin } from './services/auth-guard-admin.service'; + +export function authHttpServiceFactory(http: Http, options: RequestOptions) { + return new AuthHttp(new AuthConfig({ + tokenName: 'token', + tokenGetter: (() => localStorage.getItem('token')), + globalHeaders: [{'Content-Type': 'application/json'}], + }), http, options); +} + +@NgModule({ + providers: [ + AuthService, + AuthGuardLogin, + AuthGuardAdmin, + { + provide: AuthHttp, + useFactory: authHttpServiceFactory, + deps: [Http, RequestOptions] + } + ] +}) + +export class AuthModule { } diff --git a/client/app/login/login.component.ts b/client/app/login/login.component.ts index 414a270f..ef841fe5 100644 --- a/client/app/login/login.component.ts +++ b/client/app/login/login.component.ts @@ -28,7 +28,7 @@ export class LoginComponent implements OnInit { public toast: ToastComponent) { } ngOnInit() { - if (this.auth.loggedIn) { + if (this.auth.loggedIn()) { this.router.navigate(['/']); } this.loginForm = this.formBuilder.group({ diff --git a/client/app/services/auth-guard-login.service.ts b/client/app/services/auth-guard-login.service.ts index e42e78e5..0cf05c4e 100644 --- a/client/app/services/auth-guard-login.service.ts +++ b/client/app/services/auth-guard-login.service.ts @@ -8,7 +8,11 @@ export class AuthGuardLogin implements CanActivate { constructor(public auth: AuthService, private router: Router) {} canActivate() { - return this.auth.loggedIn; + if (this.auth.loggedIn()) { + return true; + } else { + return false; + } } } diff --git a/client/app/services/auth.service.ts b/client/app/services/auth.service.ts index c2a33a2f..28626ed6 100644 --- a/client/app/services/auth.service.ts +++ b/client/app/services/auth.service.ts @@ -1,12 +1,11 @@ import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; -import { JwtHelper } from 'angular2-jwt'; +import { JwtHelper, tokenNotExpired } from 'angular2-jwt'; import { UserService } from '../services/user.service'; @Injectable() export class AuthService { - loggedIn = false; isAdmin = false; jwtHelper: JwtHelper = new JwtHelper(); @@ -28,14 +27,13 @@ export class AuthService { localStorage.setItem('token', res.token); const decodedUser = this.decodeUserFromToken(res.token); this.setCurrentUser(decodedUser); - return this.loggedIn; + return this.loggedIn(); } ); } logout() { localStorage.removeItem('token'); - this.loggedIn = false; this.isAdmin = false; this.currentUser = { _id: '', username: '', role: '' }; this.router.navigate(['/']); @@ -46,7 +44,6 @@ export class AuthService { } setCurrentUser(decodedUser) { - this.loggedIn = true; this.currentUser._id = decodedUser._id; this.currentUser.username = decodedUser.username; this.currentUser.role = decodedUser.role; @@ -54,4 +51,8 @@ export class AuthService { delete decodedUser.role; } + loggedIn() { + return tokenNotExpired(); + } + } diff --git a/client/app/services/cat.service.ts b/client/app/services/cat.service.ts index 0179611e..8263709a 100644 --- a/client/app/services/cat.service.ts +++ b/client/app/services/cat.service.ts @@ -1,5 +1,6 @@ import { Injectable } from '@angular/core'; import { Http, Headers, RequestOptions } from '@angular/http'; +import { AuthHttp } from 'angular2-jwt'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; @@ -7,33 +8,30 @@ import 'rxjs/add/operator/map'; @Injectable() export class CatService { - private headers = new Headers({ 'Content-Type': 'application/json', 'charset': 'UTF-8' }); - private options = new RequestOptions({ headers: this.headers }); - - constructor(private http: Http) { } + constructor(public authHttp: AuthHttp) { } getCats(): Observable { - return this.http.get('/api/cats').map(res => res.json()); + return this.authHttp.get('/api/cats').map(res => res.json()); } countCats(): Observable { - return this.http.get('/api/cats/count').map(res => res.json()); + return this.authHttp.get('/api/cats/count').map(res => res.json()); } addCat(cat): Observable { - return this.http.post('/api/cat', JSON.stringify(cat), this.options); + return this.authHttp.post('/api/cat', JSON.stringify(cat)); } getCat(cat): Observable { - return this.http.get(`/api/cat/${cat._id}`).map(res => res.json()); + return this.authHttp.get(`/api/cat/${cat._id}`).map(res => res.json()); } editCat(cat): Observable { - return this.http.put(`/api/cat/${cat._id}`, JSON.stringify(cat), this.options); + return this.authHttp.put(`/api/cat/${cat._id}`, JSON.stringify(cat)); } deleteCat(cat): Observable { - return this.http.delete(`/api/cat/${cat._id}`, this.options); + return this.authHttp.delete(`/api/cat/${cat._id}`); } } diff --git a/client/app/services/user.service.ts b/client/app/services/user.service.ts index e179c0b8..8de03239 100644 --- a/client/app/services/user.service.ts +++ b/client/app/services/user.service.ts @@ -1,5 +1,6 @@ import { Injectable } from '@angular/core'; import { Http, Headers, RequestOptions } from '@angular/http'; +import { AuthHttp } from 'angular2-jwt'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; @@ -10,7 +11,8 @@ export class UserService { private headers = new Headers({ 'Content-Type': 'application/json', 'charset': 'UTF-8' }); private options = new RequestOptions({ headers: this.headers }); - constructor(private http: Http) { } + constructor(private http: Http, + public authHttp: AuthHttp) { } register(user): Observable { return this.http.post('/api/user', JSON.stringify(user), this.options); @@ -21,27 +23,27 @@ export class UserService { } getUsers(): Observable { - return this.http.get('/api/users').map(res => res.json()); + return this.authHttp.get('/api/users').map(res => res.json()); } countUsers(): Observable { - return this.http.get('/api/users/count').map(res => res.json()); + return this.authHttp.get('/api/users/count').map(res => res.json()); } addUser(user): Observable { - return this.http.post('/api/user', JSON.stringify(user), this.options); + return this.authHttp.post('/api/user', JSON.stringify(user)); } getUser(user): Observable { - return this.http.get(`/api/user/${user._id}`).map(res => res.json()); + return this.authHttp.get(`/api/user/${user._id}`).map(res => res.json()); } editUser(user): Observable { - return this.http.put(`/api/user/${user._id}`, JSON.stringify(user), this.options); + return this.authHttp.put(`/api/user/${user._id}`, JSON.stringify(user)); } deleteUser(user): Observable { - return this.http.delete(`/api/user/${user._id}`, this.options); + return this.authHttp.delete(`/api/user/${user._id}`); } } diff --git a/package.json b/package.json index afa0d6ef..337a1fe1 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "core-js": "^2.5.1", "dotenv": "^4.0.0", "express": "^4.16.2", + "express-jwt": "^5.3.0", "font-awesome": "^4.7.0", "jquery": "^3.2.1", "jsonwebtoken": "^8.1.0", diff --git a/server/controllers/base.ts b/server/controllers/base.ts index 04d8656d..623be379 100644 --- a/server/controllers/base.ts +++ b/server/controllers/base.ts @@ -4,58 +4,95 @@ abstract class BaseCtrl { // Get all getAll = (req, res) => { - this.model.find({}, (err, docs) => { - if (err) { return console.error(err); } - res.json(docs); - }); - } + if (!req.payload.user._id) { + res.status(401).json({ + 'message' : 'UnauthorizedError: private' + }); + } else { + this.model.find({}, (err, items) => { + if (err) { return console.error(err); } + res.json(items); + }); + } + }; // Count all count = (req, res) => { - this.model.count((err, count) => { - if (err) { return console.error(err); } - res.json(count); - }); - } + if (!req.payload.user._id) { + res.status(401).json({ + 'message' : 'UnauthorizedError: private' + }); + } else { + this.model.count((err, count) => { + if (err) { return console.error(err); } + res.json(count); + }); + } + }; // Insert insert = (req, res) => { - const obj = new this.model(req.body); - obj.save((err, item) => { - // 11000 is the code for duplicate key error - if (err && err.code === 11000) { - res.sendStatus(400); - } - if (err) { - return console.error(err); - } - res.status(200).json(item); - }); - } + if (!req.payload.user._id) { + res.status(401).json({ + 'message' : 'UnauthorizedError: private' + }); + } else { + const obj = new this.model(req.body); + obj.save((err, item) => { + // 11000 is the code for duplicate key error + if (err && err.code === 11000) { + res.sendStatus(400); + } + if (err) { + return console.error(err); + } + res.status(200).json(item); + }); + } + }; // Get by id get = (req, res) => { - this.model.findOne({ _id: req.params.id }, (err, obj) => { - if (err) { return console.error(err); } - res.json(obj); - }); - } + if (!req.payload.user._id) { + res.status(401).json({ + 'message' : 'UnauthorizedError: private' + }); + } else { + this.model.findOne({ _id: req.params.id }, (err, obj) => { + if (err) { return console.error(err); } + res.json(obj); + }); + } + }; // Update by id update = (req, res) => { - this.model.findOneAndUpdate({ _id: req.params.id }, req.body, (err) => { - if (err) { return console.error(err); } - res.sendStatus(200); - }); - } + if (!req.payload.user._id) { + res.status(401).json({ + 'message' : 'UnauthorizedError: private' + }); + } else { + this.model.findOneAndUpdate({ _id: req.params.id }, req.body, (err) => { + if (err) { return console.error(err); } + res.sendStatus(200); + }); + } + }; // Delete by id delete = (req, res) => { - this.model.findOneAndRemove({ _id: req.params.id }, (err) => { - if (err) { return console.error(err); } - res.sendStatus(200); - }); - } + if (!req.payload.user._id) { + res.status(401).json({ + 'message' : 'UnauthorizedError: private' + }); + } else { + this.model.findOneAndRemove({ _id: req.params.id }, (err) => { + if (err) { return console.error(err); } + res.sendStatus(200); + }); + } + }; + } export default BaseCtrl; diff --git a/server/controllers/user.ts b/server/controllers/user.ts index 4bbc7574..21179c49 100644 --- a/server/controllers/user.ts +++ b/server/controllers/user.ts @@ -7,6 +7,20 @@ import BaseCtrl from './base'; export default class UserCtrl extends BaseCtrl { model = User; + register = (req, res) => { + const obj = new this.model(req.body); + obj.save((err, item) => { + // 11000 is the code for duplicate key error + if (err && err.code === 11000) { + res.sendStatus(400); + } + if (err) { + return console.error(err); + } + res.status(200).json(item); + }); + }; + login = (req, res) => { this.model.findOne({ email: req.body.email }, (err, user) => { if (!user) { return res.sendStatus(403); } diff --git a/server/routes.ts b/server/routes.ts index 1ec8093e..4f0acc78 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -1,4 +1,6 @@ import * as express from 'express'; +import * as jwt from 'express-jwt'; +import * as dotenv from 'dotenv'; import CatCtrl from './controllers/cat'; import UserCtrl from './controllers/user'; @@ -8,26 +10,30 @@ import User from './models/user'; export default function setRoutes(app) { const router = express.Router(); + const auth = jwt({ + secret: process.env.SECRET_TOKEN, + userProperty: 'payload' + }); const catCtrl = new CatCtrl(); const userCtrl = new UserCtrl(); // Cats - router.route('/cats').get(catCtrl.getAll); - router.route('/cats/count').get(catCtrl.count); - router.route('/cat').post(catCtrl.insert); - router.route('/cat/:id').get(catCtrl.get); - router.route('/cat/:id').put(catCtrl.update); - router.route('/cat/:id').delete(catCtrl.delete); + router.route('/cats').get(auth, catCtrl.getAll); + router.route('/cats/count').get(auth, catCtrl.count); + router.route('/cat').post(auth, catCtrl.insert); + router.route('/cat/:id').get(auth, catCtrl.get); + router.route('/cat/:id').put(auth, catCtrl.update); + router.route('/cat/:id').delete(auth, catCtrl.delete); // Users router.route('/login').post(userCtrl.login); - router.route('/users').get(userCtrl.getAll); - router.route('/users/count').get(userCtrl.count); - router.route('/user').post(userCtrl.insert); - router.route('/user/:id').get(userCtrl.get); - router.route('/user/:id').put(userCtrl.update); - router.route('/user/:id').delete(userCtrl.delete); + router.route('/users').get(auth, userCtrl.getAll); + router.route('/users/count').get(auth, userCtrl.count); + router.route('/user').post(userCtrl.register); + router.route('/user/:id').get(auth, userCtrl.get); + router.route('/user/:id').put(auth, userCtrl.update); + router.route('/user/:id').delete(auth, userCtrl.delete); // Apply the routes to our application with the prefix /api app.use('/api', router);