From faa0d716c678993fc5ebe3e821f6a6e2bb61c29b Mon Sep 17 00:00:00 2001 From: Eric Fithian Date: Tue, 27 Feb 2024 16:14:13 -0700 Subject: [PATCH 1/6] admin is added on database init --- backend/controllers/AdminController.js | 28 ++++++++++++++++++++++++++ backend/routes/AdminRouter.js | 5 ++--- backend/routes/VendorRouter.js | 4 +--- sql-scripts/init.sql | 10 +++++++-- 4 files changed, 39 insertions(+), 8 deletions(-) diff --git a/backend/controllers/AdminController.js b/backend/controllers/AdminController.js index 2c7284a..68238f4 100644 --- a/backend/controllers/AdminController.js +++ b/backend/controllers/AdminController.js @@ -171,6 +171,33 @@ const createAdminMiddleware = async (req, res, next) => { } }; +const authenticateAdmin = async (req, res, next) => { + try { + const {password} = req.body; + const {admin} = res.locals; + + if (admin === undefined) { + return res.status(401).json({message: 'Unauthorized: Admin'}); + } + + const match = await bcrypt.compare(password, admin.password); + + if (match) { + res.locals.data = { + 'message': 'Login successful', + 'status': 'success', + }; + next(); + } else { + res.status(401).json({message: 'Incorrect admin email or password.'}); + } + } catch (err) { + console.log(err); + res.status(500).json({error: 'Internal Server Error'}); + + } +} + module.exports = { getEventRequests, getAllEventRequests, @@ -181,4 +208,5 @@ module.exports = { processEventRequest, getAdminByEmail, createAdminMiddleware, + authenticateAdmin, }; \ No newline at end of file diff --git a/backend/routes/AdminRouter.js b/backend/routes/AdminRouter.js index f9b04d1..d5bd92b 100644 --- a/backend/routes/AdminRouter.js +++ b/backend/routes/AdminRouter.js @@ -19,6 +19,7 @@ const { processEventRequest, getAdminByEmail, createAdminMiddleware, + authenticateAdmin, } = require('../controllers/AdminController'); const sendSuccessResponse = require('../middleware/successResponse'); @@ -37,9 +38,7 @@ router.post('/violations/:vendorId', verify('admin'), createVendorViolation, sen router.delete('/violations/:violationId', verify('admin'), deleteVendorViolation, sendSuccessResponse); -router.post('/login', getAdminByEmail, signAdminToken, (req, res) => { - res.status(200).json({status: 'success'}); -}); +router.post('/login', getAdminByEmail, authenticateAdmin, signAdminToken, sendSuccessResponse); // UNFINISHED: Create an admin account // Useful for creating an admin account for testing purposes. Password in database needs to be hashed for login to work properly. diff --git a/backend/routes/VendorRouter.js b/backend/routes/VendorRouter.js index 8bc39b1..ab056d3 100644 --- a/backend/routes/VendorRouter.js +++ b/backend/routes/VendorRouter.js @@ -27,9 +27,7 @@ router.get('/', getVendors, sendSuccessResponse); router.get('/:vendorId', getVendorById, sendSuccessResponse); // Creates a new vendor -router.post('/', createVendor, (req, res) => { - res.status(200).json({status: 'success'}); -}); +router.post('/', createVendor, sendSuccessResponse); // Create Vendor event request router.post('/events/request', verify('vendor'), createEventRequest, sendSuccessResponse); diff --git a/sql-scripts/init.sql b/sql-scripts/init.sql index b85945a..4f8d690 100644 --- a/sql-scripts/init.sql +++ b/sql-scripts/init.sql @@ -1,5 +1,7 @@ --- Create the tables +-- Enable pgcrypto extension for password hashing +CREATE EXTENSION IF NOT EXISTS pgcrypto; +-- Create the tables -- Admins table CREATE TABLE IF NOT EXISTS Admins ( admin_id SERIAL PRIMARY KEY, @@ -56,4 +58,8 @@ CREATE TABLE IF NOT EXISTS EventRequests ( -- Indexes (as per the diagram) CREATE INDEX idx_admin_email ON Admins(email); -CREATE INDEX idx_vendor_email ON Vendors(email); \ No newline at end of file +CREATE INDEX idx_vendor_email ON Vendors(email); + +-- Insert initial admin user - Uncomment and modify for initial setup +INSERT INTO Admins (name, email, password) +VALUES ('Admin', 'admin@pim.com', crypt('pim', gen_salt('bf'))); \ No newline at end of file From 2fa67a0dbb339ddf254572b01d14233af42f22ad Mon Sep 17 00:00:00 2001 From: Eric Fithian Date: Wed, 28 Feb 2024 18:38:11 -0700 Subject: [PATCH 2/6] tests not working but vendor auth working and user is stored in context --- backend/controllers/AuthController.js | 4 +- backend/index.js | 17 +++++- frontend/src/config.js | 2 +- frontend/src/index.jsx | 17 +++--- frontend/src/objects/Permission.js | 9 ---- frontend/src/objects/User.js | 34 ++---------- frontend/src/routes/login.jsx | 16 ++++-- frontend/src/routes/register.jsx | 15 ++++-- .../src/services/Admins/AdminsRepository.js | 24 +++++++++ frontend/src/services/Admins/AdminsService.js | 16 ++++++ frontend/src/services/HttpClient.js | 23 ++++++-- .../src/services/Vendors/VendorsRepository.js | 7 ++- .../src/services/Vendors/VendorsService.js | 1 + frontend/src/services/handleLogin.js | 9 ---- frontend/src/services/handleRegister.js | 12 ----- frontend/src/tests/integration/admin.test.js | 52 +++++++++++++++++++ 16 files changed, 173 insertions(+), 85 deletions(-) delete mode 100644 frontend/src/objects/Permission.js create mode 100644 frontend/src/services/Admins/AdminsRepository.js create mode 100644 frontend/src/services/Admins/AdminsService.js delete mode 100644 frontend/src/services/handleLogin.js delete mode 100644 frontend/src/services/handleRegister.js create mode 100644 frontend/src/tests/integration/admin.test.js diff --git a/backend/controllers/AuthController.js b/backend/controllers/AuthController.js index 14d0487..39ffb0c 100644 --- a/backend/controllers/AuthController.js +++ b/backend/controllers/AuthController.js @@ -13,7 +13,7 @@ const signToken = async (req, res, next) => { // Sign the token with JWT_SECRET const token = await jwt.sign(res.locals.vendor, process.env.JWT_SECRET); // Return the token in a cookie - res.cookie('auth', token, {httpOnly: true, secure: false}); + res.cookie('auth', token, {secure: false}); next(); }; @@ -62,7 +62,7 @@ const signAdminToken = async (req, res, next) => { const token = await jwt.sign(res.locals.admin, process.env.JWT_SECRET); // Return the token in a cookie - res.cookie('auth_pim', token, {httpOnly: true, secure: false}); + res.cookie('auth_pim', token, {secure: false}); next(); }; diff --git a/backend/index.js b/backend/index.js index 5e79bf3..f11fd10 100644 --- a/backend/index.js +++ b/backend/index.js @@ -18,7 +18,22 @@ app.use(errorHandler({ dumbExceptions: true, showStack: true })); // Allows cross origin requests const cors = require('cors'); -app.use(cors()); + +// Allowed origins +const allowedOrigins = ['http://localhost:3000', 'http://localhost:4000']; + +// CORS options to dynamically match the allowed origins and allow credentials +const corsOptionsDelegate = function (req, callback) { + let corsOptions; + if (allowedOrigins.indexOf(req.header('Origin')) !== -1) { + corsOptions = { origin: true, credentials: true }; // Reflect (enable) the requested origin in the CORS response + } else { + corsOptions = { origin: false }; // Disable CORS for this request + } + callback(null, corsOptions); // Callback expects two parameters: error and options +}; + +app.use(cors(corsOptionsDelegate)); // Parses cookies attached to the client request object const cookieParser = require('cookie-parser'); diff --git a/frontend/src/config.js b/frontend/src/config.js index 1b4f02a..c2895fb 100644 --- a/frontend/src/config.js +++ b/frontend/src/config.js @@ -1,6 +1,6 @@ const config = { environment: 'prod', - baseUrl: 'http://172.18.0.1:3001/api', + baseUrl: 'http://localhost:3001/api', }; export default config; diff --git a/frontend/src/index.jsx b/frontend/src/index.jsx index 53acae8..23d552f 100644 --- a/frontend/src/index.jsx +++ b/frontend/src/index.jsx @@ -28,14 +28,18 @@ import MockVendorService from './services/MockServices/MockVendorService.js'; import EventsService from './services/Events/EventsService.js'; import VendorsService from './services/Vendors/VendorsService.js'; -import {handleLoginVendor} from './services/handleLogin.js'; -import {handleRegister} from './services/handleRegister.js'; +// import {handleLoginVendor} from './services/handleLogin.js'; +// import {handleRegister} from './services/handleRegister.js'; // Import configuration variables import config from './config.js'; +// Import HttpClient +import HttpClient from './services/HttpClient.js'; + let eventService; let vendorService; +let httpClient; if (config.environment == 'dev') { MockVendorService.init(); @@ -46,10 +50,11 @@ if (config.environment == 'dev') { } else if (config.environment == 'prod') { // Load base url for the backend const baseUrl = config.baseUrl; + httpClient = new HttpClient(baseUrl); // Initilize Services - const eventsService = new EventsService(baseUrl); - const vendorsService = new VendorsService(baseUrl); + const eventsService = new EventsService(httpClient); + const vendorsService = new VendorsService(httpClient); eventService = eventsService; vendorService = vendorsService; @@ -66,12 +71,12 @@ const router = createBrowserRouter([ }, { path: '/login', - element: , + element: , }, { path: '/register', - element: , + element: , }, { path: '/reset_password', diff --git a/frontend/src/objects/Permission.js b/frontend/src/objects/Permission.js deleted file mode 100644 index 79e1e4b..0000000 --- a/frontend/src/objects/Permission.js +++ /dev/null @@ -1,9 +0,0 @@ -/* -Permission enum -*/ -const Permission = { - Admin: 'admin', - Vendor: 'vendor', -}; - -export default Permission; diff --git a/frontend/src/objects/User.js b/frontend/src/objects/User.js index 6a31089..2555073 100644 --- a/frontend/src/objects/User.js +++ b/frontend/src/objects/User.js @@ -1,41 +1,15 @@ -import Permission from './Permission.js'; -import Cookies from 'js-cookie'; -import {jwtDecode} from 'jwt-decode'; + /* Storage mechanism for the currently logged-in user. */ export default class User { - constructor(id, name, email, permission, phone_number=null, website=null) { + constructor(id, name, email, isadmin, phoneNumber=null, website=null) { this.id = id; this.name = name; this.email = email; - this.permission = permission; - this.phone_number = phone_number; + this.isadmin = isadmin; + this.phoneNumber = phoneNumber; this.website = website; } - - isAdmin() { - return this.permission === Permission.Admin; - } - - isVendor() { - return this.permission === Permission.Vendor; - } - - static createFromCookie() { - if (Cookies.get('auth_pim') != undefined) { - const cookie = Cookies.get('auth_pim'); - const decode = jwtDecode(cookie); - - return User(decode.admin_id, decode.name, decode.email, Permission.Admin, null, null); - } else if ( Cookies.get('auth') != undefined ) { - const cookie = Cookies.get('auth'); - const decode = jwtDecode(cookie); - - return User(decode.vendor_id, decode.name, decode.email, Permission.Vendor, decode.phone_number, decode.website); - } else { - return undefined; - } - } } diff --git a/frontend/src/routes/login.jsx b/frontend/src/routes/login.jsx index 0d21196..942cf91 100644 --- a/frontend/src/routes/login.jsx +++ b/frontend/src/routes/login.jsx @@ -6,19 +6,20 @@ import {useContext} from 'react'; import {Context} from '../services/context'; import PropTypes from 'prop-types'; -export default function Login({loginService}) { +export default function Login({vendorService}) { const [email, setEmail] = useState(''); const [pass, setPass] = useState(''); const navigate = useNavigate(); - const {setMessage, setBad} = useContext(Context); + const {setMessage, setBad, setUser} = useContext(Context); async function handleLogin() { const data = {email: email, password: pass}; - const user = await loginService(data); + const user = await vendorService.authenticateVendor(data); console.log(user); if (user != undefined) { + setUser(vendorService.httpClient.user); setBad(false); setMessage('Logged in succesfully'); navigate('/events'); @@ -43,7 +44,7 @@ export default function Login({loginService}) {
setEmail(e.target.value)}>
@@ -77,5 +78,10 @@ export default function Login({loginService}) { } Login.propTypes = { - loginService: PropTypes.func.isRequired, + vendorService: PropTypes.shape( { + authenticateVendor: PropTypes.func.isRequired, + httpClient: PropTypes.shape( { + user: PropTypes.object, + }).isRequired, + }).isRequired, }; diff --git a/frontend/src/routes/register.jsx b/frontend/src/routes/register.jsx index c25929b..42387a9 100644 --- a/frontend/src/routes/register.jsx +++ b/frontend/src/routes/register.jsx @@ -6,9 +6,10 @@ import {Context} from '../services/context'; import Alert from '../components/alert.jsx'; import {useContext} from 'react'; import PropTypes from 'prop-types'; +import Vendor from '../objects/Vendor.js'; -export default function Register({registerService}) { +export default function Register({vendorService}) { const [name, setName] = useState(''); const [pass, setPass] = useState(''); const [pass2, setPass2] = useState(''); @@ -19,9 +20,11 @@ export default function Register({registerService}) { const navigate = useNavigate(); async function handleRegister() { - const data = {name: name, email: email, password: pass, website: website, phoneNumber: phone}; + const vendor = new Vendor(name, email, website, phone); - if (await registerService(data)) { + const response = await vendorService.createVendor(vendor, pass); + console.log(response); + if (response) { setBad(false); setMessage('Registered succesfully'); console.log('Registered!'); @@ -60,7 +63,7 @@ export default function Register({registerService}) {
- setPass2(e.target.value)}> + setPass2(e.target.value)}>
{pass !== pass2 && } @@ -79,5 +82,7 @@ export default function Register({registerService}) { } Register.propTypes = { - registerService: PropTypes.func.isRequired, + vendorService: PropTypes.shape({ + createVendor: PropTypes.func.isRequired, + }).isRequired, }; diff --git a/frontend/src/services/Admins/AdminsRepository.js b/frontend/src/services/Admins/AdminsRepository.js new file mode 100644 index 0000000..d55a6f3 --- /dev/null +++ b/frontend/src/services/Admins/AdminsRepository.js @@ -0,0 +1,24 @@ +export default class AdminsRepository { + constructor(httpClient) { + this.httpClient = httpClient; + } + async authenticateAdmin(adminData) { + try { + const response = await this.httpClient.axiosInstance.post('admins/login', adminData); + // this.httpClient.processCookie(response.headers['set-cookie'][0]); + return response; + } catch (error) { + console.error('Error logging in admin:'); + } + } + + async createAdmin(adminData) { + try { + const response = await this.httpClient.axiosInstance.post('/admins', adminData); + return response.data; + } catch (error) { + console.error('Error creating admin:'); + throw error; + } + } +} diff --git a/frontend/src/services/Admins/AdminsService.js b/frontend/src/services/Admins/AdminsService.js new file mode 100644 index 0000000..daa9b6f --- /dev/null +++ b/frontend/src/services/Admins/AdminsService.js @@ -0,0 +1,16 @@ +import AdminsRepository from './AdminsRepository.js'; + +export default class AdminsService { + constructor(httpClient) { + this.adminsRepository = new AdminsRepository(httpClient); + } + + async authenticateAdmin(adminData) { + const response = await this.adminsRepository.authenticateAdmin(adminData); + return response; + } + + async createAdmin(adminData) { + return await this.adminsRepository.createAdmin(adminData); + } +} diff --git a/frontend/src/services/HttpClient.js b/frontend/src/services/HttpClient.js index 6d802d9..3288472 100644 --- a/frontend/src/services/HttpClient.js +++ b/frontend/src/services/HttpClient.js @@ -1,4 +1,7 @@ import axios from 'axios'; +import Cookies from 'js-cookie'; +import {jwtDecode} from 'jwt-decode'; +import User from '../objects/User.js'; class HttpClient { constructor(baseURL) { @@ -6,12 +9,24 @@ class HttpClient { baseURL: baseURL, withCredentials: true, }); - this.cookie = ''; + this.user = null; } - setCookie(cookie) { - this.cookie = cookie; - this.axiosInstance.defaults.headers.common['Cookie'] = cookie; + processCookie() { + // need to do this for testing? + // this.axiosInstance.defaults.headers.common['Cookie'] = cookie; + + if (Cookies.get('auth_pim') != undefined) { + const cookie = Cookies.get('auth_pim'); + const decode = jwtDecode(cookie); + + this.user = new User(decode.admin_id, decode.name, decode.email, true, null, null); + } else if ( Cookies.get('auth') != undefined ) { + const cookie = Cookies.get('auth'); + const decode = jwtDecode(cookie); + + this.user = new User(decode.vendor_id, decode.name, decode.email, false, decode.phone_number, decode.website); + } } } diff --git a/frontend/src/services/Vendors/VendorsRepository.js b/frontend/src/services/Vendors/VendorsRepository.js index 62c207a..63dc7fa 100644 --- a/frontend/src/services/Vendors/VendorsRepository.js +++ b/frontend/src/services/Vendors/VendorsRepository.js @@ -25,7 +25,12 @@ export default class VendorsRepository { async authenticateVendor(vendorData) { try { const response = await this.httpClient.axiosInstance.post('vendors/login', vendorData); - this.httpClient.setCookie(response.headers['set-cookie'][0]); + // if (response.status == 200) { + // this.httpClient.processCookie(response.headers['set-cookie'][0]); + // } else { + // throw new Error('Failed to authenticate vendor'); + // } + this.httpClient.processCookie(); return response; } catch (error) { console.error('Error logging in vendor:'); diff --git a/frontend/src/services/Vendors/VendorsService.js b/frontend/src/services/Vendors/VendorsService.js index 57e4d13..82602df 100644 --- a/frontend/src/services/Vendors/VendorsService.js +++ b/frontend/src/services/Vendors/VendorsService.js @@ -3,6 +3,7 @@ import Vendor from '../../objects/Vendor.js'; export default class VendorsService { constructor(httpClient) { + this.httpClient = httpClient; this.vendorsRepository = new VendorsRepository(httpClient); } diff --git a/frontend/src/services/handleLogin.js b/frontend/src/services/handleLogin.js deleted file mode 100644 index 342e05e..0000000 --- a/frontend/src/services/handleLogin.js +++ /dev/null @@ -1,9 +0,0 @@ -import axios from 'axios'; -import config from '../config.js'; -export async function handleLoginVendor(data) { - const server = axios.create({ - baseURL: config.baseUrl, - }); - const res = await server.post('/vendors/login', data); - return res; -} diff --git a/frontend/src/services/handleRegister.js b/frontend/src/services/handleRegister.js deleted file mode 100644 index 7634e3b..0000000 --- a/frontend/src/services/handleRegister.js +++ /dev/null @@ -1,12 +0,0 @@ -import axios from 'axios'; -import config from '../config.js'; -// function that returns a boolean, true if the registration was successful, false otherwise. -export async function handleRegister(data) { - const server = axios.create({ - baseURL: config.baseUrl, - withCredentials: false, - }); - let success; - await server.post('/vendors', data).then(() => success = true).catch((e) => success = false && console.log('error:', e)); - return success; -} diff --git a/frontend/src/tests/integration/admin.test.js b/frontend/src/tests/integration/admin.test.js new file mode 100644 index 0000000..6e8d161 --- /dev/null +++ b/frontend/src/tests/integration/admin.test.js @@ -0,0 +1,52 @@ +import {expect} from 'chai'; +import AdminsService from '../../services/Admins/AdminsService.js'; +import axios from 'axios'; +import HttpClient from '../../services/HttpClient.js'; + +describe('Admins Service', function() { + this.timeout(10000); // Set timeout for all tests in this describe block + const httpClient = new HttpClient('http://backend-test:4001/api'); + const adminService = new AdminsService(httpClient); + + // Use async/await with the before hook + before(async function() { + await waitForService('http://backend-test:4001/api/health', 100000, 5000); + }); + + describe('Login Admin', function() { + it('should login an admin', async function() { + const adminData = { + email: 'admin@pim.com', + password: 'pim', + }; + try { + const response = await adminService.authenticateAdmin(adminData); + expect(response.status).to.equal(200); + } catch (error) { + // Handle errors or failed assertions + console.error('Test failed', error); + throw error; // Rethrow to make the test fail + } + }); + }); +}); + +// Function to wait for the backend service to be ready +async function waitForService(url, timeout = 5000, interval = 1000) { + console.log(`Waiting for service at ${url} to be ready...`); + const startTime = Date.now(); + + while (true) { + try { + await axios.get(url); + console.log(`Service at ${url} is ready.`); + break; // Exit loop if request succeeds + } catch (error) { + console.log(`Service at ${url} is not ready yet: ${error.message}`); + if (Date.now() - startTime > timeout) { + throw new Error(`Service at ${url} did not become ready in time: ${error.message}`); + } + await new Promise((resolve) => setTimeout(resolve, interval)); // Wait before next attempt + } + } +} From 648471123cb5bda511e88425de6a990b5316d473 Mon Sep 17 00:00:00 2001 From: Eric Fithian Date: Wed, 28 Feb 2024 20:08:43 -0700 Subject: [PATCH 3/6] login cookie auth and user set working, cookie testing also implemented --- backend/controllers/AuthController.js | 2 ++ frontend/src/index.jsx | 17 ++++------------- frontend/src/objects/User.js | 8 ++++++-- frontend/src/routes/login.jsx | 2 +- frontend/src/services/HttpClient.js | 19 +++++++++---------- .../src/services/Vendors/VendorsRepository.js | 6 +----- frontend/src/tests/integration/admin.test.js | 2 ++ frontend/src/tests/integration/vendor.test.js | 3 +++ 8 files changed, 28 insertions(+), 31 deletions(-) diff --git a/backend/controllers/AuthController.js b/backend/controllers/AuthController.js index 39ffb0c..1423828 100644 --- a/backend/controllers/AuthController.js +++ b/backend/controllers/AuthController.js @@ -15,6 +15,8 @@ const signToken = async (req, res, next) => { // Return the token in a cookie res.cookie('auth', token, {secure: false}); + console.log('Token:', token); + next(); }; diff --git a/frontend/src/index.jsx b/frontend/src/index.jsx index a33e6ef..a40fb81 100644 --- a/frontend/src/index.jsx +++ b/frontend/src/index.jsx @@ -28,11 +28,6 @@ import MockVendorService from './services/MockServices/MockVendorService.js'; import EventsService from './services/Events/EventsService.js'; import VendorsService from './services/Vendors/VendorsService.js'; -// import {handleLoginVendor} from './services/handleLogin.js'; -// import {handleRegister} from './services/handleRegister.js'; - -import HttpClient from './services/HttpClient.js'; - // Import configuration variables import config from './config.js'; @@ -52,17 +47,13 @@ if (config.environment == 'dev') { } else if (config.environment == 'prod') { // Load base url for the backend const baseUrl = config.baseUrl; - httpClient = new HttpClient(baseUrl); - // Create HttpClient - const httpClient = new HttpClient(baseUrl); + // Initialize HttpClient + httpClient = new HttpClient(baseUrl); // Initilize Services - const eventsService = new EventsService(httpClient); - const vendorsService = new VendorsService(httpClient); - - eventService = eventsService; - vendorService = vendorsService; + eventService = new EventsService(httpClient); + vendorService = new VendorsService(httpClient); } const router = createBrowserRouter([ diff --git a/frontend/src/objects/User.js b/frontend/src/objects/User.js index 2555073..5a73da6 100644 --- a/frontend/src/objects/User.js +++ b/frontend/src/objects/User.js @@ -1,5 +1,4 @@ - - +import {jwtDecode} from 'jwt-decode'; /* Storage mechanism for the currently logged-in user. */ @@ -12,4 +11,9 @@ export default class User { this.phoneNumber = phoneNumber; this.website = website; } + + static newUserFromCookie(cookie, isadmin) { + const decode = jwtDecode(cookie); + return new User(decode.admin_id, decode.name, decode.email, isadmin, null, null); + } } diff --git a/frontend/src/routes/login.jsx b/frontend/src/routes/login.jsx index 892a9a1..a6a18c6 100644 --- a/frontend/src/routes/login.jsx +++ b/frontend/src/routes/login.jsx @@ -21,7 +21,7 @@ export default function Login({vendorService}) { setBad(false); setMessage('Logged in succesfully'); navigate('/events'); - console.log('Logged in!'); + console.log('Logged in as user: ', vendorService.httpClient.user); } else if (loginResponse.status == 401) { setBad(true); setMessage('Bad Request. Check username and password.'); diff --git a/frontend/src/services/HttpClient.js b/frontend/src/services/HttpClient.js index 5ffe2ec..0a5e39a 100644 --- a/frontend/src/services/HttpClient.js +++ b/frontend/src/services/HttpClient.js @@ -1,32 +1,31 @@ import axios from 'axios'; import Cookies from 'js-cookie'; -import {jwtDecode} from 'jwt-decode'; import User from '../objects/User.js'; class HttpClient { constructor(baseURL) { this.axiosInstance = axios.create({ baseURL: baseURL, + withCredentials: true, }); this.user = null; } processCookie() { - // need to do this for testing? - // this.axiosInstance.defaults.headers.common['Cookie'] = cookie; - if (Cookies.get('auth_pim') != undefined) { const cookie = Cookies.get('auth_pim'); - const decode = jwtDecode(cookie); - - this.user = new User(decode.admin_id, decode.name, decode.email, true, null, null); + this.user = User.newUserFromCookie(cookie, true); } else if ( Cookies.get('auth') != undefined ) { const cookie = Cookies.get('auth'); - const decode = jwtDecode(cookie); - - this.user = new User(decode.vendor_id, decode.name, decode.email, false, decode.phone_number, decode.website); + this.user = User.newUserFromCookie(cookie, false); } } + + // for testing only + manuallySetCookie(cookie, isadmin) { + this.axiosInstance.defaults.headers.common['Cookie'] = cookie; + this.user = User.newUserFromCookie(cookie, isadmin); + } } export default HttpClient; diff --git a/frontend/src/services/Vendors/VendorsRepository.js b/frontend/src/services/Vendors/VendorsRepository.js index ca3370e..ff45220 100644 --- a/frontend/src/services/Vendors/VendorsRepository.js +++ b/frontend/src/services/Vendors/VendorsRepository.js @@ -26,11 +26,7 @@ export default class VendorsRepository { async authenticateVendor(vendorData) { try { const response = await this.httpClient.axiosInstance.post('vendors/login', vendorData); - // if (response.status == 200) { - // this.httpClient.processCookie(response.headers['set-cookie'][0]); - // } else { - // throw new Error('Failed to authenticate vendor'); - // } + console.log(response.headers['set-cookie']); this.httpClient.processCookie(); return response; } catch (error) { diff --git a/frontend/src/tests/integration/admin.test.js b/frontend/src/tests/integration/admin.test.js index 6e8d161..d239579 100644 --- a/frontend/src/tests/integration/admin.test.js +++ b/frontend/src/tests/integration/admin.test.js @@ -21,6 +21,8 @@ describe('Admins Service', function() { }; try { const response = await adminService.authenticateAdmin(adminData); + const cookie = response.headers['set-cookie'][0]; + httpClient.manuallySetCookie(cookie, true); expect(response.status).to.equal(200); } catch (error) { // Handle errors or failed assertions diff --git a/frontend/src/tests/integration/vendor.test.js b/frontend/src/tests/integration/vendor.test.js index 2e52486..8429af6 100644 --- a/frontend/src/tests/integration/vendor.test.js +++ b/frontend/src/tests/integration/vendor.test.js @@ -62,7 +62,10 @@ describe('Vendors Service', function() { const password = 'password'; try { const response = await vendorService.authenticateVendor({email, password}); + const cookie = response.headers['set-cookie'][0]; + httpClient.manuallySetCookie(cookie, false); expect(response.status).to.equal(200); + expect(httpClient.user.email).to.equal(email); } catch (error) { console.error('Test failed', error); throw error; From 772f169286f7e442f626d41002e0faa9dbeb388f Mon Sep 17 00:00:00 2001 From: Eric Fithian Date: Wed, 28 Feb 2024 20:35:48 -0700 Subject: [PATCH 4/6] fixed newUserFromCookie Method --- frontend/src/objects/User.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/objects/User.js b/frontend/src/objects/User.js index 5a73da6..e4787ed 100644 --- a/frontend/src/objects/User.js +++ b/frontend/src/objects/User.js @@ -14,6 +14,7 @@ export default class User { static newUserFromCookie(cookie, isadmin) { const decode = jwtDecode(cookie); - return new User(decode.admin_id, decode.name, decode.email, isadmin, null, null); + if (isadmin) return new User(decode.admin_id, decode.name, decode.email, isadmin, null, null); + return new User(decode.vendor_id, decode.name, decode.email, isadmin, decode.phone_number, decode.website); } } From 0298b8e45340712df7bb5c71b60689d36e16b401 Mon Sep 17 00:00:00 2001 From: Eric Fithian Date: Wed, 28 Feb 2024 20:38:53 -0700 Subject: [PATCH 5/6] added httpClient as instance variable to services --- frontend/src/services/Admins/AdminsService.js | 1 + frontend/src/services/Events/EventsService.js | 1 + 2 files changed, 2 insertions(+) diff --git a/frontend/src/services/Admins/AdminsService.js b/frontend/src/services/Admins/AdminsService.js index daa9b6f..13191de 100644 --- a/frontend/src/services/Admins/AdminsService.js +++ b/frontend/src/services/Admins/AdminsService.js @@ -2,6 +2,7 @@ import AdminsRepository from './AdminsRepository.js'; export default class AdminsService { constructor(httpClient) { + this.httpClient = httpClient; this.adminsRepository = new AdminsRepository(httpClient); } diff --git a/frontend/src/services/Events/EventsService.js b/frontend/src/services/Events/EventsService.js index 82da5d1..f208e5d 100644 --- a/frontend/src/services/Events/EventsService.js +++ b/frontend/src/services/Events/EventsService.js @@ -3,6 +3,7 @@ import Event from '../../objects/Event.js'; export default class EventsService { constructor(httpClient) { + this.httpClient = httpClient; this.eventsRepository = new EventsRepository(httpClient); } From 935d70cfea72dd5b16d079d00bff8f6941f52b26 Mon Sep 17 00:00:00 2001 From: nh602 Date: Thu, 29 Feb 2024 18:47:19 +0000 Subject: [PATCH 6/6] Remove password from admin cookie. Frontend doesn't need to see the password hash. --- backend/controllers/AuthController.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/controllers/AuthController.js b/backend/controllers/AuthController.js index 1423828..165f066 100644 --- a/backend/controllers/AuthController.js +++ b/backend/controllers/AuthController.js @@ -60,6 +60,9 @@ const signAdminToken = async (req, res, next) => { return res.status(401).json({message: 'Unauthorized: Admin'}); } + // Remove admin password from cookie + delete res.locals.admin['password']; + // Sign the token with JWT_SECRET const token = await jwt.sign(res.locals.admin, process.env.JWT_SECRET);