How to define an ACL policy applied globally for all models in a LoopbackJS application - loopbackjs

Is there a way to define a global ACL which would apply to all models included in the Loopback application?
Ideally, this globally defined ACL could be overriden by an ACL defined in a model JSON file.
Does Loopback supports something like this ?

Try to use the loopback-cli tool:
$ lb acl
? Select the model to apply the ACL entry to: (all existing models)
? Select the ACL scope: All methods and properties
? Select the access type: All (match all types)
? Select the role All users
? Select the permission to apply Explicitly deny access
it will update all your models acl's, and then you can continue with adding new ACL rules.
Otherwise, if you need to do it programmatically, you could add this code somewhere to your boot scripts:
https://github.com/strongloop/loopback/issues/3318
Just replace the Model in the example with the array of your models - assuming that the app is server/server.js, then in app.models you have all the models defined.

Related

AWS Amplify (AppSync + Cognito) Authorization using dynamic groups per organitzation/tenant

I have an AWS Amplify application that has a structure with multi-organizations:
Organization A -> Content of Organization A
Organization B -> Content of Organization B
Let's say we have the user Alice, Alice belongs to both organizations, however, she has different roles in each one, on organization A Alice is an administrator and has more privileges (i.e: can delete content or modify other's content), while on Organization B she is a regular user.
For this reason I cannot simply set regular groups on Amplify (Cognito), because some users, like Alice, can belong to different groups on different organizations.
One solution that I thought was having a group for each combination of organization and role.
i.e: OrganizationA__ADMIN, OrganizationB__USER, etc
So I could restrict the access on the schema using a group auth directive on the Content model:
{allow: group, groupsField: "group", operations: [update]},
The content would have a group field with a value: OrganizationA__ADMIN
Then I could add the user to the group using the Admin Queries API
However, it doesn't seem to be possible to add a user to a group dynamically, I'd have to manually create each group every time a new organization is created, which pretty much kills my idea.
Any other idea on how I can achieve the result I'm aiming for?
I know that I can add the restriction on code, but this is less safe, and I'd rather to have this constraint on the database layer.
Look into generating additional claims in you pre-token-generation handler
Basically you can create an attribute that includes organization role mapping
e.g.
{
// ...
"custom:orgmapping": "OrgA:User,OrgB:Admin"
}
then transform them in your pre-token-generation handler into "pseudo" groups that don't actually exist in the pool.

GCP Bigquery create autorized views by terraform

I have a specific question regarding the authorized views in Bigquery and terraform.
Situation: I have already created the simple terraform script to create some Bigquery datasets, tables, views and an IAM entries also. Especially, I create two datasets (source_dataset and target_dataset), some tables in the source_dataset and views in the target_dataset, which are based on the source_database. The clue is to use Bigquery authorized views to separate permissions - the views should be accessible by the group od viewers, which don't have an access to the original source_dataset, but are still able to query the views.
Question: Is it possible to authorized the views from the terraform code? When i try to use the terraform code, the chicken/egg issue emerge. I know, that it's possible to separate to build configuration - write some code in the terraform and authorize the views after that by the python code, but ideally would be to use 100% terraform only.
Thanks.
Seems the chicken and egg issue has been resolved on the upcoming release:
bigquery dataset view access creates circular dependency #2686
The problem as defined:
The view can't be created because it depends on another dataset,
That other dataset can't be created because it depends on the view
A circular dependency exists.
The resolution:
Once it gets released (it should appear in version 3.17.0) it'll be usable as a google_bigquery_dataset_access resource. Here's a preview of what the docs page will look like: https://github.com/terraform-providers/terraform-provider-google/blob/master/website/docs/r/bigquery_dataset_access.html.markdown

AWS Amplify filter for #searchable annotation

