Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

Discussion: Better alternative to user abilities based on roles

未关闭
#1,150 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

评估

难度
5/5
预计耗时
一周以上
新手友好度
20/100
Issue 类型
功能
描述清晰度
需要澄清
活跃度
停滞
技术栈
javascript

调研方向

从 issue 中的 ability 示例开始,尤其是 projectMembership、userRole 以及 route before/afterModel checks。检查列出的 current-user sideloading 和 API abilities 选项;完成条件是就异步 ability 检查和过多的 project membership requests 达成一致的解决方案,但没有指定文件或测试。

由索引模型根据 Issue 内容生成。

描述

Difficulty: Medium

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:

  1. fetch project.projectUsers
  2. for each projectUser, actually load the record from the backend, because just having the id is 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

主要语言
JavaScript
星标
120
派生
75
PR 合并指标
30 天内没有已合并 PR

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

code-corps/code-corps-ember 的其他 Issue

查看 code-corps/code-corps-ember 的全部 Issue

相似的 Issue

更多 JavaScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。