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