cdimascio / express-openapi-validator

🦋 Auto-validates api requests, responses, and securities using ExpressJS and an OpenAPI 3.x specification
MIT License
906 stars 205 forks source link

readOnly fields with data in request throw errors #627

Open Morriz opened 3 years ago

Morriz commented 3 years ago

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.

cdimascio commented 3 years ago

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

Morriz commented 3 years ago

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

tkarls commented 2 years ago

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.

pilerou commented 2 years ago

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

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

pilerou commented 2 years ago

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.

sweethuman commented 2 years ago

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

pilerou commented 2 years ago

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.

sweethuman commented 2 years ago

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.

cdimascio commented 1 year ago

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.

Morriz commented 1 year ago

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.

pilerou commented 1 year ago

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 :

Depending on your answer, I can make the PR.

cdimascio commented 1 year ago

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

doppio commented 9 months ago

@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.

cdimascio commented 9 months ago

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

pilerou commented 7 months ago

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 commented 7 months ago

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.

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.