You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hugegraph.apache.org by ji...@apache.org on 2026/08/30 10:58:20 UTC
(hugegraph-toolchain) 05/24: fix(ci): align coverage and quality checks
This is an automated email from the ASF dual-hosted git repository.
imbajin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hugegraph-toolchain.git
commit 84e3ef456dcade683f4c23c11e811e37d9f689b2
Author: dark <ji...@apache.org>
AuthorDate: Tue Aug 18 12:31:26 2026 +0800
fix(ci): align coverage and quality checks
- upload coverage from real module paths
- cover permission update behavior
- normalize PD states and the 120-column contract
---
.editorconfig | 4 +-
.github/actions/upload-coverage/action.yml | 1 +
.github/workflows/client-ci.yml | 2 +-
.github/workflows/hubble-ci.yml | 2 +-
.github/workflows/loader-ci.yml | 2 +-
.github/workflows/tools-ci.yml | 6 -
.serena/memories/README_INDEX.md | 2 +-
.serena/memories/code_style_and_conventions.md | 2 +-
README.md | 2 +-
hugegraph-hubble/.prettierrc | 2 +-
.../controller/auth/GraphSpaceUserController.java | 6 +-
.../hugegraph/service/auth/AuthContextService.java | 4 +-
.../service/auth/GraphSpaceUserService.java | 177 ++++++++++++--
.../service/op/OperationsPayloadParser.java | 29 ++-
.../GraphSpaceAuthMutationAuthorizationTest.java | 3 +-
.../auth/GraphSpaceAuthOwnershipTest.java | 73 +++++-
.../service/auth/AuthContextServiceTest.java | 2 +
.../service/auth/GraphSpaceUserServiceTest.java | 266 ++++++++++++++++++++-
.../service/op/LiveOperationsCollectorTest.java | 40 +++-
.../service/op/OperationsPayloadParserTest.java | 19 ++
.../apache/hugegraph/unit/AuthSecurityTest.java | 3 +-
.../unit/UserServiceCompatibilityTest.java | 101 ++++++++
hugegraph-hubble/hubble-fe/src/api/auth.js | 9 +-
.../src/modules/navigation/ConsoleItem/index.js | 3 +-
.../hubble-fe/src/pages/Account/EditLayer.js | 12 +-
.../hubble-fe/src/pages/Account/SpaceAccess.js | 3 +-
hugegraph-hubble/hubble-fe/src/utils/rules.test.js | 8 +-
27 files changed, 684 insertions(+), 99 deletions(-)
diff --git a/.editorconfig b/.editorconfig
index cf79877b0..4a6e05ecb 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -21,9 +21,9 @@ root = true
charset = utf-8
end_of_line = lf
insert_final_newline = true
-max_line_length = 100
+max_line_length = 120
ij_wrap_on_typing = true
-ij_visual_guides = 100
+ij_visual_guides = 120
[*.{java,xml,py}]
diff --git a/.github/actions/upload-coverage/action.yml b/.github/actions/upload-coverage/action.yml
index 143da955b..bf8ba8cd8 100644
--- a/.github/actions/upload-coverage/action.yml
+++ b/.github/actions/upload-coverage/action.yml
@@ -17,4 +17,5 @@ runs:
uses: codecov/codecov-action@v7
with:
token: ${{ inputs.token }}
+ slug: ${{ github.repository }}
files: ${{ inputs.file }}
diff --git a/.github/workflows/client-ci.yml b/.github/workflows/client-ci.yml
index e076dd256..90d7e8110 100644
--- a/.github/workflows/client-ci.yml
+++ b/.github/workflows/client-ci.yml
@@ -68,4 +68,4 @@ jobs:
uses: ./.github/actions/upload-coverage
with:
token: ${{ secrets.CODECOV_TOKEN }}
- file: target/jacoco.xml
+ file: hugegraph-client/target/jacoco.xml
diff --git a/.github/workflows/hubble-ci.yml b/.github/workflows/hubble-ci.yml
index d61370480..ae7290aa4 100644
--- a/.github/workflows/hubble-ci.yml
+++ b/.github/workflows/hubble-ci.yml
@@ -208,4 +208,4 @@ jobs:
uses: ./.github/actions/upload-coverage
with:
token: ${{ secrets.CODECOV_TOKEN }}
- file: target/site/jacoco/*.xml
+ file: hugegraph-hubble/hubble-be/target/jacoco.xml
diff --git a/.github/workflows/loader-ci.yml b/.github/workflows/loader-ci.yml
index f8eee02d3..8389ecab6 100644
--- a/.github/workflows/loader-ci.yml
+++ b/.github/workflows/loader-ci.yml
@@ -94,4 +94,4 @@ jobs:
uses: ./.github/actions/upload-coverage
with:
token: ${{ secrets.CODECOV_TOKEN }}
- file: target/jacoco.xml
+ file: hugegraph-loader/target/jacoco.xml
diff --git a/.github/workflows/tools-ci.yml b/.github/workflows/tools-ci.yml
index 340f71aa1..4613bcd0f 100644
--- a/.github/workflows/tools-ci.yml
+++ b/.github/workflows/tools-ci.yml
@@ -59,9 +59,3 @@ jobs:
- name: Run test
run: |
mvn test -Dtest=FuncTestSuite -pl hugegraph-tools -ntp
-
- - name: Upload coverage to Codecov
- uses: ./.github/actions/upload-coverage
- with:
- token: ${{ secrets.CODECOV_TOKEN }}
- file: target/jacoco.xml
diff --git a/.serena/memories/README_INDEX.md b/.serena/memories/README_INDEX.md
index 3f506906f..4f5dd6806 100644
--- a/.serena/memories/README_INDEX.md
+++ b/.serena/memories/README_INDEX.md
@@ -212,7 +212,7 @@ git --no-pager diff HEAD~1
4. ❌ **DON'T** use `System.out.println` (use logger instead)
5. ❌ **DON'T** forget Apache 2.0 license headers
6. ❌ **DON'T** use tabs (use 4 spaces for Java, 2 for frontend)
-7. ❌ **DON'T** exceed 100 character line length
+7. ❌ **DON'T** exceed 120 character line length
8. ❌ **DON'T** commit code that fails CI checks
## Getting Help
diff --git a/.serena/memories/code_style_and_conventions.md b/.serena/memories/code_style_and_conventions.md
index 0c14759c6..594063a5f 100644
--- a/.serena/memories/code_style_and_conventions.md
+++ b/.serena/memories/code_style_and_conventions.md
@@ -12,7 +12,7 @@
### Basic Formatting
- **Indentation**: 4 spaces (NO TABS)
- **Continuation Indent**: 8 spaces
-- **Line Length**: Maximum 100 characters
+- **Line Length**: Maximum 120 characters
- **Line Wrapping**: Enabled for long lines
- **Blank Lines**:
- Keep max 1 blank line in declarations
diff --git a/README.md b/README.md
index 3edc10958..bdc68a7cd 100644
--- a/README.md
+++ b/README.md
@@ -388,7 +388,7 @@ mvn clean install -DskipTests -Dmaven.javadoc.skip=true -ntp
### Code Style
Checkstyle is enforced via `tools/checkstyle.xml`:
-- Max line length: 100 characters
+- Max line length: 120 characters
- 4-space indentation (no tabs)
- No star imports
- No `System.out.println`
diff --git a/hugegraph-hubble/.prettierrc b/hugegraph-hubble/.prettierrc
index afb777869..b6d7333f3 100644
--- a/hugegraph-hubble/.prettierrc
+++ b/hugegraph-hubble/.prettierrc
@@ -1,6 +1,6 @@
{
"singleQuote": true,
"tabWidth": 2,
- "printWidth": 80,
+ "printWidth": 120,
"trailingComma": "none"
}
diff --git a/hugegraph-hubble/hubble-be/src/main/java/org/apache/hugegraph/controller/auth/GraphSpaceUserController.java b/hugegraph-hubble/hubble-be/src/main/java/org/apache/hugegraph/controller/auth/GraphSpaceUserController.java
index 692c385d0..03dd4fb70 100644
--- a/hugegraph-hubble/hubble-be/src/main/java/org/apache/hugegraph/controller/auth/GraphSpaceUserController.java
+++ b/hugegraph-hubble/hubble-be/src/main/java/org/apache/hugegraph/controller/auth/GraphSpaceUserController.java
@@ -145,10 +145,8 @@ public class GraphSpaceUserController extends AuthController {
private HugeClient requireMemberManager(String graphSpace,
String userId) {
HugeClient client = this.requireGraphSpaceManager(graphSpace);
- User account = client.auth().getUser(userId);
- if (account != null &&
- client.auth().listSpaceAdmin(graphSpace)
- .contains(account.name())) {
+ if (client.auth().listSuperAdmin().contains(userId) ||
+ client.auth().listSpaceAdmin(graphSpace).contains(userId)) {
return this.requireGraphSpaceAuthorizationAdmin(graphSpace);
}
return client;
diff --git a/hugegraph-hubble/hubble-be/src/main/java/org/apache/hugegraph/service/auth/AuthContextService.java b/hugegraph-hubble/hubble-be/src/main/java/org/apache/hugegraph/service/auth/AuthContextService.java
index 8e0c19105..c0e699172 100644
--- a/hugegraph-hubble/hubble-be/src/main/java/org/apache/hugegraph/service/auth/AuthContextService.java
+++ b/hugegraph-hubble/hubble-be/src/main/java/org/apache/hugegraph/service/auth/AuthContextService.java
@@ -209,8 +209,8 @@ public class AuthContextService {
capabilities.add(GRAPHSPACES_MANAGE);
}
}
- if (pdEnabled && (SUPERADMIN.equals(role) ||
- SPACEADMIN.equals(role))) {
+ if (pdEnabled && permissionPresets &&
+ (SUPERADMIN.equals(role) || SPACEADMIN.equals(role))) {
capabilities.add(GRAPHSPACE_MEMBERS_MANAGE);
}
if (pdEnabled && SUPERADMIN.equals(role)) {
diff --git a/hugegraph-hubble/hubble-be/src/main/java/org/apache/hugegraph/service/auth/GraphSpaceUserService.java b/hugegraph-hubble/hubble-be/src/main/java/org/apache/hugegraph/service/auth/GraphSpaceUserService.java
index d02ea3538..7feeadedc 100644
--- a/hugegraph-hubble/hubble-be/src/main/java/org/apache/hugegraph/service/auth/GraphSpaceUserService.java
+++ b/hugegraph-hubble/hubble-be/src/main/java/org/apache/hugegraph/service/auth/GraphSpaceUserService.java
@@ -223,27 +223,56 @@ public class GraphSpaceUserService extends AuthService {
String userId, String preset) {
requirePermissionPreset(preset);
requirePermissionPresets(client);
- User account = client.auth().getUser(userId);
- E.checkNotNull(account, "User");
- this.clearCustomRoles(client, graphSpace, userId);
- this.clearDefaultRoles(client, graphSpace, account.name());
- if (!client.auth().listSpaceMember(graphSpace)
- .contains(account.name())) {
- client.auth().addSpaceMember(account.name(), graphSpace);
- }
- if ("GS_ADMIN".equals(preset)) {
- if (!client.auth().listSpaceAdmin(graphSpace)
- .contains(account.name())) {
- client.auth().addSpaceAdmin(account.name(), graphSpace);
+ E.checkArgument(!client.auth().listSuperAdmin().contains(userId),
+ "Can't assign GraphSpace preset to super " +
+ "administrator '%s'", userId);
+ boolean wasMember =
+ client.auth().listSpaceMember(graphSpace).contains(userId);
+ String username = userId;
+ SpacePresetState previous = null;
+ try {
+ if (!wasMember) {
+ client.auth().addSpaceMember(userId, graphSpace);
}
- this.setDefaultRole(client, graphSpace, account.name(), "analyst");
- return;
- }
- if (client.auth().listSpaceAdmin(graphSpace).contains(account.name())) {
- client.auth().delSpaceAdmin(account.name(), graphSpace);
+ User account = client.auth().getUser(userId);
+ E.checkNotNull(account, "User");
+ username = account.name();
+ previous = this.capturePresetState(client, graphSpace, userId,
+ username, wasMember);
+ previous.customRoles.forEach(
+ belong -> this.belongService.deleteById(
+ client, graphSpace, belong.getId()));
+ if (previous.analyst) {
+ client.graphSpace().deleteDefaultRole(
+ graphSpace, username, "analyst");
+ }
+ if (previous.observer) {
+ client.graphSpace().deleteDefaultRole(
+ graphSpace, username, "observer");
+ }
+ if ("GS_ADMIN".equals(preset)) {
+ if (!previous.admin) {
+ client.auth().addSpaceAdmin(username, graphSpace);
+ }
+ this.setDefaultRole(client, graphSpace, username, "analyst");
+ return;
+ }
+ if (previous.admin) {
+ client.auth().delSpaceAdmin(username, graphSpace);
+ }
+ String role = "GS_READ_ONLY".equals(preset) ?
+ "observer" : "analyst";
+ this.setDefaultRole(client, graphSpace, username, role);
+ } catch (RuntimeException e) {
+ if (previous == null) {
+ this.rollbackNewMember(client, graphSpace, userId,
+ wasMember, e);
+ } else {
+ this.restorePresetState(client, graphSpace, userId, username,
+ previous, e);
+ }
+ throw e;
}
- String role = "GS_READ_ONLY".equals(preset) ? "observer" : "analyst";
- this.setDefaultRole(client, graphSpace, account.name(), role);
}
public void removeSpacePreset(HugeClient client, String graphSpace,
@@ -258,6 +287,97 @@ public class GraphSpaceUserService extends AuthService {
}
}
+ private SpacePresetState capturePresetState(HugeClient client,
+ String graphSpace,
+ String userId,
+ String username,
+ boolean member) {
+ List<BelongEntity> customRoles = this.belongService.list(
+ client, graphSpace, null, userId);
+ boolean analyst = client.graphSpace().checkDefaultRole(
+ graphSpace, username, "analyst");
+ boolean observer = client.graphSpace().checkDefaultRole(
+ graphSpace, username, "observer");
+ boolean admin = client.auth().listSpaceAdmin(graphSpace)
+ .contains(username);
+ return new SpacePresetState(customRoles, member, admin,
+ analyst, observer);
+ }
+
+ private void restorePresetState(HugeClient client, String graphSpace,
+ String userId, String username,
+ SpacePresetState previous,
+ RuntimeException failure) {
+ previous.customRoles.forEach(belong -> {
+ this.tryRestore(() -> {
+ Set<String> currentRoles = this.belongService.list(
+ client, graphSpace, null, userId).stream()
+ .map(BelongEntity::getRoleId)
+ .collect(Collectors.toSet());
+ if (!currentRoles.contains(belong.getRoleId())) {
+ this.belongService.add(client, graphSpace,
+ belong.getRoleId(), userId);
+ }
+ }, graphSpace, userId,
+ "custom role " + belong.getRoleId(), failure);
+ });
+ this.tryRestore(() -> this.restoreDefaultRole(
+ client, graphSpace, username, "analyst", previous.analyst),
+ graphSpace, userId, "analyst role", failure);
+ this.tryRestore(() -> this.restoreDefaultRole(
+ client, graphSpace, username, "observer", previous.observer),
+ graphSpace, userId, "observer role", failure);
+ this.tryRestore(() -> {
+ boolean current = client.auth().listSpaceAdmin(graphSpace)
+ .contains(username);
+ if (previous.admin && !current) {
+ client.auth().addSpaceAdmin(username, graphSpace);
+ } else if (!previous.admin && current) {
+ client.auth().delSpaceAdmin(username, graphSpace);
+ }
+ }, graphSpace, userId, "administrator", failure);
+ this.rollbackNewMember(client, graphSpace, userId,
+ previous.member, failure);
+ }
+
+ private void restoreDefaultRole(HugeClient client, String graphSpace,
+ String username, String role,
+ boolean expected) {
+ boolean current = client.graphSpace().checkDefaultRole(
+ graphSpace, username, role);
+ if (expected && !current) {
+ client.graphSpace().setDefaultRole(graphSpace, username, role);
+ } else if (!expected && current) {
+ client.graphSpace().deleteDefaultRole(graphSpace, username, role);
+ }
+ }
+
+ private void rollbackNewMember(HugeClient client, String graphSpace,
+ String userId, boolean expected,
+ RuntimeException failure) {
+ this.tryRestore(() -> {
+ boolean current = client.auth().listSpaceMember(graphSpace)
+ .contains(userId);
+ if (expected && !current) {
+ client.auth().addSpaceMember(userId, graphSpace);
+ } else if (!expected && current) {
+ client.auth().delSpaceMember(userId, graphSpace);
+ }
+ }, graphSpace, userId, "membership", failure);
+ }
+
+ private void tryRestore(Runnable action, String graphSpace,
+ String userId, String state,
+ RuntimeException failure) {
+ try {
+ action.run();
+ } catch (RuntimeException rollbackFailure) {
+ failure.addSuppressed(rollbackFailure);
+ log.warn("Failed to restore GraphSpace {} for '{}' in '{}'",
+ state, userId, graphSpace, rollbackFailure);
+ }
+ }
+
private static void requireLegacyRoleAssignments(HugeClient client) {
if (client.supportsDefaultRole()) {
throw new ParameterizedException(
@@ -367,4 +487,23 @@ public class GraphSpaceUserService extends AuthService {
}
return users;
}
+
+ private static class SpacePresetState {
+
+ private final List<BelongEntity> customRoles;
+ private final boolean member;
+ private final boolean admin;
+ private final boolean analyst;
+ private final boolean observer;
+
+ private SpacePresetState(List<BelongEntity> customRoles,
+ boolean member, boolean admin,
+ boolean analyst, boolean observer) {
+ this.customRoles = customRoles;
+ this.member = member;
+ this.admin = admin;
+ this.analyst = analyst;
+ this.observer = observer;
+ }
+ }
}
diff --git a/hugegraph-hubble/hubble-be/src/main/java/org/apache/hugegraph/service/op/OperationsPayloadParser.java b/hugegraph-hubble/hubble-be/src/main/java/org/apache/hugegraph/service/op/OperationsPayloadParser.java
index f36e946c8..c5631914d 100644
--- a/hugegraph-hubble/hubble-be/src/main/java/org/apache/hugegraph/service/op/OperationsPayloadParser.java
+++ b/hugegraph-hubble/hubble-be/src/main/java/org/apache/hugegraph/service/op/OperationsPayloadParser.java
@@ -565,16 +565,27 @@ public class OperationsPayloadParser {
return "UNKNOWN";
}
String value = state.toUpperCase(Locale.ROOT);
- if (value.contains("OK") || value.equals("UP")) {
- return "UP";
- }
- if (value.contains("WARN") || value.contains("DEGRADED")) {
- return "DEGRADED";
+ switch (value) {
+ case "CLUSTER_OK":
+ case "OK":
+ case "UP":
+ return "UP";
+ case "CLUSTER_WARN":
+ case "CLUSTER_NOT_READY":
+ case "CLUSTER_OFFLINE":
+ case "WARN":
+ case "WARNING":
+ case "DEGRADED":
+ case "NOT_READY":
+ case "OFFLINE":
+ return "DEGRADED";
+ case "CLUSTER_FAULT":
+ case "FAULT":
+ case "DOWN":
+ return "DOWN";
+ default:
+ return "UNKNOWN";
}
- if (value.contains("DOWN") || value.contains("FAULT")) {
- return "DOWN";
- }
- return "UNKNOWN";
}
private String health(String state) {
diff --git a/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/controller/auth/GraphSpaceAuthMutationAuthorizationTest.java b/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/controller/auth/GraphSpaceAuthMutationAuthorizationTest.java
index 9cf151c9e..e96ae5bac 100644
--- a/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/controller/auth/GraphSpaceAuthMutationAuthorizationTest.java
+++ b/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/controller/auth/GraphSpaceAuthMutationAuthorizationTest.java
@@ -275,7 +275,8 @@ public class GraphSpaceAuthMutationAuthorizationTest {
.contentType(MediaType.APPLICATION_JSON)
.content("{\"role_id\":\"r\",\"user_id\":\"u\"}"));
assertForbidden(mvc(access),
- post("/api/v1.3/graphspaces/SPACE/auth/accesses").contentType(MediaType.APPLICATION_JSON).content("{}"));
+ post("/api/v1.3/graphspaces/SPACE/auth/accesses")
+ .contentType(MediaType.APPLICATION_JSON).content("{}"));
assertForbidden(mvc(target),
post("/api/v1.3/graphspaces/SPACE/auth/targets").contentType(MediaType.APPLICATION_JSON).content("{}"));
}
diff --git a/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/controller/auth/GraphSpaceAuthOwnershipTest.java b/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/controller/auth/GraphSpaceAuthOwnershipTest.java
index bf8a7d083..3eaefb867 100644
--- a/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/controller/auth/GraphSpaceAuthOwnershipTest.java
+++ b/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/controller/auth/GraphSpaceAuthOwnershipTest.java
@@ -86,6 +86,8 @@ public class GraphSpaceAuthOwnershipTest {
.thenReturn(Collections.emptyList());
Mockito.when(this.auth.listSpaceMember(Mockito.anyString()))
.thenReturn(Collections.emptyList());
+ Mockito.when(this.auth.listSuperAdmin())
+ .thenReturn(Collections.emptyList());
}
@Test
@@ -474,7 +476,8 @@ public class GraphSpaceAuthOwnershipTest {
.id("belong-a")
.userId("user-id")
.build();
- Mockito.when(belongs.list(this.client, "SPACE_A", null, "user-id")).thenReturn(Collections.singletonList(scoped));
+ Mockito.when(belongs.list(this.client, "SPACE_A", null, "user-id"))
+ .thenReturn(Collections.singletonList(scoped));
GraphSpaceUserService service = new GraphSpaceUserService();
ReflectionTestUtils.setField(service, "belongService", belongs);
@@ -490,15 +493,18 @@ public class GraphSpaceAuthOwnershipTest {
@Test
public void testReadOnlyPresetAppliesSpaceWideObserver() {
User account = new User();
- account.setId("user-id");
+ account.setId("graph-user");
account.name("graph-user");
- Mockito.when(this.auth.getUser("user-id")).thenReturn(account);
+ Mockito.when(this.auth.getUser("graph-user")).thenReturn(account);
BelongService belongs = Mockito.mock(BelongService.class);
- Mockito.when(belongs.list(this.client, "SPACE_A", null, "user-id")).thenReturn(Collections.emptyList());
+ Mockito.when(belongs.list(this.client, "SPACE_A", null,
+ "graph-user"))
+ .thenReturn(Collections.emptyList());
GraphSpaceUserService service = new GraphSpaceUserService();
ReflectionTestUtils.setField(service, "belongService", belongs);
- service.applySpacePreset(this.client, "SPACE_A", "user-id", "GS_READ_ONLY");
+ service.applySpacePreset(this.client, "SPACE_A", "graph-user",
+ "GS_READ_ONLY");
Mockito.verify(this.auth).addSpaceMember("graph-user", "SPACE_A");
Mockito.verify(this.graphSpace).setDefaultRole(
@@ -588,6 +594,63 @@ public class GraphSpaceAuthOwnershipTest {
.andExpect(status().isForbidden());
}
+ @Test
+ public void testSpaceAdminCanAssignPresetToNewMemberWithoutPreRead()
+ throws Exception {
+ UserService authorization = Mockito.mock(UserService.class);
+ Mockito.when(authorization.isAssignSpaceAdmin(this.client, "SPACE"))
+ .thenReturn(true);
+ GraphSpaceUserService members =
+ Mockito.mock(GraphSpaceUserService.class);
+ TestGraphSpaceUserController controller =
+ new TestGraphSpaceUserController(this.client);
+ setBaseUserService(controller, authorization);
+ ReflectionTestUtils.setField(controller, "userService", members);
+ MockMvc mvc = MockMvcBuilders.standaloneSetup(controller)
+ .setControllerAdvice(
+ new ExceptionAdvisor())
+ .build();
+
+ mvc.perform(put("/api/v1.3/graphspaces/SPACE/auth/users/" +
+ "new-member/preset")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("{\"permission_preset\":\"GS_READ_WRITE\"}"))
+ .andExpect(status().isOk());
+
+ Mockito.verify(members).applySpacePreset(
+ this.client, "SPACE", "new-member", "GS_READ_WRITE");
+ Mockito.verify(this.auth, Mockito.never())
+ .getUser(Mockito.anyString());
+ }
+
+ @Test
+ public void testSpaceAdminCannotChangeGlobalAdminPreset()
+ throws Exception {
+ Mockito.when(this.auth.listSuperAdmin())
+ .thenReturn(Collections.singletonList("global-admin"));
+ UserService authorization = Mockito.mock(UserService.class);
+ Mockito.when(authorization.isAssignSpaceAdmin(this.client, "SPACE"))
+ .thenReturn(true);
+ GraphSpaceUserService members =
+ Mockito.mock(GraphSpaceUserService.class);
+ TestGraphSpaceUserController controller =
+ new TestGraphSpaceUserController(this.client);
+ setBaseUserService(controller, authorization);
+ ReflectionTestUtils.setField(controller, "userService", members);
+ MockMvc mvc = MockMvcBuilders.standaloneSetup(controller)
+ .setControllerAdvice(
+ new ExceptionAdvisor())
+ .build();
+
+ mvc.perform(put("/api/v1.3/graphspaces/SPACE/auth/users/" +
+ "global-admin/preset")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("{\"permission_preset\":\"GS_READ_WRITE\"}"))
+ .andExpect(status().isForbidden());
+
+ Mockito.verifyZeroInteractions(members);
+ }
+
private static Target target(String id, String graphSpace) {
Target target = new Target();
target.setId(id);
diff --git a/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/service/auth/AuthContextServiceTest.java b/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/service/auth/AuthContextServiceTest.java
index 3eb8eecc6..7f1b082b3 100644
--- a/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/service/auth/AuthContextServiceTest.java
+++ b/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/service/auth/AuthContextServiceTest.java
@@ -116,6 +116,8 @@ public class AuthContextServiceTest {
Assert.assertFalse(capabilities(context).contains(
"account_permission_presets"));
+ Assert.assertFalse(capabilities(context).contains(
+ "graphspace_members_manage"));
Assert.assertTrue(actions(context, "members").isEmpty());
Assert.assertTrue(actions(context, "roles").isEmpty());
Assert.assertTrue(actions(context, "authorizations").isEmpty());
diff --git a/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/service/auth/GraphSpaceUserServiceTest.java b/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/service/auth/GraphSpaceUserServiceTest.java
index acace9f1d..a9489d97c 100644
--- a/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/service/auth/GraphSpaceUserServiceTest.java
+++ b/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/service/auth/GraphSpaceUserServiceTest.java
@@ -23,6 +23,7 @@ import java.util.Map;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.InOrder;
import org.mockito.Mockito;
import org.springframework.test.util.ReflectionTestUtils;
@@ -56,6 +57,8 @@ public class GraphSpaceUserServiceTest {
Mockito.when(this.client.auth()).thenReturn(this.auth);
Mockito.when(this.client.graphSpace()).thenReturn(this.graphSpace);
Mockito.when(this.client.supportsDefaultRole()).thenReturn(true);
+ Mockito.when(this.auth.listSuperAdmin())
+ .thenReturn(Collections.emptyList());
}
@Test
@@ -155,14 +158,14 @@ public class GraphSpaceUserServiceTest {
@Test
public void testApplyReadOnlyPresetAndRemoveUnrequestedSpace() {
- User user = user("u-1", "alice");
+ User user = user("alice", "alice");
Mockito.when(this.client.findUserByName("alice")).thenReturn(user);
- Mockito.when(this.auth.getUser("u-1")).thenReturn(user);
+ Mockito.when(this.auth.getUser("alice")).thenReturn(user);
Mockito.when(this.graphSpace.listGraphSpace())
.thenReturn(java.util.Arrays.asList("team", "old"));
Mockito.when(this.belongService.list(
Mockito.eq(this.client), Mockito.anyString(),
- Mockito.isNull(), Mockito.eq("u-1")))
+ Mockito.isNull(), Mockito.eq("alice")))
.thenReturn(Collections.emptyList());
Mockito.when(this.auth.listSpaceMember("team"))
.thenReturn(Collections.emptyList());
@@ -184,17 +187,17 @@ public class GraphSpaceUserServiceTest {
@Test
public void testApplyAdminPresetAddsManagementAndWriteAccess() {
- User user = user("u-1", "alice");
- Mockito.when(this.auth.getUser("u-1")).thenReturn(user);
+ User user = user("alice", "alice");
+ Mockito.when(this.auth.getUser("alice")).thenReturn(user);
Mockito.when(this.belongService.list(
- this.client, "team", null, "u-1"))
+ this.client, "team", null, "alice"))
.thenReturn(Collections.emptyList());
Mockito.when(this.auth.listSpaceMember("team"))
.thenReturn(Collections.emptyList());
Mockito.when(this.auth.listSpaceAdmin("team"))
.thenReturn(Collections.emptyList());
- this.service.applySpacePreset(this.client, "team", "u-1",
+ this.service.applySpacePreset(this.client, "team", "alice",
"GS_ADMIN");
Mockito.verify(this.auth).addSpaceMember("alice", "team");
@@ -205,17 +208,17 @@ public class GraphSpaceUserServiceTest {
@Test
public void testApplyReadWritePresetRemovesAdminAccess() {
- User user = user("u-1", "alice");
- Mockito.when(this.auth.getUser("u-1")).thenReturn(user);
+ User user = user("alice", "alice");
+ Mockito.when(this.auth.getUser("alice")).thenReturn(user);
Mockito.when(this.belongService.list(
- this.client, "team", null, "u-1"))
+ this.client, "team", null, "alice"))
.thenReturn(Collections.emptyList());
Mockito.when(this.auth.listSpaceMember("team"))
.thenReturn(Collections.singletonList("alice"));
Mockito.when(this.auth.listSpaceAdmin("team"))
.thenReturn(Collections.singletonList("alice"));
- this.service.applySpacePreset(this.client, "team", "u-1",
+ this.service.applySpacePreset(this.client, "team", "alice",
"GS_READ_WRITE");
Mockito.verify(this.auth).delSpaceAdmin("alice", "team");
@@ -225,6 +228,247 @@ public class GraphSpaceUserServiceTest {
.addSpaceMember(Mockito.anyString(), Mockito.anyString());
}
+ @Test
+ public void testApplyPresetAddsMemberBeforeScopedUserRead() {
+ User user = user("alice", "alice");
+ Mockito.when(this.auth.listSpaceMember("team"))
+ .thenReturn(Collections.emptyList());
+ Mockito.when(this.auth.getUser("alice")).thenReturn(user);
+ Mockito.when(this.belongService.list(
+ this.client, "team", null, "alice"))
+ .thenReturn(Collections.emptyList());
+ Mockito.when(this.auth.listSpaceAdmin("team"))
+ .thenReturn(Collections.emptyList());
+
+ this.service.applySpacePreset(this.client, "team", "alice",
+ "GS_READ_ONLY");
+
+ InOrder order = Mockito.inOrder(this.auth);
+ order.verify(this.auth).addSpaceMember("alice", "team");
+ order.verify(this.auth).getUser("alice");
+ Mockito.verify(this.graphSpace)
+ .setDefaultRole("team", "alice", "observer");
+ }
+
+ @Test
+ public void testApplyPresetRollsBackBootstrapMemberOnUserReadFailure() {
+ Mockito.when(this.auth.listSpaceMember("team"))
+ .thenReturn(Collections.emptyList())
+ .thenReturn(Collections.singletonList("alice"));
+ Mockito.when(this.auth.getUser("alice"))
+ .thenThrow(new RuntimeException("read failed"));
+
+ Assert.assertThrows(RuntimeException.class,
+ () -> this.service.applySpacePreset(
+ this.client, "team", "alice",
+ "GS_READ_ONLY"));
+
+ InOrder order = Mockito.inOrder(this.auth);
+ order.verify(this.auth).addSpaceMember("alice", "team");
+ order.verify(this.auth).getUser("alice");
+ order.verify(this.auth).delSpaceMember("alice", "team");
+ }
+
+ @Test
+ public void testApplyPresetReconcilesMemberAfterLostAddResponse() {
+ Mockito.when(this.auth.listSpaceMember("team"))
+ .thenReturn(Collections.emptyList())
+ .thenReturn(Collections.singletonList("alice"));
+ Mockito.when(this.auth.addSpaceMember("alice", "team"))
+ .thenThrow(new RuntimeException("response lost"));
+
+ Assert.assertThrows(RuntimeException.class,
+ () -> this.service.applySpacePreset(
+ this.client, "team", "alice",
+ "GS_READ_ONLY"));
+
+ Mockito.verify(this.auth).delSpaceMember("alice", "team");
+ Mockito.verify(this.auth, Mockito.never()).getUser("alice");
+ }
+
+ @Test
+ public void testApplyPresetRollsBackMemberOnDefaultRoleFailure() {
+ User user = user("alice", "alice");
+ Mockito.when(this.auth.listSpaceMember("team"))
+ .thenReturn(Collections.emptyList())
+ .thenReturn(Collections.singletonList("alice"));
+ Mockito.when(this.auth.getUser("alice")).thenReturn(user);
+ Mockito.when(this.belongService.list(
+ this.client, "team", null, "alice"))
+ .thenReturn(Collections.emptyList());
+ Mockito.when(this.auth.listSpaceAdmin("team"))
+ .thenReturn(Collections.emptyList());
+ Mockito.when(this.graphSpace.setDefaultRole(
+ "team", "alice", "observer"))
+ .thenThrow(new RuntimeException("role failed"));
+
+ Assert.assertThrows(RuntimeException.class,
+ () -> this.service.applySpacePreset(
+ this.client, "team", "alice",
+ "GS_READ_ONLY"));
+
+ InOrder order = Mockito.inOrder(this.auth, this.graphSpace);
+ order.verify(this.auth).addSpaceMember("alice", "team");
+ order.verify(this.auth).getUser("alice");
+ order.verify(this.graphSpace)
+ .setDefaultRole("team", "alice", "observer");
+ order.verify(this.auth).delSpaceMember("alice", "team");
+ }
+
+ @Test
+ public void testApplyAdminPresetRollsBackAdminAndMemberOnRoleFailure() {
+ User user = user("alice", "alice");
+ Mockito.when(this.auth.listSpaceMember("team"))
+ .thenReturn(Collections.emptyList())
+ .thenReturn(Collections.singletonList("alice"));
+ Mockito.when(this.auth.getUser("alice")).thenReturn(user);
+ Mockito.when(this.belongService.list(
+ this.client, "team", null, "alice"))
+ .thenReturn(Collections.emptyList());
+ Mockito.when(this.auth.listSpaceAdmin("team"))
+ .thenReturn(Collections.emptyList())
+ .thenReturn(Collections.singletonList("alice"));
+ Mockito.when(this.graphSpace.setDefaultRole(
+ "team", "alice", "analyst"))
+ .thenThrow(new RuntimeException("role failed"));
+
+ Assert.assertThrows(RuntimeException.class,
+ () -> this.service.applySpacePreset(
+ this.client, "team", "alice",
+ "GS_ADMIN"));
+
+ InOrder order = Mockito.inOrder(this.auth, this.graphSpace);
+ order.verify(this.auth).addSpaceMember("alice", "team");
+ order.verify(this.auth).getUser("alice");
+ order.verify(this.auth).addSpaceAdmin("alice", "team");
+ order.verify(this.graphSpace)
+ .setDefaultRole("team", "alice", "analyst");
+ order.verify(this.auth).delSpaceAdmin("alice", "team");
+ order.verify(this.auth).delSpaceMember("alice", "team");
+ }
+
+ @Test
+ public void testApplyPresetRestoresExistingAdminStateOnFailure() {
+ User user = user("alice", "alice");
+ BelongEntity custom = BelongEntity.builder()
+ .id("belong-1")
+ .userId("alice")
+ .roleId("role-1")
+ .build();
+ Mockito.when(this.auth.listSpaceMember("team"))
+ .thenReturn(Collections.singletonList("alice"));
+ Mockito.when(this.auth.getUser("alice")).thenReturn(user);
+ Mockito.when(this.belongService.list(
+ this.client, "team", null, "alice"))
+ .thenReturn(Collections.singletonList(custom))
+ .thenReturn(Collections.emptyList());
+ Mockito.when(this.graphSpace.checkDefaultRole(
+ "team", "alice", "analyst"))
+ .thenReturn(true, false);
+ Mockito.when(this.graphSpace.checkDefaultRole(
+ "team", "alice", "observer"))
+ .thenReturn(false);
+ Mockito.when(this.auth.listSpaceAdmin("team"))
+ .thenReturn(Collections.singletonList("alice"))
+ .thenReturn(Collections.emptyList());
+ Mockito.when(this.graphSpace.setDefaultRole(
+ "team", "alice", "analyst"))
+ .thenThrow(new RuntimeException("role failed"))
+ .thenReturn(Collections.emptyMap());
+
+ Assert.assertThrows(RuntimeException.class,
+ () -> this.service.applySpacePreset(
+ this.client, "team", "alice",
+ "GS_READ_WRITE"));
+
+ Mockito.verify(this.belongService)
+ .add(this.client, "team", "role-1", "alice");
+ Mockito.verify(this.graphSpace, Mockito.times(2))
+ .setDefaultRole("team", "alice", "analyst");
+ Mockito.verify(this.auth).addSpaceAdmin("alice", "team");
+ Mockito.verify(this.auth, Mockito.never())
+ .delSpaceMember(Mockito.anyString(), Mockito.anyString());
+ }
+
+ @Test
+ public void testApplyPresetContinuesRestoringCustomRolesAfterFailure() {
+ User user = user("alice", "alice");
+ BelongEntity first = BelongEntity.builder()
+ .id("belong-1")
+ .userId("alice")
+ .roleId("role-1")
+ .build();
+ BelongEntity second = BelongEntity.builder()
+ .id("belong-2")
+ .userId("alice")
+ .roleId("role-2")
+ .build();
+ Mockito.when(this.auth.listSpaceMember("team"))
+ .thenReturn(Collections.singletonList("alice"));
+ Mockito.when(this.auth.getUser("alice")).thenReturn(user);
+ Mockito.when(this.belongService.list(
+ this.client, "team", null, "alice"))
+ .thenReturn(java.util.Arrays.asList(first, second))
+ .thenReturn(Collections.emptyList())
+ .thenReturn(Collections.emptyList());
+ Mockito.when(this.graphSpace.checkDefaultRole(
+ "team", "alice", "analyst"))
+ .thenReturn(false);
+ Mockito.when(this.graphSpace.checkDefaultRole(
+ "team", "alice", "observer"))
+ .thenReturn(false);
+ Mockito.when(this.auth.listSpaceAdmin("team"))
+ .thenReturn(Collections.emptyList());
+ Mockito.when(this.graphSpace.setDefaultRole(
+ "team", "alice", "observer"))
+ .thenThrow(new RuntimeException("role failed"));
+ Mockito.doThrow(new RuntimeException("first restore failed"))
+ .when(this.belongService)
+ .add(this.client, "team", "role-1", "alice");
+
+ Assert.assertThrows(RuntimeException.class,
+ () -> this.service.applySpacePreset(
+ this.client, "team", "alice",
+ "GS_READ_ONLY"));
+
+ Mockito.verify(this.belongService)
+ .add(this.client, "team", "role-1", "alice");
+ Mockito.verify(this.belongService)
+ .add(this.client, "team", "role-2", "alice");
+ }
+
+ @Test
+ public void testApplyPresetRemovesRoleCommittedBeforeClientFailure() {
+ User user = user("alice", "alice");
+ Mockito.when(this.auth.listSpaceMember("team"))
+ .thenReturn(Collections.emptyList())
+ .thenReturn(Collections.singletonList("alice"));
+ Mockito.when(this.auth.getUser("alice")).thenReturn(user);
+ Mockito.when(this.belongService.list(
+ this.client, "team", null, "alice"))
+ .thenReturn(Collections.emptyList());
+ Mockito.when(this.graphSpace.checkDefaultRole(
+ "team", "alice", "analyst"))
+ .thenReturn(false);
+ Mockito.when(this.graphSpace.checkDefaultRole(
+ "team", "alice", "observer"))
+ .thenReturn(false, true);
+ Mockito.when(this.auth.listSpaceAdmin("team"))
+ .thenReturn(Collections.emptyList());
+ Mockito.when(this.graphSpace.setDefaultRole(
+ "team", "alice", "observer"))
+ .thenThrow(new RuntimeException("response lost"));
+
+ Assert.assertThrows(RuntimeException.class,
+ () -> this.service.applySpacePreset(
+ this.client, "team", "alice",
+ "GS_READ_ONLY"));
+
+ Mockito.verify(this.graphSpace)
+ .deleteDefaultRole("team", "alice", "observer");
+ Mockito.verify(this.auth).delSpaceMember("alice", "team");
+ }
+
@Test
public void testRemovePresetCleansEveryGrantType() {
User user = user("u-1", "alice");
diff --git a/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/service/op/LiveOperationsCollectorTest.java b/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/service/op/LiveOperationsCollectorTest.java
index bb5d4bf7b..daef59055 100644
--- a/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/service/op/LiveOperationsCollectorTest.java
+++ b/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/service/op/LiveOperationsCollectorTest.java
@@ -162,8 +162,7 @@ public class LiveOperationsCollectorTest {
}
@Test
- public void testPdDegradedStatusMakesOverallSnapshotDegraded()
- throws IOException {
+ public void testPdDegradedStatusMakesOverallSnapshotDegraded() throws IOException {
String degraded = cluster().replace("Cluster_OK", "Cluster_Warn");
HttpServer pd = pdServer(200, degraded, 200, stores());
Snapshot snapshot;
@@ -174,13 +173,36 @@ public class LiveOperationsCollectorTest {
}
Assert.assertEquals("DEGRADED", snapshot.getStatus());
- Assert.assertEquals("DEGRADED",
- snapshot.getSources().get("pd").getStatus());
+ Assert.assertEquals("DEGRADED", snapshot.getSources().get("pd").getStatus());
}
@Test
- public void testPdUnknownStatusMakesOverallSnapshotDegraded()
- throws IOException {
+ public void testPdNotReadyKeepsPdNodesUpAndOverallDegraded() throws IOException {
+ String notReady = cluster().replace("Cluster_OK", "Cluster_Not_Ready");
+ HttpServer pd = pdServer(200, notReady, 200, stores());
+ Snapshot snapshot;
+ try {
+ snapshot = collector(true, pd).collect(serverClient(), false);
+ } finally {
+ pd.stop(0);
+ }
+
+ OperationsModels.SourceStatus pdSource = snapshot.getSources().get("pd");
+ Assert.assertEquals("AVAILABLE", pdSource.getAvailability());
+ Assert.assertTrue(pdSource.isFresh());
+ Assert.assertEquals("DEGRADED", pdSource.getStatus());
+ Assert.assertEquals("DEGRADED", snapshot.getStatus());
+ long pdCount = snapshot.getNodes().stream()
+ .filter(node -> "PD".equals(node.getType()))
+ .count();
+ Assert.assertEquals(1L, pdCount);
+ Assert.assertTrue(snapshot.getNodes().stream()
+ .filter(node -> "PD".equals(node.getType()))
+ .allMatch(node -> "UP".equals(node.getStatus())));
+ }
+
+ @Test
+ public void testPdUnknownStatusMakesOverallSnapshotDegraded() throws IOException {
String unknown = cluster().replace("Cluster_OK", "Cluster_Starting");
HttpServer pd = pdServer(200, unknown, 200, stores());
Snapshot snapshot;
@@ -191,8 +213,7 @@ public class LiveOperationsCollectorTest {
}
Assert.assertEquals("DEGRADED", snapshot.getStatus());
- Assert.assertEquals("UNKNOWN",
- snapshot.getSources().get("pd").getStatus());
+ Assert.assertEquals("UNKNOWN", snapshot.getSources().get("pd").getStatus());
}
@Test
@@ -202,8 +223,7 @@ public class LiveOperationsCollectorTest {
Snapshot snapshot = collector.collect(serverClient(), false);
- Assert.assertEquals("UNKNOWN",
- snapshot.getSources().get("pd").getStatus());
+ Assert.assertEquals("DEGRADED", snapshot.getSources().get("pd").getStatus());
Assert.assertEquals(0, http.leaderRequests());
}
diff --git a/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/service/op/OperationsPayloadParserTest.java b/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/service/op/OperationsPayloadParserTest.java
index 805de2106..de195a44d 100644
--- a/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/service/op/OperationsPayloadParserTest.java
+++ b/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/service/op/OperationsPayloadParserTest.java
@@ -100,6 +100,25 @@ public class OperationsPayloadParserTest {
"capacity_used_bytes"));
}
+ @Test
+ public void testMapsExactClusterStates() {
+ OperationsPayloadParser parser = new OperationsPayloadParser(MAPPER);
+ String[] states = {"Cluster_OK", "Cluster_Warn", "Cluster_Not_Ready",
+ "Cluster_Offline", "Cluster_Fault", null, "Cluster_OKish"};
+ String[] expected = {"UP", "DEGRADED", "DEGRADED", "DEGRADED", "DOWN",
+ "UNKNOWN", "UNKNOWN"};
+ String stores = "{\"status\":0,\"data\":{\"stores\":[]}}";
+
+ for (int i = 0; i < states.length; i++) {
+ String state = states[i];
+ String field = state == null ? "" : "\"state\":\"" +
+ state + "\",";
+ String cluster = "{\"status\":0,\"data\":{" + field +
+ "\"pdList\":[]}}";
+ Assert.assertEquals(expected[i], parser.parseTopology(cluster, stores).getStatus());
+ }
+ }
+
@Test
public void testParsesOnlyStoreMetricTargetsAndInternalHostMap() {
OperationsPayloadParser parser = new OperationsPayloadParser(MAPPER);
diff --git a/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/unit/AuthSecurityTest.java b/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/unit/AuthSecurityTest.java
index 3c4e9b065..e947eb2a2 100644
--- a/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/unit/AuthSecurityTest.java
+++ b/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/unit/AuthSecurityTest.java
@@ -415,7 +415,8 @@ public class AuthSecurityTest {
.thenReturn(false);
AuthModeService mode = new AuthModeService(config);
ReflectionTestUtils.setField(interceptor, "authMode", mode);
- MockHttpServletRequest request = new MockHttpServletRequest("GET", "/api/v1.3/graphspaces/SPACE/graphs/graph/schema");
+ MockHttpServletRequest request = new MockHttpServletRequest(
+ "GET", "/api/v1.3/graphspaces/SPACE/graphs/graph/schema");
Assert.assertTrue(interceptor.preHandle(request, new MockHttpServletResponse(), null));
Assert.assertEquals(1, interceptor.unauthClients);
diff --git a/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/unit/UserServiceCompatibilityTest.java b/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/unit/UserServiceCompatibilityTest.java
index 2e60a2260..24a9947d3 100644
--- a/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/unit/UserServiceCompatibilityTest.java
+++ b/hugegraph-hubble/hubble-be/src/test/java/org/apache/hugegraph/unit/UserServiceCompatibilityTest.java
@@ -269,6 +269,107 @@ public class UserServiceCompatibilityTest {
Mockito.anyString(), Mockito.anyString());
}
+ @Test
+ public void testModernUserUpdateReconcilesAdminSpaces() {
+ Mockito.when(this.config.get(HubbleOptions.PD_ENABLED)).thenReturn(true);
+ Mockito.when(this.client.supportsDefaultRole()).thenReturn(true);
+ Mockito.when(this.graphSpace.listGraphSpace())
+ .thenReturn(Arrays.asList("OLD", "NEW"));
+ Mockito.when(this.auth.listSpaceAdmin("OLD"))
+ .thenReturn(Collections.singletonList("user"));
+ Mockito.when(this.auth.listSpaceAdmin("NEW"))
+ .thenReturn(Collections.emptyList());
+ Mockito.when(this.client.findUserByName("user"))
+ .thenReturn(user("user"));
+ UserEntity account = UserEntity.builder()
+ .id("user")
+ .name("user")
+ .adminSpaces(
+ Collections.singletonList("NEW"))
+ .build();
+ account.setSuperadmin(true);
+
+ this.service.update(this.client, account);
+
+ Mockito.verify(this.graphSpaceUsers).applySpacePreset(
+ this.client, "NEW", "user", "GS_ADMIN");
+ Mockito.verify(this.graphSpaceUsers).removeSpacePreset(
+ this.client, "OLD", "user");
+ Mockito.verify(this.auth).addSuperAdmin("user");
+ }
+
+ @Test
+ public void testLegacyUserUpdateReconcilesAdminSpaces() {
+ Mockito.when(this.config.get(HubbleOptions.PD_ENABLED)).thenReturn(true);
+ Mockito.when(this.client.supportsDefaultRole()).thenReturn(false);
+ Mockito.when(this.graphSpace.listGraphSpace())
+ .thenReturn(Arrays.asList("OLD", "NEW"));
+ Mockito.when(this.auth.listSpaceAdmin("OLD"))
+ .thenReturn(Collections.singletonList("user"));
+ Mockito.when(this.auth.listSpaceAdmin("NEW"))
+ .thenReturn(Collections.emptyList());
+ Mockito.when(this.client.findUserByName("user"))
+ .thenReturn(user("user"));
+ Mockito.when(this.auth.listSuperAdmin())
+ .thenReturn(Collections.singletonList("user"));
+ UserEntity account = UserEntity.builder()
+ .id("user")
+ .name("user")
+ .adminSpaces(
+ Collections.singletonList("NEW"))
+ .build();
+
+ this.service.update(this.client, account);
+
+ Mockito.verify(this.auth).addSpaceAdmin("user", "NEW");
+ Mockito.verify(this.auth).delSpaceAdmin("user", "OLD");
+ Mockito.verify(this.auth).delSuperAdmin("user");
+ Mockito.verify(this.graphSpaceUsers, Mockito.never())
+ .applySpacePreset(Mockito.any(), Mockito.anyString(),
+ Mockito.anyString(), Mockito.anyString());
+ }
+
+ @Test
+ public void testPdUserListReportsPresetAndCustomRoleState() {
+ Mockito.when(this.config.get(HubbleOptions.PD_ENABLED)).thenReturn(true);
+ Mockito.when(this.client.supportsDefaultRole()).thenReturn(true);
+ Mockito.when(this.auth.listUsers())
+ .thenReturn(Collections.singletonList(user("alice")));
+ Mockito.when(this.graphSpace.listGraphSpace())
+ .thenReturn(Arrays.asList("ADMIN", "WRITE", "READ", "CUSTOM"));
+ Mockito.when(this.auth.listSpaceAdmin("ADMIN"))
+ .thenReturn(Collections.singletonList("alice"));
+ Mockito.when(this.auth.listSpaceAdmin("WRITE"))
+ .thenReturn(Collections.emptyList());
+ Mockito.when(this.auth.listSpaceAdmin("READ"))
+ .thenReturn(Collections.emptyList());
+ Mockito.when(this.auth.listSpaceAdmin("CUSTOM"))
+ .thenReturn(Collections.emptyList());
+ Mockito.when(this.graphSpace.checkDefaultRole(
+ "WRITE", "alice", "analyst")).thenReturn(true);
+ Mockito.when(this.graphSpace.checkDefaultRole(
+ "READ", "alice", "observer")).thenReturn(true);
+ Mockito.when(this.graphSpaceUsers.hasCustomRoles(
+ this.client, "CUSTOM", "alice")).thenReturn(true);
+
+ UserEntity account = this.service.listUsers(this.client).get(0);
+
+ Assert.assertEquals(Integer.valueOf(1), account.getSpacenum());
+ Assert.assertEquals(Collections.singletonList("ADMIN"),
+ account.getAdminSpaces());
+ Assert.assertEquals(3, account.getGraphspacePermissions().size());
+ Assert.assertEquals("GS_ADMIN",
+ account.getGraphspacePermissions().get(0)
+ .get("permission_preset"));
+ Assert.assertEquals("GS_READ_WRITE",
+ account.getGraphspacePermissions().get(1)
+ .get("permission_preset"));
+ Assert.assertEquals("GS_READ_ONLY",
+ account.getGraphspacePermissions().get(2)
+ .get("permission_preset"));
+ Assert.assertEquals("LEGACY_CUSTOM", account.getPermissionPreset());
+ }
+
@Test
public void testLegacyUserCreationSkipsPermissionPresetApis() {
Mockito.when(this.config.get(HubbleOptions.PD_ENABLED)).thenReturn(true);
diff --git a/hugegraph-hubble/hubble-fe/src/api/auth.js b/hugegraph-hubble/hubble-fe/src/api/auth.js
index 541c099f8..5b7075c89 100644
--- a/hugegraph-hubble/hubble-fe/src/api/auth.js
+++ b/hugegraph-hubble/hubble-fe/src/api/auth.js
@@ -98,18 +98,15 @@ const getSpaceMembers = (graphspace, params, config = {}) => {
};
const getSpaceAdmins = (graphspace, params, config = {}) => {
- return request.get(scopedAuthPath(graphspace, 'users/spaceadmin'),
- {...config, params});
+ return request.get(scopedAuthPath(graphspace, 'users/spaceadmin'), {...config, params});
};
const setSpaceAdmin = (graphspace, id, config) => {
- return request.post(scopedAuthPath(graphspace, 'users/spaceadmin', id),
- undefined, config);
+ return request.post(scopedAuthPath(graphspace, 'users/spaceadmin', id), undefined, config);
};
const removeSpaceAdmin = (graphspace, id, config) => {
- return request.delete(scopedAuthPath(graphspace, 'users/spaceadmin', id),
- undefined, config);
+ return request.delete(scopedAuthPath(graphspace, 'users/spaceadmin', id), undefined, config);
};
const setSpacePreset = (graphspace, id, preset, config) => {
diff --git a/hugegraph-hubble/hubble-fe/src/modules/navigation/ConsoleItem/index.js b/hugegraph-hubble/hubble-fe/src/modules/navigation/ConsoleItem/index.js
index fc47de692..18ebe299c 100644
--- a/hugegraph-hubble/hubble-fe/src/modules/navigation/ConsoleItem/index.js
+++ b/hugegraph-hubble/hubble-fe/src/modules/navigation/ConsoleItem/index.js
@@ -105,8 +105,7 @@ const ConsoleItem = ({embedded = false}) => {
? () => openDashboard(dashboard.url + path)
: undefined,
});
- const nativeItem = (titleKey, path, required, modeAvailable = true,
- modeReason = '') => {
+ const nativeItem = (titleKey, path, required, modeAvailable = true, modeReason = '') => {
const available = modeAvailable && capabilities.includes(required);
const disabled = capabilitiesLoading || Boolean(capabilitiesError) || !available;
return {
diff --git a/hugegraph-hubble/hubble-fe/src/pages/Account/EditLayer.js b/hugegraph-hubble/hubble-fe/src/pages/Account/EditLayer.js
index 9d957fe21..8e25be843 100644
--- a/hugegraph-hubble/hubble-fe/src/pages/Account/EditLayer.js
+++ b/hugegraph-hubble/hubble-fe/src/pages/Account/EditLayer.js
@@ -36,8 +36,7 @@ import {
const PAGE_ERROR_CONFIG = {suppressBusinessErrorToast: true};
const DEFAULT_ALLOWED_OPERATIONS = {create: true, edit: true, auth: true};
const PRESERVE_PERMISSIONS = 'PRESERVE_PERMISSIONS';
-const permissionPresetChanged = (prev, next) => (prev.permission_preset !== next.permission_preset
-);
+const permissionPresetChanged = (prev, next) => prev.permission_preset !== next.permission_preset;
const toProfilePayload = values => ({
user_name: values.user_name,
user_nickname: values.user_nickname,
@@ -107,8 +106,7 @@ const EditLayer = ({
&& values.permission_preset !== PRESERVE_PERMISSIONS
? toPermissionPayload(values)
: toProfilePayload(values);
- return api.auth.updateUser(data.id, payload, PAGE_ERROR_CONFIG
- ).then(res => {
+ return api.auth.updateUser(data.id, payload, PAGE_ERROR_CONFIG).then(res => {
if (res.status === 200) {
message.success(t('common.msg.update_success'));
onCancel();
@@ -126,8 +124,7 @@ const EditLayer = ({
...values,
permission_preset: PERMISSION_PRESETS.GS_ADMIN,
});
- return api.auth.updateAdminspace(data.id, payload.adminSpaces, PAGE_ERROR_CONFIG
- ).then(res => {
+ return api.auth.updateAdminspace(data.id, payload.adminSpaces, PAGE_ERROR_CONFIG).then(res => {
if (res.status === 200) {
message.success(t('common.msg.set_success'));
onCancel();
@@ -226,8 +223,7 @@ const EditLayer = ({
if (op !== 'detail') {
form.setFieldsValue({
...res.data,
- permission_preset: getAccountPreset(res.data)
- ?? PRESERVE_PERMISSIONS,
+ permission_preset: getAccountPreset(res.data) ?? PRESERVE_PERMISSIONS,
graphspaces: op === 'auth'
? (res.data?.adminSpaces ?? [])
: getPresetSpaces(res.data),
diff --git a/hugegraph-hubble/hubble-fe/src/pages/Account/SpaceAccess.js b/hugegraph-hubble/hubble-fe/src/pages/Account/SpaceAccess.js
index be575f469..8dd3fa1b0 100644
--- a/hugegraph-hubble/hubble-fe/src/pages/Account/SpaceAccess.js
+++ b/hugegraph-hubble/hubble-fe/src/pages/Account/SpaceAccess.js
@@ -85,8 +85,7 @@ const rolesPreset = roles => {
if (presets.some(preset => preset === null)) {
return null;
}
- if (values.some(role => (role?.permission_preset ?? role?.permissionPreset)
- === PERMISSION_PRESETS.GS_ADMIN)) {
+ if (values.some(role => (role?.permission_preset ?? role?.permissionPreset) === PERMISSION_PRESETS.GS_ADMIN)) {
return PERMISSION_PRESETS.GS_ADMIN;
}
return values.length > 0 && presets.every(Boolean) && new Set(presets).size === 1 ? presets[0] : null;
diff --git a/hugegraph-hubble/hubble-fe/src/utils/rules.test.js b/hugegraph-hubble/hubble-fe/src/utils/rules.test.js
index 662d39e0a..3ff86a5d8 100644
--- a/hugegraph-hubble/hubble-fe/src/utils/rules.test.js
+++ b/hugegraph-hubble/hubble-fe/src/utils/rules.test.js
@@ -56,8 +56,8 @@ jest.mock('../i18n', () => ({
'common.validation.jdbc_rule':
'请输入正确的jdbc url, 例如:jdbc:mysql://127.0.0.1:3306/db_name',
'common.validation.account_name_rule':
- '使用 1–16 个不含空格的字符,支持中文/东亚兼容字符、字母、数字和下划线;'
- + '下划线不能位于首尾',
+ '使用 1–16 个不含空格的字符,支持中文/东亚兼容字符、字母、数字和'
+ + '下划线;下划线不能位于首尾',
'common.validation.favorite_name_rule': '只能包含中文、字母、数字、_, 不能超过48个字符',
'common.validation.invalid_data_format': '非法的数据格式',
},
@@ -194,8 +194,8 @@ describe('rules i18n defaults', () => {
[rules.isJDBC(), '请输入正确的jdbc url, 例如:jdbc:mysql://127.0.0.1:3306/db_name'],
[
rules.isAccountName(),
- '使用 1–16 个不含空格的字符,支持中文/东亚兼容字符、字母、数字和下划线;'
- + '下划线不能位于首尾',
+ '使用 1–16 个不含空格的字符,支持中文/东亚兼容字符、字母、数字和'
+ + '下划线;下划线不能位于首尾',
],
[rules.isFavoriteName(), '只能包含中文、字母、数字、_, 不能超过48个字符'],
[rules.isUUID(), '非法的数据格式'],