Skip to content
This repository has been archived by the owner on Aug 5, 2024. It is now read-only.

Commit

Permalink
Ajoute la fonctionnalité : maj de l'adresse principale (#971)
Browse files Browse the repository at this point in the history
* Added update primary email api

* Added interface to change primary email

* Refacto canChangeSecondaryEmail/canChangePrimaryEmail to one boolean

* Update src/controllers/usersController.ts

Co-authored-by: Alejandro M Guillén <[email protected]>

Co-authored-by: Alejandro M Guillén <[email protected]>
  • Loading branch information
LucasCharrier and alemangui authored Dec 8, 2021
1 parent 33991df commit 07641a9
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 17 deletions.
9 changes: 5 additions & 4 deletions src/controllers/accountController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,15 @@ export async function deleteEmailResponder(req, res) {

export async function getCurrentAccount(req, res) {
try {
const [currentUser, marrainageState, secondaryEmail] = await Promise.all([
const [currentUser, marrainageState, dbUser] = await Promise.all([
(async () => utils.userInfos(req.user.id, true))(),
(async () => {
const [state] = await knex('marrainage').where({ username: req.user.id });
return state;
})(),
(async () => {
const rows = await knex('users').where({ username: req.user.id });
return rows.length === 1 ? rows[0].secondary_email : null;
return rows.length === 1 ? rows[0] : null;
})(),
]);
const today = new Date()
Expand All @@ -115,9 +115,10 @@ export async function getCurrentAccount(req, res) {
canCreateEmail: currentUser.canCreateEmail,
canCreateRedirection: currentUser.canCreateRedirection,
canChangePassword: currentUser.canChangePassword,
canChangeSecondaryEmail: currentUser.canChangeSecondaryEmail,
canChangeEmails: currentUser.canChangeEmails,
redirections: currentUser.redirections,
secondaryEmail,
secondaryEmail: dbUser.secondary_email,
primaryEmail: dbUser.primary_email,
activeTab: 'account',
marrainageState,
formData: {
Expand Down
48 changes: 47 additions & 1 deletion src/controllers/usersController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import * as utils from "./utils";
import knex from "../db/index";
import { createRequestForUser } from "./marrainageController";
import { addEvent, EventCode } from '../lib/events'
import { MemberWithPermission } from "../models/member";
import { DBUser } from "../models/dbUser";

export async function createEmail(username, creator, toEmail) {
const email = utils.buildBetaEmail(username);
Expand Down Expand Up @@ -308,14 +310,58 @@ export async function deleteEmailForUser(req, res) {
}
}

export async function managePrimaryEmailForUser(req, res) {
const { username } = req.params;
const isCurrentUser = req.user.id === username;
const { primaryEmail } = req.body;
const user : MemberWithPermission = await utils.userInfos(username, isCurrentUser);
try {
if (!user.canChangeEmails) {
throw new Error(`L'utilisateur n'est pas autorisé à changer l'email primaire`);
}
const isPublicServiceEmail = await utils.isPublicServiceEmail(primaryEmail)
if (!isPublicServiceEmail) {
throw new Error(`L'email renseigné n'est pas un email de service public`);
}
const dbUser: DBUser = await knex('users').where({
username,
}).then(db => db[0])
await knex('users')
.insert({
primary_email: primaryEmail,
username
})
.onConflict('username')
.merge({
primary_email: primaryEmail,
username
});
addEvent(EventCode.MEMBER_PRIMARY_EMAIL_UPDATED, {
created_by_username: req.user.id,
action_on_username: username,
action_metadata: {
value: primaryEmail,
old_value: dbUser ? dbUser.primary_email : null,
}
})
req.flash('message', 'Ton compte email primaire a bien été mis à jour.');
console.log(`${req.user.id} a mis à jour son adresse mail primaire.`);
res.redirect(`/community/${username}`);
} catch (err) {
console.error(err);
req.flash('error', err.message);
res.redirect(`/community/${username}`);
}
}

export async function manageSecondaryEmailForUser(req, res) {
const { username } = req.params;
const isCurrentUser = req.user.id === username;
const { secondaryEmail } = req.body;
const user = await utils.userInfos(username, isCurrentUser);

try {
if (user.canChangeSecondaryEmail) {
if (user.canChangeEmails) {
const dbUser = await knex('users').where({
username,
}).then(db => db[0])
Expand Down
4 changes: 2 additions & 2 deletions src/controllers/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ export async function userInfos(id, isCurrentUser) {
emailInfos
);

const canChangeSecondaryEmail = !!(
const canChangeEmails = !!(
hasUserInfos &&
!isExpired &&
isCurrentUser
Expand All @@ -272,7 +272,7 @@ export async function userInfos(id, isCurrentUser) {
canCreateEmail,
canCreateRedirection,
canChangePassword,
canChangeSecondaryEmail,
canChangeEmails,
responder
};
} catch (err) {
Expand Down
4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ app.post(
'/users/:username/secondary_email',
usersController.manageSecondaryEmailForUser
);
app.post(
'/users/:username/primary_email',
usersController.managePrimaryEmailForUser
);
app.post('/users/:username/end-date', usersController.updateEndDateForUser);
app.post(
'/notifications/github',
Expand Down
1 change: 1 addition & 0 deletions src/lib/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export enum EventCode {
MARRAINAGE_ACCEPTED="MARRAINAGE_ACCEPTED",
MEMBER_MARRAINAGE_DECLINED="MEMBER_MARRAINAGE_DECLINED",
MEMBER_SECONDARY_EMAIL_UPDATED="MEMBER_SECONDARY_EMAIL_UPDATED",
MEMBER_PRIMARY_EMAIL_UPDATED="MEMBER_PRIMARY_EMAIL_UPDATED",
MEMBER_END_DATE_UPDATED="MEMBER_END_DATE_UPDATED",
}

Expand Down
5 changes: 5 additions & 0 deletions src/models/member.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,8 @@ export interface Member {
export interface MemberWithPrimaryEmail extends Member {
primary_email: string;
}

export interface MemberWithPermission {
userInfos: Member,
canChangeEmails: boolean
}
74 changes: 66 additions & 8 deletions tests/test-user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -600,9 +600,6 @@ describe('User', () => {
done();
});

// fixme: this test test nothing since it fail silently on chai.request
// because user.canChangeSecondaryEmail is false. It should be tested with
// a valid BetaGouvUser
it('should add secondary email', async () => {
const username = 'membre.nouveau';
const secondaryEmail = '[email protected]';
Expand Down Expand Up @@ -637,11 +634,6 @@ describe('User', () => {
.update({
secondary_email: secondaryEmail,
})
const dbRes = await knex('users')
.select()
.where({ username })
.first()
dbRes.secondary_email.should.equal(secondaryEmail);
await chai.request(app)
.post(`/users/${username}/secondary_email/`)
.set('Cookie', `token=${utils.getJWT('membre.nouveau')}`)
Expand All @@ -657,6 +649,72 @@ describe('User', () => {
secondary_email: null
})
});

it('should not update primary email if user is not current user', async() => {
const username = 'membre.nouveau';
const primaryEmail = '[email protected]';
const isPublicServiceEmailStub = sinon
.stub(controllerUtils, 'isPublicServiceEmail')
.returns(Promise.resolve(false));

await chai.request(app)
.post(`/users/${username}/primary_email/`)
.set('Cookie', `token=${utils.getJWT('julien.dauphant')}`)
.type('form')
.send({
username,
primaryEmail: primaryEmail,
});
isPublicServiceEmailStub.called.should.be.false;
isPublicServiceEmailStub.restore();
});

it('should not update primary email if email is not public service email', async() => {
const isPublicServiceEmailStub = sinon
.stub(controllerUtils, 'isPublicServiceEmail')
.returns(Promise.resolve(false));
const username = 'membre.nouveau';
const primaryEmail = '[email protected]';

await chai.request(app)
.post(`/users/${username}/primary_email/`)
.type('form')
.set('Cookie', `token=${utils.getJWT('membre.nouveau')}`)
.send({
username,
primaryEmail: primaryEmail,
});
const dbNewRes = await knex('users').select().where({ username: 'membre.nouveau' })
dbNewRes.length.should.equal(1);
dbNewRes[0].primary_email.should.not.equal(primaryEmail);
isPublicServiceEmailStub.called.should.be.true;
isPublicServiceEmailStub.restore()
});

it('should update primary email', async() => {
const isPublicServiceEmailStub = sinon
.stub(controllerUtils, 'isPublicServiceEmail')
.returns(Promise.resolve(true));
const username = 'membre.nouveau';
const primaryEmail = '[email protected]';

await chai.request(app)
.post(`/users/${username}/primary_email/`)
.type('form')
.set('Cookie', `token=${utils.getJWT('membre.nouveau')}`)
.send({
username,
primaryEmail: primaryEmail,
});
const dbNewRes = await knex('users').select().where({ username: 'membre.nouveau' })
dbNewRes.length.should.equal(1);
dbNewRes[0].primary_email.should.equal(primaryEmail);
await knex('users').where({ username: 'membre.nouveau' }).update({
primary_email: `${username}@${config.domain}`
})
isPublicServiceEmailStub.called.should.be.true;
isPublicServiceEmailStub.restore()
});
});

describe('POST /users/:username/redirections/:email/delete authenticated', () => {
Expand Down
27 changes: 25 additions & 2 deletions views/account.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,30 @@
<hr />
</div>
</div>

<div class="collapse">
<h6 class="margin-10-0 collapse-header">
<button aria-expanded="false">
Configurer mon email principal
<div class="icon fa fa-chevron-down"></div>
</button>
</h6>
<div hidden class="collapse-content">
<p>
L'email principal est utilisé pour toutes les communications en rapport avec Betagouv.
Ce doit être un email d'agent public. Il s'agit par défaut de <%= userInfos.id %>@beta.gouv.fr.
<br>
<br>
En cas d'utilisation d'une adresse autre, l'email <%= userInfos.id %>@beta.gouv.fr sera supprimé.
</p>
<% if (canChangeEmails) { %>
<form class="no-margin" action="/users/<%= userInfos.id %>/primary_email" method="POST" onsubmit="event.submitter && (event.submitter.disabled = true);">
<input id="primaryEmail" name="primaryEmail" value="<%= primaryEmail %>" type="email">
<button class="button margin-10-0" type="submit">Sauvegarder l'email principal</button>
</form>
<% } %>
<hr />
</div>
</div>
<div class="collapse">
<h6 class="margin-10-0 collapse-header">
<button aria-expanded="false">
Expand All @@ -306,7 +329,7 @@
<p>
L'email secondaire est utile pour récupérer son mot de passe ou garder contact après ton départ.
</p>
<% if (canChangeSecondaryEmail) { %>
<% if (canChangeEmails) { %>
<form class="no-margin" action="/users/<%= userInfos.id %>/secondary_email" method="POST" onsubmit="event.submitter && (event.submitter.disabled = true);">
<input id="secondaryEmail" name="secondaryEmail" value="<%= secondaryEmail %>" type="email">
<button class="button margin-10-0" type="submit">Sauvegarder l'email secondaire</button>
Expand Down

0 comments on commit 07641a9

Please sign in to comment.