remove roles, as a simplification

This commit is contained in:
Rick Watson 2019-05-25 09:45:49 +01:00
parent 0c630f0f93
commit 6935b63706
5 changed files with 59 additions and 109 deletions

View File

@ -1,16 +1,15 @@
{ {
"jwt_secret":"esp8266-react", "jwt_secret":"esp8266-react",
"roles": ["admin", "guest"],
"users": [ "users": [
{ {
"username": "admin", "username": "admin",
"password": "admin", "password": "admin",
"role": "admin" "admin": true
}, },
{ {
"username": "guest", "username": "guest",
"password": "guest", "password": "guest",
"role": "guest" "admin": false
} }
] ]
} }

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator'; import { ValidatorForm } from 'react-material-ui-form-validator';
import { withStyles } from '@material-ui/core/styles'; import { withStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button'; import Button from '@material-ui/core/Button';
@ -13,14 +13,16 @@ import TableCell from '@material-ui/core/TableCell';
import TableFooter from '@material-ui/core/TableFooter'; import TableFooter from '@material-ui/core/TableFooter';
import TableHead from '@material-ui/core/TableHead'; import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow'; import TableRow from '@material-ui/core/TableRow';
import Chip from '@material-ui/core/Chip';
import EditIcon from '@material-ui/icons/Edit'; import EditIcon from '@material-ui/icons/Edit';
import DeleteIcon from '@material-ui/icons/Delete'; import DeleteIcon from '@material-ui/icons/Delete';
import CloseIcon from '@material-ui/icons/Close';
import CheckIcon from '@material-ui/icons/Check';
import IconButton from '@material-ui/core/IconButton'; import IconButton from '@material-ui/core/IconButton';
import SectionContent from '../components/SectionContent'; import SectionContent from '../components/SectionContent';
import UserForm from './UserForm'; import UserForm from './UserForm';
import { withAuthenticationContext } from '../authentication/Context';
const styles = theme => ({ const styles = theme => ({
loadingSettings: { loadingSettings: {
@ -30,26 +32,15 @@ const styles = theme => ({
margin: theme.spacing.unit * 4, margin: theme.spacing.unit * 4,
textAlign: "center" textAlign: "center"
}, },
switchControl: {
width: "100%",
marginTop: theme.spacing.unit * 2,
marginBottom: theme.spacing.unit
},
textField: {
width: "100%"
},
button: { button: {
marginRight: theme.spacing.unit * 2, marginRight: theme.spacing.unit * 2,
marginTop: theme.spacing.unit * 2, marginTop: theme.spacing.unit * 2,
}, },
chip: {
margin: theme.spacing.unit,
},
table: { table: {
'& td, & th': { padding: theme.spacing.unit } '& td, & th': { padding: theme.spacing.unit }
}, },
actions: { actions: {
color: theme.palette.text.secondary, whiteSpace: "nowrap"
} }
}); });
@ -76,7 +67,7 @@ class ManageUsersForm extends React.Component {
user: { user: {
username: "", username: "",
password: "", password: "",
roles: [] admin: true
} }
}); });
}; };
@ -85,8 +76,8 @@ class ManageUsersForm extends React.Component {
return !this.props.userData.users.find(u => u.username === username); return !this.props.userData.users.find(u => u.username === username);
} }
usersValid = username => { noAdminConfigured = () => {
return !!this.props.userData.users.find(u => u.roles.includes("admin")); return !this.props.userData.users.find(u => u.admin);
} }
startEditingUser = user => { startEditingUser = user => {
@ -102,10 +93,6 @@ class ManageUsersForm extends React.Component {
}); });
} }
sortedUsers(users) {
return users.sort(compareUsers);
}
doneEditingUser = () => { doneEditingUser = () => {
const { user } = this.state; const { user } = this.state;
const { userData } = this.props; const { userData } = this.props;
@ -120,17 +107,29 @@ class ManageUsersForm extends React.Component {
handleUserValueChange = name => event => { handleUserValueChange = name => event => {
const { user } = this.state; const { user } = this.state;
if (user) {
this.setState({ this.setState({
user: { user: {
...user, [name]: event.target.value ...user, [name]: event.target.value
} }
}); });
}
}; };
handleUserCheckboxChange = name => event => {
const { user } = this.state;
this.setState({
user: {
...user, [name]: event.target.checked
}
});
}
onSubmit = () => {
this.props.onSubmit();
this.props.authenticationContex.refresh();
}
render() { render() {
const { classes, userData, userDataFetched, errorMessage, onSubmit, onReset, handleValueChange } = this.props; const { classes, userData, userDataFetched, errorMessage, onReset } = this.props;
const { user, creating } = this.state; const { user, creating } = this.state;
return ( return (
<SectionContent title="Manage Users"> <SectionContent title="Manage Users">
@ -148,33 +147,31 @@ class ManageUsersForm extends React.Component {
<UserForm <UserForm
user={user} user={user}
creating={creating} creating={creating}
roles={userData.roles}
onDoneEditing={this.doneEditingUser} onDoneEditing={this.doneEditingUser}
onCancelEditing={this.cancelEditingUser} onCancelEditing={this.cancelEditingUser}
handleValueChange={this.handleUserValueChange} handleValueChange={this.handleUserValueChange}
handleCheckboxChange={this.handleUserCheckboxChange}
uniqueUsername={this.uniqueUsername} uniqueUsername={this.uniqueUsername}
/> />
: :
<ValidatorForm onSubmit={onSubmit}> <ValidatorForm onSubmit={this.onSubmit}>
<Table className={classes.table}> <Table className={classes.table}>
<TableHead> <TableHead>
<TableRow> <TableRow>
<TableCell>Username</TableCell> <TableCell>Username</TableCell>
<TableCell align="center">Role(s)</TableCell> <TableCell align="center">Admin?</TableCell>
<TableCell align="center">Action</TableCell> <TableCell align="center">Action</TableCell>
</TableRow> </TableRow>
</TableHead> </TableHead>
<TableBody> <TableBody>
{this.sortedUsers(userData.users).map(user => ( {userData.users.sort(compareUsers).map(user => (
<TableRow key={user.username}> <TableRow key={user.username}>
<TableCell component="th" scope="row"> <TableCell component="th" scope="row">
{user.username} {user.username}
</TableCell> </TableCell>
<TableCell align="center"> <TableCell align="center">
{ {
user.roles.map(role => ( user.admin ? <CheckIcon /> : <CloseIcon />
<Chip label={role} className={classes.chip} />
))
} }
</TableCell> </TableCell>
<TableCell align="center"> <TableCell align="center">
@ -192,7 +189,7 @@ class ManageUsersForm extends React.Component {
<TableRow> <TableRow>
<TableCell colSpan={2}> <TableCell colSpan={2}>
{ {
!this.usersValid() && this.noAdminConfigured() &&
<Typography variant="body1" color="error"> <Typography variant="body1" color="error">
You must have at least one admin user configured. You must have at least one admin user configured.
</Typography> </Typography>
@ -206,7 +203,7 @@ class ManageUsersForm extends React.Component {
</TableRow> </TableRow>
</TableFooter> </TableFooter>
</Table> </Table>
<Button variant="contained" color="primary" className={classes.button} type="submit"> <Button variant="contained" color="primary" className={classes.button} type="submit" disabled={this.noAdminConfigured()}>
Save Save
</Button> </Button>
<Button variant="contained" color="secondary" className={classes.button} onClick={onReset}> <Button variant="contained" color="secondary" className={classes.button} onClick={onReset}>
@ -230,6 +227,7 @@ class ManageUsersForm extends React.Component {
} }
ManageUsersForm.propTypes = { ManageUsersForm.propTypes = {
authenticationContex: PropTypes.object.isRequired,
classes: PropTypes.object.isRequired, classes: PropTypes.object.isRequired,
userData: PropTypes.object, userData: PropTypes.object,
userDataFetched: PropTypes.bool.isRequired, userDataFetched: PropTypes.bool.isRequired,
@ -240,4 +238,4 @@ ManageUsersForm.propTypes = {
handleValueChange: PropTypes.func.isRequired handleValueChange: PropTypes.func.isRequired
}; };
export default withStyles(styles)(ManageUsersForm); export default withAuthenticationContext(withStyles(styles)(ManageUsersForm));

View File

@ -7,12 +7,9 @@ import Button from '@material-ui/core/Button';
import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator'; import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator';
import PasswordValidator from '../components/PasswordValidator'; import PasswordValidator from '../components/PasswordValidator';
import Input from '@material-ui/core/Input'; import FormControlLabel from '@material-ui/core/FormControlLabel';
import InputLabel from '@material-ui/core/InputLabel'; import Switch from '@material-ui/core/Switch';
import MenuItem from '@material-ui/core/MenuItem'; import FormGroup from '@material-ui/core/FormGroup';
import FormControl from '@material-ui/core/FormControl';
import Select from '@material-ui/core/Select';
import Chip from '@material-ui/core/Chip';
const styles = theme => ({ const styles = theme => ({
textField: { textField: {
@ -41,7 +38,7 @@ class UserForm extends React.Component {
} }
render() { render() {
const { classes, user, roles, creating, handleValueChange, onDoneEditing, onCancelEditing } = this.props; const { classes, user, creating, handleValueChange, handleCheckboxChange, onDoneEditing, onCancelEditing } = this.props;
return ( return (
<ValidatorForm onSubmit={onDoneEditing}> <ValidatorForm onSubmit={onDoneEditing}>
<TextValidator <TextValidator
@ -62,31 +59,15 @@ class UserForm extends React.Component {
label="Password" label="Password"
className={classes.textField} className={classes.textField}
value={user.password} value={user.password}
onChange={handleValueChange('password')} onChange={handleCheckboxChange('password')}
margin="normal" margin="normal"
/> />
<FormControl className={classes.textField}> <FormGroup>
<InputLabel htmlFor="roles">Roles</InputLabel> <FormControlLabel
<Select control={<Switch checked={user.admin} onChange={handleCheckboxChange('admin')} id="admin" />}
multiple label="Admin?"
value={user.roles} />
onChange={handleValueChange('roles')} </FormGroup>
input={<Input id="roles" />}
renderValue={selected => (
<div className={classes.chips}>
{selected.map(value => (
<Chip key={value} label={value} className={classes.chip} />
))}
</div>
)}
>
{roles.map(name => (
<MenuItem key={name} value={name}>
{name}
</MenuItem>
))}
</Select>
</FormControl>
<Button variant="contained" color="primary" className={classes.button} type="submit"> <Button variant="contained" color="primary" className={classes.button} type="submit">
Save Save
</Button> </Button>
@ -102,11 +83,11 @@ UserForm.propTypes = {
classes: PropTypes.object.isRequired, classes: PropTypes.object.isRequired,
user: PropTypes.object.isRequired, user: PropTypes.object.isRequired,
creating: PropTypes.bool.isRequired, creating: PropTypes.bool.isRequired,
roles: PropTypes.array.isRequired,
onDoneEditing: PropTypes.func.isRequired, onDoneEditing: PropTypes.func.isRequired,
onCancelEditing: PropTypes.func.isRequired, onCancelEditing: PropTypes.func.isRequired,
uniqueUsername: PropTypes.func.isRequired, uniqueUsername: PropTypes.func.isRequired,
handleValueChange: PropTypes.func.isRequired handleValueChange: PropTypes.func.isRequired,
handleCheckboxChange: PropTypes.func.isRequired
}; };
export default withStyles(styles)(UserForm); export default withStyles(styles)(UserForm);

View File

@ -7,26 +7,11 @@ void SecurityManager::readFromJsonObject(JsonObject& root) {
// secret // secret
_jwtSecret = root["jwt_secret"] | DEFAULT_JWT_SECRET; _jwtSecret = root["jwt_secret"] | DEFAULT_JWT_SECRET;
// roles
_roles.clear();
if (root["roles"].is<JsonArray>()) {
JsonArray roles = root["roles"];
for (JsonVariant role : roles) {
_roles.push_back(role.as<String>());
}
}
// users // users
_users.clear(); _users.clear();
if (root["users"].is<JsonArray>()) { if (root["users"].is<JsonArray>()) {
for (JsonVariant user : root["users"].as<JsonArray>()) { for (JsonVariant user : root["users"].as<JsonArray>()) {
std::list<String> roles; _users.push_back(User(user["username"], user["password"], user["admin"]));
if (user["roles"].is<JsonArray>()) {
for (JsonVariant role : user["roles"].as<JsonArray>()) {
roles.push_back(role.as<String>());
}
}
_users.push_back(User(user["username"], user["password"], roles));
} }
} }
} }
@ -36,22 +21,13 @@ void SecurityManager::writeToJsonObject(JsonObject& root) {
// secret // secret
root["jwt_secret"] = _jwtSecret; root["jwt_secret"] = _jwtSecret;
// roles
JsonArray roles = root.createNestedArray("roles");
for (String _role : _roles) {
roles.add(_role);
}
// users // users
JsonArray users = root.createNestedArray("users"); JsonArray users = root.createNestedArray("users");
for (User _user : _users) { for (User _user : _users) {
JsonObject user = users.createNestedObject(); JsonObject user = users.createNestedObject();
user["username"] = _user.getUsername(); user["username"] = _user.getUsername();
user["password"] = _user.getPassword(); user["password"] = _user.getPassword();
JsonArray roles = user.createNestedArray("roles"); user["admin"] = _user.isAdmin();
for (String _role : _user.getRoles()){
roles.add(_role);
}
} }
} }
@ -100,10 +76,7 @@ Authentication SecurityManager::authenticate(String username, String password) {
inline void populateJWTPayload(JsonObject &payload, User *user) { inline void populateJWTPayload(JsonObject &payload, User *user) {
payload["username"] = user->getUsername(); payload["username"] = user->getUsername();
JsonArray roles = payload.createNestedArray("roles"); payload["admin"] = user -> isAdmin();
for (String _role : user->getRoles()){
roles.add(_role);
}
} }
boolean SecurityManager::validatePayload(JsonObject &parsedPayload, User *user) { boolean SecurityManager::validatePayload(JsonObject &parsedPayload, User *user) {

View File

@ -28,17 +28,17 @@ class User {
private: private:
String _username; String _username;
String _password; String _password;
std::list<String> _roles; bool _admin;
public: public:
User(String username, String password, std::list<String> roles): _username(username), _password(password), _roles(roles) {} User(String username, String password, bool admin): _username(username), _password(password), _admin(admin) {}
String getUsername() { String getUsername() {
return _username; return _username;
} }
String getPassword() { String getPassword() {
return _password; return _password;
} }
std::list<String> getRoles() { bool isAdmin() {
return _roles; return _admin;
} }
}; };
@ -97,7 +97,6 @@ class SecurityManager : public SettingsService {
// access point settings // access point settings
String _jwtSecret; String _jwtSecret;
std::list<String> _roles;
std::list<User> _users; std::list<User> _users;
// endpoint functions // endpoint functions