mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-10-12 23:37:00 +08:00
CB-2508 feat: user/role projects access tab
This commit is contained in:
@@ -11,13 +11,14 @@ import { runInAction } from 'mobx';
|
||||
import { AdminObjectGrantInfo, EAdminPermission } from '@cloudbeaver/core-administration';
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
import { SessionPermissionsResource } from '@cloudbeaver/core-root';
|
||||
import { GraphQLService, CachedMapResource, CachedMapAllKey, ResourceKey, ResourceKeyUtils, resourceKeyList, RmProject, ResourceKeyList, RmProjectPermissions } from '@cloudbeaver/core-sdk';
|
||||
import { GraphQLService, CachedMapResource, CachedMapAllKey, ResourceKey, ResourceKeyUtils, resourceKeyList, RmProject, ResourceKeyList, RmProjectPermissions, RmSubjectProjectPermissions } from '@cloudbeaver/core-sdk';
|
||||
import { isArraysEqual } from '@cloudbeaver/core-utils';
|
||||
|
||||
const newSymbol = Symbol('new-project');
|
||||
|
||||
export type SharedProject = RmProject;
|
||||
export type ProjectPermission = RmProjectPermissions;
|
||||
export type ProjectPermission = RmSubjectProjectPermissions;
|
||||
export type ProjectSubjectPermission = RmProjectPermissions;
|
||||
type SharedProjectNew = SharedProject & { [newSymbol]: boolean };
|
||||
|
||||
interface IProjectConfig {
|
||||
@@ -53,6 +54,13 @@ export class SharedProjectsResource extends CachedMapResource<string, SharedProj
|
||||
});
|
||||
}
|
||||
|
||||
async setSubjectProjectsAccess(subject: string, permissions: ProjectSubjectPermission[]): Promise<void> {
|
||||
await this.graphQLService.sdk.setSubjectProjectsPermissions({
|
||||
subject,
|
||||
permissions,
|
||||
});
|
||||
}
|
||||
|
||||
async loadAccessSubjects(projectId: string): Promise<AdminObjectGrantInfo[]> {
|
||||
const { grantedPermissions } = await this.graphQLService.sdk.getProjectGrantedPermissions({
|
||||
projectId,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
mutation setProjectPermissions(
|
||||
$projectId: String!
|
||||
$permissions: [RMProjectPermissions!]!
|
||||
$permissions: [RMSubjectProjectPermissions!]!
|
||||
) {
|
||||
rmSetProjectPermissions(
|
||||
projectId: $projectId
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
mutation setSubjectProjectsPermissions(
|
||||
$subject: String!
|
||||
$permissions: [RMProjectPermissions!]!
|
||||
) {
|
||||
rmSetSubjectProjectPermissions(
|
||||
subject: $subject
|
||||
permissions: $permissions
|
||||
)
|
||||
}
|
||||
@@ -403,6 +403,7 @@ export interface Mutation {
|
||||
rmDeleteResource?: Maybe<Scalars['Boolean']>;
|
||||
rmMoveResource: Scalars['String'];
|
||||
rmSetProjectPermissions: Scalars['Boolean'];
|
||||
rmSetSubjectProjectPermissions: Scalars['Boolean'];
|
||||
rmWriteResourceStringContent: Scalars['String'];
|
||||
setConnectionNavigatorSettings: ConnectionInfo;
|
||||
setUserConfigurationParameter: Scalars['Boolean'];
|
||||
@@ -596,11 +597,17 @@ export interface MutationRmMoveResourceArgs {
|
||||
|
||||
|
||||
export interface MutationRmSetProjectPermissionsArgs {
|
||||
permissions: Array<RmProjectPermissions>;
|
||||
permissions: Array<RmSubjectProjectPermissions>;
|
||||
projectId: Scalars['String'];
|
||||
}
|
||||
|
||||
|
||||
export interface MutationRmSetSubjectProjectPermissionsArgs {
|
||||
permissions: Array<RmProjectPermissions>;
|
||||
subject: Scalars['String'];
|
||||
}
|
||||
|
||||
|
||||
export interface MutationRmWriteResourceStringContentArgs {
|
||||
data: Scalars['String'];
|
||||
forceOverwrite: Scalars['Boolean'];
|
||||
@@ -1349,7 +1356,7 @@ export interface RmProject {
|
||||
|
||||
export interface RmProjectPermissions {
|
||||
permissions: Array<Scalars['String']>;
|
||||
subjectId: Scalars['String'];
|
||||
projectId: Scalars['String'];
|
||||
}
|
||||
|
||||
export interface RmResource {
|
||||
@@ -1358,6 +1365,11 @@ export interface RmResource {
|
||||
name: Scalars['String'];
|
||||
}
|
||||
|
||||
export interface RmSubjectProjectPermissions {
|
||||
permissions: Array<Scalars['String']>;
|
||||
subjectId: Scalars['String'];
|
||||
}
|
||||
|
||||
export enum ResultDataFormat {
|
||||
Document = 'document',
|
||||
Graph = 'graph',
|
||||
@@ -2576,12 +2588,20 @@ export type ReadResourceQuery = { value: string };
|
||||
|
||||
export type SetProjectPermissionsMutationVariables = Exact<{
|
||||
projectId: Scalars['String'];
|
||||
permissions: Array<RmProjectPermissions> | RmProjectPermissions;
|
||||
permissions: Array<RmSubjectProjectPermissions> | RmSubjectProjectPermissions;
|
||||
}>;
|
||||
|
||||
|
||||
export type SetProjectPermissionsMutation = { rmSetProjectPermissions: boolean };
|
||||
|
||||
export type SetSubjectProjectsPermissionsMutationVariables = Exact<{
|
||||
subject: Scalars['String'];
|
||||
permissions: Array<RmProjectPermissions> | RmProjectPermissions;
|
||||
}>;
|
||||
|
||||
|
||||
export type SetSubjectProjectsPermissionsMutation = { rmSetSubjectProjectPermissions: boolean };
|
||||
|
||||
export type WriteResourceContentMutationVariables = Exact<{
|
||||
projectId: Scalars['String'];
|
||||
resourcePath: Scalars['String'];
|
||||
@@ -4145,10 +4165,15 @@ export const ReadResourceDocument = `
|
||||
}
|
||||
`;
|
||||
export const SetProjectPermissionsDocument = `
|
||||
mutation setProjectPermissions($projectId: String!, $permissions: [RMProjectPermissions!]!) {
|
||||
mutation setProjectPermissions($projectId: String!, $permissions: [RMSubjectProjectPermissions!]!) {
|
||||
rmSetProjectPermissions(projectId: $projectId, permissions: $permissions)
|
||||
}
|
||||
`;
|
||||
export const SetSubjectProjectsPermissionsDocument = `
|
||||
mutation setSubjectProjectsPermissions($subject: String!, $permissions: [RMProjectPermissions!]!) {
|
||||
rmSetSubjectProjectPermissions(subject: $subject, permissions: $permissions)
|
||||
}
|
||||
`;
|
||||
export const WriteResourceContentDocument = `
|
||||
mutation writeResourceContent($projectId: String!, $resourcePath: String!, $data: String!, $forceOverwrite: Boolean!) {
|
||||
rmWriteResourceStringContent(
|
||||
@@ -4687,6 +4712,9 @@ export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper =
|
||||
setProjectPermissions(variables: SetProjectPermissionsMutationVariables, requestHeaders?: Dom.RequestInit["headers"]): Promise<SetProjectPermissionsMutation> {
|
||||
return withWrapper((wrappedRequestHeaders) => client.request<SetProjectPermissionsMutation>(SetProjectPermissionsDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'setProjectPermissions', 'mutation');
|
||||
},
|
||||
setSubjectProjectsPermissions(variables: SetSubjectProjectsPermissionsMutationVariables, requestHeaders?: Dom.RequestInit["headers"]): Promise<SetSubjectProjectsPermissionsMutation> {
|
||||
return withWrapper((wrappedRequestHeaders) => client.request<SetSubjectProjectsPermissionsMutation>(SetSubjectProjectsPermissionsDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'setSubjectProjectsPermissions', 'mutation');
|
||||
},
|
||||
writeResourceContent(variables: WriteResourceContentMutationVariables, requestHeaders?: Dom.RequestInit["headers"]): Promise<WriteResourceContentMutation> {
|
||||
return withWrapper((wrappedRequestHeaders) => client.request<WriteResourceContentMutation>(WriteResourceContentDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'writeResourceContent', 'mutation');
|
||||
},
|
||||
|
||||
+2
-2
@@ -116,9 +116,9 @@ export class RoleOptionsTabService extends Bootstrap {
|
||||
}
|
||||
} catch (exception: any) {
|
||||
if (create) {
|
||||
status.error('administration_roles_role_create_error', exception);
|
||||
status.error(exception, 'administration_roles_role_create_error');
|
||||
} else {
|
||||
status.error('administration_roles_role_save_error', exception);
|
||||
status.error(exception, 'administration_roles_role_save_error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-3
@@ -27,7 +27,7 @@ export interface IRoleFormStatus {
|
||||
messages: string[];
|
||||
exception: Error | null;
|
||||
info: (message: string) => void;
|
||||
error: (message: string, exception?: Error) => void;
|
||||
error: (exception: Error | null, message?: string) => void;
|
||||
}
|
||||
|
||||
@injectable()
|
||||
@@ -86,8 +86,10 @@ export class RoleFormService {
|
||||
info(message: string) {
|
||||
this.messages.push(message);
|
||||
},
|
||||
error(message: string, exception: Error | null = null) {
|
||||
this.messages.push(message);
|
||||
error(exception: Error | null, message?: string) {
|
||||
if (message) {
|
||||
this.messages.push(message);
|
||||
}
|
||||
this.saved = false;
|
||||
this.exception = exception;
|
||||
},
|
||||
|
||||
+7
-1
@@ -110,7 +110,13 @@ export const UserForm = observer<Props>(function UserForm({
|
||||
controller.update(user, editing, onCancel);
|
||||
|
||||
return styled(useStyles(styles))(
|
||||
<TabsState container={service.tabsContainer} user={user} controller={controller} editing={editing}>
|
||||
<TabsState
|
||||
localState={controller.partsState}
|
||||
container={service.tabsContainer}
|
||||
user={user}
|
||||
controller={controller}
|
||||
editing={editing}
|
||||
>
|
||||
<box>
|
||||
<connection-top-bar>
|
||||
<connection-top-bar-tabs>
|
||||
|
||||
+42
-16
@@ -8,15 +8,19 @@
|
||||
|
||||
import { observable, computed, makeObservable } from 'mobx';
|
||||
|
||||
import { compareRoles, isLocalUser, RoleInfo, RolesResource, UsersResource } from '@cloudbeaver/core-authentication';
|
||||
import { AdminUser, compareRoles, isLocalUser, RoleInfo, RolesResource, UsersResource } from '@cloudbeaver/core-authentication';
|
||||
import { ConnectionInfoProjectKey, ConnectionInfoResource, DatabaseConnection, DBDriverResource } from '@cloudbeaver/core-connections';
|
||||
import { injectable, IInitializableController, IDestructibleController } from '@cloudbeaver/core-di';
|
||||
import { CommonDialogService } from '@cloudbeaver/core-dialogs';
|
||||
import { ENotificationType, NotificationService } from '@cloudbeaver/core-events';
|
||||
import { Executor, ExecutorInterrupter } from '@cloudbeaver/core-executor';
|
||||
import type { TLocalizationToken } from '@cloudbeaver/core-localization';
|
||||
import { ErrorDetailsDialog } from '@cloudbeaver/core-notifications';
|
||||
import { PROJECT_GLOBAL_ID } from '@cloudbeaver/core-projects';
|
||||
import { GQLErrorCatcher, AdminConnectionGrantInfo, AdminSubjectType, AdminUserInfo, CachedMapAllKey } from '@cloudbeaver/core-sdk';
|
||||
import { GQLErrorCatcher, AdminConnectionGrantInfo, AdminSubjectType, AdminUserInfo } from '@cloudbeaver/core-sdk';
|
||||
import { MetadataMap } from '@cloudbeaver/core-utils';
|
||||
|
||||
import type { IUserFormState } from './UserFormService';
|
||||
|
||||
interface IStatusMessage {
|
||||
status: ENotificationType;
|
||||
@@ -41,6 +45,7 @@ export class UserFormController implements IInitializableController, IDestructib
|
||||
credentials: IUserCredentials;
|
||||
enabled: boolean;
|
||||
statusMessage: IStatusMessage | null;
|
||||
partsState: MetadataMap<string, any>;
|
||||
|
||||
get connections(): DatabaseConnection[] {
|
||||
return this.connectionInfoResource.get(ConnectionInfoProjectKey(PROJECT_GLOBAL_ID)) as DatabaseConnection[];
|
||||
@@ -58,6 +63,7 @@ export class UserFormController implements IInitializableController, IDestructib
|
||||
|
||||
readonly error: GQLErrorCatcher;
|
||||
|
||||
readonly afterSubmitTask: Executor<IUserFormState>;
|
||||
private isDestructed: boolean;
|
||||
private connectionAccessChanged: boolean;
|
||||
private connectionAccessLoaded: boolean;
|
||||
@@ -72,18 +78,8 @@ export class UserFormController implements IInitializableController, IDestructib
|
||||
private readonly connectionInfoResource: ConnectionInfoResource,
|
||||
private readonly dbDriverResource: DBDriverResource
|
||||
) {
|
||||
makeObservable(this, {
|
||||
selectedConnections: observable,
|
||||
grantedConnections: observable,
|
||||
isSaving: observable,
|
||||
isLoading: observable,
|
||||
credentials: observable,
|
||||
enabled: observable.ref,
|
||||
statusMessage: observable,
|
||||
connections: computed,
|
||||
roles: computed,
|
||||
});
|
||||
|
||||
this.partsState = new MetadataMap();
|
||||
this.afterSubmitTask = new Executor();
|
||||
this.selectedConnections = new Map();
|
||||
this.grantedConnections = [];
|
||||
this.isSaving = false;
|
||||
@@ -101,6 +97,18 @@ export class UserFormController implements IInitializableController, IDestructib
|
||||
this.connectionAccessChanged = false;
|
||||
this.connectionAccessLoaded = false;
|
||||
this.statusMessage = null;
|
||||
|
||||
makeObservable(this, {
|
||||
selectedConnections: observable,
|
||||
grantedConnections: observable,
|
||||
isSaving: observable,
|
||||
isLoading: observable,
|
||||
credentials: observable,
|
||||
enabled: observable.ref,
|
||||
statusMessage: observable,
|
||||
connections: computed,
|
||||
roles: computed,
|
||||
});
|
||||
}
|
||||
|
||||
init(): void { }
|
||||
@@ -129,8 +137,9 @@ export class UserFormController implements IInitializableController, IDestructib
|
||||
|
||||
this.isSaving = true;
|
||||
try {
|
||||
let user: AdminUser;
|
||||
if (!this.editing) {
|
||||
await this.usersResource.create({
|
||||
user = await this.usersResource.create({
|
||||
userId: this.credentials.login,
|
||||
credentials: {
|
||||
profile: '0',
|
||||
@@ -141,6 +150,7 @@ export class UserFormController implements IInitializableController, IDestructib
|
||||
metaParameters: this.credentials.metaParameters,
|
||||
grantedConnections: this.getGrantedConnections(),
|
||||
});
|
||||
|
||||
this.collapse();
|
||||
this.notificationService.logSuccess({ title: 'authentication_administration_user_created' });
|
||||
} else {
|
||||
@@ -157,9 +167,25 @@ export class UserFormController implements IInitializableController, IDestructib
|
||||
await this.saveUserStatus();
|
||||
await this.saveConnectionPermissions();
|
||||
await this.saveMetaParameters();
|
||||
await this.usersResource.refresh(this.user.userId);
|
||||
user = await this.usersResource.refresh(this.user.userId);
|
||||
|
||||
this.notificationService.logSuccess({ title: 'authentication_administration_user_updated' });
|
||||
}
|
||||
|
||||
const context = await this.afterSubmitTask.execute({
|
||||
user,
|
||||
partsState: this.partsState,
|
||||
props: {
|
||||
controller: this,
|
||||
user: this.user,
|
||||
editing: this.editing,
|
||||
},
|
||||
});
|
||||
|
||||
if (ExecutorInterrupter.isInterrupted(context)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.error.clear();
|
||||
this.statusMessage = null;
|
||||
} catch (exception: any) {
|
||||
|
||||
+8
@@ -6,9 +6,11 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import type { AdminUser } from '@cloudbeaver/core-authentication';
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
import type { AdminUserInfo } from '@cloudbeaver/core-sdk';
|
||||
import { TabsContainer } from '@cloudbeaver/core-ui';
|
||||
import type { MetadataMap } from '@cloudbeaver/core-utils';
|
||||
|
||||
import type { UserFormController } from './UserFormController';
|
||||
|
||||
@@ -18,6 +20,12 @@ export interface IUserFormProps {
|
||||
editing?: boolean;
|
||||
}
|
||||
|
||||
export interface IUserFormState {
|
||||
user: AdminUser;
|
||||
partsState: MetadataMap<string, any>;
|
||||
props: IUserFormProps;
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class UserFormService {
|
||||
readonly tabsContainer: TabsContainer<IUserFormProps>;
|
||||
|
||||
@@ -6,3 +6,4 @@ export * from './Administration/Users/UsersAdministrationNavigationService';
|
||||
export * from './Administration/Users/Roles/RoleFormService';
|
||||
export * from './Administration/Users/Roles/IRoleFormProps';
|
||||
export * from './Administration/Users/Roles/Contexts/roleContext';
|
||||
export * from './Administration/Users/UserForm/UserFormService';
|
||||
|
||||
Reference in New Issue
Block a user