feat: Split v1 and v2 apis
This commit is contained in:
124
v2/server/src/Apps/Account/Controller/index.js
Normal file
124
v2/server/src/Apps/Account/Controller/index.js
Normal file
@@ -0,0 +1,124 @@
|
||||
'use strict';
|
||||
/// Router instance
|
||||
const router = require('express').Router();
|
||||
const Application = require('../Domain');
|
||||
|
||||
function dummy_middleware( req, res ){
|
||||
return res.status(500).send({ error:"Not implemented yet" });
|
||||
}
|
||||
|
||||
router.post('/register', dummy_middleware );
|
||||
|
||||
router.post('/authorize', async( req, res ) => {
|
||||
try{
|
||||
const email = req.body.email;
|
||||
const password = req.body.password;
|
||||
const data = await Application.authorize_credentials( email , password );
|
||||
if( data.error ){
|
||||
const error = data.error;
|
||||
return res.status( error.code ).send( { error : error.msg } );
|
||||
}
|
||||
return res.send( data );
|
||||
}catch(error){
|
||||
console.error( error );
|
||||
return res.status( 500 ).send( { error } );
|
||||
}
|
||||
} );
|
||||
|
||||
router.get('/authorize/:session_token', async( req, res ) => {
|
||||
try{
|
||||
const session_token = req.params.session_token;
|
||||
const data = await Application.authorize_token( session_token );
|
||||
if( data.error ){
|
||||
const error = data.error;
|
||||
return res.status( error.code ).send( { error : error.msg } );
|
||||
}
|
||||
return res.send( data );
|
||||
}catch(error){
|
||||
console.error( error );
|
||||
return res.status( 500 ).send( { error } );
|
||||
}
|
||||
} );
|
||||
|
||||
router.get('/check-account/:email', async( req, res ) => {
|
||||
try{
|
||||
const email = req.params.email;
|
||||
const data = await Application.check_account( email );
|
||||
if( data.error ){
|
||||
const error = data.error;
|
||||
return res.status( error.code ).send( { error : error.msg } );
|
||||
}
|
||||
return res.send( data );
|
||||
}catch(error){
|
||||
console.error( error );
|
||||
return res.status( 500 ).send( { error } );
|
||||
}
|
||||
} );
|
||||
|
||||
router.post('/signup', async(req,res) => {
|
||||
try{
|
||||
const email = req.body.email;
|
||||
const password = req.body.password;
|
||||
const data = await Application.genOTPChecksum( email, password, "signup" );
|
||||
if( data.error ){
|
||||
const error = data.error;
|
||||
return res.status( error.code ).send( { error : error.msg } );
|
||||
}
|
||||
return res.send( data );
|
||||
}catch(error){
|
||||
console.error( error );
|
||||
return res.status( 500 ).send( { error } );
|
||||
}
|
||||
} );
|
||||
router.patch('/signup', async(req,res) => {
|
||||
try{
|
||||
const email = req.body.email;
|
||||
const password = req.body.password;
|
||||
const otp = req.body.otp;
|
||||
const checksum = req.body.checksum;
|
||||
const data = await Application.signup( email , password, otp, checksum);
|
||||
if( data.error ){
|
||||
const error = data.error;
|
||||
return res.status( error.code ).send( { error : error.msg } );
|
||||
}
|
||||
return res.send( data );
|
||||
}catch(error){
|
||||
console.error( error );
|
||||
return res.status( 500 ).send( { error } );
|
||||
}
|
||||
} );
|
||||
|
||||
router.post('/recover', async(req,res) => {
|
||||
try{
|
||||
const email = req.body.email;
|
||||
const password = req.body.password;
|
||||
const data = await Application.genOTPChecksum( email, password, "recover" );
|
||||
if( data.error ){
|
||||
const error = data.error;
|
||||
return res.status( error.code ).send( { error : error.msg } );
|
||||
}
|
||||
return res.send( data );
|
||||
}catch(error){
|
||||
console.error( error );
|
||||
return res.status( 500 ).send( { error } );
|
||||
}
|
||||
} );
|
||||
router.patch('/recover', async(req,res) => {
|
||||
try{
|
||||
const email = req.body.email;
|
||||
const password = req.body.password;
|
||||
const otp = req.body.otp;
|
||||
const checksum = req.body.checksum;
|
||||
const data = await Application.recover( email , password, otp, checksum);
|
||||
if( data.error ){
|
||||
const error = data.error;
|
||||
return res.status( error.code ).send( { error : error.msg } );
|
||||
}
|
||||
return res.send( data );
|
||||
}catch(error){
|
||||
console.error( error );
|
||||
return res.status( 500 ).send( { error } );
|
||||
}
|
||||
} );
|
||||
|
||||
module.exports = router;
|
||||
202
v2/server/src/Apps/Account/Domain/index.js
Normal file
202
v2/server/src/Apps/Account/Domain/index.js
Normal file
@@ -0,0 +1,202 @@
|
||||
'use strict';
|
||||
|
||||
const Repository = require('../Repository');
|
||||
const jsonwebtoken = require('jsonwebtoken');
|
||||
const { toSha256,
|
||||
publishEvent,
|
||||
jwtRenewalTimeout,
|
||||
jwtTimeout,
|
||||
jwtOptions,
|
||||
jwtSecret,
|
||||
tokenSecret,
|
||||
pwdSecret,
|
||||
genErrorResponse } = require('../Ports/Interfaces');
|
||||
|
||||
class Account {
|
||||
constructor(){
|
||||
}
|
||||
|
||||
genOTP( email ){
|
||||
const len = 5;
|
||||
const shacode = toSha256( email + new Date() + tokenSecret );
|
||||
const otp_hex = shacode.slice(0 , len );
|
||||
const otp_dec = Number.parseInt( otp_hex , 16 );
|
||||
return ""+otp_dec;
|
||||
}
|
||||
|
||||
genSafePassword( password ){
|
||||
return toSha256( password + pwdSecret );
|
||||
}
|
||||
|
||||
async check_account( email ){
|
||||
const projection = ["id","email","password","company_id"];
|
||||
const user = await Repository.getByEmail( email , projection );
|
||||
const retVal = {
|
||||
has_account:false,
|
||||
isVerified:false,
|
||||
has_password:false
|
||||
};
|
||||
|
||||
if( !user ){
|
||||
retVal.has_account = false;
|
||||
retVal.isVerified = false;
|
||||
retVal.has_password = false;
|
||||
}else{
|
||||
retVal.has_account = true;
|
||||
retVal.isVerified = user.isVerified;
|
||||
retVal.has_password = ( !user.password )? false : true;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
async genOTPChecksum( email, password, reason="signup" ){
|
||||
let event_id = reason;
|
||||
|
||||
if( reason === "signup" ){
|
||||
event_id = "getchecksum:signup";
|
||||
|
||||
const it_exists = await Repository.getByEmail( email );
|
||||
if( it_exists ){
|
||||
return genErrorResponse( "User already registered!" );
|
||||
}
|
||||
|
||||
}else if( reason === "recover" ){
|
||||
event_id = "getchecksum:recover";
|
||||
}else{
|
||||
return genErrorResponse( "OPT Event type not defined" , 500 );
|
||||
}
|
||||
|
||||
const otp = this.genOTP( email );
|
||||
|
||||
const content = { OTP : otp, user_name : email, email };
|
||||
|
||||
const checksum_entry = {
|
||||
email,
|
||||
password,
|
||||
otp
|
||||
};
|
||||
|
||||
const checksum = toSha256( JSON.stringify(checksum_entry)).slice(0, 32);
|
||||
|
||||
publishEvent( event_id , content );
|
||||
|
||||
return { checksum };
|
||||
}
|
||||
|
||||
async signup( email, password, otp, checksum ){
|
||||
const it_exists = await Repository.getByEmail( email );
|
||||
|
||||
if( it_exists ){
|
||||
return genErrorResponse( "User already registered!" );
|
||||
}
|
||||
|
||||
const checksum_entry = {email, password, otp};
|
||||
const recomputed_checksum = toSha256( JSON.stringify(checksum_entry)).slice(0, 32);
|
||||
|
||||
if( recomputed_checksum != checksum ){
|
||||
return genErrorResponse( "Wrong OTP" );
|
||||
}
|
||||
|
||||
await Repository.createOne( email, this.genSafePassword( password ) );
|
||||
|
||||
const content = { user_name : email, email };
|
||||
|
||||
publishEvent( "signupconfirmed" , content );
|
||||
|
||||
return await this.authorize_credentials( email, password );
|
||||
}
|
||||
|
||||
async recover( email, password, otp, checksum ){
|
||||
const user = await Repository.getByEmail( email );
|
||||
|
||||
if( !user ){
|
||||
return genErrorResponse( "Email is not registered!" );
|
||||
}
|
||||
|
||||
const checksum_entry = {email, password, otp};
|
||||
const recomputed_checksum = toSha256( JSON.stringify(checksum_entry)).slice(0, 32);
|
||||
|
||||
if( recomputed_checksum != checksum ){
|
||||
return genErrorResponse( "Wrong OTP" );
|
||||
}
|
||||
|
||||
await Repository.updatePassword( user.id, this.genSafePassword( password ) );
|
||||
|
||||
return await this.authorize_credentials( email, password );
|
||||
}
|
||||
|
||||
async authorize_credentials( email, password ){
|
||||
const user = await Repository.findByEmailPassword( email, this.genSafePassword( password ) );
|
||||
|
||||
if( !user ){
|
||||
return genErrorResponse( "Not able to log in", 401 );
|
||||
}
|
||||
const current_date = new Date();
|
||||
const iat = Math.floor( (current_date.getTime())/1000 );
|
||||
const renewal_exp = ( iat + 3600*jwtRenewalTimeout ) * 1000;
|
||||
/**
|
||||
* Renew session token on every login event.
|
||||
* Previous session token is lost
|
||||
*/
|
||||
const session_token = toSha256( `${new Date()}` );
|
||||
const session_token_exp = new Date( renewal_exp );
|
||||
|
||||
await Repository.addSessionToken( user.id, session_token, session_token_exp );
|
||||
|
||||
const payload = {
|
||||
iat: iat,
|
||||
exp: iat + jwtTimeout * 3600,
|
||||
aud: jwtOptions.audience,
|
||||
iss: jwtOptions.audience,
|
||||
sub: user.id,
|
||||
};
|
||||
const jwt = jsonwebtoken.sign( payload , jwtSecret );
|
||||
return {
|
||||
accessToken : jwt,
|
||||
payload : payload,
|
||||
session_token,
|
||||
session_token_exp,
|
||||
user : user
|
||||
};
|
||||
}
|
||||
|
||||
async authorize_token( token ){
|
||||
const user = await Repository.findBySessionToken( token );
|
||||
|
||||
if( !user ){
|
||||
return genErrorResponse( "Invalid Session Token", 401 );
|
||||
}
|
||||
|
||||
const current_date = new Date();
|
||||
const iat = Math.floor( (current_date.getTime())/1000 );
|
||||
const renewal_exp = ( iat + 3600*jwtRenewalTimeout ) * 1000;
|
||||
/**
|
||||
* Renew session token on every login event.
|
||||
* Previous session token is lost
|
||||
*/
|
||||
const session_token = toSha256( `${new Date()}` );
|
||||
const session_token_exp = new Date( renewal_exp );
|
||||
|
||||
await Repository.addSessionToken( user.id, session_token, session_token_exp );
|
||||
|
||||
const payload = {
|
||||
iat: iat,
|
||||
exp: iat + jwtTimeout * 3600,
|
||||
aud: jwtOptions.audience,
|
||||
iss: jwtOptions.audience,
|
||||
sub: user.id,
|
||||
};
|
||||
const jwt = jsonwebtoken.sign( payload , jwtSecret );
|
||||
return {
|
||||
accessToken : jwt,
|
||||
payload : payload,
|
||||
session_token,
|
||||
session_token_exp,
|
||||
user : user
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
module.exports = new Account();
|
||||
9
v2/server/src/Apps/Account/Ports/Events/index.js
Normal file
9
v2/server/src/Apps/Account/Ports/Events/index.js
Normal file
@@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
const App = require('../../App');
|
||||
|
||||
/**
|
||||
* Dictionary of event ids and handlers
|
||||
*/
|
||||
module.exports = {
|
||||
// "event_id" : App.onEvent
|
||||
};
|
||||
38
v2/server/src/Apps/Account/Ports/Interfaces/index.js
Normal file
38
v2/server/src/Apps/Account/Ports/Interfaces/index.js
Normal file
@@ -0,0 +1,38 @@
|
||||
'use strict';
|
||||
|
||||
const { toSha256 } = require('../../../../Shared/ShaUtils');
|
||||
|
||||
const { authentication } = require('../../../../../config/apiConfig.json');
|
||||
|
||||
const { genErrorResponse } = require('../../../../Shared/ErrorResponse');
|
||||
|
||||
const SharedResources = require('../../../../Shared/Resources');
|
||||
|
||||
function publishEvent( event , data = null ){
|
||||
const EventBus = SharedResources.get("SysS:EventManager");
|
||||
const AppEventDomain = "App:Account:"
|
||||
const event_id = AppEventDomain + event;
|
||||
|
||||
console.log( event_id );
|
||||
EventBus.publishEvent( event_id , data );
|
||||
}
|
||||
|
||||
const tokenSecret = authentication.tokenSecret;
|
||||
const jwtRenewalTimeout = authentication.jwtRenewalTimeout;
|
||||
const jwtTimeout = authentication.jwtTimeout;
|
||||
const jwtOptions = authentication.jwtOptions;
|
||||
const jwtSecret = authentication.jwtSecret;
|
||||
|
||||
const pwdSecret = authentication.pwdSecret;
|
||||
|
||||
module.exports = {
|
||||
toSha256,
|
||||
tokenSecret,
|
||||
jwtRenewalTimeout,
|
||||
jwtTimeout,
|
||||
jwtOptions,
|
||||
jwtSecret,
|
||||
pwdSecret,
|
||||
genErrorResponse,
|
||||
publishEvent
|
||||
};
|
||||
3
v2/server/src/Apps/Account/Ports/Public/index.js
Normal file
3
v2/server/src/Apps/Account/Ports/Public/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
'use strict';
|
||||
|
||||
module.export = {};
|
||||
89
v2/server/src/Apps/Account/Repository/Objection/index.js
Normal file
89
v2/server/src/Apps/Account/Repository/Objection/index.js
Normal file
@@ -0,0 +1,89 @@
|
||||
'use strict';
|
||||
|
||||
const { getModel } = require('../../../../Shared/Models/Objection');
|
||||
const Users = getModel('users');
|
||||
const Sessions = getModel('users_sessions');
|
||||
const Companies = getModel('companies');
|
||||
|
||||
class SpecificModelRepository{
|
||||
constructor(){}
|
||||
|
||||
async populate( user ){
|
||||
if( user.company_id ){
|
||||
const company = await Companies.query().findById( user.company_id );
|
||||
user.company = company;
|
||||
}else{
|
||||
user.company = null;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
async getByEmail( email ){
|
||||
const user = await Users.query()
|
||||
.select( "*" )
|
||||
.where("email","=",email).first();
|
||||
return user;
|
||||
}
|
||||
|
||||
async createOne( email, safe_password ){
|
||||
const user = await Users.query().insert({
|
||||
email,
|
||||
password : safe_password,
|
||||
"name":"No name",
|
||||
"last_name":"No lastname",
|
||||
"createdAt" : new Date().toISOString()
|
||||
});
|
||||
return await this.populate( user );
|
||||
}
|
||||
|
||||
async findByEmailPassword( email, safe_password ){
|
||||
const user = await Users.query().select('*')
|
||||
.where("email","=",email)
|
||||
.where("password","=",safe_password)
|
||||
.first();
|
||||
return await this.populate( user );
|
||||
}
|
||||
|
||||
async updateSessionToken( old_token, token, expiration ){
|
||||
const entry = await Sessions.query().select('*').where('token','=',old_token).first();
|
||||
const data = {
|
||||
token,
|
||||
expiration,
|
||||
};
|
||||
if( entry ){
|
||||
return await Sessions.query().patch( data ).where('token','=',old_token).first();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async updatePassword( userId , safe_password ){
|
||||
return await Users.query()
|
||||
.findById( userId )
|
||||
.patch( { password : safe_password } );
|
||||
}
|
||||
|
||||
async addSessionToken( userId , token, expiration ){
|
||||
const entry = await Sessions.query().select('*').where("user_id",'=',userId).first();
|
||||
const data = {
|
||||
token,
|
||||
expiration : expiration.toISOString(),
|
||||
};
|
||||
if( entry ){
|
||||
return await Sessions.query()
|
||||
.findById( entry.id )
|
||||
.patch(data);
|
||||
}else{
|
||||
data.user_id = userId;
|
||||
return await Sessions.query().insert( data );
|
||||
}
|
||||
}
|
||||
|
||||
async findBySessionToken( token ){
|
||||
const session = await Sessions.query().select("*").where("token","=",token).first();
|
||||
const user = await Users.query().findById( session.user_id );
|
||||
return await this.populate( user );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = new SpecificModelRepository();
|
||||
5
v2/server/src/Apps/Account/Repository/index.js
Normal file
5
v2/server/src/Apps/Account/Repository/index.js
Normal file
@@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
const SpecificModelRepository = require('./Objection');
|
||||
|
||||
module.exports = SpecificModelRepository;
|
||||
Reference in New Issue
Block a user