Fix authentication

This commit is contained in:
Thomas Nordquist
2019-01-15 22:00:35 +01:00
parent f905ba8e89
commit 61b64a5ac2
4 changed files with 210 additions and 105 deletions

View File

@@ -3,8 +3,13 @@ import * as React from 'react'
import { import {
Button, Button,
CircularProgress, CircularProgress,
FormControl,
FormControlLabel, FormControlLabel,
Grid, Grid,
IconButton,
Input,
InputAdornment,
InputLabel,
MenuItem, MenuItem,
Modal, Modal,
Paper, Paper,
@@ -17,6 +22,10 @@ import { DataSourceState, MqttOptions } from '../../../../backend/src/DataSource
import { StyleRulesCallback, Theme, withStyles } from '@material-ui/core/styles' import { StyleRulesCallback, Theme, withStyles } from '@material-ui/core/styles'
import { addMqttConnectionEvent, makeConnectionStateEvent, removeConnection, rendererEvents } from '../../../../events' import { addMqttConnectionEvent, makeConnectionStateEvent, removeConnection, rendererEvents } from '../../../../events'
import Notification from './Notification'
import Visibility from '@material-ui/icons/Visibility'
import VisibilityOff from '@material-ui/icons/VisibilityOff'
import sha1 = require('sha1') import sha1 = require('sha1')
interface Props { interface Props {
@@ -34,7 +43,7 @@ const protocols = [
interface State { interface State {
connecting: boolean connecting: boolean
connectionId?: string connectionId?: string
error?: Error error?: string
visible: boolean visible: boolean
host: string host: string
protocol: string protocol: string
@@ -44,6 +53,7 @@ interface State {
clientId: string clientId: string
username: string username: string
password: string password: string
showPassword: boolean
} }
declare var window: any declare var window: any
@@ -71,6 +81,7 @@ class Connection extends React.Component<Props, State> {
password: '', password: '',
connecting: false, connecting: false,
connectionId: undefined, connectionId: undefined,
showPassword: false,
} }
this.state = Object.assign({}, defaultState, storedSettings) this.state = Object.assign({}, defaultState, storedSettings)
@@ -80,6 +91,10 @@ class Connection extends React.Component<Props, State> {
window.localStorage.setItem('connectionSettings', JSON.stringify(this.state)) window.localStorage.setItem('connectionSettings', JSON.stringify(this.state))
} }
private handleClickShowPassword = () => {
this.setState({ showPassword: !this.state.showPassword })
}
private optionsFromState(): MqttOptions { private optionsFromState(): MqttOptions {
const protocol = this.state.protocol === 'tcp://' ? 'mqtt://' : this.state.protocol const protocol = this.state.protocol === 'tcp://' ? 'mqtt://' : this.state.protocol
const url = `${protocol}${this.state.host}:${this.state.port}` const url = `${protocol}${this.state.host}:${this.state.port}`
@@ -87,7 +102,7 @@ class Connection extends React.Component<Props, State> {
return { return {
url, url,
username: this.state.username || undefined, username: this.state.username || undefined,
password: this.state.username || undefined, password: this.state.password || undefined,
tls: this.state.tls, tls: this.state.tls,
certValidation: this.state.certValidation, certValidation: this.state.certValidation,
} }
@@ -110,8 +125,8 @@ class Connection extends React.Component<Props, State> {
this.props.onConnection(connectionId) this.props.onConnection(connectionId)
this.setState({ visible: false }) this.setState({ visible: false })
} else if (state.error) { } else if (state.error) {
console.log('error', state.error)
this.setState({ error: state.error }) this.setState({ error: state.error })
this.disconnect()
} }
}) })
} }
@@ -150,6 +165,9 @@ class Connection extends React.Component<Props, State> {
button: { button: {
margin: theme.spacing.unit, margin: theme.spacing.unit,
}, },
passwordFormControl: {
marginTop: '16px',
},
} }
} }
@@ -163,16 +181,41 @@ class Connection extends React.Component<Props, State> {
public render() { public render() {
const { classes } = this.props const { classes } = this.props
return <Modal open={this.state.visible} disableAutoFocus={true} onClose={() => { console.log('close') }}> const passwordVisibilityButton = (
<InputAdornment position="end">
<IconButton
aria-label="Toggle password visibility"
onClick={this.handleClickShowPassword}
>
{this.state.showPassword ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>
)
let renderError = null
if (this.state.error) {
renderError = (
<Notification
message={this.state.error}
type="error"
onClose={() => { this.setState({ error: undefined }) }}
/>
)
}
return (
<div>
{renderError}
<Modal open={this.state.visible} disableAutoFocus={true}>
<Paper className={classes.root}> <Paper className={classes.root}>
<Toolbar> <Toolbar>
<Typography className={classes.title} variant="h6" color="inherit">MQTT Connection</Typography> <Typography className={classes.title} variant="h6" color="inherit">MQTT Connection</Typography>
</Toolbar> </Toolbar>
<form className={classes.container} noValidate autoComplete="off"> <form className={classes.container} noValidate={true} autoComplete="off">
<Grid container spacing={24}> <Grid container={true} spacing={24}>
<Grid item xs={2}> <Grid item={true} xs={2}>
<TextField <TextField
select select={true}
label="Protocol" label="Protocol"
className={classes.textField} className={classes.textField}
value={this.state.protocol} value={this.state.protocol}
@@ -195,7 +238,7 @@ class Connection extends React.Component<Props, State> {
margin="normal" margin="normal"
/> />
</Grid> </Grid>
<Grid item xs={3}> <Grid item={true} xs={3}>
<TextField <TextField
label="Port" label="Port"
className={classes.textField} className={classes.textField}
@@ -204,7 +247,7 @@ class Connection extends React.Component<Props, State> {
margin="normal" margin="normal"
/> />
</Grid> </Grid>
<Grid item xs={5}> <Grid item={true} xs={5}>
<TextField <TextField
label="Username" label="Username"
className={classes.textField} className={classes.textField}
@@ -213,17 +256,19 @@ class Connection extends React.Component<Props, State> {
margin="normal" margin="normal"
/> />
</Grid> </Grid>
<Grid item xs={5}> <Grid item={true} xs={5}>
<TextField <FormControl className={`${classes.textField} ${classes.passwordFormControl}`}>
label="Password" <InputLabel htmlFor="adornment-password">Password</InputLabel>
type="type" <Input
className={classes.textField} id="adornment-password"
type={this.state.showPassword ? 'text' : 'password'}
value={this.state.password} value={this.state.password}
onChange={this.handleChange('password')} onChange={this.handleChange('password')}
margin="normal" endAdornment={passwordVisibilityButton}
/> />
</FormControl>
</Grid> </Grid>
<Grid item xs={4}> <Grid item={true} xs={4}>
<div className={classes.switch}> <div className={classes.switch}>
<FormControlLabel <FormControlLabel
control={( control={(
@@ -238,7 +283,7 @@ class Connection extends React.Component<Props, State> {
/> />
</div> </div>
</Grid> </Grid>
<Grid item xs={4}> <Grid item={true} xs={4}>
<div className={classes.switch}> <div className={classes.switch}>
<FormControlLabel <FormControlLabel
control={( control={(
@@ -253,7 +298,7 @@ class Connection extends React.Component<Props, State> {
/> />
</div> </div>
</Grid> </Grid>
<Grid item xs={4}></Grid> <Grid item={true} xs={4} />
</Grid> </Grid>
<br /> <br />
<div style={{ textAlign: 'right' }}> <div style={{ textAlign: 'right' }}>
@@ -265,6 +310,8 @@ class Connection extends React.Component<Props, State> {
</form> </form>
</Paper> </Paper>
</Modal> </Modal>
</div>
)
} }
private renderConnectButton() { private renderConnectButton() {

View File

@@ -0,0 +1,55 @@
import * as React from 'react'
import { Snackbar, SnackbarContent } from '@material-ui/core'
import { Theme, withStyles } from '@material-ui/core/styles'
import { green, red } from '@material-ui/core/colors'
enum MessageType {
success = 'success',
error = 'error',
}
interface Props {
message?: string
type: MessageType
onClose: () => void
classes: any
}
class Notification extends React.Component<Props, {}> {
constructor(props: Props) {
super(props)
}
public static styles = (theme: Theme) => ({
success: {
backgroundColor: green[600],
color: theme.typography.button.color,
},
error: {
backgroundColor: red[600],
color: theme.typography.button.color,
},
})
public render() {
return (
<Snackbar
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
open={Boolean(this.props.message)}
autoHideDuration={10000}
onClose={this.props.onClose}
>
<SnackbarContent
className={this.props.classes[this.props.type]}
message={this.props.message}
/>
</Snackbar>
)
}
}
export default withStyles(Notification.styles)(Notification)

View File

@@ -3,7 +3,7 @@ import { EventDispatcher } from '../../../events'
export interface DataSourceState { export interface DataSourceState {
connecting: boolean connecting: boolean
connected: boolean connected: boolean
error?: Error error?: string
} }
export class DataSourceStateMachine { export class DataSourceStateMachine {
@@ -25,7 +25,7 @@ export class DataSourceStateMachine {
public setError(error: Error) { public setError(error: Error) {
this.state = { this.state = {
error, error: error.message,
connected: false, connected: false,
connecting: false, connecting: false,
} }

View File

@@ -1,6 +1,7 @@
import * as Url from 'url'
import { Client, connect as mqttConnect } from 'mqtt' import { Client, connect as mqttConnect } from 'mqtt'
import { DataSource, DataSourceStateMachine } from './' import { DataSource, DataSourceStateMachine } from './'
import * as Url from 'url'
export interface MqttOptions { export interface MqttOptions {
url: string url: string
@@ -36,6 +37,8 @@ export class MqttSource implements DataSource<MqttOptions> {
const client = mqttConnect(url, { const client = mqttConnect(url, {
resubscribe: false, resubscribe: false,
rejectUnauthorized: !options.certValidation, rejectUnauthorized: !options.certValidation,
username: options.username,
password: options.password,
}) })
this.client = client this.client = client