Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLDR-7405 Mail link to reset password #3764

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions tools/cldr-apps/js/src/esm/cldrComponents.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { App } from "vue";

// local components
import CldrError from "../views/CldrError.vue";
import CldrUser from "../views/CldrUser.vue";
import CldrValue from "../views/CldrValue.vue";
import LoginButton from "../views/LoginButton.vue";
import OverallErrors from "../views/OverallErrors.vue";
Expand Down Expand Up @@ -87,6 +88,7 @@ function setup(app) {
app.component("cldr-overall-errors", OverallErrors);
app.component("cldr-report-response", ReportResponse);
app.component("cldr-searchbutton", SearchButton);
app.component("cldr-user", CldrUser);
app.component("cldr-value", CldrValue);
}

Expand Down
39 changes: 39 additions & 0 deletions tools/cldr-apps/js/src/esm/cldrLoad.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as cldrAccount from "./cldrAccount.mjs";
import * as cldrAdmin from "./cldrAdmin.mjs";
import * as cldrAjax from "./cldrAjax.mjs";
import * as cldrBulkClosePosts from "./cldrBulkClosePosts.mjs";
import * as cldrClient from "./cldrClient.mjs";
import * as cldrCoverage from "./cldrCoverage.mjs";
import * as cldrCreateLogin from "./cldrCreateLogin.mjs";
import * as cldrDom from "./cldrDom.mjs";
Expand Down Expand Up @@ -134,6 +135,8 @@ function doHashChange(event) {
function parseHashAndUpdate(hash) {
if (hash) {
const pieces = hash.split("/");
// always set the pieces
cldrStatus.setCurrentPieces(pieces);
// pieces[1] is ALWAYS assumed to be locale or empty
if (pieces.length > 1) {
cldrStatus.setCurrentLocale(pieces[1]); // could be null
Expand All @@ -159,6 +162,7 @@ function parseHashAndUpdate(hash) {
cldrStatus.setCurrentId("");
cldrStatus.setCurrentPage("");
cldrStatus.setCurrentSection("");
cldrStatus.setCurrentPieces([]);
}
updateWindowTitle();

Expand Down Expand Up @@ -1132,6 +1136,39 @@ function linkToLocale(subLoc) {
);
}

/**
* Login with emailed jwt. returns {sessionId, user}
* @param {String} jwt
*/
async function loginWithJwt(jwt) {
const client = await cldrClient.getClient();

const r = await client.apis.auth.login(
{ remember: true },
{ requestBody: { jwt } }
);
if (!r) {
throw Error(`Error: login() did not return a value.`);
}
const { body } = r;
if (!body) {
throw Error(`Error: body was not returned from login() API`);
}
return body;
}

/**
* Send a reset hash
* @param {String} email
* @param {String} session
*/
async function sendResetHash(email, session) {
const client = await cldrClient.getClient();

const r = await client.apis.auth.reset({}, { requestBody: { email } });
return;
}

export {
appendLocaleLink,
continueInitializing,
Expand All @@ -1147,10 +1184,12 @@ export {
insertLocaleSpecialNote,
linkToLocale,
localeSpecialNote,
loginWithJwt,
myLoad,
parseHashAndUpdate,
reloadV,
replaceHash,
sendResetHash,
setLoading,
setTheLocaleMap,
showCurrentId,
Expand Down
16 changes: 16 additions & 0 deletions tools/cldr-apps/js/src/esm/cldrStatus.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const refs = {
surveyUser: ref(null),
currentId: ref(null),
sessionId: ref(null),
currentPieces: ref(null),
};

/**
Expand Down Expand Up @@ -144,6 +145,18 @@ function setContextPath(path) {
}
}

/** An array of additional pieces of the hashes. */
let currentPieces = [];

function setCurrentPieces(pieces) {
currentPieces = pieces;
setRef("currentPieces", pieces);
}

function getCurrentPieces() {
return currentPieces;
}

/**
* A string such as '' (empty), or '821c2a2fc5c206d' (identifying an xpath),
* or '12345' (identifying a user) or other string (identifying a forum post)
Expand All @@ -154,6 +167,7 @@ function getCurrentId() {
return currentId;
}

/** This also calls setPieces([]) */
function setCurrentId(id) {
if (!id) {
currentId = "";
Expand Down Expand Up @@ -472,6 +486,7 @@ export {
getCurrentLocale,
getCurrentLocaleName,
getCurrentPage,
getCurrentPieces,
getCurrentSection,
getCurrentSpecial,
getIsPhaseBeta,
Expand All @@ -498,6 +513,7 @@ export {
setCurrentLocale,
setCurrentLocaleName,
setCurrentPage,
setCurrentPieces,
setCurrentSection,
setCurrentSpecial,
setIsDisconnected,
Expand Down
2 changes: 2 additions & 0 deletions tools/cldr-apps/js/src/esm/cldrText.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -490,11 +490,13 @@ const strings = {
special_list_emails: "List Email Addresses",
special_list_users: "List Users",
special_locales: "Locale List",
special_login: "Login from Email Link",
special_lock_account: "Lock (Disable) My Account",
special_lookup: "Look up a code or xpath",
special_mail: "Notifications (SMOKETEST ONLY)",
special_menu: "☰",
special_oldvotes: "Import Old Votes",
special_reset: "Forgot Password",
special_upload: "Upload (Bulk Import)",
// The special_r_* are the names of reports, used by ReportResponse.vue and others
special_r_compact: "Numbers",
Expand Down
6 changes: 5 additions & 1 deletion tools/cldr-apps/js/src/esm/cldrVueMap.mjs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import AboutPanel from "../views/AboutPanel.vue";
import AnnouncePanel from "../views/AnnouncePanel.vue";
import AddUser from "../views/AddUser.vue";
import AnnouncePanel from "../views/AnnouncePanel.vue";
import AutoImport from "../views/AutoImport.vue";
import CldrLogin from "../views/CldrLogin.vue";
import DowngradedVotes from "../views/DowngradedVotes.vue";
import GeneralInfo from "../views/GeneralInfo.vue";
import LockAccount from "../views/LockAccount.vue";
import LookUp from "../views/LookUp.vue";
import MainMenu from "../views/MainMenu.vue";
import ResetPassword from "../views/ResetPassword.vue";
import TestPanel from "../views/TestPanel.vue";
import TransferVotes from "../views/TransferVotes.vue";
import UnknownPanel from "../views/UnknownPanel.vue";
Expand All @@ -26,8 +28,10 @@ const specialToComponentMap = {
downgraded: DowngradedVotes,
general: GeneralInfo,
lock_account: LockAccount,
login: CldrLogin,
lookup: LookUp,
menu: MainMenu,
reset: ResetPassword,
retry: WaitingPanel,
retry_inplace: WaitingPanel, // Like retry, but do NOT redirect after resume.
test_panel: TestPanel, // for testing
Expand Down
74 changes: 74 additions & 0 deletions tools/cldr-apps/js/src/views/CldrLogin.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<template>
<!-- if the user closes the alert, just go back to the ST homepage-->
<a-alert
closable="true"
v-on:close="stay()"
v-show="loginMessage"
type="info"
:message="loginMessage"
/>

<!-- show user login -->
<a-card hoverable v-if="jwtGood">
<cldr-user v-if="jwtGood && loginId && loginName && loginEmail" v-bind:uid="loginId" />
<p>You are now logged in via the email link.</p>
<a-button v-on:click="stay()">Keep me logged in.</a-button>
</a-card>
</template>

<script setup>
/**
* This page is for extended login requirements, such as
* logging in from an emailed jwt link.
*/
import { ref, onMounted } from "vue";

import * as cldrLoad from "../esm/cldrLoad.mjs";
import * as cldrStatus from "../esm/cldrStatus.mjs";

const { surveyUser, sessionId, currentPieces } = cldrStatus.refs;

const jwtHash = ref(cldrStatus.getCurrentPieces()[3]); // get the JWT hash which was stored from cldrLoad

const loginMessage = ref("");
const jwtGood = ref(false);

const loginName = ref(null);
const loginEmail = ref(null);
const loginId = ref(0);

onMounted(async () => {
if (jwtHash.value) {
loginMessage.value = "Logging in..";
await doLogin();
} else {
loginMessage.value =
"No login hash found. Please make sure to click on the login link in email.";
}
});

async function doLogin() {
jwtGood.value = false;
loginMessage.value = `Logging in to ${jwtHash.value}`;
const { sessionId, user, name, email, id } = await cldrLoad.loginWithJwt(
jwtHash.value
);
// OK, we are now logged in.
if (!user || !sessionId) {
loginMessage.value = "Could not log in with that link.";
return;
}
jwtGood.value = true; // Give the users their next options.
loginName.value = name;
loginEmail.value = email;
loginId.value = id;
loginMessage.value = "Logged in!";
// do NOT set any properties here, or the page will reload.
}

function stay() {
// just reload at the top level
window.location.replace("v#");
window.location.reload();
}
</script>
2 changes: 1 addition & 1 deletion tools/cldr-apps/js/src/views/CldrUser.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export default {
},
async created() {
this.info = await cldrUsers.getUserInfo(this.uid);
const { org, name } = this.info;
const { org, name } = this.info ?? {};
this.org = org || null;
this.name = name || null;
this.$emit("org", this.org);
Expand Down
38 changes: 28 additions & 10 deletions tools/cldr-apps/js/src/views/LoginButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,20 @@
<a-checkbox v-model:checked="remember">Stay Logged In</a-checkbox>
&nbsp;
<a-alert
v-if="loginErrorMessage"
v-if="incorrectPassword"
type="error"
v-model:message="loginErrorMessage"
/>
message="Username or Password Incorrect"
description="Please check the email address and password carefully."
>
</a-alert>
<a-button
class="cldr-nav-btn"
v-on:click="resetpw()"
v-if="incorrectPassword"
size="small"
type="ghost"
>Forgot Password</a-button
>
</template>
<a-tooltip placement="bottomRight">
<template #title>
Expand All @@ -47,10 +57,10 @@ import { ref } from "vue";
export default {
setup() {
const loginShown = ref(false);
const loginErrorMessage = ref(null);
const incorrectPassword = ref(false);
return {
loginShown,
loginErrorMessage,
incorrectPassword,
};
},
created: function () {
Expand Down Expand Up @@ -84,6 +94,7 @@ export default {
* Log out
*/
async logout() {
this.incorrectPassword = false;
await fetch(`api/auth/logout?session=${cldrStatus.getSessionId()}`);
// now reload this page now that we've logged out
await window.location.reload();
Expand All @@ -92,6 +103,7 @@ export default {
* Log in (from header button)
*/
async login() {
this.incorrectPassword = false;
function errBox(message) {
console.error("LoginButton.vue: " + message);
notification.error({
Expand All @@ -100,7 +112,6 @@ export default {
});
}

this.loginErrorMessage = null;
if (this.userName && this.password) {
try {
const response = await fetch(
Expand All @@ -119,7 +130,7 @@ export default {
);
if (!response.ok) {
if (response.status == 403) {
return errBox("Unauthorized:\nCheck the username and password.");
this.incorrectPassword = true; // show the reset instructions
} else {
return errBox(`Login failed: HTTP ${response.status}`);
}
Expand Down Expand Up @@ -153,9 +164,7 @@ export default {
async loginout() {
if (this.loginShown) {
// cancel
this.loginShown = false;
this.logText = "Log In";
this.logTitle = null;
this.cancel();
} else if (!this.loggedIn) {
// Log In (show popup)
this.loginShown = true;
Expand All @@ -165,6 +174,15 @@ export default {
await this.logout();
}
},
cancel() {
this.loginShown = false;
this.logText = "Log In";
this.logTitle = null;
},
resetpw() {
this.cancel();
window.location.replace("v#reset");
},
},
data() {
return {
Expand Down
Loading
Loading