Has anyone seen this message? I have updated to 1.7.1 and my request doesn't work anymore
/api/round-trips/62be80ce39974d8a7aaa22c1?publish=true&locale=all
I don't have API authentication enabled, maybe thats the reason?
so we released a new feature that
validatesqueries
you may be wanting to query on
_status
but are actually querying on
status
which might not exist
(the field that we inject into your config for drafts / autosave is called
_status
)
hmm, let me check
This is interesting - I'm logged-in in the yellow browser but not in incognito
queries also now respect access control
for security reasons
you should not be able to query a field if field-level access control prevents you from reading the field
this may be related
hmm, so how can I quickly add a token that can read everything so that our pages are working again? 😅
I think I have
access: {
read: () => true,
},
everywhere, but I'm double checking
aha! Found it!
Thanks
@364124941832159242beautiful
I was migrating from a custom implementation to
versions
so these hard-rules are a bit unfortunate because there maybe docs that are not migrated or still have the old value
well, we don't throw any errors based on if a doc
has a valuewe only will throw where query validation errors if you 1. don't have access to the field or 2. the field doesn't exist in the schema itself
Hi, i keep getting the same error. what i'm doing wrong?
that's my code
const DataRooms: CollectionConfig = {
slug: "sponsors",
fields: [...],
access: DefaultAuthConfig,
...
}
const DefaultAuthConfig = {
create: ({ req }) => {
return DenyApiKeyOrNotLogged(req);
},
read: ({ req }) => {
` return DenyApiKeyOrNotLogged(req, true);
},
...
}
const DenyApiKeyOrNotLogged = (req: any, isReading?: boolean) => {
if (req.user == null) {
return false;
} else {
if (req.user.role == "APIKEY" && req.user.apiKey) {
if (isReading) {
const canRead =
req.collection?.config?.slug != null &&
readableCollections.includes(req.collection.config.slug);
if (canRead)
return {
or: [
{
_status: {
equals: "published",
},
},
{
_status: {
exists: false,
},
},
],
};
return canRead;
} else {
return false;
}
} else if (req.user.role == "administrator") {
return true;
}
}
};
if i remove the "querying" filter on access control, i'm able to querying collection by request querystring like
?where[_status][equals]=published
First of all I'd say that you could improve the readability by using early returns, so the level of nesting descreases.
Is
canRead
true?
const denyApiKeyOrNotLogged = (req: any, isReading?: boolean) => {
if (req.user === null) {
return false;
}
if (req.user.role == 'administrator') {
return true;
}
if (!isReading) {
return false;
}
if (req.user.role === 'APIKEY' && req.user.apiKey) {
const canRead =
req.collection?.config?.slug !== null &&
readableCollections.includes(req.collection.config.slug);
if (!canRead) {
return false;
}
return {
or: [
{
_status: {
equals: 'published'
}
},
{
_status: {
exists: false
}
}
]
};
}
};
thanks for the refactor 😉
btw, same error is returned
{
"errors": [
{
"name": "QueryError",
"message": "The following paths cannot be queried: _status, _status",
"data": [
{
"path": "_status"
},
{
"path": "_status"
}
]
}
]
}
in that case, yes. it is true
Hmm, not sure then
and
_status: {
exists: false,
},
works as well?
What happens if you return true after canRead? 😄 (just for testing purposes)
same error
user with API-KEY can access collection
and
if (req.user.role === 'APIKEY' && req.user.apiKey) {
return true;
}
i tryed to filter on that single collection too
const DataRooms: CollectionConfig = {
slug: "sponsors",
fields: [...],
access: {
read ({ req: { user } }) => {
if (user) {
return true;
}
return {
_status: {
equals: 'published'
}
}
}
}
...
}
Doesn't work either?
Are you using the latest version of payload?
i'm using 1.7.4
"dependencies": {
"@payloadcms/plugin-seo": "^1.0.14-canary.0",
"@websolutespa/payload-plugin-localization": "^0.0.3",
"axios": "^1.3.6",
"express": "^4.17.1",
"payload": "^1.7.4",
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
nothing seems work.
i'll try to update payload
payload update 1.11.3
Yep, it works
update: it works always, except for apiKey users
const DataRooms: CollectionConfig = {
slug: "sponsors",
access: {
read: ({ req: { user } }) => {
if (user == null) return false;
return {
or: [
{
_status: {
equals: "published",
},
},
{
_status: {
exists: false,
},
},
],
};
},
},
...
Then two questions
How do you know what the user is an API user if you have this check
req.user.role === 'APIKEY'
And second question
req.user.apiKey
- how is the apiKey added to the request? Is it a http header, or a query parameter?
apikey is added on header
I added a radio field on my Users collection that specify my "role"
Values can be "administrator", "APIKEY", others
yes, but how does the incoming request know if the role is API user (if we don't have a cookie / JWT or sth else)
you log
req.user.role
and
req.user.apiKey
and see if they are defined?
there's the "enableAPIKey" on User
APIKEY user
{
id: '6453ae2a8200c13a0fe97464',
role: 'APIKEY',
enableAPIKey: true,
apiKey: 'a8861d6e-315a-4351-8fbb-da2447d0838f',
email: 'xxxxxxx@xxxxxxx.xx',
createdAt: '2023-05-04T13:07:54.919Z',
updatedAt: '2023-05-04T13:07:54.919Z',
collection: 'users',
_strategy: 'api-key'
}
administrator user
{
id: '6452232f749f4190c8547ed1',
role: 'administrator',
enableAPIKey: false,
apiKey: '1936273e-c16a-413a-9aa7-9bbaf536cec8',
email: 'xxxxxxx@xxxxxxx.xx',
createdAt: '2023-05-03T09:02:39.814Z',
updatedAt: '2023-07-21T10:08:18.712Z',
collection: 'users',
_strategy: 'local-jwt'
}
ah ok, thank you!
and if you console.log
req.role.user
and
req.user.apiKey
are they defined?
yes. they're defined
But if this works, then the other case with the api user should work as well
it
should, but it doesn't 😕
const DataRooms: CollectionConfig = {
slug: "sponsors",
access: {
read: ({ req: { user } }) => {
if (user == null) return false;
if (req.role.user && req.user.apiKey) {
return {
or: [
{
_status: {
equals: "published",
},
},
{
_status: {
exists: false,
},
},
],
}
};
},
},
...
this doesn't work?
wait wait...
req is PayloadRequest
role and apiKey are properties of user
if (user.role && user.apiKey) {
You are right
Technically if both are defined (as you said) then there should be no difference
just another conditional check between
exactly
same error
Error means - no filtering?
{
"errors": [
{
"name": "QueryError",
"message": "The following paths cannot be queried: _status, _status",
"data": [
{
"path": "_status"
},
{
"path": "_status"
}
]
}
]
}
Is draft enabled?
versions: {
// maxPerDoc: 50,
drafts: true,
},
have you saved (and published) a document after enabling it?
i added all documents after enabling it
just to be sure, the error happens with and without this line:
if (user.role && user.apiKey) { ... }
correct
Interesting
As
@364124941832159242wrote
we only will throw where query validation errors if you 1. don't have access to the field or 2. the field doesn't exist in the schema itself
If you have drafts and (versions) enabled, I can't see why it should not work
import { CollectionConfig } from 'payload/types';
export const Pages: CollectionConfig = {
slug: 'pages',
access: {
read: ({ req }) => {
// If there is a user logged in,
// let them retrieve all documents
if (req.user) return true;
// If there is no user,
// restrict the documents that are returned
// to only those where `_status` is equal to `published`
return {
_status: {
equals: 'published',
},
};
},
},
versions: {
drafts: true
},
//.. the rest of the Pages config here
}
Like this example
Out of curiosity - if you apply the example code, you still see the same error? (just once, not twice)
Can I just jump in here and have you clarify that, the collection variable name is “DataRooms”, but the
slugis “sponsors”, is that correct? i.e. you don’t have 2 collections with the same slug do you? (Can’t remember if we throw an error or not if this happens)
yes, it is correct. collection "sponsors" is unique
update
i'm trying filter another collection.
this is my "auth config" now
...
switch (req.user.role) {
case "administrator":
return true;
case "APIKEY":
if (!req.user.enableAPIKey || req.user.apiKey == null) return false;
if (!isReading) return false;
if ( req.baseUrl.startsWith("/media") || req.baseUrl == "/api/globals/siteconfigs" )
return true;
if (
req.collection?.config?.slug != null &&
APIKeyAllowedCollections.includes(req.collection.config.slug)
) {
switch (req.collection.config.slug) {
case "vetrine":
let today = new Date();
return {
and: [
{
startVisibility: {
less_than_equal: today,
},
},
{
endVisibility: {
greater_than_equal: today,
},
},
],
};
default:
return true;
}
}
i'm working on the auto filter on items in collection "vetrine"
apiKey users need to get the list of vetrine where today is between startVisibility and endVisibility
that's the result
{
"errors": [
{
"name": "QueryError",
"message": "The following paths cannot be queried: startVisibility, endVisibility",
"data": [
{
"path": "startVisibility"
},
{
"path": "endVisibility"
}
]
}
]
}
UPDATE
i found out that if a "vetrina" doesn't match the filter, everything works
example 1:
today = 2023-08-03;
startVisibility = 2023-09-01;
endVisibility = 2023-09-10
->
{
"docs": [],
"totalDocs": 0,
"limit": 10,
"totalPages": 1,
"page": 1,
"pagingCounter": 1,
"hasPrevPage": false,
"hasNextPage": false,
"prevPage": null,
"nextPage": null
}
example :2
today = 2023-08-03;
startVisibility = 2023-08-01;
endVisibility = 2023-09-10
->
{
"errors": [
{
"name": "QueryError",
"message": "The following paths cannot be queried: startVisibility, endVisibility",
"data": [
{
"path": "startVisibility"
},
{
"path": "endVisibility"
}
]
}
]
}
UPDATE
I can filter by doc.id
i'm still not able to filter by other fields
that's my collection
const Vetrine: CollectionConfig = {
slug: "vetrine",
admin: {
useAsTitle: "title",
},
access: DefaultAuthConfig,
fields: [
{
type: "row",
fields: [
{
name: "badge",
type: "text",
},
{
name: "target",
required: true,
type: "text",
},
],
},
{
type: "row",
fields: [
{
name: "imageUrl",
type: "text",
},
{
name: "customImage",
type: "upload",
relationTo: "media",
},
],
},
{
name: "title",
required: true,
localized: true,
type: "text",
},
{
name: "text",
localized: true,
type: "text",
},
{
type: "row",
admin: {
position: "sidebar",
},
fields: [
{
name: "startVisibility",
type: "date",
required: true,
admin: {
date: {
pickerAppearance: "dayAndTime",
timeFormat: "HH:mm",
displayFormat: "dd MMM yyyy - HH:mm",
},
},
},
{
name: "endVisibility",
type: "date",
required: true,
admin: {
date: {
pickerAppearance: "dayAndTime",
timeFormat: "HH:mm",
displayFormat: "dd MMM yyyy - HH:mm",
},
},
},
],
},
],
versions: {
maxPerDoc: 1,
drafts: true,
},
};
i can filter by id, title
i cannot filter by startVisibility, badge...
idk if this can help
guys, i think i've found the problem.
payload let me filter by title and id because also collection "Tribunali" (in relationship) has id and title.
i was debugging find.js and buildQuery.js and notice that
it gives me error because startVisibility is not a field of collection "tribunali"
same thing for the past messages.
collection sponsors has status field, but the collection of relationship field, doesn't have status
i want to filter only the collection "vetrine"
how can i do it?
I wonder if we have a bug where nesting the field in the rows is breaking query validation. If you move the visibility fields out of the row wrapping them does it work?
This might have been fixed in an earlier version, I can't remember.
I tried but no. It doesn't work
Oh! I see the issue.
You are using one access function for many collections.
In that code you shared you have a switch on the req.collection.
This doesn't work because this logic is applying to relations that don't have those fields.
That's why it works when results don't match.
Your media collection doesn't have startVisibility.
The req.colletion is always bound to the API of the request so you are getting incorrect access based queries returned for relationships.
Did that make sense
@443492031021580288?
Exactly
The "auth" function is used in all my collections
Debugging those files, I got to your point.
Im now trying to replace the req.collection.config.access.read, based on slug, but nothing... That is another wrong workaround.
So, Is It possibile having a single access function filtering results based on collection or should i implement those access functions in every collection?
You could verify that this the issue by removing the relationship field and making sure your query works.
I'm trying to think of a way you could do the switch on a different property...
You would have to wrap your access function in a function that passes in the slug.
That way in your collection config you can call the getDefaultAccess('vetrine') and use that arg instead of the req.collection to get the slug
i tried
it works
collection ->
...
access: DefaultAuthConfig("contentpages"),
...
const DefaultAuthConfig = (col: string) => {
return {
...
read: ({ req }) => {
return DenyApiKeyOrNotLogged(req, col, true);
},
...
}
const DenyApiKeyOrNotLogged = (req: any, coll: string, isReading?: boolean) => {
...
switch (coll) {
case "vetrine":
let today = new Date();
return {
and: [
{
startVisibility: {
less_than_equal: today,
},
},
{
endVisibility: {
greater_than_equal: today,
},
},
],
};
default:
return true;
}
...
}
🎉
Star
Discord
online
Get dedicated engineering support directly from the Payload team.