diff --git a/db/models/session.js b/db/models/session.js new file mode 100644 index 0000000..bda014f --- /dev/null +++ b/db/models/session.js @@ -0,0 +1,25 @@ +const pg = require('../pg') +const joinjs = require('join-js').default; +const debug = require('debug')('sos:db:user') +const mappings = require('../mappings') + +const session = module.exports = {} + +session.create = async (user_uuid, ip_address, user_agent, referer, origin_link_uuid) => { + const query = { + text: 'select * from login_user_session($1, $2, $3, $4, $5, $6)', + values: [ + user_uuid, + '2 hours', + ip_address, + user_agent, + referer, + origin_link_uuid + ] + } + + debug(query); + + const {rows} = await pg.query(query) + return joinjs.map(rows, mappings, 'sessionMap', 'session_'); +} \ No newline at end of file diff --git a/db/models/user.js b/db/models/user.js index 2c5f73f..bbcc3cc 100644 --- a/db/models/user.js +++ b/db/models/user.js @@ -4,6 +4,7 @@ const debug = require('debug')('sos:db:user') const mappings = require('../mappings') const bcrypt = require('bcrypt') +const session = require('./session') const user = module.exports = {} @@ -17,14 +18,54 @@ user.findById = async (user_uuid) => { debug(query); - const result = await pg.query(query) - return joinjs.map(result.rows, mappings, 'userMap', 'user_')[0]; + const {rows} = await pg.query(query) + return joinjs.map(rows, mappings, 'userMap', 'user_')[0]; +} + +user.findByEmail = async (email) => { + const query = { + text: 'select * from "user" where user_email = $1', + values: [ + email + ] + } + + debug(query); + + const {rows} = await pg.query(query) + return joinjs.map(rows, mappings, 'userMap', 'user_')[0] } user.register = async (email, password) => { const hash = await bcrypt.hash(password, process.env.PW_SALTROUNDS || 10) const query = { + text: 'select * from register_user($1, $2)', + values: [ + email, + hash + ] + } + debug(query); + + const {rows} = await pg.query(query) + return joinjs.map(rows, mappings, 'userMap', 'user_')[0]; +} + +user.login = async (email, password) => { + const user = await user.findByEmail(email) + + if(!user){ + // Avoid early exit timing difference + await bcrypt.hash(password) + throw new Error("User not found") } + + const passwordCorrect = await bcrypt.compare(password, user.password_hash) + + if(!passwordCorrect) + throw new Error("Password incorrect") + + return user } diff --git a/db/sql/3-functions.sql b/db/sql/3-functions.sql new file mode 100644 index 0000000..1457f1c --- /dev/null +++ b/db/sql/3-functions.sql @@ -0,0 +1,53 @@ +create or replace function public.register_user(_email text, _password_hash text) + returns setof public.user + language plpgsql +as $function$ +declare + _user_uuid uuid; +begin + insert into "user" ( + user_email, + user_password_hash + ) values ( + _email, + _password_hash + ) returning user_uuid into _user_uuid; + + return query select * from "user" where user_uuid = _user_uuid; +end; $function$; + +create or replace function public.validate_session(_session_uuid uuid) + returns setof public.v_session + language plpgsql +as $function$ +begin + return query select * from v_session + where session_uuid = _session_uuid + and session_time_last_active + session_timeout_length < now(); +end; $function$; + +create or replace function public.login_user_session(_user_uuid uuid, _timeout_length interval, _ip_addr varchar(50), _user_agent varchar(500), _referer varchar(500), _link uuid) + returns setof public.v_session + language plpgsql +as $function$ +declare + _session_uuid uuid; +begin + insert into "session" ( + session_user_uuid, + session_timeout_length, + session_ip_address, + session_user_agent, + session_referer, + session_originating_link + ) values ( + _user_uuid, + _timeout_length, + _ip_addr, + _user_agent, + _referer, + _link + ) returning session_uuid into _session_uuid; + + return query select * from public.validate_session(_session_uuid); +end; $function$; diff --git a/package.json b/package.json index 9d3f6f0..44fc829 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "dependencies": { "axios": "^0.19.2", "bcrypt": "^3.0.8", + "body-parser": "^1.19.0", "debug": "^4.1.1", "dotenv": "^8.2.0", "express": "^4.17.1",