From 25db6ac35919126c3300a161c9b3af27baf09492 Mon Sep 17 00:00:00 2001 From: Aleksandr Skoblikov Date: Tue, 30 Apr 2024 20:22:03 +0400 Subject: [PATCH 01/21] CB-5067 team supervisor --- .../io/cloudbeaver/model/user/WebUser.java | 1 + .../io/cloudbeaver/server/CBApplication.java | 4 + .../schema/service.admin.graphqls | 2 + .../service/admin/DBWServiceAdmin.java | 9 +++ .../service/admin/impl/WebServiceAdmin.java | 20 +++++ .../schema/service.auth.graphqls | 7 ++ .../cloudbeaver/service/auth/WebUserInfo.java | 16 ++++ .../service/auth/WebUserTeamInfo.java | 49 +++++++++++ .../db/cb_schema_create.sql | 15 ++++ .../db/cb_schema_update_20.sql | 2 + .../CBEmbeddedSecurityController.java | 81 +++++++++++++++++-- .../service/security/db/CBDatabase.java | 2 +- 12 files changed, 199 insertions(+), 9 deletions(-) create mode 100644 server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebUserTeamInfo.java create mode 100644 server/bundles/io.cloudbeaver.service.security/db/cb_schema_update_20.sql diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/user/WebUser.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/user/WebUser.java index bfc843edb4..9b6262fa60 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/user/WebUser.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/user/WebUser.java @@ -70,6 +70,7 @@ public Map getConfigurationParameters() { return Collections.emptyMap(); } + @NotNull public String[] getTeams() { return user.getUserTeams(); } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java index ac154189aa..340da012c0 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java @@ -685,6 +685,10 @@ public List getAvailableAuthRoles() { return List.of(); } + public List getAvailableTeamRoles() { + return List.of(); + } + @Override public WSEventController getEventController() { return eventController; diff --git a/server/bundles/io.cloudbeaver.service.admin/schema/service.admin.graphqls b/server/bundles/io.cloudbeaver.service.admin/schema/service.admin.graphqls index 586a0e8f96..7d7bfb6656 100644 --- a/server/bundles/io.cloudbeaver.service.admin/schema/service.admin.graphqls +++ b/server/bundles/io.cloudbeaver.service.admin/schema/service.admin.graphqls @@ -150,6 +150,8 @@ extend type Query { setUserAuthRole(userId: ID!, authRole: String): Boolean + setUserTeamRole(userId: ID!, teamId: ID!,teamRole: String): Boolean + #### Connection management # All connection configurations diff --git a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/DBWServiceAdmin.java b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/DBWServiceAdmin.java index 19271b41f5..f23808efa2 100644 --- a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/DBWServiceAdmin.java +++ b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/DBWServiceAdmin.java @@ -69,6 +69,9 @@ AdminUserInfo createUser( @WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN) List listAuthRoles(); + @WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN) + List listTeamRoles(); + @WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN) boolean deleteUser(@NotNull WebSession webSession, String userName) throws DBWebException; @@ -202,4 +205,10 @@ WebPropertyInfo saveUserMetaParameter(WebSession webSession, String id, String d @WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN) Boolean setUserAuthRole(WebSession webSession, String userId, String authRole) throws DBWebException; + @WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN) + Boolean setUserTeamRole( + @NotNull WebSession webSession, @NotNull String userId, + @NotNull String teamId, @Nullable String teamRole + ) throws DBWebException; + } diff --git a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/WebServiceAdmin.java b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/WebServiceAdmin.java index 69c203f7c8..846a49d9ac 100644 --- a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/WebServiceAdmin.java +++ b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/WebServiceAdmin.java @@ -173,6 +173,11 @@ public List listAuthRoles() { return CBApplication.getInstance().getAvailableAuthRoles(); } + @Override + public List listTeamRoles() { + return CBApplication.getInstance().getAvailableAuthRoles(); + } + @Override public boolean deleteUser(@NotNull WebSession webSession, String userName) throws DBWebException { if (CommonUtils.equalObjects(userName, webSession.getUser().getUserId())) { @@ -392,6 +397,21 @@ public Boolean setUserAuthRole(WebSession webSession, String userId, String auth } } + @Override + public Boolean setUserTeamRole( + @NotNull WebSession webSession, + @NotNull String userId, + @NotNull String teamId, + @Nullable String teamRole + ) throws DBWebException { + try { + webSession.getAdminSecurityController().setUserTeamRole(userId, teamId, teamRole); + return true; + } catch (Exception e) { + throw new DBWebException("Error updating user auth role", e); + } + } + //////////////////////////////////////////////////////////////////// // Connection management diff --git a/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls b/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls index 6ffefdf0f7..eb5b4b4d43 100644 --- a/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls +++ b/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls @@ -134,7 +134,14 @@ type UserInfo { metaParameters: Object! # User configuration parameters configurationParameters: Object! + # User teams + teams: [UserTeamInfo!]! +} +type UserTeamInfo { + teamId: String! + teamName: String! + teamRole: String } extend type Query { diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebUserInfo.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebUserInfo.java index 7232a030de..8e9c44e00c 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebUserInfo.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebUserInfo.java @@ -19,6 +19,7 @@ import io.cloudbeaver.DBWebException; import io.cloudbeaver.model.session.WebSession; import io.cloudbeaver.model.user.WebUser; +import org.jkiss.code.NotNull; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.meta.Property; @@ -88,4 +89,19 @@ public Map getConfigurationParameters() throws DBWebException { return session.getUserContext().getPreferenceStore().getCustomUserParameters(); } + @NotNull + @Property + public List getTeams() throws DBWebException { + if (session.getUserContext().isNonAnonymousUserAuthorizedInSM()) { + try { + return Arrays.stream(session.getSecurityController().getCurrentUserTeams()) + .map(WebUserTeamInfo::new) + .toList(); + } catch (DBException e) { + throw new DBWebException("Error reading user's teams", e); + } + } else { + return List.of(); + } + } } diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebUserTeamInfo.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebUserTeamInfo.java new file mode 100644 index 0000000000..029d0dd08a --- /dev/null +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebUserTeamInfo.java @@ -0,0 +1,49 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.cloudbeaver.service.auth; + +import org.jkiss.code.NotNull; +import org.jkiss.code.Nullable; +import org.jkiss.dbeaver.model.meta.Property; +import org.jkiss.dbeaver.model.security.user.SMUserTeam; + +public class WebUserTeamInfo { + @NotNull + private final SMUserTeam userTeam; + + public WebUserTeamInfo(@NotNull SMUserTeam userTeam) { + this.userTeam = userTeam; + } + + @NotNull + @Property + public String getTeamId() { + return userTeam.getTeamId(); + } + + @NotNull + @Property + public String getTeamName() { + return userTeam.getTeamName(); + } + + @Nullable + @Property + public String getTeamRole() { + return userTeam.getTeamRole(); + } +} diff --git a/server/bundles/io.cloudbeaver.service.security/db/cb_schema_create.sql b/server/bundles/io.cloudbeaver.service.security/db/cb_schema_create.sql index af81539fcf..01642438d3 100644 --- a/server/bundles/io.cloudbeaver.service.security/db/cb_schema_create.sql +++ b/server/bundles/io.cloudbeaver.service.security/db/cb_schema_create.sql @@ -63,6 +63,20 @@ CREATE TABLE {table_prefix}CB_AUTH_PERMISSIONS FOREIGN KEY (SUBJECT_ID) REFERENCES {table_prefix}CB_AUTH_SUBJECT (SUBJECT_ID) ON DELETE CASCADE ); +CREATE TABLE {table_prefix}CB_TEAM_PERMISSIONS +( + USER_ID VARCHAR(128) NOT NULL, + TEAM_ID VARCHAR(128) NOT NULL, + PERMISSION_ID VARCHAR(64) NOT NULL, + + GRANT_TIME TIMESTAMP NOT NULL, + GRANTED_BY VARCHAR(128) NOT NULL, + + PRIMARY KEY (USER_ID, TEAM_ID, PERMISSION_ID), + FOREIGN KEY (USER_ID) REFERENCES {table_prefix}CB_AUTH_SUBJECT (SUBJECT_ID) ON DELETE CASCADE, + FOREIGN KEY (TEAM_ID) REFERENCES {table_prefix}CB_AUTH_SUBJECT (SUBJECT_ID) ON DELETE NO ACTION +); + CREATE TABLE {table_prefix}CB_OBJECT_PERMISSIONS ( OBJECT_ID VARCHAR(128) NOT NULL, @@ -130,6 +144,7 @@ CREATE TABLE {table_prefix}CB_USER_TEAM ( USER_ID VARCHAR(128) NOT NULL, TEAM_ID VARCHAR(128) NOT NULL, + TEAM_ROLE VARCHAR(128), GRANT_TIME TIMESTAMP NOT NULL, GRANTED_BY VARCHAR(128) NOT NULL, diff --git a/server/bundles/io.cloudbeaver.service.security/db/cb_schema_update_20.sql b/server/bundles/io.cloudbeaver.service.security/db/cb_schema_update_20.sql new file mode 100644 index 0000000000..230dfbec43 --- /dev/null +++ b/server/bundles/io.cloudbeaver.service.security/db/cb_schema_update_20.sql @@ -0,0 +1,2 @@ +ALTER TABLE {table_prefix}CB_USER_TEAM + ADD TEAM_ROLE VARCHAR(128) NULL; diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java index 0c03282900..fd6a9c993e 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java @@ -19,6 +19,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; +import io.cloudbeaver.DBWConstants; import io.cloudbeaver.auth.*; import io.cloudbeaver.model.app.WebAppConfiguration; import io.cloudbeaver.model.app.WebAuthApplication; @@ -223,6 +224,33 @@ public void setUserTeams(String userId, String[] teamIds, String grantorId) thro addSubjectPermissionsUpdateEvent(userId, SMSubjectType.user); } + @Override + public void setUserTeamRole( + @NotNull String userId, + @NotNull String teamId, + @Nullable String teamRole + ) throws DBException { + if (!isSubjectExists(userId)) { + throw new DBCException("User '" + userId + "' doesn't exists"); + } + if (!isSubjectExists(teamId)) { + throw new DBCException("Team '" + teamId + "' doesn't exists"); + } + + try ( + var dbCon = database.openConnection(); + PreparedStatement dbStat = dbCon.prepareStatement( + database.normalizeTableNames("UPDATE {table_prefix}CB_USER_TEAM " + + "SET TEAM_ROLE=? WHERE USER_ID=? AND TEAM_ID=?")) + ) { + dbStat.setString(1, userId); + dbStat.setString(2, teamId); + JDBCUtils.setStringOrNull(dbStat, 3, teamId); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + public void setUserTeams(@NotNull Connection dbCon, String userId, String[] teamIds, String grantorId) throws SQLException { JDBCUtils.executeStatement( @@ -252,12 +280,12 @@ public void setUserTeams(@NotNull Connection dbCon, String userId, String[] team @NotNull @Override - public SMTeam[] getUserTeams(String userId) throws DBException { - Map teams = new LinkedHashMap<>(); + public SMUserTeam[] getUserTeams(String userId) throws DBException { + Map teams = new LinkedHashMap<>(); try (Connection dbCon = database.openConnection()) { String defaultUserTeam = application.getAppConfiguration().getDefaultUserTeam(); try (PreparedStatement dbStat = dbCon.prepareStatement(database.normalizeTableNames( - "SELECT R.*,S.IS_SECRET_STORAGE FROM {table_prefix}CB_USER_TEAM UR, {table_prefix}CB_TEAM R, " + + "SELECT R.*,S.IS_SECRET_STORAGE,UR.TEAM_ROLE FROM {table_prefix}CB_USER_TEAM UR, {table_prefix}CB_TEAM R, " + "{table_prefix}CB_AUTH_SUBJECT S " + "WHERE UR.USER_ID=? AND UR.TEAM_ID = R.TEAM_ID " + "AND S.SUBJECT_ID IN (R.TEAM_ID,?)")) @@ -267,12 +295,13 @@ public SMTeam[] getUserTeams(String userId) throws DBException { try (ResultSet dbResult = dbStat.executeQuery()) { while (dbResult.next()) { var team = fetchTeam(dbResult); - teams.put(team.getTeamId(), team); + String teamRole = dbResult.getString("TEAM_ROLE"); + teams.put(team.getTeamId(), new SMUserTeam(team, teamRole)); } } } readSubjectsMetas(dbCon, SMSubjectType.team, null, teams); - return teams.values().toArray(new SMTeam[0]); + return teams.values().toArray(new SMUserTeam[0]); } catch (SQLException e) { throw new DBCException("Error while reading user teams", e); } @@ -296,7 +325,7 @@ private Set getAllLinkedSubjects(Connection dbCon, String subjectId) thr @NotNull @Override - public SMTeam[] getCurrentUserTeams() throws DBException { + public SMUserTeam[] getCurrentUserTeams() throws DBException { return getUserTeams(getUserIdOrThrow()); } @@ -2727,6 +2756,42 @@ public void deleteAllObjectPermissions(@NotNull String objectId, @NotNull SMObje } } + @Override + public boolean hasAccessToUsers(@NotNull String teamRole, @NotNull Set userIds) throws DBException { + if (CommonUtils.isEmpty(userIds)) { + return true; + } + String currentUserId = getUserIdOrThrow(); + var currentPermissions = getUserPermissions(currentUserId); + if (currentPermissions.contains(DBWConstants.PERMISSION_ADMIN)) { + return true; + } + + String sql = "SELECT COUNT(DISTINCT UT.USER_ID) FROM {table_prefix}CB_USER_TEAM UT " + + "WHERE TEAM_ID IN (SELECT TEAM_ID FROM {table_prefix}CB_USER_TEAM WHERE USER_ID = ? and TEAM_ROLE = ?) " + + "AND UT.USER_ID IN(" + SQLUtils.generateParamList(userIds.size()) + ")"; + try (var dbCon = database.openConnection(); + var dbStat = dbCon.prepareStatement(database.normalizeTableNames(sql)) + ) { + dbStat.setString(1, currentUserId); + dbStat.setString(2, teamRole); + int index = 3; + for (String userId : userIds) { + dbStat.setString(index++, userId); + } + try (ResultSet dbResult = dbStat.executeQuery()) { + if (dbResult.next()) { + int matchesUsersCount = dbResult.getInt(1); + return matchesUsersCount == userIds.size(); + } else { + return false; + } + } + } catch (SQLException e) { + throw new DBCException("Error validating user access", e); + } + } + @Override public void deleteAllSubjectObjectPermissions(@NotNull String subjectId, @NotNull SMObjectType objectType) throws DBException { try (Connection dbCon = database.openConnection()) { @@ -2977,7 +3042,7 @@ private String buildRedirectLink(String originalLink, String authId) { @NotNull - private String getUserIdOrThrow() throws SMException { + protected String getUserIdOrThrow() throws SMException { String userId = getUserIdOrNull(); if (userId == null) { throw new SMException("User not authenticated"); @@ -2986,7 +3051,7 @@ private String getUserIdOrThrow() throws SMException { } @Nullable - private String getUserIdOrNull() { + protected String getUserIdOrNull() { SMCredentials activeUserCredentials = credentialsProvider.getActiveUserCredentials(); if (activeUserCredentials == null || activeUserCredentials.getUserId() == null) { return null; diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/CBDatabase.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/CBDatabase.java index eed4c7926a..95268033c8 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/CBDatabase.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/db/CBDatabase.java @@ -74,7 +74,7 @@ public class CBDatabase { public static final String SCHEMA_UPDATE_SQL_PATH = "db/cb_schema_update_"; private static final int LEGACY_SCHEMA_VERSION = 1; - private static final int CURRENT_SCHEMA_VERSION = 19; + private static final int CURRENT_SCHEMA_VERSION = 20; private static final String DEFAULT_DB_USER_NAME = "cb-data"; private static final String DEFAULT_DB_PWD_FILE = ".database-credentials.dat"; From 1368e63eb26b2073aa97ca560e89564bb7ecaa6f Mon Sep 17 00:00:00 2001 From: Aleksandr Skoblikov Date: Wed, 8 May 2024 19:32:19 +0400 Subject: [PATCH 02/21] CB-4460 extended team members info api --- .../schema/service.admin.graphqls | 7 +++ .../service/admin/AdminTeamInfo.java | 5 ++ .../service/admin/WebServiceBindingAdmin.java | 2 + .../service/admin/impl/WebServiceAdmin.java | 2 +- .../CBEmbeddedSecurityController.java | 60 ++++++++++++------- 5 files changed, 53 insertions(+), 23 deletions(-) diff --git a/server/bundles/io.cloudbeaver.service.admin/schema/service.admin.graphqls b/server/bundles/io.cloudbeaver.service.admin/schema/service.admin.graphqls index 7d7bfb6656..3ec3d71afa 100644 --- a/server/bundles/io.cloudbeaver.service.admin/schema/service.admin.graphqls +++ b/server/bundles/io.cloudbeaver.service.admin/schema/service.admin.graphqls @@ -11,6 +11,11 @@ type AdminConnectionGrantInfo { subjectType: AdminSubjectType! } +type AdminUserTeamGrantInfo { + userId: ID! + teamRole: String +} + type AdminObjectPermissions { objectId: ID! permissions: [String!]! @@ -53,6 +58,7 @@ type AdminTeamInfo { metaParameters: Object! grantedUsers: [ID!]! + grantedUsersInfo: [AdminUserTeamGrantInfo]! grantedConnections: [AdminConnectionGrantInfo!]! teamPermissions: [ID!]! @@ -128,6 +134,7 @@ extend type Query { listTeams(teamId: ID): [AdminTeamInfo!]! listPermissions: [AdminPermissionInfo!]! listAuthRoles: [String!]! + listTeamRoles: [String!]! listTeamMetaParameters: [ObjectPropertyInfo!]! createUser(userId: ID!, enabled: Boolean!, authRole: String): AdminUserInfo! diff --git a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/AdminTeamInfo.java b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/AdminTeamInfo.java index 3df4023bbe..6951cbf6b0 100644 --- a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/AdminTeamInfo.java +++ b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/AdminTeamInfo.java @@ -21,6 +21,7 @@ import org.jkiss.dbeaver.model.meta.Property; import org.jkiss.dbeaver.model.security.SMDataSourceGrant; import org.jkiss.dbeaver.model.security.SMObjectType; +import org.jkiss.dbeaver.model.security.SMTeamMemberInfo; import org.jkiss.dbeaver.model.security.user.SMTeam; import java.util.ArrayList; @@ -88,4 +89,8 @@ public String[] getGrantedUsers() throws DBException { return session.getAdminSecurityController().getTeamMembers(getTeamId()); } + @Property + public List getGrantedUsersInfo() throws DBException { + return session.getAdminSecurityController().getTeamMembersInfo(getTeamId()); + } } diff --git a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/WebServiceBindingAdmin.java b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/WebServiceBindingAdmin.java index 9bbc7895ad..cee7458482 100644 --- a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/WebServiceBindingAdmin.java +++ b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/WebServiceBindingAdmin.java @@ -60,6 +60,8 @@ public void bindWiring(DBWBindingContext model) throws DBWebException { env -> getService(env).listPermissions(getWebSession(env))) .dataFetcher("listAuthRoles", env -> getService(env).listAuthRoles()) + .dataFetcher("listTeamRoles", + env -> getService(env).listAuthRoles()) .dataFetcher("listTeamMetaParameters", env -> getService(env).listTeamMetaParameters(getWebSession(env))) .dataFetcher("createUser", diff --git a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/WebServiceAdmin.java b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/WebServiceAdmin.java index 846a49d9ac..b7a32416a6 100644 --- a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/WebServiceAdmin.java +++ b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/WebServiceAdmin.java @@ -175,7 +175,7 @@ public List listAuthRoles() { @Override public List listTeamRoles() { - return CBApplication.getInstance().getAvailableAuthRoles(); + return CBApplication.getInstance().getAvailableTeamRoles(); } @Override diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java index fd6a9c993e..7e404ff79c 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java @@ -163,7 +163,7 @@ public void createUser( dbStat.execute(); } saveSubjectMetas(dbCon, userId, metaParameters); - String defaultTeamName = application.getAppConfiguration().getDefaultUserTeam(); + String defaultTeamName = getDefaultUserTeam(); if (!CommonUtils.isEmpty(defaultTeamName)) { setUserTeams(dbCon, userId, new String[]{defaultTeamName}, userId); } @@ -258,7 +258,7 @@ public void setUserTeams(@NotNull Connection dbCon, String userId, String[] team database.normalizeTableNames("DELETE FROM {table_prefix}CB_USER_TEAM WHERE USER_ID=?"), userId ); - String defaultUserTeam = application.getAppConfiguration().getDefaultUserTeam(); + String defaultUserTeam = getDefaultUserTeam(); if (CommonUtils.isNotEmpty(defaultUserTeam) && !ArrayUtils.contains(teamIds, defaultUserTeam)) { teamIds = ArrayUtils.add(String.class, teamIds, defaultUserTeam); } @@ -283,7 +283,7 @@ public void setUserTeams(@NotNull Connection dbCon, String userId, String[] team public SMUserTeam[] getUserTeams(String userId) throws DBException { Map teams = new LinkedHashMap<>(); try (Connection dbCon = database.openConnection()) { - String defaultUserTeam = application.getAppConfiguration().getDefaultUserTeam(); + String defaultUserTeam = getDefaultUserTeam(); try (PreparedStatement dbStat = dbCon.prepareStatement(database.normalizeTableNames( "SELECT R.*,S.IS_SECRET_STORAGE,UR.TEAM_ROLE FROM {table_prefix}CB_USER_TEAM UR, {table_prefix}CB_TEAM R, " + "{table_prefix}CB_AUTH_SUBJECT S " + @@ -353,7 +353,7 @@ public SMUser getUserById(String userId) throws DBException { try (PreparedStatement dbStat = dbCon.prepareStatement( database.normalizeTableNames("SELECT TEAM_ID FROM {table_prefix}CB_USER_TEAM WHERE USER_ID=?")) ) { - String defaultUserTeam = application.getAppConfiguration().getDefaultUserTeam(); + String defaultUserTeam = getDefaultUserTeam(); dbStat.setString(1, userId); try (ResultSet dbResult = dbStat.executeQuery()) { Set teamIDs = new LinkedHashSet<>(); @@ -964,7 +964,7 @@ public SMPropertyDescriptor[] getMetaParametersBySubjectType(SMSubjectType subje @Override public SMTeam[] readAllTeams() throws DBCException { try (Connection dbCon = database.openConnection()) { - String defaultUserTeam = application.getAppConfiguration().getDefaultUserTeam(); + String defaultUserTeam = getDefaultUserTeam(); Map teams = new LinkedHashMap<>(); String query = database.normalizeTableNames( "SELECT T.*, S.IS_SECRET_STORAGE FROM {table_prefix}CB_TEAM T, " + @@ -1009,32 +1009,43 @@ public SMTeam findTeam(String teamId) throws DBCException { @NotNull @Override - public String[] getTeamMembers(String teamId) throws DBCException { + public String[] getTeamMembers(String teamId) throws DBException { + return getTeamMembersInfo(teamId).stream().map(SMTeamMemberInfo::userId).toArray(String[]::new); + } + + @NotNull + @Override + public List getTeamMembersInfo(@NotNull String teamId) throws DBException { try (Connection dbCon = database.openConnection()) { - if (application.getAppConfiguration().getDefaultUserTeam().equals(teamId)) { + Map usersRoles = new LinkedHashMap<>(); + if (getDefaultUserTeam().equals(teamId)) { try (PreparedStatement dbStat = dbCon.prepareStatement( - database.normalizeTableNames("SELECT USER_ID FROM {table_prefix}CB_USER"))) { - List subjects = new ArrayList<>(); + database.normalizeTableNames("SELECT USER_ID FROM {table_prefix}CB_USER")) + ) { try (ResultSet dbResult = dbStat.executeQuery()) { while (dbResult.next()) { - subjects.add(dbResult.getString(1)); + usersRoles.put(dbResult.getString(1), null); } } - return subjects.toArray(new String[0]); } - } else { - try (PreparedStatement dbStat = dbCon.prepareStatement( - database.normalizeTableNames("SELECT USER_ID FROM {table_prefix}CB_USER_TEAM WHERE TEAM_ID=?"))) { - dbStat.setString(1, teamId); - List subjects = new ArrayList<>(); - try (ResultSet dbResult = dbStat.executeQuery()) { - while (dbResult.next()) { - subjects.add(dbResult.getString(1)); - } + } + try (PreparedStatement dbStat = dbCon.prepareStatement( + database.normalizeTableNames( + "SELECT USER_ID,TEAM_ROLE FROM {table_prefix}CB_USER_TEAM WHERE TEAM_ID=?")) + ) { + dbStat.setString(1, teamId); + try (ResultSet dbResult = dbStat.executeQuery()) { + while (dbResult.next()) { + String userId = dbResult.getString(1); + String teamRole = dbResult.getString(2); + usersRoles.put(userId, teamRole); } - return subjects.toArray(new String[0]); } } + return usersRoles.entrySet() + .stream() + .map(entry -> new SMTeamMemberInfo(entry.getKey(), entry.getValue())) + .toList(); } catch (SQLException e) { throw new DBCException("Error while reading team members", e); } @@ -1123,7 +1134,7 @@ public void updateTeam(String teamId, String name, String description) throws DB @Override public void deleteTeam(String teamId, boolean force) throws DBCException { - String defaultUsersTeam = application.getAppConfiguration().getDefaultUserTeam(); + String defaultUsersTeam = getDefaultUserTeam(); if (CommonUtils.isNotEmpty(defaultUsersTeam) && defaultUsersTeam.equals(teamId)) { throw new DBCException("Default users team cannot be deleted"); } @@ -3146,4 +3157,9 @@ private String getUserId() { var credentials = credentialsProvider.getActiveUserCredentials(); return credentials == null ? null : credentials.getUserId(); } + + @NotNull + private String getDefaultUserTeam() { + return application.getAppConfiguration().getDefaultUserTeam(); + } } From 0bbb69f15cf1477bece3b21e4905b8174c94e8f5 Mon Sep 17 00:00:00 2001 From: naumov Date: Fri, 10 May 2024 00:29:36 +0200 Subject: [PATCH 03/21] CB-5118 support supervisor role --- .../src/TeamRolesResource.ts | 40 +++++++++++++++++++ .../core-authentication/src/TeamsResource.ts | 15 +++++-- .../src/UserInfoResource.ts | 4 ++ .../packages/core-authentication/src/index.ts | 8 ++++ .../core-authentication/src/manifest.ts | 2 + .../queries/authentication/getActiveUser.gql | 9 ++++- .../teams/getTeamGrantedUsers.gql | 7 +++- .../authentication/teams/getTeamRoles.gql | 3 ++ .../teams/updateUserTeamRole.gql | 3 ++ webapp/packages/core-utils/src/index.ts | 1 + .../core-utils/src/types/UndefinedToNull.ts | 12 ++++++ .../Teams/GrantedUsers/GrantedUserList.tsx | 19 ++++++--- .../Users/Teams/GrantedUsers/GrantedUsers.tsx | 35 ++++++++++------ .../GrantedUsers/GrantedUsersTabService.ts | 17 +++++--- .../GrantedUsersTableInnerHeader.tsx | 4 +- .../GrantedUsers/GrantedUsersTableItem.m.css | 11 +++++ .../GrantedUsers/GrantedUsersTableItem.tsx | 31 ++++++++++++-- .../Users/Teams/GrantedUsers/IGrantedUser.ts | 12 ++++++ .../GrantedUsers/IGrantedUsersTabState.ts | 5 ++- .../Users/Teams/GrantedUsers/UserList.tsx | 8 ++-- .../Teams/GrantedUsers/UserTeamGrantInfo.ts | 11 +++++ .../GrantedUsers/UsersTableInnerHeader.tsx | 29 ++++++++++++++ .../Teams/GrantedUsers/UsersTableItem.m.css | 11 +++++ .../Teams/GrantedUsers/UsersTableItem.tsx | 36 +++++++++++++++++ .../Teams/GrantedUsers/useGrantedUsers.tsx | 22 ++++++---- .../src/locales/en.ts | 8 ++++ .../src/locales/it.ts | 8 ++++ .../src/locales/ru.ts | 8 ++++ .../src/locales/zh.ts | 8 ++++ 29 files changed, 338 insertions(+), 49 deletions(-) create mode 100644 webapp/packages/core-authentication/src/TeamRolesResource.ts create mode 100644 webapp/packages/core-sdk/src/queries/authentication/teams/getTeamRoles.gql create mode 100644 webapp/packages/core-sdk/src/queries/authentication/teams/updateUserTeamRole.gql create mode 100644 webapp/packages/core-utils/src/types/UndefinedToNull.ts create mode 100644 webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/IGrantedUser.ts create mode 100644 webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UserTeamGrantInfo.ts create mode 100644 webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UsersTableInnerHeader.tsx create mode 100644 webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UsersTableItem.m.css create mode 100644 webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UsersTableItem.tsx diff --git a/webapp/packages/core-authentication/src/TeamRolesResource.ts b/webapp/packages/core-authentication/src/TeamRolesResource.ts new file mode 100644 index 0000000000..ba1d4cc893 --- /dev/null +++ b/webapp/packages/core-authentication/src/TeamRolesResource.ts @@ -0,0 +1,40 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +import { injectable } from '@cloudbeaver/core-di'; +import { CachedDataResource } from '@cloudbeaver/core-resource'; +import { EAdminPermission, SessionPermissionsResource } from '@cloudbeaver/core-root'; +import { GraphQLService } from '@cloudbeaver/core-sdk'; + +export const USER_TEAM_ROLE_SUPERVISOR = 'SUPERVISOR'; + +@injectable() +export class TeamRolesResource extends CachedDataResource { + constructor( + private readonly graphQLService: GraphQLService, + sessionPermissionsResource: SessionPermissionsResource, + ) { + super(() => []); + + sessionPermissionsResource.require(this, EAdminPermission.admin).outdateResource(this); + } + + async assignTeamRoleToUser(userId: string, teamId: string, teamRole: string) { + const { result } = await this.graphQLService.sdk.updateUserTeamRole({ + userId, + teamId, + teamRole, + }); + + return result; + } + + protected async loader(): Promise { + const { roles } = await this.graphQLService.sdk.getTeamRoles(); + return roles; + } +} diff --git a/webapp/packages/core-authentication/src/TeamsResource.ts b/webapp/packages/core-authentication/src/TeamsResource.ts index f12208a913..eb7e460747 100644 --- a/webapp/packages/core-authentication/src/TeamsResource.ts +++ b/webapp/packages/core-authentication/src/TeamsResource.ts @@ -15,12 +15,19 @@ import { type ResourceKeySimple, ResourceKeyUtils, } from '@cloudbeaver/core-resource'; -import { AdminConnectionGrantInfo, AdminTeamInfoFragment, GetTeamsListQueryVariables, GraphQLService } from '@cloudbeaver/core-sdk'; -import { isArraysEqual } from '@cloudbeaver/core-utils'; +import { + AdminConnectionGrantInfo, + AdminTeamInfoFragment, + AdminUserTeamGrantInfo, + GetTeamsListQueryVariables, + GraphQLService, +} from '@cloudbeaver/core-sdk'; +import { isArraysEqual, UndefinedToNull } from '@cloudbeaver/core-utils'; const NEW_TEAM_SYMBOL = Symbol('new-team'); export type TeamInfo = AdminTeamInfoFragment; +export type UserTeamGrantInfo = UndefinedToNull; type TeamResourceIncludes = Omit; type NewTeam = TeamInfo & { [NEW_TEAM_SYMBOL]: boolean; timestamp: number }; @@ -85,9 +92,9 @@ export class TeamsResource extends CachedMapResource { + async loadGrantedUsers(teamId: string): Promise { const { team } = await this.graphQLService.sdk.getTeamGrantedUsers({ teamId }); - return team[0].grantedUsers; + return team[0].grantedUsersInfo as UserTeamGrantInfo[]; } async getSubjectConnectionAccess(subjectId: string): Promise { diff --git a/webapp/packages/core-authentication/src/UserInfoResource.ts b/webapp/packages/core-authentication/src/UserInfoResource.ts index fb8b6e6729..31ac89d2da 100644 --- a/webapp/packages/core-authentication/src/UserInfoResource.ts +++ b/webapp/packages/core-authentication/src/UserInfoResource.ts @@ -40,6 +40,10 @@ export class UserInfoResource extends CachedDataResource = T extends undefined ? null : T; + +export type UndefinedToNull = { + [Prop in keyof T]-?: T[Prop] extends object ? UndefinedToNull : UnionUndefinedToNull; +}; diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUserList.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUserList.tsx index af186ae5e1..e092471051 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUserList.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUserList.tsx @@ -9,7 +9,7 @@ import { observable } from 'mobx'; import { observer } from 'mobx-react-lite'; import { useCallback, useState } from 'react'; -import { UsersResource } from '@cloudbeaver/core-authentication'; +import { TeamRolesResource, UsersResource } from '@cloudbeaver/core-authentication'; import { Button, getComputed, @@ -21,28 +21,30 @@ import { TableColumnValue, TableItem, useObjectRef, + useResource, useS, useTranslate, } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; import type { TLocalizationToken } from '@cloudbeaver/core-localization'; import { ServerConfigResource } from '@cloudbeaver/core-root'; -import type { AdminUserInfoFragment } from '@cloudbeaver/core-sdk'; import { getFilteredUsers } from './getFilteredUsers'; import style from './GrantedUserList.m.css'; import { GrantedUsersTableHeader, IFilterState } from './GrantedUsersTableHeader/GrantedUsersTableHeader'; import { GrantedUsersTableInnerHeader } from './GrantedUsersTableHeader/GrantedUsersTableInnerHeader'; import { GrantedUsersTableItem } from './GrantedUsersTableItem'; +import type { IGrantedUser } from './IGrantedUser'; interface Props { - grantedUsers: AdminUserInfoFragment[]; + grantedUsers: IGrantedUser[]; disabled: boolean; onRevoke: (subjectIds: string[]) => void; + onTeamRoleAssign: (subjectId: string, teamRole: string | null) => void; onEdit: () => void; } -export const GrantedUserList = observer(function GrantedUserList({ grantedUsers, disabled, onRevoke, onEdit }) { +export const GrantedUserList = observer(function GrantedUserList({ grantedUsers, disabled, onRevoke, onTeamRoleAssign, onEdit }) { const styles = useS(style); const props = useObjectRef({ onRevoke, onEdit }); const translate = useTranslate(); @@ -50,12 +52,14 @@ export const GrantedUserList = observer(function GrantedUserList({ grante const usersResource = useService(UsersResource); const serverConfigResource = useService(ServerConfigResource); + const teamRolesResource = useResource(GrantedUserList, TeamRolesResource, undefined); + const [selectedSubjects] = useState>(() => observable(new Map())); const [filterState] = useState(() => observable({ filterValue: '' })); const selected = getComputed(() => Array.from(selectedSubjects.values()).some(v => v)); - const users = getFilteredUsers(grantedUsers, filterState.filterValue); + const users = getFilteredUsers(grantedUsers, filterState.filterValue) as IGrantedUser[]; const keys = users.map(user => user.userId); const revoke = useCallback(() => { @@ -93,7 +97,7 @@ export const GrantedUserList = observer(function GrantedUserList({ grante
isEditable(item)}> - + 0} /> {tableInfoText && ( @@ -108,7 +112,10 @@ export const GrantedUserList = observer(function GrantedUserList({ grante tooltip={isEditable(user.userId) ? user.userId : translate('administration_teams_team_granted_users_permission_denied')} icon="/icons/user.svg" iconTooltip={translate('authentication_user_icon_tooltip')} + teamRole={user.teamRole} + teamRoles={teamRolesResource.data} disabled={disabled} + onTeamRoleAssign={onTeamRoleAssign} /> ))} diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.tsx index 4977c9cb6e..cc744b98b6 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.tsx @@ -7,11 +7,10 @@ */ import { observer } from 'mobx-react-lite'; -import { AdminUser, UsersResource, UsersResourceFilterKey } from '@cloudbeaver/core-authentication'; +import { UsersResource, UsersResourceFilterKey } from '@cloudbeaver/core-authentication'; import { ColoredContainer, Container, - getComputed, Group, InfoItem, Loader, @@ -29,6 +28,7 @@ import { TabContainerPanelComponent, useTab } from '@cloudbeaver/core-ui'; import type { ITeamFormProps } from '../ITeamFormProps'; import { GrantedUserList } from './GrantedUserList'; import style from './GrantedUsers.m.css'; +import type { IGrantedUser } from './IGrantedUser'; import { useGrantedUsers } from './useGrantedUsers'; import { UserList } from './UserList'; @@ -46,9 +46,19 @@ export const GrantedUsers: TabContainerPanelComponent = observer active: selected && !isDefaultTeam, }); - const grantedUsers = getComputed(() => - users.data.filter((user): user is AdminUser => !!user && state.state.grantedUsers.includes(user.userId)), - ); + const grantedUserIds = state.state.grantedUsers.map(user => user.userId); + const grantedUsers: IGrantedUser[] = []; + + for (const user of users.data) { + const granted = state.state.grantedUsers.find(grantedUser => grantedUser.userId === user?.userId); + + if (granted && user) { + grantedUsers.push({ + ...user, + teamRole: granted.teamRole, + }); + } + } useAutoLoad(GrantedUsers, state, selected && !state.state.loaded && !isDefaultTeam); @@ -78,14 +88,15 @@ export const GrantedUsers: TabContainerPanelComponent = observer <> {formState.mode === 'edit' && state.changed && } - + {state.state.editing && ( - + )} diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTabService.ts b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTabService.ts index d262c3a9a4..72dac6eacc 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTabService.ts +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTabService.ts @@ -7,7 +7,7 @@ */ import React from 'react'; -import { TeamsResource, UsersResource } from '@cloudbeaver/core-authentication'; +import { TeamRolesResource, TeamsResource, UsersResource } from '@cloudbeaver/core-authentication'; import { Bootstrap, injectable } from '@cloudbeaver/core-di'; import { NotificationService } from '@cloudbeaver/core-events'; import type { IExecutionContextProvider } from '@cloudbeaver/core-executor'; @@ -32,6 +32,7 @@ export class GrantedUsersTabService extends Bootstrap { private readonly usersResource: UsersResource, private readonly teamsResource: TeamsResource, private readonly notificationService: NotificationService, + private readonly teamRolesResource: TeamRolesResource, ) { super(); this.key = 'granted-users'; @@ -91,14 +92,20 @@ export class GrantedUsersTabService extends Bootstrap { try { for (const user of revokedUsers) { - await this.usersResource.revokeTeam(user, config.teamId); - revoked.push(user); + await this.usersResource.revokeTeam(user.userId, config.teamId); + revoked.push(user.userId); } for (const user of state.grantedUsers) { if (!initial.includes(user)) { - await this.usersResource.grantTeam(user, config.teamId); - granted.push(user); + await this.usersResource.grantTeam(user.userId, config.teamId); + + // ask the backend whether we need to reset the team role on revoke or it will be done automatically + if (user.teamRole) { + await this.teamRolesResource.assignTeamRoleToUser(user.userId, config.teamId, user.teamRole); + } + + granted.push(user.userId); } } diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableHeader/GrantedUsersTableInnerHeader.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableHeader/GrantedUsersTableInnerHeader.tsx index 74b2888814..7b3c8214af 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableHeader/GrantedUsersTableInnerHeader.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableHeader/GrantedUsersTableInnerHeader.tsx @@ -11,10 +11,11 @@ import { TableColumnHeader, TableHeader, TableSelect, useTranslate } from '@clou interface Props { disabled?: boolean; + showUserTeamRole?: boolean; className?: string; } -export const GrantedUsersTableInnerHeader = observer(function GrantedUsersTableInnerHeader({ disabled, className }) { +export const GrantedUsersTableInnerHeader = observer(function GrantedUsersTableInnerHeader({ disabled, showUserTeamRole, className }) { const translate = useTranslate(); return ( @@ -24,6 +25,7 @@ export const GrantedUsersTableInnerHeader = observer(function GrantedUser {translate('administration_teams_team_granted_users_user_id')} + {showUserTeamRole && {translate('plugin_authentication_administration_team_user_team_role_supervisor')}} ); }); diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableItem.m.css b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableItem.m.css index 5378504fe0..5d43928a80 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableItem.m.css +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableItem.m.css @@ -1,4 +1,15 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ .staticImage { display: flex; width: 24px; } + +.roleSelector { + padding: 8px 0; +} diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableItem.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableItem.tsx index cd45c1d52f..6ecd0a0293 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableItem.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableItem.tsx @@ -7,30 +7,53 @@ */ import { observer } from 'mobx-react-lite'; -import { StaticImage, TableColumnValue, TableItem, TableItemSelect } from '@cloudbeaver/core-blocks'; +import { USER_TEAM_ROLE_SUPERVISOR } from '@cloudbeaver/core-authentication'; +import { Checkbox, StaticImage, TableColumnValue, TableItem, TableItemSelect } from '@cloudbeaver/core-blocks'; -import style from './GrantedUsersTableItem.m.css'; +import classes from './GrantedUsersTableItem.m.css'; interface Props { id: any; name: string; icon: string; disabled: boolean; + teamRole: string | null; + teamRoles: string[]; iconTooltip?: string; tooltip?: string; + onTeamRoleAssign: (subjectId: string, teamRole: string | null) => void; className?: string; } -export const GrantedUsersTableItem = observer(function GrantedUsersTableItem({ id, name, icon, iconTooltip, tooltip, disabled, className }) { +export const GrantedUsersTableItem = observer(function GrantedUsersTableItem({ + id, + name, + icon, + iconTooltip, + tooltip, + teamRole, + teamRoles, + onTeamRoleAssign, + disabled, + className, +}) { return ( - + {name} + {teamRoles.length > 0 && ( + + onTeamRoleAssign(id, value ? USER_TEAM_ROLE_SUPERVISOR : null)} + /> + + )} ); }); diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/IGrantedUser.ts b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/IGrantedUser.ts new file mode 100644 index 0000000000..94c92e5e11 --- /dev/null +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/IGrantedUser.ts @@ -0,0 +1,12 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +import type { AdminUserInfoFragment } from '@cloudbeaver/core-sdk'; + +export interface IGrantedUser extends AdminUserInfoFragment { + teamRole: string | null; +} diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/IGrantedUsersTabState.ts b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/IGrantedUsersTabState.ts index 88ae8e632a..ceb852c226 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/IGrantedUsersTabState.ts +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/IGrantedUsersTabState.ts @@ -5,11 +5,12 @@ * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. */ +import type { UserTeamGrantInfo } from '@cloudbeaver/core-authentication'; export interface IGrantedUsersTabState { loading: boolean; loaded: boolean; - grantedUsers: string[]; - initialGrantedUsers: string[]; + grantedUsers: UserTeamGrantInfo[]; + initialGrantedUsers: UserTeamGrantInfo[]; editing: boolean; } diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UserList.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UserList.tsx index 21cb1e9bbf..e902fb0ccd 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UserList.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UserList.tsx @@ -30,9 +30,9 @@ import type { AdminUserInfoFragment } from '@cloudbeaver/core-sdk'; import { getFilteredUsers } from './getFilteredUsers'; import { GrantedUsersTableHeader, IFilterState } from './GrantedUsersTableHeader/GrantedUsersTableHeader'; -import { GrantedUsersTableInnerHeader } from './GrantedUsersTableHeader/GrantedUsersTableInnerHeader'; -import { GrantedUsersTableItem } from './GrantedUsersTableItem'; import style from './UserList.m.css'; +import { UsersTableInnerHeader } from './UsersTableInnerHeader'; +import { UsersTableItem } from './UsersTableItem'; interface Props { userList: AdminUserInfoFragment[]; @@ -85,7 +85,7 @@ export const UserList = observer(function UserList({ userList, grantedUse selectedItems={selectedSubjects} isItemSelectable={item => isEditable(item) && !grantedUsers.includes(item)} > - + {!users.length && filterState.filterValue && ( @@ -93,7 +93,7 @@ export const UserList = observer(function UserList({ userList, grantedUse )} {users.map(user => ( - ; diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UsersTableInnerHeader.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UsersTableInnerHeader.tsx new file mode 100644 index 0000000000..5b941f12d7 --- /dev/null +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UsersTableInnerHeader.tsx @@ -0,0 +1,29 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +import { observer } from 'mobx-react-lite'; + +import { TableColumnHeader, TableHeader, TableSelect, useTranslate } from '@cloudbeaver/core-blocks'; + +interface Props { + disabled?: boolean; + className?: string; +} + +export const UsersTableInnerHeader = observer(function UsersTableInnerHeader({ disabled, className }) { + const translate = useTranslate(); + + return ( + + + + + + {translate('administration_teams_team_granted_users_user_id')} + + ); +}); diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UsersTableItem.m.css b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UsersTableItem.m.css new file mode 100644 index 0000000000..bd43f5c8d6 --- /dev/null +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UsersTableItem.m.css @@ -0,0 +1,11 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +.staticImage { + display: flex; + width: 24px; +} diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UsersTableItem.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UsersTableItem.tsx new file mode 100644 index 0000000000..73d1799bd5 --- /dev/null +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UsersTableItem.tsx @@ -0,0 +1,36 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +import { observer } from 'mobx-react-lite'; + +import { StaticImage, TableColumnValue, TableItem, TableItemSelect } from '@cloudbeaver/core-blocks'; + +import style from './GrantedUsersTableItem.m.css'; + +interface Props { + id: any; + name: string; + icon: string; + disabled: boolean; + iconTooltip?: string; + tooltip?: string; + className?: string; +} + +export const UsersTableItem = observer(function UsersTableItem({ id, name, icon, iconTooltip, tooltip, disabled, className }) { + return ( + + + + + + + + {name} + + ); +}); diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/useGrantedUsers.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/useGrantedUsers.tsx index 874917487e..f6e49ca23e 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/useGrantedUsers.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/useGrantedUsers.tsx @@ -5,14 +5,14 @@ * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. */ -import { action, computed, observable } from 'mobx'; +import { action, computed, observable, toJS } from 'mobx'; import { TeamInfo, TeamsResource } from '@cloudbeaver/core-authentication'; import { useObservableRef } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; import { NotificationService } from '@cloudbeaver/core-events'; import { useTabState } from '@cloudbeaver/core-ui'; -import { ILoadableState, isArraysEqual } from '@cloudbeaver/core-utils'; +import { ILoadableState, isArraysEqual, isObjectsEqual } from '@cloudbeaver/core-utils'; import type { TeamFormMode } from '../ITeamFormProps'; import type { IGrantedUsersTabState } from './IGrantedUsersTabState'; @@ -23,6 +23,7 @@ interface State extends ILoadableState { edit: () => void; revoke: (subjectIds: string[]) => void; grant: (subjectIds: string[]) => void; + assignTeamRole: (subjectId: string, teamRole: string | null) => void; load: () => Promise; } @@ -34,7 +35,7 @@ export function useGrantedUsers(team: TeamInfo, mode: TeamFormMode): Readonly ({ get changed() { - return !isArraysEqual(this.state.initialGrantedUsers, this.state.grantedUsers); + return !isArraysEqual(this.state.initialGrantedUsers, this.state.grantedUsers, isObjectsEqual); }, isLoading() { return this.state.loading; @@ -49,10 +50,17 @@ export function useGrantedUsers(team: TeamInfo, mode: TeamFormMode): Readonly !subjectIds.includes(subject)); + this.state.grantedUsers = this.state.grantedUsers.filter(subject => !subjectIds.includes(subject.userId)); }, grant(subjectIds: string[]) { - this.state.grantedUsers.push(...subjectIds); + this.state.grantedUsers.push(...subjectIds.map(id => ({ userId: id, teamRole: null }))); + }, + assignTeamRole(subjectId: string, teamRole: string | null) { + const user = this.state.grantedUsers.find(user => user.userId === subjectId); + + if (user) { + user.teamRole = teamRole; + } }, async load() { if (this.state.loaded || this.state.loading) { @@ -65,7 +73,7 @@ export function useGrantedUsers(team: TeamInfo, mode: TeamFormMode): Readonly Date: Fri, 10 May 2024 13:25:01 +0400 Subject: [PATCH 04/21] CB-4460 api fix --- .../io/cloudbeaver/service/admin/WebServiceBindingAdmin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/WebServiceBindingAdmin.java b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/WebServiceBindingAdmin.java index cee7458482..c45007c432 100644 --- a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/WebServiceBindingAdmin.java +++ b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/WebServiceBindingAdmin.java @@ -61,7 +61,7 @@ public void bindWiring(DBWBindingContext model) throws DBWebException { .dataFetcher("listAuthRoles", env -> getService(env).listAuthRoles()) .dataFetcher("listTeamRoles", - env -> getService(env).listAuthRoles()) + env -> getService(env).listTeamRoles()) .dataFetcher("listTeamMetaParameters", env -> getService(env).listTeamMetaParameters(getWebSession(env))) .dataFetcher("createUser", From f5883c0756090db219be9b9c40b8fc90043afa3a Mon Sep 17 00:00:00 2001 From: naumov Date: Fri, 10 May 2024 11:25:46 +0200 Subject: [PATCH 05/21] CB-4460 change role name --- webapp/packages/core-authentication/src/TeamRolesResource.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/packages/core-authentication/src/TeamRolesResource.ts b/webapp/packages/core-authentication/src/TeamRolesResource.ts index ba1d4cc893..e880d93648 100644 --- a/webapp/packages/core-authentication/src/TeamRolesResource.ts +++ b/webapp/packages/core-authentication/src/TeamRolesResource.ts @@ -10,7 +10,7 @@ import { CachedDataResource } from '@cloudbeaver/core-resource'; import { EAdminPermission, SessionPermissionsResource } from '@cloudbeaver/core-root'; import { GraphQLService } from '@cloudbeaver/core-sdk'; -export const USER_TEAM_ROLE_SUPERVISOR = 'SUPERVISOR'; +export const USER_TEAM_ROLE_SUPERVISOR = 'Supervisor'; @injectable() export class TeamRolesResource extends CachedDataResource { From 78927c478d49d9aee312e1f6d09af2bfbd03b40d Mon Sep 17 00:00:00 2001 From: Aleksandr Skoblikov Date: Fri, 10 May 2024 14:06:43 +0400 Subject: [PATCH 06/21] CB-4460 gql fix --- .../cloudbeaver/service/admin/WebServiceBindingAdmin.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/WebServiceBindingAdmin.java b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/WebServiceBindingAdmin.java index c45007c432..973f010f6d 100644 --- a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/WebServiceBindingAdmin.java +++ b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/WebServiceBindingAdmin.java @@ -108,6 +108,14 @@ public void bindWiring(DBWBindingContext model) throws DBWebException { env -> getService(env).enableUser(getWebSession(env), env.getArgument("userId"), env.getArgument("enabled"))) .dataFetcher("setUserAuthRole", env -> getService(env).setUserAuthRole(getWebSession(env), env.getArgument("userId"), env.getArgument("authRole"))) + .dataFetcher("setUserTeamRole", + env -> getService(env).setUserTeamRole( + getWebSession(env), + env.getArgument("userId"), + env.getArgument("teamId"), + env.getArgument("teamRole") + ) + ) .dataFetcher("searchConnections", env -> getService(env).searchConnections(getWebSession(env), env.getArgument("hostNames"))) .dataFetcher("getConnectionSubjectAccess", env -> getService(env).getConnectionSubjectAccess( From 7e9cca4d7b7d74a6d4bffbbff274e027a1473669 Mon Sep 17 00:00:00 2001 From: Aleksandr Skoblikov Date: Fri, 10 May 2024 15:40:49 +0400 Subject: [PATCH 07/21] CB-4460 fix unexpected team role modification --- .../CBEmbeddedSecurityController.java | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java index 7e404ff79c..af2f05dc1e 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java @@ -251,23 +251,43 @@ public void setUserTeamRole( } } - public void setUserTeams(@NotNull Connection dbCon, String userId, String[] teamIds, String grantorId) + //TODO implement add/delete user teams api + protected void setUserTeams(@NotNull Connection dbCon, String userId, String[] teamIds, String grantorId) throws SQLException { - JDBCUtils.executeStatement( - dbCon, - database.normalizeTableNames("DELETE FROM {table_prefix}CB_USER_TEAM WHERE USER_ID=?"), - userId - ); + + String deleteUserTeamsSql = "DELETE FROM {table_prefix}CB_USER_TEAM WHERE USER_ID=?"; + + if (!ArrayUtils.isEmpty(teamIds)) { + deleteUserTeamsSql = + deleteUserTeamsSql + " AND TEAM_ID NOT IN (" + SQLUtils.generateParamList(teamIds.length) + ")"; + } + try (PreparedStatement dbStat = dbCon.prepareStatement(database.normalizeTableNames(deleteUserTeamsSql))) { + int index = 1; + dbStat.setString(index++, userId); + for (String teamId : teamIds) { + dbStat.setString(index++, teamId); + } + } + String defaultUserTeam = getDefaultUserTeam(); if (CommonUtils.isNotEmpty(defaultUserTeam) && !ArrayUtils.contains(teamIds, defaultUserTeam)) { teamIds = ArrayUtils.add(String.class, teamIds, defaultUserTeam); } if (!ArrayUtils.isEmpty(teamIds)) { + Set currentUserTeams = new HashSet<>(JDBCUtils.queryStrings( + dbCon, + "SELECT TEAM_ID FROM {table_prefix}CB_USER_TEAM WHERE USER_ID=?", + userId + )); + try (PreparedStatement dbStat = dbCon.prepareStatement( database.normalizeTableNames("INSERT INTO {table_prefix}CB_USER_TEAM" + "(USER_ID,TEAM_ID,GRANT_TIME,GRANTED_BY) VALUES(?,?,?,?)")) ) { for (String teamId : teamIds) { + if (currentUserTeams.contains(teamId)) { + continue; + } dbStat.setString(1, userId); dbStat.setString(2, teamId); dbStat.setTimestamp(3, new Timestamp(System.currentTimeMillis())); From 814f16a0aac32f398809a0b926844f351ce1ca9a Mon Sep 17 00:00:00 2001 From: Aleksandr Skoblikov Date: Fri, 10 May 2024 15:50:35 +0400 Subject: [PATCH 08/21] CB-4460 fix unexpected team role modification --- .../service/security/CBEmbeddedSecurityController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java index af2f05dc1e..9b92494d01 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java @@ -276,7 +276,7 @@ protected void setUserTeams(@NotNull Connection dbCon, String userId, String[] t if (!ArrayUtils.isEmpty(teamIds)) { Set currentUserTeams = new HashSet<>(JDBCUtils.queryStrings( dbCon, - "SELECT TEAM_ID FROM {table_prefix}CB_USER_TEAM WHERE USER_ID=?", + database.normalizeTableNames("SELECT TEAM_ID FROM {table_prefix}CB_USER_TEAM WHERE USER_ID=?"), userId )); From ea2ba042fe1f782664c464b9bf40dce27b10ea9e Mon Sep 17 00:00:00 2001 From: Aleksandr Skoblikov Date: Fri, 10 May 2024 16:11:22 +0400 Subject: [PATCH 09/21] CB-4460 fix unexpected team role modification --- .../service/security/CBEmbeddedSecurityController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java index 9b92494d01..5e9893a88e 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java @@ -243,8 +243,8 @@ public void setUserTeamRole( database.normalizeTableNames("UPDATE {table_prefix}CB_USER_TEAM " + "SET TEAM_ROLE=? WHERE USER_ID=? AND TEAM_ID=?")) ) { - dbStat.setString(1, userId); - dbStat.setString(2, teamId); + dbStat.setString(1, teamRole); + dbStat.setString(2, userId); JDBCUtils.setStringOrNull(dbStat, 3, teamId); } catch (SQLException e) { throw new RuntimeException(e); From 82eb48ed0696e0c9d11879646974ba36bb061497 Mon Sep 17 00:00:00 2001 From: Aleksandr Skoblikov Date: Fri, 10 May 2024 16:31:44 +0400 Subject: [PATCH 10/21] CB-4460 fix anonymous preferences --- .../service/security/CBEmbeddedSecurityController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java index 5e9893a88e..273c9033d3 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java @@ -243,9 +243,9 @@ public void setUserTeamRole( database.normalizeTableNames("UPDATE {table_prefix}CB_USER_TEAM " + "SET TEAM_ROLE=? WHERE USER_ID=? AND TEAM_ID=?")) ) { - dbStat.setString(1, teamRole); + JDBCUtils.setStringOrNull(dbStat, 1, teamRole); dbStat.setString(2, userId); - JDBCUtils.setStringOrNull(dbStat, 3, teamId); + dbStat.setString(3, teamId); } catch (SQLException e) { throw new RuntimeException(e); } From a26dd4a1dd4f77a65ade4af5cb49570d2252263a Mon Sep 17 00:00:00 2001 From: naumov Date: Fri, 10 May 2024 15:14:52 +0200 Subject: [PATCH 11/21] CB-4460 adjust compare fn --- .../src/TeamRolesResource.ts | 4 ++-- .../teams/updateUserTeamRole.gql | 2 +- .../GrantedUsers/GrantedUsersTabService.ts | 20 ++++++++++--------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/webapp/packages/core-authentication/src/TeamRolesResource.ts b/webapp/packages/core-authentication/src/TeamRolesResource.ts index e880d93648..cccfc91f56 100644 --- a/webapp/packages/core-authentication/src/TeamRolesResource.ts +++ b/webapp/packages/core-authentication/src/TeamRolesResource.ts @@ -23,11 +23,11 @@ export class TeamRolesResource extends CachedDataResource { sessionPermissionsResource.require(this, EAdminPermission.admin).outdateResource(this); } - async assignTeamRoleToUser(userId: string, teamId: string, teamRole: string) { + async assignTeamRoleToUser(userId: string, teamId: string, teamRole: string | null) { const { result } = await this.graphQLService.sdk.updateUserTeamRole({ userId, teamId, - teamRole, + teamRole: teamRole ?? undefined, }); return result; diff --git a/webapp/packages/core-sdk/src/queries/authentication/teams/updateUserTeamRole.gql b/webapp/packages/core-sdk/src/queries/authentication/teams/updateUserTeamRole.gql index f999c3f656..a2bfdd5f63 100644 --- a/webapp/packages/core-sdk/src/queries/authentication/teams/updateUserTeamRole.gql +++ b/webapp/packages/core-sdk/src/queries/authentication/teams/updateUserTeamRole.gql @@ -1,3 +1,3 @@ -query updateUserTeamRole($userId: ID!, $teamId: ID!, $teamRole: String!) { +query updateUserTeamRole($userId: ID!, $teamId: ID!, $teamRole: String) { result: setUserTeamRole(userId: $userId, teamId: $teamId, teamRole: $teamRole) } diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTabService.ts b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTabService.ts index 72dac6eacc..c631baa12f 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTabService.ts +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTabService.ts @@ -11,7 +11,7 @@ import { TeamRolesResource, TeamsResource, UsersResource } from '@cloudbeaver/co import { Bootstrap, injectable } from '@cloudbeaver/core-di'; import { NotificationService } from '@cloudbeaver/core-events'; import type { IExecutionContextProvider } from '@cloudbeaver/core-executor'; -import { isArraysEqual, MetadataValueGetter } from '@cloudbeaver/core-utils'; +import { isArraysEqual, isObjectsEqual, MetadataValueGetter } from '@cloudbeaver/core-utils'; import { teamContext } from '../Contexts/teamContext'; import type { ITeamFormProps, ITeamFormSubmitData } from '../ITeamFormProps'; @@ -79,7 +79,7 @@ export class GrantedUsersTabService extends Bootstrap { const initial = await this.teamsResource.loadGrantedUsers(config.teamId); - const changed = !isArraysEqual(initial, state.grantedUsers); + const changed = !isArraysEqual(initial, state.grantedUsers, isObjectsEqual); if (!changed) { return; @@ -88,7 +88,7 @@ export class GrantedUsersTabService extends Bootstrap { const granted: string[] = []; const revoked: string[] = []; - const revokedUsers = initial.filter(user => !state.grantedUsers.includes(user)); + const revokedUsers = initial.filter(user => !state.grantedUsers.some(grantedUser => grantedUser.userId === user.userId)); try { for (const user of revokedUsers) { @@ -97,15 +97,17 @@ export class GrantedUsersTabService extends Bootstrap { } for (const user of state.grantedUsers) { - if (!initial.includes(user)) { + const initialUser = initial.find(grantedUser => grantedUser.userId === user.userId); + + if (!initialUser) { await this.usersResource.grantTeam(user.userId, config.teamId); + granted.push(user.userId); + } - // ask the backend whether we need to reset the team role on revoke or it will be done automatically - if (user.teamRole) { - await this.teamRolesResource.assignTeamRoleToUser(user.userId, config.teamId, user.teamRole); - } + const initialRole = initialUser?.teamRole ?? null; - granted.push(user.userId); + if (user.teamRole !== initialRole) { + await this.teamRolesResource.assignTeamRoleToUser(user.userId, config.teamId, user.teamRole); } } From 5805c7b6492671f24ad4b63a2733cbd90db82448 Mon Sep 17 00:00:00 2001 From: Aleksandr Skoblikov Date: Fri, 10 May 2024 17:39:05 +0400 Subject: [PATCH 12/21] CB-4460 execute update team role query --- .../service/security/CBEmbeddedSecurityController.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java index 273c9033d3..c13a1629fe 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java @@ -246,6 +246,7 @@ public void setUserTeamRole( JDBCUtils.setStringOrNull(dbStat, 1, teamRole); dbStat.setString(2, userId); dbStat.setString(3, teamId); + dbStat.execute(); } catch (SQLException e) { throw new RuntimeException(e); } From c08f64aec646d25c5f7ed28b63d4eb2393b9485f Mon Sep 17 00:00:00 2001 From: Aleksandr Skoblikov Date: Fri, 10 May 2024 17:43:33 +0400 Subject: [PATCH 13/21] CB-4460 execute team deletion query --- .../service/security/CBEmbeddedSecurityController.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java index c13a1629fe..c39e8a7d1f 100644 --- a/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java +++ b/server/bundles/io.cloudbeaver.service.security/src/io/cloudbeaver/service/security/CBEmbeddedSecurityController.java @@ -268,6 +268,7 @@ protected void setUserTeams(@NotNull Connection dbCon, String userId, String[] t for (String teamId : teamIds) { dbStat.setString(index++, teamId); } + dbStat.execute(); } String defaultUserTeam = getDefaultUserTeam(); From 20e93b03468da851a1edbec86123e7d91a5bcc33 Mon Sep 17 00:00:00 2001 From: naumov Date: Fri, 10 May 2024 18:26:35 +0200 Subject: [PATCH 14/21] CB-4460 code cleanup --- .../Users/Teams/GrantedUsers/GrantedUsers.tsx | 8 ++++++-- .../Teams/GrantedUsers/GrantedUsersTableItem.m.css | 4 ---- .../Users/Teams/GrantedUsers/UserTeamGrantInfo.ts | 11 ----------- 3 files changed, 6 insertions(+), 17 deletions(-) delete mode 100644 webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UserTeamGrantInfo.ts diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.tsx index cc744b98b6..2d21e44a2f 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.tsx @@ -46,7 +46,6 @@ export const GrantedUsers: TabContainerPanelComponent = observer active: selected && !isDefaultTeam, }); - const grantedUserIds = state.state.grantedUsers.map(user => user.userId); const grantedUsers: IGrantedUser[] = []; for (const user of users.data) { @@ -96,7 +95,12 @@ export const GrantedUsers: TabContainerPanelComponent = observer onTeamRoleAssign={state.assignTeamRole} /> {state.state.editing && ( - + user.userId)} + disabled={formState.disabled} + onGrant={state.grant} + /> )} diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableItem.m.css b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableItem.m.css index 5d43928a80..bd43f5c8d6 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableItem.m.css +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableItem.m.css @@ -9,7 +9,3 @@ display: flex; width: 24px; } - -.roleSelector { - padding: 8px 0; -} diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UserTeamGrantInfo.ts b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UserTeamGrantInfo.ts deleted file mode 100644 index cee3a5424d..0000000000 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UserTeamGrantInfo.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2024 DBeaver Corp and others - * - * Licensed under the Apache License, Version 2.0. - * you may not use this file except in compliance with the License. - */ -import type { AdminUserTeamGrantInfo } from '@cloudbeaver/core-sdk'; -import type { UndefinedToNull } from '@cloudbeaver/core-utils'; - -export type UserTeamGrantInfo = UndefinedToNull; From c9257cfc424df69c66ca0246894c9809e533ee50 Mon Sep 17 00:00:00 2001 From: Aleksandr Skoblikov Date: Mon, 13 May 2024 13:35:18 +0400 Subject: [PATCH 15/21] CB-4460 qql schema review fix --- .../schema/service.admin.graphqls | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/bundles/io.cloudbeaver.service.admin/schema/service.admin.graphqls b/server/bundles/io.cloudbeaver.service.admin/schema/service.admin.graphqls index 3ec3d71afa..559b9b90e4 100644 --- a/server/bundles/io.cloudbeaver.service.admin/schema/service.admin.graphqls +++ b/server/bundles/io.cloudbeaver.service.admin/schema/service.admin.graphqls @@ -11,7 +11,7 @@ type AdminConnectionGrantInfo { subjectType: AdminSubjectType! } -type AdminUserTeamGrantInfo { +type AdminUserTeamGrantInfo @since(version: "24.0.5"){ userId: ID! teamRole: String } @@ -58,7 +58,7 @@ type AdminTeamInfo { metaParameters: Object! grantedUsers: [ID!]! - grantedUsersInfo: [AdminUserTeamGrantInfo]! + grantedUsersInfo: [AdminUserTeamGrantInfo!]! @since(version: "24.0.5") grantedConnections: [AdminConnectionGrantInfo!]! teamPermissions: [ID!]! @@ -157,7 +157,7 @@ extend type Query { setUserAuthRole(userId: ID!, authRole: String): Boolean - setUserTeamRole(userId: ID!, teamId: ID!,teamRole: String): Boolean + setUserTeamRole(userId: ID!, teamId: ID!, teamRole: String): Boolean @since(version: "24.0.5") #### Connection management From 4c4ec33f0026177ad8e8e6522b30b82b54f54786 Mon Sep 17 00:00:00 2001 From: Aleksandr Skoblikov Date: Mon, 13 May 2024 14:00:47 +0400 Subject: [PATCH 16/21] CB-4460 remove unused table --- .../db/cb_schema_create.sql | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/server/bundles/io.cloudbeaver.service.security/db/cb_schema_create.sql b/server/bundles/io.cloudbeaver.service.security/db/cb_schema_create.sql index 01642438d3..3e290e80ea 100644 --- a/server/bundles/io.cloudbeaver.service.security/db/cb_schema_create.sql +++ b/server/bundles/io.cloudbeaver.service.security/db/cb_schema_create.sql @@ -63,20 +63,6 @@ CREATE TABLE {table_prefix}CB_AUTH_PERMISSIONS FOREIGN KEY (SUBJECT_ID) REFERENCES {table_prefix}CB_AUTH_SUBJECT (SUBJECT_ID) ON DELETE CASCADE ); -CREATE TABLE {table_prefix}CB_TEAM_PERMISSIONS -( - USER_ID VARCHAR(128) NOT NULL, - TEAM_ID VARCHAR(128) NOT NULL, - PERMISSION_ID VARCHAR(64) NOT NULL, - - GRANT_TIME TIMESTAMP NOT NULL, - GRANTED_BY VARCHAR(128) NOT NULL, - - PRIMARY KEY (USER_ID, TEAM_ID, PERMISSION_ID), - FOREIGN KEY (USER_ID) REFERENCES {table_prefix}CB_AUTH_SUBJECT (SUBJECT_ID) ON DELETE CASCADE, - FOREIGN KEY (TEAM_ID) REFERENCES {table_prefix}CB_AUTH_SUBJECT (SUBJECT_ID) ON DELETE NO ACTION -); - CREATE TABLE {table_prefix}CB_OBJECT_PERMISSIONS ( OBJECT_ID VARCHAR(128) NOT NULL, From 0947071b0102f762db80a4f3f5e3bd637c78c291 Mon Sep 17 00:00:00 2001 From: naumov Date: Mon, 13 May 2024 15:26:19 +0200 Subject: [PATCH 17/21] CB-4460 review fixes --- .../core-authentication/src/TeamsResource.ts | 2 +- .../core-blocks/src/FormControls/Combobox.tsx | 9 ++- .../src/FormControls/TagsCombobox.tsx | 75 +++++++++++++++++++ .../src/FormControls/TagsComboboxLoader.ts | 10 +++ webapp/packages/core-blocks/src/index.ts | 1 + 5 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 webapp/packages/core-blocks/src/FormControls/TagsCombobox.tsx create mode 100644 webapp/packages/core-blocks/src/FormControls/TagsComboboxLoader.ts diff --git a/webapp/packages/core-authentication/src/TeamsResource.ts b/webapp/packages/core-authentication/src/TeamsResource.ts index eb7e460747..08373dfec0 100644 --- a/webapp/packages/core-authentication/src/TeamsResource.ts +++ b/webapp/packages/core-authentication/src/TeamsResource.ts @@ -94,7 +94,7 @@ export class TeamsResource extends CachedMapResource { const { team } = await this.graphQLService.sdk.getTeamGrantedUsers({ teamId }); - return team[0].grantedUsersInfo as UserTeamGrantInfo[]; + return team[0].grantedUsersInfo.map(user => ({ userId: user.userId, teamRole: user.teamRole ?? null })); } async getSubjectConnectionAccess(subjectId: string): Promise { diff --git a/webapp/packages/core-blocks/src/FormControls/Combobox.tsx b/webapp/packages/core-blocks/src/FormControls/Combobox.tsx index b72d54e063..405d2cf99a 100644 --- a/webapp/packages/core-blocks/src/FormControls/Combobox.tsx +++ b/webapp/packages/core-blocks/src/FormControls/Combobox.tsx @@ -24,7 +24,10 @@ import { FieldDescription } from './FieldDescription'; import { FieldLabel } from './FieldLabel'; import { FormContext } from './FormContext'; -type BaseProps = Omit, 'onChange' | 'onSelect' | 'name' | 'value' | 'defaultValue'> & +export type ComboboxBaseProps = Omit< + React.InputHTMLAttributes, + 'onChange' | 'onSelect' | 'name' | 'value' | 'defaultValue' +> & ILayoutSizeProps & { propertyName?: string; items: TValue[]; @@ -41,7 +44,7 @@ type BaseProps = Omit, inline?: boolean; }; -type ControlledProps = BaseProps & { +type ControlledProps = ComboboxBaseProps & { name?: string; value?: TKey; onSelect?: (value: TKey, name: string | undefined, prev: TKey) => void; @@ -49,7 +52,7 @@ type ControlledProps = BaseProps & { state?: never; }; -type ObjectProps = BaseProps & { +type ObjectProps = ComboboxBaseProps & { name: TKey; state: TState; onSelect?: (value: TState[TKey], name: TKey | undefined, prev: TState[TKey]) => void; diff --git a/webapp/packages/core-blocks/src/FormControls/TagsCombobox.tsx b/webapp/packages/core-blocks/src/FormControls/TagsCombobox.tsx new file mode 100644 index 0000000000..904624fa58 --- /dev/null +++ b/webapp/packages/core-blocks/src/FormControls/TagsCombobox.tsx @@ -0,0 +1,75 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +import { observer } from 'mobx-react-lite'; + +import { Container } from '../Containers/Container'; +import { ITag, Tag } from '../Tags/Tag'; +import { Tags } from '../Tags/Tags'; +import { Combobox, ComboboxBaseProps } from './Combobox'; + +interface IItemValue { + id: string; + label: string; + icon?: string; +} + +// @TODO use [name] and [state] pattern for the component +export interface ITagsComboboxProps extends ComboboxBaseProps { + addedItems: string[]; + onAdd: (key: string) => void; + onRemove: (key: string, index: number) => void; +} + +export const TagsCombobox: React.FC = observer(function TagsCombobox({ addedItems, onAdd, onRemove, ...rest }) { + const tags: ITag[] = []; + + for (const addedItem of addedItems) { + const item = rest.items.find(item => item.id === addedItem); + + if (item) { + tags.push({ + id: item.id, + label: item.label, + icon: item.icon, + }); + } + } + + function add(key: string) { + if (!addedItems.includes(key)) { + onAdd(key); + } + } + + function remove(key: string) { + const index = addedItems.indexOf(key); + + if (index !== -1) { + onRemove(key, index); + } + } + + return ( + + item.id} + valueSelector={value => value.label} + iconSelector={value => value.icon} + isDisabled={item => addedItems.includes(item.id)} + searchable + onSelect={add} + {...rest} + /> + + {tags.map(tag => ( + + ))} + + + ); +}); diff --git a/webapp/packages/core-blocks/src/FormControls/TagsComboboxLoader.ts b/webapp/packages/core-blocks/src/FormControls/TagsComboboxLoader.ts new file mode 100644 index 0000000000..ec8a0783ce --- /dev/null +++ b/webapp/packages/core-blocks/src/FormControls/TagsComboboxLoader.ts @@ -0,0 +1,10 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +import { importLazyComponent } from '../importLazyComponent'; + +export const TagsCombobox = importLazyComponent(() => import('./TagsCombobox').then(m => m.TagsCombobox)); diff --git a/webapp/packages/core-blocks/src/index.ts b/webapp/packages/core-blocks/src/index.ts index a81629c5d6..d34a1d3b4e 100644 --- a/webapp/packages/core-blocks/src/index.ts +++ b/webapp/packages/core-blocks/src/index.ts @@ -236,3 +236,4 @@ export * from './useMergeRefs'; export * from './usePasswordValidation'; export * from './manifest'; export * from './importLazyComponent'; +export * from './FormControls/TagsComboboxLoader'; From 1600861ebfcfbe2ac8fe39c7ee7348476cf03790 Mon Sep 17 00:00:00 2001 From: naumov Date: Mon, 13 May 2024 16:32:10 +0200 Subject: [PATCH 18/21] CB-4460 do not render icon component if there is not icons provided --- webapp/packages/core-blocks/src/FormControls/TagsCombobox.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webapp/packages/core-blocks/src/FormControls/TagsCombobox.tsx b/webapp/packages/core-blocks/src/FormControls/TagsCombobox.tsx index 904624fa58..ba5b604a21 100644 --- a/webapp/packages/core-blocks/src/FormControls/TagsCombobox.tsx +++ b/webapp/packages/core-blocks/src/FormControls/TagsCombobox.tsx @@ -27,6 +27,7 @@ export interface ITagsComboboxProps extends ComboboxBaseProps = observer(function TagsCombobox({ addedItems, onAdd, onRemove, ...rest }) { const tags: ITag[] = []; + const hasIcons = rest.items.some(item => !!item.icon); for (const addedItem of addedItems) { const item = rest.items.find(item => item.id === addedItem); @@ -59,7 +60,7 @@ export const TagsCombobox: React.FC = observer(function Tags item.id} valueSelector={value => value.label} - iconSelector={value => value.icon} + iconSelector={hasIcons ? value => value.icon : undefined} isDisabled={item => addedItems.includes(item.id)} searchable onSelect={add} From 06db2bd337629b09525a618e52887997d211d225 Mon Sep 17 00:00:00 2001 From: naumov Date: Mon, 13 May 2024 17:52:38 +0200 Subject: [PATCH 19/21] CB-4460 change ru locale for supervisor --- .../plugin-authentication-administration/src/locales/ru.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/packages/plugin-authentication-administration/src/locales/ru.ts b/webapp/packages/plugin-authentication-administration/src/locales/ru.ts index 60a4bfd1fd..ef6ebb135a 100644 --- a/webapp/packages/plugin-authentication-administration/src/locales/ru.ts +++ b/webapp/packages/plugin-authentication-administration/src/locales/ru.ts @@ -144,5 +144,5 @@ export default [ ['plugin_authentication_administration_user_team_default_readonly_tooltip', 'Команда по умолчанию. Не может быть отозвана'], ['plugin_authentication_administration_team_default_users_tooltip', 'Команда по умолчанию. Содержит всех пользователей'], - ['plugin_authentication_administration_team_user_team_role_supervisor', 'Руководитель'], + ['plugin_authentication_administration_team_user_team_role_supervisor', 'Супервайзер'], ]; From f5db296bcd2cbd520a4bb72f2572a312c0b16863 Mon Sep 17 00:00:00 2001 From: naumov Date: Mon, 13 May 2024 18:07:28 +0200 Subject: [PATCH 20/21] CB-4460 add supervisor description --- .../GrantedUsersTableInnerHeader.tsx | 6 +++++- .../Users/Teams/GrantedUsers/GrantedUsersTableItem.tsx | 6 ++++-- .../plugin-authentication-administration/src/locales/en.ts | 1 + .../plugin-authentication-administration/src/locales/it.ts | 1 + .../plugin-authentication-administration/src/locales/ru.ts | 4 ++++ .../plugin-authentication-administration/src/locales/zh.ts | 1 + 6 files changed, 16 insertions(+), 3 deletions(-) diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableHeader/GrantedUsersTableInnerHeader.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableHeader/GrantedUsersTableInnerHeader.tsx index 7b3c8214af..6508115c54 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableHeader/GrantedUsersTableInnerHeader.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableHeader/GrantedUsersTableInnerHeader.tsx @@ -25,7 +25,11 @@ export const GrantedUsersTableInnerHeader = observer(function GrantedUser {translate('administration_teams_team_granted_users_user_id')} - {showUserTeamRole && {translate('plugin_authentication_administration_team_user_team_role_supervisor')}} + {showUserTeamRole && ( + + {translate('plugin_authentication_administration_team_user_team_role_supervisor')} + + )} ); }); diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableItem.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableItem.tsx index 6ecd0a0293..45047fd881 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableItem.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsersTableItem.tsx @@ -8,7 +8,7 @@ import { observer } from 'mobx-react-lite'; import { USER_TEAM_ROLE_SUPERVISOR } from '@cloudbeaver/core-authentication'; -import { Checkbox, StaticImage, TableColumnValue, TableItem, TableItemSelect } from '@cloudbeaver/core-blocks'; +import { Checkbox, StaticImage, TableColumnValue, TableItem, TableItemSelect, useTranslate } from '@cloudbeaver/core-blocks'; import classes from './GrantedUsersTableItem.m.css'; @@ -37,6 +37,8 @@ export const GrantedUsersTableItem = observer(function GrantedUsersTableI disabled, className, }) { + const translate = useTranslate(); + return ( @@ -47,7 +49,7 @@ export const GrantedUsersTableItem = observer(function GrantedUsersTableI {name} {teamRoles.length > 0 && ( - + onTeamRoleAssign(id, value ? USER_TEAM_ROLE_SUPERVISOR : null)} diff --git a/webapp/packages/plugin-authentication-administration/src/locales/en.ts b/webapp/packages/plugin-authentication-administration/src/locales/en.ts index 4e25023a9d..976efadfa9 100644 --- a/webapp/packages/plugin-authentication-administration/src/locales/en.ts +++ b/webapp/packages/plugin-authentication-administration/src/locales/en.ts @@ -140,4 +140,5 @@ export default [ ['plugin_authentication_administration_user_team_default_readonly_tooltip', "Default team. Can't be revoked"], ['plugin_authentication_administration_team_default_users_tooltip', 'Default team. Contains all users'], ['plugin_authentication_administration_team_user_team_role_supervisor', 'Supervisor'], + ['plugin_authentication_administration_team_user_team_role_supervisor_description', 'Supervisors can view their team’s executed queries'], ]; diff --git a/webapp/packages/plugin-authentication-administration/src/locales/it.ts b/webapp/packages/plugin-authentication-administration/src/locales/it.ts index ed383d4ce1..2b9dbad200 100644 --- a/webapp/packages/plugin-authentication-administration/src/locales/it.ts +++ b/webapp/packages/plugin-authentication-administration/src/locales/it.ts @@ -85,4 +85,5 @@ export default [ ['plugin_authentication_administration_user_team_default_readonly_tooltip', "Default team. Can't be revoked"], ['plugin_authentication_administration_team_default_users_tooltip', 'Default team. Contains all users'], ['plugin_authentication_administration_team_user_team_role_supervisor', 'Supervisor'], + ['plugin_authentication_administration_team_user_team_role_supervisor_description', 'Supervisors can view their team’s executed queries'], ]; diff --git a/webapp/packages/plugin-authentication-administration/src/locales/ru.ts b/webapp/packages/plugin-authentication-administration/src/locales/ru.ts index ef6ebb135a..1f46b3324a 100644 --- a/webapp/packages/plugin-authentication-administration/src/locales/ru.ts +++ b/webapp/packages/plugin-authentication-administration/src/locales/ru.ts @@ -145,4 +145,8 @@ export default [ ['plugin_authentication_administration_user_team_default_readonly_tooltip', 'Команда по умолчанию. Не может быть отозвана'], ['plugin_authentication_administration_team_default_users_tooltip', 'Команда по умолчанию. Содержит всех пользователей'], ['plugin_authentication_administration_team_user_team_role_supervisor', 'Супервайзер'], + [ + 'plugin_authentication_administration_team_user_team_role_supervisor_description', + 'Супервайзеры могут просматривать выполненные запросы своей команды', + ], ]; diff --git a/webapp/packages/plugin-authentication-administration/src/locales/zh.ts b/webapp/packages/plugin-authentication-administration/src/locales/zh.ts index 5a4bba794d..a20812e636 100644 --- a/webapp/packages/plugin-authentication-administration/src/locales/zh.ts +++ b/webapp/packages/plugin-authentication-administration/src/locales/zh.ts @@ -122,4 +122,5 @@ export default [ ['plugin_authentication_administration_user_team_default_readonly_tooltip', "Default team. Can't be revoked"], ['plugin_authentication_administration_team_default_users_tooltip', 'Default team. Contains all users'], ['plugin_authentication_administration_team_user_team_role_supervisor', 'Supervisor'], + ['plugin_authentication_administration_team_user_team_role_supervisor_description', 'Supervisors can view their team’s executed queries'], ]; From ed4c72a1c439fdbad805c6d80e5ea5a2a1a40b1c Mon Sep 17 00:00:00 2001 From: naumov Date: Tue, 14 May 2024 18:54:11 +0200 Subject: [PATCH 21/21] CB-4460 remove unused styles --- .../Users/Teams/GrantedUsers/UsersTableItem.m.css | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UsersTableItem.m.css diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UsersTableItem.m.css b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UsersTableItem.m.css deleted file mode 100644 index bd43f5c8d6..0000000000 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/UsersTableItem.m.css +++ /dev/null @@ -1,11 +0,0 @@ -/* - * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2024 DBeaver Corp and others - * - * Licensed under the Apache License, Version 2.0. - * you may not use this file except in compliance with the License. - */ -.staticImage { - display: flex; - width: 24px; -}