diff --git a/webapp/packages/plugin-authentication/src/Administration/Roles/GrantedConnections/GrantedConnections.tsx b/webapp/packages/plugin-authentication/src/Administration/Roles/GrantedConnections/GrantedConnections.tsx index 7b3be6cdd5..47fcf450a5 100644 --- a/webapp/packages/plugin-authentication/src/Administration/Roles/GrantedConnections/GrantedConnections.tsx +++ b/webapp/packages/plugin-authentication/src/Administration/Roles/GrantedConnections/GrantedConnections.tsx @@ -8,7 +8,7 @@ import { computed } from 'mobx'; import { observer } from 'mobx-react-lite'; -import { useMemo } from 'react'; +import { useEffect, useMemo } from 'react'; import styled, { css } from 'reshadow'; import { @@ -65,7 +65,7 @@ export const GrantedConnections: TabContainerPanelComponent = ob const { state, edit, grant, load, revoke } = useGrantedConnections(formState.config, formState.mode); - const { selected } = useTab(tabId, load); + const { selected } = useTab(tabId); const dbDriverResource = useMapResource(DBDriverResource, selected ? 'all' : null); const connections = useMapResource(ConnectionsResource, selected ? ConnectionsResource.keyAll : null); @@ -74,6 +74,12 @@ export const GrantedConnections: TabContainerPanelComponent = ob .filter(connection => state.grantedSubjects.includes(connection.id)) ), [state.grantedSubjects, connections.resource]); + useEffect(() => { + if (selected && !state.loaded) { + load(); + } + }, [selected, state.loaded, load]); + if (!selected) { return null; } diff --git a/webapp/packages/plugin-authentication/src/Administration/Roles/GrantedConnections/GrantedConnectionsTabService.ts b/webapp/packages/plugin-authentication/src/Administration/Roles/GrantedConnections/GrantedConnectionsTabService.ts index fa7dacb889..b84f2cd813 100644 --- a/webapp/packages/plugin-authentication/src/Administration/Roles/GrantedConnections/GrantedConnectionsTabService.ts +++ b/webapp/packages/plugin-authentication/src/Administration/Roles/GrantedConnections/GrantedConnectionsTabService.ts @@ -6,6 +6,7 @@ * you may not use this file except in compliance with the License. */ +import { RolesResource } from '@cloudbeaver/core-authentication'; import { Bootstrap, injectable } from '@cloudbeaver/core-di'; import { NotificationService } from '@cloudbeaver/core-events'; import type { IExecutionContextProvider } from '@cloudbeaver/core-executor'; @@ -24,6 +25,7 @@ export class GrantedConnectionsTabService extends Bootstrap { constructor( private readonly roleFormService: RoleFormService, + private readonly rolesResource: RolesResource, private readonly graphQLService: GraphQLService, private readonly notificationService: NotificationService ) { @@ -73,9 +75,16 @@ export class GrantedConnectionsTabService extends Bootstrap { { state: data.state } ); - const changed = !isArraysEqual(state.initialGrantedSubjects, state.grantedSubjects); + if (!config.roleId || !state.loaded) { + return; + } - if (!config.roleId || !state.loaded || !changed) { + const grantInfo = await this.rolesResource.getSubjectConnectionAccess(config.roleId); + const initial = grantInfo.map(info => info.connectionId); + + const changed = !isArraysEqual(initial, state.grantedSubjects); + + if (!changed) { return; } @@ -84,10 +93,10 @@ export class GrantedConnectionsTabService extends Bootstrap { subjectId: config.roleId, connections: state.grantedSubjects, }); + + state.loaded = false; } catch (exception) { this.notificationService.logException(exception); } - - state.initialGrantedSubjects = state.grantedSubjects.slice(); } } diff --git a/webapp/packages/plugin-authentication/src/Administration/Roles/GrantedUsers/GrantedUsers.tsx b/webapp/packages/plugin-authentication/src/Administration/Roles/GrantedUsers/GrantedUsers.tsx index dd2496fd6b..9404402b8a 100644 --- a/webapp/packages/plugin-authentication/src/Administration/Roles/GrantedUsers/GrantedUsers.tsx +++ b/webapp/packages/plugin-authentication/src/Administration/Roles/GrantedUsers/GrantedUsers.tsx @@ -8,7 +8,7 @@ import { computed } from 'mobx'; import { observer } from 'mobx-react-lite'; -import { useMemo } from 'react'; +import { useEffect, useMemo } from 'react'; import styled, { css } from 'reshadow'; import { UsersResource } from '@cloudbeaver/core-authentication'; @@ -65,13 +65,19 @@ export const GrantedUsers: TabContainerPanelComponent = observer const { state, edit, grant, load, revoke } = useGrantedUsers(formState.config, formState.mode); - const users = useMapResource(UsersResource, UsersResource.keyAll); + const { selected } = useTab(tabId); + + const users = useMapResource(UsersResource, selected ? UsersResource.keyAll : null); const grantedUsers = useMemo(() => computed(() => users.resource.values .filter(user => state.grantedUsers.includes(user.userId)) ), [state.grantedUsers, users.resource]); - const { selected } = useTab(tabId, load); + useEffect(() => { + if (selected && !state.loaded) { + load(); + } + }, [selected, state.loaded, load]); if (!selected) { return null; diff --git a/webapp/packages/plugin-authentication/src/Administration/Roles/GrantedUsers/GrantedUsersTabService.ts b/webapp/packages/plugin-authentication/src/Administration/Roles/GrantedUsers/GrantedUsersTabService.ts index 14b63de285..b4ee6188ac 100644 --- a/webapp/packages/plugin-authentication/src/Administration/Roles/GrantedUsers/GrantedUsersTabService.ts +++ b/webapp/packages/plugin-authentication/src/Administration/Roles/GrantedUsers/GrantedUsersTabService.ts @@ -6,7 +6,7 @@ * you may not use this file except in compliance with the License. */ -import { UsersResource } from '@cloudbeaver/core-authentication'; +import { RolesResource, UsersResource } from '@cloudbeaver/core-authentication'; import { Bootstrap, injectable } from '@cloudbeaver/core-di'; import { NotificationService } from '@cloudbeaver/core-events'; import type { IExecutionContextProvider } from '@cloudbeaver/core-executor'; @@ -25,6 +25,7 @@ export class GrantedUsersTabService extends Bootstrap { constructor( private readonly roleFormService: RoleFormService, private readonly usersResource: UsersResource, + private readonly rolesResource: RolesResource, private readonly notificationService: NotificationService ) { super(); @@ -73,16 +74,22 @@ export class GrantedUsersTabService extends Bootstrap { { state: data.state } ); - const changed = !isArraysEqual(state.initialGrantedUsers, state.grantedUsers); + if (!config.roleId || !state.loaded) { + return; + } - if (!config.roleId || !state.loaded || !changed) { + const initial = await this.rolesResource.loadGrantedUsers(config.roleId); + + const changed = !isArraysEqual(initial, state.grantedUsers); + + if (!changed) { return; } const granted: string[] = []; const revoked: string[] = []; - const revokedUsers = state.initialGrantedUsers.filter(user => !state.grantedUsers.includes(user)); + const revokedUsers = initial.filter(user => !state.grantedUsers.includes(user)); try { for (const user of revokedUsers) { @@ -91,11 +98,13 @@ export class GrantedUsersTabService extends Bootstrap { } for (const user of state.grantedUsers) { - if (!state.initialGrantedUsers.includes(user)) { + if (!initial.includes(user)) { await this.usersResource.grantRole(user, config.roleId); granted.push(user); } } + + state.loaded = false; } catch (exception) { this.notificationService.logException(exception); } @@ -107,9 +116,5 @@ export class GrantedUsersTabService extends Bootstrap { if (revoked.length) { status.info(`Revoked users: "${revoked.join(', ')}"`); } - - if (granted.length || revoked.length) { - state.initialGrantedUsers = state.grantedUsers.slice(); - } } } diff --git a/webapp/packages/plugin-authentication/src/Administration/Roles/GrantedUsers/useGrantedUsers.tsx b/webapp/packages/plugin-authentication/src/Administration/Roles/GrantedUsers/useGrantedUsers.tsx index 97d283c863..4073e21f5c 100644 --- a/webapp/packages/plugin-authentication/src/Administration/Roles/GrantedUsers/useGrantedUsers.tsx +++ b/webapp/packages/plugin-authentication/src/Administration/Roles/GrantedUsers/useGrantedUsers.tsx @@ -49,7 +49,7 @@ export function useGrantedUsers(role: RoleInfo, mode: RoleFormMode): IConnection if (mode === 'edit') { const grantedUsers = await resource.loadGrantedUsers(role.roleId); - state.grantedUsers.push(...grantedUsers); + state.grantedUsers = grantedUsers; state.initialGrantedUsers = state.grantedUsers.slice(); }