Discussion: Better alternative to user abilities based on roles
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 5/5
- Thời gian dự kiến
- Hơn một tuần
- Mức phù hợp với người mới
- 20/100
- Loại issue
- Tính năng
- Độ rõ ràng
- Cần làm rõ
- Mức độ hoạt động
- Đình trệ
- Công nghệ
- javascript
- Lĩnh vực
- api, authorization, frontend
Hướng nghiên cứu
Bắt đầu với ví dụ về ability trong issue, đặc biệt là projectMembership, userRole và các route before/afterModel checks. Xem xét các tùy chọn current-user sideloading và API abilities được liệt kê; công việc được xem là hoàn tất khi có giải pháp đã thống nhất cho các kiểm tra ability bất đồng bộ và số lượng project membership requests quá mức, nhưng không có file hoặc test nào được nêu tên.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Problem
Right now, most of our authorization stuff is based in a user's membership level in a project. They can be unrelated, they can have a pending membership (meaning they requested membership, but they are yet to be approved), or they can be any of [contributor, admin owner].
Based on the level of their role, they are allowed or not allowed to perform certain project-related actions.
First Problem: Amount of requests
In order to determine a role level of a user in a project, we usually need to perform the following:
- fetch
project.projectUsers - for each
projectUser, actually load the record from the backend, because just having theidis not enough.
Right now, this is not a big deal performance-wise. However, we expect projects to grow, and this is, worst-case, n+1 requests for a project with n members. Luckily, we use colasced id requests to significantly reduce this, but it's still something that's potentially problematic.
Second Problem: Ember-Can does not play well with async
The ability above is a computed, the content of which is partially dependant on async network requests.
Basically, in most cases we have something like this:
projectMembership: computed('project.projectUsers', 'currentUser.user.id', function() {
let currentUserId = get(this, 'currentUser.user.id');
if (isEmpty(currentUserId)) {
return false;
} else {
return get(this, 'project.projectUsers').find((item) => {
return get(item, 'user.id') === currentUserId;
});
}
}),
userRole: alias('projectMembership.role'),
userIsContributor: equal('userRole', 'contributor'),
userIsAdmin: equal('userRole', 'admin'),
userIsOwner: equal('userRole', 'owner'),
canEdit: or('{userIsAuthor,userIsAdmin,userIsOwner}'),
canAssign: or('canEdit', 'userIsContributor'),
canReposition: alias('canAssign')
With template usage, where we do or do not render parts of the UI based on abilities, this works amazingly well, mostly thanks to DS.PromiseObject and DS.PromiseArray classes.
As results are being fetched, eventually, we will get a currentUserId and we will get a projectMembership.role. Until that happens, the ability is evaluated as false. For templates, this is almost exactly what we need. Eventually, when we determine a user is able to do something, that part of the UI will render.
For the case of routes and redirecting in case of lack of abilities, this is problematic. In those cases, we basically get the current value of the ability, which is usually false at the moment of a route's before/afterModel hook. Due to that, we are forced to use a dirty solution of explicitly fetching records before checking the ability:
return get(project, 'projectUsers').then(() => {
if (this.cannot('manage project', project)) {
return this.transitionTo('project');
}
});
Its a very clear code smell and it will end up being highly problematic as the number of users grows.
We need a solution, primarily for the second, but also for the first problem
Potential solutions
We sideload the projectUser relationship as part of the user request
This works, but also means a lot of data we don't need being loaded. Most user will not need information for other user records.
We sideload the projectUser relationship, but only for the current user.
Our API could sideload it in this specific case, if the GET /users/:id matches the currently authenticated user.
Really, this would tackle most cases. We'd have to rethink our currentUser/abilities structure, but it could work quite nicely, I think.
We could go further and have the authentication request return a user record with all the sideloaded information, which we can then push into the ember store manually.
The more I think about it, the more I like this solution.
We send some sort of computed ability table structure for each user
Probably highly complex and something I'm not looking forward to tackling and would vote against, but wanted to get the option listed here anyway.
Have some sort of ability endpoint on the API, which can tell each user if they can/cannot do something.
Again, a solution I do not personally like, but listing it for the sake of discussion.
Other options
Listed above are the options I could think of, but there may be other.
Side effects
Depending on how we deal with this, we could eliminate the need to fix #1151
- Ngôn ngữ chính
- JavaScript
- Star
- 120
- Fork
- 75
- Chỉ số merge pull request
- Không có pull request nào được merge trong 30 ngày
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của code-corps/code-corps-ember
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
code-corps/code-corps-ember#1616 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 65/100
code-corps/code-corps-ember#1613 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
code-corps/code-corps-ember#1612 ·
-
Difficulty: Medium Skill: ember-cli-page-object
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
code-corps/code-corps-ember#1063 · 1 reaction ·
-
greenkeeper
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 25/100
code-corps/code-corps-ember#1765 · 1 bình luận ·
Tất cả issue của code-corps/code-corps-ember
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
mksglu/context-mode#1200 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
neondatabase/website#5944 ·
-
module: core
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
bigbluebutton/bigbluebutton#25849 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
jaegertracing/jaeger-ui#4506 ·