what is the best way to build multiple permission group on mongodb? - node.js

I started a complex project management application and I have the challenge of building resource permissions management for different types of user profiles.
My challenge is:
User story
John is an user with a common user profile.
John creates a project in the application.
John creates several tasks and adds them to the project.
John adds an user responsible for each task.
Added users must have access to the project and the tasks to which they have been added.
John creates a specific task and adds it as a subtask to one of the project's tasks.
In this subtask John adds an user as responsible, automatically that user must have access to the subtask, the task and the project.
And at any time, John can restrict access to a project resource, such as defining that a specific user can only view tasks
The way I started.
I created a specification pattern for each use case, where I inform the variables and it returns a true or false answer.
However, I have to request for each resource and, in my opinion, this is not performative.
What I mentioned is one of the simplest cases, there are others that are more complex.
canEditTaskOnProject(): boolean {
if (!this.project) {
console.error(
`Project not provided on ${TaskPermission.name}.${this.canEditTaskOnProject.name}`
);
return false;
}
return new ProjectLeader(this.project, this.userId)
.or(new Creator(this.task, this.userId))
.or(new FullAccessTaskPermission(this.project, this.userId))
.or(new TaskResponsible(this.task, this.userId))
.or(
new RestrictTaskPermission(this.project, this.userId).and(
new Creator(this.task, this.userId).or(
new TaskResponsible(this.task, this.userId)
)
)
)
.or(
new ReadAndWriteTaskPermission(this.project, this.userId).and(
new TaskResponsible(this.task, this.userId)
)
)
.isSatisfiedBy(this.userId);
}
I would very much like suggestions from experienced people who have already done something similar. I am a beginner in the area and in the company where I work, there are no seniors.
Thank you in advance!

I found the best way using casl:
import { defineAbility } from '#casl/ability';
export default defineAbility((can, cannot) => {
can('read', 'Article');
cannot('read', 'Article', { published: false }); // inverted rule
});

Related

Intercepting an assignment to a table relation field

I created a one-to one relationship between two tables in strapi.
As an example, suppose that Bob currently has a job, say messenger, if we assign Bob’s Job to secretary, Strapi simply reassigns the new Job, without warning that Bob was already in a job
If a person is not in a current job, it’s job would be ‘none’
I’d like to forbid the reassignment of the job, if Bob was already in a job (the user would have to assign the Bob's job to ‘none’ before assigning a new job)
In strapi, what would be the right way to forbid it (checking if the current job is not ‘none’, and, if it’s the case, stopping the assignment), using a service, a controller or a lifecycle hook?
One way to handle this in Strapi would be to use a lifecycle hook. Lifecycle hooks allow you to perform specific actions at certain stages of the CRUD operations (create, update, delete) on a model. In this case, you can use the beforeUpdate hook to check if the current job is not none before allowing the assignment of a new job:
// api/person/models/Person.js
module.exports = {
lifecycles: {
// This hook will be called before updating a person
async beforeUpdate(params, data) {
// Check if the current job is not 'none'
if (params.current.job !== 'none') {
// If the current job is not 'none', throw an error
throw new Error('Cannot reassign a job to a person who already has a job');
}
}
}
};
You can also use a service or a controller to handle this logic, but using a lifecycle hook allows you to centralize this logic and keep it separate from your business logic.

Discord.js - Command to Remove All Users Containing Role

I know that you can remove certain roles from users, and remove all roles from a user, but I was thinking of doing the reverse. I looked at this guide, which provides a way to retrieve all of the people who have a specific role. It seems like you could manipulate the collection/map to go through each member and remove the role. However, I cannot seem to achieve this.
I've hard-coded the one specific role that I am targeting as well as the message that should trigger the command.
Current code that I've been trying out (only seems to be working if there's just one person assigned the role):
if (msg.startsWith('!new round')) {
//check for everyone and remove the role
//roleID is just the roleID number string; I've stated it outside the if loop, for other command use cases as well
let membersWithRole = message.guild.roles.cache.get(roleID).members;
console.log(membersWithRole);
for (let member of membersWithRole) {
let mem = member[1]
mem.roles.remove(role).catch(console.error);
message.reply("Everyone with the jail role is now back in the game!");
}
}
Bottom line: Given a collection of the list of "guild" members that have the specified role (provided in the guide), could I iterate through a list* in order to remove the role from each member?
*I haven't found said list containing the members, but it's probably the objects themselves, so the whole collection
you need to learn documentation of discord.js
and yes you can do it by looping through all members.
if(msg.startsWith('!new round')){
console.log('command used by '+msg.author);
let role =msg.guild.roles.cache.get(roleId);
role.members.each(member=>{
member.roles.remove(role);
});
console.log('removed role from all members');
}
and also if you want to remove role from all members, so why you are not just deleting the role?
delete role:
msg.guild.roles.cache.get(roleId).delete();

Django-viewflow how to get the current user?

