Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sanitize.contentAPI not populating relations #14251

Closed
sjoukedv opened this issue Aug 30, 2022 · 7 comments
Closed

sanitize.contentAPI not populating relations #14251

sjoukedv opened this issue Aug 30, 2022 · 7 comments
Labels
issue: bug Issue reporting a bug severity: low If the issue only affects a very niche base of users and an easily implemented workaround can solve source: plugin:users-permissions Source is plugin/users-permissions package status: can not reproduce Not enough information to reproduce

Comments

@sjoukedv
Copy link
Contributor

Bug report

Required System information

  • Strapi version: 4.3.6
  • Database: Postgres

Describe the bug

I am trying to populate relations on the user object by calling api/users?populate=*. Using the custom extension below:

// path: src/extensions/users-permissions/strapi-server.js

const utils = require('@strapi/utils');
const { sanitize } = utils;

const getService = (name) => {
  return strapi.plugin('users-permissions').service(name);
};

export default (plugin) => {
    const sanitizeOutput = (user, ctx) => {
      // <-- here the user contains all the relations
      const schema = strapi.getModel('plugin::users-permissions.user');
      const { auth } = ctx.state;
    
      return sanitize.contentAPI.output(user, schema, { auth });
    };
  
    plugin.controllers.user.me = async (ctx) => {
      const authUser = ctx.state.user;
      const { query } = ctx;
  
      if (!authUser) {
        return ctx.unauthorized();
      }
  
      const user = await getService('user').fetch(authUser.id, query);
       
      ctx.body = await sanitizeOutput(user, ctx);
    };
  
    plugin.controllers.user.find = async (ctx) => {
      const users = await getService('user').fetchAll(ctx.query);
  
      ctx.body = await Promise.all(users.map((user) => sanitizeOutput(user, ctx)));
    };

    return plugin;
  };
schema
{
  collectionName: 'up_users',
  info: {
    name: 'user',
    description: '',
    singularName: 'user',
    pluralName: 'users',
    displayName: 'User'
  },
  options: { draftAndPublish: false, timestamps: true },
  attributes: {
    username: {
      type: 'string',
      minLength: 3,
      unique: true,
      configurable: false,
      required: false
    },
    email: {
      type: 'email',
      minLength: 6,
      unique: true,
      configurable: false,
      required: true
    },
    provider: { type: 'string', configurable: false },
    password: {
      type: 'password',
      minLength: 6,
      configurable: false,
      private: true
    },
    resetPasswordToken: { type: 'string', configurable: false, private: true },
    confirmationToken: { type: 'string', configurable: false, private: true },
    confirmed: { type: 'boolean', default: false, configurable: false },
    blocked: { type: 'boolean', default: false, configurable: false },
    role: {
      type: 'relation',
      relation: 'manyToOne',
      target: 'plugin::users-permissions.role',
      inversedBy: 'users',
      configurable: false
    },
    source: { type: 'enumeration', enum: [Array], required: true },
    subscriptions: {
      type: 'relation',
      relation: 'oneToMany',
      target: 'api::subscription.subscription',
      mappedBy: 'user'
    },
    favoriteSessions: {
      type: 'relation',
      relation: 'oneToMany',
      target: 'api::session.session'
    },
    uuid: { type: 'uid', required: false, minLength: 35 },
    newsletterOptIn: { type: 'boolean', default: false, required: false },
    firstName: { type: 'string' },
    lastName: { type: 'string' },
    createdAt: { type: 'datetime' },
    updatedAt: { type: 'datetime' },
    createdBy: {
      type: 'relation',
      relation: 'oneToOne',
      target: 'admin::user',
      configurable: false,
      writable: false,
      visible: false,
      useJoinTable: false,
      private: true
    },
    updatedBy: {
      type: 'relation',
      relation: 'oneToOne',
      target: 'admin::user',
      configurable: false,
      writable: false,
      visible: false,
      useJoinTable: false,
      private: true
    }
  },
  config: {
    attributes: {
      resetPasswordToken: [Object],
      confirmationToken: [Object],
      provider: [Object]
    }
  },
  kind: 'collectionType',
  __filename__: 'schema.json',
  __schema__: {
    collectionName: 'up_users',
    info: {
      name: 'user',
      description: '',
      singularName: 'user',
      pluralName: 'users',
      displayName: 'User'
    },
    options: { draftAndPublish: false, timestamps: true },
    attributes: {
      username: [Object],
      email: [Object],
      provider: [Object],
      password: [Object],
      resetPasswordToken: [Object],
      confirmationToken: [Object],
      confirmed: [Object],
      blocked: [Object],
      role: [Object],
      source: [Object],
      subscriptions: [Object],
      favoriteSessions: [Object],
      uuid: [Object],
      newsletterOptIn: [Object],
      firstName: [Object],
      lastName: [Object]
    },
    kind: 'collectionType'
  },
  modelType: 'contentType',
  modelName: 'user',
  connection: 'default',
  uid: 'plugin::users-permissions.user',
  plugin: 'users-permissions',
  globalId: 'UsersPermissionsUser',
  actions: undefined,
  lifecycles: undefined
}

