Skip to content

Commit

Permalink
feat(app): basic password authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
mrfelton committed Sep 23, 2019
1 parent e55f3a3 commit bfb1d0e
Show file tree
Hide file tree
Showing 12 changed files with 207 additions and 55 deletions.
72 changes: 72 additions & 0 deletions renderer/components/Login/Login.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React, { useEffect, useRef } from 'react'
import PropTypes from 'prop-types'
import { FormattedMessage } from 'react-intl'
import { Box, Flex } from 'rebass/styled-components'
import { Button, CenteredContent, Message, Text } from 'components/UI'
import { Form, PasswordInput } from 'components/Form'
import ArrowRight from 'components/Icon/ArrowRight'
import ZapLogo from 'components/Icon/ZapLogo'
import messages from './messages'

const Login = ({ login, loginError, clearLoginError, ...rest }) => {
const formApiRef = useRef(null)

useEffect(() => {
const { current: formApi } = formApiRef
loginError && formApi.setFormError(loginError)
clearLoginError()
}, [loginError, formApiRef, clearLoginError])

const handleSubmit = ({ password }) => login(password)

return (
<Form getApi={api => (formApiRef.current = api)} onSubmit={handleSubmit} width={1}>
{({ formState: { submits, error } }) => {
const willValidateInline = submits > 0
return (
<CenteredContent {...rest} mx="auto" width={11 / 16}>
<Box px={0} py={4} width={1} {...rest}>
<Box mx="auto" width={5 / 8}>
<Flex justifyContent="center" mb={4}>
<ZapLogo height={34} width={34} />
</Flex>
<Text mb={4} textAlign="center">
<FormattedMessage {...messages.intro} />
</Text>
{error && (
<Flex alignItems="center" flexDirection="column">
<Message mb={3} variant="error">
{error}
</Message>
</Flex>
)}
<Flex alignItems="flex-start" width={1}>
<PasswordInput
field="password"
hasMessageSpacer
isRequired
validateOnBlur={willValidateInline}
validateOnChange={willValidateInline}
width={1}
willAutoFocus
/>
<Button ml={2} px={3} type="submit">
<ArrowRight />
</Button>
</Flex>
</Box>
</Box>
</CenteredContent>
)
}}
</Form>
)
}

Login.propTypes = {
clearLoginError: PropTypes.func.isRequired,
login: PropTypes.func.isRequired,
loginError: PropTypes.string,
}

export default Login
3 changes: 3 additions & 0 deletions renderer/components/Login/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Login from './Login'

export default Login
6 changes: 6 additions & 0 deletions renderer/components/Login/messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { defineMessages } from 'react-intl'