Currently I am using a DynamoDB instance for my social media application. While designing the schema I sticked to the "one table" rule. So I am putting every data in the same table like posts, users, comments etc. Now I want to make flexible queries for my data. Here I found out that I could use the #searchable annotation to create an Elastic Search instance for a table which is annotated with #model
In my GraphQL schema I only have one #model, since I only have one table. My problem now is that I don't want to make everything in the table searchable, since that would be most likely very expensive. There are some data which don't have to be added to the Elastic Search instance (For example comment related data). How could I handle it ? Do I really have to split my schema down into multiple tables to be able to manage the #searchable annotation ? Couldn't I decide If the row should be stored to the Elastic Search with help of the Partitionkey / Primarykey, acting like a filter ?
The current implementation of the amplify-cli uses a predefined python Lambda that are added once we add the #searchable directive to one of our models.
The Lambda code can not be edited and currently, there is no option to define a custom Lambda, you read about it
https://github.com/aws-amplify/amplify-cli/issues/1113
https://github.com/aws-amplify/amplify-cli/issues/1022
If you want a custom Lambda where you can filter what goes to the Elasticsearch Instance, you can follow the steps described here https://github.com/aws-amplify/amplify-cli/issues/1113#issuecomment-476193632
The closest you can get is by creating a template in amplify\backend\api\myapiname\stacks\ where you can manage all the resources related to Elasticsearch. A good start point is to
Add #searchable to one of your model in the schema.grapql
Run amplify api gql-compile
Copy the generated template in the build folder, \amplify\backend\api\myapiname\build\stacks\SearchableStack.json to amplify\backend\api\myapiname\stacks\
Remove the #searchable directive from the model added in step 1
Start editing your new template copied in step 3
Add a Lambda and use it in the template as the resolver for the DynamoDB Stream
Using this approach will give you total control of the resources related to the Elasticsearch service, but, will also require to do it all by your own.
Or, just go by creating a table for each model.
Hope it helps
It is now possible to override the generated streaming function code as well.
thanks to the AWS Support for the information provided
leaved a message on the related github issue as well https://github.com/aws-amplify/amplify-category-api/issues/437#issuecomment-1351556948
All you need is to run
amplify override api
edit the corresponding overrode.ts
change the code with the resources.opensearch.OpenSearchStreamingLambdaFunction.code
resources.opensearch.OpenSearchStreamingLambdaFunction.functionName = 'python_streaming_function';
resources.opensearch.OpenSearchStreamingLambdaFunction.handler = 'index.lambda_handler';
resources.opensearch.OpenSearchStreamingLambdaFunction.code = {
zipFile: `
# python streaming function customized code goes here
`
}
Resources:
[1] https://docs.amplify.aws/cli/graphql/override/#customize-amplify-generated-resources-for-searchable-opensearch-directive
[2]AWS::Lambda::Function Code - Properties - https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#aws-properties-lambda-function-code-properties

Django role based permissions

I'm developing a huge application in django and I need a permission system and I assume that the native user/group permission within django is not sufficient. Here my needs:
The application will be available through multiple departments. In each department there will be nearly the same actions. But maybe an user will be allowed to add a new team member in department A and in department B he is only allowed to view the team list and in the other departments he has no access at all.
I though using a RBAC system would be most appropriate. Roles must also be inheritable, stored in a model an managable through an interface. Any good ideas or suggestions? Regards
What you are looking for is called abac aka Attribute-Based Access Control. It's an evolution of RBAC as an access control model. In RBAC, you define access control in terms of roles, groups, and potentially permissions. You then have to write code within your application to make sense of the roles and groups. This is called identity-centric access control.
In ABAC, there are 2 new elements:
attributes which are a generalization of groups and roles. Attributes are a key-value pair that can describe anyone and anything. For instance, department, member, and action are all attributes.
policies tie attributes together to determine whether access should be granted or denied. Policies are a human-friendly way of expressing authorization. Rather than write custom code in your app, you write a policy that can be centrally managed and reused across apps, databases and APIs.
There are a couple of ABAC languages such as xacml and alfa. Using ALFA, I could write the following policy:
A user will be allowed to add a new team member in department A
In department B he is only allowed to view the team list
In the other departments he has no access at all.
Roles must also be inheritable, stored in a model an managable through an interface.
policyset appAccess{
apply firstApplicable
policy members{
target clause object = "member"
apply firstApplicable
/**
* A user can add a member to a department if they are a manager and if they are assigned to that department.
*/
rule addMember{
target clause role == "manager" and action == "add"
permit
condition user.department == target.department
}
}
}
One of the key benefits of ABAC is that you can develop as many policies as you like, audit them, share them, and not have to touch your application code at all because you end up externalizing authorization.
There are several engines / projects that implement ABAC such as:
AuthZForce (a Java library for XACML authorization)
Axiomatics Policy Server (commercial product - disclaimer: I work there)
AT&T XACML
There are two components to this question:
First, role management. Roles can be achieved through group membership, i.e. departmentA_addMember & departmentB_listMembers. These Groups would have corresponding permissions attached, e.g. "Member | Add" and "Member | View". A department in this context may have more resources included, that require separate permissions. Django allows to extend Objects with custom Permissions.
Second, inheritance. Do I understand you want to have individual Groups being member of other groups? Then this is something Django would require you to implement yourself.
However, should you be looking for a really more complex authentication solution, it may be worthwhile to integrate with 3rd party services through, e.g. django-allauth. There are sure more/other solutions, just to throw in one name.

Relationships and ACL in Baqend

