a better, simpler user interface for token regeneration (#191)

This commit is contained in:
Michael Quigley 2024-02-16 16:32:51 -05:00
parent 9949a55c1f
commit 36067f5e91
No known key found for this signature in database
GPG Key ID: 9B60314A9DD20A62
4 changed files with 93 additions and 100 deletions

View File

@ -20,6 +20,11 @@ func (handler *resetTokenHandler) Handle(params account.ResetTokenParams, princi
} }
logrus.Infof("received token reset request for email '%v'", params.Body.EmailAddress) logrus.Infof("received token reset request for email '%v'", params.Body.EmailAddress)
if params.Body.EmailAddress != principal.Email {
logrus.Errorf("mismatched account '%v' for '%v'", params.Body.EmailAddress, principal.Email)
return account.NewResetTokenNotFound()
}
tx, err := str.Begin() tx, err := str.Begin()
if err != nil { if err != nil {
logrus.Errorf("error starting transaction for '%v': %v", params.Body.EmailAddress, err) logrus.Errorf("error starting transaction for '%v': %v", params.Body.EmailAddress, err)

View File

@ -1,12 +1,12 @@
import React, {useState} from "react"; import React, {useState} from "react";
import ChangePassword from "./actions/ChangePassword"; import ChangePassword from "./actions/ChangePassword";
import ResetToken from "./actions/ResetToken";
import {Button} from "react-bootstrap"; import {Button} from "react-bootstrap";
import RegenerateToken from "./actions/RegenerateToken";
const ActionsTab = (props) => { const ActionsTab = (props) => {
const [showResetTokenModal, setShowResetTokenModal] = useState(false); const [showRegenerateTokenModal, setShowRegenerateTokenModal] = useState(false);
const openResetTokenModal = () => setShowResetTokenModal(true); const openRegenerateTokenModal = () => setShowRegenerateTokenModal(true);
const closeResetTokenModal = () => setShowResetTokenModal(false); const closeRegenerateTokenModal = () => setShowRegenerateTokenModal(false);
const [showChangePasswordModal, setShowChangePasswordModal] = useState(false); const [showChangePasswordModal, setShowChangePasswordModal] = useState(false);
const openChangePasswordModal = () => setShowChangePasswordModal(true); const openChangePasswordModal = () => setShowChangePasswordModal(true);
@ -40,8 +40,8 @@ const ActionsTab = (props) => {
need to preserve, your best bet is to update the <code>zrok_token</code> in those environments as need to preserve, your best bet is to update the <code>zrok_token</code> in those environments as
described above. described above.
</p> </p>
<Button variant={"danger"} onClick={openResetTokenModal}>Regenerate Account Token</Button> <Button variant={"danger"} onClick={openRegenerateTokenModal}>Regenerate Account Token</Button>
<ResetToken show={showResetTokenModal} onHide={closeResetTokenModal} user={props.user}/> <RegenerateToken show={showRegenerateTokenModal} onHide={closeRegenerateTokenModal} user={props.user}/>
</div> </div>
</div> </div>
) )

View File

@ -0,0 +1,82 @@
import Modal from "react-bootstrap/Modal";
import {Button, Container, Form, Row} from "react-bootstrap";
import React, {useState} from "react";
import * as account from "../../../../api/account";
const RegenerateToken = (props) => {
const [confirmEmail, setConfirmEmail] = useState('');
const [message, setMessage] = useState('');
const hide = () => {
props.onHide();
setConfirmEmail('');
setMessage('');
};
const handleSubmit = (event) => {
event.preventDefault();
if(confirmEmail !== props.user.email) {
setMessage("Email address confirmation does not match!");
return;
}
account.resetToken({body: {emailAddress: props.user.email}})
.then(resp => {
console.log(resp);
let user = JSON.parse(localStorage.getItem('user'));
localStorage.setItem('user', JSON.stringify({
email: user.email,
token: resp.data.token
}));
document.dispatchEvent(new Event('storage'));
setMessage("Your new account token is: " + resp.data.token);
}).catch(err => {
setMessage("Account token regeneration failed!");
console.log("account token regeneration failed", err);
});
};
return (
<Modal show={props.show} onHide={hide} size={"md"} centered>
<Modal.Header closeButton>Are you very sure?</Modal.Header>
<Modal.Body>
<Form onSubmit={handleSubmit}>
<Container>
<p>
Did you read the warning on the previous screen? This action will reset all of your active
environments and shares!
</p>
<p>
You will need to update each of
your <code> &#36;&#123;HOME&#125;/.zrok/environments.yml</code> files
with your new token to allow them to continue working!
</p>
<p>
Hit <code> Escape </code> or click the 'X' to abort!
</p>
<Form.Group controlId={"confirmEmail"}>
<Form.Control
placeholder={"Confirm Your Email Address"}
onChange={t => {
setMessage('');
setConfirmEmail(t.target.value);
}}
value={confirmEmail}
style={{marginBottom: "1em"}}
/>
</Form.Group>
<Row style={{ justifyContent: "center", marginTop: "1em" }}>
<p style={{ color: "red" }}>{message}</p>
</Row>
<Row style={{ justifyContent: "right", marginTop: "1em" }}>
<Button variant={"danger"} type={"submit"}>Regenerate Account Token</Button>
</Row>
</Container>
</Form>
</Modal.Body>
</Modal>
);
};
export default RegenerateToken;

View File

@ -1,94 +0,0 @@
import React, {useRef, useState} from "react";
import Modal from "react-bootstrap/Modal";
import {mdiContentCopy} from "@mdi/js";
import Icon from "@mdi/react";
import { Button, Overlay, Tooltip } from "react-bootstrap";
import * as account from "../../../../api/account";
const ResetToken = (props) => {
const target = useRef(null);
const [showTooltip, setShowTooltip] = useState(false);
const handleCopy = async () => {
let copiedText = document.getElementById("zrok-token").innerHTML;
try {
await navigator.clipboard.writeText(copiedText);
setShowTooltip(true);
setTimeout(() => setShowTooltip(false), 1000);
} catch(err) {
console.error("failed to copy", err);
}
}
let resetToken = () => {
account.resetToken({ body: { "emailAddress": props.user.email } }).then(resp => {
console.log(resp)
let user = JSON.parse(localStorage.getItem('user'))
localStorage.setItem('user', JSON.stringify({
"email": user.email,
"token": resp.data.token
}));
document.dispatchEvent(new Event('storage'))
setModalBody((
<div>
<p>
You will need to update your environment files <code> &#36;&#123;HOME&#125;/.zrok/environment.json </code>
with the new <code> zrok_token </code>.
</p>
<p>
Your new <code> zrok_token </code> is: <code><span id={"zrok-token"}>{resp.data.token}</span></code>{' '}
<Icon ref={target} path={mdiContentCopy} size={0.7} onClick={handleCopy}/>
</p>
</div>
));
setModalHeader((
<span>Account Token Regenerated!</span>
))
}).catch(err => {
console.log("err", err);
});
}
let hide = () => {
setModalHeader(defaultHeader)
setModalBody(defaultModal)
props.onHide()
}
let defaultHeader = (<span>Are you sure?</span>)
let defaultModal = (
<div>
<p>Did you read the warning on the previous screen? This action will reset all of your active environments and shares!</p>
<p>You will need to update each of your <code> &#36;&#123;HOME&#125;/.zrok/environments.yml</code> files with your new token!</p>
<p align={"right"}>
<Button variant={"danger"} onClick={resetToken}>Regenerate Token</Button>
</p>
</div>
);
const [modalBody, setModalBody] = useState(defaultModal);
const [modalHeader, setModalHeader] = useState(defaultHeader);
return (
<div>
<Modal show={props.show} onHide={hide} size={"lg"} centered>
<Modal.Header closeButton>{modalHeader}</Modal.Header>
<Modal.Body>
{modalBody}
</Modal.Body>
</Modal>
<Overlay target={target.current} show={showTooltip} placement={"bottom"}>
{(props) => (
<Tooltip id={"copy-tooltip"} {...props}>
Copied!
</Tooltip>
)}
</Overlay>
</div>
)
}
export default ResetToken;