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 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,14 +184,7 @@ 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 | ||||||
| @@ -203,6 +193,14 @@ class ManageUsersForm extends React.Component { | |||||||
|                       </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}> | ||||||
|   | |||||||
| @@ -1,46 +1,51 @@ | |||||||
| 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}> | ||||||
|  |         <Dialog onClose={onCancelEditing} aria-labelledby="modify-user-dialog-title" open={true} scroll="paper"> | ||||||
|  |           <DialogTitle id="modify-user-dialog-title">Modify User</DialogTitle> | ||||||
|  |           <DialogContent> | ||||||
|             <TextValidator |             <TextValidator | ||||||
|               validators={creating ? ['required', 'uniqueUsername', 'matchRegexp:^[a-zA-Z0-9_\\.]{1,24}$'] : []} |               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 '.'"] : []} |               errorMessages={creating ? ['Username is required', "That username already exists", "Must be 1-24 characters: alpha numberic, '_' or '.'"] : []} | ||||||
| @@ -59,7 +64,7 @@ class UserForm extends React.Component { | |||||||
|               label="Password" |               label="Password" | ||||||
|               className={classes.textField} |               className={classes.textField} | ||||||
|               value={user.password} |               value={user.password} | ||||||
|           onChange={handleCheckboxChange('password')} |               onChange={handleValueChange('password')} | ||||||
|               margin="normal" |               margin="normal" | ||||||
|             /> |             /> | ||||||
|             <FormGroup> |             <FormGroup> | ||||||
| @@ -68,12 +73,16 @@ class UserForm extends React.Component { | |||||||
|                 label="Admin?" |                 label="Admin?" | ||||||
|               /> |               /> | ||||||
|             </FormGroup> |             </FormGroup> | ||||||
|         <Button variant="contained" color="primary" className={classes.button} type="submit"> |           </DialogContent> | ||||||
|           Save |           <DialogActions > | ||||||
|  |             <Button variant="contained" color="primary" className={classes.button} type="submit" onClick={this.submit}> | ||||||
|  |               Done | ||||||
|             </Button> |             </Button> | ||||||
|         <Button variant="contained" color="secondary" className={classes.button} onClick={onCancelEditing}> |             <Button variant="contained" color="secondary" className={classes.button} type="submit" onClick={onCancelEditing}> | ||||||
|           Back |               Cancel | ||||||
|             </Button>             |             </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,10 +43,11 @@ 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(); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user