From c37871dc392fa1e5f29637b095b428d23772748c Mon Sep 17 00:00:00 2001 From: dan Date: Sun, 30 Jun 2019 01:10:13 -0600 Subject: [PATCH] Enable creating user from UI --- .vscode/launch.json | 12 ++ Client/src/app/app-routing.module.ts | 5 + Client/src/app/app.module.ts | 10 +- .../members-page/members-page.component.css | 0 .../members-page/members-page.component.html | 17 ++ .../members-page.component.spec.ts | 25 +++ .../members-page/members-page.component.ts | 39 ++++ .../app/components/menu/menu.component.html | 14 +- .../add-user-popup.component.css | 3 + .../add-user-popup.component.html | 40 ++++ .../add-user-popup.component.spec.ts | 25 +++ .../add-user-popup.component.ts | 92 +++++++++ Client/src/app/constants/urls.ts | 1 + Client/src/app/constants/user-rights.ts | 8 + Client/src/app/interfaces/user.ts | 16 ++ Client/src/app/services/login.service.ts | 17 +- Client/src/app/services/user.service.ts | 51 +++++ Server/src/authentication/hash.js | 7 +- Server/src/authentication/login.js | 66 ++++++- Server/src/database/connectionasync.js | 25 +++ Server/src/database/users.js | 182 +++++------------- Server/src/package-lock.json | 155 ++++++++------- Server/src/routes/api/login.js | 10 +- Server/src/routes/api/require-auth.js | 18 +- Server/src/routes/api/users.js | 18 +- 25 files changed, 608 insertions(+), 248 deletions(-) create mode 100644 Client/src/app/components/members-page/members-page.component.css create mode 100644 Client/src/app/components/members-page/members-page.component.html create mode 100644 Client/src/app/components/members-page/members-page.component.spec.ts create mode 100644 Client/src/app/components/members-page/members-page.component.ts create mode 100644 Client/src/app/components/popups/add-user-popup/add-user-popup.component.css create mode 100644 Client/src/app/components/popups/add-user-popup/add-user-popup.component.html create mode 100644 Client/src/app/components/popups/add-user-popup/add-user-popup.component.spec.ts create mode 100644 Client/src/app/components/popups/add-user-popup/add-user-popup.component.ts create mode 100644 Client/src/app/constants/user-rights.ts create mode 100644 Client/src/app/interfaces/user.ts create mode 100644 Client/src/app/services/user.service.ts create mode 100644 Server/src/database/connectionasync.js diff --git a/.vscode/launch.json b/.vscode/launch.json index b169bf9..58b16e8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,18 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { + "type": "node", + "request": "attach", + "name": "Attach by Process ID", + "processId": "${command:PickProcess}" + }, + { + "type": "node", + "request": "attach", + "name": "Attach", + "port": 25776 + }, { "type": "chrome", "request": "launch", diff --git a/Client/src/app/app-routing.module.ts b/Client/src/app/app-routing.module.ts index 36c3f49..49ce67a 100644 --- a/Client/src/app/app-routing.module.ts +++ b/Client/src/app/app-routing.module.ts @@ -11,6 +11,7 @@ import { EventsPageComponent } from './components/events-page/events-page.compon import { ContactPageComponent } from './components/contact-page/contact-page.component'; import { SalvationPageComponent } from './components/salvation-page/salvation-page.component'; import { CampPageComponent } from './components/camp-page/camp-page.component'; +import { MembersPageComponent } from './components/members-page/members-page.component'; const routes = [ @@ -58,6 +59,10 @@ const routes = path: 'salvation', component: SalvationPageComponent }, + { + path: 'user', + component: MembersPageComponent + }, { path: 'camp', component: CampPageComponent diff --git a/Client/src/app/app.module.ts b/Client/src/app/app.module.ts index 65bbbfc..c92c08c 100644 --- a/Client/src/app/app.module.ts +++ b/Client/src/app/app.module.ts @@ -60,6 +60,9 @@ import { OfbDatePipe } from './pipes/ofb-date.pipe'; // Routing import { AppRoutingModule } from './app-routing.module'; import { CampPageComponent } from './components/camp-page/camp-page.component'; +import { MembersPageComponent } from './components/members-page/members-page.component'; +import { AddUserPopupComponent } from './components/popups/add-user-popup/add-user-popup.component'; +import { UserService } from './services/user.service'; @@ -100,7 +103,9 @@ import { CampPageComponent } from './components/camp-page/camp-page.component'; SalvationPageComponent, MediaPageComponent, VideoPopupComponent, - CampPageComponent + CampPageComponent, + MembersPageComponent, + AddUserPopupComponent ], imports: [ BrowserModule, @@ -115,7 +120,7 @@ import { CampPageComponent } from './components/camp-page/camp-page.component'; MatSnackBarModule, MatDialogModule, ], - providers: [LoginService,GoogleAnalyticsService,SermonService,EventService,EmailService,WindowRefService], + providers: [LoginService,UserService,GoogleAnalyticsService,SermonService,EventService,EmailService,WindowRefService], entryComponents: [AddSermonPopupComponent, LoginPopupComponent, OkPopupComponent, @@ -124,6 +129,7 @@ import { CampPageComponent } from './components/camp-page/camp-page.component'; UpdateSermonPopupComponent, SharePopupComponent, AddEventPopupComponent, + AddUserPopupComponent, VideoPopupComponent], bootstrap: [AppComponent] }) diff --git a/Client/src/app/components/members-page/members-page.component.css b/Client/src/app/components/members-page/members-page.component.css new file mode 100644 index 0000000..e69de29 diff --git a/Client/src/app/components/members-page/members-page.component.html b/Client/src/app/components/members-page/members-page.component.html new file mode 100644 index 0000000..fd76479 --- /dev/null +++ b/Client/src/app/components/members-page/members-page.component.html @@ -0,0 +1,17 @@ + +
+
Loading...
+
+ Welcome {{user.userName}} +
+ +
+
+
+ +
+
+ +
\ No newline at end of file diff --git a/Client/src/app/components/members-page/members-page.component.spec.ts b/Client/src/app/components/members-page/members-page.component.spec.ts new file mode 100644 index 0000000..a07bfa8 --- /dev/null +++ b/Client/src/app/components/members-page/members-page.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MembersPageComponent } from './members-page.component'; + +describe('MembersPageComponent', () => { + let component: MembersPageComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ MembersPageComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MembersPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/Client/src/app/components/members-page/members-page.component.ts b/Client/src/app/components/members-page/members-page.component.ts new file mode 100644 index 0000000..b378ada --- /dev/null +++ b/Client/src/app/components/members-page/members-page.component.ts @@ -0,0 +1,39 @@ +import { Component, OnInit } from '@angular/core'; +import { LoginService } from 'src/app/services/login.service'; +import { User } from 'src/app/interfaces/user'; +import { UserRights } from 'src/app/constants/user-rights'; +import { MatDialog } from '@angular/material'; +import { AddUserPopupComponent } from '../popups/add-user-popup/add-user-popup.component'; +import { take } from 'rxjs/operators'; + +@Component({ + selector: 'app-members-page', + templateUrl: './members-page.component.html', + styleUrls: ['./members-page.component.css'] +}) +export class MembersPageComponent implements OnInit { + + public loading: boolean = true; + public user: User; + public get canAddUser(): boolean { + return this.user && this.user.canDo(UserRights.userAdd); + } + + constructor(private loginService: LoginService, private matDialog: MatDialog) { } + + ngOnInit() { + this.loginService.isLoggedIn(true).pipe(take(1)).subscribe(r => { + this.loading = false; + console.log(r); + if (r == false) { + this.addUserModal(); + } + }) + this.loginService.user.subscribe(u => this.user = u); + } + + addUserModal() { + let dialog = this.matDialog.open(AddUserPopupComponent); + } + +} diff --git a/Client/src/app/components/menu/menu.component.html b/Client/src/app/components/menu/menu.component.html index 6267802..f36d7e0 100644 --- a/Client/src/app/components/menu/menu.component.html +++ b/Client/src/app/components/menu/menu.component.html @@ -4,13 +4,13 @@
    -
  • -
  • -
  • -
  • -
  • -
  • -
  • +
  • +
  • +
  • +
  • +
  • +
  • +
\ No newline at end of file diff --git a/Client/src/app/components/popups/add-user-popup/add-user-popup.component.css b/Client/src/app/components/popups/add-user-popup/add-user-popup.component.css new file mode 100644 index 0000000..8286984 --- /dev/null +++ b/Client/src/app/components/popups/add-user-popup/add-user-popup.component.css @@ -0,0 +1,3 @@ +.full-width { + width: 100%; +} \ No newline at end of file diff --git a/Client/src/app/components/popups/add-user-popup/add-user-popup.component.html b/Client/src/app/components/popups/add-user-popup/add-user-popup.component.html new file mode 100644 index 0000000..d751f60 --- /dev/null +++ b/Client/src/app/components/popups/add-user-popup/add-user-popup.component.html @@ -0,0 +1,40 @@ +
+

Add User

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +

+
+

+ {{error}} +

+
+ +
+
\ No newline at end of file diff --git a/Client/src/app/components/popups/add-user-popup/add-user-popup.component.spec.ts b/Client/src/app/components/popups/add-user-popup/add-user-popup.component.spec.ts new file mode 100644 index 0000000..d493176 --- /dev/null +++ b/Client/src/app/components/popups/add-user-popup/add-user-popup.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AddUserPopupComponent } from './add-user-popup.component'; + +describe('AddUserPopupComponent', () => { + let component: AddUserPopupComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ AddUserPopupComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AddUserPopupComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/Client/src/app/components/popups/add-user-popup/add-user-popup.component.ts b/Client/src/app/components/popups/add-user-popup/add-user-popup.component.ts new file mode 100644 index 0000000..e44038f --- /dev/null +++ b/Client/src/app/components/popups/add-user-popup/add-user-popup.component.ts @@ -0,0 +1,92 @@ +import { Component, OnInit } from '@angular/core'; +import { MatDialog, MatDialogRef } from '@angular/material'; +import { LoginService } from 'src/app/services/login.service'; +import { LoginPopupComponent } from '../login-popup/login-popup.component'; +import { OkPopupComponent } from '../ok-popup/ok-popup.component'; +import { UserService } from 'src/app/services/user.service'; + +@Component({ + selector: 'app-add-user-popup', + templateUrl: './add-user-popup.component.html', + styleUrls: ['./add-user-popup.component.css'] +}) +export class AddUserPopupComponent implements OnInit { + public addUserButtonText: string = "Add User"; + public addUserButtonDisabled: boolean = false; + + // Form Fields + public firstName: string; + public lastName: string; + public street: string; + public city: string; + public state: string; + public zip: string; + public country: string = 'United States'; + + public errorMessages: string[] = []; + + constructor(private MatDialog: MatDialog, + private MatDialogRef: MatDialogRef, + private loginService: LoginService, + private userService: UserService) { } + + ngOnInit() { + + } + + onSubmit(){ + this.updateAddButton(false); + + //First check to see if we are logged in + this.loginService.isLoggedIn(true).subscribe(is => { + if (is === true){ + this.addUser(); + } else { + let popup = this.MatDialog.open(LoginPopupComponent); + popup.afterClosed().subscribe(r => { + if (r === true){ + this.addUser(); + } else { + this.updateAddButton(true); + } + }); + } + }, + error =>{ + this.updateAddButton(true); + let errorDialog = this.MatDialog.open(OkPopupComponent,{data:{title:'Error',message:'There was an error adding the event\n' + error}}); + }); + } + + updateAddButton(enable: boolean){ + if (enable){ + this.addUserButtonText = "Add User"; + } else { + this.addUserButtonText = "Please Wait..."; + } + this.addUserButtonDisabled = !enable; + } + + private addUser(): void { + console.log("Add User"); + this.userService.create( + this.firstName, + this.lastName, + this.street, + this.city, + this.state, + this.zip, + this.country).subscribe(data => { + this.MatDialogRef.close(data['user']); + }, error => { + this.updateAddButton(true); + let errorDialog = this.MatDialog.open(OkPopupComponent,{data:{title:'User Error',message:'There was an error creating the user: ' + error}}); + }); + } + + cancel(evt){ + evt.preventDefault(); + this.MatDialogRef.close(); + } + +} diff --git a/Client/src/app/constants/urls.ts b/Client/src/app/constants/urls.ts index 6cac148..0d54698 100644 --- a/Client/src/app/constants/urls.ts +++ b/Client/src/app/constants/urls.ts @@ -12,6 +12,7 @@ export const SERMON_ADD_URL = environment.baseUrl + "/api2/sermons/a/"; export const SERMON_DELETE_URL = environment.baseUrl + "/api2/sermons/a/"; export const SERMON_UPDATE_URL = environment.baseUrl + "/api2/sermons/a/"; export const SERMON_DOWNLOAD_URL = environment.baseUrl + "/api2/sermons/download/"; +export const USER_CREATE_URL = environment.baseUrl + "/api2/users/a/"; export const LOGIN_URL = environment.baseUrl + '/api2/login'; export const LOGIN_VALIDATE_TOKEN = ''; export const EMAIL_URL = environment.baseUrl + "/api2/email"; diff --git a/Client/src/app/constants/user-rights.ts b/Client/src/app/constants/user-rights.ts new file mode 100644 index 0000000..17a0a51 --- /dev/null +++ b/Client/src/app/constants/user-rights.ts @@ -0,0 +1,8 @@ +export const UserRights = { + userAdd: "user_add", + userUpdateSelf: "user_update_self", + userDeleteSelf: "user_delete_self", + sermonsAdd: "sermons_add", + sermonsUpdate: "sermons_update", + sermonsDelete: "sermons_delete" +} \ No newline at end of file diff --git a/Client/src/app/interfaces/user.ts b/Client/src/app/interfaces/user.ts new file mode 100644 index 0000000..3514f5c --- /dev/null +++ b/Client/src/app/interfaces/user.ts @@ -0,0 +1,16 @@ +export class User { + id: number; + userName: string; + firstName: string; + lastName: string; + email: string; + verified: boolean; + emailVerified: boolean; + federated: boolean; + rights: string[]; + + canDo(right: string): boolean { + if (!this.rights || this.rights.length == 0) return false; + return this.rights.findIndex(r => r === right) > -1; + } +} \ No newline at end of file diff --git a/Client/src/app/services/login.service.ts b/Client/src/app/services/login.service.ts index a714c75..dc146a3 100644 --- a/Client/src/app/services/login.service.ts +++ b/Client/src/app/services/login.service.ts @@ -2,9 +2,10 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { Observable, Subject, throwError, of } from 'rxjs'; -import { catchError, map } from 'rxjs/operators'; +import { catchError, map, tap } from 'rxjs/operators'; import { LOGIN_URL } from '../constants/urls'; +import { User } from '../interfaces/user'; @Injectable() export class LoginService { @@ -12,6 +13,7 @@ export class LoginService { private onChange: Subject<{isLoggedIn: boolean}>; private lastLoginCheck: Date; private options: {}; + public user: Subject = new Subject(); constructor(private httpClient: HttpClient){ this.options = { @@ -23,7 +25,16 @@ export class LoginService { } login(username: string, password: string){ - return this.httpClient.post(LOGIN_URL, {userName: username, password: password}, this.options).pipe(catchError(this.handleError)); + return this.httpClient + .post(LOGIN_URL, {userName: username, password: password}, this.options) + .pipe( + tap(res => console.log(res)), + tap(res => { + const u = (res).user; + const usr = Object.assign(new User(), u); + this.user.next(usr); + }), + catchError(this.handleError)); } isLoggedIn(fromServer: boolean = false){ @@ -32,10 +43,12 @@ export class LoginService { if (hours > 0.75 || fromServer === true){ return this.httpClient.post(LOGIN_URL + "/isloggedin",{},this.options) .pipe(map((d:any) => { + console.log(d); let is = d.loggedIn === true; this.loggedIn = is; this.lastLoginCheck = new Date(); this.onChange.next({isLoggedIn: this.loggedIn}); + this.user.next(Object.assign(new User(), d.user)); return this.loggedIn; }), catchError(this.handleError)); diff --git a/Client/src/app/services/user.service.ts b/Client/src/app/services/user.service.ts new file mode 100644 index 0000000..e80ff89 --- /dev/null +++ b/Client/src/app/services/user.service.ts @@ -0,0 +1,51 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; + +import { Observable, Subject, throwError, of } from 'rxjs'; +import { catchError, map, tap, first } from 'rxjs/operators'; + +import { LOGIN_URL, USER_CREATE_URL } from '../constants/urls'; + +@Injectable() +export class UserService { + + private options: {}; + + constructor(private httpClient: HttpClient){ + this.options = { + withCredentials: true + }; + } + + create(firstName: string, lastName: string, street: string, city: string, state: string, zip: string, country: string) { + const body = { + firstName: firstName, + lastName: lastName, + street: street, + city: city, + state: state, + zip: zip, + country: country + }; + console.log(body); + return this.httpClient.post(USER_CREATE_URL, body, this.options) + .pipe(tap(res => console.log(res)), catchError(this.handleError)); + } + + private handleError(error: HttpErrorResponse) { + if (error.error instanceof ErrorEvent) { + // A client-side or network error occurred. Handle it accordingly. + console.error('An error occurred:', error.error.message); + } else { + // The backend returned an unsuccessful response code. + // The response body may contain clues as to what went wrong, + console.error( + `Backend returned code ${error.status}, ` + + `body was: ${error.error}`); + console.error(error); + } + // return an observable with a user-facing error message + return throwError((error && error.error && error.error.message) ? error.error.message : + 'Something bad happened; please try again later.'); + }; +} diff --git a/Server/src/authentication/hash.js b/Server/src/authentication/hash.js index bc41cf8..e327e02 100644 --- a/Server/src/authentication/hash.js +++ b/Server/src/authentication/hash.js @@ -81,5 +81,10 @@ function verifyPassword(password, combined, callback) { }); } +function createVerificationCode() { + return crypto.randomBytes(8).toString("hex"); +} + exports.hashPassword = hashPassword; -exports.verifyPassword = verifyPassword; \ No newline at end of file +exports.verifyPassword = verifyPassword; +exports.createVerificationCode = createVerificationCode; \ No newline at end of file diff --git a/Server/src/authentication/login.js b/Server/src/authentication/login.js index 297d893..d517fce 100644 --- a/Server/src/authentication/login.js +++ b/Server/src/authentication/login.js @@ -4,6 +4,7 @@ var hash = require("./hash"); var crypto = require("crypto"); var dbUsers = require("../database/users"); var dbTokens = require("../database/tokens"); +var dbRights = require("../database/user-rights"); exports.login = function(userName, password, topCallback){ @@ -33,37 +34,82 @@ exports.login = function(userName, password, topCallback){ callback("Invalid UserName or Password"); return; } - callback(null,isMatch, user.id); + callback(null,isMatch, user); }); }, - function(isMatch, userId, callback){ + function(isMatch, user, callback){ //Password is a match so lets generate a token - dbTokens.createToken(userId,function(error,token){ + dbTokens.createToken(user.id,function(error,token){ if (error){ callback(error); return; } - callback(null, token); + callback(null, token, user); + }); + }, + function(token, user, callback) { + dbRights.getRights(user.id, function(error, rights){ + if (error) { + callback(error); + return; + } + user.rights = rights; + callback(null, token, user); }); } - ],function(error,token){ + ],function(error,token, user){ if (error){ topCallback(error); return; } - topCallback(null, token); + user.password = undefined; + user.verificationCode = undefined; + topCallback(null, token, user); }); } -exports.verifyToken = function(userId, tokenId, token, callback){ +function verifyToken(userId, tokenId, token, callback){ if (typeof userId == 'object' && typeof tokenId == 'function'){ verifyTokenObject(userId,tokenId); return; } - dbTokens.verifyToken(userId, tokenId, token, callback); + dbTokens.verifyToken(userId, tokenId, token, function(error, isValid){ + console.log(userId); + if (error) { + callback(error); + return; + } else if (!isValid) { + callback(null, isValid); + return; + } + dbUsers.getUser(userId, function(error, user){ + if(error) { + callback(error); + return; + } + if (!user) { + callback("User not Found"); + return; + } + user.password = undefined; + user.verificationCode = undefined; + dbRights.getRights(userId, function(error, rights){ + if (error) { + callback(error); + return; + } + user.rights = rights; + callback(null, isValid, user); + }); + + }); + }); } function verifyTokenObject(tokenObj, callback){ - dbTokens.verifyToken(tokenObj.userId,tokenObj.id,tokenObj.value,callback); -} \ No newline at end of file + verifyToken(tokenObj.userId, tokenObj.id, tokenObj.value, callback); + //dbTokens.verifyToken(tokenObj.userId,tokenObj.id,tokenObj.value,callback); +} + +exports.verifyToken = verifyToken; \ No newline at end of file diff --git a/Server/src/database/connectionasync.js b/Server/src/database/connectionasync.js new file mode 100644 index 0000000..0a72fa5 --- /dev/null +++ b/Server/src/database/connectionasync.js @@ -0,0 +1,25 @@ +var connection = require('./connection'); + +exports.query = function(sql, args) { + return new Promise(function(resolve, reject){ + connection.query(sql,args,function(error,rows,fields){ + if (error){ + reject(error) + } else { + resolve({rows:rows,fields:fields}); + } + }); + }); +} + +exports.nonQuery = function(sql, args) { + return new Promise(function(resolve, reject){ + connection.query(sql,args,function(error,result){ + if (error){ + reject(error) + } else { + resolve(result); + } + }); + }); +} \ No newline at end of file diff --git a/Server/src/database/users.js b/Server/src/database/users.js index 6adbde7..176d368 100644 --- a/Server/src/database/users.js +++ b/Server/src/database/users.js @@ -1,4 +1,5 @@ var connection = require("./connection"); +var connectionAsync = require("./connectionasync"); var hash = require("../authentication/hash"); var dbRights = require("./user-rights"); var async = require("async"); @@ -25,6 +26,10 @@ exports.getUser = function(userIdOrUserName, callback){ user.email = rows[0].Email; user.password = rows[0].Password; user.deletedDate = rows[0].deletedDate; + user.verified = rows[0].Verified; + user.verifiedDate = rows[0].VerifiedDate; + user.verificationCode = rows[0].VerificationCode; + user.verificationCodeDate = rows[0].verificationCodeDate; } console.log(user); //console.log(fields); @@ -32,142 +37,55 @@ exports.getUser = function(userIdOrUserName, callback){ }); } -exports.insertUser = function(userNameOrUserObject, passwordOrCallbackFunction, email, topCallback){ - if (typeof passwordOrCallbackFunction === 'function'){ - insertUserFromObject(userNameOrUserObject,passwordOrCallbackFunction); - return; +exports.getUser2 = async function(email) { + const queryResult = await connectionAsync.query('SELECT * FROM Users WHERE UserName = ? AND DeletedDate IS NULL;', [email]); + if (queryResult && queryResult.rows && queryResult.rows.length > 0) { + const row = queryResult.rows[0]; + const user = {}; + user.id = row.Id; + user.userName = row.UserName; + user.password = row.Password; + user.firstName = row.FirstName; + user.lastName = row.LastName; + user.street = row.Street; + user.city = row.City; + user.state = row.State; + user.country = row.Country; + user.emailVerified = row.EmailVerified; + user.emailVerificationCode = row.EmailVerificationCode; + user.emailVerificationCodeDate = row.EmailVerificationCodeDate; + user.federated = row.Federated; + user.federationCode = row.federationCode; + return user; + } else { + return null; } - - async.waterfall([ - //First step is to check if the user name already exists - function(callback){ - dbUsers.getUser(userNameOrUserObject,function(error,user){ - if (error){ - callback(error); - return; - } - if (user){ - callback("user-exists"); //The user exists - return; - } else { - callback(null,false); //The user does not exist - return; - } - }); - }, - //If the user does not exist - get the hashed version of the password - function(userExists,callback){ - //Get the password hash - hash.hashPassword(passwordOrCallbackFunction,function(error,hashedPW){ - if (error){ - callback(error); - return; - } - callback(null,hashedPW); - }); - }, - //Insert the user into the database with the hashed password - function(hashedPW,callback){ - //User does not exist. Lets add it - connection.query("INSERT INTO Users (CreatedDate,ModifiedDate,Id,UserName,Password,DeletedDate,Email) VALUES(CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,NULL,?,?,NULL,?)",[userNameOrUserObject,hashedPW,email],function(error,result){ - if (error){ - callback(error); - return; - } - callback(null,result); - }); - }, - //Get the new user from the database and return it to the final function - function(insertResult,callback){ - //Get the inserted user - dbUsers.getUser(insertResult.insertId,function(error,user){ - if(error){ - callback(error); - return; - } - if (!user){ - callback("error retrieving new user"); - return; - } - callback(null,user); - }); - }, - //add the rights for this user - function(user, callback){ - var rightIdArray = [1,2]; - dbRights.addRights(user.id,rightIdArray,function(error){ - if (error){ - callback(error); - } else { - callback(null,user); - } - }); - } - ],function(err,result){ - //Complete - if (err == "user-exists"){ - topCallback("User already Exists"); - return; - } - if (err){ - topCallback(err); - return; - } - result.password = "*"; - topCallback(null,result); - }); } -function insertUserFromObject(sermon, callback){ - dbSermons.insertUser(user.userName,user.password,user.email,callback); -} - -exports.updateUser = function(userIdOrUserObject, userNameOrCallbackFunction, password, email, deletedDate, topCallback){ - if (typeof userNameOrCallbackFunction == 'function'){ - updateUserFromObject(userIdOrUserObject,userNameOrCallbackFunction); - return; +exports.createUser = async function(firstName, lastName, street, city, state, zip, country) { + // Check to see if email already exists + const tempUserName = firstName + " " + lastName; + const existing = await this.getUser2(tempUserName); + if (existing) { + throw `${tempUserName} already exists`; } - - async.waterfall([ - //First Step: == GET Hashed Password - function(callback){ - hash.hashPassword(password,function(error, hashedPW){ - if (error){ - callback(error); - return; - } - callback(null,hashedPW); - }); - }, - function(hashedPW, callback){ - connection.query("UPDATE Users SET UserName = ?, Password = ?, Email = ?, DeletedDate = ? WHERE Id = ?;",[userNameOrCallbackFunction,hashedPW,email,deletedDate,userIdOrUserObject],function(error,result){ - if (error){ - callback(error); - return; - } - callback(null, result); - }); - }, - function(updateResult, callback){ - dbUsers.getUser(userIdOrUserObject,function(error,user){ - if (error){ - callback(error); - return; - } - callback(null, user); - }); - } - ], function(error, updatedUser){ - if (error){ - topCallback(error); - return; - } - topCallback(null,updatedUser); - }); -} - -function updateUserFromObject(user,callback){ - dbUsers.updateUser(user.id,user.userName,user.password,user.email,user.deletedDate,callback); + const newUser = { + CreatedDate: new Date(), + ModifiedDate: new Date(), + UserName: tempUserName, + FirstName: firstName, + LastName: lastName, + Street: street, + City: city, + State: state, + Zip: zip, + Country: country, + EmailVerified: false, + Federated: false, + FederationCode: hash.createVerificationCode() + }; + const newUserResult = await connectionAsync.nonQuery('INSERT INTO Users Set ?', newUser); + return await this.getUser2(tempUserName); } exports.deleteUser = function(userIdOrUserObject, callback){ diff --git a/Server/src/package-lock.json b/Server/src/package-lock.json index 4818c57..85231f0 100644 --- a/Server/src/package-lock.json +++ b/Server/src/package-lock.json @@ -9,7 +9,7 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", "requires": { - "mime-types": "2.1.17", + "mime-types": "~2.1.16", "negotiator": "0.6.1" } }, @@ -28,7 +28,7 @@ "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", "requires": { - "lodash": "4.17.4" + "lodash": "^4.14.0" } }, "bignumber.js": { @@ -42,15 +42,15 @@ "integrity": "sha512-KL2pZpGvy6xuZHgYUznB1Zfw4AoGMApfRanT5NafeLvglbaSM+4CCtmlyYOv66oYXqvKL1xpaFb94V/AZVUnYg==", "requires": { "bytes": "3.0.0", - "content-type": "1.0.4", + "content-type": "~1.0.4", "debug": "2.6.8", - "depd": "1.1.1", - "http-errors": "1.6.2", + "depd": "~1.1.1", + "http-errors": "~1.6.2", "iconv-lite": "0.4.19", - "on-finished": "2.3.0", + "on-finished": "~2.3.0", "qs": "6.5.1", "raw-body": "2.3.2", - "type-is": "1.6.15" + "type-is": "~1.6.15" }, "dependencies": { "content-type": { @@ -71,7 +71,7 @@ "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", "requires": { "dicer": "0.2.5", - "readable-stream": "1.1.14" + "readable-stream": "1.1.x" }, "dependencies": { "isarray": { @@ -84,10 +84,10 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", "isarray": "0.0.1", - "string_decoder": "0.10.31" + "string_decoder": "~0.10.x" } }, "string_decoder": { @@ -107,9 +107,9 @@ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.3", - "typedarray": "0.0.6" + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" } }, "content-disposition": { @@ -174,7 +174,7 @@ "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", "requires": { - "readable-stream": "1.1.14", + "readable-stream": "1.1.x", "streamsearch": "0.1.2" }, "dependencies": { @@ -188,10 +188,10 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", "isarray": "0.0.1", - "string_decoder": "0.10.31" + "string_decoder": "~0.10.x" } }, "string_decoder": { @@ -226,34 +226,34 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.15.4.tgz", "integrity": "sha1-Ay4iU0ic+PzgJma+yj0R7XotrtE=", "requires": { - "accepts": "1.3.4", + "accepts": "~1.3.3", "array-flatten": "1.1.1", "content-disposition": "0.5.2", - "content-type": "1.0.2", + "content-type": "~1.0.2", "cookie": "0.3.1", "cookie-signature": "1.0.6", "debug": "2.6.8", - "depd": "1.1.1", - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "etag": "1.8.0", - "finalhandler": "1.0.4", + "depd": "~1.1.1", + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "etag": "~1.8.0", + "finalhandler": "~1.0.4", "fresh": "0.5.0", "merge-descriptors": "1.0.1", - "methods": "1.1.2", - "on-finished": "2.3.0", - "parseurl": "1.3.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.1", "path-to-regexp": "0.1.7", - "proxy-addr": "1.1.5", + "proxy-addr": "~1.1.5", "qs": "6.5.0", - "range-parser": "1.2.0", + "range-parser": "~1.2.0", "send": "0.15.4", "serve-static": "1.12.4", "setprototypeof": "1.0.3", - "statuses": "1.3.1", - "type-is": "1.6.15", + "statuses": "~1.3.1", + "type-is": "~1.6.15", "utils-merge": "1.0.0", - "vary": "1.1.1" + "vary": "~1.1.1" } }, "finalhandler": { @@ -262,12 +262,12 @@ "integrity": "sha512-16l/r8RgzlXKmFOhZpHBztvye+lAhC5SU7hXavnerC9UfZqZxxXl3BzL8MhffPT3kF61lj9Oav2LKEzh0ei7tg==", "requires": { "debug": "2.6.8", - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "on-finished": "2.3.0", - "parseurl": "1.3.1", - "statuses": "1.3.1", - "unpipe": "1.0.0" + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.1", + "statuses": "~1.3.1", + "unpipe": "~1.0.0" } }, "forwarded": { @@ -288,7 +288,7 @@ "depd": "1.1.1", "inherits": "2.0.3", "setprototypeof": "1.0.3", - "statuses": "1.3.1" + "statuses": ">= 1.3.1 < 2" } }, "iconv-lite": { @@ -346,7 +346,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", "requires": { - "mime-db": "1.30.0" + "mime-db": "~1.30.0" } }, "minimist": { @@ -362,6 +362,19 @@ "minimist": "0.0.8" } }, + "moment": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" + }, + "moment-timezone": { + "version": "0.5.25", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.25.tgz", + "integrity": "sha512-DgEaTyN/z0HFaVcVbSyVCUU6HeFdnNC3vE4c9cgu2dgMTvjBUBdBzWfasTBmAW45u5OIMeCJtU8yNjM22DHucw==", + "requires": { + "moment": ">= 2.9.0" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -372,14 +385,14 @@ "resolved": "https://registry.npmjs.org/multer/-/multer-1.3.0.tgz", "integrity": "sha1-CSsmcPaEb6SRSWXvyM+Uwg/sbNI=", "requires": { - "append-field": "0.1.0", - "busboy": "0.2.14", - "concat-stream": "1.6.0", - "mkdirp": "0.5.1", - "object-assign": "3.0.0", - "on-finished": "2.3.0", - "type-is": "1.6.15", - "xtend": "4.0.1" + "append-field": "^0.1.0", + "busboy": "^0.2.11", + "concat-stream": "^1.5.0", + "mkdirp": "^0.5.1", + "object-assign": "^3.0.0", + "on-finished": "^2.3.0", + "type-is": "^1.6.4", + "xtend": "^4.0.0" } }, "mysql": { @@ -436,7 +449,7 @@ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.5.tgz", "integrity": "sha1-ccDuOxAt4/IC87ZPYI0XP8uhqRg=", "requires": { - "forwarded": "0.1.0", + "forwarded": "~0.1.0", "ipaddr.js": "1.4.0" } }, @@ -466,13 +479,13 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.0.3", + "util-deprecate": "~1.0.1" } }, "safe-buffer": { @@ -486,18 +499,18 @@ "integrity": "sha1-mF+qPihLAnPHkzZKNcZze9k5Bbk=", "requires": { "debug": "2.6.8", - "depd": "1.1.1", - "destroy": "1.0.4", - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "etag": "1.8.0", + "depd": "~1.1.1", + "destroy": "~1.0.4", + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "etag": "~1.8.0", "fresh": "0.5.0", - "http-errors": "1.6.2", + "http-errors": "~1.6.2", "mime": "1.3.4", "ms": "2.0.0", - "on-finished": "2.3.0", - "range-parser": "1.2.0", - "statuses": "1.3.1" + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.3.1" } }, "serve-static": { @@ -505,9 +518,9 @@ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.12.4.tgz", "integrity": "sha1-m2qpjutyU8Tu3Ewfb9vKYJkBqWE=", "requires": { - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "parseurl": "1.3.1", + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "parseurl": "~1.3.1", "send": "0.15.4" } }, @@ -536,7 +549,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "~5.1.0" } }, "type-is": { @@ -545,7 +558,7 @@ "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", "requires": { "media-typer": "0.3.0", - "mime-types": "2.1.17" + "mime-types": "~2.1.15" } }, "typedarray": { diff --git a/Server/src/routes/api/login.js b/Server/src/routes/api/login.js index f5133fd..5a8be7a 100644 --- a/Server/src/routes/api/login.js +++ b/Server/src/routes/api/login.js @@ -10,7 +10,7 @@ router.post("/",function(req,res){ res.status(200).json({"status":200,"message":"userName and password is needed to login!"}); return; } - auth.login(req.body.userName,req.body.password,function(error,token){ + auth.login(req.body.userName,req.body.password,function(error,token,user){ if (error){ res.status(400).json({"status":400,"message":"Error logging in","error":error}); return; @@ -19,7 +19,7 @@ router.post("/",function(req,res){ res.cookie("tokenId",token.id,{ httpOnly: true, secured: true, signed: true }); res.cookie("tokenValue",token.value,{ httpOnly: true, secured: true, signed: true }); res.cookie("tokenUser",token.userId,{ httpOnly: true, secured: true, signed: true}); - res.status(200).json({"status":200,"message":"Logged In"}); + res.status(200).json({"status":200,"message":"Logged In","user":user}); }); }); @@ -32,11 +32,11 @@ router.post("/isloggedin",function(req,res){ } var token = {}; - token.userId = req.signedCookies.tokenUser; + token.userId = +req.signedCookies.tokenUser; token.id = req.signedCookies.tokenId; token.value = req.signedCookies.tokenValue; - auth.verifyToken(token,function(error,isValid){ + auth.verifyToken(token,function(error,isValid,user){ if (error){ res.status(400).json({"status":400,"message":"error validating token","loggedIn":loggedIn}); return; @@ -46,7 +46,7 @@ router.post("/isloggedin",function(req,res){ return; } else { loggedIn = true; - res.status(200).json({"status":200,"message":"valid token","loggedIn":loggedIn}); + res.status(200).json({"status":200,"message":"valid token","loggedIn":loggedIn,"user":user}); } } }); diff --git a/Server/src/routes/api/require-auth.js b/Server/src/routes/api/require-auth.js index 849257b..4acf529 100644 --- a/Server/src/routes/api/require-auth.js +++ b/Server/src/routes/api/require-auth.js @@ -41,8 +41,8 @@ router.use(upload.single('file'),function(req,res,next){ } var token = {}; - token.userId = req.signedCookies.tokenUser; - token.id = req.signedCookies.tokenId; + token.userId = +req.signedCookies.tokenUser; + token.id = +req.signedCookies.tokenId; token.value = req.signedCookies.tokenValue; auth.verifyToken(token,function(error,isValid){ @@ -57,14 +57,14 @@ router.use(upload.single('file'),function(req,res,next){ return; } } - }); - if (req.file){ - req.body.file = req.file.originalname; - req.body.finalPath = finalStorage + req.file.filename; - req.body.tmpPath = req.file.destination + req.file.filename; - } - next(); + if (req.file){ + req.body.file = req.file.originalname; + req.body.finalPath = finalStorage + req.file.filename; + req.body.tmpPath = req.file.destination + req.file.filename; + } + next(); + }); }); diff --git a/Server/src/routes/api/users.js b/Server/src/routes/api/users.js index 802e67a..9bf3f17 100644 --- a/Server/src/routes/api/users.js +++ b/Server/src/routes/api/users.js @@ -3,19 +3,19 @@ var router = express.Router(); var dbUsers = require("../../database/users"); -router.post("/a/",function(req,res){ +router.post("/a/",async function(req,res) { console.log("new user"); - if (!req.body.userName || !req.body.password || !req.body.email){ - res.status(400).json({"status":400,"message":"userName, password, and email are required fields in the body"}); + console.log(req.body); + if (!req.body.lastName){ + res.status(400).json({"status":400,"message":"last name is required fields in the body"}); return; } - dbUsers.insertUser(req.body.userName,req.body.password,req.body.email,function(error,newUser){ - if (error){ - res.status(400).json({"status":400,"message":"error creating new user","error":error}); - return; - } + try { + var newUser = await dbUsers.createUser(req.body.firstName, req.body.lastName, req.body.street, req.body.city, req.body.state, req.body.zip, req.body.country); res.status(201).json({"status":201,"message":"user created","user":newUser}); - }); + } catch (ex) { + res.status(500).json({"status":500,"message":ex}); + } }); router.put("/a/",function(req,res){