Skip to content

Commit

Permalink
[apache#2236] feat(core): Add the support of Group entity (apache#2735)
Browse files Browse the repository at this point in the history
### What changes were proposed in this pull request?

Add the support of Group entity

### Why are the changes needed?

Fix: apache#2236 

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

No.

### How was this patch tested?
Add a new UT.

---------

Co-authored-by: Heng Qin <[email protected]>
  • Loading branch information
qqqttt123 and Heng Qin authored Mar 30, 2024
1 parent 2dc8c54 commit 5b4eb40
Show file tree
Hide file tree
Showing 16 changed files with 661 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2024 Datastrato Pvt Ltd.
* This software is licensed under the Apache License version 2.
*/
package com.datastrato.gravitino.authorization;

import com.datastrato.gravitino.Auditable;
import com.datastrato.gravitino.annotation.Evolving;
import java.util.List;

/** The interface of a Group. The Group is the entity which contains users. */
@Evolving
public interface Group extends Auditable {

/**
* The name of the group.
*
* @return The name of the group.
*/
String name();

/**
* The roles of the group.
*
* @return The roles of the group.
*/
List<String> roles();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2024 Datastrato Pvt Ltd.
* This software is licensed under the Apache License version 2.
*/

package com.datastrato.gravitino.exceptions;

import com.google.errorprone.annotations.FormatMethod;
import com.google.errorprone.annotations.FormatString;

/** An exception thrown when a group already exists. */
public class GroupAlreadyExistsException extends AlreadyExistsException {

/**
* Constructs a new exception with the specified detail message.
*
* @param message the detail message.
* @param args the arguments to the message.
*/
@FormatMethod
public GroupAlreadyExistsException(@FormatString String message, Object... args) {
super(message, args);
}

/**
* Constructs a new exception with the specified detail message and cause.
*
* @param cause the cause.
* @param message the detail message.
* @param args the arguments to the message.
*/
@FormatMethod
public GroupAlreadyExistsException(Throwable cause, String message, Object... args) {
super(cause, message, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2024 Datastrato Pvt Ltd.
* This software is licensed under the Apache License version 2.
*/

package com.datastrato.gravitino.exceptions;

import com.google.errorprone.annotations.FormatMethod;
import com.google.errorprone.annotations.FormatString;

/** Exception thrown when a group with specified name is not existed. */
public class NoSuchGroupException extends NotFoundException {
/**
* Constructs a new exception with the specified detail message.
*
* @param message the detail message.
* @param args the arguments to the message.
*/
@FormatMethod
public NoSuchGroupException(@FormatString String message, Object... args) {
super(message, args);
}

/**
* Constructs a new exception with the specified detail message and cause.
*
* @param cause the cause.
* @param message the detail message.
* @param args the arguments to the message.
*/
@FormatMethod
public NoSuchGroupException(Throwable cause, String message, Object... args) {
super(cause, message, args);
}
}
1 change: 1 addition & 0 deletions core/src/main/java/com/datastrato/gravitino/Entity.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ enum EntityType {
FILESET("fi", 5),
TOPIC("to", 6),
USER("us", 7),
GROUP("gr", 8),

AUDIT("au", 65534);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
package com.datastrato.gravitino.authorization;

import com.datastrato.gravitino.EntityStore;
import com.datastrato.gravitino.exceptions.GroupAlreadyExistsException;
import com.datastrato.gravitino.exceptions.NoSuchGroupException;
import com.datastrato.gravitino.exceptions.NoSuchUserException;
import com.datastrato.gravitino.exceptions.UserAlreadyExistsException;
import com.datastrato.gravitino.storage.IdGenerator;
Expand All @@ -15,10 +17,10 @@
*/
public class AccessControlManager {

private final UserManager userManager;
private final UserGroupManager userGroupManager;

public AccessControlManager(EntityStore store, IdGenerator idGenerator) {
this.userManager = new UserManager(store, idGenerator);
this.userGroupManager = new UserGroupManager(store, idGenerator);
}

/**
Expand All @@ -31,7 +33,7 @@ public AccessControlManager(EntityStore store, IdGenerator idGenerator) {
* @throws RuntimeException If adding the User encounters storage issues.
*/
public User addUser(String metalake, String name) throws UserAlreadyExistsException {
return userManager.addUser(metalake, name);
return userGroupManager.addUser(metalake, name);
}

/**
Expand All @@ -43,7 +45,7 @@ public User addUser(String metalake, String name) throws UserAlreadyExistsExcept
* @throws RuntimeException If removing the User encounters storage issues.
*/
public boolean removeUser(String metalake, String user) {
return userManager.removeUser(metalake, user);
return userGroupManager.removeUser(metalake, user);
}

/**
Expand All @@ -56,6 +58,44 @@ public boolean removeUser(String metalake, String user) {
* @throws RuntimeException If getting the User encounters storage issues.
*/
public User getUser(String metalake, String user) throws NoSuchUserException {
return userManager.getUser(metalake, user);
return userGroupManager.getUser(metalake, user);
}

/**
* Adds a new Group.
*
* @param metalake The Metalake of the Group.
* @param group The name of the Group.
* @return The Added Group instance.
* @throws GroupAlreadyExistsException If a Group with the same identifier already exists.
* @throws RuntimeException If adding the Group encounters storage issues.
*/
public Group addGroup(String metalake, String group) throws GroupAlreadyExistsException {
return userGroupManager.addGroup(metalake, group);
}

/**
* Removes a Group.
*
* @param metalake The Metalake of the Group.
* @param group THe name of the Group.
* @return `true` if the Group was successfully removed, `false` otherwise.
* @throws RuntimeException If removing the Group encounters storage issues.
*/
public boolean removeGroup(String metalake, String group) {
return userGroupManager.removeGroup(metalake, group);
}

/**
* Gets a Group.
*
* @param metalake The Metalake of the Group.
* @param group THe name of the Group.
* @return The getting Group instance.
* @throws NoSuchGroupException If the Group with the given identifier does not exist.
* @throws RuntimeException If getting the Group encounters storage issues.
*/
public Group getGroup(String metalake, String group) throws NoSuchGroupException {
return userGroupManager.getGroup(metalake, group);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,42 @@
import com.datastrato.gravitino.EntityStore;
import com.datastrato.gravitino.NameIdentifier;
import com.datastrato.gravitino.Namespace;
import com.datastrato.gravitino.exceptions.GroupAlreadyExistsException;
import com.datastrato.gravitino.exceptions.NoSuchEntityException;
import com.datastrato.gravitino.exceptions.NoSuchGroupException;
import com.datastrato.gravitino.exceptions.NoSuchUserException;
import com.datastrato.gravitino.exceptions.UserAlreadyExistsException;
import com.datastrato.gravitino.meta.AuditInfo;
import com.datastrato.gravitino.meta.CatalogEntity;
import com.datastrato.gravitino.meta.GroupEntity;
import com.datastrato.gravitino.meta.UserEntity;
import com.datastrato.gravitino.storage.IdGenerator;
import com.datastrato.gravitino.utils.PrincipalUtils;
import com.google.common.collect.Lists;
import java.io.IOException;
import java.time.Instant;
import java.util.Collections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* UserManager is used for add, remove and get users from one metalake. UserManager doesn't manage
* users, just sets up the relationship between the metalake and the user. Metalake is like a
* concept of the organization. `AddUser` means that a user enter an organization.
* UserGroupManager is used for add, remove and get users or roles from one metalake.
* UserGroupManager doesn't manage users or groups, just sets up the relationship between the
* metalake and the user or group. Metalake is like a concept of the organization. `AddUser` or
* `AddGroup` means that a role or user enters an organization.
*/
public class UserManager {
public class UserGroupManager {

private static final Logger LOG = LoggerFactory.getLogger(UserManager.class);
private static final Logger LOG = LoggerFactory.getLogger(UserGroupManager.class);
private static final String USER_DOES_NOT_EXIST_MSG = "User %s does not exist in th metalake %s";

private static final String GROUP_DOES_NOT_EXIST_MSG =
"Group %s does not exist in th metalake %s";

private final EntityStore store;
private final IdGenerator idGenerator;

public UserManager(EntityStore store, IdGenerator idGenerator) {
public UserGroupManager(EntityStore store, IdGenerator idGenerator) {
this.store = store;
this.idGenerator = idGenerator;
}
Expand Down Expand Up @@ -121,8 +129,95 @@ public User getUser(String metalake, String user) throws NoSuchUserException {
}
}

/**
* Adds a new Group.
*
* @param metalake The Metalake of the Group.
* @param group The name of the Group.
* @return The Added Group instance.
* @throws GroupAlreadyExistsException If a Group with the same identifier already exists.
* @throws RuntimeException If adding the Group encounters storage issues.
*/
public Group addGroup(String metalake, String group) throws GroupAlreadyExistsException {
GroupEntity groupEntity =
GroupEntity.builder()
.withId(idGenerator.nextId())
.withName(group)
.withNamespace(
Namespace.of(
metalake,
CatalogEntity.SYSTEM_CATALOG_RESERVED_NAME,
GroupEntity.GROUP_SCHEMA_NAME))
.withRoles(Collections.emptyList())
.withAuditInfo(
AuditInfo.builder()
.withCreator(PrincipalUtils.getCurrentPrincipal().getName())
.withCreateTime(Instant.now())
.build())
.build();
try {
store.put(groupEntity, false /* overwritten */);
return groupEntity;
} catch (EntityAlreadyExistsException e) {
LOG.warn("Group {} in the metalake {} already exists", group, metalake, e);
throw new GroupAlreadyExistsException(
"Group %s in the metalake %s already exists", group, metalake);
} catch (IOException ioe) {
LOG.error(
"Adding group {} failed in the metalake {} due to storage issues", group, metalake, ioe);
throw new RuntimeException(ioe);
}
}

/**
* Removes a Group.
*
* @param metalake The Metalake of the Group.
* @param group THe name of the Group.
* @return `true` if the Group was successfully removed, `false` otherwise.
* @throws RuntimeException If removing the Group encounters storage issues.
*/
public boolean removeGroup(String metalake, String group) {
try {
return store.delete(ofGroup(metalake, group), Entity.EntityType.GROUP);
} catch (IOException ioe) {
LOG.error(
"Removing group {} in the metalake {} failed due to storage issues",
group,
metalake,
ioe);
throw new RuntimeException(ioe);
}
}

/**
* Gets a Group.
*
* @param metalake The Metalake of the Group.
* @param group THe name of the Group.
* @return The getting Group instance.
* @throws NoSuchGroupException If the Group with the given identifier does not exist.
* @throws RuntimeException If getting the Group encounters storage issues.
*/
public Group getGroup(String metalake, String group) {
try {
return store.get(ofGroup(metalake, group), Entity.EntityType.GROUP, GroupEntity.class);
} catch (NoSuchEntityException e) {
LOG.warn("Group {} does not exist in the metalake {}", group, metalake, e);
throw new NoSuchGroupException(GROUP_DOES_NOT_EXIST_MSG, group, metalake);
} catch (IOException ioe) {
LOG.error("Getting group {} failed due to storage issues", group, ioe);
throw new RuntimeException(ioe);
}
}

private NameIdentifier ofUser(String metalake, String user) {
return NameIdentifier.of(
metalake, CatalogEntity.SYSTEM_CATALOG_RESERVED_NAME, UserEntity.USER_SCHEMA_NAME, user);
}

private NameIdentifier ofGroup(String metalake, String group) {
return NameIdentifier.of(
metalake, CatalogEntity.SYSTEM_CATALOG_RESERVED_NAME, GroupEntity.GROUP_SCHEMA_NAME, group);
}
}
Loading

0 comments on commit 5b4eb40

Please sign in to comment.