CB-2508 feat: user/role projects access tab

This commit is contained in:
Wroud
2022-09-30 13:05:05 +03:00
parent e3a2dfe193
commit 52943edff3
10 changed files with 117 additions and 29 deletions
@@ -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
@@ -0,0 +1,9 @@
mutation setSubjectProjectsPermissions(
$subject: String!
$permissions: [RMProjectPermissions!]!
) {
rmSetSubjectProjectPermissions(
subject: $subject
permissions: $permissions
)
}
+32 -4
View File
@@ -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');
},
@@ -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');
}
}
}
@@ -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;
},
@@ -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>
@@ -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) {
@@ -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';