/* eslint-disable max-len */
export default defineMessages({
intro: 'Enter your password to continue.',
})
6 changes: 6 additions & 0 deletions renderer/components/Settings/SettingsForm.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'
import get from 'lodash/get'
import { injectIntl } from 'react-intl'
import { intlShape } from '@zap/i18n'
import { Form } from 'components/Form'
Expand All @@ -18,6 +19,11 @@ const SettingsForm = ({
}) => {
const handleSubmit = async values => {
try {
// If password feature has been disabled, reset the password to null.
if (!get(values, 'password.value')) {
values.password.value = null
}

// Save the updated settings.
await saveConfigOverrides(values)

Expand Down
35 changes: 28 additions & 7 deletions renderer/components/Settings/SettingsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,18 @@ import SettingsFieldsGeneral from './SettingsFieldsGeneral'
import SettingsFieldsSecurity from './SettingsFieldsSecurity'
import messages from './messages'

const SettingsMenu = ({ group, setGroup, ...rest }) => {
const items = [
const SettingsMenu = ({ group, setGroup, isLoggedIn, ...rest }) => {
// Items accessible to unauthenticated users.
const anonItems = [
{
id: 'general',
title: <FormattedMessage {...messages.fieldgroup_general} />,
onClick: () => setGroup('general'),
},
]

// Items only accessible to authenticated users.
const authItems = [
{
id: 'wallet',
title: <FormattedMessage {...messages.fieldgroup_wallet} />,
Expand All @@ -33,11 +38,17 @@ const SettingsMenu = ({ group, setGroup, ...rest }) => {
},
]

let items = [...anonItems]
if (isLoggedIn) {
items = [...items, ...authItems]
}

return <Menu items={items} p={2} selectedItem={group} {...rest} />
}

SettingsMenu.propTypes = {
group: PropTypes.string,
isLoggedIn: PropTypes.bool,
setGroup: PropTypes.func.isRequired,
}

Expand Down Expand Up @@ -91,9 +102,20 @@ SettingsActions.propTypes = {
currentConfig: PropTypes.object.isRequired,
}

const SettingsPage = ({ currentConfig, ...rest }) => {
const SettingsPage = ({ currentConfig, isLoggedIn, ...rest }) => {
const [group, setGroup] = useState('general')

const fieldgroups = {
general: SettingsFieldsGeneral,
wallet: SettingsFieldsWallet,
security: SettingsFieldsSecurity,
}

const FieldGroup = fieldgroups[group]
if (!FieldGroup) {
return null
}

return (
<Flex width={1} {...rest}>
<Sidebar.medium pt={40}>
Expand All @@ -102,7 +124,7 @@ const SettingsPage = ({ currentConfig, ...rest }) => {
<ZapLogo height={28} width={28} />
</Panel.Header>
<Panel.Body sx={{ overflowY: 'overlay' }}>
<SettingsMenu group={group} setGroup={setGroup} />
<SettingsMenu group={group} isLoggedIn={isLoggedIn} setGroup={setGroup} />
</Panel.Body>
</Panel>
</Sidebar.medium>
Expand All @@ -112,9 +134,7 @@ const SettingsPage = ({ currentConfig, ...rest }) => {
</Heading.h1>

<SettingsForm>
{group === 'general' && <SettingsFieldsGeneral currentConfig={currentConfig} />}
{group === 'wallet' && <SettingsFieldsWallet currentConfig={currentConfig} />}
{group === 'security' && <SettingsFieldsSecurity currentConfig={currentConfig} />}
<FieldGroup currentConfig={currentConfig} />
<SettingsActions currentConfig={currentConfig} />
</SettingsForm>
</MainContent>
Expand All @@ -124,6 +144,7 @@ const SettingsPage = ({ currentConfig, ...rest }) => {

SettingsPage.propTypes = {
currentConfig: PropTypes.object.isRequired,
isLoggedIn: PropTypes.bool,
}

export default SettingsPage
1 change: 1 addition & 0 deletions renderer/components/UI/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export BackgroundTertiary from './BackgroundTertiary'
export Bar from './Bar'
export Button from './Button'
export ButtonCreate from './ButtonCreate'
export CenteredContent from './CenteredContent'
export ClippedText from './ClippedText'
export CopyBox from './CopyBox'
export CopyButton from './CopyButton'
Expand Down
45 changes: 2 additions & 43 deletions renderer/containers/Initializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,7 @@ import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Redirect } from 'react-router'
import { initLocale } from 'reducers/locale'
import { initCurrency } from 'reducers/ticker'
import { initWallets, walletSelectors } from 'reducers/wallet'
import { initNeutrino } from 'reducers/neutrino'
import { startActiveWallet } from 'reducers/lnd'
import { initAutopay } from 'reducers/autopay'
import { initChannels } from 'reducers/channels'
import { walletSelectors } from 'reducers/wallet'

/**
* Root component that deals with mounting the app and managing top level routing.
Expand All @@ -18,36 +12,11 @@ class Initializer extends React.Component {
activeWallet: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
activeWalletSettings: PropTypes.object,
hasWallets: PropTypes.bool,
initAutopay: PropTypes.func.isRequired,
initChannels: PropTypes.func.isRequired,
initCurrency: PropTypes.func.isRequired,
initLocale: PropTypes.func.isRequired,
initNeutrino: PropTypes.func.isRequired,
initWallets: PropTypes.func.isRequired,
isWalletOpen: PropTypes.bool,
isWalletsLoaded: PropTypes.bool.isRequired,
lndConnect: PropTypes.string,
}

// Initialize app state.
componentDidMount() {
const {
initLocale,
initCurrency,
initWallets,
initChannels,
initAutopay,
initNeutrino,
} = this.props
initNeutrino()

initLocale()
initCurrency()
initAutopay()
initWallets()
initChannels()
}

/**
* getLocation - Returns current location based on app initialization state and referrer.
*
Expand Down Expand Up @@ -98,17 +67,7 @@ const mapStateToProps = state => ({
isWalletsLoaded: walletSelectors.isWalletsLoaded(state),
})

const mapDispatchToProps = {
startActiveWallet,
initNeutrino,
initCurrency,
initLocale,
initWallets,
initAutopay,
initChannels,
}

export default connect(
mapStateToProps,
mapDispatchToProps
null
)(Initializer)
34 changes: 34 additions & 0 deletions renderer/containers/Login/Login.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Redirect } from 'react-router'
import { login, clearLoginError, accountSelectors } from 'reducers/account'
import Login from 'components/Login'

const WrappedLogin = ({ isAccountPasswordEnabled, isLoggedIn, ...rest }) => {
if (isAccountPasswordEnabled && !isLoggedIn) {
return <Login {...rest} />
}
return <Redirect to="/init" />
}

WrappedLogin.propTypes = {
isAccountPasswordEnabled: PropTypes.bool,
isLoggedIn: PropTypes.bool,
}

const mapStateToProps = state => ({
isAccountPasswordEnabled: accountSelectors.isAccountPasswordEnabled(state),
isLoggedIn: accountSelectors.isLoggedIn(state),
loginError: accountSelectors.loginError(state),
})

const mapDispatchToProps = {
login,
clearLoginError,
}

export default connect(
mapStateToProps,
mapDispatchToProps
)(WrappedLogin)
3 changes: 3 additions & 0 deletions renderer/containers/Login/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Login from './Login'

export default Login
Loading

0 comments on commit bfb1d0e

Please sign in to comment.