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

[#3342] feat(core): Refactor the role and privilege model #3389

Merged
merged 11 commits into from
May 22, 2024

Conversation

jerqi
Copy link
Contributor

@jerqi jerqi commented May 14, 2024

What changes were proposed in this pull request?

Add support for ALLOW/DENY effect for privileges.
Move privileges from the role to the securable object.

Why are the changes needed?

Fix: #3342

Does this PR introduce any user-facing change?

Yes.

How was this patch tested?

UT passed.

@jerqi jerqi marked this pull request as draft May 14, 2024 11:26
@jerqi jerqi changed the title [#3342] Refactor the role and privilege model [#3342] feat(core): Refactor the role and privilege model May 15, 2024
@jerqi jerqi force-pushed the ISSUE-3342 branch 6 times, most recently from 612ee96 to 8828bf6 Compare May 15, 2024 12:22
@qqqttt123 qqqttt123 marked this pull request as ready for review May 15, 2024 13:09
@jerryshao
Copy link
Contributor

@jerqi is this ready for review?

@jerqi
Copy link
Contributor Author

jerqi commented May 16, 2024

* means that you are denied to use the privilege. If you have `ALLOW` and `DENY` for the same
* privilege name of the same securable object, the `DENY` will take effect.
*/
enum Effect {
Copy link
Contributor

Choose a reason for hiding this comment

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

What do other systems like Ranger describe this behavior, do they use Effect or other better word?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Gravitiy use the word Effect in their api.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ranger use Allow conditions and Deny conditions.

Copy link
Contributor

Choose a reason for hiding this comment

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

I prefer to use "Condition" than "Effect".

*
* @param privilege The string representation of the privilege.
* @return The Privilege.
*/
public static Privilege fromString(String privilege) {
public static Privilege allowPrivilegeFromString(String privilege) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the method name is a little verbose, maybe we can change to like:

public Privilege allow(String name);
public Privilege allow(Privilege.Name name);
public Privilege deny(String name);
public Privilege deny(Privilege.Name name);

*
* @return The privileges of the role.
*/
List<Privilege> privileges();
Copy link
Contributor

Choose a reason for hiding this comment

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

Are these privileges inheritable in your current design?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They are not inheritable.

@Override
public void bindPrivileges(List<Privilege> privileges) {
this.privileges = privileges.stream().map(DTOConverters::toDTO).toArray(PrivilegeDTO[]::new);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

The interface (securable object) you designed and achieved here in DTO is a bit weird, who and when will call this method?

It is because your SecurableObject has both getter and setter interfaces, it is mutable as the interface design. But DTO is immutable, so the interface design will make the DTO implementation a bit weird. I would suggest you rethink about how to design this SecurableObject based on our UI requirement as well as code itself.

@@ -250,21 +250,16 @@ public boolean isMetalakeAdmin(String user) {
* @param role The name of the Role.
* @param properties The properties of the Role.
* @param securableObject The securable object of the Role.
* @param privileges The privileges of the Role.
* @return The created Role instance.
* @throws RoleAlreadyExistsException If a Role with the same name already exists.
* @throws NoSuchMetalakeException If the Metalake with the given name does not exist.
* @throws RuntimeException If creating the Role encounters storage issues.
*/
public Role createRole(
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we support add new securable object to the existing role?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now, we don't support it. We can support it in the future.

AuditInfo audit_info = 7;
repeated string privilege_effects = 4;
string securable_object_full_name = 5;
string securable_object_type = 6;
Copy link
Contributor

Choose a reason for hiding this comment

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

I would suggest you support multiple securable objects here, otherwise you need to update the storage structure later on, which is hard to do so.

In the meantime, since you changed the definition of securable object, should you also update the layout here?

Copy link
Contributor

Choose a reason for hiding this comment

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

Will you leave this storage part to the next PR?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, I will leave the storage to the next PR.

@qqqttt123
Copy link
Contributor

qqqttt123 commented May 20, 2024

After last review,

  1. I remove bindPrivileges and make the interface immutable.
  2. I change the effect to condition.

Could you give me further review? @jerryshao

/**
* The privileges of the securable object. For example: If the securable object is a table, the
* privileges could be `READ TABLE`, `WRITE TABLE`, etc. If a schema has the privilege of `LOAD
* TABLE`. It means the role can all tables of the schema.
Copy link
Contributor

Choose a reason for hiding this comment

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

“It means the role can all”?

Copy link
Contributor

Choose a reason for hiding this comment

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

Done.

public static SecurableObject ofSchema(SecurableObject catalog, String schema) {
checkCatalog(catalog);
public static SecurableObject ofSchema(
String catalogFullName, String schema, List<Privilege> privileges) {
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the meaning of catalogFullName?

Copy link
Contributor

Choose a reason for hiding this comment

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

How do we represent the full name of the catalog, schema, etc, "a.b.c"? I think it will be better to use a class to restrict the format of the full name, otherwise, it is hard for users to formalize the input string.

Copy link
Contributor

Choose a reason for hiding this comment

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

I replace this by securable object.

.map(
privilege -> {
return PrivilegeDTO.builder()
.withEffect(privilege.condition())
Copy link
Contributor

Choose a reason for hiding this comment

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

It should be changed to withCondition

Copy link
Contributor

Choose a reason for hiding this comment

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

Done.

@JsonProperty("name")
private Name name;

@JsonProperty("effect")
Copy link
Contributor

Choose a reason for hiding this comment

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

This should also be updated.

Copy link
Contributor

Choose a reason for hiding this comment

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

Done.

AuditInfo audit_info = 7;
repeated string privilege_effects = 4;
string securable_object_full_name = 5;
string securable_object_type = 6;
Copy link
Contributor

Choose a reason for hiding this comment

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

Will you leave this storage part to the next PR?


SecurableObjectImpl(SecurableObject parent, String name, Type type) {
this.parent = parent;
SecurableObjectImpl(String parentFullName, String name, Type type, List<Privilege> privileges) {
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the purpose of using parentFullName instead of SecurableObject parent? How about using NameIdentifier for unification?

Copy link
Contributor

Choose a reason for hiding this comment

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

NameIdentifier will include the information of the metalake.


return new SecurableObjectImpl(null, metalake, SecurableObject.Type.METALAKE);
return new SecurableObjectImpl(null, metalake, SecurableObject.Type.METALAKE, privileges);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you can use SecurableObject.of(xxx), that has some checks before creating a securable object.

Copy link
Contributor

Choose a reason for hiding this comment

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

Done.

@@ -138,6 +138,7 @@ CREATE TABLE IF NOT EXISTS `role_meta` (
`securable_object_full_name` VARCHAR(256) NOT NULL COMMENT 'securable object full name',
`securable_object_type` VARCHAR(32) NOT NULL COMMENT 'securable object type',
`privileges` VARCHAR(64) NOT NULL COMMENT 'securable privileges',
`privilege_conditions` VARCHAR(64) NOT NULL COMMENT 'securable privilege conditions',
Copy link
Contributor

Choose a reason for hiding this comment

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

Will you leave the multiple securable object support in the next pr?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, I will

@jerqi
Copy link
Contributor Author

jerqi commented May 21, 2024

@jerryshao Could you review again?

return GetRole.deny();

default:
throw new IllegalArgumentException("Don't support the privilege: " + name);
Copy link
Contributor

Choose a reason for hiding this comment

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

“Doesn't...”

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

case GET_ROLE:
return GetRole.get();
return GetRole.allow();

default:
throw new IllegalArgumentException("Don't support the privilege: " + name);
Copy link
Contributor

Choose a reason for hiding this comment

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

“Doesn't support...”

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

*/
@Nullable
SecurableObject parent();
String parentFullName();
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use String parent(), which seems simpler, WDYT?

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, maybe it's enough for us to tell user this is a full name string.

@qqqttt123
Copy link
Contributor

@jerryshao Comments are addressed.

@jerryshao jerryshao merged commit 5a4931b into apache:main May 22, 2024
22 checks passed
github-actions bot pushed a commit that referenced this pull request May 22, 2024
### What changes were proposed in this pull request?
Add support for ALLOW/DENY effect for privileges.
Move privileges from the role to the securable object.

### Why are the changes needed?

Fix: #3342

### Does this PR introduce _any_ user-facing change?
Yes. 

### How was this patch tested?
UT passed.

---------

Co-authored-by: Heng Qin <[email protected]>
diqiu50 pushed a commit to diqiu50/gravitino that referenced this pull request Jun 13, 2024
…he#3389)

### What changes were proposed in this pull request?
Add support for ALLOW/DENY effect for privileges.
Move privileges from the role to the securable object.

### Why are the changes needed?

Fix: apache#3342

### Does this PR introduce _any_ user-facing change?
Yes. 

### How was this patch tested?
UT passed.

---------

Co-authored-by: Heng Qin <[email protected]>
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.

[Improvement] Privilege should have deny mechanism
3 participants