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 {
Button,
CircularProgress,
FormControl,
FormControlLabel,
Grid,
IconButton,
Input,
InputAdornment,
InputLabel,
MenuItem,
Modal,
Paper,
@@ -17,6 +22,10 @@ import { DataSourceState, MqttOptions } from '../../../../backend/src/DataSource
import { StyleRulesCallback, Theme, withStyles } from '@material-ui/core/styles'
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')
interface Props {
@@ -34,7 +43,7 @@ const protocols = [
interface State {
connecting: boolean
connectionId?: string
error?: Error
error?: string
visible: boolean
host: string
protocol: string
@@ -44,6 +53,7 @@ interface State {
clientId: string
username: string
password: string
showPassword: boolean
}
declare var window: any
@@ -71,6 +81,7 @@ class Connection extends React.Component<Props, State> {
password: '',
connecting: false,
connectionId: undefined,
showPassword: false,
}
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))
}
private handleClickShowPassword = () => {
this.setState({ showPassword: !this.state.showPassword })
}
private optionsFromState(): MqttOptions {
const protocol = this.state.protocol === 'tcp://' ? 'mqtt://' : this.state.protocol
const url = `${protocol}${this.state.host}:${this.state.port}`
@@ -87,7 +102,7 @@ class Connection extends React.Component<Props, State> {
return {
url,
username: this.state.username || undefined,
password: this.state.username || undefined,
password: this.state.password || undefined,
tls: this.state.tls,
certValidation: this.state.certValidation,
}
@@ -110,8 +125,8 @@ class Connection extends React.Component<Props, State> {
this.props.onConnection(connectionId)
this.setState({ visible: false })
} else if (state.error) {
console.log('error', state.error)
this.setState({ error: state.error })
this.disconnect()
}
})
}
@@ -150,6 +165,9 @@ class Connection extends React.Component<Props, State> {
button: {
margin: theme.spacing.unit,
},
passwordFormControl: {
marginTop: '16px',
},
}
}
@@ -163,16 +181,41 @@ class Connection extends React.Component<Props, State> {
public render() {
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}>
<Toolbar>
<Typography className={classes.title} variant="h6" color="inherit">MQTT Connection</Typography>
</Toolbar>
<form className={classes.container} noValidate autoComplete="off">
<Grid container spacing={24}>
<Grid item xs={2}>
<form className={classes.container} noValidate={true} autoComplete="off">
<Grid container={true} spacing={24}>
<Grid item={true} xs={2}>
<TextField
select
select={true}
label="Protocol"
className={classes.textField}
value={this.state.protocol}
@@ -195,7 +238,7 @@ class Connection extends React.Component<Props, State> {
margin="normal"
/>
</Grid>
<Grid item xs={3}>
<Grid item={true} xs={3}>
<TextField
label="Port"
className={classes.textField}
@@ -204,7 +247,7 @@ class Connection extends React.Component<Props, State> {
margin="normal"
/>
</Grid>
<Grid item xs={5}>
<Grid item={true} xs={5}>
<TextField
label="Username"
className={classes.textField}
@@ -213,17 +256,19 @@ class Connection extends React.Component<Props, State> {
margin="normal"
/>
</Grid>
<Grid item xs={5}>
<TextField
label="Password"
type="type"
className={classes.textField}
<Grid item={true} xs={5}>
<FormControl className={`${classes.textField} ${classes.passwordFormControl}`}>
<InputLabel htmlFor="adornment-password">Password</InputLabel>
<Input
id="adornment-password"
type={this.state.showPassword ? 'text' : 'password'}
value={this.state.password}
onChange={this.handleChange('password')}
margin="normal"
endAdornment={passwordVisibilityButton}
/>
</FormControl>
</Grid>
<Grid item xs={4}>
<Grid item={true} xs={4}>
<div className={classes.switch}>
<FormControlLabel
control={(
@@ -238,7 +283,7 @@ class Connection extends React.Component<Props, State> {
/>
</div>
</Grid>
<Grid item xs={4}>
<Grid item={true} xs={4}>
<div className={classes.switch}>
<FormControlLabel
control={(
@@ -253,18 +298,20 @@ class Connection extends React.Component<Props, State> {
/>
</div>
</Grid>
<Grid item xs={4}></Grid>
<Grid item={true} xs={4} />
</Grid>
<br />
<div style={{ textAlign: 'right' }}>
<Button variant="contained" color="secondary" className={classes.button} onClick={() => this.saveConnectionSettings()}>
Save
</Button>
{ this.renderConnectButton() }
{this.renderConnectButton()}
</div>
</form>
</Paper>
</Modal>
</div>
)
}
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 {
connecting: boolean
connected: boolean
error?: Error
error?: string
}
export class DataSourceStateMachine {
@@ -25,7 +25,7 @@ export class DataSourceStateMachine {
public setError(error: Error) {
this.state = {
error,
error: error.message,
connected: false,
connecting: false,
}

View File

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