How To Implement Multi-Tenant In Loopback - multi-tenant

We're using Loopback for our REST endpoints and need to implement a multi-tenant capability. All of our tables have 'tenantid'. We have our own user model and do not use the Loopback user model. Is there a way to capture all inbound requests and verify or update the tenantid on-the-fly? The goal is to ensure that all 'get' requests be appended with something like 'filter[where][tenantid]=tenantid' and all save oriented requests have 'tenantid' populated properly.

The solution is to use the 'parse' middleware in your server.js and modify the filter for all requests on the fly:
app.middleware('parse', parse)
function parse(req, res, next) {
// Add an 'and' filter that specifies the tenant in all requests.
req.query.filter.where.and.push({ tenantid: 283 });
}

Related

Authorize based on field value in another document in AppSync GraphQL custom authentication via Lambda Resolver

I am new to Amplify Datastore & AppSync w/ GraphQL, but in Firestore, you can write an auth rule like: allow delete: if request.auth != null && get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true [https://firebase.google.com/docs/firestore/security/rules-conditions#access_other_documents] -> which would grab the document at /users/{id}/ and I can access the admin field to write a security rule logic.
How do you do the same in a Lambda function resolver?
I am aware that the solution may involve a Lambda resolver (https://stackoverflow.com/a/68581796/9824103) but I cannot find any reference to reading a specific document and doing logic to authorize or deny an operation based on a field value in a document. I am only asking how to do this specific thing. Thank you!
I followed https://docs.amplify.aws/cli/graphql/authorization-rules/#custom-authorization-rule to create a custom authorization rule via adding the #rule directive: type MyModel #model #auth(rules: [{ allow: custom }]) and.. although the lambda function isn't really getting called when I try to write a listMyModel or createMyModel (any hints as to why that would be great), I am focused on writing the lambda function to read query a document and check a certain field to meet my custom auth condition.
fyi, I am using Flutter based amplify-cli.

Verify graphql query

I'm building a simple platform using graphql as api gateway and a frontend that send some queries to this api, I'm blocked on how can I validate a query before run it to avoid malicious query to be ran. I was thinking to use persistgraphql but I just noticed that is now archived so I'm not sure if it's a good idea to use it, the second problem is that the api and the frontend are in 2 different repo so I didn't find yet a solution to whitelisting the query in the frontend and use this whitelist in the api...what's the best solution to whitelist a query with graphql?
If your concern is limiting access to certain fields based on who is making the request, then you should implement some kind of authorization strategy. You can populate the context with information about the logged in user and then use this information inside your resolvers for the fields you want to protect to determine whether the value of the field should be returned or not.
const resolvers = {
User: {
somePrivateField: (user, args, ctx) => {
// Make sure the request is from a logged in user and the user making the
// request is the same as the requested user OR the user is an admin
if (ctx.user && ( ctx.user.id === user.id || ctx.user.isAdmin )) {
return user.somePrivateField
}
// throw an error or just return null or undefined to resolve the field to
// null in the event authorization fails
}
}
}
More sophisticated strategies are possible using directives or existing libraries like graphql-shield.
Of course, certain fields that may exist on your database model -- like passwords -- should probably never be exposed in your API in the first place.

Asp.net core response caching by country

I need to use use response caching for certain controller actions based on the country the request is coming from.
I have figured out how to get the country code from the request (it involves reading from a database, expensive, so I want to do this just for the actions that require a country), but I am not sure how to do the caching part.
I am thinking of writing a middleware (e.g., named CountryResolver)that will run before the response caching middleware and set the country SOMEHOW in the request and have the Response Cache middleware vary by country.
app.UseCountryResolver();
app.UseResponseCaching();
There are two main problems I am facing:
Problem 1-
I need that middleware not to run for every request, but only for some requests that are routed to country-specific actions. I am thinking of annotating such country-specific actions via a custom attribute (e.g., [CountryRequired]).
[CountryRequired]
[ResponseCache(VaryByQueryKeys = new string[] { "country"}]
public IActionResult MyAction()
{
However I don't know how the middleware can pickup the actions that have such annotation so it can decide whether to lookup the country or not.
Problem 2- "...set the country SOMEHOW in the request"
I was thinking of using VaryByQueryKeys and have the middleware set the country to a query string key named "country" BUT HttpContext.Request.Query collection is readonly. So I am not sure what other mechanism can I use for this.
Any help is very appreciated.

Setting the route programmatically in Spring Cloud Netflix Zuul

I have created two AWS Beanstalk envs, each with their own version of the applications. The urls for these envs are https://beta.myserver.com/v1073 and https://beta.myserver.com/v1084. These urls point to the load balancer.
Now I also have a Zuul implementation that have the following configurations.
zuul:
routes:
beta:
path: /api/**
serviceId: beta-root
strip-prefix: false
sensitive-headers: Cookie,Set-Cookie
ribbon:
eureka:
enabled: false
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 5000
beta-root:
ribbon:
listOfServers: https://beta.myserver.com
Request for my application must have a version header. I have a Zuul "pre" filter that inspect this header value. The goal is to route the request based on the header value.
I have been able to intercept the request, but have not been able to route it from the filter. The code snippet is below. After running the code the request still tries to go https://beta.myserver.com/api/...
#Override
public Object run() {
/* Logic to get version header etc */
/* Set the new route */
Map<String, ZuulRoute> routes = zuulProps.getRoutes();
ZuulRoute currentRoute = routes.get("beta-root");
currentRoute.setLocation("https://beta.myserver.com/v1073");
/* Refresh the route */
routeLocator.getRoutes();
logger.warn("Current Route:" + currentRoute.getLocation());
return null;
}
Any suggestion how to resolve this issue?
What you really need to do is that changing requestURI, not server location in your case. You can easily do that like below.
First, your filter's order should be bigger than PreDecorationFilter's order that is currently 5 in Dalston release. (You need to check the value from the release you're using).
PreDecorationFilter processes request header and fill necessary info into RequestContext. And this RequestContext will be used to define actual URL for your request. requestURI is the key that you need to change. In you case, you can append or modify requestURI based on headers. The below is snippet for your pre-filter.
#Override
public int filterOrder() {
return 6;
}
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
// override request URI
ctx.set("requestURI", "/v1073" + ctx.get("requestURI"));
return null;
}
Your original implementation seems to have some problems. First, you're trying to change location in ZuulProperties route. This object is shared for all requests. If you change it just for a specific request, other request that may have different headers could be routed to wrong location.
Second, you're setting location property in ZuulRoute object like below.
currentRoute.setLocation("https://beta.myserver.com/v1073");
Originally location property value is your service-id -beta-root - with your configuration. If you change it with a specific url that has 'http' or 'httpsprefix, originalRibbonRoutingFilterwill not work. Instead,SimpleRoutingHostFilterwill process your request. It means that your not-modified requests will be handled byRibbonRoutingFilterand modified requests will be handled bySimpleHostRoutingFilter`.
Update : To route different hosts based on Http Header
If you want to route to different hosts based on http headers, there are several ways to do that.
Case 1: In case that you are using Ribbon (and RibbonRoutingFilter)
The following feature only works on Edgware.SR1 and later version. From Eddware.SR1, you can put specify value for loadbalancer called FilterConstants.LOAD_BALANCER_KEY in RequestContext. This value will be passed into Ribbon load balancer. You can put any value(any object) that you want in your prefilter. If you want to change route based on a special http header, you can do that in your custom prefilter.
And then define your own IRule implementation for Ribbon.LOAD_BALANCER_KEY will be given to your IRule implementation. Therefore you can choose the specific server from the list of server that Ribbon has based on the value of LOAD_BALANCER_KEY that you set.
You can find brief documentation here.
You can find sample code from the test case in PR. (RibbonRoutingFilterLoadBalancerKeyIntegrationTests.java)
Case 2: In case that you are using SimpleHostRoutingFilter (without Ribbon)
If you specify url instead of serviceId in zuul's route properties, the request will be routed by SimpleHostRoutingFilter without Ribbon.
SimpleHostRoutingFilter just use the host address by the below code
RequestContext.getCurrentContext().getRouteHost();
This value is set by PreDecorationFilter. So you can change this info in your prefilter.
Make your own custom prefilter that has the order value between PreDecorationFilter's and SimpleHostRoutingFilter's.
Inside your filter, check route host. If it is any known host that you want to change it based on HTTP header, change the route host via RequestContext.getCurrentContext().setRouteHost based on Http Header.
In case of the second approach, I didn't try to do that by myself. It's just theoretical solution that I think.
The problem of the second approach is that it is using SimpleHostRoutingFilter. SimpleHostRoutingFilter doesn't make any HystrixCommand for the request, so you can't use any circuit breaker features that is provided by Hystrix inside Zuul. If you are using Edgware release, the first approach is better as I think.

Whats the point of composing middleware in Koa?

I am diving into Koa2 and I see koa-compose. I get that I give it middlewares and it returns one, but why? What is the benefit of having multiple middleware wrapped as one instead of just adding them separately?
app.use(compose(m1, m2))
vs:
app.use(m1)
app.use(m2)
KoaJS uses koa-compose underneath (here), so app.use(compoase([m1,m2])); and app.use(m1); app.use(m2); are the same. Using koa-compose explicitly can give more power for customization. Following is one such case:
Adding middlewares through app.use(middleware), will cause all of the middlewares to be executed upon each request in the specified order. But if you want to selectively run different set of middlewares for each route (or in a different order), you can use explicitly use koa-compose to create specialized middleware stacks for each route.
var app = require('koa')();
var router = require('koa-router')();
var compose = require('koa-compose');
var allMiddlewares = compose([m1,m2,m3]);
router.get('/', allMiddlewares);
// selectively enable logging middleware for this route
router.get('/test', compose(logger, allMiddlewares));
app
.use(router.routes())
.use(router.allowedMethods());
I had the same questions of why we need to use koa-compose, since koa itself can handle multiple middlewares. But recently I have been working on the authentication part of my koa server.
I have to check if user is authenticated and sometimes I need to check if user role meets the requirement. In that case, I have two middlewares one is called isAuthenticated, another is hasRoles
Some routes expose to any user that is authenticated, so I can do
.get('/', auth.isAuthenticated, handler())
But for routes need to check if user role meets the requirement, I need to do
.get('/', auth.isAuthenticated, auth.hasRole('admin'), handler())
When I have other authentication middlewares, the middlewares I put in the route becomes pretty long.
I am benefited by using koa-compose, since in my case I can chain the isAuthenticated and hasRoles middlewares together.
requiresRole(role) {
return compose([isAuthenticated, hasRole(role)])
}
.get('/', auth.requiresRole('admin'), handler())
It's neat and less errors.

Resources