Fix authentication
This commit is contained in:
@@ -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() {
|
||||
|
||||
55
app/src/components/ConnectionSetup/Notification.tsx
Normal file
55
app/src/components/ConnectionSetup/Notification.tsx
Normal 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)
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user