Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CB-4097 fix: set user role when creation user #2101

Merged
merged 8 commits into from
Nov 1, 2023
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();
}
Loading