Merge pull request #345 from lightninglabs/read-only-session

Add support for read-only macaroons
This commit is contained in:
Jamal James 2022-03-23 14:26:42 -04:00 committed by GitHub
commit c0f0b309cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 109 additions and 12 deletions

View file

@ -58,6 +58,7 @@ interface Props {
extra?: ReactNode;
placeholder?: string;
onChange?: (value: string) => void;
className?: string;
}
const FormSelect: React.FC<Props> = ({
@ -66,17 +67,17 @@ const FormSelect: React.FC<Props> = ({
value,
placeholder,
onChange,
className,
}) => {
const { Wrapper, Select } = Styled;
return (
<Wrapper>
<Wrapper className={className}>
<Select
value={value}
onChange={v => onChange && onChange(v as string)}
placeholder={placeholder}
aria-label={label}
options={options}
dropdownClassName="asdf"
/>
</Wrapper>
);

View file

@ -2,11 +2,13 @@ import React, { useCallback, useState } from 'react';
import { observer } from 'mobx-react-lite';
import styled from '@emotion/styled';
import { usePrefixedTranslation } from 'hooks';
import * as LIT from 'types/generated/lit-sessions_pb';
import { MAX_DATE } from 'util/constants';
import { useStore } from 'store';
import { Button, Column, HeaderFour, Row } from 'components/base';
import FormField from 'components/common/FormField';
import FormInput from 'components/common/FormInput';
import FormSelect from 'components/common/FormSelect';
import PurpleButton from './PurpleButton';
const Styled = {
@ -21,6 +23,17 @@ const Styled = {
padding: 12px 16px;
}
`,
FormSelect: styled(FormSelect)`
.rc-select {
font-family: ${props => props.theme.fonts.open.regular};
font-size: ${props => props.theme.sizes.m};
padding: 12px 40px 8px 0px;
}
.rc-select-selection-item {
padding-left: 14px;
}
`,
};
interface Props {
@ -32,18 +45,25 @@ const AddSession: React.FC<Props> = ({ primary }) => {
const { sessionStore } = useStore();
const [label, setLabel] = useState('');
const [permissions, setPermissions] = useState('admin');
const [editing, setEditing] = useState(false);
const toggleEditing = useCallback(() => setEditing(e => !e), []);
const handleSubmit = useCallback(async () => {
const session = await sessionStore.addSession(label, MAX_DATE);
const sessionType =
permissions === 'admin'
? LIT.SessionType.TYPE_MACAROON_ADMIN
: LIT.SessionType.TYPE_MACAROON_READONLY;
const session = await sessionStore.addSession(label, sessionType, MAX_DATE);
if (session) {
setLabel('');
setEditing(false);
}
}, [label]);
}, [label, permissions]);
const { Wrapper, FormHeader, FormInput } = Styled;
const { Wrapper, FormHeader, FormInput, FormSelect } = Styled;
if (!editing) {
return (
<PurpleButton tertiary={!primary} onClick={toggleEditing}>
@ -54,13 +74,32 @@ const AddSession: React.FC<Props> = ({ primary }) => {
return (
<Wrapper>
<FormHeader>{l('label')}</FormHeader>
<Row>
<Column>
<FormHeader>{l('label')}</FormHeader>
</Column>
<Column>
<FormHeader>{l('permissions')}</FormHeader>
</Column>
</Row>
<Row>
<Column cols={6}>
<FormField>
<FormInput value={label} onChange={setLabel} placeholder={l('labelHint')} />
</FormField>
</Column>
<Column>
<FormField>
<FormSelect
value={permissions}
onChange={setPermissions}
options={[
{ label: 'Admin', value: 'admin' },
{ label: 'Read Only', value: 'read-only' },
]}
/>
</FormField>
</Column>
<Column>
<PurpleButton onClick={handleSubmit}>{l('common.submit')}</PurpleButton>
<Button ghost borderless onClick={toggleEditing}>

View file

@ -1,5 +1,6 @@
import React from 'react';
import { observer } from 'mobx-react-lite';
import { Global, Theme } from '@emotion/react';
import styled from '@emotion/styled';
import { useStore } from 'store';
import { Background, Menu } from 'components/base';
@ -10,6 +11,47 @@ interface CollapsedProps {
fullWidth?: boolean;
}
const GlobalStyles = (theme: Theme) => `
.rc-select-dropdown {
padding-top: 10px;
background-color: transparent;
& > div {
color: ${theme.colors.offWhite};
background-color: ${theme.colors.lightBlue};
border-width: 0;
border-radius: 8px;
box-shadow: 0px 16px 16px rgba(0, 0, 0, 0.15);
overflow: hidden;
}
}
.rc-select-item {
color: ${theme.colors.white};
font-family: ${theme.fonts.open.regular};
font-weight: 600;
font-size: ${theme.sizes.s};
line-height: 24px;
padding: 16px;
border-bottom: 1px solid ${theme.colors.paleBlue};
&:last-of-type {
border-bottom: none;
}
&:hover {
color: ${theme.colors.white};
background-color: ${theme.colors.blue};
cursor: pointer;
}
& > .rc-select-item-option-state {
top: 16px;
right: 12px;
}
}
`;
const Styled = {
Container: styled.div<{ fullWidth: boolean }>`
position: relative;
@ -86,6 +128,7 @@ export const Layout: React.FC = ({ children }) => {
<Fluid className="container-fluid">{children}</Fluid>
</Content>
</Container>
<Global styles={GlobalStyles} />
</Background>
);
};

View file

@ -42,6 +42,8 @@ const theme: Theme = {
purple: '#57038d',
overlay: 'rgba(245,245,245,0.04)',
gradient: 'linear-gradient(325.53deg, #252F4A 0%, #46547B 100%);',
lightBlue: '#384770',
paleBlue: '#2E3A5C',
},
};

View file

@ -38,6 +38,8 @@ declare module '@emotion/react' {
purple: string;
overlay: string;
gradient: string;
lightBlue: string;
paleBlue: string;
};
}
}

