Add rpc method to get private key (#664)

* Add rpc method to get private key

* Add export private key to frontend
This commit is contained in:
Jonathan Zernik 2021-01-20 11:41:02 -08:00 committed by GitHub
parent c624ca2460
commit 4b0f3bc637
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 284 additions and 0 deletions

View file

@ -0,0 +1,124 @@
import React, {useState, useEffect} from 'react';
import {
Paper,
IconButton,
Menu,
MenuItem,
Typography,
Grid,
Box,
Link,
Dialog,
DialogTitle,
DialogContent,
DialogContentText,
TextField,
DialogActions,
Button,
FormControl,
InputLabel,
Select,
} from "@material-ui/core";
import { MoreVert as MoreIcon } from "@material-ui/icons";
import {useHistory} from "react-router-dom";
import classnames from "classnames";
// styles
import useStyles from "./styles";
import Widget from "../../components/Widget";
import SqueakThreadItem from "../../components/SqueakThreadItem";
import {
getSqueakProfilePrivateKey,
} from "../../squeakclient/requests"
export default function ExportPrivateKeyDialog({
open,
handleClose,
profile,
...props
}) {
var classes = useStyles();
const history = useHistory();
var [privateKey, setPrivateKey] = useState('');
const resetFields = () => {
setPrivateKey('');
};
const handleChangePrivateKey = (event) => {
setPrivateKey(event.target.value);
};
const getPrivateKey = () => {
var profileId = profile.getProfileId();
getSqueakProfilePrivateKey(profileId, (response) => {
setPrivateKey(response.getPrivateKey());
});
};
function handleSubmit(event) {
event.preventDefault();
getPrivateKey();
}
function DisplayPrivateKey() {
return (
<TextField
id="standard-textarea"
label="private-key"
required
autoFocus
value={privateKey}
fullWidth
inputProps={{
readOnly: true,
}}
/>
)
}
function CancelButton() {
return (
<Button
onClick={handleClose}
variant="contained"
color="secondary"
>
Cancel
</Button>
)
}
function ShowPrivateKeyButton() {
return (
<Button
type="submit"
variant="contained"
color="primary"
className={classes.button}
>
Show private key
</Button>
)
}
return (
<Dialog open={open} onEnter={resetFields} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Export Private Key</DialogTitle>
<form className={classes.root} onSubmit={handleSubmit} noValidate autoComplete="off">
<DialogContent>
{DisplayPrivateKey()}
</DialogContent>
<DialogActions>
{CancelButton()}
{ShowPrivateKeyButton()}
</DialogActions>
</form>
</Dialog>
)
}

View file

@ -0,0 +1,6 @@
{
"name": "ExportPrivateKeyDialog",
"version": "0.0.0",
"private": true,
"main": "ExportPrivateKeyDialog.js"
}

View file

@ -0,0 +1,43 @@
import { makeStyles } from "@material-ui/styles";
export default makeStyles(theme => ({
widgetWrapper: {
display: "flex",
minHeight: "100%",
},
widgetHeader: {
padding: theme.spacing(3),
paddingBottom: theme.spacing(1),
display: "flex",
justifyContent: "space-between",
alignItems: "center",
},
widgetRoot: {
boxShadow: theme.customShadows.widget,
},
widgetBody: {
paddingBottom: theme.spacing(3),
paddingRight: theme.spacing(3),
paddingLeft: theme.spacing(3),
},
noPadding: {
padding: 0,
},
paper: {
display: "flex",
flexDirection: "column",
flexGrow: 1,
overflow: "hidden",
},
moreButton: {
margin: -theme.spacing(1),
padding: 0,
width: 40,
height: 40,
color: theme.palette.text.hint,
"&:hover": {
backgroundColor: theme.palette.primary.main,
color: "rgba(255, 255, 255, 0.35)",
},
},
}));

View file

@ -19,6 +19,7 @@ import useStyles from "./styles";
import PageTitle from "../../components/PageTitle";
import Widget from "../../components/Widget";
import DeleteProfileDialog from "../../components/DeleteProfileDialog";
import ExportPrivateKeyDialog from "../../components/ExportPrivateKeyDialog";
import {
getSqueakProfileRequest,
@ -32,6 +33,7 @@ export default function ProfilePage() {
const { id } = useParams();
const [squeakProfile, setSqueakProfile] = useState(null);
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
const [exportPrivateKeyDialogOpen, setExportPrivateKeyDialogOpen] = useState(false);
const history = useHistory();
const getSqueakProfile = (id) => {
@ -61,10 +63,18 @@ export default function ProfilePage() {
setDeleteDialogOpen(true);
};
const handleClickOpenExportPrivateKeyDialog = () => {
setExportPrivateKeyDialogOpen(true);
};
const handleCloseDeleteDialog = () => {
setDeleteDialogOpen(false);
};
const handleCloseExportPrivateKeyDialog = () => {
setExportPrivateKeyDialogOpen(false);
};
const handleSettingsFollowingChange = (event) => {
console.log("Following changed for profile id: " + id);
console.log("Following changed to: " + event.target.checked);
@ -94,6 +104,9 @@ export default function ProfilePage() {
{ProfileSettingsForm()}
{ViewSqueaksButton()}
{DeleteProfileButton()}
{squeakProfile.getHasPrivateKey() &&
ExportPrivateKeyButton()
}
</>
)
}
@ -133,6 +146,23 @@ export default function ProfilePage() {
)
}
function ExportPrivateKeyButton() {
return (
<>
<Grid item xs={12}>
<div className={classes.root}>
<Button
variant="contained"
onClick={() => {
handleClickOpenExportPrivateKeyDialog();
}}>Export Private Key
</Button>
</div>
</Grid>
</>
)
}
function ViewSqueaksButton() {
return (
<>
@ -162,6 +192,18 @@ export default function ProfilePage() {
)
}
function ExportPrivateKeyDialogContent() {
return (
<>
<ExportPrivateKeyDialog
open={exportPrivateKeyDialogOpen}
handleClose={handleCloseExportPrivateKeyDialog}
profile={squeakProfile}
></ExportPrivateKeyDialog>
</>
)
}
return (
<>
<PageTitle title={'Squeak Profile: ' + (squeakProfile ? squeakProfile.getProfileName() : null)} />
@ -172,6 +214,7 @@ export default function ProfilePage() {
}
</div>
{DeleteProfileDialogContent()}
{ExportPrivateKeyDialogContent()}
</>
);
}

View file

@ -86,6 +86,8 @@ import {
GetReceivedPaymentsReply,
GetNetworkRequest,
GetNetworkReply,
GetSqueakProfilePrivateKeyRequest,
GetSqueakProfilePrivateKeyReply,
} from "../proto/squeak_admin_pb"
console.log('The value of REACT_APP_SERVER_PORT is:', process.env.REACT_APP_SERVER_PORT);
@ -1007,3 +1009,14 @@ export function getNetworkRequest(handleResponse) {
}
);
};
export function getSqueakProfilePrivateKey(id, handleResponse) {
var request = new GetSqueakProfilePrivateKeyRequest();
request.setProfileId(id);
makeRequest(
'getsqueakprofileprivatekey',
request,
GetSqueakProfilePrivateKeyReply.deserializeBinary,
handleResponse,
);
};

View file

@ -500,6 +500,18 @@ def test_delete_profile(server_stub, admin_stub, contact_profile_id):
assert "Profile not found." in str(excinfo.value)
def test_get_profile_private_key(server_stub, admin_stub, signing_profile_id):
# Get the private key
private_key_response = admin_stub.GetSqueakProfilePrivateKey(
squeak_admin_pb2.GetSqueakProfilePrivateKeyRequest(
profile_id=signing_profile_id,
)
)
print(private_key_response.private_key)
assert len(private_key_response.private_key) > 0
def test_get_following_squeaks(
server_stub, admin_stub, saved_squeak_hash, signing_profile_id
):

View file

@ -100,6 +100,10 @@ service SqueakAdmin {
*/
rpc SetSqueakProfileSharing (SetSqueakProfileSharingRequest) returns (SetSqueakProfileSharingReply) {}
/** sqkadmin: `getsqueakprofileprivatekey`
*/
rpc GetSqueakProfilePrivateKey (GetSqueakProfilePrivateKeyRequest) returns (GetSqueakProfilePrivateKeyReply) {}
/** sqkadmin: `deletesqueakprofile`
*/
rpc DeleteSqueakProfile (DeleteSqueakProfileRequest) returns (DeleteSqueakProfileReply) {}
@ -293,6 +297,16 @@ message SetSqueakProfileSharingRequest {
message SetSqueakProfileSharingReply {
}
message GetSqueakProfilePrivateKeyRequest {
/// The profile id
int32 profile_id = 1;
}
message GetSqueakProfilePrivateKeyReply {
/// The private key
string private_key = 1;
}
message DeleteSqueakProfileRequest {
/// The profile id
int32 profile_id = 1;

View file

@ -190,6 +190,16 @@ class SqueakAdminServerHandler(object):
self.squeak_controller.delete_squeak_profile(profile_id)
return squeak_admin_pb2.DeleteSqueakProfileReply()
def handle_get_squeak_profile_private_key(self, request):
profile_id = request.profile_id
logger.info(
"Handle get squeak profile private key for id: {}".format(profile_id))
private_key = self.squeak_controller.get_squeak_profile_private_key(
profile_id)
return squeak_admin_pb2.GetSqueakProfilePrivateKeyReply(
private_key=private_key
)
def handle_make_squeak(self, request):
profile_id = request.profile_id
content_str = request.content

View file

@ -91,6 +91,9 @@ class SqueakAdminServerServicer(squeak_admin_pb2_grpc.SqueakAdminServicer):
def DeleteSqueakProfile(self, request, context):
return self.handler.handle_delete_squeak_profile(request)
def GetSqueakProfilePrivateKey(self, request, context):
return self.handler.handle_get_squeak_profile_private_key(request)
def MakeSqueak(self, request, context):
return self.handler.handle_make_squeak(request)

View file

@ -433,6 +433,14 @@ def create_app(handler, username, password):
handler.handle_get_network,
)
@app.route("/getsqueakprofileprivatekey", methods=["POST"])
@login_required
def getsqueakprofileprivatekey():
return handle_request(
squeak_admin_pb2.GetSqueakProfilePrivateKeyRequest(),
handler.handle_get_squeak_profile_private_key,
)
return app

View file

@ -154,6 +154,14 @@ class SqueakController:
def delete_squeak_profile(self, profile_id: int):
self.squeak_db.delete_profile(profile_id)
def get_squeak_profile_private_key(self, profile_id: int):
profile = self.get_squeak_profile(profile_id)
if profile.private_key is None:
raise Exception("Profile with id: {} does not have a private key.".format(
profile_id
))
return profile.private_key
def make_squeak(self, profile_id: int, content_str: str, replyto_hash: bytes):
squeak_profile = self.squeak_db.get_profile(profile_id)
squeak_entry = self.squeak_core.make_squeak(