summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jordan Johnson-Doyle <jordan@doyle.la> 2019-03-17 00:30:17 +0000
committerGravatar Jordan Johnson-Doyle <jordan@doyle.la> 2019-03-17 00:30:17 +0000
commitc28127d96a2a50b0bd6db863a7932c2dcb847490 (patch)
tree6cc383ae36a5a12e4e37fbe8a5e244b5a4f27b1a
parentc7f0e10239c3edcdf492e45be562fb22b4635fbc (diff)
Make browser action user approval a bit nicer looking
-rw-r--r--src/common/GetKeybaseUserForDomainEvent.ts11
-rw-r--r--src/common/KeybaseUser.ts12
-rw-r--r--src/content/KeyRing.ts40
-rw-r--r--src/content/ScriptInterceptor.ts2
-rw-r--r--src/popup/js/App.tsx12
-rw-r--r--src/popup/js/index.tsx9
-rw-r--r--src/popup/js/pages/Index.tsx85
7 files changed, 128 insertions, 43 deletions
diff --git a/src/common/GetKeybaseUserForDomainEvent.ts b/src/common/GetKeybaseUserForDomainEvent.ts
index 2aa79f5..238387b 100644
--- a/src/common/GetKeybaseUserForDomainEvent.ts
+++ b/src/common/GetKeybaseUserForDomainEvent.ts
@@ -1,4 +1,5 @@
import { IEvent } from "./IEvent";
+import { KeybaseUser } from "./KeybaseUser";
export class GetKeybaseUserForDomainEvent extends IEvent {
public static readonly TYPE = "GET_KEYBASE_USER_FOR_DOMAIN";
@@ -8,13 +9,11 @@ export class GetKeybaseUserForDomainEvent extends IEvent {
}
}
-type KeybaseUser = string;
-
export class GetKeybaseUserForDomainResponse {
constructor(
- public readonly keybaseUsers: KeybaseUser[],
- public readonly trusted: KeybaseUser[],
- public readonly barred: KeybaseUser[],
- public readonly pending: KeybaseUser[],
+ public readonly keybaseUsers: {[name: string]: KeybaseUser},
+ public readonly trusted: string[],
+ public readonly barred: string[],
+ public readonly pending: string[],
) {}
}
diff --git a/src/common/KeybaseUser.ts b/src/common/KeybaseUser.ts
new file mode 100644
index 0000000..5920ea6
--- /dev/null
+++ b/src/common/KeybaseUser.ts
@@ -0,0 +1,12 @@
+export interface Proof {
+ type: string,
+ name: string,
+ state: number, // TODO: make this enum
+ url: string,
+}
+
+export interface KeybaseUser {
+ name: string,
+ avatar: string,
+ proofs: {[name: string]: Proof}
+} \ No newline at end of file
diff --git a/src/content/KeyRing.ts b/src/content/KeyRing.ts
index 9e46250..5cebc4d 100644
--- a/src/content/KeyRing.ts
+++ b/src/content/KeyRing.ts
@@ -2,6 +2,7 @@ import * as P from "bluebird";
import { Buffer, KeyFetcher, KeyManager } from "kbpgp";
import { PendingSignerError } from "./PendingSignerError";
import { getConfig, ConfigKey } from "../common/config";
+import { KeybaseUser, Proof } from "../common/KeybaseUser";
const importFromArmoredPgp = P.promisify<KeyManager, { armored: string }>(KeyManager.import_from_armored_pgp, {
context: KeyManager
@@ -12,7 +13,7 @@ export default class KeyRing extends KeyFetcher {
[kid: string]: {
keyManager: KeyManager;
key: any;
- keybaseUser: string;
+ keybaseUser: KeybaseUser;
};
} = {};
@@ -33,13 +34,23 @@ export default class KeyRing extends KeyFetcher {
return [
...new Set([
- ...Object.values(this.allKeyIdsForDomainFromKeybase).map(k => k.keybaseUser),
+ ...Object.values(this.allKeyIdsForDomainFromKeybase).map(k => k.keybaseUser.name),
...(await this.getTrustedUsers()),
...(await this.getBarredUsers())
])
];
}
+ public async getAllCurrentKeybaseOwners() {
+ await this.populateKeysForDomain();
+
+ return Object.values(this.allKeyIdsForDomainFromKeybase)
+ .reduce((acc: {[name: string]: KeybaseUser}, k) => {
+ acc[k.keybaseUser.name] = k.keybaseUser;
+ return acc;
+ }, {});
+ }
+
/**
* Get users that have been previously allowed to run scripts
* on the `domain` that was used to instantiate this KeyRing.
@@ -103,19 +114,19 @@ export default class KeyRing extends KeyFetcher {
const k = this.allKeyIdsForDomainFromKeybase[kid];
if (k && k.key && k.key.key) {
- if (this.barredUsers.includes(k.keybaseUser)) {
+ if (this.barredUsers.includes(k.keybaseUser.name)) {
return cb(
- new Error(`Keybase user ${k.keybaseUser} is barred from signing scripts from ${this.domain}`),
+ new Error(`Keybase user ${k.keybaseUser.name} is barred from signing scripts from ${this.domain}`),
k.keyManager,
i
);
- } else if (this.pendingApproval.includes(k.keybaseUser)) {
+ } else if (this.pendingApproval.includes(k.keybaseUser.name)) {
return cb(
- new PendingSignerError(`Keybase user ${k.keybaseUser} is not yet approved for script signing on ${this.domain}`),
+ new PendingSignerError(`Keybase user ${k.keybaseUser.name} is not yet approved for script signing on ${this.domain}`),
k.keyManager,
i
);
- } else if (this.trustedUsers.includes(k.keybaseUser) && k.key.key.can_perform(ops)) {
+ } else if (this.trustedUsers.includes(k.keybaseUser.name) && k.key.key.can_perform(ops)) {
console.debug(`Allowing script from ${this.domain} to run as it was signed by ${k.keybaseUser}`);
return k.keyManager.fetch(ids, ops, cb);
}
@@ -167,7 +178,20 @@ export default class KeyRing extends KeyFetcher {
this.allKeyIdsForDomainFromKeybase[kid] = {
key,
keyManager: km,
- keybaseUser: username
+ keybaseUser: {
+ name: username,
+ avatar: (user.pictures.primary || Object.entries(user.basics.pictures)[0] || {})['url'],
+ proofs: Object.entries(user.proofs_summary.by_presentation_group)
+ .reduce((acc: {[name: string]: KeybaseUser}, [k, v]: [string, any]) => {
+ acc[k] = {
+ type: v.proof_type,
+ name: v.nametag,
+ state: v.state,
+ url: v.human_url
+ } as any;
+ return acc;
+ }, {})
+ }
};
}
}
diff --git a/src/content/ScriptInterceptor.ts b/src/content/ScriptInterceptor.ts
index 941ffc6..29b792d 100644
--- a/src/content/ScriptInterceptor.ts
+++ b/src/content/ScriptInterceptor.ts
@@ -25,7 +25,7 @@ export default new class ScriptInterceptor implements EventListenerObject {
const keyRing = this.getKeyRingForDomain(event.domain);
return new GetKeybaseUserForDomainResponse(
- await keyRing.getKeybaseUsers(),
+ await keyRing.getAllCurrentKeybaseOwners(),
await keyRing.getTrustedUsers(),
await keyRing.getBarredUsers(),
await keyRing.getPendingApproval()
diff --git a/src/popup/js/App.tsx b/src/popup/js/App.tsx
index 26fdafc..1aeeb65 100644
--- a/src/popup/js/App.tsx
+++ b/src/popup/js/App.tsx
@@ -5,12 +5,22 @@ import { Switch, Route } from "react-router-dom";
import { Index } from "./pages/Index";
import { Users } from "./pages/Users";
+import AppBar from '@material-ui/core/AppBar';
+import Toolbar from '@material-ui/core/Toolbar';
+import Typography from '@material-ui/core/Typography';
+
export class App extends React.Component<any, any> {
render() {
return (
<HashRouter>
<div>
- <h1 className="header">KPJS</h1>
+ <AppBar position="static">
+ <Toolbar variant="dense">
+ <Typography variant="h6" color="inherit">
+ KPJS
+ </Typography>
+ </Toolbar>
+ </AppBar>
<Switch>
<Route exact path="/" component={Index} />
diff --git a/src/popup/js/index.tsx b/src/popup/js/index.tsx
index 6414ead..1cd4530 100644
--- a/src/popup/js/index.tsx
+++ b/src/popup/js/index.tsx
@@ -3,11 +3,18 @@ import * as React from "react";
import { App } from "./App";
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles';
import CssBaseline from "@material-ui/core/CssBaseline";
+import blue from '@material-ui/core/colors/blue';
+import red from '@material-ui/core/colors/red';
import "typeface-open-sans-condensed";
import "../scss/app.sass";
-const theme = createMuiTheme();
+const theme = createMuiTheme({
+ palette: {
+ primary: blue,
+ secondary: red
+ },
+});
ReactDOM.render(
<MuiThemeProvider theme={theme}>
diff --git a/src/popup/js/pages/Index.tsx b/src/popup/js/pages/Index.tsx
index b970bc7..4c1d3c8 100644
--- a/src/popup/js/pages/Index.tsx
+++ b/src/popup/js/pages/Index.tsx
@@ -6,24 +6,38 @@ import { AllowUserEvent, DeniedUserEvent } from '../../../common/GetUsersAwaitin
import Avatar from '@material-ui/core/Avatar';
import Chip from '@material-ui/core/Chip';
import FaceIcon from '@material-ui/icons/Face';
+import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles';
+import green from '@material-ui/core/colors/green';
+import red from '@material-ui/core/colors/red';
+import Typography from '@material-ui/core/Typography';
+import DoneIcon from '@material-ui/icons/Done';
+import { KeybaseUser } from '../../../common/KeybaseUser';
+
interface ToolbarProps {
}
interface ToolbarState {
domain: string,
- keybaseUsers: string[],
+ keybaseUsers: {[name: string]: KeybaseUser},
trustedUsers: string[],
barredUsers: string[],
needsApproval: string[]
}
+const pillTheme = createMuiTheme({
+ palette: {
+ primary: green,
+ secondary: red
+ },
+});
+
export class Index extends React.Component<ToolbarProps, ToolbarState> {
constructor(props: any) {
super(props);
this.state = {
domain: "",
- keybaseUsers: [],
+ keybaseUsers: {},
trustedUsers: [],
barredUsers: [],
needsApproval: []
@@ -69,38 +83,57 @@ export class Index extends React.Component<ToolbarProps, ToolbarState> {
render() {
return <div className="container">
- <Row header="Trusted Keybase Users">
- { this.state.trustedUsers.map((u) => (
+ <MuiThemeProvider theme={pillTheme}>
+ <div>
+ <Typography variant="h6" color="inherit">Trusted Users</Typography>
+ { this.state.trustedUsers.map((u) => (
+ <Chip
+ avatar={
+ <Avatar src={this.state.keybaseUsers[u].avatar}>
+ {!this.state.keybaseUsers[u].avatar ? <FaceIcon /> : ''}
+ </Avatar>
+ }
+ color="primary"
+ label={u}
+ onClick={() => alert('clicked')}
+ onDelete={() => this.deny(u)} />
+ )) }
+ </div>
+
+ <div>
+ <Typography variant="h6" color="inherit">Barred Users</Typography>
+ { this.state.barredUsers.map((u) => (
+ <Chip
+ avatar={
+ <Avatar src={this.state.keybaseUsers[u].avatar}>
+ {!this.state.keybaseUsers[u].avatar ? <FaceIcon /> : ''}
+ </Avatar>
+ }
+ color="secondary"
+ label={u}
+ onClick={() => alert('clicked')}
+ onDelete={() => this.approve(u)}
+ deleteIcon={<DoneIcon />} />
+ )) }
+ </div>
+ </MuiThemeProvider>
+
+ <div>
+ <Typography variant="h6" color="inherit">Needs Approval</Typography>
+ { this.state.needsApproval.map((u) => (
<Chip
avatar={
- <Avatar>
- <FaceIcon />
+ <Avatar src={this.state.keybaseUsers[u].avatar}>
+ {!this.state.keybaseUsers[u].avatar ? <FaceIcon /> : ''}
</Avatar>
}
color="primary"
label={u}
onClick={() => alert('clicked')}
- onDelete={() => this.deny(u)} />
+ onDelete={() => this.approve(u)}
+ deleteIcon={<DoneIcon />} />
)) }
- </Row>
- <Row header="Barred Keybase Users">
- <ul>
- { this.state.barredUsers.map((u) => <li>
- { u }&nbsp;
- <a href="#" onClick={ e => this.approve(u) } style={{ color: '#4CAF50' }}>approve</a>
- </li>) }
- </ul>
- </Row>
- <Row header="Needs Approval">
- <ul>
- { this.state.needsApproval.map((u) => <li>
- { u }&nbsp;
- <a href="#" onClick={ e => this.approve(u) } style={{ color: '#4CAF50' }}>approve</a>&nbsp;
- <a href="#" onClick={ e => this.deny(u) } style={{ color: '#F44336' }}>deny</a>
- </li>) }
- </ul>
- </Row>
-
+ </div>
</div>;
}
}