From 3c5ef6190ca9822ca6d32ca9b92707414f4bd18b Mon Sep 17 00:00:00 2001 From: Ainur Date: Tue, 8 Nov 2022 14:33:51 +0100 Subject: [PATCH 01/21] CB-2149 add showReadOnlyConnectionInfo property --- .../cloudbeaver/model/WebConnectionInfo.java | 10 +++++++++- .../model/app/BaseWebAppConfiguration.java | 7 +++++++ .../io/cloudbeaver/server/CBApplication.java | 20 ++++++++++--------- .../io/cloudbeaver/server/CBConstants.java | 1 + 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionInfo.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionInfo.java index 3f24947c00..2fc776a468 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionInfo.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionInfo.java @@ -17,10 +17,12 @@ package io.cloudbeaver.model; import io.cloudbeaver.WebProjectImpl; +import io.cloudbeaver.model.app.BaseWebAppConfiguration; import io.cloudbeaver.model.session.WebSession; import io.cloudbeaver.service.security.SMUtils; import io.cloudbeaver.service.sql.WebDataFormat; import io.cloudbeaver.utils.CBModelConstants; +import io.cloudbeaver.utils.WebAppUtils; import io.cloudbeaver.utils.WebCommonUtils; import org.jkiss.dbeaver.model.DBPDataSource; import org.jkiss.dbeaver.model.DBPDataSourceContainer; @@ -341,7 +343,7 @@ public class WebConnectionInfo { @Property public boolean isCanViewSettings() { - return true; + return canViewReadOnlyConnections(); } @Property @@ -371,4 +373,10 @@ public class WebConnectionInfo { } return SMUtils.hasProjectPermission(session, ((WebProjectImpl) project).getRmProject(), projectPermission); } + + private boolean canViewReadOnlyConnections() { + BaseWebAppConfiguration appConfig = (BaseWebAppConfiguration) WebAppUtils.getWebApplication().getAppConfiguration(); + return appConfig.isShowReadOnlyConnectionInfo(); + + } } diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/BaseWebAppConfiguration.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/BaseWebAppConfiguration.java index 82a17315e5..7d02c127f5 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/BaseWebAppConfiguration.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/BaseWebAppConfiguration.java @@ -31,6 +31,7 @@ public abstract class BaseWebAppConfiguration implements WebAppConfiguration { protected final Map plugins; protected String defaultUserTeam; protected boolean resourceManagerEnabled; + protected boolean showReadOnlyConnectionInfo; protected String[] enabledFeatures; public BaseWebAppConfiguration() { @@ -38,6 +39,7 @@ public abstract class BaseWebAppConfiguration implements WebAppConfiguration { this.defaultUserTeam = DEFAULT_APP_ANONYMOUS_TEAM_NAME; this.resourceManagerEnabled = true; this.enabledFeatures = null; + this.showReadOnlyConnectionInfo = false; } public BaseWebAppConfiguration(BaseWebAppConfiguration src) { @@ -45,6 +47,7 @@ public abstract class BaseWebAppConfiguration implements WebAppConfiguration { this.defaultUserTeam = src.defaultUserTeam; this.resourceManagerEnabled = src.resourceManagerEnabled; this.enabledFeatures = src.enabledFeatures; + this.showReadOnlyConnectionInfo = src.showReadOnlyConnectionInfo; } @Override @@ -98,4 +101,8 @@ public abstract class BaseWebAppConfiguration implements WebAppConfiguration { public void setEnabledFeatures(String[] enabledFeatures) { this.enabledFeatures = enabledFeatures; } + + public boolean isShowReadOnlyConnectionInfo() { + return showReadOnlyConnectionInfo; + } } 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 e44699337e..3ab312a6fa 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 @@ -930,23 +930,25 @@ public class CBApplication extends BaseWebApplication implements WebAuthApplicat rootConfig.put("app", appConfigProperties); copyConfigValue( - oldAppConfig, appConfigProperties, "anonymousAccessEnabled", appConfig.isAnonymousAccessEnabled()); + oldAppConfig, appConfigProperties, "anonymousAccessEnabled", appConfig.isAnonymousAccessEnabled()); copyConfigValue( - oldAppConfig, appConfigProperties, "supportsCustomConnections", appConfig.isSupportsCustomConnections()); + oldAppConfig, appConfigProperties, "supportsCustomConnections", appConfig.isSupportsCustomConnections()); copyConfigValue( - oldAppConfig, appConfigProperties, "publicCredentialsSaveEnabled", appConfig.isPublicCredentialsSaveEnabled()); + oldAppConfig, appConfigProperties, "publicCredentialsSaveEnabled", appConfig.isPublicCredentialsSaveEnabled()); copyConfigValue( - oldAppConfig, appConfigProperties, "adminCredentialsSaveEnabled", appConfig.isAdminCredentialsSaveEnabled()); + oldAppConfig, appConfigProperties, "adminCredentialsSaveEnabled", appConfig.isAdminCredentialsSaveEnabled()); copyConfigValue( - oldAppConfig, appConfigProperties, "enableReverseProxyAuth", appConfig.isEnabledReverseProxyAuth()); + oldAppConfig, appConfigProperties, "enableReverseProxyAuth", appConfig.isEnabledReverseProxyAuth()); copyConfigValue( - oldAppConfig, appConfigProperties, "forwardProxy", appConfig.isEnabledForwardProxy()); + oldAppConfig, appConfigProperties, "forwardProxy", appConfig.isEnabledForwardProxy()); copyConfigValue( - oldAppConfig, appConfigProperties, "linkExternalCredentialsWithUser", appConfig.isLinkExternalCredentialsWithUser()); + oldAppConfig, appConfigProperties, "linkExternalCredentialsWithUser", appConfig.isLinkExternalCredentialsWithUser()); copyConfigValue( - oldAppConfig, appConfigProperties, "redirectOnFederatedAuth", appConfig.isRedirectOnFederatedAuth()); + oldAppConfig, appConfigProperties, "redirectOnFederatedAuth", appConfig.isRedirectOnFederatedAuth()); copyConfigValue( - oldAppConfig, appConfigProperties, CBConstants.PARAM_RESOURCE_MANAGER_ENABLED, appConfig.isResourceManagerEnabled()); + oldAppConfig, appConfigProperties, CBConstants.PARAM_RESOURCE_MANAGER_ENABLED, appConfig.isResourceManagerEnabled()); + copyConfigValue( + oldAppConfig, appConfigProperties, CBConstants.PARAM_SHOW_READ_ONLY_CONN_INFO, appConfig.isShowReadOnlyConnectionInfo()); Map resourceQuotas = new LinkedHashMap<>(); Map originResourceQuotas = JSONUtils.getObject(oldAppConfig, CBConstants.PARAM_RESOURCE_QUOTAS); diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBConstants.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBConstants.java index 5b2e04a638..b774d61635 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBConstants.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBConstants.java @@ -39,6 +39,7 @@ public class CBConstants { public static final String PARAM_STATIC_CONTENT = "staticContent"; public static final String PARAM_RESOURCE_QUOTAS = "resourceQuotas"; public static final String PARAM_RESOURCE_MANAGER_ENABLED = "resourceManagerEnabled"; + public static final String PARAM_SHOW_READ_ONLY_CONN_INFO = "showReadOnlyConnectionInfo"; public static final String PARAM_AUTH_PROVIDERS = "authConfiguration"; public static final String PARAM_DB_CONFIGURATION = "database"; From f1ed8f15bdf2fc0e825447132b5e55bd671c0034 Mon Sep 17 00:00:00 2001 From: Ainur Date: Tue, 8 Nov 2022 15:17:28 +0100 Subject: [PATCH 02/21] CB-2861 do not show connection config in gql api --- .../cloudbeaver/model/WebConnectionInfo.java | 41 ++++++++++++++----- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionInfo.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionInfo.java index 2fc776a468..601ceefbad 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionInfo.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionInfo.java @@ -40,10 +40,7 @@ import org.jkiss.dbeaver.model.rm.RMProjectPermission; import org.jkiss.dbeaver.runtime.DBWorkbench; import org.jkiss.utils.CommonUtils; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; /** @@ -51,6 +48,7 @@ import java.util.stream.Collectors; */ public class WebConnectionInfo { + private static final String SECURED_VALUE = "********"; private final WebSession session; private final DBPDataSourceContainer dataSourceContainer; private WebServerError connectError; @@ -101,12 +99,18 @@ public class WebConnectionInfo { @Property public String getHost() { - return dataSourceContainer.getConnectionConfiguration().getHostName(); + if (canViewReadOnlyConnections()) { + return dataSourceContainer.getConnectionConfiguration().getHostName(); + } + return SECURED_VALUE; } @Property public String getPort() { - return dataSourceContainer.getConnectionConfiguration().getHostPort(); + if (canViewReadOnlyConnections()) { + return dataSourceContainer.getConnectionConfiguration().getHostPort(); + } + return SECURED_VALUE; } @Property @@ -116,17 +120,26 @@ public class WebConnectionInfo { @Property public String getDatabaseName() { - return dataSourceContainer.getConnectionConfiguration().getDatabaseName(); + if (canViewReadOnlyConnections()) { + return dataSourceContainer.getConnectionConfiguration().getDatabaseName(); + } + return SECURED_VALUE; } @Property public String getUrl() { - return dataSourceContainer.getConnectionConfiguration().getUrl(); + if (canViewReadOnlyConnections()) { + return dataSourceContainer.getConnectionConfiguration().getUrl(); + } + return SECURED_VALUE; } @Property public Map getProperties() { - return dataSourceContainer.getConnectionConfiguration().getProperties(); + if (canViewReadOnlyConnections()) { + return dataSourceContainer.getConnectionConfiguration().getProperties(); + } + return Collections.emptyMap(); } @Property @@ -303,8 +316,11 @@ public class WebConnectionInfo { @Property public List getNetworkHandlersConfig() { - return dataSourceContainer.getConnectionConfiguration().getHandlers().stream() - .map(WebNetworkHandlerConfig::new).collect(Collectors.toList()); + if (canViewReadOnlyConnections()) { + return dataSourceContainer.getConnectionConfiguration().getHandlers().stream() + .map(WebNetworkHandlerConfig::new).collect(Collectors.toList()); + } + return Collections.emptyList(); } @Property @@ -375,6 +391,9 @@ public class WebConnectionInfo { } private boolean canViewReadOnlyConnections() { + if (isCanEdit()) { + return true; + } BaseWebAppConfiguration appConfig = (BaseWebAppConfiguration) WebAppUtils.getWebApplication().getAppConfiguration(); return appConfig.isShowReadOnlyConnectionInfo(); From 7b2c42ef7c20e00ce016b98ef27153d32b7e9514 Mon Sep 17 00:00:00 2001 From: Ainur Date: Wed, 9 Nov 2022 17:27:47 +0100 Subject: [PATCH 03/21] CB-2149 add parameter to conf file --- config/sample-databases/DefaultConfiguration/cloudbeaver.conf | 1 + config/sample-databases/SQLiteConfiguration/cloudbeaver.conf | 1 + 2 files changed, 2 insertions(+) diff --git a/config/sample-databases/DefaultConfiguration/cloudbeaver.conf b/config/sample-databases/DefaultConfiguration/cloudbeaver.conf index 168e296f93..e4f986e684 100644 --- a/config/sample-databases/DefaultConfiguration/cloudbeaver.conf +++ b/config/sample-databases/DefaultConfiguration/cloudbeaver.conf @@ -38,6 +38,7 @@ anonymousAccessEnabled: true, anonymousUserRole: "user", supportsCustomConnections: false, + showReadOnlyConnectionInfo: false, forwardProxy: false, diff --git a/config/sample-databases/SQLiteConfiguration/cloudbeaver.conf b/config/sample-databases/SQLiteConfiguration/cloudbeaver.conf index a1f14be291..5dbc3c8d71 100644 --- a/config/sample-databases/SQLiteConfiguration/cloudbeaver.conf +++ b/config/sample-databases/SQLiteConfiguration/cloudbeaver.conf @@ -38,6 +38,7 @@ anonymousAccessEnabled: true, anonymousUserRole: "user", supportsCustomConnections: false, + showReadOnlyConnectionInfo: false, forwardProxy: false, From 10d2e7e4ffb1a604079848133c2919d66c28c3a2 Mon Sep 17 00:00:00 2001 From: Ainur Date: Thu, 10 Nov 2022 16:20:13 +0100 Subject: [PATCH 04/21] CB-2860 sort accessible projects fix --- .../io/cloudbeaver/model/rm/local/LocalResourceController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java index 1e6b103bef..31f7bab26a 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java @@ -154,6 +154,7 @@ public class LocalResourceController implements RMController { if (userProject != null) { projects.add(0, userProject); } + projects.sort(Comparator.comparing(RMProject::getDisplayName)); return projects.toArray(new RMProject[0]); } @@ -171,7 +172,6 @@ public class LocalResourceController implements RMController { RMProjectType.SHARED, true) ) .filter(Objects::nonNull) - .sorted(Comparator.comparing(RMProject::getDisplayName)) .collect(Collectors.toList()); } From 180b49828bb6b78934381b36ef49fcd622cdd6df Mon Sep 17 00:00:00 2001 From: naumov Date: Fri, 11 Nov 2022 17:33:11 +0400 Subject: [PATCH 05/21] CB-2337 add filtering to the users table --- .../Users/UserForm/UserForm.tsx | 1 + .../Users/UserForm/UserInfo.tsx | 2 +- .../UsersTable/Filters/UsersTableFilters.tsx | 115 ++++++++++ .../Filters/useUsersTableFilters.tsx | 73 ++++++ .../Users/UsersTable/UsersTable.tsx | 216 +++++++++--------- .../Users/UsersTable/useUsersTable.tsx | 115 ++++++++++ .../src/locales/en.ts | 6 + .../src/locales/it.ts | 6 + .../src/locales/ru.ts | 6 + .../src/locales/zh.ts | 6 + 10 files changed, 442 insertions(+), 104 deletions(-) create mode 100644 webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/Filters/UsersTableFilters.tsx create mode 100644 webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/Filters/useUsersTableFilters.tsx create mode 100644 webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/useUsersTable.tsx diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UserForm/UserForm.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UserForm/UserForm.tsx index c5a88f7cd8..3a15ac737e 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UserForm/UserForm.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UserForm/UserForm.tsx @@ -62,6 +62,7 @@ const topBarStyles = css` position: relative; display: flex; padding-top: 16px; + margin-bottom: 24px; &:before { content: ''; diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UserForm/UserInfo.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UserForm/UserInfo.tsx index 7dd1f357d4..68b2af96bf 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UserForm/UserInfo.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UserForm/UserInfo.tsx @@ -37,7 +37,7 @@ export const UserInfo: TabContainerPanelComponent = observer(fun ); return styled(style)( - + {translate('authentication_user_credentials')} diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/Filters/UsersTableFilters.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/Filters/UsersTableFilters.tsx new file mode 100644 index 0000000000..1ccb4e4b84 --- /dev/null +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/Filters/UsersTableFilters.tsx @@ -0,0 +1,115 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2022 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 { useState } from 'react'; +import styled, { css, use } from 'reshadow'; + +import { AuthRolesResource } from '@cloudbeaver/core-authentication'; +import { BASE_CONTAINERS_STYLES, Combobox, Filter, Group, IconOrImage, useDataResource, useStyles, useTranslate } from '@cloudbeaver/core-blocks'; + +import { IUserFilters, USER_ROLE_ALL, USER_STATUSES } from './useUsersTableFilters'; + +const styles = css` + filter-container { + display: flex; + gap: 12px; + } + + Filter { + flex: 1; + } + + actions { + composes: theme-form-element-radius theme-background-surface theme-text-on-surface theme theme-border-color-background from global; + box-sizing: border-box; + border: 2px solid; + height: 32px; + } + + button { + position: relative; + box-sizing: border-box; + background: inherit; + cursor: pointer; + width: 28px; + height: 100%; + padding: 4px; + + &:hover { + opacity: 0.8; + } + + &[|active] { + background: var(--theme-secondary); + } + } + + IconOrImage { + width: 100%; + height: 100%; + } +`; + +interface Props { + filters: IUserFilters; +} + +export const UsersTableFilters = observer(function UsersTableFilters({ filters }) { + const translate = useTranslate(); + const style = useStyles(BASE_CONTAINERS_STYLES, styles); + const authRolesResource = useDataResource(UsersTableFilters, AuthRolesResource, undefined); + + const [open, setOpen] = useState(false); + + return styled(style)( + + + + + + + + + {open && ( + + translate(value.label)} + keySelector={value => value.value} + keepSize + onSelect={filters.setStatus} + > + {translate('authentication_user_status')} + + {!!authRolesResource.data.length && ( + + {translate('authentication_user_role')} + + )} + + )} + + ); +}); \ No newline at end of file diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/Filters/useUsersTableFilters.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/Filters/useUsersTableFilters.tsx new file mode 100644 index 0000000000..713835d43b --- /dev/null +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/Filters/useUsersTableFilters.tsx @@ -0,0 +1,73 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2022 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 { action, observable } from 'mobx'; + +import { useObservableRef } from '@cloudbeaver/core-blocks'; +import type { TLocalizationToken } from '@cloudbeaver/core-localization'; + +export enum EUserStatus { + ENABLED = 'ENABLED', + DISABLED = 'DISABLED', + ALL = 'ALL' +} + +interface IStatus { + label: TLocalizationToken; + value: EUserStatus; +} + +export const USER_ROLE_ALL = 'ALL'; + +export const USER_STATUSES: IStatus[] = [ + { + label: 'authentication_administration_users_filters_status_enabled', + value: EUserStatus.ENABLED, + }, + { + label: 'authentication_administration_users_filters_status_disabled', + value: EUserStatus.DISABLED, + }, + { + label: 'authentication_administration_users_filters_status_all', + value: EUserStatus.ALL, + }, +]; + +export interface IUserFilters { + search: string; + role: string; + status: EUserStatus; + setSearch: (value: string) => void; + setRole: (role: string) => void; + setStatus: (status: EUserStatus) => void; +} + +export function useUsersTableFilters() { + return useObservableRef(() => ({ + search: '', + role: USER_ROLE_ALL, + status: EUserStatus.ENABLED, + setSearch(value: string) { + this.search = value; + }, + setRole(role: string) { + this.role = role; + }, + setStatus(status: EUserStatus) { + this.status = status; + }, + }), { + search: observable.ref, + role: observable.ref, + status: observable.ref, + setSearch: action.bound, + setRole: action.bound, + setStatus: action.bound, + }, false); +} \ No newline at end of file diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/UsersTable.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/UsersTable.tsx index 9a2f65d48b..89cdca4194 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/UsersTable.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/UsersTable.tsx @@ -7,36 +7,23 @@ */ import { observer } from 'mobx-react-lite'; -import styled, { css, use } from 'reshadow'; +import styled, { css } from 'reshadow'; import { ADMINISTRATION_TOOLS_PANEL_STYLES, IAdministrationItemSubItem } from '@cloudbeaver/core-administration'; -import { AdminUser, AuthRolesResource, UsersResource } from '@cloudbeaver/core-authentication'; -import { Table, TableHeader, TableColumnHeader, TableBody, TableSelect, useMapResource, ToolsAction, ToolsPanel, Loader, BASE_LAYOUT_GRID_STYLES, useTranslate, useStyles, useDataResource } from '@cloudbeaver/core-blocks'; -import { useController, useService } from '@cloudbeaver/core-di'; +import { AuthProvidersResource, AuthRolesResource, AUTH_PROVIDER_LOCAL_ID, UsersResource } from '@cloudbeaver/core-authentication'; +import { + Table, TableHeader, TableColumnHeader, TableBody, useMapResource, ToolsAction, + ToolsPanel, Loader, useTranslate, useStyles, useDataResource, + BASE_CONTAINERS_STYLES, ColoredContainer, Container, Group +} from '@cloudbeaver/core-blocks'; +import { useService } from '@cloudbeaver/core-di'; import { CachedMapAllKey } from '@cloudbeaver/core-sdk'; -import { filterUndefined } from '@cloudbeaver/core-utils'; import { CreateUser } from './CreateUser'; import { CreateUserService } from './CreateUserService'; +import { UsersTableFilters } from './Filters/UsersTableFilters'; import { User } from './User'; -import { UsersTableController } from './UsersTableController'; - -const layoutStyles = css` - layout-grid { - width: 100%; - overflow: auto; - } - - layout-grid-inner { - min-height: 100%; - } - - layout-grid-cell { - composes: theme-background-surface theme-text-on-surface theme-border-color-background from global; - position: relative; - border: solid 1px; - } - `; +import { useUsersTable } from './useUsersTable'; const loaderStyle = css` ExceptionMessage { @@ -49,12 +36,36 @@ const styles = css` width: 100%; } + TableHeader { + background: var(--theme-surface); + position: sticky; + top: 0; + z-index: 1; + } + + content { + display: flex; + flex-direction: column; + overflow: auto; + gap: 24px; + } + + Group { + padding: 0; + } + + table-container { + overflow: auto; + height: 100%; + } + [|table] { min-height: 140px; /* loader overlay size */ } ToolsPanel { border-bottom: none; + border-radius: inherit; } `; @@ -65,53 +76,47 @@ interface Props { export const UsersTable = observer(function UsersTable({ sub, param }) { const translate = useTranslate(); - const style = useStyles(BASE_LAYOUT_GRID_STYLES, styles, ADMINISTRATION_TOOLS_PANEL_STYLES, layoutStyles); - const createUserService = useService(CreateUserService); - const controller = useController(UsersTableController); - const usersResource = useMapResource(UsersTable, UsersResource, CachedMapAllKey); - const authRoles = useDataResource(UsersTable, AuthRolesResource, undefined); - const isLocalProviderAvailable = controller.isLocalProviderAvailable; - const users = usersResource.resource.values - .filter(filterUndefined) - .sort((a, b) => { - if (usersResource.resource.isNew(a.userId) === usersResource.resource.isNew(b.userId)) { - return a.userId.localeCompare(b.userId); - } - if (usersResource.resource.isNew(a.userId)) { - return -1; - } - return 1; - }); + const style = useStyles(styles, ADMINISTRATION_TOOLS_PANEL_STYLES, BASE_CONTAINERS_STYLES); + const createUserService = useService(CreateUserService); + + const usersResource = useMapResource(UsersTable, UsersResource, CachedMapAllKey); + const authProvidersResource = useMapResource(UsersTable, AuthProvidersResource, CachedMapAllKey); + const authRolesResource = useDataResource(UsersTable, AuthRolesResource, undefined); + + const table = useUsersTable(usersResource.resource); + + const isLocalProviderAvailable = authProvidersResource.resource.has(AUTH_PROVIDER_LOCAL_ID); const create = param === 'create'; - const keys = users.map(user => user.userId); - const displayAuthRole = authRoles.data.length > 0; + const keys = table.users.map(user => user.userId); + const displayAuthRole = authRolesResource.data.length > 0; return styled(style)( - - - - - {isLocalProviderAvailable && ( + + + + + + {isLocalProviderAvailable && ( + + {translate('ui_add')} + + )} - {translate('ui_add')} + {translate('ui_refresh')} - )} - - {translate('ui_refresh')} - - {/* {isLocalProviderAvailable && ( + {/* {isLocalProviderAvailable && ( (function UsersTable({ sub, param }) { {translate('ui_delete')} )} */} - - + + + + - {create && createUserService.user && ( - - - - )} - - - - - {/* {isLocalProviderAvailable && ( + + {create && createUserService.user && ( + + + + )} + + + +
+ + {/* {isLocalProviderAvailable && ( )} */} - - {translate('authentication_user_name')} - {displayAuthRole && ( - {translate('authentication_user_role')} - )} - {translate('authentication_user_team')} - {translate('authentication_user_enabled')} - - - - {users.map(user => ( - - ))} - -
-
-
- - + + {translate('authentication_user_name')} + {displayAuthRole && ( + {translate('authentication_user_role')} + )} + {translate('authentication_user_team')} + {translate('authentication_user_enabled')} + + + + {table.users.map(user => ( + + ))} + + + + + + +
); }); diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/useUsersTable.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/useUsersTable.tsx new file mode 100644 index 0000000000..abe09a9c63 --- /dev/null +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/useUsersTable.tsx @@ -0,0 +1,115 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2022 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 { action, computed, observable } from 'mobx'; + +import type { AdminUser, UsersResource } from '@cloudbeaver/core-authentication'; +import { TableState, useObservableRef, useTranslate } from '@cloudbeaver/core-blocks'; +import { useService } from '@cloudbeaver/core-di'; +import { CommonDialogService, ConfirmationDialogDelete, DialogueStateResult } from '@cloudbeaver/core-dialogs'; +import { NotificationService } from '@cloudbeaver/core-events'; +import { resourceKeyList } from '@cloudbeaver/core-sdk'; + +import { EUserStatus, IUserFilters, USER_ROLE_ALL, useUsersTableFilters } from './Filters/useUsersTableFilters'; + +interface State { + usersResource: UsersResource; + loading: boolean; + state: TableState; + filters: IUserFilters; + users: AdminUser[]; + update: () => Promise; + delete: () => Promise; +} + +export function useUsersTable(usersResource: UsersResource) { + const translate = useTranslate(); + const notificationService = useService(NotificationService); + const commonDialogService = useService(CommonDialogService); + + const filters = useUsersTableFilters(); + + return useObservableRef(() => ({ + usersResource, + filters, + loading: false, + state: new TableState(), + get users() { + return this.usersResource.values + .filter(user => { + const matchSearch = user.userId.toLowerCase().includes(this.filters.search.trim().toLowerCase()); + const matchStatus = this.filters.status === EUserStatus.ALL + || (this.filters.status === EUserStatus.ENABLED ? user.enabled : !user.enabled); + const matchRole = this.filters.role === USER_ROLE_ALL || this.filters.role === user.authRole; + + return matchSearch && matchStatus && matchRole; + }) + .sort((a, b) => { + if (this.usersResource.isNew(a.userId) === this.usersResource.isNew(b.userId)) { + return a.userId.localeCompare(b.userId); + } + if (this.usersResource.isNew(a.userId)) { + return -1; + } + return 1; + }); + }, + async update() { + try { + await this.usersResource.refreshAll(); + notificationService.logSuccess({ title: 'authentication_administration_tools_refresh_success' }); + } catch (exception: any) { + notificationService.logException(exception, 'authentication_administration_tools_refresh_fail'); + } + }, + async delete() { + if (this.loading) { + return; + } + + const deletionList = this.state.selectedList.filter(([_, value]) => value).map(([userId]) => userId); + if (deletionList.length === 0) { + return; + } + + const userNames = deletionList.map(name => `"${name}"`).join(', '); + const message = `${translate('authentication_administration_users_delete_confirmation')}${userNames}. ${translate('ui_are_you_sure')}`; + + const result = await commonDialogService.open(ConfirmationDialogDelete, { + title: 'ui_data_delete_confirmation', + message, + confirmActionText: 'ui_delete', + }); + + if (result === DialogueStateResult.Rejected) { + return; + } + + this.loading = true; + + try { + await this.usersResource.delete(resourceKeyList(deletionList)); + this.state.unselect(); + + for (const id of deletionList) { + this.state.unexpand(id); + } + } catch (exception: any) { + notificationService.logException(exception, 'authentication_administration_user_delete_fail'); + + } finally { + this.loading = false; + } + }, + }), { + loading: observable.ref, + users: computed, + update: action.bound, + delete: action.bound, + }, { usersResource, filters }); +} \ No newline at end of file diff --git a/webapp/packages/plugin-authentication-administration/src/locales/en.ts b/webapp/packages/plugin-authentication-administration/src/locales/en.ts index 93671c7e15..161e83ef8b 100644 --- a/webapp/packages/plugin-authentication-administration/src/locales/en.ts +++ b/webapp/packages/plugin-authentication-administration/src/locales/en.ts @@ -18,11 +18,17 @@ export default [ ['authentication_administration_tools_refresh_tooltip', 'Refresh user list'], ['authentication_administration_tools_delete_tooltip', 'Delete selected users'], ['authentication_administration_tools_refresh_success', 'User list was refreshed'], + ['authentication_administration_tools_refresh_fail', 'Users update failed'], + ['authentication_administration_user_delete_fail', 'Error deleting user'], ['authentication_administration_user_update_failed', 'Error saving user'], ['authentication_administration_user_updated', 'User updated'], ['authentication_administration_user_created', 'User was created successfully'], ['authentication_administration_user_create_failed', 'Error creating new user'], ['authentication_administration_users_delete_confirmation', "You're going to delete these users: "], + ['authentication_administration_users_filters_search_placeholder', 'Search for the user name...'], + ['authentication_administration_users_filters_status_enabled', 'ENABLED'], + ['authentication_administration_users_filters_status_disabled', 'DISABLED'], + ['authentication_administration_users_filters_status_all', 'ALL'], ['administration_configuration_wizard_configuration_admin', 'Administrator Credentials'], ['administration_configuration_wizard_configuration_admin_name', 'Login'], diff --git a/webapp/packages/plugin-authentication-administration/src/locales/it.ts b/webapp/packages/plugin-authentication-administration/src/locales/it.ts index 7948205273..52c9cc25a0 100644 --- a/webapp/packages/plugin-authentication-administration/src/locales/it.ts +++ b/webapp/packages/plugin-authentication-administration/src/locales/it.ts @@ -15,10 +15,16 @@ export default [ ['authentication_administration_tools_refresh_tooltip', 'Aggiorna la lista utenti'], ['authentication_administration_tools_delete_tooltip', 'Elimina gli utenti selezionati'], ['authentication_administration_tools_refresh_success', 'Lista degli utenti ricaricata'], + ['authentication_administration_tools_refresh_fail', 'Users update failed'], + ['authentication_administration_user_delete_fail', 'Error deleting user'], ['authentication_administration_user_update_failed', 'Errore al salvataggio utente'], ['authentication_administration_user_updated', 'Utente aggiornato'], ['authentication_administration_user_created', 'Utente creato con successo'], ['authentication_administration_user_create_failed', 'Errore di creazione nuovo utente'], + ['authentication_administration_users_filters_search_placeholder', 'Search for the user name...'], + ['authentication_administration_users_filters_status_enabled', 'ENABLED'], + ['authentication_administration_users_filters_status_disabled', 'DISABLED'], + ['authentication_administration_users_filters_status_all', 'ALL'], ['administration_teams_team_info_created', 'Team created'], ['administration_teams_team_info_updated', 'Team updated'], diff --git a/webapp/packages/plugin-authentication-administration/src/locales/ru.ts b/webapp/packages/plugin-authentication-administration/src/locales/ru.ts index 966b36cbc2..e5828b9155 100644 --- a/webapp/packages/plugin-authentication-administration/src/locales/ru.ts +++ b/webapp/packages/plugin-authentication-administration/src/locales/ru.ts @@ -18,11 +18,17 @@ export default [ ['authentication_administration_tools_refresh_tooltip', 'Обновить список пользователей'], ['authentication_administration_tools_delete_tooltip', 'Удалить выбранных пользователей'], ['authentication_administration_tools_refresh_success', 'Список пользователей обновлен'], + ['authentication_administration_tools_refresh_fail', 'Не удалось обновить список пользователей'], + ['authentication_administration_user_delete_fail', 'Не удалось удалить пользователя'], ['authentication_administration_user_update_failed', 'Не удалось обновить пользователя'], ['authentication_administration_user_updated', 'Пользователь обновлен'], ['authentication_administration_user_created', 'Пользователь создан успешно'], ['authentication_administration_user_create_failed', 'Не удалось создать пользователя'], ['authentication_administration_users_delete_confirmation', 'Вы собираетесь удалить следующих пользователей: '], + ['authentication_administration_users_filters_search_placeholder', 'Поиск по имени пользователя...'], + ['authentication_administration_users_filters_status_enabled', 'ВКЛЮЧЕН'], + ['authentication_administration_users_filters_status_disabled', 'ВЫКЛЮЧЕН'], + ['authentication_administration_users_filters_status_all', 'ВСЕ'], ['administration_configuration_wizard_configuration_admin', 'Учетные данные администратора'], ['administration_configuration_wizard_configuration_admin_name', 'Логин'], diff --git a/webapp/packages/plugin-authentication-administration/src/locales/zh.ts b/webapp/packages/plugin-authentication-administration/src/locales/zh.ts index 773be62e07..8dd5eed4da 100644 --- a/webapp/packages/plugin-authentication-administration/src/locales/zh.ts +++ b/webapp/packages/plugin-authentication-administration/src/locales/zh.ts @@ -17,11 +17,17 @@ export default [ ['authentication_administration_tools_refresh_tooltip', '刷新用户列表'], ['authentication_administration_tools_delete_tooltip', '删除选中的用户'], ['authentication_administration_tools_refresh_success', '用户列表已刷新'], + ['authentication_administration_tools_refresh_fail', 'Users update failed'], + ['authentication_administration_user_delete_fail', 'Error deleting user'], ['authentication_administration_user_update_failed', '保存用户出错'], ['authentication_administration_user_updated', '用户信息已更新'], ['authentication_administration_user_created', '创建用户成功'], ['authentication_administration_user_create_failed', '创建新用户出错'], ['authentication_administration_users_delete_confirmation', '您将删除这些用户:'], + ['authentication_administration_users_filters_search_placeholder', 'Search for the user name...'], + ['authentication_administration_users_filters_status_enabled', 'ENABLED'], + ['authentication_administration_users_filters_status_disabled', 'DISABLED'], + ['authentication_administration_users_filters_status_all', 'ALL'], ['administration_configuration_wizard_configuration_admin', '管理员凭据'], ['administration_configuration_wizard_configuration_admin_name', '登录'], From a61a41534a9176cab9b730a0817b0ff084250c59 Mon Sep 17 00:00:00 2001 From: naumov Date: Fri, 11 Nov 2022 20:56:40 +0400 Subject: [PATCH 06/21] CB-2337 code style fixes --- .../Users/UsersTable/UsersTable.tsx | 28 +++++++++---------- .../Users/UsersTable/useUsersTable.tsx | 6 ++-- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/UsersTable.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/UsersTable.tsx index 89cdca4194..86683a7950 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/UsersTable.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/UsersTable.tsx @@ -32,15 +32,9 @@ const loaderStyle = css` `; const styles = css` - Table { - width: 100%; - } - - TableHeader { - background: var(--theme-surface); - position: sticky; - top: 0; - z-index: 1; + ToolsPanel { + border-bottom: none; + border-radius: inherit; } content { @@ -53,6 +47,17 @@ const styles = css` Group { padding: 0; } + + Table { + width: 100%; + } + + TableHeader { + background: var(--theme-surface); + position: sticky; + top: 0; + z-index: 1; + } table-container { overflow: auto; @@ -62,11 +67,6 @@ const styles = css` [|table] { min-height: 140px; /* loader overlay size */ } - - ToolsPanel { - border-bottom: none; - border-radius: inherit; - } `; interface Props { diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/useUsersTable.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/useUsersTable.tsx index abe09a9c63..51c1604c56 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/useUsersTable.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/useUsersTable.tsx @@ -18,7 +18,6 @@ import { resourceKeyList } from '@cloudbeaver/core-sdk'; import { EUserStatus, IUserFilters, USER_ROLE_ALL, useUsersTableFilters } from './Filters/useUsersTableFilters'; interface State { - usersResource: UsersResource; loading: boolean; state: TableState; filters: IUserFilters; @@ -34,8 +33,7 @@ export function useUsersTable(usersResource: UsersResource) { const filters = useUsersTableFilters(); - return useObservableRef(() => ({ - usersResource, + const state: State = useObservableRef(() => ({ filters, loading: false, state: new TableState(), @@ -112,4 +110,6 @@ export function useUsersTable(usersResource: UsersResource) { update: action.bound, delete: action.bound, }, { usersResource, filters }); + + return state; } \ No newline at end of file From 4163be160b80b8838f31e4b120756caf4307188e Mon Sep 17 00:00:00 2001 From: naumov Date: Fri, 11 Nov 2022 21:16:12 +0400 Subject: [PATCH 07/21] CB-2337 remove unused code --- .../src/Administration/Users/UsersTable/UsersTable.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/UsersTable.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/UsersTable.tsx index 86683a7950..ba4da8ced7 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/UsersTable.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/UsersTable.tsx @@ -63,10 +63,6 @@ const styles = css` overflow: auto; height: 100%; } - - [|table] { - min-height: 140px; /* loader overlay size */ - } `; interface Props { From 4d175e27a202c01ff31dd3001cdcd1cfdc79ce76 Mon Sep 17 00:00:00 2001 From: naumov Date: Fri, 11 Nov 2022 21:17:59 +0400 Subject: [PATCH 08/21] CB-2337 remove controller --- .../Users/UsersTable/UsersTableController.ts | 133 ------------------ 1 file changed, 133 deletions(-) delete mode 100644 webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/UsersTableController.ts diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/UsersTableController.ts b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/UsersTableController.ts deleted file mode 100644 index cc9716e039..0000000000 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/UsersTableController.ts +++ /dev/null @@ -1,133 +0,0 @@ -/* - * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2022 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 { observable, computed, makeObservable } from 'mobx'; - -import { AdminUser, AuthProvidersResource, AUTH_PROVIDER_LOCAL_ID, UsersResource } from '@cloudbeaver/core-authentication'; -import { injectable, IInitializableController } from '@cloudbeaver/core-di'; -import { CommonDialogService, ConfirmationDialogDelete, DialogueStateResult } from '@cloudbeaver/core-dialogs'; -import { NotificationService } from '@cloudbeaver/core-events'; -import { LocalizationService } from '@cloudbeaver/core-localization'; -import { ErrorDetailsDialog } from '@cloudbeaver/core-notifications'; -import { GQLErrorCatcher, resourceKeyList } from '@cloudbeaver/core-sdk'; - -@injectable() -export class UsersTableController implements IInitializableController { - isDeleting = false; - readonly selectedItems = observable(new Map()); - readonly expandedItems = observable(new Map()); - readonly error = new GQLErrorCatcher(); - - get users(): AdminUser[] { - return Array.from(this.usersResource.data.values()) - .sort((a, b) => { - if (this.usersResource.isNew(a.userId) === this.usersResource.isNew(b.userId)) { - return a.userId.localeCompare(b.userId); - } - if (this.usersResource.isNew(a.userId)) { - return -1; - } - return 1; - }); - } - - get isProvidersLoading(): boolean { - return this.authProvidersResource.isLoading(); - } - - get isLocalProviderAvailable(): boolean { - return this.authProvidersResource.has(AUTH_PROVIDER_LOCAL_ID); - } - - get isLoading(): boolean { - return this.usersResource.isLoading() || this.isDeleting; - } - - get itemsSelected(): boolean { - return Array.from(this.selectedItems.values()).some(v => v); - } - - constructor( - private notificationService: NotificationService, - private authProvidersResource: AuthProvidersResource, - private usersResource: UsersResource, - private commonDialogService: CommonDialogService, - private localizationService: LocalizationService - ) { - makeObservable(this, { - isDeleting: observable, - users: computed, - itemsSelected: computed, - }); - } - - init(): void { - this.authProvidersResource.loadAll(); - } - - update = async () => { - try { - await this.usersResource.refreshAll(); - this.notificationService.logSuccess({ title: 'authentication_administration_tools_refresh_success' }); - } catch (exception: any) { - if (!this.error.catch(exception)) { - this.notificationService.logException(exception, 'Users update failed'); - } - } - }; - - delete = async () => { - if (this.isDeleting) { - return; - } - - const deletionList = Array - .from(this.selectedItems) - .filter(([_, value]) => value) - .map(([userId]) => userId); - if (deletionList.length === 0) { - return; - } - - const userNames = deletionList.map(name => `"${name}"`).join(', '); - const message = `${this.localizationService.translate('authentication_administration_users_delete_confirmation')}${userNames}. ${this.localizationService.translate('ui_are_you_sure')}`; - - const result = await this.commonDialogService.open(ConfirmationDialogDelete, { - title: 'ui_data_delete_confirmation', - message, - confirmActionText: 'ui_delete', - }); - - if (result === DialogueStateResult.Rejected) { - return; - } - - this.isDeleting = true; - - try { - await this.usersResource.delete(resourceKeyList(deletionList)); - this.selectedItems.clear(); - - for (const id of deletionList) { - this.expandedItems.delete(id); - } - } catch (exception: any) { - if (!this.error.catch(exception)) { - this.notificationService.logException(exception, 'User delete failed'); - } - } finally { - this.isDeleting = false; - } - }; - - showDetails = () => { - if (this.error.exception) { - this.commonDialogService.open(ErrorDetailsDialog, this.error.exception); - } - }; -} From 1385267befdd4815399ae6526cc31a99e564a89e Mon Sep 17 00:00:00 2001 From: naumov Date: Mon, 14 Nov 2022 22:39:38 +0300 Subject: [PATCH 09/21] CB-2337 remove filters logic from table hook --- .../core-utils/src/isArrayEquals.test.ts | 26 ++++++++++++++++++- .../packages/core-utils/src/isArraysEqual.ts | 12 +++++++-- .../Filters/useUsersTableFilters.tsx | 25 +++++++++++++++--- .../Users/UsersTable/UsersTable.tsx | 8 +++--- .../Users/UsersTable/useUsersTable.tsx | 20 +++----------- 5 files changed, 65 insertions(+), 26 deletions(-) diff --git a/webapp/packages/core-utils/src/isArrayEquals.test.ts b/webapp/packages/core-utils/src/isArrayEquals.test.ts index 32c4613b37..ba35107347 100644 --- a/webapp/packages/core-utils/src/isArrayEquals.test.ts +++ b/webapp/packages/core-utils/src/isArrayEquals.test.ts @@ -30,6 +30,30 @@ describe('Is array equals', () => { }); test('should use isEqual argument if passed', () => { - expect(isArraysEqual([{ a:1 }], [{ a: 1 }], (a, b) => a.a === b.a)).toBe(true); + expect(isArraysEqual([{ a: 1 }], [{ a: 1 }], (a, b) => a.a === b.a)).toBe(true); + }); + + test('should return "true" when arrays are the same and "order" argument is "true"', () => { + expect(isArraysEqual([1, '1', true], [1, '1', true], undefined, true)).toBe(true); + }); + + test('should return "false" when arrays are not the same and "order" argument is "true"', () => { + expect(isArraysEqual([1, '2', true], [2, '1', true], undefined, true)).toBe(false); + }); + + test('should return "false" when arrays have the same elements but elements order is different and "order" argument is "true"', () => { + expect(isArraysEqual(['1', 1, true], [1, '1', true], undefined, true)).toBe(false); + }); + + test('should return "false" when arrays length is different and the first array is larger and "order" argument is "true"', () => { + expect(isArraysEqual([1, true, '1'], [1, true], undefined, true)).toBe(false); + }); + + test('should return "false" when arrays length is different and the second array is larger and "order" argument is "true"', () => { + expect(isArraysEqual([1, true], [1, true, '1'], undefined, true)).toBe(false); + }); + + test('should use isEqual argument if passed and "order" argument is "true"', () => { + expect(isArraysEqual([{ a: 1 }], [{ a: 1 }], (a, b) => a.a === b.a, true)).toBe(true); }); }); \ No newline at end of file diff --git a/webapp/packages/core-utils/src/isArraysEqual.ts b/webapp/packages/core-utils/src/isArraysEqual.ts index 571ed8cc74..a35aa7a72a 100644 --- a/webapp/packages/core-utils/src/isArraysEqual.ts +++ b/webapp/packages/core-utils/src/isArraysEqual.ts @@ -11,11 +11,19 @@ const isObjectEqual = (a: T, b: T) => a === b; export function isArraysEqual( first: T[], second: T[], - isEqual: (a: T, b: T) => boolean = isObjectEqual + isEqual: (a: T, b: T) => boolean = isObjectEqual, + order?: boolean ): boolean { if (first.length !== second.length) { return false; } - return !first.some(b => !second.some(a => isEqual(a, b))); + + return !first.some((b, index) => { + if (order) { + return !isEqual(second[index], b); + } + + return !second.some(a => isEqual(a, b)); + }); } diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/Filters/useUsersTableFilters.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/Filters/useUsersTableFilters.tsx index 713835d43b..b471d3d58b 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/Filters/useUsersTableFilters.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/Filters/useUsersTableFilters.tsx @@ -6,10 +6,12 @@ * you may not use this file except in compliance with the License. */ -import { action, observable } from 'mobx'; +import { action, computed, observable } from 'mobx'; import { useObservableRef } from '@cloudbeaver/core-blocks'; import type { TLocalizationToken } from '@cloudbeaver/core-localization'; +import type { AdminUser } from '@cloudbeaver/core-authentication'; +import { isArraysEqual } from '@cloudbeaver/core-utils'; export enum EUserStatus { ENABLED = 'ENABLED', @@ -43,16 +45,27 @@ export interface IUserFilters { search: string; role: string; status: EUserStatus; + filteredUsers: AdminUser[]; setSearch: (value: string) => void; setRole: (role: string) => void; setStatus: (status: EUserStatus) => void; } -export function useUsersTableFilters() { - return useObservableRef(() => ({ +export function useUsersTableFilters(users: AdminUser[]) { + const filters: IUserFilters = useObservableRef(() => ({ search: '', role: USER_ROLE_ALL, status: EUserStatus.ENABLED, + get filteredUsers() { + return this.users.filter(user => { + const matchSearch = user.userId.toLowerCase().includes(this.search.trim().toLowerCase()); + const matchStatus = this.status === EUserStatus.ALL + || (this.status === EUserStatus.ENABLED ? user.enabled : !user.enabled); + const matchRole = this.role === USER_ROLE_ALL || this.role === user.authRole; + + return matchSearch && matchStatus && matchRole; + }) + }, setSearch(value: string) { this.search = value; }, @@ -66,8 +79,12 @@ export function useUsersTableFilters() { search: observable.ref, role: observable.ref, status: observable.ref, + users: observable.ref, + filteredUsers: computed({ equals: (first, second) => isArraysEqual(first, second, undefined, true) }), setSearch: action.bound, setRole: action.bound, setStatus: action.bound, - }, false); + }, { users }); + + return filters; } \ No newline at end of file diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/UsersTable.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/UsersTable.tsx index ba4da8ced7..e6d65de2ab 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/UsersTable.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UsersTable/UsersTable.tsx @@ -22,6 +22,7 @@ import { CachedMapAllKey } from '@cloudbeaver/core-sdk'; import { CreateUser } from './CreateUser'; import { CreateUserService } from './CreateUserService'; import { UsersTableFilters } from './Filters/UsersTableFilters'; +import { useUsersTableFilters } from './Filters/useUsersTableFilters'; import { User } from './User'; import { useUsersTable } from './useUsersTable'; @@ -81,11 +82,12 @@ export const UsersTable = observer(function UsersTable({ sub, param }) { const authRolesResource = useDataResource(UsersTable, AuthRolesResource, undefined); const table = useUsersTable(usersResource.resource); + const filters = useUsersTableFilters(table.users); const isLocalProviderAvailable = authProvidersResource.resource.has(AUTH_PROVIDER_LOCAL_ID); const create = param === 'create'; - const keys = table.users.map(user => user.userId); const displayAuthRole = authRolesResource.data.length > 0; + const keys = filters.filteredUsers.map(user => user.userId); return styled(style)( @@ -125,7 +127,7 @@ export const UsersTable = observer(function UsersTable({ sub, param }) { )} */} - + @@ -159,7 +161,7 @@ export const UsersTable = observer(function UsersTable({ sub, param }) { - {table.users.map(user => ( + {filters.filteredUsers.map(user => ( Promise; delete: () => Promise; @@ -31,22 +29,12 @@ export function useUsersTable(usersResource: UsersResource) { const notificationService = useService(NotificationService); const commonDialogService = useService(CommonDialogService); - const filters = useUsersTableFilters(); - const state: State = useObservableRef(() => ({ - filters, loading: false, state: new TableState(), get users() { return this.usersResource.values - .filter(user => { - const matchSearch = user.userId.toLowerCase().includes(this.filters.search.trim().toLowerCase()); - const matchStatus = this.filters.status === EUserStatus.ALL - || (this.filters.status === EUserStatus.ENABLED ? user.enabled : !user.enabled); - const matchRole = this.filters.role === USER_ROLE_ALL || this.filters.role === user.authRole; - - return matchSearch && matchStatus && matchRole; - }) + .slice() .sort((a, b) => { if (this.usersResource.isNew(a.userId) === this.usersResource.isNew(b.userId)) { return a.userId.localeCompare(b.userId); @@ -106,10 +94,10 @@ export function useUsersTable(usersResource: UsersResource) { }, }), { loading: observable.ref, - users: computed, + users: computed({ equals: (first, second) => isArraysEqual(first, second, undefined, true) }), update: action.bound, delete: action.bound, - }, { usersResource, filters }); + }, { usersResource }); return state; } \ No newline at end of file From d7726de3a0c4c0eff031e58b3081861eb31527d6 Mon Sep 17 00:00:00 2001 From: Alexander Skoblikov Date: Wed, 16 Nov 2022 14:11:06 +0300 Subject: [PATCH 10/21] CB-2759 aws configuration service (#1279) --- .../service/DBWServiceServerConfigurator.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) rename server/bundles/{io.cloudbeaver.server => io.cloudbeaver.model}/src/io/cloudbeaver/service/DBWServiceServerConfigurator.java (75%) diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/DBWServiceServerConfigurator.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/service/DBWServiceServerConfigurator.java similarity index 75% rename from server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/DBWServiceServerConfigurator.java rename to server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/service/DBWServiceServerConfigurator.java index 72a8f87879..3eb0ee5d74 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/DBWServiceServerConfigurator.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/service/DBWServiceServerConfigurator.java @@ -16,8 +16,8 @@ */ package io.cloudbeaver.service; +import io.cloudbeaver.model.app.WebAppConfiguration; import io.cloudbeaver.model.session.WebSession; -import io.cloudbeaver.server.CBAppConfig; import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; @@ -28,8 +28,12 @@ import org.jkiss.dbeaver.model.app.DBPApplication; */ public interface DBWServiceServerConfigurator extends DBWServiceBinding { - void configureServer(@NotNull DBPApplication application, @Nullable WebSession session, @NotNull CBAppConfig appConfig) throws DBException; + void configureServer( + @NotNull DBPApplication application, + @Nullable WebSession session, + @NotNull WebAppConfiguration appConfig + ) throws DBException; - void reloadConfiguration(@NotNull CBAppConfig appConfig) throws DBException; + void reloadConfiguration(@NotNull WebAppConfiguration appConfig) throws DBException; } From 89f8313da0971cc5ca6e1b5158ef6c2a6967b45d Mon Sep 17 00:00:00 2001 From: Alexander Skoblikov Date: Wed, 16 Nov 2022 17:01:37 +0300 Subject: [PATCH 11/21] CB-2764 empty default team perms for TE (#1281) --- .../security/CBEmbeddedSecurityController.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 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 28d736399b..3120c8b850 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 @@ -44,11 +44,11 @@ import org.jkiss.dbeaver.model.impl.jdbc.JDBCUtils; import org.jkiss.dbeaver.model.impl.jdbc.exec.JDBCTransaction; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.model.runtime.LoggingProgressMonitor; +import org.jkiss.dbeaver.model.security.*; import org.jkiss.dbeaver.model.security.exception.SMAccessTokenExpiredException; import org.jkiss.dbeaver.model.security.exception.SMException; import org.jkiss.dbeaver.model.security.exception.SMRefreshTokenExpiredException; import org.jkiss.dbeaver.model.security.user.*; -import org.jkiss.dbeaver.model.security.*; import org.jkiss.dbeaver.runtime.DBWorkbench; import org.jkiss.utils.ArrayUtils; import org.jkiss.utils.CommonUtils; @@ -757,8 +757,11 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen dbStat.execute(); } - insertPermissions(dbCon, teamId, - new String[] {DBWConstants.PERMISSION_PUBLIC} , grantor); + insertPermissions(dbCon, + teamId, + getDefaultTeamPermissions(), + grantor + ); txn.commit(); } @@ -767,6 +770,10 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen } } + protected String[] getDefaultTeamPermissions() { + return new String[]{DBWConstants.PERMISSION_PUBLIC}; + } + @Override public void updateTeam(String teamId, String name, String description) throws DBCException { if (!isSubjectExists(teamId)) { From b5c34da6ecc1cdf1bdb31a06b8131cb65d6edaa3 Mon Sep 17 00:00:00 2001 From: Alexander Skoblikov Date: Thu, 17 Nov 2022 00:36:40 +0300 Subject: [PATCH 12/21] CB-2675 force add public permission for users (#1289) --- .../model/session/WebUserContext.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebUserContext.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebUserContext.java index 0269f7248e..c6105717e9 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebUserContext.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebUserContext.java @@ -59,7 +59,7 @@ public class WebUserContext implements SMCredentialsProvider { public WebUserContext(WebApplication application) throws DBException { this.application = application; this.securityController = application.createSecurityController(this); - this.userPermissions = getDefaultPermissions(); + setUserPermissions(getDefaultPermissions()); } /** @@ -87,7 +87,7 @@ public class WebUserContext implements SMCredentialsProvider { authPermissions.getPermissions() ); this.refreshToken = smAuthInfo.getSmRefreshToken(); - this.userPermissions = authPermissions.getPermissions(); + setUserPermissions(authPermissions.getPermissions()); this.securityController = application.createSecurityController(this); this.adminSecurityController = application.getAdminSecurityController(this); this.secretController = application.getSecretController(this); @@ -122,7 +122,7 @@ public class WebUserContext implements SMCredentialsProvider { } catch (Exception e) { log.error("Error logging out user", e); } - this.userPermissions = getDefaultPermissions(); + setUserPermissions(getDefaultPermissions()); this.smCredentials = null; this.user = null; this.securityController = application.createSecurityController(this); @@ -171,11 +171,16 @@ public class WebUserContext implements SMCredentialsProvider { */ public synchronized void refreshPermissions() throws DBException { if (isAuthorizedInSecurityManager()) { - this.userPermissions = securityController.getTokenPermissions().getPermissions(); + setUserPermissions(securityController.getTokenPermissions().getPermissions()); } else { - this.userPermissions = getDefaultPermissions(); + setUserPermissions(getDefaultPermissions()); } - if (userPermissions != null && !userPermissions.isEmpty()) { + } + + private void setUserPermissions(Set permissions) { + this.userPermissions = permissions; + // FIXME: automatically assign public permission in sm controller˚ + if (!CommonUtils.isEmpty(userPermissions)) { userPermissions.add(DBWConstants.PERMISSION_PUBLIC); } } From 3b8eeb59a9f1735223fc1ec8cf0871e2316f5ba8 Mon Sep 17 00:00:00 2001 From: Aleksey Potsetsuev Date: Thu, 17 Nov 2022 13:25:56 +0300 Subject: [PATCH 13/21] CB-2875 fix: can open event --- .../src/NodesManager/NavNodeManagerService.ts | 2 +- .../src/PluginBootstrap.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/webapp/packages/core-navigation-tree/src/NodesManager/NavNodeManagerService.ts b/webapp/packages/core-navigation-tree/src/NodesManager/NavNodeManagerService.ts index 06b397d508..98c5043229 100644 --- a/webapp/packages/core-navigation-tree/src/NodesManager/NavNodeManagerService.ts +++ b/webapp/packages/core-navigation-tree/src/NodesManager/NavNodeManagerService.ts @@ -174,7 +174,7 @@ export class NavNodeManagerService extends Bootstrap { } async canOpen(nodeId: string, parentId: string, folderId?: string): Promise { - if (!this.navTree.has(nodeId)) { + if (!this.navNodeInfoResource.has(nodeId)) { return false; } diff --git a/webapp/packages/plugin-sql-editor-navigation-tab-resource/src/PluginBootstrap.ts b/webapp/packages/plugin-sql-editor-navigation-tab-resource/src/PluginBootstrap.ts index 0d37cdafde..f68136b500 100644 --- a/webapp/packages/plugin-sql-editor-navigation-tab-resource/src/PluginBootstrap.ts +++ b/webapp/packages/plugin-sql-editor-navigation-tab-resource/src/PluginBootstrap.ts @@ -191,9 +191,9 @@ export class PluginBootstrap extends Bootstrap { try { const nodeInfo = contexts.getContext(this.navNodeManagerService.navigationNavNodeContext); - const node = await this.navNodeInfoResource.load(data.nodeId); + const node = this.navNodeInfoResource.get(data.nodeId); - if (node.nodeType !== RESOURCE_NODE_TYPE || !isScript(node.id)) { + if (!node || node.nodeType !== RESOURCE_NODE_TYPE || !isScript(node.id)) { return; } From 43b687df22cc9d5c564f7319f7ac6404b03ee088 Mon Sep 17 00:00:00 2001 From: Aleksey Potsetsuev Date: Thu, 17 Nov 2022 13:31:48 +0300 Subject: [PATCH 14/21] chore: version update 22.2.5 --- webapp/packages/product-default/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webapp/packages/product-default/package.json b/webapp/packages/product-default/package.json index 391e924033..1f0894c714 100644 --- a/webapp/packages/product-default/package.json +++ b/webapp/packages/product-default/package.json @@ -1,7 +1,7 @@ { "name": "@cloudbeaver/product-default", "sideEffects": false, - "version": "22.2.4", + "version": "22.2.5", "description": "", "license": "Apache-2.0", "main": "lib/index.js", @@ -22,8 +22,6 @@ "@cloudbeaver/core-bootstrap": "0.1.0", "@cloudbeaver/core-di": "0.1.0", "@cloudbeaver/core-product": "0.1.0", - "@cloudbeaver/plugin-user-profile": "0.1.0", - "@cloudbeaver/plugin-help": "0.1.0", "@cloudbeaver/plugin-administration": "0.1.0", "@cloudbeaver/plugin-authentication": "0.1.0", "@cloudbeaver/plugin-authentication-administration": "0.1.0", @@ -36,6 +34,7 @@ "@cloudbeaver/plugin-data-viewer": "0.1.0", "@cloudbeaver/plugin-datasource-context-switch": "0.1.0", "@cloudbeaver/plugin-ddl-viewer": "0.1.0", + "@cloudbeaver/plugin-help": "0.1.0", "@cloudbeaver/plugin-localization": "0.1.0", "@cloudbeaver/plugin-log-viewer": "0.1.0", "@cloudbeaver/plugin-navigation-tree": "0.1.0", @@ -48,6 +47,7 @@ "@cloudbeaver/plugin-sql-editor-screen": "0.1.0", "@cloudbeaver/plugin-theme": "0.1.0", "@cloudbeaver/plugin-top-app-bar": "0.1.0", + "@cloudbeaver/plugin-user-profile": "0.1.0", "@cloudbeaver/plugin-version": "0.1.0", "@cloudbeaver/plugin-version-update-administration": "0.1.0" }, From fd66fb281cef0fb662eb2bb38714ce12e2bb9d40 Mon Sep 17 00:00:00 2001 From: Aleksey Potsetsuev Date: Thu, 17 Nov 2022 13:36:23 +0300 Subject: [PATCH 15/21] chore: version update 22.3.0 --- webapp/packages/product-default/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/packages/product-default/package.json b/webapp/packages/product-default/package.json index 1f0894c714..a5029ce2a6 100644 --- a/webapp/packages/product-default/package.json +++ b/webapp/packages/product-default/package.json @@ -1,7 +1,7 @@ { "name": "@cloudbeaver/product-default", "sideEffects": false, - "version": "22.2.5", + "version": "22.3.0", "description": "", "license": "Apache-2.0", "main": "lib/index.js", From 82634fa59de51162d9045ee73bf20783b27651eb Mon Sep 17 00:00:00 2001 From: Alexander Skoblikov Date: Thu, 17 Nov 2022 17:13:57 +0300 Subject: [PATCH 16/21] CB-2873 ssh creds saving doesnt work when database creds arent saved (#1296) * CB-2873 revert loader behaviour, otherwise state mutates * CB-2886 always send ssh config Co-authored-by: naumov --- .../cloudbeaver/model/WebConnectionInfo.java | 7 ++--- .../NetworkHandlerAuthForm.tsx | 1 + .../DatabaseAuthDialog/DBAuthDialogFooter.tsx | 1 - .../DatabaseAuthDialog/DatabaseAuthDialog.tsx | 29 ++++++++++--------- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionInfo.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionInfo.java index dbfe02cb02..e395d82658 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionInfo.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionInfo.java @@ -327,11 +327,8 @@ public class WebConnectionInfo { @Property public List getNetworkHandlersConfig() { - if (canViewReadOnlyConnections()) { - return dataSourceContainer.getConnectionConfiguration().getHandlers().stream() - .map(WebNetworkHandlerConfig::new).collect(Collectors.toList()); - } - return Collections.emptyList(); + return dataSourceContainer.getConnectionConfiguration().getHandlers().stream() + .map(WebNetworkHandlerConfig::new).collect(Collectors.toList()); } @Property diff --git a/webapp/packages/plugin-connections/src/ConnectionAuthentication/NetworkHandlerAuthForm.tsx b/webapp/packages/plugin-connections/src/ConnectionAuthentication/NetworkHandlerAuthForm.tsx index b8caa50e86..f3a4e28e46 100644 --- a/webapp/packages/plugin-connections/src/ConnectionAuthentication/NetworkHandlerAuthForm.tsx +++ b/webapp/packages/plugin-connections/src/ConnectionAuthentication/NetworkHandlerAuthForm.tsx @@ -26,6 +26,7 @@ export const NetworkHandlerAuthForm = observer(function NetworkHandlerAut const translate = useTranslate(); const handler = useMapResource(NetworkHandlerAuthForm, NetworkHandlerResource, id); + //@TODO Do not mutate state in component body if (!networkHandlersConfig.some(state => state.id === id)) { networkHandlersConfig.push({ id, diff --git a/webapp/packages/plugin-connections/src/DatabaseAuthDialog/DBAuthDialogFooter.tsx b/webapp/packages/plugin-connections/src/DatabaseAuthDialog/DBAuthDialogFooter.tsx index 6a157e2ad9..2699eb7beb 100644 --- a/webapp/packages/plugin-connections/src/DatabaseAuthDialog/DBAuthDialogFooter.tsx +++ b/webapp/packages/plugin-connections/src/DatabaseAuthDialog/DBAuthDialogFooter.tsx @@ -49,7 +49,6 @@ export const DBAuthDialogFooter = observer>(funct type="button" mod={['unelevated']} disabled={isAuthenticating} - loading={isAuthenticating} onClick={onLogin} > {translate('authentication_login')} diff --git a/webapp/packages/plugin-connections/src/DatabaseAuthDialog/DatabaseAuthDialog.tsx b/webapp/packages/plugin-connections/src/DatabaseAuthDialog/DatabaseAuthDialog.tsx index d7a0708583..93b6e10305 100644 --- a/webapp/packages/plugin-connections/src/DatabaseAuthDialog/DatabaseAuthDialog.tsx +++ b/webapp/packages/plugin-connections/src/DatabaseAuthDialog/DatabaseAuthDialog.tsx @@ -15,6 +15,7 @@ import { useFocus, ErrorMessage, useStyles, + Loader, } from '@cloudbeaver/core-blocks'; import { IConnectionInfoParams, useConnectionInfo, useDBDriver } from '@cloudbeaver/core-connections'; import { useController } from '@cloudbeaver/core-di'; @@ -86,20 +87,20 @@ export const DatabaseAuthDialog: DialogComponent = observer(function Da )} onReject={options?.persistent ? undefined : rejectDialog} > - - + + {!connection.isLoaded() || connection.isLoading() || !controller.configured + ? + : ( + + )} ); From b1ab382c394a0efe8592d06fafc0de574e055d73 Mon Sep 17 00:00:00 2001 From: Ainur <59531286+yagudin10@users.noreply.github.com> Date: Thu, 17 Nov 2022 22:04:01 +0100 Subject: [PATCH 17/21] CB-2815 update datasources in private project (#1271) --- .../server/events/CBDataSourceUpdatedEventHandlerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/CBDataSourceUpdatedEventHandlerImpl.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/CBDataSourceUpdatedEventHandlerImpl.java index 92cafe80aa..825b71203f 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/CBDataSourceUpdatedEventHandlerImpl.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/CBDataSourceUpdatedEventHandlerImpl.java @@ -42,7 +42,7 @@ public class CBDataSourceUpdatedEventHandlerImpl extends CBProjectUpdatedEventHa CBEventConstants.EventType eventType = CBEventConstants.EventType.valueOf(JSONUtils.getString(event.getEventData(), "eventType")); WebProjectImpl project = activeUserSession.getProjectById(projectId); - if (project == null || !project.getRmProject().isShared()) { + if (project == null) { return; } activeUserSession.updateProjectConnection(project, dataSourceIds, eventType); From 010e21ad262fc861225cc52b4c658b63bf457d23 Mon Sep 17 00:00:00 2001 From: Ainur <59531286+yagudin10@users.noreply.github.com> Date: Thu, 17 Nov 2022 22:04:38 +0100 Subject: [PATCH 18/21] CB-2872 do not return project databases for node parents (#1272) --- .../service/navigator/impl/WebServiceNavigator.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java index 767f3563ac..d36ac13c19 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java @@ -161,6 +161,9 @@ public class WebServiceNavigator implements DBWServiceNavigator { List nodeParents = new ArrayList<>(); for (DBNNode parent = node.getParentNode(); parent != null && !(parent instanceof DBNRoot); parent = parent.getParentNode()) { + if (parent instanceof DBNProjectDatabases) { + continue; + } nodeParents.add(new WebNavigatorNodeInfo(session, parent)); } From 7071873b75a7c68636aa25c7d3659d635c7d9c50 Mon Sep 17 00:00:00 2001 From: Ainur <59531286+yagudin10@users.noreply.github.com> Date: Thu, 17 Nov 2022 22:05:33 +0100 Subject: [PATCH 19/21] CB-2858 update in memory projects (#1267) --- .../src/io/cloudbeaver/model/session/WebSession.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java index 658249f951..d0803aeeda 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java @@ -64,6 +64,7 @@ import org.jkiss.dbeaver.model.runtime.ProxyProgressMonitor; import org.jkiss.dbeaver.model.security.*; import org.jkiss.dbeaver.model.security.user.SMObjectPermissions; import org.jkiss.dbeaver.model.sql.DBQuotaException; +import org.jkiss.dbeaver.registry.BaseProjectImpl; import org.jkiss.dbeaver.runtime.DBWorkbench; import org.jkiss.dbeaver.runtime.jobs.DisconnectJob; import org.jkiss.utils.CommonUtils; @@ -373,11 +374,7 @@ public class WebSession extends AbstractSessionPersistent RMController controller = getRmController(); RMProject[] rmProjects = controller.listAccessibleProjects(); for (RMProject project : rmProjects) { - WebProjectImpl virtualProject = createWebProject(project); - if (!virtualProject.getRmProject().getProjectPermissions().contains(RMProjectPermission.DATA_SOURCES_EDIT.getPermissionId())) { - // Projects for which user don't have edit permission can't be saved. So mark the as in memory - virtualProject.setInMemory(true); - } + createWebProject(project); } if (user == null) { WebProjectImpl anonymousProject = createWebProject(RMUtils.createAnonymousProject()); From 611bade50c99248fa459e6cfbe1f51248a364dc4 Mon Sep 17 00:00:00 2001 From: Aleksey Potsetsuev Date: Fri, 18 Nov 2022 18:25:46 +0300 Subject: [PATCH 20/21] CB-2892 fix: top nav bar submenu styles --- .../plugin-top-app-bar/src/TopNavBar/shared/topMenuStyles.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/packages/plugin-top-app-bar/src/TopNavBar/shared/topMenuStyles.ts b/webapp/packages/plugin-top-app-bar/src/TopNavBar/shared/topMenuStyles.ts index f56fb47f9a..8ab9438dba 100644 --- a/webapp/packages/plugin-top-app-bar/src/TopNavBar/shared/topMenuStyles.ts +++ b/webapp/packages/plugin-top-app-bar/src/TopNavBar/shared/topMenuStyles.ts @@ -129,7 +129,7 @@ export const topMenuStyles = css` display: none; } } - MenuButton, MenuBarElement { + MenuBarElement { &:hover, &:global([aria-expanded="true"]) { background: #338ecc; } From 6f47c8f9353a2277e56669e649383774c7bf43fc Mon Sep 17 00:00:00 2001 From: Alexander Skoblikov Date: Fri, 18 Nov 2022 19:37:28 +0300 Subject: [PATCH 21/21] unexpected error in update session (#1299) --- .../security/CBEmbeddedSecurityController.java | 15 +++++++++++++-- 1 file changed, 13 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 3120c8b850..c510186d28 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 @@ -1810,7 +1810,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen @Override public void updateSession(@NotNull String sessionId, @NotNull Map parameters) throws DBCException { - String userId = getUserIdOrThrow(); + String userId = getUserIdOrNull(); try (Connection dbCon = database.openConnection()) { try (PreparedStatement dbStat = dbCon.prepareStatement( "UPDATE CB_SESSION SET USER_ID=?,LAST_ACCESS_TIME=?,LAST_ACCESS_REMOTE_ADDRESS=?,LAST_ACCESS_USER_AGENT=?,LAST_ACCESS_INSTANCE_ID=? WHERE SESSION_ID=?")) { @@ -2163,10 +2163,21 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen return originalLink + "?authId=" + authId; } + + @NotNull private String getUserIdOrThrow() throws SMException { + String userId = getUserIdOrNull(); + if (userId == null) { + throw new SMException("User not authenticated"); + } + return userId; + } + + @Nullable + private String getUserIdOrNull() { SMCredentials activeUserCredentials = credentialsProvider.getActiveUserCredentials(); if (activeUserCredentials == null || activeUserCredentials.getUserId() == null) { - throw new SMException("User not authenticated"); + return null; } return activeUserCredentials.getUserId(); }