From a230dac79ea954aefc3a336b34e39965b540d449 Mon Sep 17 00:00:00 2001 From: Alexey Date: Fri, 19 Jan 2024 21:11:39 +0300 Subject: [PATCH 1/2] Cb 4403 map ad roles (#2295) * CB-4403. Implement import with auth roles for AD * CB-4403 feat: add auth role provided flag * CB-4403. Refactor after review, added auto configurable flag * CB-4403. Refactor after review * CB-4403. Refactor after review * CB-4403. Refactor after review * CB-4403. Fixed set teams for imported users * CB-4403. Refactor after review * CB-4403. Refactor after review --------- Co-authored-by: Denis Sinelnikov Co-authored-by: DenisSinelnikov <142215442+DenisSinelnikov@users.noreply.github.com> Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com> --- .../auth/provisioning/SMProvisioner.java | 4 +++ .../model/app/WebAuthApplication.java | 4 +++ .../model/user/WebAuthProviderInfo.java | 6 +++++ .../schema/service.auth.graphqls | 1 + .../CBEmbeddedSecurityController.java | 27 +++++++++---------- .../AuthProviderConfigurationInfo.gql | 1 + 6 files changed, 28 insertions(+), 15 deletions(-) diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/provisioning/SMProvisioner.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/provisioning/SMProvisioner.java index 7c40493752..291b73ea1a 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/provisioning/SMProvisioner.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/auth/provisioning/SMProvisioner.java @@ -31,4 +31,8 @@ List listExternalUsers( @NotNull SMAuthProviderCustomConfiguration customConfiguration, @NotNull SMProvisioningFilter filter ) throws DBException; + + default boolean isAuthRoleProvided(SMAuthProviderCustomConfiguration configuration) { + return false; + } } diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/WebAuthApplication.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/WebAuthApplication.java index fae36cfecb..4817849c8d 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/WebAuthApplication.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/WebAuthApplication.java @@ -20,6 +20,8 @@ import io.cloudbeaver.auth.CBAuthConstants; import org.jkiss.dbeaver.DBException; +import java.util.List; + public interface WebAuthApplication extends WebApplication { WebAuthConfiguration getAuthConfiguration(); @@ -30,4 +32,6 @@ default long getMaxSessionIdleTime() { } void flushConfiguration() throws DBException; + + String getDefaultAuthRole(); } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebAuthProviderInfo.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebAuthProviderInfo.java index 2a6a036f57..c3c211c970 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebAuthProviderInfo.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebAuthProviderInfo.java @@ -86,6 +86,12 @@ public boolean isPrivate() { public boolean isRequired() { return descriptor.isRequired(); } + public boolean isAuthRoleProvided(SMAuthProviderCustomConfiguration configuration) { + if (descriptor.getInstance() instanceof SMProvisioner provisioner) { + return provisioner.isAuthRoleProvided(configuration); + } + return false; + } public boolean isSupportProvisioning() { return descriptor.getInstance() instanceof SMProvisioner; 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 0f4fca656f..06085838cf 100644 --- a/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls +++ b/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls @@ -30,6 +30,7 @@ type AuthProviderConfiguration { id: ID! displayName: String! disabled: Boolean! + authRoleProvided: Boolean iconURL: String description: String 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 c7ab132345..b3fffe8bc0 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 @@ -133,10 +133,6 @@ public void createUser( try (Connection dbCon = database.openConnection()) { try (JDBCTransaction txn = new JDBCTransaction(dbCon)) { createUser(dbCon, userId, metaParameters, enabled, defaultAuthRole); - String defaultTeamName = application.getAppConfiguration().getDefaultUserTeam(); - if (!CommonUtils.isEmpty(defaultTeamName)) { - setUserTeams(dbCon, userId, new String[]{defaultTeamName}, userId); - } txn.commit(); } } catch (SQLException e) { @@ -167,31 +163,32 @@ public void createUser( dbStat.execute(); } saveSubjectMetas(dbCon, userId, metaParameters); - + String defaultTeamName = application.getAppConfiguration().getDefaultUserTeam(); + if (!CommonUtils.isEmpty(defaultTeamName)) { + setUserTeams(dbCon, userId, new String[]{defaultTeamName}, userId); + } } @Override public void importUsers(@NotNull SMUserImportList userImportList) throws DBException { - for (SMUserProvisioning user : userImportList.getUsers()) { - if (isSubjectExists(user.getUserId())) { - log.info("Skip already exist user: " + user.getUserId()); - setUserAuthRole(user.getUserId(), userImportList.getAuthRole()); - continue; - } - createUser(user.getUserId(), user.getMetaParameters(), true, userImportList.getAuthRole()); - } + try (var dbCon = database.openConnection()) { + importUsers(dbCon, userImportList); + } catch (SQLException e) { + log.error("Failed attempt import user: " + e.getMessage()); + } } protected void importUsers(@NotNull Connection connection, @NotNull SMUserImportList userImportList) throws DBException, SQLException { for (SMUserProvisioning user : userImportList.getUsers()) { + String authRole = user.getAuthRole() == null ? userImportList.getAuthRole() : user.getAuthRole(); if (isSubjectExists(user.getUserId())) { log.info("User already exist : " + user.getUserId()); - setUserAuthRole(connection, user.getUserId(), userImportList.getAuthRole()); + setUserAuthRole(connection, user.getUserId(), authRole); enableUser(connection, user.getUserId(), true); continue; } - createUser(connection, user.getUserId(), user.getMetaParameters(), true, userImportList.getAuthRole()); + createUser(connection, user.getUserId(), user.getMetaParameters(), true, authRole); } } diff --git a/webapp/packages/core-sdk/src/queries/fragments/AuthProviderConfigurationInfo.gql b/webapp/packages/core-sdk/src/queries/fragments/AuthProviderConfigurationInfo.gql index 64ef403ca4..99c0e023f4 100644 --- a/webapp/packages/core-sdk/src/queries/fragments/AuthProviderConfigurationInfo.gql +++ b/webapp/packages/core-sdk/src/queries/fragments/AuthProviderConfigurationInfo.gql @@ -1,6 +1,7 @@ fragment AuthProviderConfigurationInfo on AuthProviderConfiguration { id displayName + authRoleProvided iconURL description signInLink From 6b124072e940b37ed3830ae0ca7c21c54325a711 Mon Sep 17 00:00:00 2001 From: Alexey Date: Sun, 21 Jan 2024 16:18:35 +0300 Subject: [PATCH 2/2] chore: performance improvements (#2319) --- .../src/Tree/TreeNode/TreeNodeIcon.m.css | 1 + .../src/Tree/TreeNode/TreeNodeNested.tsx | 1 - .../core-ui/src/DragAndDrop/useDNDData.ts | 2 +- webapp/packages/core-utils/src/TempMap.ts | 19 ++++++++----------- .../NavigationTreeNode/useNavigationNode.ts | 4 +--- 5 files changed, 11 insertions(+), 16 deletions(-) diff --git a/webapp/packages/core-blocks/src/Tree/TreeNode/TreeNodeIcon.m.css b/webapp/packages/core-blocks/src/Tree/TreeNode/TreeNodeIcon.m.css index 20eb274d11..8dd258248a 100644 --- a/webapp/packages/core-blocks/src/Tree/TreeNode/TreeNodeIcon.m.css +++ b/webapp/packages/core-blocks/src/Tree/TreeNode/TreeNodeIcon.m.css @@ -1,6 +1,7 @@ .treeNodeIcon { position: relative; box-sizing: border-box; + pointer-events: none; flex-shrink: 0; width: 16px; height: 16px; diff --git a/webapp/packages/core-blocks/src/Tree/TreeNode/TreeNodeNested.tsx b/webapp/packages/core-blocks/src/Tree/TreeNode/TreeNodeNested.tsx index b379a6922a..1abdf77717 100644 --- a/webapp/packages/core-blocks/src/Tree/TreeNode/TreeNodeNested.tsx +++ b/webapp/packages/core-blocks/src/Tree/TreeNode/TreeNodeNested.tsx @@ -13,7 +13,6 @@ import { useS } from '../../useS'; import style from './TreeNodeNested.m.css'; interface Props extends React.PropsWithChildren { - expanded?: boolean; root?: boolean; className?: string; } diff --git a/webapp/packages/core-ui/src/DragAndDrop/useDNDData.ts b/webapp/packages/core-ui/src/DragAndDrop/useDNDData.ts index ab46528e1c..7c3cfe6ec1 100644 --- a/webapp/packages/core-ui/src/DragAndDrop/useDNDData.ts +++ b/webapp/packages/core-ui/src/DragAndDrop/useDNDData.ts @@ -68,7 +68,7 @@ export function useDNDData(context: IDataContextProvider, options: IOptions = {} } } - state.isDragging = monitor.isDragging(); + state.isDragging = dragging; }, })); diff --git a/webapp/packages/core-utils/src/TempMap.ts b/webapp/packages/core-utils/src/TempMap.ts index cf63ec3d77..144d3c29ff 100644 --- a/webapp/packages/core-utils/src/TempMap.ts +++ b/webapp/packages/core-utils/src/TempMap.ts @@ -23,7 +23,7 @@ export class TempMap implements Map { return 'TempMap'; } - private readonly deleted: TKey[]; + private readonly deleted: Map; private readonly temp: Map; private flushTask: NodeJS.Timeout | null; private readonly keysTemp: ICachedValueObject; @@ -33,7 +33,7 @@ export class TempMap implements Map { constructor(private readonly target: Map, private readonly onSync?: () => void) { this.temp = new Map(); this.flushTask = null; - this.deleted = []; + this.deleted = new Map(); this.keysTemp = cacheValue(); this.entriesTemp = cacheValue(); this.valuesTemp = cacheValue(); @@ -45,7 +45,7 @@ export class TempMap implements Map { } isDeleted(key: TKey): boolean { - return this.deleted.includes(key); + return this.deleted.get(key) || false; } /** @@ -56,7 +56,7 @@ export class TempMap implements Map { clearTimeout(this.flushTask); this.flushTask = null; } - this.deleted.splice(0, this.deleted.length); + this.deleted.clear(); this.temp.clear(); this.keysTemp.invalidate(); this.valuesTemp.invalidate(); @@ -65,7 +65,7 @@ export class TempMap implements Map { delete(key: TKey): boolean { this.temp.delete(key); - this.deleted.push(key); + this.deleted.set(key, true); this.scheduleFlush(); return this.has(key); } @@ -105,10 +105,7 @@ export class TempMap implements Map { set(key: TKey, value: TValue): this { this.temp.set(key, value); - const indexOfDeleted = this.deleted.indexOf(key); - if (indexOfDeleted !== -1) { - this.deleted.splice(indexOfDeleted, 1); - } + this.deleted.delete(key); this.scheduleFlush(); return this; @@ -139,10 +136,10 @@ export class TempMap implements Map { this.flushTask = setTimeout( action(() => { - for (const deleted of this.deleted) { + for (const [deleted] of this.deleted) { this.target.delete(deleted); } - this.deleted.splice(0, this.deleted.length); + this.deleted.clear(); for (const [key, value] of this.temp) { this.target.set(key, value); diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/useNavigationNode.ts b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/useNavigationNode.ts index 7f296afb28..d1d72f5ad3 100644 --- a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/useNavigationNode.ts +++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/useNavigationNode.ts @@ -9,7 +9,6 @@ import React, { useContext, useEffect, useRef } from 'react'; import { getComputed, useExecutor, useObjectRef } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; -import { SyncExecutor } from '@cloudbeaver/core-executor'; import { EObjectFeature, type NavNode, NavNodeInfoResource } from '@cloudbeaver/core-navigation-tree'; import { resourceKeyList } from '@cloudbeaver/core-resource'; import type { IDNDData } from '@cloudbeaver/core-ui'; @@ -17,7 +16,6 @@ import type { IDNDData } from '@cloudbeaver/core-ui'; import { useChildren } from '../../../NodesManager/useChildren'; import { useNode } from '../../../NodesManager/useNode'; import { ElementsTreeContext } from '../ElementsTreeContext'; -import type { IElementsTreeAction } from '../IElementsTreeAction'; import type { NavTreeControlComponent } from '../NavigationNodeComponent'; import type { IElementsTree } from '../useElementsTree'; @@ -94,7 +92,7 @@ export function useNavigationNode(node: NavNode, path: string[]): INavigationNod }, []); useExecutor({ - executor: contextRef.context?.tree.actions || new SyncExecutor(), + executor: contextRef.context?.tree.actions, handlers: [ function refreshRoot({ type, nodeId }) { if (type === 'show' && nodeId === node.id) {