Skip to content

Commit

Permalink
Merge branch 'release/2021-11-16.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
tomw1808 committed Nov 16, 2021
2 parents ac05064 + ea8642a commit 2e2af6b
Show file tree
Hide file tree
Showing 11 changed files with 151 additions and 13 deletions.
33 changes: 29 additions & 4 deletions backend-node/src/controllers/wallet.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ export async function updateEmail(req: Request, res: Response) {
if (email2faVerification === undefined) {
const verificationCode = await updateEmail2fa(user.id);
if (environment !== 'development') {
await sendEmail2FA(verificationCode, newEmail);
await sendEmail2FA(verificationCode, newEmail, user);
}
transaction.commit(); //close the transaction after the 2fa was sent
return successResponse(res, 'sent 2fa code to new email address');
Expand All @@ -223,7 +223,7 @@ export async function updateEmail(req: Request, res: Response) {
stringified_headers: JSON.stringify(req.headers)
});
if (environment !== 'development') {
await sendEmailChanged(newEmail, user.email);
await sendEmailChanged(newEmail, user.email, user);
} //send the old user an info-mail that his email address got updated.

Logger.info({
Expand Down Expand Up @@ -694,7 +694,7 @@ export async function change2FAMethods(req, res) {
const verificationCode = await updateEmail2fa(user.id);

if (environment !== 'development') {
await sendEmail2FA(verificationCode, user.email);
await sendEmail2FA(verificationCode, user.email, user);
}

Logger.info({
Expand Down Expand Up @@ -873,7 +873,7 @@ export async function send2FAEmail(req, res) {
const verificationCode = await updateEmail2fa(user.id);

if (environment !== 'development') {
await sendEmail2FA(verificationCode, user.email);
await sendEmail2FA(verificationCode, user.email, user);
}

Logger.info({
Expand Down Expand Up @@ -1059,3 +1059,28 @@ async function verifyGoogle2FA(user_id: string, code: string, getSeed: boolean =
return user.payload.authenticator === false || authenticator.check(code, user.authenticator_secret);
} else return authenticator.check(code, user.authenticator_secret);
}

export async function updateUserPayload(req, res) {
try {
const key = req.header('key');
const payloadColumn = req.body.column;
const payloadValue = req.body.value;

// Create a new recovery method.
const recovery = await Recovery.findOne({ where: { key, recovery_type_id: 1 } });
const user = await User.findOne({ where: { id: recovery.user_id }});

if(user){
user.payload[payloadColumn] = payloadValue;
user.changed('payload', true);
await user.save();

return successResponse(res, true);
}
} catch (error) {
Logger.error({ source: 'updateUserPayload', data: req.body, message: error.message || error.toString() } );
return errorResponse(res, 'INTERNAL_SERVER_ERROR', 500);
}
//error out in any other case
return errorResponse(res, 'INTERNAL_SERVER_ERROR', 500);
}
6 changes: 6 additions & 0 deletions backend-node/src/database/models/Email_Template.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,10 @@ export class Email_Template extends Model {
})
template_text;

@Column({
type: DataType.ENUM,
values: ['en', 'ru'],
defaultValue: 'en'
})
lang;
}
22 changes: 17 additions & 5 deletions backend-node/src/helpers/functions/email.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
const AWS = require('aws-sdk');

import { Logger } from './winston';
import { Email_Template, Email_Log } from '../../database/models';
import { Email_Template, Email_Log, User } from '../../database/models';

export async function sendEmail2FA(payload, email, user) {
const lang = user.payload.app_lang || 'en';

let email_template = await Email_Template.findOne({ where: { template_name: 'Email 2FA', lang }})
// Fallback in case we add new languages and don't have a template yet.
if(!email_template){
email_template = await Email_Template.findOne({ where: { template_name: 'Email 2FA', lang: 'en' }})
}

export async function sendEmail2FA(payload, email) {
const email_template = await Email_Template.findOne({where: { template_name: 'Email 2FA' }})
const SES = new AWS.SES({
accessKeyId: process.env.ACCESS_KEY_ID,
secretAccessKey: process.env.ACCESS_KEY_SECRET,
Expand Down Expand Up @@ -62,9 +69,14 @@ export async function sendEmail2FA(payload, email) {

}

export async function sendEmailChanged(payload, email) {
export async function sendEmailChanged(payload, email, user) {
const lang = user.payload.app_lang || 'en';
let email_template = await Email_Template.findOne({ where: { template_name: 'Email Changed', lang }});

const email_template = await Email_Template.findOne({where: { template_name: 'Email Changed' }})
// Fallback in case we add new languages and don't have a template yet.
if(!email_template){
email_template = await Email_Template.findOne({ where: { template_name: 'Email Changed', lang: 'en' }})
}

const SES = new AWS.SES({
accessKeyId: process.env.ACCESS_KEY_ID,
Expand Down
1 change: 1 addition & 0 deletions backend-node/src/routes/v1/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ module.exports = function (express) {
router.post('/auth/addRecoveryMethod', WalletController.addRecoveryMethod);
router.post('/auth/getRecoveryMethods', WalletController.getRecoveryMethods);
router.post('/auth/deleteAccount', WalletController.deleteAccount);
router.post('/auth/updateUserPayload', WalletController.updateUserPayload);

/**
* Email Notifications
Expand Down
2 changes: 1 addition & 1 deletion vue/src/components/Change2FAEmail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</div>

<div class="error mt-3" v-if="logonError">
<p>⚠️ <span v-html="logonError"></span></p>
<p>⚠️ <span data-cy="2faEmailError" v-html="logonError"></span></p>
</div>

<button
Expand Down
10 changes: 8 additions & 2 deletions vue/src/components/Footer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,14 @@
</template>

<script lang="ts">
import { Vue, Component, Prop } from 'vue-property-decorator';
import { Prop } from 'vue-property-decorator';
import Component, { mixins } from 'vue-class-component';
import Cookie from 'js-cookie';
import { BackgroundNFT } from '../utils/backgroundNFT';
import { Authenticated, Global } from "@/mixins/mixins";
@Component({})
export default class Footer extends Vue {
export default class Footer extends mixins(Global, Authenticated) {
@Prop()
NFTBackground!: BackgroundNFT | null;
Expand Down Expand Up @@ -112,6 +114,10 @@ export default class Footer extends Vue {
if (lang === 'ar') document.querySelector('html')?.setAttribute('dir', 'rtl');
else document.querySelector('html')?.setAttribute('dir', '');
Cookie.set('locale', lang);
if(this.store.keystore){
this.updateUserPayload({ column: 'app_lang', value: lang });
}
}
}
Expand Down
16 changes: 16 additions & 0 deletions vue/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,22 @@ if (!currentLocale) {
if (lang === 'ar') document.querySelector('html')?.setAttribute('dir', 'rtl');
else document.querySelector('html')?.setAttribute('dir', '');
Cookie.set('locale', lang);

if(store.state.keystore){
store.dispatch('updateUserPayload', { column: 'app_lang', value: lang });
}

} else {
i18n.locale = defaultLocale;
document.querySelector('html')?.setAttribute('lang', defaultLocale);
if (defaultLocale === 'ar') document.querySelector('html')?.setAttribute('dir', 'rtl');
else document.querySelector('html')?.setAttribute('dir', '');
Cookie.set('locale', defaultLocale);

if(store.state.keystore){
store.dispatch('updateUserPayload', { column: 'app_lang', value: defaultLocale });
}

}
} else {
if (!supportedLocales.includes(currentLocale)) {
Expand All @@ -45,11 +55,17 @@ if (!currentLocale) {
if (defaultLocale === 'ar') document.querySelector('html')?.setAttribute('dir', 'rtl');
else document.querySelector('html')?.setAttribute('dir', '');
i18n.locale = defaultLocale;
if(store.state.keystore){
store.dispatch('updateUserPayload', { column: 'app_lang', value: defaultLocale });
}
} else {
i18n.locale = currentLocale;
document.querySelector('html')?.setAttribute('lang', currentLocale);
if (currentLocale === 'ar') document.querySelector('html')?.setAttribute('dir', 'rtl');
else document.querySelector('html')?.setAttribute('dir', '');
if(store.state.keystore){
store.dispatch('updateUserPayload', { column: 'app_lang', value: currentLocale });
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions vue/src/mixins/mixins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
Type2FAUpdateParams,
TypeRecoveryParams,
TypeAddRecoveryParams,
TypeUpdateUserPayload,
TypeResetRecovery,
TypeExportPhraseKeyVariables,
TypeShowPhraseKeyVariables
Expand Down Expand Up @@ -288,4 +289,7 @@ export class Authenticated extends Global {

@Action
public updateRecoveryMethods!: () => void;

@Action
public updateUserPayload!: (params: TypeUpdateUserPayload) => void;
}
23 changes: 22 additions & 1 deletion vue/src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ import {
TypeUpdateSeedPhrase,
TypeShowPhraseKeyVariables,
TypeExportPhraseKeyVariables,
TypeUpdateRecovery
TypeUpdateRecovery,
TypeUpdateUserPayload
} from '../types/global-types';

import isIframe from '../utils/isIframe';
Expand Down Expand Up @@ -291,6 +292,11 @@ const store: Store<RootState> = new Vuex.Store({
});
window.localStorage.setItem('iconSeed', parseInt(payload.accounts[0].slice(2, 10), 16).toString());
saveSessionStore('password', payload.hashedPassword);

const currentLocale = Cookie.get('locale');
if (currentLocale) {
store.dispatch('updateUserPayload', { column: 'app_lang', value: currentLocale });
}
},
seedExported(state: RootState) {
state.seedExported = true;
Expand Down Expand Up @@ -693,6 +699,17 @@ const store: Store<RootState> = new Vuex.Store({
}
});
},
updateUserPayload({ commit, dispatch }, params: TypeUpdateUserPayload) {
return new Promise((resolve, reject) => {
dispatch('sendSignedRequest', {
body: { column: params.column, value: params.value },
method: 'POST',
url: getBackendEndpoint() + '/v1/auth/updateUserPayload'
})
.then(() => { resolve(true) })
.catch(reject);
});
},
changePassword({ commit, state, dispatch }, params: TypeChangePassword) {
return new Promise(async (resolve, reject) => {
try {
Expand Down Expand Up @@ -1170,6 +1187,10 @@ if (isIframe()) {
if (lang === 'ar') document.querySelector('html')?.setAttribute('dir', 'rtl');
else document.querySelector('html')?.setAttribute('dir', '');
Cookie.set('locale', lang);

if(store.state.keystore){
store.dispatch('updateUserPayload', { column: 'app_lang', value: lang });
}
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions vue/src/types/global-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ export type TypeAddRecoveryParams = {
recoveryTypeId: number;
};

export type TypeUpdateUserPayload = {
__typename?: 'TypeUpdateUserPayload';
column: string;
value: string;
};

export type TypeExportPhraseKeyVariables = {
__typename?: 'TypeExportPhraseKeyVariables';
account: string;
Expand Down
41 changes: 41 additions & 0 deletions vue/tests/e2e/specs/03-Email-2FA.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,47 @@ describe('Email 2FA', () => {
});
});

it('Change Login Email Error', () => {
cy.visit('/');

cy.get('[data-cy=walletEmail]')
.type(email)
.should('have.value', email);

cy.get('[data-cy=walletPassword]')
.type(password)
.should('have.value', password);

cy.get('[data-cy=submit]').click();

cy.waitUntil(() => cy.get('[data-cy=currentEmail]').contains(email));

cy.get('[data-cy=sendButton]').contains('Send');
cy.get('[data-cy=settingsButton]').contains('Settings');

cy.get('[data-cy=settingsButton]').click();
cy.get('[data-cy=emailPasswordButton]').click();
cy.get('[data-cy=emailChangeButton]').click();

cy.get('[data-cy=newEmail]')
.type(secondEmail)
.should('have.value', secondEmail);

cy.get('[data-cy=confirmPassword]')
.type(password)
.should('have.value', password);

cy.get('[data-cy=updateEmailButton]').click();

cy.waitUntil(() => cy.get('[data-cy=emailConfirmationTitle]').contains('Email Confirmation'));

cy.get('[data-cy=2faEmailCode]').type('111111');

cy.get('[data-cy=confirmButton]').click();

cy.waitUntil(() => cy.get('[data-cy=2faEmailError]').contains('Incorrect Email 2FA code. Check your email and Try again.'));
});

it('Change Login Email', () => {
cy.visit('/');

Expand Down

0 comments on commit 2e2af6b

Please sign in to comment.