Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

strapi - restrict user to fetch only data related to him

Tags:

koa

strapi

Usually, a logged-in user gets all entries of a Content Type.

I created a "snippets" content type (_id,name,content,users<<->>snippets)

<<->> means "has and belongs to many" relation.

I created some test users and make a request: curl -H 'Authorization: Bearer eyJ...' http://localhost:1337/snippets/

Main Problem: an authenticated user should only see the entries assigned to him. Instead, a logged-in user gets all snippets, which is bad.

How is it possible to modify the fetchAll(ctx.query); query to take that into account so it does something like fetchAll(ctx.state.user.id); at the /-route->find-method ?

The basic find method is here:

find: async (ctx) => {

    if (ctx.query._q) {
      return strapi.services.snippet.search(ctx.query);
    } else {
      return strapi.services.snippet.fetchAll(ctx.query);
    }
},

Sub-Question: Does strapi even know which user is logged in when I do Bearer-Token Authentication ?

like image 359
sgohl Avatar asked Nov 12 '18 15:11

sgohl


People also ask

How to retrieve a user from the strapi database?

That way, you should be able to retrieve the user info you need. Now, you can simply call the callback function with the username and email of your user. That way, Strapi will be able to retrieve your user from the database and log you in. Now, we need to configure our 'model' for our new provider.

Can I connect my strapi V3 application to a pre-existing database?

Strapi applications are not meant to be connected to a pre-existing database, not created by a Strapi application, nor connected to a Strapi v3 database. The Strapi team will not support such attempts. Attempting to connect to an unsupported database may, and most likely will, result in lost data.

How many records can be returned from strapi API?

Default Strapi API is limiting to maximum 100 records only. We have a Content-Type with more than 100 records but since Strapi has default limit of 100, the API is only returning max 100 records.

How to configure a strapi provider?

Configure the new provider in the Provider.js file at the getProfile function. The getProfile takes three params: provider: The name of the used provider as a string. query: The query is the result of the provider callback. callback: The callback function who will continue the internal Strapi login logic.


3 Answers

You could set up a /snippets/me route under the snippets config.

That route could call the Snippets.me controller method which would check for the user then query snippets based on the user.

So in api/snippet/config/routes.json there would be something like :

    {
      "method": "GET",
      "path": "/snippets/me",
      "handler": "Snippets.me",
      "config": {
        "policies": []
      }
    },

Then in the controller (api/snippet/controllers/Snippet.js), you could do something like:

  me: async (ctx) => {
    const user = ctx.state.user;    
    if (!user) {
      return ctx.badRequest(null, [{ messages: [{ id: 'No authorization header was found' }] }]);
    }

    const data = await strapi.services.snippet.fetch({user:user.id});  

    if(!data){
      return ctx.notFound();
    }

    ctx.send(data);
  },

Then you would give authenticated users permissions for the me route not for the overall snippets route.

like image 194
Moses Avatar answered Oct 17 '22 05:10

Moses


A possibility would be to extend the query used by find and findOne in the controllers with a restriction regarding the logged in user. In this case you might also want to adapt the count endpoint to be consistent.

This would result in:

withOwnerQuery: (ctx, ownerPath) => {
  const user = ctx.state.user;
  if (!user) {
    ctx.badRequest(null, [
      { messages: [{ id: "No authorization header was found" }] },
    ]);
    return null;
  }
  return { ...ctx.query, [ownerPath]: user.id };
};

find: async (ctx) => {
    ctx.query = withOwnerQuery(ctx, "owner.id");
    if (ctx.query._q) {
      return strapi.services.snippet.search(ctx.query);
    } else {
      return strapi.services.snippet.fetchAll(ctx.query);
    }
},

// analogous for for findOne

Depending on your usage of controllers and services you could achieve the same thing via adapting the service methods.

This kind of solution would work with the GraphQL plugin.

like image 36
manuelkruisz Avatar answered Oct 17 '22 06:10

manuelkruisz


The above is correct, except with newer versions of strapi. Use find and not fetch :)

const data = await strapi.services.snippet.find({ user: user.id });

Strapi v3.0.0-beta.20

like image 4
Jeremy Rajan Avatar answered Oct 17 '22 07:10

Jeremy Rajan