Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ISSUE #5]Add permission control when loginRequired is true. #6

Merged
merged 5 commits into from Sep 1, 2021

Conversation

zhangjidi2016
Copy link
Contributor

What is the purpose of the change

#5

When the login user calls all interfaces, the permission of each interface is verified by AOP, by checking whether the role to which the user belongs has the access permission of the interface. All interface urls that a common user role has access permissions are saved in a file. The file is hot updated to facilitate adding or deleting permissions.

Brief changelog

XX

Verifying this change

XXXX

Follow this checklist to help us incorporate your contribution quickly and easily. Notice, it would be helpful if you could finish the following 5 checklist(the last one is not necessary)before request the community to review your PR.

  • Make sure there is a Github issue filed for the change (usually before you start working on it). Trivial changes like typos do not require a Github issue. Your pull request should address just this issue, without pulling in other changes - one PR resolves one issue.
  • Format the pull request title like [ISSUE #123] Fix UnknownException when host config not exist. Each commit in the pull request should have a meaningful subject line and body.
  • Write a pull request description that is detailed enough to understand what the pull request does, how, and why.
  • Write necessary unit-test(over 80% coverage) to verify your logic correction, more mock a little better when cross module dependency exist. If the new feature or significant change is committed, please remember to add integration-test in test module.
  • Run mvn -B clean apache-rat:check findbugs:findbugs checkstyle:checkstyle to make sure basic checks pass. Run mvn clean install -DskipITs to make sure unit-test pass. Run mvn clean test-compile failsafe:integration-test to make sure integration-test pass.
  • If this contribution is large, please file an Apache Individual Contributor License Agreement.

Copy link
Member

@StyleTang StyleTang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zhangjidi2016 LGTM
Coverage decreased (-0.3%) to 79.213% Could you add more UT for this PR?

}
int type = userInfo.getUser().getType();
// 1:admin 0:ordinary
String loginUserRole = type == 1 ? ADMIN : ORDINARY;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a UserRoleEnum, it seems so many place use it.

if (loginUserRole == ADMIN) {
return joinPoint.proceed();
}
Map<String, List<String>> map = permissionService.queryRolePerms();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The permission check logic should not be here. It should belongs to PermissionService.
suggest to use

permissionService.checkUriAvailable(uri , userInfo);

In this permission check aspect, just invoke it.

Comment on lines 41 to 43
RMQConfigure configure;

FileBasedPermissionStore fileBasedPermissionStore;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggest to use private but not default.

}
}