Printing the user object before it is passed to sanitize.contentAPI.output(...) shows the object with the relations. After sanitizing the output only the favoriteSessions relation is populated and not the other relations like the default role.

Steps to reproduce the behavior

  1. Add a relation to the user object
  2. Add the extension in src/extensions/users-permissions/strapi-server.js (there is no overriding happening in the snippet above).
  3. Call the api/users/me endpoint

Expected behavior

I am expecting the sanitize function to return the relations on the user object as defined in the schema.

Additional context

There has been issues in the past regarding population see #11957.

@sjoukedv
Copy link
Contributor Author

FYI: I am using an extension to override the controller so that I could enforce the population of fields for plugin.controllers.user.me with

const user = await strapi.entityService.findOne(
    'plugin::users-permissions.user', 
    authUser.id, 
    {
        populate: ['role', ...]
     }
);      

@derrickmehaffy
Copy link
Member

Please see this entry in the documentation: https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/rest/populating-fields.html#relation-media-fields

Specifically:

image

Can you please confirm if you have enabled the find permission for the related content-types you are trying to populate?

@derrickmehaffy derrickmehaffy added issue: bug Issue reporting a bug severity: low If the issue only affects a very niche base of users and an easily implemented workaround can solve status: can not reproduce Not enough information to reproduce source: plugin:users-permissions Source is plugin/users-permissions package labels Aug 31, 2022
@sjoukedv
Copy link
Contributor Author

sjoukedv commented Sep 1, 2022

Please see this entry in the documentation: https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/rest/populating-fields.html#relation-media-fields

Specifically:

image

Can you please confirm if you have enabled the find permission for the related content-types you are trying to populate?

@derrickmehaffy That is indeed the culprit. It might be a feature request to allow the population of relations as I might not want to 'open' all the items in a collection to a role, but just the ones that are linked (by means of a relation) to the up-user in this case. Right now, I will have to overwrite the find handler for this.

e.g. I have a user with collection item id 1 en 2 linked to it. Assigning the find permissions to the collection means it can also retrieve the item with id 3 by just sending a request to api/<collection>/3

@derrickmehaffy
Copy link
Member

It's a use-case we are widely aware of but cannot fix with the current plugin and we are evaluating building a new one but no ETA on that. Basically it resolves around this feature: https://feedback.strapi.io/feature-requests/p/field-level-permissions-end-user-management

Since you confirmed it I'll mark this as closed but I'll mention this issue on that FR

@sjoukedv
Copy link
Contributor Author

sjoukedv commented Sep 1, 2022

Thanks for the quick reply!

Indeed this relates to the up-plugin. In hindsight, this also explains why I cannot POST an entry to a collection with a relation to which the role does not have access to. For up-users I need the find or findOne permission to do this, but this means that e.g. every authenticated user is able to fetch other user's details. I am hoping this gets more priority soon because the find access rights should definitely not be needed when creating an entry where you pass the ID of whatever you find.

@derrickmehaffy
Copy link
Member

Thanks for the quick reply!

Indeed this relates to the up-plugin. In hindsight, this also explains why I cannot POST an entry to a collection with a relation to which the role does not have access to. For up-users I need the find or findOne permission to do this, but this means that e.g. every authenticated user is able to fetch other user's details. I am hoping this gets more priority soon because the find access rights should definitely not be needed when creating an entry where you pass the ID of whatever you find.

You can create a route policy or middleware to restrict actual find access but indeed the permission isn't ideal

@strapi-bot
Copy link

This issue has been mentioned on Strapi Community Forum. There might be relevant details there:

https://forum.strapi.io/t/strapi-v4-search-by-slug-instead-id/13469/43

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
issue: bug Issue reporting a bug severity: low If the issue only affects a very niche base of users and an easily implemented workaround can solve source: plugin:users-permissions Source is plugin/users-permissions package status: can not reproduce Not enough information to reproduce
Projects
Archived in project
Development

No branches or pull requests

3 participants