Fix authentication
This commit is contained in:
@@ -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() {
|
||||||
|
|||||||
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 {
|
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,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user