From e28b70b5d17eb48d012b31215b12e01b743e14b4 Mon Sep 17 00:00:00 2001 From: Aleksandr Skoblikov Date: Tue, 6 Sep 2022 17:27:15 +0300 Subject: [PATCH 1/4] CB-2408 create disabled user api --- .../schema/service.admin.graphqls | 2 +- .../src/io/cloudbeaver/service/admin/DBWServiceAdmin.java | 2 +- .../cloudbeaver/service/admin/WebServiceBindingAdmin.java | 2 +- .../io/cloudbeaver/service/admin/impl/WebServiceAdmin.java | 4 ++-- .../service/security/CBEmbeddedSecurityController.java | 6 +++--- .../src/io/cloudbeaver/service/security/db/CBDatabase.java | 2 +- 6 files changed, 9 insertions(+), 9 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 efdbdbf132..dd27bbb77c 100644 --- a/server/bundles/io.cloudbeaver.service.admin/schema/service.admin.graphqls +++ b/server/bundles/io.cloudbeaver.service.admin/schema/service.admin.graphqls @@ -108,7 +108,7 @@ extend type Query { listRoles(roleId: ID): [AdminRoleInfo!]! listPermissions: [AdminPermissionInfo!]! - createUser(userId: ID!): AdminUserInfo! + createUser(userId: ID!, enabled: Boolean!): AdminUserInfo! deleteUser(userId: ID!): Boolean createRole(roleId: ID!, roleName: String, description: String): AdminRoleInfo! 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 5c2eb5fc34..357bf83552 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 @@ -53,7 +53,7 @@ public interface DBWServiceAdmin extends DBWService { @WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN) @NotNull - AdminUserInfo createUser(@NotNull WebSession webSession, String userName) throws DBWebException; + AdminUserInfo createUser(@NotNull WebSession webSession, String userName, @NotNull Boolean enabled) throws DBWebException; @WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN) boolean deleteUser(@NotNull WebSession webSession, String userName) throws DBWebException; 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 661b571481..e0a8bef7b6 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 @@ -50,7 +50,7 @@ public class WebServiceBindingAdmin extends WebServiceBindingBase getService(env).listPermissions(getWebSession(env))) .dataFetcher("createUser", - env -> getService(env).createUser(getWebSession(env), env.getArgument("userId"))) + env -> getService(env).createUser(getWebSession(env), env.getArgument("userId"), env.getArgument("enabled"))) .dataFetcher("deleteUser", env -> getService(env).deleteUser(getWebSession(env), env.getArgument("userId"))) .dataFetcher("createRole", 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 b087a8fda2..3d69bd720d 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 @@ -134,7 +134,7 @@ public class WebServiceAdmin implements DBWServiceAdmin { @NotNull @Override - public AdminUserInfo createUser(@NotNull WebSession webSession, String userName) throws DBWebException { + public AdminUserInfo createUser(@NotNull WebSession webSession, String userName, @NotNull Boolean enabled) throws DBWebException { if (userName.isEmpty()) { throw new DBWebException("Empty user name"); } @@ -142,7 +142,7 @@ public class WebServiceAdmin implements DBWServiceAdmin { try { var securityController = webSession.getAdminSecurityController(); - securityController.createUser(userName, Map.of()); + securityController.createUser(userName, Map.of(), enabled); var smUser = securityController.getUserById(userName); return new AdminUserInfo(webSession, new WebUser(smUser)); } catch (Exception e) { 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 a6d2c5de00..24aeab49a5 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 @@ -107,7 +107,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen // Users @Override - public void createUser(String userId, Map metaParameters) throws DBException { + public void createUser(String userId, Map metaParameters, boolean enabled) throws DBException { if (isSubjectExists(userId)) { throw new DBCException("User or role '" + userId + "' already exists"); } @@ -116,7 +116,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen createAuthSubject(dbCon, userId, SUBJECT_USER); try (PreparedStatement dbStat = dbCon.prepareStatement("INSERT INTO CB_USER(USER_ID,IS_ACTIVE,CREATE_TIME) VALUES(?,?,?)")) { dbStat.setString(1, userId); - dbStat.setString(2, CHAR_BOOL_TRUE); + dbStat.setString(2, enabled ? CHAR_BOOL_TRUE : CHAR_BOOL_FALSE); dbStat.setTimestamp(3, new Timestamp(System.currentTimeMillis())); dbStat.execute(); } @@ -1360,7 +1360,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen userId = userIdFromCredentials; if (!isSubjectExists(userId)) { var newUser = new SMUser(userId); - createUser(newUser.getUserId(), newUser.getMetaParameters()); + createUser(newUser.getUserId(), newUser.getMetaParameters(), true); String defaultRoleName = WebAppUtils.getWebApplication().getAppConfiguration().getDefaultUserRole(); if (!CommonUtils.isEmpty(defaultRoleName)) { setUserRoles(userId, new String[]{defaultRoleName}, userId); 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 6bd385c8b6..a83400ac79 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 @@ -246,7 +246,7 @@ public class CBDatabase { if (adminUser == null) { adminUser = new SMUser(adminName); - adminSecurityController.createUser(adminUser.getUserId(), adminUser.getMetaParameters()); + adminSecurityController.createUser(adminUser.getUserId(), adminUser.getMetaParameters(), true); } if (!CommonUtils.isEmpty(adminPassword)) { From 24c6ef66e77cfc7d2e039c15b8e56a74857d9d59 Mon Sep 17 00:00:00 2001 From: Konstantin Vasilev Date: Fri, 9 Sep 2022 10:39:28 +0300 Subject: [PATCH 2/4] CB-2408 feat(core-authentication): Send enabled option on user creation --- webapp/packages/core-authentication/src/UsersResource.ts | 3 ++- .../src/queries/authentication/users/createUser.gql | 6 +++--- webapp/packages/core-sdk/src/sdk.ts | 7 +++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/webapp/packages/core-authentication/src/UsersResource.ts b/webapp/packages/core-authentication/src/UsersResource.ts index 929113d4a2..9a577c481e 100644 --- a/webapp/packages/core-authentication/src/UsersResource.ts +++ b/webapp/packages/core-authentication/src/UsersResource.ts @@ -98,13 +98,14 @@ export class UsersResource extends CachedMapResource { const { user } = await this.graphQLService.sdk.createUser({ userId, + enabled, ...this.getDefaultIncludes(), ...this.getIncludesMap(userId), }); try { await this.updateCredentials(userId, credentials); - await this.enableUser(userId, enabled, true); + for (const roleId of roles) { await this.grantRole(userId, roleId, true); } diff --git a/webapp/packages/core-sdk/src/queries/authentication/users/createUser.gql b/webapp/packages/core-sdk/src/queries/authentication/users/createUser.gql index 9f33b54016..45b4c9b5f4 100644 --- a/webapp/packages/core-sdk/src/queries/authentication/users/createUser.gql +++ b/webapp/packages/core-sdk/src/queries/authentication/users/createUser.gql @@ -1,5 +1,5 @@ -query createUser($userId: ID!, $includeMetaParameters: Boolean!, $customIncludeOriginDetails: Boolean!) { - user: createUser(userId: $userId) { +query createUser($userId: ID!, $enabled: Boolean!, $includeMetaParameters: Boolean!, $customIncludeOriginDetails: Boolean!) { + user: createUser(userId: $userId, enabled: $enabled) { ...AdminUserInfo - } + } } \ No newline at end of file diff --git a/webapp/packages/core-sdk/src/sdk.ts b/webapp/packages/core-sdk/src/sdk.ts index 5ae56d4f47..8fc381bcd3 100644 --- a/webapp/packages/core-sdk/src/sdk.ts +++ b/webapp/packages/core-sdk/src/sdk.ts @@ -330,6 +330,7 @@ export interface DriverInfo { promotedScore?: Maybe; providerId?: Maybe; providerProperties: Array; + requiresDatabaseName?: Maybe; requiresServerName?: Maybe; sampleURL?: Maybe; } @@ -957,6 +958,7 @@ export interface QueryCreateRoleArgs { export interface QueryCreateUserArgs { + enabled: Scalars['Boolean']; userId: Scalars['ID']; } @@ -1695,6 +1697,7 @@ export type SaveUserMetaParametersQuery = { setUserMetaParameterValues: boolean export type CreateUserQueryVariables = Exact<{ userId: Scalars['ID']; + enabled: Scalars['Boolean']; includeMetaParameters: Scalars['Boolean']; customIncludeOriginDetails: Scalars['Boolean']; }>; @@ -3099,8 +3102,8 @@ export const SaveUserMetaParametersDocument = ` } `; export const CreateUserDocument = ` - query createUser($userId: ID!, $includeMetaParameters: Boolean!, $customIncludeOriginDetails: Boolean!) { - user: createUser(userId: $userId) { + query createUser($userId: ID!, $enabled: Boolean!, $includeMetaParameters: Boolean!, $customIncludeOriginDetails: Boolean!) { + user: createUser(userId: $userId, enabled: $enabled) { ...AdminUserInfo } } From b3de3d9d29217dc5fba010b40c4cfa5198ebc5a8 Mon Sep 17 00:00:00 2001 From: Konstantin Vasilev Date: Tue, 13 Sep 2022 14:23:50 +0300 Subject: [PATCH 3/4] CB-2505 feat(core-block): Make properties table filterable --- .../src/PropertiesTable/PropertiesTable.tsx | 24 +++++++++++++++---- .../core-blocks/src/PropertiesTable/styles.ts | 11 +++++++++ webapp/packages/core-blocks/src/locales/en.ts | 1 + .../DriverProperties/DriverProperties.tsx | 1 + 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/webapp/packages/core-blocks/src/PropertiesTable/PropertiesTable.tsx b/webapp/packages/core-blocks/src/PropertiesTable/PropertiesTable.tsx index 4faac3c4f7..19a6910742 100644 --- a/webapp/packages/core-blocks/src/PropertiesTable/PropertiesTable.tsx +++ b/webapp/packages/core-blocks/src/PropertiesTable/PropertiesTable.tsx @@ -8,13 +8,14 @@ import { computed } from 'mobx'; import { observer } from 'mobx-react-lite'; -import { useCallback, useMemo } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import styled from 'reshadow'; import { useTranslate } from '@cloudbeaver/core-localization'; import { useStyles } from '@cloudbeaver/core-theming'; import { Button } from '../Button'; +import { ShadowInput } from '../FormControls/ShadowInput'; import { useObjectRef } from '../useObjectRef'; import type { IProperty } from './IProperty'; import { PropertyItem } from './PropertyItem'; @@ -31,6 +32,7 @@ interface Props { onAdd?: () => void; onRemove?: (property: IProperty) => void; className?: string; + filterable?: boolean; } export const PropertiesTable = observer(function PropertiesTable(props) { @@ -38,8 +40,13 @@ export const PropertiesTable = observer(function PropertiesTable(props) { const translate = useTranslate(); const propsRef = useObjectRef({ ...props }); - const sortedProperties = useMemo(() => computed(() => propsRef.properties.slice().sort( - (a, b) => (a?.displayName ?? '').localeCompare(b?.displayName ?? ''))), [propsRef.properties]); + const [filterValue, setFilterValue] = useState(''); + + const sortedProperties = useMemo(() => computed(() => propsRef.properties + .slice() + .sort((a, b) => (a.displayName ?? '').localeCompare(b.displayName ?? '')) + .filter(p => p.displayName?.includes(filterValue) ?? true ) + ), [propsRef.properties, filterValue]); const changeName = useCallback((id: string, key: string) => { const { properties, propertiesState, onKeyChange } = propsRef; @@ -109,7 +116,16 @@ export const PropertiesTable = observer(function PropertiesTable(props) { - {translate('block_properties_table_name')} +
+ {translate('block_properties_table_name')} +
+ {props.filterable ? ( + + ) : null}
{translate('block_properties_table_value')} diff --git a/webapp/packages/core-blocks/src/PropertiesTable/styles.ts b/webapp/packages/core-blocks/src/PropertiesTable/styles.ts index 588cef2aea..2a0e569767 100644 --- a/webapp/packages/core-blocks/src/PropertiesTable/styles.ts +++ b/webapp/packages/core-blocks/src/PropertiesTable/styles.ts @@ -19,6 +19,7 @@ export const PROPERTIES_TABLE_STYLES = css` box-sizing: border-box; display: inline-flex; border-bottom: solid 1px; + align-items: center; } properties-header-name, properties-header-value { composes: theme-typography--caption from global; @@ -30,6 +31,8 @@ export const PROPERTIES_TABLE_STYLES = css` properties-header-name { flex: 0 0 auto; width: 300px; + display: flex; + align-items: center; } properties-header-add { flex: 0 0 auto; @@ -54,6 +57,14 @@ export const PROPERTIES_TABLE_STYLES = css` height: 24px; pointer-events: none; } + + ShadowInput { + composes: theme-background-surface from global; + box-sizing: border-box; + height: 24px; + flex: 1; + margin-left: 8px; + } `; export const PROPERTIES_TABLE_ADD_STYLES = css` diff --git a/webapp/packages/core-blocks/src/locales/en.ts b/webapp/packages/core-blocks/src/locales/en.ts index 7a21d63aa8..2d1e49c6e7 100644 --- a/webapp/packages/core-blocks/src/locales/en.ts +++ b/webapp/packages/core-blocks/src/locales/en.ts @@ -1,6 +1,7 @@ export default [ ['block_properties_table_name', 'Name'], ['block_properties_table_value', 'Value'], + ['block_properties_table_filter_name', 'Filter'], ['block_properties_table_add', 'Add property'], ['combobox_no_results_placeholder', 'No results'], ]; diff --git a/webapp/packages/plugin-connections/src/ConnectionForm/DriverProperties/DriverProperties.tsx b/webapp/packages/plugin-connections/src/ConnectionForm/DriverProperties/DriverProperties.tsx index 4d7ac0dec2..f4412b6b73 100644 --- a/webapp/packages/plugin-connections/src/ConnectionForm/DriverProperties/DriverProperties.tsx +++ b/webapp/packages/plugin-connections/src/ConnectionForm/DriverProperties/DriverProperties.tsx @@ -105,6 +105,7 @@ export const DriverProperties: TabContainerPanelComponent properties={joinedProperties.get()} propertiesState={formState.config.properties} readOnly={formState.readonly} + filterable onAdd={state.add} onRemove={state.remove} /> From 45c0a68bc1553e5a8cd3927e02f7ae887859b71c Mon Sep 17 00:00:00 2001 From: Konstantin Vasilev Date: Tue, 13 Sep 2022 16:35:49 +0300 Subject: [PATCH 4/4] CB-2505 feat(core-blocks): Fix filter driver properties --- .../core-blocks/src/PropertiesTable/PropertiesTable.tsx | 4 ++-- .../core-blocks/src/PropertiesTable/PropertyItem.tsx | 7 ++++++- webapp/packages/core-blocks/src/locales/ru.ts | 1 + 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/webapp/packages/core-blocks/src/PropertiesTable/PropertiesTable.tsx b/webapp/packages/core-blocks/src/PropertiesTable/PropertiesTable.tsx index 19a6910742..661ecaaeb5 100644 --- a/webapp/packages/core-blocks/src/PropertiesTable/PropertiesTable.tsx +++ b/webapp/packages/core-blocks/src/PropertiesTable/PropertiesTable.tsx @@ -6,7 +6,7 @@ * you may not use this file except in compliance with the License. */ -import { computed } from 'mobx'; +import { computed, toJS } from 'mobx'; import { observer } from 'mobx-react-lite'; import { useCallback, useMemo, useState } from 'react'; import styled from 'reshadow'; @@ -45,7 +45,7 @@ export const PropertiesTable = observer(function PropertiesTable(props) { const sortedProperties = useMemo(() => computed(() => propsRef.properties .slice() .sort((a, b) => (a.displayName ?? '').localeCompare(b.displayName ?? '')) - .filter(p => p.displayName?.includes(filterValue) ?? true ) + .filter(p => p.key.toLocaleLowerCase().includes(filterValue.toLocaleLowerCase())) ), [propsRef.properties, filterValue]); const changeName = useCallback((id: string, key: string) => { diff --git a/webapp/packages/core-blocks/src/PropertiesTable/PropertyItem.tsx b/webapp/packages/core-blocks/src/PropertiesTable/PropertyItem.tsx index e286e9449c..0963bafc07 100644 --- a/webapp/packages/core-blocks/src/PropertiesTable/PropertyItem.tsx +++ b/webapp/packages/core-blocks/src/PropertiesTable/PropertyItem.tsx @@ -139,7 +139,12 @@ export const PropertyItem = observer(function PropertyItem({ const handleRemove = useCallback(() => onRemove(property.id), [property]); useLayoutEffect(() => { - if (keyInputRef.current && isDeletable && property.new) { + if ( + keyInputRef.current + && isDeletable + && property.new + && !(document.activeElement instanceof HTMLInputElement) + ) { keyInputRef.current.focus(); } }, [property]); diff --git a/webapp/packages/core-blocks/src/locales/ru.ts b/webapp/packages/core-blocks/src/locales/ru.ts index 3e15075e52..acbd18ef61 100644 --- a/webapp/packages/core-blocks/src/locales/ru.ts +++ b/webapp/packages/core-blocks/src/locales/ru.ts @@ -1,6 +1,7 @@ export default [ ['block_properties_table_name', 'Название'], ['block_properties_table_value', 'Значение'], + ['block_properties_table_filter_name', 'Фильтр'], ['block_properties_table_add', 'Добавить параметр'], ['combobox_no_results_placeholder', 'Нет совпадений'], ];