View file

@ -33,6 +33,7 @@
"cmps.common.Wizard.backTip": "Back to Previous",
"cmps.connect.AddSession.create": "Create a new session",
"cmps.connect.AddSession.label": "Label",
"cmps.connect.AddSession.permissions": "Permissions",
"cmps.connect.AddSession.labelHint": "My First Session",
"cmps.connect.AddSession.expiration": "Expiration",
"cmps.connect.AddSession.expirationSuffix": "",

View file

@ -51,11 +51,11 @@ export default class Session {
get typeLabel() {
switch (this.type) {
case LIT.SessionType.TYPE_MACAROON_READONLY:
return 'Readonly Macaroon';
return 'Read-Only';
case LIT.SessionType.TYPE_MACAROON_ADMIN:
return 'Admin Macaroon';
return 'Admin';
case LIT.SessionType.TYPE_MACAROON_CUSTOM:
return 'Custom Macaroon';
return 'Custom';
case LIT.SessionType.TYPE_UI_PASSWORD:
return 'LiT UI Password';
}

View file

@ -83,7 +83,11 @@ export default class SessionStore {
s.label.startsWith('Default Session'),
).length;
const countText = count === 0 ? '' : `(${count})`;
await this.addSession(`Default Session ${countText}`, MAX_DATE);
await this.addSession(
`Default Session ${countText}`,
LIT.SessionType.TYPE_MACAROON_ADMIN,
MAX_DATE,
);
}
} catch (error: any) {
this._store.appView.handleError(error, 'Unable to fetch sessions');
@ -93,11 +97,16 @@ export default class SessionStore {
/**
* Adds a new session
* @param label the user defined label for this session
* @param type the type of session being created (admin, read-only, etc)
* @param expiry how long the session should be valid for
* @param mailboxServerAddr the address where the mailbox server is reachable
* @param devServer whether the mailbox server is a dev server that has no valid TLS cert
*/
async addSession(label: string, expiry: Date) {
async addSession(
label: string,
type: LIT.SessionTypeMap[keyof LIT.SessionTypeMap],
expiry: Date,
) {
try {
this._store.log.info(`submitting session with label ${label}`, {
expiry,
@ -107,7 +116,7 @@ export default class SessionStore {
const { session } = await this._store.api.lit.addSession(
label,
LIT.SessionType.TYPE_UI_PASSWORD,
type,
expiry,
this.proxyServer,
!IS_PROD,