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 9, 2019
1 parent ddc70b4 commit 2ad04e8
Show file tree
Hide file tree
Showing 12 changed files with 195 additions and 52 deletions.
71 changes: 71 additions & 0 deletions renderer/components/Login/Login.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
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" width="auto">
{error}
</Message>
</Flex>
)}
<Flex alignItems="flex-start" width={1}>
<PasswordInput
field="password"
hasMessageSpacer
isRequired
validateOnBlur={willValidateInline}
validateOnChange={willValidateInline}
width={1}
/>
<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, rest the password to null.
if (!get(values, 'password.value')) {
values.password.value = null
}

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

Expand Down
20 changes: 16 additions & 4 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 accessle 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,7 +102,7 @@ SettingsActions.propTypes = {
currentConfig: PropTypes.object.isRequired,
}

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

return (
Expand All @@ -102,7 +113,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 @@ -124,6 +135,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)
35 changes: 35 additions & 0 deletions renderer/containers/Login/Login.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Redirect } from 'react-router'
import { settingsSelectors } from 'reducers/settings'
import { login, clearLoginError, accountSelectors } from 'reducers/account'
import Login from 'components/Login'

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

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

const mapStateToProps = state => ({
isPasswordEnabled: settingsSelectors.currentConfig(state).password.active,
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
54 changes: 49 additions & 5 deletions renderer/containers/Root.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,20 @@ import { initDatabase, setLoading, setMounted, appSelectors } from 'reducers/app
import { initSettings } from 'reducers/settings'
import { initTheme, themeSelectors } from 'reducers/theme'
import { initAccount } from 'reducers/account'
import { walletSelectors } from 'reducers/wallet'
import { initLocale } from 'reducers/locale'
import { initCurrency } from 'reducers/ticker'
import { initWallets, walletSelectors } from 'reducers/wallet'
import { initNeutrino } from 'reducers/neutrino'
import { initAutopay } from 'reducers/autopay'
import { initChannels } from 'reducers/channels'
import { isLoading, isLoadingPerPath, getLoadingMessage } from 'reducers/utils'
import { Page, Titlebar, GlobalStyle } from 'components/UI'
import GlobalNotification from 'components/GlobalNotification'
import { withLoading } from 'hocs'
import { DialogLndCrashed } from './Dialog'
import Initializer from './Initializer'
import Logout from './Logout'
import Login from './Login'
import Home from './Home'
import ModalStack from './RootModalStack'
import Onboarding from './Onboarding/Onboarding'
Expand All @@ -34,6 +40,12 @@ const Root = ({
initSettings,
initTheme,
initAccount,
initNeutrino,
initLocale,
initCurrency,
initAutopay,
initWallets,
initChannels,
isMounted,
setMounted,
hasWallets,
Expand All @@ -55,12 +67,31 @@ const Root = ({
setMounted(true)
await initDatabase()
await initSettings()
await initTheme()
await initAccount()
initTheme()
initAccount()
initNeutrino()
initLocale()
initCurrency()
initAutopay()
initWallets()
initChannels()
}
}
init()
}, [initDatabase, initSettings, initTheme, initAccount, isMounted, setMounted])
}, [
initDatabase,
initSettings,
initTheme,
initAccount,
isMounted,
setMounted,
initNeutrino,
initLocale,
initCurrency,
initAutopay,
initWallets,
initChannels,
])

const redirectToHome = () => history.push('/home')
const redirectToLogout = () => history.push('/logout')
Expand All @@ -85,7 +116,8 @@ const Root = ({
>
{isRootReady && (
<Switch>
<Route component={Initializer} exact path="/" />
<Route component={Login} exact path="/" />
<Route component={Initializer} exact path="/init" />
<Route component={WalletStarter} exact path="/wallet-starter" />
<Route component={Home} path="/home" />
<Route
Expand Down Expand Up @@ -122,9 +154,15 @@ Root.propTypes = {
hasWallets: PropTypes.bool,
history: PropTypes.object.isRequired,
initAccount: PropTypes.func.isRequired,
initAutopay: PropTypes.func.isRequired,
initChannels: PropTypes.func.isRequired,
initCurrency: PropTypes.func.isRequired,
initDatabase: PropTypes.func.isRequired,
initLocale: PropTypes.func.isRequired,
initNeutrino: PropTypes.func.isRequired,
initSettings: PropTypes.func.isRequired,
initTheme: PropTypes.func.isRequired,
initWallets: PropTypes.func.isRequired,
isAppReady: PropTypes.bool.isRequired,
isLoading: PropTypes.bool.isRequired,
isMounted: PropTypes.bool.isRequired,
Expand Down Expand Up @@ -153,6 +191,12 @@ const mapDispatchToProps = {
initSettings,
initTheme,
initAccount,
initNeutrino,
initCurrency,
initLocale,
initWallets,
initAutopay,
initChannels,
setLoading,
setMounted,
}
Expand Down
Loading

0 comments on commit 2ad04e8

Please sign in to comment.