Skip to content

Commit

Permalink
CB-4097 fix: set user role when creation user (#2101)
Browse files Browse the repository at this point in the history
* CB-4097 fix: set user role when creation user

* CB-4097 fix: validate role correctly

* CB-4097 validate team before deleting

* CB-4097 fix: detect auth role correctly

---------

Co-authored-by: Daria Marutkina <[email protected]>
Co-authored-by: Aleksandr Skoblikov <[email protected]>
  • Loading branch information
3 people authored Nov 1, 2023
1 parent 0610759 commit b3c1ceb
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1034,6 +1034,10 @@ public void updateTeam(String teamId, String name, String description) throws DB

@Override
public void deleteTeam(String teamId, boolean force) throws DBCException {
String defaultUsersTeam = application.getAppConfiguration().getDefaultUserTeam();
if (CommonUtils.isNotEmpty(defaultUsersTeam) && defaultUsersTeam.equals(teamId)) {
throw new DBCException("Default users team cannot be deleted");
}
try (Connection dbCon = database.openConnection()) {
if (!force) {
try (PreparedStatement dbStat = dbCon.prepareStatement(
Expand Down
4 changes: 3 additions & 1 deletion webapp/packages/core-authentication/src/UsersResource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export const UsersResourceNewUsers = resourceKeyListAlias('@users-resource/new-u

interface UserCreateOptions {
userId: string;
authRole?: string;
}

@injectable()
Expand Down Expand Up @@ -131,9 +132,10 @@ export class UsersResource extends CachedMapResource<string, AdminUser, UserReso
await this.graphQLService.sdk.saveUserMetaParameters({ userId, parameters });
}

async create({ userId }: UserCreateOptions): Promise<AdminUser> {
async create({ userId, authRole }: UserCreateOptions): Promise<AdminUser> {
const { user } = await this.graphQLService.sdk.createUser({
userId,
authRole,
enabled: false,
...this.getDefaultIncludes(),
...this.getIncludesMap(userId),
Expand Down
8 changes: 6 additions & 2 deletions webapp/packages/core-ui/src/Form/FormState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { action, makeObservable, observable } from 'mobx';
import { dataContextAddDIProvider, type IDataContext, TempDataContext } from '@cloudbeaver/core-data-context';
import type { App } from '@cloudbeaver/core-di';
import type { ENotificationType } from '@cloudbeaver/core-events';
import { Executor, IExecutionContextProvider, type IExecutor } from '@cloudbeaver/core-executor';
import { Executor, ExecutorInterrupter, IExecutionContextProvider, type IExecutor } from '@cloudbeaver/core-executor';
import { isLoadableStateHasException, MetadataMap, uuid } from '@cloudbeaver/core-utils';
import { DATA_CONTEXT_LOADABLE_STATE, loadableStateContext } from '@cloudbeaver/core-view';

Expand Down Expand Up @@ -193,7 +193,11 @@ export class FormState<TState> implements IFormState<TState> {
async save(): Promise<boolean> {
try {
this.isDisabled = true;
await this.submitTask.execute(this);
const context = await this.submitTask.execute(this);

if (ExecutorInterrupter.isInterrupted(context)) {
return false;
}

this.exception = null;
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { UsersResource } from '@cloudbeaver/core-authentication';
import { AuthRolesResource, UsersResource } from '@cloudbeaver/core-authentication';
import { createDataContext, DATA_CONTEXT_DI_PROVIDER } from '@cloudbeaver/core-data-context';
import { ServerConfigResource } from '@cloudbeaver/core-root';
import { DATA_CONTEXT_FORM_STATE } from '@cloudbeaver/core-ui';
Expand All @@ -18,6 +18,7 @@ export const DATA_CONTEXT_USER_FORM_INFO_PART = createDataContext<UserFormInfoPa
const di = context.get(DATA_CONTEXT_DI_PROVIDER);
const usersResource = di.getServiceByClass(UsersResource);
const serverConfigResource = di.getServiceByClass(ServerConfigResource);
const authRolesResource = di.getServiceByClass(AuthRolesResource);

return new UserFormInfoPart(serverConfigResource, form, usersResource);
return new UserFormInfoPart(authRolesResource, serverConfigResource, form, usersResource);
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ export interface IUserFormInfoState {
password: string;
metaParameters: Record<string, any>;
teams: string[];

authRole: string; // used in TE product
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/
import { observable } from 'mobx';

import type { AdminUser, UsersResource } from '@cloudbeaver/core-authentication';
import type { AdminUser, AuthRolesResource, UsersResource } from '@cloudbeaver/core-authentication';
import type { IExecutionContextProvider } from '@cloudbeaver/core-executor';
import { getCachedDataResourceLoaderState } from '@cloudbeaver/core-resource';
import type { ServerConfigResource } from '@cloudbeaver/core-root';
Expand All @@ -23,6 +23,7 @@ const DEFAULT_ENABLED = true;

export class UserFormInfoPart extends FormPart<IUserFormInfoState, IUserFormState> {
constructor(
private readonly authRolesResource: AuthRolesResource,
private readonly serverConfigResource: ServerConfigResource,
formState: AdministrationUserFormState,
private readonly usersResource: UsersResource,
Expand All @@ -33,6 +34,7 @@ export class UserFormInfoPart extends FormPart<IUserFormInfoState, IUserFormStat
password: '',
metaParameters: {},
teams: [],
authRole: '',
});
}

Expand Down Expand Up @@ -66,14 +68,16 @@ export class UserFormInfoPart extends FormPart<IUserFormInfoState, IUserFormStat
!isValuesEqual(this.state.enabled, this.initialState.enabled, null) ||
!isValuesEqual(this.state.password, this.initialState.password, null) ||
!isObjectsEqual(this.state.metaParameters, this.initialState.metaParameters) ||
!isArraysEqual(this.state.teams, this.initialState.teams)
!isArraysEqual(this.state.teams, this.initialState.teams) ||
!isValuesEqual(this.state.authRole, this.initialState.authRole, '')
);
}

protected override async saveChanges(): Promise<void> {
if (this.formState.mode === FormMode.Create) {
const user = await this.usersResource.create({
userId: this.state.userId,
authRole: getTransformedAuthRole(this.state.authRole),
});
this.initialState.userId = user.userId;
this.formState.setMode(FormMode.Edit);
Expand All @@ -84,6 +88,7 @@ export class UserFormInfoPart extends FormPart<IUserFormInfoState, IUserFormStat

await this.updateCredentials();
await this.updateTeams();
await this.updateAuthRole(); // we must update role before enabling user to prevent situation when user current role will reach the limit
await this.updateStatus();
await this.updateMetaParameters();

Expand All @@ -106,11 +111,21 @@ export class UserFormInfoPart extends FormPart<IUserFormInfoState, IUserFormStat
validation.error('authentication_user_password_not_set');
}
}

if (this.authRolesResource.data.length > 0) {
const authRole = getTransformedAuthRole(this.state.authRole);
if (!authRole || !this.authRolesResource.data.includes(authRole)) {
validation.error('authentication_user_role_not_set');
}
}
}
protected override configure() {
const loadableStateContext = this.formState.dataContext.get(DATA_CONTEXT_LOADABLE_STATE);

loadableStateContext.getState('user-info', () => [getCachedDataResourceLoaderState(this.serverConfigResource, undefined)]);
loadableStateContext.getState('user-info', () => [
getCachedDataResourceLoaderState(this.serverConfigResource, undefined),
getCachedDataResourceLoaderState(this.authRolesResource, undefined),
]);
}

private async updateCredentials() {
Expand All @@ -122,6 +137,17 @@ export class UserFormInfoPart extends FormPart<IUserFormInfoState, IUserFormStat
}
}

private async updateAuthRole() {
if (this.state.userId && this.authRolesResource.data.length > 0) {
const authRole = getTransformedAuthRole(this.state.authRole);
const user = this.usersResource.get(this.state.userId);

if (!isValuesEqual(authRole, user?.authRole, '')) {
await this.usersResource.setAuthRole(this.state.userId, authRole, true);
}
}
}

private async updateTeams() {
let grantedTeams: string[] = [];

Expand Down Expand Up @@ -179,10 +205,16 @@ export class UserFormInfoPart extends FormPart<IUserFormInfoState, IUserFormStat

this.setInitialState({
userId: user?.userId || this.formState.state.userId || '',
enabled: user?.enabled || DEFAULT_ENABLED,
enabled: user?.enabled ?? DEFAULT_ENABLED,
metaParameters: observable(user?.metaParameters || {}),
teams: observable(user?.grantedTeams || [serverConfig?.defaultUserTeam].filter(isDefined)),
password: '',

authRole: user?.authRole ?? serverConfig?.defaultAuthRole ?? '',
});
}
}

function getTransformedAuthRole(authRole: string): string {
return authRole.trim();
}

0 comments on commit b3c1ceb

Please sign in to comment.