I'm trying to figure out if this is possible with baqend, or even the correct approach to begin with.
I have a bunch of users, using the default user account system that comes with Baqend.
Some of these users will be administrators of a company. A company will have somewhere between 1 and 5 users who are administrators.
There is a separate data class that contains a record for the company and an array of users who are the administrators.
Like this:
{
id: "/db/Companies/123-456-789",
name: "Test Co",
admins: [
{ id: "/db/Users/10", name: "Joe Schmo" },
{ id: "/db/Users/11", name: "Kate Skate" },
{ id: "/db/Users/12", name: "Johny Begood" }
]
}
What is the approach to ensure that only users 10, 11, and 12 can modify the contents of the admins array and whatever else is contained in /db/Companies/123-456-789 ?
Is it as simple as inserting the additional admin's info into the array and also adding that person to the ACL of /db/Companies/123-456-789 at the same time or right after?
Also what is the way to remove a persons ACL? I see how to set it here: https://www.baqend.com/guide/topics/user-management/#permissions but how do we do remove or delete? And what is the difference between explicitly denying that user in the ACL vs that user simply not existing (and I guess by default being denied? Assuming the entire collection is set to NOT be public in the first place).
For our use, just because an administrator leaves does not mean he leaves OUR APP, he might go work for another customer who uses our app and his user account should remain valid, but with no more access to the company record.
I think you already have it quite right: you can add administrators to a company by adding them to the admins array and by explicitly allowing them read and write access in the ACLs. To remove admin rights, you can simply remove the explicit allow rules for the soon-to-be-ex admin.
In Baqend, permissions are enforced like this:
superusers are always allowed.
explicitly denied users and roles are always denied.
if there is no allow rule for a record, access is public. As soon as there is at least one allow rule, only allowed users are granted access.
Since every record is public unless there is at least one allow rule, your record will be protected when you allow access to the administrators. However, it will be public again as soon as you remove the last administrator from the access ruleset. Therefore, it's probably a good idea to always explicitly allow write access to your Baqend app admin, so that there is always at least one allow rule.
let me try to explain how exactly ACLs in Baqend work.
TL;DR
To secure your object /db/Companies/123-456-789 you can simply add an allow rule for each of your three user ids (/db/Users/10. /db/Users/11, /db/Users/12) to the object acls of your company object like this:
db.Companies.load("/db/Companies/123-456-789").then(function(company) {
company.allowWriteAccess("/db/Users/10");
company.allowWriteAccess("/db/Users/11");
company.allowWriteAccess("/db/Users/12");
return company.save();
})
This ensures that only these users can edit the company object. Notably, this list of rules is independent of the list of admins contained in your company object. To revoke the write access of a user, you can use deleteWriteAccess in the same way we used allowWriteAccess before.
This means your users can leave a Company easily without leaving your app.
I hope this answers your question. Because ACLs are complex I will try to explain the general approach in more detail now.
How ACLs Work
On which level can access be controlled?
There are two levels to control access to your data:
On table level (so-called Schema ACLs)
On object level (so-called Object ACLs)
Schema ACLs define who is allowed access to the table in general. For example, you could define that the User table is not readable for the public by granting read access only to the admin:
allowReadAccess("/db/Role/admin") // Schema ACLs can only be set by the admin
You can define rules for reading, updating, inserting and querying on the table separately.
Object ACLs defines access on the lower level. You can use it to deny access to a specific object. For example, you could define that only the user itself can update its own User object, like this:
allowWriteAccess("<userId>")
For objects, you can define rules for reading and writing separately.
Who has access now (how are permission evaluated)?
In order to access an object, a user needs to have general permission to access the table (Schema ACLs) and also permission to access the object itself (Object ACLs). This means the Schema ACLs are evaluated first if they grant you access, the Object ACLs are evaluated as well.
Which rules can I define?
There are two types of rules that can be defined to allow or deny access:
Allow Rules define who has access in general. These rules are checked first. If you do not define allow rules, everyone has general access.
Deny Rules defines who is denied access (even if the user was allowed by an allow rule). These rules are checked after the allow rules.
Take a look at the JS API for ACLs for the actual method documentation.
These separate rules can be tricky at the start but they are really powerful. Let's do some examples. How can I use these rules to ...
Deny access for everyone: --> Set the only allow rule for admin
Allow access for logged-in users but not for some guy Peter (like when you block someone is a chat application): --> Set an allow rule for the loggedin role and a deny rule for Peter.
Only allow access from backend code modules: --> Set an allow rule for the node role (see below for the explanation of Roles).
Who can I grant or deny access?
There are two entities you can use in your allow and deny rules:
Users from the predefined User table can be granted or denied access
Groups of Users defined in the predefined Role table can be granted or denied access
The predefined roles admin, loggedin (represents all logged-in users) and node (represents backend code modules that access the database)
What is the default access for my tables?
Tables and objects are publically accessible by default if not configured otherwise.
What about attribute-level ACLs
There are no attribute level ACLs in Baqend. This means when you have a User object with a private email address and a public name you can only make the object private or public.
The solution for this is to use two objects, one for the private information and one for the public information and then link the two. For the User, this would mean you make the actual User object private and define a new Profile table where you keep the public user information.
While this solution is more work when defining your schema, there are good reasons why Baqend does not support attribute-level ACLs. Without going into too much detail:
Better caching. Attribute-level ACLs would severely limit how we can cache and therefore accelerate your database requests.
Expensive evaluation. Attribute-level ACLs are much harder to evaluate and therefore slow down database access. Object-level ACLs, on the other hand, can be pushed down to our database system and are evaluated very efficiently.
Something missing
I hope these explanations help to understand the ACL system better. If there is something missing here, just comment and I will add it.