diff --git a/lib/serverroutes.js b/lib/serverroutes.js index 762e7d230..0f241387f 100644 --- a/lib/serverroutes.js +++ b/lib/serverroutes.js @@ -257,6 +257,84 @@ module.exports = function(app, saveSecurityConfig, getSecurityConfig) { ) }) + app.put('/security/access/requests/:identifier/:status', (req, res) => { + if (app.securityStrategy.allowConfigure(req)) { + var config = getSecurityConfig(app) + app.securityStrategy.setAccessRequestStatus( + config, + req.params.identifier, + req.params.status, + req.body, + (err, config) => { + if (err) { + console.log(err) + res.status(500) + res.send(`Unable update request: ${err.message}`) + return + } + if (config) { + saveSecurityConfig(app, config, err => { + if (err) { + console.log(err) + res.status(500) + res.send('Unable to save configuration change') + return + } + res.send('Request updated') + }) + } else { + res.send('Request updated') + } + } + ) + } else { + res.status(401).json('Security config not allowed') + } + }) + + app.get('/security/access/requests', (req, res) => { + if (app.securityStrategy.allowConfigure(req)) { + res.json(app.securityStrategy.getAccessRequestsResponse()) + } else { + res.status(401).json('Security config not allowed') + } + }) + + app.post('/access/requests', (req, res) => { + var config = getSecurityConfig(app) + let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress + app.securityStrategy.requestAccess( + config, + req.body, + ip, + (err, response, config) => { + if (err) { + console.log(err) + res.status(500) + res.send(`Unable to create request: ${err.message}`) + return + } + if (!response) { + res.send('Request created') + } else { + res.json(response) + } + } + ) + }) + + app.get('/access/requests/:id', (req, res) => { + app.securityStrategy.checkRequest(req.params.id, (err, result) => { + if (err) { + console.log(err) + res.status(500) + res.send(`Unable to check request: ${err.message}`) + return + } + res.json(result) + }) + }) + app.get('/settings', (req, res, next) => { var settings = { interfaces: {}, diff --git a/lib/tokensecurity.js b/lib/tokensecurity.js index 169e6556c..9b755891a 100644 --- a/lib/tokensecurity.js +++ b/lib/tokensecurity.js @@ -22,6 +22,7 @@ const fs = require('fs') const path = require('path') const bcrypt = require('bcryptjs') const getSourceId = require('@signalk/signalk-schema').getSourceId +const uuidv4 = require('uuid/v4') const CONFIG_PLUGINID = 'sk-simple-token-security-config' const passwordSaltRounds = 10 @@ -30,6 +31,7 @@ const permissionDeniedMessage = module.exports = function (app, config) { const strategy = {} + let accessRequests = [] let { allow_readonly = true, @@ -39,8 +41,11 @@ module.exports = function (app, config) { .randomBytes(256) .toString('hex'), users = [], + devices = [], immutableConfig = false, - acls = [] + acls = [], + allowDeviceAccessRequests = true, + allowNewUserRegistration = true } = config if (process.env.ADMINUSER) { @@ -64,13 +69,25 @@ module.exports = function (app, config) { immutableConfig = true } + if (process.env.ALLOW_DEVICE_ACCESS_REQUESTS) { + allowDeviceAccessRequests = + process.env.ALLOW_DEVICE_ACCESS_REQUESTS === 'true' + } + + if (process.env.ALLOW_NEW_USER_REGISTRATION) { + allowNewUserRegistration = + process.env.ALLOW_NEW_USER_REGISTRATION === 'true' + } + let options = { allow_readonly, expiration, secretKey, users, immutableConfig, - acls + acls, + allowDeviceAccessRequests, + allowNewUserRegistration } // so that enableSecurity gets the defaults to save @@ -273,7 +290,9 @@ module.exports = function (app, config) { var result = { status: req.skIsAuthenticated ? 'loggedIn' : 'notLoggedIn', readOnlyAccess: configuration.allow_readonly, - authenticationRequired: true + authenticationRequired: true, + allowNewUserRegistration: configuration.allowNewUserRegistration, + allowDeviceAccessRequests: configuration.allowDeviceAccessRequests } if (req.skIsAuthenticated) { result.userLevel = req.skUser.type @@ -294,6 +313,7 @@ module.exports = function (app, config) { strategy.setConfig = (config, newConfig) => { assertConfigImmutability() newConfig.users = config.users + newConfig.devices = config.devices newConfig.secretKey = config.secretKey options = newConfig return newConfig @@ -701,6 +721,203 @@ module.exports = function (app, config) { } } + strategy.getAccessRequestsResponse = () => { + return accessRequests.filter(r => r.active).map(r => ({ + identifier: r.identifier, + description: r.description, + ip: r.ip, + expiration: r.expiration, + config: r.config, + active: r.active + })) + } + + function sendAccessRequestsUpdate () { + app.emit('serverevent', { + type: 'ACCESS_REQUEST', + from: CONFIG_PLUGINID, + data: strategy.getAccessRequestsResponse() + }) + } + + strategy.checkRequest = (requestId, cb) => { + var idx = accessRequests.findIndex(r => r.requestId == requestId) + if (idx == -1) { + cb(new Error('not found')) + return + } + + var request = accessRequests[idx] + var result = { + status: request.active + ? 'PENDING' + : request.approved ? 'APPROVED' : 'DENIED' + } + if (request.approved) { + result.token = request.token + delete request.token + } + cb(null, result) + } + + strategy.setAccessRequestStatus = (config, identifier, status, body, cb) => { + var idx = accessRequests.findIndex(r => r.identifier == identifier) + if (idx == -1) { + cb(new Error('not found')) + return + } + + var request = accessRequests[idx] + + if (status === 'approved') { + if (request.uuid) { + var payload = { device: identifier } + var jwtOptions = {} + + expiration = body.expiration + if (expiration !== 'NEVER') { + jwtOptions.expiresIn = expiration + } + debug(`expiration ${expiration} ${body.expiration}`) + var token = jwt.sign(payload, config.secretKey, jwtOptions) + + if (!config.devices) { + config.devices = [] + } + + config.devices.push({ + uuid: request.uuid, + permissions: request.permissions, + config: body.config + }) + request.token = token + } else { + config.users.push({ + username: identifier, + password: request.password, + type: 'readonly' + }) + } + request.approved = true + } else if (status === 'denied') { + request.approved = false + } else { + cb(new Error('Unkown status value'), config) + return + } + // accessRequests = accessRequests.filter(r => r.identifier != identifier) + request.active = false + cb(null, config) + options = config + sendAccessRequestsUpdate() + } + + function validateAccessRequest (request) { + if (request.userId) { + return !_.isUndefined(request.password) + } else if (request.uuid) { + return !_.isUndefined(request.description) + } else { + return false + } + } + + strategy.requestAccess = (config, request, sourceIp, cb) => { + if (!validateAccessRequest(request)) { + cb(new Error('Invalid request')) + return + } + + request.ip = sourceIp + request.active = true + request.date = new Date() + + var alertMessage + var response + if (request.uuid) { + if (!options.allowDeviceAccessRequests) { + cb(new Error('Device access not allowed')) + return + } + + if ( + config.devices && + config.devices.find(device => device.uuid == request.uuid) + ) { + cb(new Error(`A device with uuid '${request.uuid}' already has access`)) + return + } + + if (accessRequests.find(r => r.uuid == request.uuid)) { + cb( + new Error( + `A device with uuid '${request.uuid}' has already requested access` + ) + ) + return + } + + request.identifier = request.uuid + request.requestId = uuidv4() + + /* + req.on('close', (err) => { + accessRequests = accessRequests.filter(r => r.uuid != request.uuid) + sendAccessRequestsUpdate() + }) + */ + response = { requestId: request.requestId } + + alertMessage = `A device with IP ${request.ip} and UUID ${ + request.uuid + } has requested access to the server` + debug(alertMessage) + } else { + if (!options.allowNewUserRegistration) { + cb(new Error('new user registration not allowed')) + return + } + + var existing = options.users.find(user => user.username == request.userId) + if (existing) { + cb(new Error('User already exists')) + return + } + request.description = 'New User Request' + request.identifier = request.userId + request.password = bcrypt.hashSync( + request.password, + bcrypt.genSaltSync(passwordSaltRounds) + ) + alertMessage = `${request.userid} has requested server access` + debug(alertMessage) + } + + request.permissions = 'readonly' + request.expiration = config.expiration + accessRequests.push(request) + sendAccessRequestsUpdate() + app.handleMessage(CONFIG_PLUGINID, { + context: 'vessels.' + app.selfId, + updates: [ + { + values: [ + { + path: 'notifications.security.accessRequest', + value: { + state: 'alert', + method: ['visual', 'sound'], + message: alertMessage, + timestamp: new Date().toISOString() + } + } + ] + } + ] + }) + cb(null, response, config) + } + setupApp() return strategy diff --git a/packages/server-admin-ui/src/actions.js b/packages/server-admin-ui/src/actions.js index d7fdf571d..52e8c00cd 100644 --- a/packages/server-admin-ui/src/actions.js +++ b/packages/server-admin-ui/src/actions.js @@ -126,6 +126,7 @@ export const fetchLoginStatus = buildFetchAction('/loginStatus', 'RECEIVE_LOGIN_ export const fetchPlugins = buildFetchAction('/plugins', 'RECEIVE_PLUGIN_LIST') export const fetchWebapps = buildFetchAction('/webapps', 'RECEIVE_WEBAPPS_LIST') export const fetchApps = buildFetchAction('/appstore/available', 'RECEIVE_APPSTORE_LIST') +export const fetchAccessRequests = buildFetchAction('/security/access/requests', 'ACCESS_REQUEST') export const fetchServerSpecification = buildFetchAction('/signalk', 'RECEIVE_SERVER_SPEC') export function fetchAllData (dispatch) { @@ -133,7 +134,8 @@ export function fetchAllData (dispatch) { fetchWebapps(dispatch) fetchApps(dispatch) fetchLoginStatus(dispatch) - fetchServerSpecification(dispatch) + fetchServerSpecification(dispatch), + fetchAccessRequests(dispatch) } export function openServerEventsConnection (dispatch) { diff --git a/packages/server-admin-ui/src/components/Sidebar/Sidebar.js b/packages/server-admin-ui/src/components/Sidebar/Sidebar.js index b3d13c75b..b9de87501 100644 --- a/packages/server-admin-ui/src/components/Sidebar/Sidebar.js +++ b/packages/server-admin-ui/src/components/Sidebar/Sidebar.js @@ -154,6 +154,7 @@ const mapStateToProps = state => { var appUpdates = state.appStore.updates.length var updatesBadge = null var availableBadge = null + var accessRequestsBadge = null if (appUpdates > 0) { updatesBadge = { variant: 'danger', @@ -162,6 +163,14 @@ const mapStateToProps = state => { } } + if ( state.accessRequests.length > 0 ) { + accessRequestsBadge = { + variant: 'danger', + text: `${state.accessRequests.length}`, + color: 'danger' + } + } + if (!state.appStore.storeAvailable) { updatesBadge = availableBadge = { variant: 'danger', @@ -240,11 +249,33 @@ const mapStateToProps = state => { state.loginStatus.authenticationRequired === false || state.loginStatus.userLevel == 'admin' ) { - result.items.push({ + var security = { name: 'Security', url: '/security', - icon: 'icon-settings' - }) + icon: 'icon-settings', + badge: accessRequestsBadge, + children: [ + { + name: 'Settings', + url: '/security/settings' + }, + { + name: 'Users', + url: '/security/users' + } + ] + } + if ( + state.loginStatus.allowNewUserRegistration || + state.allowDeviceAccessRequests ) { + security.children.push({ + name: 'Access Requests', + url: '/security/access/requests', + badge: accessRequestsBadge, + }) + + } + result.items.push(security) } return result diff --git a/packages/server-admin-ui/src/containers/Full/Full.js b/packages/server-admin-ui/src/containers/Full/Full.js index 2725b8ec2..f976340ae 100644 --- a/packages/server-admin-ui/src/containers/Full/Full.js +++ b/packages/server-admin-ui/src/containers/Full/Full.js @@ -13,8 +13,11 @@ import Dashboard from '../../views/Dashboard/' import Webapps from '../../views/Webapps/' import Apps from '../../views/appstore/Apps/' import Configuration from '../../views/Configuration' -import Login from '../../views/Login' -import Security from '../../views/Security' +import Login from '../../views/security/Login' +import SecuritySettings from '../../views/security/Settings' +import Users from '../../views/security/Users' +import Register from '../../views/security/Register' +import AccessRequests from '../../views/security/AccessRequests' import VesselConfiguration from '../../views/ServerConfig/VesselConfiguration' import ProvidersConfiguration from '../../views/ServerConfig/ProvidersConfiguration' import Settings from '../../views/ServerConfig/Settings' @@ -72,8 +75,11 @@ class Full extends Component { path='/serverConfiguration/providers' component={loginOrOriginal(ProvidersConfiguration)} /> - + + + + diff --git a/packages/server-admin-ui/src/index.js b/packages/server-admin-ui/src/index.js index a893477f0..0d750fca8 100644 --- a/packages/server-admin-ui/src/index.js +++ b/packages/server-admin-ui/src/index.js @@ -33,7 +33,8 @@ const state = { serverSpecification: {}, websocketStatus: 'initial', webSocket: null, - restarting: false + restarting: false, + accessRequests: [] } let store = createStore( @@ -159,6 +160,12 @@ let store = createStore( state.webSocket.close() } } + if ( action.type === 'ACCESS_REQUEST' ) { + return { + ...state, + accessRequests: action.data + } + } return state }, state, diff --git a/packages/server-admin-ui/src/views/security/AccessRequests.js b/packages/server-admin-ui/src/views/security/AccessRequests.js new file mode 100644 index 000000000..1317420a1 --- /dev/null +++ b/packages/server-admin-ui/src/views/security/AccessRequests.js @@ -0,0 +1,189 @@ +import React, { Component } from 'react' +import { connect } from 'react-redux' +import { + Button, + Card, + CardHeader, + CardBody, + CardFooter, + InputGroup, + InputGroupAddon, + Input, + Form, + Col, + Label, + FormGroup, + FormText, + Table, + Row +} from 'reactstrap'; +import EnableSecurity from './EnableSecurity' + +class AccessRequests extends Component { + constructor(props) { + super(props); + this.state = { + selectedRequest: null, + accessRequestsApproving: [], + accessRequestsDenying: [] + } + this.handleRequestChange = this.handleRequestChange.bind(this) + } + + handleAccessRequest(identifier, approved) { + var stateKey = approved ? 'accessRequestsApproving' : 'accessRequestsDenying' + this.state[stateKey].push(identifier) + this.setState({stateKey: this.state}) + + var payload = { + permissions: this.state.selectedRequest.permissions || 'readonly', + config: this.state.selectedRequest.config, + expiration: this.state.selectedRequest.expiration || '1y' + } + + fetch(`/security/access/requests/${identifier}/${approved ? 'approved' : 'denied'}`, { + method: 'PUT', + credentials: "include", + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload) + }).then(response => response.text()) + .then(response => { + this.state[stateKey] = this.state[stateKey].filter(id => id != identifier) + this.setState({ + stateKey: this.state[stateKey], + selectedRequest: null + }) + }); + } + + requestClicked(event, request, index) { + this.setState({ + selectedRequest: JSON.parse(JSON.stringify(request)), + selectedIndex: index + }, () => { + this.refs['selectedRequest'].scrollIntoView(); + }); + } + + handleRequestChange(event) { + const value = event.target.type === 'checkbox' ? event.target.checked : event.target.value; + this.state.selectedRequest[event.target.name] = value; + this.setState({ + selectedRequest: this.state.selectedRequest + }); + } + handleCancel(event) { + this.setState({ selectedRequest: null }) + } + + + render () { + return ( +
+ {this.props.loginStatus.authenticationRequired === false && + + } + {this.props.loginStatus.authenticationRequired && +
+ + + Access Requests + + + + + + + + + + + + {(this.props.accessRequests || []).map((req, index) => { + return ( + + + + + + ) + })} + +
IdentifierDescriptionSource IP
{req.identifier}{req.description}{req.ip}
+
+
+ + {this.state.selectedRequest && +
+ + + Request + + + + + + + + + + + + + + + + + + + + + + + + + Exmaples: 60s, 1m, 1h, 1d, NEVER + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ } + +
+ } +
+ ) + } +} + +const mapStateToProps = ({ accessRequests, loginStatus }) => ({ accessRequests, loginStatus }) + +export default connect(mapStateToProps)(AccessRequests) + + diff --git a/packages/server-admin-ui/src/views/EnableSecurity.js b/packages/server-admin-ui/src/views/security/EnableSecurity.js similarity index 97% rename from packages/server-admin-ui/src/views/EnableSecurity.js rename to packages/server-admin-ui/src/views/security/EnableSecurity.js index 96d206fab..8cc5940f0 100644 --- a/packages/server-admin-ui/src/views/EnableSecurity.js +++ b/packages/server-admin-ui/src/views/security/EnableSecurity.js @@ -14,8 +14,8 @@ import { HelpBlock } from 'reactstrap' import { connect } from 'react-redux' -import { login, enableSecurity, fetchLoginStatus } from '../actions' -import Dashboard from './Dashboard/' +import { login, enableSecurity, fetchLoginStatus } from '../../actions' +import Dashboard from '../Dashboard/' import Login from './Login' class EnableSecurity extends Component { diff --git a/packages/server-admin-ui/src/views/Login.js b/packages/server-admin-ui/src/views/security/Login.js similarity index 89% rename from packages/server-admin-ui/src/views/Login.js rename to packages/server-admin-ui/src/views/security/Login.js index 24419c017..2118d9c53 100644 --- a/packages/server-admin-ui/src/views/Login.js +++ b/packages/server-admin-ui/src/views/security/Login.js @@ -13,10 +13,12 @@ import { InputGroupAddon, HelpBlock } from 'reactstrap' +import { Link } from 'react-router-dom' import { connect } from 'react-redux' -import { login, fetchAllData } from '../actions' -import Dashboard from './Dashboard/' +import { login, fetchAllData } from '../../actions' +import Dashboard from '../Dashboard/' import EnableSecurity from './EnableSecurity' +import Register from './Register' class Login extends Component { constructor (props) { @@ -104,7 +106,14 @@ class Login extends Component {

{this.state.loginErrorMessage} -

+

+ {!this.state.loginErrorMessage && this.props.loginStatus.allowNewUserRegistration && +
+ + + +
+ } diff --git a/packages/server-admin-ui/src/views/security/Register.js b/packages/server-admin-ui/src/views/security/Register.js new file mode 100644 index 000000000..2573a3e67 --- /dev/null +++ b/packages/server-admin-ui/src/views/security/Register.js @@ -0,0 +1,114 @@ +import React, {Component} from 'react'; +import {Container, Row, Col, Card, CardBody, CardFooter, Button, Input, InputGroup, InputGroupAddon, FormText} from 'reactstrap'; + +class Register extends Component { + constructor(props) { + super(props); + this.state = { + errorMessage: null, + email: '', + password: '', + confirmPassword: '', + registrationSent: false + } + this.handleInputChange = this.handleInputChange.bind(this); + this.handleCreate = this.handleCreate.bind(this); + } + + handleInputChange(event) { + var targetName = event.target.name + this.setState({[event.target.name]: event.target.value}, () => { + if ( targetName === 'password' || + targetName === 'confirmPassword' && + this.state.password != this.state.confirmPassword ) { + this.setState({errorMessage: "Passwords do not match"}) + } else { + this.setState({errorMessage: null}) + } + }) + } + + handleCreate(event) { + if ( this.state.email.length == 0 ) { + this.setState({errorMessage: 'Please enter an email address'}) + } else if ( this.state.password.length == 0 && + this.state.confirmPassword.length == 0 ) { + this.setState({errorMessage: 'Please enter and conform your password'}) + } else if ( this.state.password != this.state.confirmPassword ) { + //error message is already thwre + return + } else { + var payload = { + userId: this.state.email, + password: this.state.password + } + fetch(`/access/requests`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + credentials: "include" + }) + .then(response => { + if ( response.status != 200 ) { + response.text().then(text => { + this.setState({errorMessage: text}) + }) + } else { + this.setState({registrationSent: true}) + } + }); + } + } + + render() { + return ( +
+ + + + + +

Register

+ {this.state.registrationSent && +

Your registration has been sent

+ } + {!this.state.registrationSent && +
+

Create your account

+ + @ + + + + + + + + + + +

{this.state.errorMessage}

+
+ } +
+ {!this.state.registrationSent && + + + + + + + + } +
+ +
+
+
+ ); + } +} + +export default Register; diff --git a/packages/server-admin-ui/src/views/security/Settings.js b/packages/server-admin-ui/src/views/security/Settings.js new file mode 100644 index 000000000..5c674b75b --- /dev/null +++ b/packages/server-admin-ui/src/views/security/Settings.js @@ -0,0 +1,196 @@ +import React, { Component } from 'react' +import { connect } from 'react-redux' +import { + Button, + Card, + CardHeader, + CardBody, + CardFooter, + InputGroup, + InputGroupAddon, + Input, + Form, + Col, + Label, + FormGroup, + FormText, + Table, + Row +} from 'reactstrap' +import EnableSecurity from './EnableSecurity' + +export function fetchSecurityConfig () { + fetch(`/security/config`, { + credentials: 'include' + }) + .then(response => response.json()) + .then(data => { + console.log(JSON.stringify(data)) + this.setState(data) + }) +} + +class Settings extends Component { + constructor (props) { + super(props) + this.state = { + allow_readonly: false, + expiration: '', + allowNewUserRegistration: true, + allowDeviceAccessRequests: true + } + + this.handleChange = this.handleChange.bind(this) + this.handleSaveConfig = this.handleSaveConfig.bind(this) + this.fetchSecurityConfig = fetchSecurityConfig.bind(this) + } + + componentDidMount () { + if (this.props.loginStatus.authenticationRequired) { + this.fetchSecurityConfig() + } + } + + handleChange (event) { + const value = + event.target.type === 'checkbox' + ? event.target.checked + : event.target.value + this.setState({ [event.target.name]: value }) + } + + handleSaveConfig () { + var payload = { + allow_readonly: this.state.allow_readonly, + expiration: this.state.expiration, + allowNewUserRegistration: this.state.allowNewUserRegistration, + allowDeviceAccessRequests: this.state.allowDeviceAccessRequests + } + fetch('/security/config', { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(payload), + credentials: 'include' + }) + .then(response => response.text()) + .then(response => { + this.fetchSecurityConfig() + alert(response) + }) + } + + render () { + return ( +
+ {this.props.loginStatus.authenticationRequired === false && ( + + )} + {this.props.loginStatus.authenticationRequired && ( +
+ + + Settings + + +
+ + + + + + +
+ +
+
+ +
+ + + + +
+ +
+
+ +
+ + + + +
+ +
+
+ +
+ + + + + + + + Exmaples: 60s, 1m, 1h, 1d + + + +
+
+ + + +
+ +
+ )} +
+ ) + } +} + +const mapStateToProps = ({ securityConfig }) => ({ securityConfig }) + +export default connect(mapStateToProps)(Settings) + diff --git a/packages/server-admin-ui/src/views/Security.js b/packages/server-admin-ui/src/views/security/Users.js similarity index 71% rename from packages/server-admin-ui/src/views/Security.js rename to packages/server-admin-ui/src/views/security/Users.js index b655a15db..57fb8d36d 100644 --- a/packages/server-admin-ui/src/views/Security.js +++ b/packages/server-admin-ui/src/views/security/Users.js @@ -19,16 +19,6 @@ import { } from 'reactstrap' import EnableSecurity from './EnableSecurity' -export function fetchSecurityConfig () { - fetch(`/security/config`, { - credentials: 'include' - }) - .then(response => response.json()) - .then(data => { - this.setState(data) - }) -} - export function fetchSecurityUsers () { fetch(`/security/users`, { credentials: 'include' @@ -39,20 +29,15 @@ export function fetchSecurityUsers () { }) } -class Security extends Component { +class Users extends Component { constructor (props) { super(props) this.state = { users: [], - allow_readonly: false, - expiration: '' } - this.handleChange = this.handleChange.bind(this) this.handleAddUser = this.handleAddUser.bind(this) - this.handleSaveConfig = this.handleSaveConfig.bind(this) this.fetchSecurityUsers = fetchSecurityUsers.bind(this) - this.fetchSecurityConfig = fetchSecurityConfig.bind(this) this.handleCancel = this.handleCancel.bind(this) this.handleApply = this.handleApply.bind(this) this.handleUserChange = this.handleUserChange.bind(this) @@ -61,19 +46,10 @@ class Security extends Component { componentDidMount () { if (this.props.loginStatus.authenticationRequired) { - this.fetchSecurityConfig() this.fetchSecurityUsers() } } - handleChange (event) { - const value = - event.target.type === 'checkbox' - ? event.target.checked - : event.target.value - this.setState({ [event.target.name]: value }) - } - handleUserChange (event) { const value = event.target.type === 'checkbox' @@ -167,26 +143,6 @@ class Security extends Component { }) } - handleSaveConfig () { - var payload = { - allow_readonly: this.state.allow_readonly, - expiration: this.state.expiration - } - fetch('/security/config', { - method: 'PUT', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(payload), - credentials: 'include' - }) - .then(response => response.text()) - .then(response => { - this.fetchSecurityConfig() - alert(response) - }) - } - userClicked (user, index) { console.log(JSON.stringify(user)) this.setState( @@ -211,72 +167,6 @@ class Security extends Component { )} {this.props.loginStatus.authenticationRequired && (
- - - Settings - - -
- - - - - - -
- -
-
- -
- - - - - - - - Exmaples: 60s, 1m, 1h, 1d - - - -
-
- - - -
- Users @@ -425,12 +315,9 @@ class Security extends Component { } } -const mapStateToProps = ({ securityConfig, securityUsers }) => ({ - securityConfig, - securityUsers -}) +const mapStateToProps = ({ securityUsers }) => ({ securityUsers }) -export default connect(mapStateToProps)(Security) +export default connect(mapStateToProps)(Users) function convertType (type) { if (type == 'readonly') {