System Information
-
Strapi Version: 4.10.something
-
Operating System: node:16-bullseye docker image
-
Database: postgres
-
Node Version: 16.latest
-
NPM Version:
-
Yarn Version:
So I’m trying to limit what some endpoints output and I managed to do filtering based on some examples pretty well:
const contentType = "api::address.address";
module.exports = createCoreController(contentType,
({
strapi
}) => ({
async find(ctx) {
const { user } = ctx.state;
const {
filters
} = ctx.query;
if (user && typeof user !== 'undefined') {
ctx.query = {
...ctx.query,
filters: {
...filters,
owner: {
id: user.id,
}
}
};
return await super.find(ctx);
} else {
const sanitizedResults = await this.sanitizeOutput([], ctx);
return this.transformResponse(sanitizedResults, {});
}
},
but the single endpoint does not work as well, when I try to do filtering:
async findOne(ctx) {
const { user } = ctx.state;
const { id } = ctx.params;
const { query } = ctx;
if (user && typeof user !== 'undefined') {
const entity = await strapi.service(contentType).findOne(id, { where: { owner: user.id }, populate: { owner: true }, ...query });
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
return this.transformResponse(sanitizedEntity);
} else {
const sanitizedResults = await this.sanitizeOutput({}, ctx);
return this.transformResponse(sanitizedResults, {});
}
},
It seems where portion of the query has no effect past the entered ID. So I’m limited to populating the owner and checking if the ID matches in the code. Yes. I can do that, but it’s more efficient to do it on the database level, and I’m wondering why that part does not work. Am I doing something wrong?
I guess (based on [v4] Filters in `findOne` controller doesn't work · Issue #12208 · strapi/strapi · GitHub) the findOne just does not support filtering.
My current solution was to do this:
async findOne(ctx) {
const { user } = ctx.state;
const { id } = ctx.params;
if (user && typeof user !== 'undefined') {
const entity = await strapi.db.query(contentType).findMany({
where: {
id,
owner: user.id
}
});
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
return this.transformResponse(sanitizedEntity);
} else {
const sanitizedResults = await this.sanitizeOutput({}, ctx);
return this.transformResponse(sanitizedResults, {});
}
},
But that is not ideal either. I would rather return 404 response in the else part, but I’m not sure how to do that exactly.
I guess, I got the result I wanted:
const { createCoreController } = require("@strapi/strapi").factories;
const utils = require('@strapi/utils');
const { NotFoundError, UnauthorizedError } = utils.errors;
const contentType = "api::address.address";
module.exports = createCoreController(contentType,
({
strapi
}) => ({
async findOne(ctx) {
const { user } = ctx.state;
const { id } = ctx.params;
if (user && typeof user !== 'undefined') {
const entity = await strapi.db.query(contentType).findMany({
where: {
id,
owner: user.id
}
});
if (entity && entity.length) {
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
return this.transformResponse(sanitizedEntity);
}
throw new NotFoundError('Not Found');
} else {
throw new UnauthorizedError('Access Forbidden');
}
},
}));
I did try doing the same thing with policies, but that did not work quite as I expected. I created this policy:
'use strict';
/**
* `user-is-owner` policy.
*/
module.exports = () => {
// Add authenticated user to filter.
return async (ctx, next) => {
ctx.query.filters = {
...ctx.query.filters,
owner: ctx.state.user.id,
};
await next();
};
};
Added it to routes
const { createCoreRouter } = require('@strapi/strapi').factories;
module.exports = createCoreRouter("api::address.address", {
config: {
findOne: {
policies: ["global::user-is-owner"]
}
}
});
But then even the places my user was owner started raising 403 errors… Not sure what was going on there.