First day learning viewflow, I managed to get the tutorial to work, but I have a use case that I don't know how to implement.
What I want is when a workflow is started, I want it to automatically assign the task to the workflow starter (the user), how do I go about reference the current request object inside the workflow?
eg.
start = (flow.Start(CreateProcessView)).Permission(auto_create=True).Next(this.fill_request)
fill_request = (flow.View(UpdateProcessView).Assign(#current user))
An .Assign(...) could be specified with a callable that takes a process activation and should return a user. Ex .Assign(lambda act: User.objects.get(...))
There are several callable shortcuts provided by Viewflow. Any this.[task_name].owner point to a user who completed that task, and activation,process.created_by points to a user who made the .Start task
fill_request = (
flow.View(UpdateProcessView)
.Assign(lambda act: act.process.created_by)
# .Assign(this.start.owner)
)

cakePHP and authorization for CRUD operations

I have a cakephp 1.3 application and I have run into a 'data leak' security hole. I am looking for the best solution using cake and not just something that will work. The application is a grade tracking system that lets teachers enter grades and students can retrieve their grades. Everything is working as expected but when I started to audit security I found that the basic CRUD operations have leaks. Meaning that student X can see student Y's grades. Students should only see their own grades. I will limit this questions to the read operation.
Using cake, I have a grade_controller.php file with this view function:
function view($id = null) {
// Extra, not related code removed
$this->set('grade', $this->grade->read(null, $id));
}
And
http://localhost/grade/view/5
Shows the grade for student $id=5. That's great. But if student #5 manipulates the URL and changes it to a 6, person #6's grades are shown. The classic data leak security hole.
I had two thoughts on the best way to resolve this. 1) I can add checks to every CRUD operations called in the controller. Or 2) add code to the model (for example using beforeFind()) to check if person X has access to that data element.
Option #1 seems like it is time consuming and error prone.
Option #2 seem like the best way to go. But, it required calling find() before some operations. The read() example above never executes beforeFind() and there is no beforeRead() callback.
Suggestions?
Instead of having a generic read() in your controller, you should move ALL finds, queries..etc into the respective model.
Then, go through each model and add any type of security checks you need on any finds that need to be restricted. 1) it will be much more DRY coding, and 2) you'll better be able to manage security risks like this since you know where all your queries are held.
For your example, I would create a getGrade($id) method in my Grade model and check the student_id field (or whatever) against your Auth user id CakeSession::read("Auth.User.id");
You could also build some generic method(s) similar to is_owner() to re-use the same logic throughout multiple methods.
If CakePHP supports isAuthorized, here's something you could do:
Create a column, that has the types of users (eg. 'student', 'teacher', ...)
Now, it the type of User is 'student', you can limit their access, to view only their data. An example of isAuthorized is as follows. I am allowing the student to edit only their profile information. You can extend the concept.
if ((($role['User']['role'] & $this->user_type['student']) == $this->user_type['student']) {
if (in_array($this->action, array('view')) == true) {
$id = $this->params->pass[0];
if ($id == $user_id) {
return (true);
}
}
}
}

How do I setup my POCO's with Subsonic 3 and the SimpleRepostitory? or where is the convention?

Is there someplace that details how to setup your POCO's when using the SimpleRepository with SubSonic 3? It sounds like it's convention over configuration, but I can't find where that convention is explained.
http://www.subsonicproject.com/docs/Conventions looks like it was meant for 2.0, and is also marked incomplete. (BTW: I'd love to help reorganize the docs into more a 2.0 and 3.0 as the current docs are a bit confusing on which version they are referring to.)
For instance I'd like to know how I'd go about setting up a
one-to-one relationship
User <=> Profile
class User {
Id
ProfileId instead of Profile? or is Profile profile possible?
}
class Profile {
Id
UserId instead of User? or is User user possible?
}
One-to-many relationship
class User {
Id
IList<Post> Posts (?) or IList<int> PostIds (?) or is this implied somehow? or is this just wrong?
}
class Post {
Id
UserId instead of User? or is User user possible?
}
Many-to-many
I'm guessing I'd need to setup a many to many table?
class User {
IList<Blog> Blogs (?) or IList<int> BlogIds (?) or is this implied somehow?
}
class BlogsUsers { // Do I have to create this class?
UserId
BlogId
}
class User {
IList<User> Users (?) or IList<int> UserIds (?) or is this implied somehow?
}
In the example solution it doesn't seem like these are set so I'm wondering how you'd go about doing a (my guess proceeds example):
one-to-one
User.Profile
r.Single<Profile>(p=>p.User == userId);
parent on one-to-many
Post.User
id = r.Single<Post>(postId).UserId;
r.Single<User>(id); // which kind of stinks with two queries, JOIN?
children on one-to-many
User.Posts
r.Find<Post>(p=>p.UserId == userId)
or many-to-many
User.Blogs
ids = r.Find<BlogsUsers>(bu=>bu.UserId == userId);
r.Find<Blog>(b=>b.BlogId == ids); // again with the two queries? :)
Blog.Users
ids = r.Find<BlogsUsers>(bu=>bu.BlogId == blogId);
r.Find<User>(u=>u.UserId == ids); // again with the two queries? :)
I would assume that there's got to be a way to not have the two queries and for these properties to already be autogenerated in some way. Truth be told though I did only have an hour to play with everything last night so I am a little afraid of Rob yelling at me. I'm SORRY! :P
If these are not autogen'd then where are views and store procedures for 3.0? Please give me a link for those as well while you're at it fellow SO'er.
This is probably your best place to start:
http://subsonicproject.com/docs/Using_SimpleRepository
Relationships are setup in code, by you, and we don't carry those forward to the DB (yet - hopefully soon). Ideally you setup your model as you need to and when you're ready you go and manually "solidify" your relationships in the DB. This is to reduce friction during development - data integrity isn't really something to worry about when building a site.
That said, I know people want this feature - I just need to build it.

Resources