express-openapi-validator icon indicating copy to clipboard operation
express-openapi-validator copied to clipboard

readOnly fields with data in request throw errors

Open Morriz opened this issue 3 years ago • 16 comments

Describe the bug

Request body with data for fields that are readOnly throws errors.

To Reproduce

Define field as readonly, and send a POST, or PUT with a payload that contains data for a readOnly field.

Actual behavior

An error is thrown that the field is readOnly and should not contain data.

Expected behavior

The field to be discarded.

Morriz avatar Jun 25 '21 14:06 Morriz

thanks @Morriz, would you be interested in submitting a PR?

cdimascio avatar Jul 03 '21 16:07 cdimascio

Ooh, dunno. If it's within my comfort zone then yes ;) But time is not something I have lots of...

Morriz avatar Jul 04 '21 16:07 Morriz

It would probably be a good idea to add support in the validateRequest options object to either fail the request or to remove the properties.

tkarls avatar Apr 05 '22 15:04 tkarls

We could associate the behaviour to removeAdditional options ? Then If the user post a body with data for a readonly field...

  • ... and removeAdditional is set to true, the field is removed
  • ... abd removeAddition is not set or is set to false, an error is thrown as it works today

If it's OK for you, for this behaviour, I can try to fix it

pilerou avatar Jul 21 '22 15:07 pilerou

Other way... easyier... Just changing ajv/index.js code only for request : from

ajv.addKeyword('readOnly', {
            modifying: true,
            compile: (sch) => {
                if (sch) {
                    return function validate(data, path, obj, propName) {
                        const isValid = !(sch === true && data != null);
                        delete obj[propName];
                        validate.errors = [
                            {
                                keyword: 'readOnly',
                                schemaPath: data,
                                dataPath: path,
                                message: `is read-only`,
                                params: { readOnly: propName },
                            },
                        ];
                        return isValid;
                    };
                }
                return () => true;
            },
        });

to

ajv.addKeyword('readOnly', {
            modifying: true,
            compile: (sch) => {
                if (sch) {
                    return function validate(data, path, obj, propName) {
                        //const isValid = !(sch === true && data != null);
                        delete obj[propName];
                        return true;
                    };
                }
                return () => true;
            },
        });

I just tried it. It works.

pilerou avatar Jul 21 '22 16:07 pilerou

If this solution works will there be a PR soon? Also it needs the label bug.

sweethuman avatar Aug 21 '22 14:08 sweethuman

Hi @sweethuman I just pushed in a new branch with a PR based on last revision on Master. Unit tests are OK.

Could you review ? Or only @cdimascio can do it ?

I can make changes if needed.

pilerou avatar Aug 23 '22 07:08 pilerou

I am not a member of the team, or know the project, from my limited experience with this library your code looks good but I have no power and not enough knowledge.

sweethuman avatar Aug 23 '22 09:08 sweethuman

readonly properties should not be provided in the request, hence the error and current behavior is correct

https://swagger.io/docs/specification/data-models/data-types/

Read-Only and Write-Only Properties You can use the readOnly and writeOnly keywords to mark specific properties as read-only or write-only. This is useful, for example, when GET returns more properties than used in POST – you can use the same schema in both GET and POST and mark the extra properties as readOnly. readOnly properties are included in responses but not in requests, and writeOnly properties may be sent in requests but not in responses.

type: object
properties:
  id:
    # Returned by GET, not used in POST/PUT/PATCH
    type: integer
    readOnly: true
  username:
    type: string
  password:
    # Used in POST/PUT/PATCH, not returned by GET
    type: string
    writeOnly: true

If a readOnly or writeOnly property is included in the required list, required affects just the relevant scope – responses only or requests only. That is, read-only required properties apply to responses only, and write-only required properties – to requests only.

cdimascio avatar Nov 25 '22 22:11 cdimascio

So if the behavior is correct we can instead focus on automation that takes this out of the hands of users. So a PR with configuration to strip out the fields from the request/response makes a lot of sense.

Morriz avatar Dec 01 '22 11:12 Morriz

Hello I've been busy since august but I expect to have more time to work on OS. I understand that removing data wouldn't respect the Openapi standard as readonly data shouldn't be sent in request. But, for easier integration, we would like to make it configurable for API developers via a new parameter such as removeAdditional.

@cdimascio Do you agree with this enhancement ? If yes, what do you prefer :

  • also use removeAdditional for this purpose : if removeAdditional is true, readonly field would be removed from the request
  • use a specific new property (ie : removeReadonly)

Depending on your answer, I can make the PR.

pilerou avatar Mar 14 '23 09:03 pilerou

@pilerou validateRequests takes the property removeAdditional. It seems reasonable for the values all and true to remove additional read-only props

cdimascio avatar Apr 30 '23 20:04 cdimascio

@cdimascio Is that something you'd accept a pull request for?

I also wonder if there could be some way to support the removal of readOnly properties but the rejection of requests that include properties that aren't defined in the schema. For my use case, I want the client to be able to provide a modified version of an object that the server sent without needing to remove the readOnly fields -- they should just be removed from the request. However, I want to reject requests with additional fields to make it clear that the request is invalid.

doppio avatar Nov 27 '23 03:11 doppio

Yes, PR is welcome. Note the AJV doc for removeAdditional https://ajv.js.org/options.html#removeadditional

perhaps with express-openapi-valida, we add a new option readonly, that’s removes readonly additional fields

cdimascio avatar Nov 27 '23 13:11 cdimascio

Hello @cdimascio I would like to spend a little time on OSS. At the end, do you prefer to add a new option such as removeReadOnlyOnResponse or to remove the field in ajv/index.ts when removeAdditional is true, all and maybe "failing" ?

I understand that the second way would be better to you but your last comment makes me hesitate.

I can work on a new PR.

pilerou avatar Jan 30 '24 10:01 pilerou

Finally i made it because I was on it :) The same behaviour needed to be done on wirteonly to be consistent. I did it for writeonly fields too.

  • If validateRequest.removeAdditional is equal to true or 'all or 'failing then the fields received in request which are noted as readonly are removed before entering the route
  • If validateResponse.removeAdditional is equal to true or all or failing then the writeonly fields present in the response object are removed from the return flow before sending the response to the client.

ajv/index.ts had been modified to do this behaviour. 2 unit tests had been created in order to check the behaviour when removeAdditional : true Previous unit tests still works as before.

pilerou avatar Jan 30 '24 16:01 pilerou