Skip to content

Commit

Permalink
fix(hasura): replace email webhook with actions (#282)
Browse files Browse the repository at this point in the history
* fix(hasura): replace email webhook with actions

* fix: remove unsused file

* Update targets/frontend/src/components/user/List.js

Co-authored-by: Julien Bouquillon <julien.bouquillon@sg.social.gouv.fr>

Co-authored-by: Julien Bouquillon <julien.bouquillon@sg.social.gouv.fr>
  • Loading branch information
lionelB and Julien Bouquillon committed Jan 22, 2021
1 parent f3a87ea commit 751e765
Show file tree
Hide file tree
Showing 14 changed files with 185 additions and 130 deletions.
5 changes: 2 additions & 3 deletions .env
@@ -1,7 +1,6 @@
##
## Hasura
##
ACCOUNT_EMAIL_WEBHOOK_URL=http://host.docker.internal:3000/api/webhooks/account
PUBLICATION_WEBHOOK_URL=http://host.docker.internal:3000/api/webhooks/publication
API_URL=http://host.docker.internal:3000/api

Expand Down Expand Up @@ -29,8 +28,8 @@ HASURA_GRAPHQL_NO_OF_RETRIES=5
HASURA_GRAPHQL_MIGRATIONS_SERVER_TIMEOUT=10
HASURA_GRAPHQL_UNAUTHORIZED_ROLE=public

# webhook
ACCOUNT_EMAIL_SECRET=a random string that will be verify when calling the webhook
# webhook & action
ACTIONS_SECRET=a random string that will be verify when calling the webhook
PUBLICATION_SECRET=a random string that will be verify when calling the webhook

NEXT_PUBLIC_CONTAINER_NAME=cdtn-dev
10 changes: 8 additions & 2 deletions targets/frontend/src/components/user/List.js
Expand Up @@ -66,7 +66,6 @@ export function UserList() {
<pre>{JSON.stringify(error, 0, 2)}</pre>
</Message>
);

return (
<>
<Dialog
Expand Down Expand Up @@ -96,7 +95,14 @@ export function UserList() {
</thead>
<tbody>
{data.users.map(
({ id, roles: [{ role }], name, email, created_at, active }) => (
({
id,
roles: [{ role } = {}],
name,
email,
created_at,
active,
}) => (
<Tr key={id}>
<Td align="center">
<Badge variant={role === "admin" ? "primary" : "secondary"}>
Expand Down
4 changes: 4 additions & 0 deletions targets/frontend/src/lib/apiError.js
Expand Up @@ -3,3 +3,7 @@ export function createErrorFor(res) {
res.status(statusCode).json(payload);
};
}

export function apiError(res, { output: { statusCode, payload } }) {
return res.status(statusCode).json(payload);
}
24 changes: 24 additions & 0 deletions targets/frontend/src/lib/emails/activateAccount.js
@@ -0,0 +1,24 @@
import sendmail from "./sendmail";

const BASE_URL =
process.env.CI_ENVIRONMENT_URL || `http://localhost:${process.env.PORT}`;

export function sendActivateAccountEmail(email, secret_token) {
const subject = "Activation de votre compte";
const activateUrl = `${BASE_URL}/change_password?token=${secret_token}&activate=1`; // todo: dynamic hostname
const text = `Bonjour,
Vous pouvez activer votre compte ${email} afin d'accéder à
l'outil d'administration du cdtn en suivant ce lien : ${activateUrl}
L'equipe veille CDTN
`;

var mailOptions = {
from: process.env.ACCOUNT_MAIL_SENDER,
subject,
text,
to: email,
};

return sendmail(mailOptions);
}
28 changes: 28 additions & 0 deletions targets/frontend/src/lib/emails/lostPassword.js
@@ -0,0 +1,28 @@
import sendmail from "./sendmail";

const BASE_URL =
process.env.CI_ENVIRONMENT_URL || `http://localhost:${process.env.PORT}`;

export function sendLostPasswordEmail(email, secret_token) {
const activateUrl = `${BASE_URL}/change_password?token=${secret_token}`; // todo: dynamic hostname
const subject = "Réinitialisation de votre mot de passe";
const text = `
Bonjour,
Une demande pour réinitialiser votre de mot de passe est en cours.
Vous pouvez suivre ce lien : ${activateUrl} pour valider la demande.
Si vous n'etes pas à l'origine de cette demande, pas de soucis,
ne tenez pas compte de de message.
L'equipe veille CDTN
`;

var mailOptions = {
from: process.env.ACCOUNT_MAIL_SENDER,
subject,
text,
to: email,
};

return sendmail(mailOptions);
}
File renamed without changes.
21 changes: 21 additions & 0 deletions targets/frontend/src/pages/api/actions/email_account_activation.js
@@ -0,0 +1,21 @@
import Boom from "@hapi/boom";
import { apiError } from "src/lib/apiError";
import { sendActivateAccountEmail } from "src/lib/emails/activateAccount";

export default async function ActivateAccount(req, res) {
if (
!req.headers["actions-secret"] ||
req.headers["actions-secret"] !== process.env.ACTIONS_SECRET
) {
return apiError(res, Boom.unauthorized("Missing secret or env"));
}

const { email, secret_token } = req.body.input;
try {
await sendActivateAccountEmail(email, secret_token);
res.json({ message: "email sent!", statusCode: 200 });
} catch (error) {
console.error(`[email] send activation email to ${email} failed`);
apiError(res, Boom.badGateway(`send activation email to ${email} failed`));
}
}
21 changes: 21 additions & 0 deletions targets/frontend/src/pages/api/actions/email_password_request.js
@@ -0,0 +1,21 @@
import Boom from "@hapi/boom";
import { apiError } from "src/lib/apiError";
import { sendLostPasswordEmail } from "src/lib/emails/lostPassword";

export default async function AskNewPassword(req, res) {
if (
!req.headers["actions-secret"] ||
req.headers["actions-secret"] !== process.env.ACTIONS_SECRET
) {
return apiError(res, Boom.unauthorized("Missing secret or env"));
}

const { email, secret_token } = req.body.input;
try {
await sendLostPasswordEmail(email, secret_token);
res.json({ message: "email sent!", statusCode: 200 });
} catch (error) {
console.error(`[email] send activation email to ${email} failed`);
apiError(res, Boom.badGateway(`send activation email to ${email} failed`));
}
}
13 changes: 12 additions & 1 deletion targets/frontend/src/pages/api/reset_password.js
Expand Up @@ -24,13 +24,14 @@ export default async function reset_password(req, res) {
}

const { email } = value;
const secret_token = uuidv4();
const result = await client
.query(udpateSecretTokenMutation, {
email,
expires: getExpiryDate(
parseInt(process.env.NEXT_PUBLIC_ACTIVATION_TOKEN_EXPIRES, 10)
),
secret_token: uuidv4(),
secret_token,
})
.toPromise();

Expand All @@ -41,6 +42,10 @@ export default async function reset_password(req, res) {
return;
}

await client
.mutation(emailPasswordRequestMutation, { email, secret_token })
.toPromise();

console.log("[reset_password]", email);
res.json({ message: "reset password" });
}
Expand All @@ -64,3 +69,9 @@ mutation updateSecretTokenMutation(
}
}
`;

const emailPasswordRequestMutation = `
mutation email($email: citext!, $secret_token: uuid!) {
email_password_request(email: $email, secret_token:$secret_token)
}
`;
91 changes: 0 additions & 91 deletions targets/frontend/src/pages/api/webhooks/account.js

This file was deleted.

19 changes: 14 additions & 5 deletions targets/frontend/src/pages/user/new.js
Expand Up @@ -10,15 +10,21 @@ import { useMutation } from "urql";

const registerUserMutation = `
mutation registerUser($user: auth_users_insert_input! ) {
insert_auth_users( objects: [$user] ) {
returning {
id
__typename
}
user: insert_auth_users_one( object: $user ) {
id
email
secret_token
__typename
}
}
`;

const emailAccountMutation = `
mutation email($email: citext!, $secret_token: uuid!) {
email_account_activation(email: $email, secret_token:$secret_token)
}
`;

function prepareMutationData(input) {
return {
user: {
Expand All @@ -33,12 +39,15 @@ function prepareMutationData(input) {
export function UserPage() {
const router = useRouter();
const [result, registerUser] = useMutation(registerUserMutation);
const [, emailAccount] = useMutation(emailAccountMutation);
const { fetching, error } = result;

function handleCreate(data) {
registerUser(prepareMutationData(data)).then((result) => {
if (!result.error) {
router.push("/users");
const { email, secret_token } = result.data.user;
emailAccount({ email, secret_token });
}
});
}
Expand Down
21 changes: 21 additions & 0 deletions targets/hasura/metadata/actions.graphql
@@ -1,3 +1,19 @@
type Mutation {
email_account_activation (
email: citext!
secret_token: uuid!
): Status
}


type Mutation {
email_password_request (
email: citext!
secret_token: uuid!
): Status
}


type Mutation {
preview_document (
cdtnId: String!
Expand All @@ -15,6 +31,11 @@ input PreviewDocument {
source : String
}

input EmailInput {
email : String!
secret_token : uuid!
}

type Status {
message : String!
statusCode : Int!
Expand Down

0 comments on commit 751e765

Please sign in to comment.