public static class FileBasedPermissionStore {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some duplicate code here, it would be better if we can reuse it.

org.apache.rocketmq.dashboard.service.impl.UserServiceImpl.FileBasedUserInfoStore

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I'll re-optimize the code as suggested.

```
* 2.确保${rocketmq.config.dataPath}定义的目录存在,并且该目录下创建登录配置文件"role-permission.yml",
如果该目录下不存在此文件,则默认使用resources/role-permission.yml文件。改文件保存了普通用户角色所有能访问的接口地址。
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

改-> 该

如果resources/role-permission.yml也不存在,会怎样?请把错误的退避规则描述的更精致一些:-)

- /dashboard/topic.query
- /dashboard/topicCurrent
....
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

考虑通配支持,后续维护太复杂否则

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, the interface path in the authentication file is added with wildcard characters

....
```
* 3.前端页面显示上,为了更好区分普通用户和admin用户权限,关于资源的删除、更新等操作按钮不对普通用户角色显示。
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

考虑重签功能,要么退出换角色登入,要么支持管理员到普通账户的一键切换

ordinary:
- /rocketmq/nsaddr
- /ops/homepage.query
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change description corresponds with CN.

@@ -37,6 +38,7 @@

@Controller
@RequestMapping("/consumer")
@Permission
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still recommend using the unified configuration, which can be unified in one directory, without annotating each method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @Permission annotation is on a class in which all the methods of the class will implement aop methods. There is no need to annotate each method

@Retention(RetentionPolicy.RUNTIME)
public @interface Permission {

String value() default "";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be helpful if we could leave the concrete field in here. such as read-only, write...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This value attribute is not used, then delete

* can also be applied to methods in a class for fine control
*/
@Pointcut("@annotation(org.apache.rocketmq.dashboard.permisssion.Permission) || @within(org.apache.rocketmq.dashboard.permisssion.Permission)")
private void permission() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does within[1] annotation work well here?

[1] https://docs.spring.io/spring-framework/docs/2.0.x/reference/aop.html

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes,it can work,@Within - limits matching to join points within types that have the given annotation (the execution of methods declared in types with the given annotation when using Spring AOP).

load(inputStream);
}
} else {
log.info(String.format("User Permission configure file is %s", filePath));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any resource leak if we do not use try with?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I will close the InputStream after use

@vongosling
Copy link
Member

@zhangjidi2016 there are many places waiting to be clarified. BTW, do we have a better way to support here. do not link permission to URL directly?

@zhangjidi2016
Copy link
Contributor Author

@zhangjidi2016 there are many places waiting to be clarified. BTW, do we have a better way to support here. do not link permission to URL directly?

I haven't thought of any other good authentication methods yet. However, regardless of the authentication method, we need to maintain the role-permission mapping, because rocketMQ-Dashboard deployment does not depend on any external storage system, only to save the mapping to a disk file.
Do you have any good suggestions for improving authentication?@vongosling @StyleTang @zongtanghu

@vongosling
Copy link
Member

vongosling commented Aug 19, 2021

One trick is you could store it in one topic if it is necessary, but it is not friendly for updates, it invokes me to think about our compact topic design...

@StyleTang
Copy link
Member

I come up with an idea for improving authentication.
For now, the relationship is

user -> role 
role -> uri

Maybe we can introduce permission

user -> role 
role -> permission 

(URI/button/tab) requires permission

user have role, role contains several permissions. A login user can have a permission set.
for example we have a topic_write permission.

For backend: invoke create topic method requires topic_write permission ==> (we can add @Permission(value = "topic_write") to the create topic method)

For front end: show create topic button requires topic_write permission ==> (we can get permissions after login,only show create topic button if the user has topic_write permission)

Do you have any suggestions?

@StyleTang
Copy link
Member

Local file storage is indeed a problem. Because of it, we need to stand-alone deployment, or the config can not sync.

It is better to have a remote shared storage, Maybe we should abstract the storage interface, local file storage is an implementation(for now), mysql is another implementation, MQ message implementation etc.

@vongosling
Copy link
Member

I come up with an idea for improving authentication.
For now, the relationship is

user -> role 
role -> uri

Maybe we can introduce permission

user -> role 
role -> permission 

(URI/button/tab) requires permission

user have role, role contains several permissions. A login user can have a permission set.
for example we have a topic_write permission.

For backend: invoke create topic method requires topic_write permission ==> (we can add @Permission(value = "topic_write") to the create topic method)

For front end: show create topic button requires topic_write permission ==> (we can get permissions after login,only show create topic button if the user has topic_write permission)

Do you have any suggestions?

Agree, specifically speaking, we could learn lessons from RBAC, here resources may have different operations

@StyleTang
Copy link
Member

@zhangjidi2016
Do you have any further updates for this PR.
If no further updates, since the feature works well, I'm OK to merge it and we can refactor it in the future.

@zhangjidi2016
Copy link
Contributor Author

@zhangjidi2016
Do you have any further updates for this PR.
If no further updates, since the feature works well, I'm OK to merge it and we can refactor it in the future.

Well, we can merge this PR first, and verify permissions according to the interface path in the first version, and then optimize this problem again according to the structure of RBAC.

@StyleTang
Copy link
Member

@zhangjidi2016
This branch has conflicts with master, could you resolve the conflicts.
Thanks.

@zhangjidi2016
Copy link
Contributor Author

@zhangjidi2016
This branch has conflicts with master, could you resolve the conflicts.
Thanks.

OK,code conflicts have been resolved

@StyleTang StyleTang merged commit 5b2a027 into apache:master Sep 1, 2021
@zhangjidi2016 zhangjidi2016 deleted the add_permission branch September 3, 2021 06:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants