fix encoding where signature contains a zero
This commit is contained in:
parent
6935b63706
commit
4fdc3eee66
@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { ValidatorForm } from 'react-material-ui-form-validator';
|
import { ValidatorForm } from 'react-material-ui-form-validator';
|
||||||
@ -13,6 +13,8 @@ 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 Box from '@material-ui/core/Box';
|
||||||
|
|
||||||
|
|
||||||
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';
|
||||||
@ -80,6 +82,12 @@ class ManageUsersForm extends React.Component {
|
|||||||
return !this.props.userData.users.find(u => u.admin);
|
return !this.props.userData.users.find(u => u.admin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeUser = user => {
|
||||||
|
const { userData } = this.props;
|
||||||
|
const users = userData.users.filter(u => u.username !== user.username);
|
||||||
|
this.props.setData({ ...userData, users });
|
||||||
|
}
|
||||||
|
|
||||||
startEditingUser = user => {
|
startEditingUser = user => {
|
||||||
this.setState({
|
this.setState({
|
||||||
creating: false,
|
creating: false,
|
||||||
@ -96,8 +104,7 @@ class ManageUsersForm extends React.Component {
|
|||||||
doneEditingUser = () => {
|
doneEditingUser = () => {
|
||||||
const { user } = this.state;
|
const { user } = this.state;
|
||||||
const { userData } = this.props;
|
const { userData } = this.props;
|
||||||
let { users } = userData;
|
const users = userData.users.filter(u => u.username !== user.username);
|
||||||
users = users.filter(u => u.username !== user.username);
|
|
||||||
users.push(user);
|
users.push(user);
|
||||||
this.props.setData({ ...userData, users });
|
this.props.setData({ ...userData, users });
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -125,7 +132,7 @@ class ManageUsersForm extends React.Component {
|
|||||||
|
|
||||||
onSubmit = () => {
|
onSubmit = () => {
|
||||||
this.props.onSubmit();
|
this.props.onSubmit();
|
||||||
this.props.authenticationContex.refresh();
|
this.props.authenticationContext.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -143,17 +150,7 @@ class ManageUsersForm extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
:
|
:
|
||||||
userData ?
|
userData ?
|
||||||
user ?
|
<Fragment>
|
||||||
<UserForm
|
|
||||||
user={user}
|
|
||||||
creating={creating}
|
|
||||||
onDoneEditing={this.doneEditingUser}
|
|
||||||
onCancelEditing={this.cancelEditingUser}
|
|
||||||
handleValueChange={this.handleUserValueChange}
|
|
||||||
handleCheckboxChange={this.handleUserCheckboxChange}
|
|
||||||
uniqueUsername={this.uniqueUsername}
|
|
||||||
/>
|
|
||||||
:
|
|
||||||
<ValidatorForm onSubmit={this.onSubmit}>
|
<ValidatorForm onSubmit={this.onSubmit}>
|
||||||
<Table className={classes.table}>
|
<Table className={classes.table}>
|
||||||
<TableHead>
|
<TableHead>
|
||||||
@ -175,7 +172,7 @@ class ManageUsersForm extends React.Component {
|
|||||||
}
|
}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell align="center">
|
<TableCell align="center">
|
||||||
<IconButton aria-label="Delete">
|
<IconButton aria-label="Delete" onClick={() => this.removeUser(user)}>
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton aria-label="Edit" onClick={() => this.startEditingUser(user)}>
|
<IconButton aria-label="Edit" onClick={() => this.startEditingUser(user)}>
|
||||||
@ -187,22 +184,23 @@ class ManageUsersForm extends React.Component {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
<TableFooter>
|
<TableFooter>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={2}>
|
<TableCell colSpan={2} />
|
||||||
{
|
|
||||||
this.noAdminConfigured() &&
|
|
||||||
<Typography variant="body1" color="error">
|
|
||||||
You must have at least one admin user configured.
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell align="center">
|
<TableCell align="center">
|
||||||
<Button variant="contained" color="secondary" className={classes.button} onClick={this.createUser}>
|
<Button variant="contained" color="secondary" className={classes.button} onClick={this.createUser}>
|
||||||
Add User
|
Add User
|
||||||
</Button>
|
</Button>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableFooter>
|
</TableFooter>
|
||||||
</Table>
|
</Table>
|
||||||
|
{
|
||||||
|
this.noAdminConfigured() &&
|
||||||
|
<Typography component="div" variant="body1">
|
||||||
|
<Box bgcolor="error.main" color="error.contrastText" p={2} m={1}>
|
||||||
|
You must have at least one admin user configured.
|
||||||
|
</Box>
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
<Button variant="contained" color="primary" className={classes.button} type="submit" disabled={this.noAdminConfigured()}>
|
<Button variant="contained" color="primary" className={classes.button} type="submit" disabled={this.noAdminConfigured()}>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
@ -210,6 +208,21 @@ class ManageUsersForm extends React.Component {
|
|||||||
Reset
|
Reset
|
||||||
</Button>
|
</Button>
|
||||||
</ValidatorForm>
|
</ValidatorForm>
|
||||||
|
{
|
||||||
|
user &&
|
||||||
|
|
||||||
|
<UserForm
|
||||||
|
user={user}
|
||||||
|
creating={creating}
|
||||||
|
onDoneEditing={this.doneEditingUser}
|
||||||
|
onCancelEditing={this.cancelEditingUser}
|
||||||
|
handleValueChange={this.handleUserValueChange}
|
||||||
|
handleCheckboxChange={this.handleUserCheckboxChange}
|
||||||
|
uniqueUsername={this.uniqueUsername}
|
||||||
|
/>
|
||||||
|
|
||||||
|
}
|
||||||
|
</Fragment>
|
||||||
:
|
:
|
||||||
<SectionContent title="Manage Users">
|
<SectionContent title="Manage Users">
|
||||||
<Typography variant="h4" className={classes.loadingSettingsDetails}>
|
<Typography variant="h4" className={classes.loadingSettingsDetails}>
|
||||||
@ -227,7 +240,7 @@ class ManageUsersForm extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ManageUsersForm.propTypes = {
|
ManageUsersForm.propTypes = {
|
||||||
authenticationContex: PropTypes.object.isRequired,
|
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,
|
||||||
|
@ -1,79 +1,88 @@
|
|||||||
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 { 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';
|
||||||
|
|
||||||
import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator';
|
|
||||||
import PasswordValidator from '../components/PasswordValidator';
|
|
||||||
|
|
||||||
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
||||||
import Switch from '@material-ui/core/Switch';
|
import Switch from '@material-ui/core/Switch';
|
||||||
import FormGroup from '@material-ui/core/FormGroup';
|
import FormGroup from '@material-ui/core/FormGroup';
|
||||||
|
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||||
|
import Dialog from '@material-ui/core/Dialog';
|
||||||
|
import DialogContent from '@material-ui/core/DialogContent';
|
||||||
|
import DialogActions from '@material-ui/core/DialogActions';
|
||||||
|
|
||||||
|
import PasswordValidator from '../components/PasswordValidator';
|
||||||
|
|
||||||
const styles = theme => ({
|
const styles = theme => ({
|
||||||
textField: {
|
textField: {
|
||||||
width: "100%"
|
width: "100%"
|
||||||
},
|
},
|
||||||
checkboxControl: {
|
|
||||||
width: "100%"
|
|
||||||
},
|
|
||||||
chips: {
|
|
||||||
display: 'flex',
|
|
||||||
flexWrap: 'wrap',
|
|
||||||
},
|
|
||||||
chip: {
|
|
||||||
marginRight: theme.spacing.unit,
|
|
||||||
},
|
|
||||||
button: {
|
button: {
|
||||||
marginRight: theme.spacing.unit * 2,
|
margin: theme.spacing.unit
|
||||||
marginTop: theme.spacing.unit * 2,
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
class UserForm extends React.Component {
|
class UserForm extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.formRef = React.createRef();
|
||||||
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
ValidatorForm.addValidationRule('uniqueUsername', this.props.uniqueUsername);
|
ValidatorForm.addValidationRule('uniqueUsername', this.props.uniqueUsername);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
submit = () => {
|
||||||
|
this.formRef.current.submit();
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { classes, user, creating, handleValueChange, handleCheckboxChange, onDoneEditing, onCancelEditing } = this.props;
|
const { classes, user, creating, handleValueChange, handleCheckboxChange, onDoneEditing, onCancelEditing } = this.props;
|
||||||
return (
|
return (
|
||||||
<ValidatorForm onSubmit={onDoneEditing}>
|
<ValidatorForm onSubmit={onDoneEditing} ref={this.formRef}>
|
||||||
<TextValidator
|
<Dialog onClose={onCancelEditing} aria-labelledby="modify-user-dialog-title" open={true} scroll="paper">
|
||||||
validators={creating ? ['required', 'uniqueUsername', 'matchRegexp:^[a-zA-Z0-9_\\.]{1,24}$'] : []}
|
<DialogTitle id="modify-user-dialog-title">Modify User</DialogTitle>
|
||||||
errorMessages={creating ? ['Username is required', "That username already exists", "Must be 1-24 characters: alpha numberic, '_' or '.'"] : []}
|
<DialogContent>
|
||||||
name="username"
|
<TextValidator
|
||||||
label="Username"
|
validators={creating ? ['required', 'uniqueUsername', 'matchRegexp:^[a-zA-Z0-9_\\.]{1,24}$'] : []}
|
||||||
className={classes.textField}
|
errorMessages={creating ? ['Username is required', "That username already exists", "Must be 1-24 characters: alpha numberic, '_' or '.'"] : []}
|
||||||
value={user.username}
|
name="username"
|
||||||
disabled={!creating}
|
label="Username"
|
||||||
onChange={handleValueChange('username')}
|
className={classes.textField}
|
||||||
margin="normal"
|
value={user.username}
|
||||||
/>
|
disabled={!creating}
|
||||||
<PasswordValidator
|
onChange={handleValueChange('username')}
|
||||||
validators={['required', 'matchRegexp:^.{0,64}$']}
|
margin="normal"
|
||||||
errorMessages={['Password is required', 'Password must be 64 characters or less']}
|
/>
|
||||||
name="password"
|
<PasswordValidator
|
||||||
label="Password"
|
validators={['required', 'matchRegexp:^.{0,64}$']}
|
||||||
className={classes.textField}
|
errorMessages={['Password is required', 'Password must be 64 characters or less']}
|
||||||
value={user.password}
|
name="password"
|
||||||
onChange={handleCheckboxChange('password')}
|
label="Password"
|
||||||
margin="normal"
|
className={classes.textField}
|
||||||
/>
|
value={user.password}
|
||||||
<FormGroup>
|
onChange={handleValueChange('password')}
|
||||||
<FormControlLabel
|
margin="normal"
|
||||||
control={<Switch checked={user.admin} onChange={handleCheckboxChange('admin')} id="admin" />}
|
/>
|
||||||
label="Admin?"
|
<FormGroup>
|
||||||
/>
|
<FormControlLabel
|
||||||
</FormGroup>
|
control={<Switch checked={user.admin} onChange={handleCheckboxChange('admin')} id="admin" />}
|
||||||
<Button variant="contained" color="primary" className={classes.button} type="submit">
|
label="Admin?"
|
||||||
Save
|
/>
|
||||||
</Button>
|
</FormGroup>
|
||||||
<Button variant="contained" color="secondary" className={classes.button} onClick={onCancelEditing}>
|
</DialogContent>
|
||||||
Back
|
<DialogActions >
|
||||||
</Button>
|
<Button variant="contained" color="primary" className={classes.button} type="submit" onClick={this.submit}>
|
||||||
|
Done
|
||||||
|
</Button>
|
||||||
|
<Button variant="contained" color="secondary" className={classes.button} type="submit" onClick={onCancelEditing}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
</ValidatorForm>
|
</ValidatorForm>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ void ArduinoJsonJWT::setSecret(String secret){
|
|||||||
* No need to pull in additional crypto libraries - lets use what we already have.
|
* No need to pull in additional crypto libraries - lets use what we already have.
|
||||||
*/
|
*/
|
||||||
String ArduinoJsonJWT::sign(String &payload) {
|
String ArduinoJsonJWT::sign(String &payload) {
|
||||||
unsigned char hmacResult[33];
|
unsigned char hmacResult[32];
|
||||||
{
|
{
|
||||||
#if defined(ESP_PLATFORM)
|
#if defined(ESP_PLATFORM)
|
||||||
mbedtls_md_context_t ctx;
|
mbedtls_md_context_t ctx;
|
||||||
@ -34,15 +34,14 @@ String ArduinoJsonJWT::sign(String &payload) {
|
|||||||
br_hmac_out(&hmacCtx, hmacResult);
|
br_hmac_out(&hmacCtx, hmacResult);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
hmacResult[32] = 0;
|
return encode((char *) hmacResult, 32);
|
||||||
return encode(String((char *) hmacResult));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String ArduinoJsonJWT::buildJWT(JsonObject &payload) {
|
String ArduinoJsonJWT::buildJWT(JsonObject &payload) {
|
||||||
// serialize, then encode payload
|
// serialize, then encode payload
|
||||||
String jwt;
|
String jwt;
|
||||||
serializeJson(payload, jwt);
|
serializeJson(payload, jwt);
|
||||||
jwt = encode(jwt);
|
jwt = encode(jwt.c_str(), jwt.length());
|
||||||
|
|
||||||
// add the header to payload
|
// add the header to payload
|
||||||
jwt = JWT_HEADER + '.' + jwt;
|
jwt = JWT_HEADER + '.' + jwt;
|
||||||
@ -89,27 +88,27 @@ void ArduinoJsonJWT::parseJWT(String jwt, JsonDocument &jsonDocument) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String ArduinoJsonJWT::encode(String value) {
|
String ArduinoJsonJWT::encode(const char *cstr, int inputLen) {
|
||||||
// prepare encoder
|
// prepare encoder
|
||||||
base64_encodestate _state;
|
base64_encodestate _state;
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
base64_init_encodestate_nonewlines(&_state);
|
base64_init_encodestate_nonewlines(&_state);
|
||||||
size_t encodedLength = base64_encode_expected_len_nonewlines(value.length()) + 1;
|
size_t encodedLength = base64_encode_expected_len_nonewlines(inputLen) + 1;
|
||||||
#elif defined(ESP_PLATFORM)
|
#elif defined(ESP_PLATFORM)
|
||||||
base64_init_encodestate(&_state);
|
base64_init_encodestate(&_state);
|
||||||
size_t encodedLength = base64_encode_expected_len(value.length()) + 1;
|
size_t encodedLength = base64_encode_expected_len(inputLen) + 1;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// prepare buffer of correct length
|
// prepare buffer of correct length
|
||||||
char buffer[encodedLength];
|
char buffer[encodedLength];
|
||||||
|
|
||||||
// encode to buffer
|
// encode to buffer
|
||||||
int len = base64_encode_block(value.c_str(), value.length(), &buffer[0], &_state);
|
int len = base64_encode_block(cstr, inputLen, &buffer[0], &_state);
|
||||||
len += base64_encode_blockend(&buffer[len], &_state);
|
len += base64_encode_blockend(&buffer[len], &_state);
|
||||||
buffer[len] = 0;
|
buffer[len] = 0;
|
||||||
|
|
||||||
// convert to arduino string
|
// convert to arduino string
|
||||||
value = String(buffer);
|
String value = String(buffer);
|
||||||
|
|
||||||
// remove padding and convert to URL safe form
|
// remove padding and convert to URL safe form
|
||||||
while (value.charAt(value.length() - 1) == '='){
|
while (value.charAt(value.length() - 1) == '='){
|
||||||
|
@ -24,7 +24,7 @@ private:
|
|||||||
|
|
||||||
String sign(String &value);
|
String sign(String &value);
|
||||||
|
|
||||||
static String encode(String value);
|
static String encode(const char *cstr, int len);
|
||||||
static String decode(String value);
|
static String decode(String value);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -43,9 +43,10 @@ Authentication SecurityManager::authenticateRequest(AsyncWebServerRequest *reque
|
|||||||
AsyncWebHeader* authorizationHeader = request->getHeader(AUTHORIZATION_HEADER);
|
AsyncWebHeader* authorizationHeader = request->getHeader(AUTHORIZATION_HEADER);
|
||||||
if (authorizationHeader) {
|
if (authorizationHeader) {
|
||||||
String value = authorizationHeader->value();
|
String value = authorizationHeader->value();
|
||||||
value.startsWith(AUTHORIZATION_HEADER_PREFIX);
|
if (value.startsWith(AUTHORIZATION_HEADER_PREFIX)){
|
||||||
value = value.substring(AUTHORIZATION_HEADER_PREFIX_LEN);
|
value = value.substring(AUTHORIZATION_HEADER_PREFIX_LEN);
|
||||||
return authenticateJWT(value);
|
return authenticateJWT(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return Authentication();
|
return Authentication();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user