diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index ab79319ed..1fe0b2a2a 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -30,6 +30,7 @@ jobs: OVH_APP_KEY: ${{ secrets.OVH_APP_KEY }} OVH_APP_SECRET: ${{ secrets.OVH_APP_SECRET }} OVH_CONSUMER_KEY: ${{ secrets.OVH_CONSUMER_KEY }} + PORT: 8100 PROTECTED_API_KEYS: test-api-key - name: Wait uses: cygnetdigital/wait_for_response@v2.0.0 diff --git a/.talismanrc b/.talismanrc index 23f02d7c3..33fbabf3e 100644 --- a/.talismanrc +++ b/.talismanrc @@ -5,6 +5,8 @@ fileignoreconfig: checksum: 07171a40047c38771624063fb61511998d3961815c3dceb135a1ee6d540f970d - filename: .github/workflows/nodejs.yml checksum: bf0ae31737daf27be68b7d2c4e63e1ef14cb799f1853462fdadcf2422ddd53a2 +- filename: __tests__/test-user.ts + checksum: 855d1520858d599dd95ebfd6b9845ad2f5df76307915568b8f5b558cec12a9b1 - filename: .github/workflows/playwright.yml checksum: 38ba47ef852e85e51e9e97d635c6b55b63db8940a3c33f2546fa8386c3311a3d - filename: __tests__/test-worker.ts @@ -27,12 +29,24 @@ fileignoreconfig: checksum: 685b3f177bb6124655bb439beb6f3cb4ecdbbeb62adeab9e1f56e811a09a9f5b - filename: src/app/api/member/[username]/route.ts checksum: a5371c646bc9955b72b6cdb5ee3aef340004a092d9e70bbbe4b308c940880370 +- filename: src/app/api/member/actions/createRedirectionForUser.ts + checksum: 12c54f94e0d66d8e3d51b9e9dd7032a1757c50fc85733b29befe9c0162fe3251 +- filename: src/app/api/member/actions/deleteRedirectionForUser.ts + checksum: deddb6b63051856b44a031e097f4614a1ff42d97ac49f1bc1d21f28ed377d7d1 +- filename: src/app/api/member/actions/updatePasswordForUser.ts + checksum: f0f76c8fc4094fb1a0ae2fa456dcf72b0d2481dbe4cfd8632241306d9b63c2d3 - filename: src/app/api/services/actions.ts checksum: 80f4cffcc5861befdb98c676df064595dae5c318349c45059ba87fe20e96f2a2 - filename: src/components/CommunityPage/Community.tsx checksum: 50ed5a9ba8e03d4ffc248026cfae6fd6331c84c21b30aaa73c0bf30f22be9e2c - filename: src/components/IncubatorForm/IncubatorForm.tsx checksum: 267f0f0e491b06862cd957ae86d22baf799a705370673cd24e588391a4613a5a +- filename: src/components/MemberPage/Email/BlocChangerMotDePasse.tsx + checksum: 9ee37641aebd9381fccbe0b967e5f2b8a546760824e62ed3a564f50752fdae1f +- filename: src/components/MemberPage/Email/EmailContainer.tsx + checksum: 2c46d4aa4dc8caece9bc7f377178e855562c5321d2b87b24ff32cb307fe6946b +- filename: src/components/MemberPage/Email/WebMailButtons.tsx + checksum: da7b71f65b7519ea53e594910c24d14e3579d32ab1cb9144f32fa81a71c0a497 - filename: src/components/IncubatorPage/IncubatorPage.tsx checksum: 61c912058a8f8c12b1173a37b7fa555c11e2ddbf36bd95d0e986576c9ef5e79a - filename: src/components/MemberPage/EmailUpgrade.tsx diff --git a/__tests__/test-admin.ts b/__tests__/test-admin.ts index 54f248962..2da1cb5ce 100644 --- a/__tests__/test-admin.ts +++ b/__tests__/test-admin.ts @@ -1,9 +1,14 @@ -import chai from "chai"; +import chai, { assert, expect } from "chai"; import chaiHttp from "chai-http"; +import * as nextAuth from "next-auth/next"; import sinon from "sinon"; import testUsers from "./users.json"; import utils from "./utils"; +import { getMattermostInfo } from "@/app/api/admin/actions/getMattermostAdmin"; +import { getMattermostUsersInfo } from "@/app/api/admin/actions/getMattermostUsersInfo"; +import { sendMessageToUsersOnChat } from "@/app/api/admin/actions/sendMattermostMessage"; +import { db } from "@/lib/kysely"; import routes from "@/routes/routes"; import config from "@/server/config"; import * as adminConfig from "@/server/config/admin.config"; @@ -16,221 +21,240 @@ import * as mattermostScheduler from "@schedulers/mattermostScheduler/removeBeta chai.use(chaiHttp); describe("Test Admin", () => { - describe("GET /admin unauthenticated", () => { - it("should redirect to login", (done) => { - chai.request(app) - .get("/api/admin") - .redirects(0) - .end((err, res) => { - res.should.have.status(500); - done(); - }); - }); - }); + describe("Test admin mattermost server action", () => { + describe("Test admin mattermost server action api if user is not admin", () => { + let getServerSessionStub; + let isPublicServiceEmailStub; + let user; - // describe("GET /admin authenticated", () => { - // let getToken; + beforeEach(async () => { + getServerSessionStub = sinon + .stub(nextAuth, "getServerSession") + .resolves({}); - // beforeEach(() => { - // getToken = sinon.stub(session, "getToken"); - // getToken.returns(utils.getJWT("membre.actif")); - // }); + await utils.createUsers(testUsers); + user = await db + .selectFrom("users") + .selectAll() + .where("username", "=", "membre.actif") + .executeTakeFirstOrThrow(); + const mockSession = { + user: { + id: "membre.actif", + isAdmin: false, + uuid: user.uuid, + }, + }; + getServerSessionStub.resolves(mockSession); + }); + afterEach(async () => { + sinon.restore(); + await utils.deleteUsers(testUsers); + }); - // afterEach(() => { - // getToken.restore(); - // }); - // it("should return a valid page", (done) => { - // chai.request(app) - // .get("/api/admin") - // .end((err, res) => { - // res.should.have.status(200); - // done(); - // }); - // }); - // }); + it("should return a forbidden error if user not in admin", async () => { + try { + await getMattermostInfo(); + } catch (err) { + assert(err instanceof Error); + assert.strictEqual( + err.message, + "L'utilisateur doit être administrateur" + ); + } + }); + it("should return a forbidden error if user not in admin", async () => { + try { + await getMattermostUsersInfo({ + fromBeta: true, + }); + } catch (err) { + assert(err instanceof Error); + assert.strictEqual( + err.message, + "L'utilisateur doit être administrateur" + ); + } + }); + it("should return a forbidden error if user not in admin", async () => { + try { + await sendMessageToUsersOnChat({ + text: "toto", + fromBeta: false, + }); + } catch (err) { + assert(err instanceof Error); + assert.strictEqual( + err.message, + "L'utilisateur doit être administrateur" + ); + } + }); + }); + describe("Test admin mattermost server action api if user is admin", () => { + let getServerSessionStub; + let user; - describe("GET /admin/mattermost authenticated", () => { - let getToken; + beforeEach(async () => { + getServerSessionStub = sinon + .stub(nextAuth, "getServerSession") + .resolves({}); - beforeEach(async () => { - getToken = sinon.stub(session, "getToken"); - getToken.returns(utils.getJWT("membre.actif")); - await utils.createUsers(testUsers); - }); + await utils.createUsers(testUsers); + user = await db + .selectFrom("users") + .selectAll() + .where("username", "=", "membre.actif") + .executeTakeFirstOrThrow(); + const mockSession = { + user: { + id: "membre.actif", + isAdmin: true, + uuid: user.uuid, + }, + }; + getServerSessionStub.resolves(mockSession); + }); + afterEach(async () => { + sinon.restore(); + await utils.deleteUsers(testUsers); + }); - afterEach(async () => { - getToken.restore(); - await utils.deleteUsers(testUsers); - }); - it("should return a forbidden error if user not in admin", async () => { - const res = await chai - .request(app) - .get(routes.ADMIN_MATTERMOST_API); - res.should.have.status(403); - }); - it("should return a forbidden error if user not in admin", async () => { - const res = await chai - .request(app) - .get(routes.ADMIN_MATTERMOST_MESSAGE_API); - res.should.have.status(403); - }); - it("should return a forbidden error if user not in admin", async () => { - const res = await chai - .request(app) - .post(routes.ADMIN_MATTERMOST_SEND_MESSAGE); - res.should.have.status(403); - }); - it("should return /api/admin/mattermost page if user is admin", async () => { - const getAdminStub = sinon - .stub(adminConfig, "getAdmin") - .returns(["membre.actif"]); - const getMattermostUsersWithStatus = sinon - .stub(mattermostScheduler, "getMattermostUsersWithStatus") - .returns(Promise.resolve([])); - const res = await chai - .request(app) - .get(routes.ADMIN_MATTERMOST_API); - res.should.have.status(200); - getAdminStub.restore(); - getMattermostUsersWithStatus.restore(); - }); - it("should return /admin/send-message page if user is admin", async () => { - const getAdminStub = sinon - .stub(adminConfig, "getAdmin") - .returns(["membre.actif"]); - const getMattermostUsersWithStatus = sinon - .stub(mattermostScheduler, "getMattermostUsersWithStatus") - .returns(Promise.resolve([])); - const getUserWithParams = sinon.stub(chat, "getUserWithParams"); - const sendInfoToChat = sinon.stub(chat, "sendInfoToChat"); - getUserWithParams.onCall(0).returns([ - { - username: "membre.actif", - email: `membre.actif@${config.domain}`, - }, - ]); - getUserWithParams.onCall(1).returns([]); - const res = await chai - .request(app) - .post(routes.ADMIN_MATTERMOST_SEND_MESSAGE) - .send({ + it("should return /api/admin/mattermost page if user is admin", async () => { + const getAdminStub = sinon + .stub(adminConfig, "getAdmin") + .returns(["membre.actif"]); + const getMattermostUsersWithStatus = sinon + .stub(mattermostScheduler, "getMattermostUsersWithStatus") + .returns(Promise.resolve([])); + const res = await getMattermostInfo(); + getAdminStub.restore(); + getMattermostUsersWithStatus.restore(); + }); + it("should return /admin/send-message page if user is admin", async () => { + const getAdminStub = sinon + .stub(adminConfig, "getAdmin") + .returns(["membre.actif"]); + const getMattermostUsersWithStatus = sinon + .stub(mattermostScheduler, "getMattermostUsersWithStatus") + .returns(Promise.resolve([])); + const getUserWithParams = sinon.stub(chat, "getUserWithParams"); + const sendInfoToChat = sinon.stub(chat, "sendInfoToChat"); + getUserWithParams.onCall(0).returns([ + { + username: "membre.actif", + email: `membre.actif@${config.domain}`, + }, + ]); + getUserWithParams.onCall(1).returns([]); + await sendMessageToUsersOnChat({ fromBeta: true, - excludeEmails: "", - includeEmails: "", text: "", }); - res.should.have.status(200); - sendInfoToChat.calledOnce.should.be.true; - getUserWithParams.callCount.should.be.eq(0); - getAdminStub.restore(); - getUserWithParams.restore(); - getMattermostUsersWithStatus.restore(); - sendInfoToChat.restore(); - }); - it("should send message to all users if prod is true and channel undefined", async () => { - const getAdminStub = sinon - .stub(adminConfig, "getAdmin") - .returns(["membre.actif"]); - const getMattermostUsersWithStatus = sinon - .stub(mattermostScheduler, "getMattermostUsersWithStatus") - .returns(Promise.resolve([])); - const getUserWithParams = sinon.stub(chat, "getUserWithParams"); - const sendInfoToChat = sinon.stub(chat, "sendInfoToChat"); - getUserWithParams.onCall(0).returns([ - { - username: "membre.actif", - email: `membre.actif@${config.domain}`, - }, - ]); - getUserWithParams.onCall(1).returns([]); - const res = await chai - .request(app) - .post(routes.ADMIN_MATTERMOST_SEND_MESSAGE) - .send({ + + sendInfoToChat.calledOnce.should.be.true; + getUserWithParams.callCount.should.be.eq(0); + getAdminStub.restore(); + getUserWithParams.restore(); + getMattermostUsersWithStatus.restore(); + sendInfoToChat.restore(); + }); + it("should send message to all users if prod is true and channel undefined", async () => { + const getAdminStub = sinon + .stub(adminConfig, "getAdmin") + .returns(["membre.actif"]); + const getMattermostUsersWithStatus = sinon + .stub(mattermostScheduler, "getMattermostUsersWithStatus") + .returns(Promise.resolve([])); + const getUserWithParams = sinon.stub(chat, "getUserWithParams"); + const sendInfoToChat = sinon.stub(chat, "sendInfoToChat"); + getUserWithParams.onCall(0).returns([ + { + username: "membre.actif", + email: `membre.actif@${config.domain}`, + }, + ]); + getUserWithParams.onCall(1).returns([]); + await sendMessageToUsersOnChat({ fromBeta: true, - excludeEmails: "", - includeEmails: "", prod: true, + text: "toto", }); - res.should.have.status(200); - getUserWithParams.callCount.should.be.eq(1); - getAdminStub.restore(); - getUserWithParams.restore(); - getMattermostUsersWithStatus.restore(); - sendInfoToChat.restore(); - }); + getUserWithParams.callCount.should.be.eq(1); + getAdminStub.restore(); + getUserWithParams.restore(); + getMattermostUsersWithStatus.restore(); + sendInfoToChat.restore(); + }); - it("should take exclude in consideration", async () => { - const getAdminStub = sinon - .stub(adminConfig, "getAdmin") - .returns(["membre.actif"]); - const getMattermostUsersWithStatus = sinon - .stub(mattermostScheduler, "getMattermostUsersWithStatus") - .returns(Promise.resolve([])); - const getMattermostUsersSpy = sinon.spy( - sendMattermostMessage, - "getMattermostUsers" - ); - const getUserWithParams = sinon.stub(chat, "getUserWithParams"); - const sendInfoToChat = sinon.stub(chat, "sendInfoToChat"); - getUserWithParams.onCall(0).returns([ - { - username: "membre.actif", - email: `membre.actif@${config.domain}`, - }, - ]); - getUserWithParams.onCall(1).returns([]); - const res = await chai - .request(app) - .post(routes.ADMIN_MATTERMOST_SEND_MESSAGE) - .send({ + it("should take exclude in consideration", async () => { + const getAdminStub = sinon + .stub(adminConfig, "getAdmin") + .returns(["membre.actif"]); + const getMattermostUsersWithStatus = sinon + .stub(mattermostScheduler, "getMattermostUsersWithStatus") + .returns(Promise.resolve([])); + const getMattermostUsersSpy = sinon.spy( + sendMattermostMessage, + "getMattermostUsers" + ); + const getUserWithParams = sinon.stub(chat, "getUserWithParams"); + const sendInfoToChat = sinon.stub(chat, "sendInfoToChat"); + getUserWithParams.onCall(0).returns([ + { + username: "membre.actif", + email: `membre.actif@${config.domain}`, + }, + ]); + getUserWithParams.onCall(1).returns([]); + await sendMessageToUsersOnChat({ fromBeta: true, - includeEmails: "", + excludeEmails: [`membre.actif@${config.domain}`], prod: true, + text: "", }); - res.should.have.status(200); - const resMatterUser = await getMattermostUsersSpy.returnValues[0]; - resMatterUser.length.should.be.eq(1); - getUserWithParams.callCount.should.be.eq(1); - getAdminStub.restore(); - getUserWithParams.restore(); - getMattermostUsersWithStatus.restore(); - sendInfoToChat.restore(); - }); + const resMatterUser = await getMattermostUsersSpy + .returnValues[0]; + resMatterUser.length.should.be.eq(0); + getUserWithParams.callCount.should.be.eq(1); + getAdminStub.restore(); + getUserWithParams.restore(); + getMattermostUsersWithStatus.restore(); + sendInfoToChat.restore(); + }); - it("should send message to all users if prod is true and channel set", async () => { - const getAdminStub = sinon - .stub(adminConfig, "getAdmin") - .returns(["membre.actif"]); - const getMattermostUsersWithStatus = sinon - .stub(mattermostScheduler, "getMattermostUsersWithStatus") - .returns(Promise.resolve([])); - const getUserWithParams = sinon.stub(chat, "getUserWithParams"); - const sendInfoToChat = sinon.stub(chat, "sendInfoToChat"); - getUserWithParams.onCall(0).returns([ - { - username: "membre.actif", - email: `membre.actif@${config.domain}`, - }, - ]); - getUserWithParams.onCall(1).returns([]); - const res = await chai - .request(app) - .post(routes.ADMIN_MATTERMOST_SEND_MESSAGE) - .send({ + it("should send message to all users if prod is true and channel set", async () => { + const getAdminStub = sinon + .stub(adminConfig, "getAdmin") + .returns(["membre.actif"]); + const getMattermostUsersWithStatus = sinon + .stub(mattermostScheduler, "getMattermostUsersWithStatus") + .returns(Promise.resolve([])); + const getUserWithParams = sinon.stub(chat, "getUserWithParams"); + const sendInfoToChat = sinon.stub(chat, "sendInfoToChat"); + getUserWithParams.onCall(0).returns([ + { + username: "membre.actif", + email: `membre.actif@${config.domain}`, + }, + ]); + getUserWithParams.onCall(1).returns([]); + await sendMessageToUsersOnChat({ fromBeta: true, - excludeEmails: "", prod: true, channel: "general", + text: "Un super texte", }); - res.should.have.status(200); - getUserWithParams.callCount.should.be.eq(0); - sendInfoToChat.getCall(0).args[0].channel.should.equal("general"); - sendInfoToChat.calledTwice.should.be.true; - getAdminStub.restore(); - getUserWithParams.restore(); - getMattermostUsersWithStatus.restore(); - sendInfoToChat.restore(); + getUserWithParams.callCount.should.be.eq(0); + sendInfoToChat + .getCall(0) + .args[0].channel.should.equal("general"); + sendInfoToChat.calledTwice.should.be.true; + getAdminStub.restore(); + getUserWithParams.restore(); + getMattermostUsersWithStatus.restore(); + sendInfoToChat.restore(); + }); }); }); }); diff --git a/__tests__/test-user.ts b/__tests__/test-user.ts index 0836a4924..b899df77c 100644 --- a/__tests__/test-user.ts +++ b/__tests__/test-user.ts @@ -1,10 +1,16 @@ import chai from "chai"; import chaiHttp from "chai-http"; +import * as nextAuth from "next-auth/next"; import nock from "nock"; +import proxyquire from "proxyquire"; import sinon from "sinon"; import testUsers from "./users.json"; import utils from "./utils"; +import { createEmail as createEmailAction } from "@/app/api/member/actions/createEmailForUser"; +import { createRedirectionForUser } from "@/app/api/member/actions/createRedirectionForUser"; +import { deleteRedirectionForUser } from "@/app/api/member/actions/deleteRedirectionForUser"; +import { updatePasswordForUser } from "@/app/api/member/actions/updatePasswordForUser"; import { db } from "@/lib/kysely"; import * as mattermost from "@/lib/mattermost"; import { Domaine, EmailStatusCode } from "@/models/member"; @@ -17,7 +23,6 @@ import app from "@/server/index"; import betagouv from "@betagouv"; import Betagouv from "@betagouv"; import * as controllerUtils from "@controllers/utils"; -import knex from "@db"; import { createEmailAddresses, createRedirectionEmailAdresses, @@ -25,69 +30,120 @@ import { unsubscribeEmailAddresses, } from "@schedulers/emailScheduler"; +const deleteEmailForUser = proxyquire("@/app/api/member/actions", { + "next/cache": { + revalidatePath: sinon.stub(), + }, +}).deleteEmailForUser; + chai.use(chaiHttp); +const { expect } = chai; describe("User", () => { let ovhPasswordNock; - describe("POST /api/users/:username/create-email unauthenticated", () => { - it("should return an Unauthorized error", (done) => { - chai.request(app) - .post( - routes.USER_CREATE_EMAIL_API.replace( - ":username", - "membre.parti" - ) - ) - .type("form") - .send({ - _method: "POST", - }) - .end((err, res) => { - res.should.have.status(401); - done(); + describe("test createEmailAction unauthenticated", () => { + let getServerSessionStub; + let isPublicServiceEmailStub; + let user; + + beforeEach(async () => { + isPublicServiceEmailStub = sinon + .stub(controllerUtils, "isPublicServiceEmail") + .returns(Promise.resolve(true)); + getServerSessionStub = sinon + .stub(nextAuth, "getServerSession") + .resolves({}); + + await utils.createUsers(testUsers); + user = await db + .selectFrom("users") + .selectAll() + .where("username", "=", "membre.actif") + .executeTakeFirstOrThrow(); + }); + afterEach(async () => { + sinon.restore(); + await utils.deleteUsers(testUsers); + isPublicServiceEmailStub.restore(); + }); + + it("should return an Unauthorized error", async () => { + try { + await createEmailAction({ + username: "membre.parti", + to_email: "lucas.charr@test.com", }); + } catch (err) { + expect(err).to.be.an("error"); + } + // chai.request(app) + // .post( + // routes.USER_CREATE_EMAIL_API.replace( + // ":username", + // "membre.parti" + // ) + // ) + // .type("form") + // .send({ + // _method: "POST", + // }) + // .end((err, res) => { + // res.should.have.status(401); + // done(); + // }); }); }); - describe("POST /api/users/:username/create-email authenticated", () => { - let getToken; - let sendEmailStub; + describe("test createEmailAction authenticated", () => { + let getServerSessionStub; + let isPublicServiceEmailStub; + let user; + beforeEach(async () => { - sendEmailStub = sinon - .stub(controllerUtils, "sendMail") + isPublicServiceEmailStub = sinon + .stub(controllerUtils, "isPublicServiceEmail") .returns(Promise.resolve(true)); - getToken = sinon.stub(session, "getToken"); - getToken.returns(utils.getJWT("membre.actif")); + getServerSessionStub = sinon + .stub(nextAuth, "getServerSession") + .resolves({}); + await utils.createUsers(testUsers); + user = await db + .selectFrom("users") + .selectAll() + .where("username", "=", "membre.actif") + .executeTakeFirstOrThrow(); }); - afterEach(async () => { - sendEmailStub.restore(); - getToken.restore(); + sinon.restore(); await utils.deleteUsers(testUsers); + isPublicServiceEmailStub.restore(); }); it("should ask OVH to create an email", async () => { + const mockSession = { + user: { id: "membre.actif", isAdmin: false, uuid: user.uuid }, + }; + getServerSessionStub.resolves(mockSession); const ovhEmailCreation = nock(/.*ovh.com/) .post(/^.*email\/domain\/.*\/account/) .reply(200); await db .updateTable("users") - .where("username", "=", "membre.nouveau") + .where("username", "=", "membre.nouveau@beta.gouv.fr") .set({ primary_email: null, }) .execute(); - await chai - .request(app) - .post( - routes.USER_CREATE_EMAIL_API.replace( - ":username", - "membre.nouveau" - ) - ) - .type("form") - .send({}); + + try { + await createEmailAction({ + username: "membre.nouveau", + to_email: "membre.nouveau@beta.gouv.fr", + }); + } catch (err) { + expect(err).to.be.an("error"); + } const res = await db .selectFrom("users") @@ -98,7 +154,7 @@ describe("User", () => { ovhEmailCreation.isDone().should.be.true; }); - it("should not allow email creation from delegate if email already exists", (done) => { + it("should not allow email creation from delegate if email already exists", async () => { // For this case we need to reset the basic nocks in order to return // a different response to indicate that membre.nouveau has an // existing email already created. @@ -109,6 +165,11 @@ describe("User", () => { utils.mockOvhTime(); utils.mockOvhRedirections(); + const mockSession = { + user: { id: "membre.actif", isAdmin: false, uuid: user.uuid }, + }; + getServerSessionStub.resolves(mockSession); + // We return an email for membre.nouveau to indicate he already has one nock(/.*ovh.com/) .get(/^.*email\/domain\/.*\/account\/.*/) @@ -120,84 +181,82 @@ describe("User", () => { const ovhEmailCreation = nock(/.*ovh.com/) .post(/^.*email\/domain\/.*\/account/) .reply(200); - - chai.request(app) - .post( - routes.USER_CREATE_EMAIL_API.replace( - ":username", - "membre.nouveau" - ) - ) - .type("form") - .send({}) - .end((err, res) => { - ovhEmailCreation.isDone().should.be.false; - done(); + try { + await createEmailAction({ + username: "membre.nouveau", + to_email: "membre.nouveau@example.com", }); + } catch (err) { + ovhEmailCreation.isDone().should.be.false; + } }); - it("should not allow email creation from delegate if github file doesn't exist", (done) => { + it("should not allow email creation from delegate if github file doesn't exist", async () => { + const mockSession = { + user: { id: "membre.actif", isAdmin: false, uuid: user.uuid }, + }; + getServerSessionStub.resolves(mockSession); const ovhEmailCreation = nock(/.*ovh.com/) .post(/^.*email\/domain\/.*\/account/) .reply(200); - - chai.request(app) - .post( - routes.USER_CREATE_EMAIL_API.replace( - ":username", - "membre.sans.fiche" - ) - ) - .type("form") - .send({}) - .end((err, res) => { - ovhEmailCreation.isDone().should.be.false; - done(); + try { + await createEmailAction({ + username: "membre.sans.fiche", + to_email: "membre.nouveau@example.com", }); + } catch (err) { + ovhEmailCreation.isDone().should.be.false; + } }); - it("should not allow email creation from delegate if user has expired", (done) => { + it("should not allow email creation from delegate if user has expired", async () => { + const mockSession = { + user: { id: "membre.actif", isAdmin: false, uuid: user.uuid }, + }; + getServerSessionStub.resolves(mockSession); const ovhEmailCreation = nock(/.*ovh.com/) .post(/^.*email\/domain\/.*\/account/) .reply(200); - chai.request(app) - .post( - routes.USER_CREATE_EMAIL_API.replace( - ":username", - "membre.expire" - ) - ) - .type("form") - .send({}) - .end((err, res) => { - ovhEmailCreation.isDone().should.be.false; - done(); + try { + await createEmailAction({ + username: "membre.expire", + to_email: "membre.nouveau@example.com", }); + } catch (err) { + ovhEmailCreation.isDone().should.be.false; + } }); - it("should not allow email creation from delegate if delegate has expired", (done) => { + it("should not allow email creation from delegate if delegate has expired", async () => { + const mockSession = { + user: { id: "membre.expire", isAdmin: false, uuid: user.uuid }, + }; + getServerSessionStub.resolves(mockSession); const ovhEmailCreation = nock(/.*ovh.com/) .post(/^.*email\/domain\/.*\/account/) .reply(200); - getToken.returns(utils.getJWT("membre.expire")); - chai.request(app) - .post( - routes.USER_CREATE_EMAIL_API.replace( - ":username", - "membre.nouveau" - ) - ) - .type("form") - .send({}) - .end((err, res) => { - ovhEmailCreation.isDone().should.be.false; - done(); + try { + await createEmailAction({ + username: "membre.nouveau", + to_email: "membre.nouveau@example.com", }); + } catch (err) { + ovhEmailCreation.isDone().should.be.false; + } }); it("should allow email creation from delegate if user is active", async () => { + const mockSession = { + user: { + id: "julien.dauphant", + isAdmin: false, + uuid: user.uuid, + }, + }; + getServerSessionStub.resolves(mockSession); + const ovhEmailCreation = nock(/.*ovh.com/) .post(/^.*email\/domain\/.*\/account/) .reply(200); @@ -208,19 +267,13 @@ describe("User", () => { primary_email: null, }) .execute(); - getToken.returns(utils.getJWT("julien.dauphant")); - await chai - .request(app) - .post( - routes.USER_CREATE_EMAIL_API.replace( - ":username", - "membre.actif" - ) - ) - .type("form") - .send({}); + await createEmailAction({ + username: "membre.actif", + to_email: "membre.nouveau@example.com", + }); + ovhEmailCreation.isDone().should.be.true; - const user = await db + const user2 = await db .selectFrom("users") .selectAll() .where("username", "=", "membre.actif") @@ -228,232 +281,216 @@ describe("User", () => { }); }); - describe("POST /api/users/:username/create-email unauthenticated", () => { - it("should return an Unauthorized error", (done) => { - chai.request(app) - .post("/api/users/membre.parti/create-email") - .send({ - _method: "POST", - to_email: "test@example.com", - }) - .end((err, res) => { - res.should.have.status(401); - done(); - }); - }); - }); - describe("POST /api/users/:username/create-email authenticated", () => { - let getToken; - let sendEmailStub; - beforeEach(async () => { - getToken = sinon.stub(session, "getToken"); - getToken.returns(utils.getJWT("membre.actif")); - sendEmailStub = sinon - .stub(controllerUtils, "sendMail") - .returns(Promise.resolve(true)); - await utils.createUsers(testUsers); - }); - - afterEach(async () => { - sendEmailStub.restore(); - getToken.restore(); - await utils.deleteUsers(testUsers); - }); - - it("should ask OVH to create an email", async () => { - const ovhEmailCreation = nock(/.*ovh.com/) - .post(/^.*email\/domain\/.*\/account/) - .reply(200); - await db - .updateTable("users") - .where("username", "=", "membre.nouveau") - .set({ - primary_email: null, - }) - .execute(); - await chai - .request(app) - .post("/api/users/membre.nouveau/create-email") - .send({ - to_email: "test@example.com", - }); - - const res = await db - .selectFrom("users") - .selectAll() - .where("username", "=", "membre.nouveau") - .executeTakeFirstOrThrow(); - res.primary_email.should.equal(`membre.nouveau@${config.domain}`); - ovhEmailCreation.isDone().should.be.true; - }); - }); - - describe("POST /api/users/:username/redirections unauthenticated", () => { - it("should return an Unauthorized error", (done) => { - chai.request(app) - .post("/api/users/membre.parti/redirections") - .type("form") - .send({ - to_email: "test@example.com", - }) - .end((err, res) => { - res.should.have.status(401); - done(); + describe("Create redirection unauthenticated", () => { + it("should return an Unauthorized error", async () => { + try { + await createRedirectionForUser({ + username: "membre.actif", + to_email: "toto@gmail.com", }); + } catch (err) { + expect(err).to.be.an("error"); + } }); }); - describe("POST /api/users/:username/redirections authenticated", () => { - let getToken; + describe("Create redirection authenticated", () => { + let getServerSessionStub; let isPublicServiceEmailStub; + let user; beforeEach(async () => { - getToken = sinon.stub(session, "getToken"); - getToken.returns(utils.getJWT("membre.actif")); isPublicServiceEmailStub = sinon .stub(controllerUtils, "isPublicServiceEmail") .returns(Promise.resolve(true)); + getServerSessionStub = sinon + .stub(nextAuth, "getServerSession") + .resolves({}); + await utils.createUsers(testUsers); + user = await db + .selectFrom("users") + .selectAll() + .where("username", "=", "membre.actif") + .executeTakeFirstOrThrow(); }); - afterEach(async () => { - getToken.restore(); + sinon.restore(); + await utils.deleteUsers(testUsers); isPublicServiceEmailStub.restore(); await utils.deleteUsers(testUsers); }); - it("should ask OVH to create a redirection", (done) => { + it("should ask OVH to create a redirection", async () => { + const mockSession = { + user: { id: "membre.actif", isAdmin: false, uuid: user.uuid }, + }; + getServerSessionStub.resolves(mockSession); + isPublicServiceEmailStub.returns(Promise.resolve(true)); const ovhRedirectionCreation = nock(/.*ovh.com/) .post(/^.*email\/domain\/.*\/redirection/) .reply(200); - chai.request(app) - .post("/api/users/membre.actif/redirections") - .type("form") - .send({ - to_email: "test@example.com", - }) - .end((err, res) => { - ovhRedirectionCreation.isDone().should.be.true; - done(); - }); + await createRedirectionForUser({ + to_email: "test@example.com", + username: "membre.actif", + }); + + ovhRedirectionCreation.isDone().should.be.true; }); - it("should not allow redirection creation from delegate", (done) => { + it("should not allow redirection creation from delegate", async () => { + const mockSession = { + user: { id: "membre.actif", isAdmin: false, uuid: user.uuid }, + }; + getServerSessionStub.resolves(mockSession); const ovhRedirectionCreation = nock(/.*ovh.com/) .post(/^.*email\/domain\/.*\/redirection/) .reply(200); - chai.request(app) - .post("/api/users/membre.nouveau/redirections") - .type("form") - .send({ + try { + await createRedirectionForUser({ to_email: "test@example.com", - }) - .end((err, res) => { - ovhRedirectionCreation.isDone().should.be.false; - done(); + username: "membre.nouveau", }); + } catch (e) { + ovhRedirectionCreation.isDone().should.be.false; + } }); - it("should not allow redirection creation from expired users", (done) => { + it("should not allow redirection creation from expired users", async () => { const ovhRedirectionCreation = nock(/.*ovh.com/) .post(/^.*email\/domain\/.*\/redirection/) .reply(200); - getToken.returns(utils.getJWT("membre.expire")); - chai.request(app) - .post("/api/users/membre.expire/redirections") - .type("form") - .send({ + user = await db + .selectFrom("users") + .selectAll() + .where("username", "=", "membre.expire") + .executeTakeFirstOrThrow(); + const mockSession = { + user: { id: "membre.expire", isAdmin: false, uuid: user.uuid }, + }; + getServerSessionStub.resolves(mockSession); + try { + await createRedirectionForUser({ to_email: "test@example.com", - }) - .end((err, res) => { - ovhRedirectionCreation.isDone().should.be.false; - done(); + username: "membre.expire", }); + } catch (e) { + ovhRedirectionCreation.isDone().should.be.false; + } }); }); - describe("Delete /api/users/:username/redirections/:email/delete unauthenticated", () => { - it("should return an Unauthorized error", (done) => { - chai.request(app) - .delete( - "/api/users/membre.parti/redirections/test@example.com/delete" - ) - .end((err, res) => { - res.should.have.status(401); - done(); + describe("Delete redirections unauthenticated", () => { + let getServerSessionStub; + + beforeEach(async () => { + getServerSessionStub = sinon + .stub(nextAuth, "getServerSession") + .resolves({}); + + await utils.createUsers(testUsers); + }); + afterEach(async () => { + sinon.restore(); + await utils.deleteUsers(testUsers); + }); + it("should return an Unauthorized error", async () => { + try { + await deleteRedirectionForUser({ + username: "membre.parti", + toEmail: "", }); + } catch (e) { + console.log(e); + } }); }); - describe("Delete /api/users/:username/redirections/:email/delete authenticated", () => { - let getToken; + describe("Delete redirections authenticated", () => { + let getServerSessionStub; let isPublicServiceEmailStub; + let user; beforeEach(async () => { - getToken = sinon.stub(session, "getToken"); - getToken.returns(utils.getJWT("membre.actif")); isPublicServiceEmailStub = sinon .stub(controllerUtils, "isPublicServiceEmail") .returns(Promise.resolve(true)); + getServerSessionStub = sinon + .stub(nextAuth, "getServerSession") + .resolves({}); + await utils.createUsers(testUsers); + user = await db + .selectFrom("users") + .selectAll() + .where("username", "=", "membre.actif") + .executeTakeFirstOrThrow(); }); - afterEach(async () => { - getToken.restore(); - isPublicServiceEmailStub.restore(); + sinon.restore(); await utils.deleteUsers(testUsers); + isPublicServiceEmailStub.restore(); }); it("should ask OVH to delete a redirection", async () => { + const mockSession = { + user: { id: "membre.actif", isAdmin: false, uuid: user.uuid }, + }; + getServerSessionStub.resolves(mockSession); const ovhRedirectionDeletion = nock(/.*ovh.com/) .delete(/^.*email\/domain\/.*\/redirection\/.*/) .reply(200); - const res = await chai - .request(app) - .delete( - "/api/users/membre.actif/redirections/test-2@example.com/delete" - ); + await deleteRedirectionForUser({ + username: "membre.actif", + toEmail: "test-2@example.com", + }); ovhRedirectionDeletion.isDone().should.be.true; }); - it("should not allow redirection deletion from delegate", (done) => { + it("should not allow redirection deletion from delegate", async () => { + const mockSession = { + user: { id: "membre.actif", isAdmin: false, uuid: user.uuid }, + }; + getServerSessionStub.resolves(mockSession); const ovhRedirectionDeletion = nock(/.*ovh.com/) .delete(/^.*email\/domain\/.*\/redirection\/.*/) .reply(200); - - chai.request(app) - .delete( - "/api/users/membre.nouveau/redirections/test-2@example.com/delete" - ) - .end((err, res) => { - ovhRedirectionDeletion.isDone().should.be.false; - done(); + try { + await deleteRedirectionForUser({ + username: "membre.nouveau", + toEmail: "test-2@example.com", }); + } catch (e) { + ovhRedirectionDeletion.isDone().should.be.false; + } }); - it("should not allow redirection deletion from expired users", (done) => { + it("should not allow redirection deletion from expired users", async () => { const ovhRedirectionDeletion = nock(/.*ovh.com/) .delete(/^.*email\/domain\/.*\/redirection\/.*/) .reply(200); - getToken.returns(utils.getJWT("membre.expire")); + const mockSession = { + user: { id: "membre.expire", isAdmin: false, uuid: user.uuid }, + }; + getServerSessionStub.resolves(mockSession); - chai.request(app) - .delete( - "/api/users/membre.expire/redirections/test-2@example.com/delete" - ) - .end((err, res) => { - ovhRedirectionDeletion.isDone().should.be.false; - done(); + try { + await deleteRedirectionForUser({ + username: "membre.expire", + toEmail: "test-2@example.com", }); + } catch (e) { + ovhRedirectionDeletion.isDone().should.be.false; + } }); }); - describe("POST /users/:username/password unauthenticated", () => { + describe("Test update password server action unauthenticated", () => { beforeEach(async () => { await utils.createUsers(testUsers); }); @@ -463,65 +500,72 @@ describe("User", () => { await utils.deleteUsers(testUsers); }); - it("should return an Unauthorized error", (done) => { - chai.request(app) - .post("/api/users/membre.actif/password") - .type("form") - .send({ + it("should return an Unauthorized error", async () => { + try { + await updatePasswordForUser({ + username: "membre.actif", new_password: "Test_Password_1234", - }) - .end((err, res) => { - res.should.have.status(401); - done(); }); + } catch (e) { + expect(e).to.be.an("error"); + } }); - it("should not allow a password change", (done) => { + it("should not allow a password change", async () => { ovhPasswordNock = nock(/.*ovh.com/) .post(/^.*email\/domain\/.*\/account\/.*\/changePassword/) .reply(200); - chai.request(app) - .post("/api/users/membre.actif/password") - .type("form") - .send({ + try { + await updatePasswordForUser({ new_password: "Test_Password_1234", - }) - .end((err, res) => { - ovhPasswordNock.isDone().should.be.false; - done(); + username: "membre.actif", }); + } catch (e) { + ovhPasswordNock.isDone().should.be.false; + } }); }); - describe("POST /api/users/:username/password authenticated", () => { - let getToken; + describe("Test update password server action authenticated", () => { + let getServerSessionStub; let isPublicServiceEmailStub; + let user; + beforeEach(async () => { - getToken = sinon.stub(session, "getToken"); - getToken.returns(utils.getJWT("membre.actif")); isPublicServiceEmailStub = sinon .stub(controllerUtils, "isPublicServiceEmail") .returns(Promise.resolve(true)); + getServerSessionStub = sinon + .stub(nextAuth, "getServerSession") + .resolves({}); + await utils.createUsers(testUsers); + user = await db + .selectFrom("users") + .selectAll() + .where("username", "=", "membre.actif") + .executeTakeFirstOrThrow(); }); - afterEach(async () => { - getToken.restore(); + sinon.restore(); + await utils.deleteUsers(testUsers); isPublicServiceEmailStub.restore(); await utils.deleteUsers(testUsers); }); it("should send error if user does not exist", async () => { - const res = await chai - .request(app) - .post("/api/users/membre.actif/password") - .type("form") - .send({ + const mockSession = { + user: { id: "membre.actif", isAdmin: false, uuid: user.uuid }, + }; + getServerSessionStub.resolves(mockSession); + try { + await updatePasswordForUser({ new_password: "Test_Password_1234", - }) - .redirects(0); - // .end((err, res) => { - res.should.have.status(500); + username: "membre.onthetom", + }); + } catch (e) { + expect(e).to.be.an("error"); + } // res.header.location.should.equal("/community/membre.actif"); // done(); // }); @@ -534,7 +578,11 @@ describe("User", () => { utils.mockSlackSecretariat(); utils.mockOvhTime(); utils.mockOvhRedirections(); - const username = "membre.nouveau"; + const mockSession = { + user: { id: "membre.actif", isAdmin: false, uuid: user.uuid }, + }; + getServerSessionStub.resolves(mockSession); + const username = "membre.actif"; await db .updateTable("users") .where("username", "=", username) @@ -544,21 +592,21 @@ describe("User", () => { .get(/^.*email\/domain\/.*\/account\/.*/) .reply(200, { accountName: username, - email: "membre.nouveau@example.com", + email: "membre.actif@example.com", }) .persist(); ovhPasswordNock = nock(/.*ovh.com/) .post(/^.*email\/domain\/.*\/account\/.*\/changePassword/) .reply(200); - getToken.returns(utils.getJWT(`${username}`)); - await chai - .request(app) - .post(`/api/users/${username}/password`) - .type("form") - .send({ + try { + await updatePasswordForUser({ + username, new_password: "Test_Password_1234", }); + } catch (e) { + expect(e).to.be.an("error"); + } ovhPasswordNock.isDone().should.be.true; }); it("should perform a password change and pass status to active if status was suspended", async () => { @@ -569,7 +617,11 @@ describe("User", () => { utils.mockSlackSecretariat(); utils.mockOvhTime(); utils.mockOvhRedirections(); - const username = "membre.nouveau"; + const username = "membre.actif"; + const mockSession = { + user: { id: "membre.actif", isAdmin: false, uuid: user.uuid }, + }; + getServerSessionStub.resolves(mockSession); await db .updateTable("users") .where("username", "=", username) @@ -581,117 +633,131 @@ describe("User", () => { .get(/^.*email\/domain\/.*\/account\/.*/) .reply(200, { accountName: username, - email: "membre.nouveau@example.com", + email: "membre.actif@example.com", }) .persist(); ovhPasswordNock = nock(/.*ovh.com/) .post(/^.*email\/domain\/.*\/account\/.*\/changePassword/) .reply(200); - getToken.returns(utils.getJWT(`${username}`)); - await chai - .request(app) - .post(`/api/users/${username}/password`) - .type("form") - .send({ - new_password: "Test_Password_1234", - }); + await updatePasswordForUser({ + new_password: "Test_Password_1234", + username: username, + }); + ovhPasswordNock.isDone().should.be.true; - const user = await db + const user2 = await db .selectFrom("users") .selectAll() .where("username", "=", username) - .executeTakeFirst(); - user.primary_email_status.should.be.equal( + .executeTakeFirstOrThrow(); + user2.primary_email_status.should.be.equal( EmailStatusCode.EMAIL_ACTIVE ); }); - it("should not allow a password change from delegate", (done) => { + it("should not allow a password change from delegate", async () => { + const mockSession = { + user: { id: "membre.actif", isAdmin: false, uuid: user.uuid }, + }; + getServerSessionStub.resolves(mockSession); + ovhPasswordNock = nock(/.*ovh.com/) .post(/^.*email\/domain\/.*\/account\/.*\/changePassword/) .reply(200); - - chai.request(app) - .post("/api/users/membre.nouveau/password") - .type("form") - .send({ + try { + await updatePasswordForUser({ new_password: "Test_Password_1234", - }) - .end((err, res) => { - ovhPasswordNock.isDone().should.be.false; - done(); + username: "membre.nouveau", }); + } catch (e) { + expect(e).to.be.an("error"); + } }); - it("should not allow a password change from expired user", (done) => { + it("should not allow a password change from expired user", async () => { ovhPasswordNock = nock(/.*ovh.com/) .post(/^.*email\/domain\/.*\/account\/.*\/changePassword/) .reply(200); - getToken.returns(utils.getJWT("membre.expire")); + user = await db + .selectFrom("users") + .selectAll() + .where("username", "=", "membre.expire") + .executeTakeFirstOrThrow(); + const mockSession = { + user: { id: "membre.expire", isAdmin: false, uuid: user.uuid }, + }; + getServerSessionStub.resolves(mockSession); - chai.request(app) - .post("/api/users/membre.expire/password") - .type("form") - .send({ + try { + await updatePasswordForUser({ new_password: "Test_Password_1234", - }) - .end((err, res) => { - ovhPasswordNock.isDone().should.be.false; - done(); + username: "membre.expire", }); + } catch (e) { + ovhPasswordNock.isDone().should.be.false; + } }); - it("should not allow a password shorter than 9 characters", (done) => { + it("should not allow a password shorter than 9 characters", async () => { ovhPasswordNock = nock(/.*ovh.com/) .post(/^.*email\/domain\/.*\/account\/.*\/changePassword/) .reply(200); - - chai.request(app) - .post("/api/users/membre.actif/password") - .type("form") - .send({ + const mockSession = { + user: { id: "membre.actif", isAdmin: false, uuid: user.uuid }, + }; + getServerSessionStub.resolves(mockSession); + try { + await updatePasswordForUser({ new_password: "12345678", - }) - .end((err, res) => { - ovhPasswordNock.isDone().should.be.false; - done(); + username: "membre.actif", }); + } catch (e) { + ovhPasswordNock.isDone().should.be.false; + } }); - it("should not allow a password longer than 30 characters", (done) => { + it("should not allow a password longer than 30 characters", async () => { ovhPasswordNock = nock(/.*ovh.com/) .post(/^.*email\/domain\/.*\/account\/.*\/changePassword/) .reply(200); - chai.request(app) - .post("/api/users/membre.actif/password") - .type("form") - .send({ + try { + await updatePasswordForUser({ new_password: "1234567890123456789012345678901", - }) - .end((err, res) => { - ovhPasswordNock.isDone().should.be.false; - done(); + username: "membre.actif", }); + } catch (e) { + ovhPasswordNock.isDone().should.be.false; + } }); }); - describe("POST /users/:username/email/delete unauthenticated", () => { - it("should return an Unauthorized error", (done) => { - chai.request(app) - .post("/api/users/membre.parti/email/delete") - .end((err, res) => { - res.should.have.status(401); - done(); + describe("Delete user email when unauthenticated", () => { + let getServerSessionStub; + beforeEach(() => { + getServerSessionStub = sinon + .stub(nextAuth, "getServerSession") + .resolves(undefined); + }); + afterEach(() => { + getServerSessionStub.restore(); + }); + it("should return an Unauthorized error", async () => { + try { + await deleteEmailForUser({ + username: "membre.parti", }); + } catch (err) { + expect(err).to.be.an("error"); + } }); }); - describe("POST /user/:username/email/delete", () => { - let getToken; + describe("Delete user email", () => { let isPublicServiceEmailStub; - + let getServerSessionStub; beforeEach(async () => { - getToken = sinon.stub(session, "getToken"); - getToken.returns(utils.getJWT("membre.actif")); + getServerSessionStub = sinon + .stub(nextAuth, "getServerSession") + .resolves({}); isPublicServiceEmailStub = sinon .stub(controllerUtils, "isPublicServiceEmail") .returns(Promise.resolve(true)); @@ -699,11 +765,24 @@ describe("User", () => { }); afterEach(async () => { - getToken.restore(); + getServerSessionStub.restore(); isPublicServiceEmailStub.restore(); await utils.deleteUsers(testUsers); }); it("should keep the user in database secretariat", async () => { + const user = await db + .selectFrom("users") + .selectAll() + .where("username", "=", "membre.actif") + .executeTakeFirstOrThrow(); + const mockSession = { + user: { + id: "membre.actif", + isAdmin: false, + uuid: user.uuid, + }, + }; + getServerSessionStub.resolves(mockSession); const addRedirection = nock(/.*ovh.com/) .post(/^.*email\/domain\/.*\/redirection/) .reply(200); @@ -714,9 +793,7 @@ describe("User", () => { .where("username", "=", "membre.actif") .execute(); dbRes.length.should.equal(1); - await chai - .request(app) - .post("/api/users/membre.actif/email/delete"); + await deleteEmailForUser({ username: "membre.actif" }); const dbNewRes = await db .selectFrom("users") .selectAll() @@ -726,7 +803,15 @@ describe("User", () => { addRedirection.isDone().should.be.true; }); - it("should ask OVH to redirect to the departs email", (done) => { + it("should ask OVH to redirect to the departs email", async () => { + const mockSession = { + user: { + id: "membre.actif", + isAdmin: false, + uuid: "membre.actif", + }, + }; + getServerSessionStub.resolves(mockSession); const expectedRedirectionBody = (body) => { return ( body.from === `membre.actif@${config.domain}` && @@ -740,46 +825,48 @@ describe("User", () => { expectedRedirectionBody ) .reply(200); - - chai.request(app) - .post("/api/users/membre.actif/email/delete") - .end((err, res) => { - ovhRedirectionDepartureEmail.isDone().should.be.true; - done(); - }); + await deleteEmailForUser({ username: "membre.actif" }); + ovhRedirectionDepartureEmail.isDone().should.be.true; }); }); - describe("POST /users/:username/secondary_email", () => { - let getToken; + describe("Test manage secondary email", () => { let isPublicServiceEmailStub; + let getServerSessionStub; + let user; + const manageSecondaryEmailForUser = proxyquire( + "@/app/api/member/actions", + { + "next/cache": { + revalidatePath: sinon.stub(), + }, + } + ).manageSecondaryEmailForUser; beforeEach(async () => { - getToken = sinon.stub(session, "getToken"); - getToken.returns(utils.getJWT("membre.nouveau")); - await utils.createUsers(testUsers); isPublicServiceEmailStub = sinon .stub(controllerUtils, "isPublicServiceEmail") .returns(Promise.resolve(true)); - }); + getServerSessionStub = sinon + .stub(nextAuth, "getServerSession") + .resolves({}); + await utils.createUsers(testUsers); + user = await db + .selectFrom("users") + .selectAll() + .where("username", "=", "membre.nouveau") + .executeTakeFirstOrThrow(); + const mockSession = { + user: { id: "membre.nouveau", isAdmin: false, uuid: user.uuid }, + }; + getServerSessionStub.resolves(mockSession); + }); afterEach(async () => { - getToken.restore(); + sinon.restore(); await utils.deleteUsers(testUsers); isPublicServiceEmailStub.restore(); - }); - it("should return 200 to add secondary email", async () => { - const username = "membre.nouveau"; - const secondaryEmail = "membre.nouveau.perso@example.com"; - const res = await chai - .request(app) - .post("/api/users/membre.nouveau/secondary_email") - .type("form") - .send({ - username, - secondaryEmail, - }); - res.should.have.status(200); + await utils.deleteUsers(testUsers); }); it("should add secondary email", async () => { @@ -791,14 +878,11 @@ describe("User", () => { .selectAll() .where("username", "=", "membre.nouveau") .execute(); - await chai - .request(app) - .post(`/api/users/${username}/secondary_email`) - .type("form") - .send({ - username, - secondaryEmail, - }); + await manageSecondaryEmailForUser({ + username, + secondaryEmail, + }); + const dbNewRes = await db .selectFrom("users") .selectAll() @@ -820,14 +904,10 @@ describe("User", () => { secondary_email: secondaryEmail, }) .execute(); - await chai - .request(app) - .post(`/api/users/${username}/secondary_email/`) - .type("form") - .send({ - username, - secondaryEmail: newSecondaryEmail, - }); + await manageSecondaryEmailForUser({ + username, + secondaryEmail: newSecondaryEmail, + }); const dbNewRes = await db .selectFrom("users") .selectAll() @@ -845,10 +925,19 @@ describe("User", () => { }); }); - describe("PUT /api/users/:username/primary_email", () => { + describe("Test action managePrimaryEmailForUser", () => { let mattermostGetUserByEmailStub; let isPublicServiceEmailStub; - let getToken; + let getServerSessionStub; + let user; + const managePrimaryEmailForUser = proxyquire( + "@/app/api/member/actions", + { + "next/cache": { + revalidatePath: sinon.stub(), + }, + } + ).managePrimaryEmailForUser; beforeEach(async () => { mattermostGetUserByEmailStub = sinon @@ -857,30 +946,38 @@ describe("User", () => { isPublicServiceEmailStub = sinon .stub(controllerUtils, "isPublicServiceEmail") .returns(Promise.resolve(true)); - getToken = sinon.stub(session, "getToken"); - getToken.returns(utils.getJWT("membre.nouveau")); + getServerSessionStub = sinon + .stub(nextAuth, "getServerSession") + .resolves({}); + await utils.createUsers(testUsers); + user = await db + .selectFrom("users") + .selectAll() + .where("username", "=", "membre.nouveau") + .executeTakeFirstOrThrow(); }); afterEach(async () => { + sinon.restore(); + await utils.deleteUsers(testUsers); mattermostGetUserByEmailStub.restore(); isPublicServiceEmailStub.restore(); - getToken.restore(); await utils.deleteUsers(testUsers); }); it("should not update primary email if user is not current user", async () => { + const mockSession = { + user: { id: "anyuser", isAdmin: false, uuid: "anyuser-uuid" }, + }; + getServerSessionStub.resolves(mockSession); const username = "membre.nouveau"; const primaryEmail = "membre.nouveau.new@example.com"; - getToken.returns(utils.getJWT("julien.dauphant")); + try { + await managePrimaryEmailForUser({ username, primaryEmail }); + } catch (e) { + console.log(e); + } - await chai - .request(app) - .put(`/api/users/${username}/primary_email/`) - .type("form") - .send({ - username, - primaryEmail: primaryEmail, - }); isPublicServiceEmailStub.called.should.be.true; mattermostGetUserByEmailStub.calledTwice.should.be.false; }); @@ -889,23 +986,29 @@ describe("User", () => { const username = "membre.nouveau"; const primaryEmail = "membre.nouveau.new@example.com"; isPublicServiceEmailStub.returns(Promise.resolve(false)); - getToken.returns(utils.getJWT("membre.nouveau")); - await chai - .request(app) - .put(`/api/users/${username}/primary_email/`) - .type("form") - .send({ - username, - primaryEmail: primaryEmail, - }); + const user = await db + .selectFrom("users") + .selectAll() + .where("username", "=", "membre.nouveau") + .executeTakeFirstOrThrow(); + + const mockSession = { + user: { id: "membre.nouveau", isAdmin: false, uuid: user.uuid }, + }; + getServerSessionStub.resolves(mockSession); + + try { + await managePrimaryEmailForUser({ username, primaryEmail }); + } catch (e) { + console.log(e); + } const dbNewRes = await db .selectFrom("users") .selectAll() .where("username", "=", "membre.nouveau") - .execute(); - dbNewRes.length.should.equal(1); - dbNewRes[0].primary_email.should.not.equal(primaryEmail); + .executeTakeFirstOrThrow(); + dbNewRes.primary_email?.should.not.equal(primaryEmail); isPublicServiceEmailStub.called.should.be.true; mattermostGetUserByEmailStub.calledOnce.should.be.false; }); @@ -915,7 +1018,11 @@ describe("User", () => { mattermostGetUserByEmailStub.returns(Promise.reject("404 error")); const username = "membre.nouveau"; const primaryEmail = "membre.nouveau.new@example.com"; - getToken.returns(utils.getJWT("membre.nouveau")); + const mockSession = { + user: { id: "membre.nouveau", isAdmin: false, uuid: user.uuid }, + }; + getServerSessionStub.resolves(mockSession); + await db .updateTable("users") .where("username", "=", "membre.nouveau") @@ -924,21 +1031,17 @@ describe("User", () => { }) .execute(); - const res = await chai - .request(app) - .put(`/api/users/${username}/primary_email/`) - .type("form") - .send({ - username, - primaryEmail: primaryEmail, - }); + try { + await managePrimaryEmailForUser({ username, primaryEmail }); + } catch (e) { + console.log(e); + } const dbNewRes = await db .selectFrom("users") .selectAll() .where("username", "=", "membre.nouveau") - .execute(); - dbNewRes.length.should.equal(1); - dbNewRes[0].primary_email.should.not.equal(primaryEmail); + .executeTakeFirstOrThrow(); + dbNewRes.primary_email?.should.not.equal(primaryEmail); mattermostGetUserByEmailStub.calledOnce.should.be.true; @@ -956,7 +1059,11 @@ describe("User", () => { mattermostGetUserByEmailStub.returns(Promise.reject("404 error")); const username = "membre.nouveau"; const primaryEmail = "admin@otherdomaine.gouv.fr"; - getToken.returns(utils.getJWT("membre.nouveau")); + const mockSession = { + user: { id: "membre.nouveau", isAdmin: false, uuid: user.uuid }, + }; + getServerSessionStub.resolves(mockSession); + await db .updateTable("users") .where("username", "=", "membre.nouveau") @@ -965,21 +1072,15 @@ describe("User", () => { }) .execute(); - await chai - .request(app) - .put(`/api/users/${username}/primary_email/`) - .type("form") - .send({ - username, - primaryEmail: primaryEmail, - }); + try { + await managePrimaryEmailForUser({ username, primaryEmail }); + } catch (e) {} const dbNewRes = await db .selectFrom("users") .selectAll() .where("username", "=", "membre.nouveau") - .execute(); - dbNewRes.length.should.equal(1); - dbNewRes[0].primary_email.should.equal( + .executeTakeFirstOrThrow(); + dbNewRes.primary_email?.should.equal( `membre.nouveau@${config.domain}` ); await db @@ -1002,23 +1103,19 @@ describe("User", () => { .returns(Promise.resolve(true)); const username = "membre.nouveau"; const primaryEmail = "membre.nouveau.new@example.com"; - getToken.returns(utils.getJWT("membre.nouveau")); + const mockSession = { + user: { id: "membre.nouveau", isAdmin: false, uuid: user.uuid }, + }; + getServerSessionStub.resolves(mockSession); + + await managePrimaryEmailForUser({ username, primaryEmail }); - const res = await chai - .request(app) - .put(`/api/users/${username}/primary_email/`) - .type("form") - .send({ - username, - primaryEmail: primaryEmail, - }); const dbNewRes = await db .selectFrom("users") .selectAll() .where("username", "=", "membre.nouveau") - .execute(); - dbNewRes.length.should.equal(1); - dbNewRes[0].primary_email.should.equal(primaryEmail); + .executeTakeFirstOrThrow(); + dbNewRes.primary_email?.should.equal(primaryEmail); await db .updateTable("users") .where("username", "=", "membre.nouveau") @@ -1035,27 +1132,41 @@ describe("User", () => { }); }); - describe("Post delete /api/users/:username/email/delete authenticated", () => { - let getToken; + describe("Email delete", () => { let isPublicServiceEmailStub; + let getServerSessionStub; beforeEach(async () => { - getToken = sinon.stub(session, "getToken"); - getToken.returns(utils.getJWT("membre.actif")); - await utils.createUsers(testUsers); + getServerSessionStub = sinon + .stub(nextAuth, "getServerSession") + .resolves({}); isPublicServiceEmailStub = sinon .stub(controllerUtils, "isPublicServiceEmail") .returns(Promise.resolve(true)); + await utils.createUsers(testUsers); }); afterEach(async () => { - getToken.restore(); - await utils.deleteUsers(testUsers); + getServerSessionStub.restore(); isPublicServiceEmailStub.restore(); + await utils.deleteUsers(testUsers); }); - it("Deleting email should ask OVH to delete all redirections", (done) => { + it("Deleting email should ask OVH to delete all redirections", async () => { nock.cleanAll(); + const user = await db + .selectFrom("users") + .selectAll() + .where("username", "=", "membre.expire") + .executeTakeFirstOrThrow(); + const mockSession = { + user: { + id: "membre.expire", + isAdmin: false, + uuid: user.uuid, + }, + }; + getServerSessionStub.resolves(mockSession); nock(/.*ovh.com/) .get(/^.*email\/domain\/.*\/redirection/) @@ -1084,28 +1195,48 @@ describe("User", () => { .delete(/^.*email\/domain\/.*\/redirection\/123123/) .reply(200); - chai.request(app) - .post("/api/users/membre.expire/email/delete") - .end((err, res) => { - ovhRedirectionDeletion.isDone().should.be.true; - done(); - }); + await deleteEmailForUser({ username: "membre.expire" }); + ovhRedirectionDeletion.isDone().should.be.true; }); - it("should not allow email deletion for active users", (done) => { + it("should not allow email deletion for active users", async () => { const ovhEmailDeletion = nock(/.*ovh.com/) .delete(/^.*email\/domain\/.*\/account\/membre.expire/) .reply(200); - - chai.request(app) - .post("/api/users/membre.actif/email/delete") - .end((err, res) => { - ovhEmailDeletion.isDone().should.be.false; - done(); - }); + const user = await db + .selectFrom("users") + .selectAll() + .where("username", "=", "membre.expire") + .executeTakeFirstOrThrow(); + const mockSession = { + user: { + id: "membre.actif", + isAdmin: false, + uuid: user.uuid, + }, + }; + getServerSessionStub.resolves(mockSession); + try { + await deleteEmailForUser({ username: "membre.actif" }); + } catch (e) { + ovhEmailDeletion.isDone().should.be.false; + } }); - it("should not allow email deletion for another user if active", (done) => { + it("should not allow email deletion for another user if active", async () => { + const user = await db + .selectFrom("users") + .selectAll() + .where("username", "=", "membre.nouveau") + .executeTakeFirstOrThrow(); + const mockSession = { + user: { + id: "membre.nouveau", + isAdmin: false, + uuid: user.uuid, + }, + }; + getServerSessionStub.resolves(mockSession); nock.cleanAll(); const ovhEmailDeletion = nock(/.*ovh.com/) @@ -1120,17 +1251,28 @@ describe("User", () => { utils.mockSlackGeneral(); utils.mockSlackSecretariat(); - getToken.returns(utils.getJWT("membre.nouveau")); - chai.request(app) - .post("/api/users/membre.actif/email/delete") - .end((err, res) => { - ovhEmailDeletion.isDone().should.be.false; - done(); - }); + try { + await deleteEmailForUser({ username: "membre.actif" }); + } catch (e) { + ovhEmailDeletion.isDone().should.be.false; + } }); - it("should allow email deletion for requester even if active", (done) => { + it("should allow email deletion for requester even if active", async () => { nock.cleanAll(); + const user = await db + .selectFrom("users") + .selectAll() + .where("username", "=", "membre.actif") + .executeTakeFirstOrThrow(); + const mockSession = { + user: { + id: "membre.actif", + isAdmin: false, + uuid: user.uuid, + }, + }; + getServerSessionStub.resolves(mockSession); nock(/.*ovh.com/) .get(/^.*email\/domain\/.*\/redirection/) @@ -1163,12 +1305,11 @@ describe("User", () => { .delete(/^.*email\/domain\/.*\/redirection\/123123/) .reply(200); - chai.request(app) - .post("/api/users/membre.actif/email/delete") - .end((err, res) => { - ovhRedirectionDeletion.isDone().should.be.true; - done(); - }); + try { + await deleteEmailForUser({ username: "membre.actif" }); + } catch (e) { + ovhRedirectionDeletion.isDone().should.be.true; + } }); }); diff --git a/package.json b/package.json index 002388918..3bbe7396e 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,11 @@ "node": "20.10.0" }, "scripts": { - "dev": "node --max_old_space_size=8192 -r ts-node/register src/server/server.ts", - "build:next": "next build", - "build:server": "ttsc -p tsconfig.server.json", + "dev": "next dev -p 8100", "build": "npm run build:next && npm run build:server", - "start": "NODE_ENV=production node dist/src/server/server.js", + "build:server": "ttsc -p tsconfig.server.json", + "build:next": "next build", + "start": "NODE_ENV=production next start --port ${PORT-3000}", "lint": "next lint", "migrate": "knex migrate:latest --esm", "rolldown": "knex migrate:down --esm", diff --git a/src/app/(private)/(dashboard)/admin/mattermost/AdminMattermostClientPage.tsx b/src/app/(private)/(dashboard)/admin/mattermost/AdminMattermostClientPage.tsx index 8297b3aba..89c8c32de 100644 --- a/src/app/(private)/(dashboard)/admin/mattermost/AdminMattermostClientPage.tsx +++ b/src/app/(private)/(dashboard)/admin/mattermost/AdminMattermostClientPage.tsx @@ -4,6 +4,7 @@ import { useEffect, useState } from "react"; import axios from "axios"; import type { Metadata } from "next"; +import { safeGetMattermostInfo } from "@/app/api/admin/actions/getMattermostAdmin"; import { AdminMattermost, AdminMattermostProps, @@ -12,16 +13,13 @@ import routes, { computeRoute } from "@/routes/routes"; export default function Page() { const [data, setData] = useState({}); - const [isLoading, setLoading] = useState(true); + const [isLoading] = useState(true); useEffect(() => { - axios - .get(computeRoute(routes.ADMIN_MATTERMOST_API), { - withCredentials: true, - }) - .then((res) => { - setData(res.data); - setLoading(false); - }); + async function fetchData() { + const res = await safeGetMattermostInfo(); + setData(res.data || {}); + } + fetchData(); }, []); if (isLoading) return
Chargement...
; diff --git a/src/app/api/admin/actions/getMattermostAdmin.ts b/src/app/api/admin/actions/getMattermostAdmin.ts new file mode 100644 index 000000000..24b568f36 --- /dev/null +++ b/src/app/api/admin/actions/getMattermostAdmin.ts @@ -0,0 +1,60 @@ +"use server"; + +import { getServerSession } from "next-auth/next"; + +import { MattermostChannel } from "@/lib/mattermost"; +import config from "@/server/config"; +import { authOptions } from "@/utils/authoptions"; +import { + AuthorizationError, + UnwrapPromise, + withErrorHandling, +} from "@/utils/error"; +import { getAllChannels } from "@infra/chat"; +import { + MattermostUserWithStatus, + getMattermostUsersWithStatus, +} from "@schedulers/mattermostScheduler/removeBetaAndParnersUsersFromCommunityTeam"; + +export async function getMattermostInfo() { + const session = await getServerSession(authOptions); + if (!session || !session.user.id) { + throw new AuthorizationError(); + } + if (!session.user.isAdmin) { + throw new AuthorizationError(`L'utilisateur doit être administrateur`); + } + + let users: MattermostUserWithStatus[] = []; + + if (process.env.NODE_ENV === "production") { + users = await getMattermostUsersWithStatus({ + nbDays: 90, + }); + } + + const channels: MattermostChannel[] = await getAllChannels( + config.mattermostTeamId + ); + try { + const title = "Admin Mattermost"; + return { + title, + users, + channelOptions: channels.map((channel) => ({ + value: channel.name, + label: channel.display_name, + })), + currentUserId: session.user.id, + isAdmin: config.ESPACE_MEMBRE_ADMIN.includes(session.user.id), + }; + } catch (err) { + console.error(err); + throw err; + } +} + +export const safeGetMattermostInfo = withErrorHandling< + UnwrapPromise