Comments (2)
Hi, I'll update this comment progressively, here are my thoughts:
Easy implementation
Fork Vulcan, and focus on the following parts:
- the GraphQL context creation function. You can extend it at will. It is based on the current request, and can do asynchronous calls. It is quite similar to using a middleware in Express to fetch data about the session.
- the default resolvers and mutations
The context should:
- If the user has not selected a tenant, fetch the default tenant for this user and put it into the context
- If the user has selected a tenant, read the selected tenantId in the request cookies, check that the user can actually access this tenant, and put the tenant in the context.
The resolvers should:
- Filter out document based on the current tenant, eg by altering the user filter or mongo query to guarantee that the tenantId is always respected
The limitation is that you fork Vulcan, but that's honestly not a huge deal. In particular, it is fine to copy the default resolvers and improve them in your app: they are just default, they are not meant to fit any kind of use case you can imagine.
We suppose that:
- the user always has a default tenant
- the user can switch tenant. This means that we store the current tenant in a Cookie client-side, so it is passed alongside all requests.
More generic implementation (for multitenancy but also: draft management, document moderation...)
Forking the context creation is a bit more annoying though. To solve this, we could simply add a callback in the context creation function to allow user to enhance the context.
This opens a lot of possibilities.
We can also add callbacks in the default resolvers to be able to alter the user filter. The difference between that and modifying useMulti
variables for instance, is that the callbacks are run server-side => they are secure.
Filtering at Mongo level
It is also good practices to go higher up if you want to truly ensure that data are filtered correctly, so user A of tenant X can't see data from user B or tenant Y.
Handling "singleton" currentUser in graphql?
How to handle a "singleton" in graphql? For instance, the currentUser will always be THE current user. You don't care about the _id or whatever, because you can have only one current user. However, in my Cypress tests, it messes with caching. For instance, I do 2 queries "getCurrentUser { profile }", "getCurrentUser { activeTenant }". I get a message like "instrument.js?ea14:110 Cache data may be lost when replacing the getCurrentUser field of a Query object." because Apollo has trouble merging both response into the "currentUser" object. GraphQL can't know if you may store multiple current user or just one.
To address this problem (which is not a bug in Apollo Client), either ensure all objects of type CurrentUserType have IDs, or define a custom merge function for the Query.getCurrentUser field, so InMemoryCache can safely merge these objects:**
- https://www.apollographql.com/docs/react/caching/cache-field-behavior/#merging-non-normalized-objects
- https://www.apollographql.com/docs/react/caching/cache-configuration/#generating-unique-identifiers
from vulcan.
Since @EloyID asked me to implement this, here's my view on the subject:
Forking Vulcan is not an option, the cost of maintaining an ever-shifting fork is too big, and Vulcan tries to give opinionated defaults but lets the devs inject their logic where they need. The goal we're trying to reach is not too far from what Vulcan offers, so it should be possible to do without forking Vulcan.
Adding callbacks to modify some objects is probably the right way of doing things. I don't know how we are documenting these however (the debug panel seems to lack a lot of these, not to mention the docs). I'm used to just read the source code, but we can't expect that from every user.
So, I added a callback to update the context in PR #2689 (thanks for the quick followup!). We can easily handle queries and mutations that affect only one document via the permissions. The multi
resolver is a bit more tricky because the "canRead" permission won't work for the totalCount. So we want a way to modify the selector
and options
given to the mongo query.
The legacy collection.getParameters
used callbacks for that, which were not included in the new Connector.filter call.
Currently, in the multi
resolver has the following:
let { selector = {}, options = {}, filteredFields = [] } = isEmpty(terms)
? await Connectors.filter(collection, input, context) // can't be changed dynamically
: await collection.getParameters(terms, {}, context); // can be changed dynamically
So in order to be able to modify selectors dynamically inside Connectors.filter too, I've created PR #2690 .
from vulcan.
Related Issues (20)
- Input filter operator _nin is not yet implemented HOT 7
- Support "reverse belongsTo" relations HOT 1
- query results are undefined during loading (due to breaking change in @apollo/client v3) HOT 11
- Migrate cache to Apollo v3 HOT 2
- Renaming Material UI base form controls HOT 12
- Cannot use SmartForm for creating new documents SmartForm.submitForm
- Smartform shows delete button regardless of collection.permissions.canDelete HOT 3
- RTL support for UI by adding top level class in HTML HOT 4
- objectSpread2 error after running unit test HOT 2
- Nested Schema document update not working HOT 2
- How do we coordinate work among Vulcan contributors? HOT 1
- useCookies is not a function or its return value is not iterable HOT 2
- I can't run the project Vulcan(Lubuntu) HOT 8
- collection.addField not working if schema is simpl-schema HOT 3
- Relations hasMany bug. HOT 12
- Duplicate Item with withMulti HOT 4
- Add className to FormComponentLoader
- Attempting to implement multiple interfaces on a type causes an error HOT 3
- Error: Cannot find module '@graphql-tools/utils'
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from vulcan.