mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-10-16 14:50:15 +08:00
Compare commits
3
Commits
release_26_0_3
...
24.2.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3154204ad7 | ||
|
|
6122926c1e | ||
|
|
697b76a05b |
+4
@@ -84,14 +84,18 @@ public abstract class BaseWebAppConfiguration implements WebAppConfiguration {
|
||||
return resourceManagerEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFeatureEnabled(String id) {
|
||||
return ArrayUtils.contains(getEnabledFeatures(), id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFeaturesEnabled(String[] features) {
|
||||
return ArrayUtils.containsAll(getEnabledFeatures(), features);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String[] getEnabledFeatures() {
|
||||
if (enabledFeatures == null) {
|
||||
// No config - enable all features (+backward compatibility)
|
||||
|
||||
+5
@@ -44,6 +44,11 @@ public interface WebAppConfiguration {
|
||||
|
||||
boolean isFeatureEnabled(String id);
|
||||
|
||||
@NotNull
|
||||
default String[] getEnabledFeatures() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
default boolean isSupportsCustomConnections() {
|
||||
return true;
|
||||
}
|
||||
|
||||
+1
-1
@@ -142,7 +142,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
|
||||
return result.subList(offset, result.size());
|
||||
}
|
||||
} catch (DBException e) {
|
||||
throw new DBWebException(e);
|
||||
throw new DBWebException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -137,6 +137,9 @@ type UserInfo {
|
||||
configurationParameters: Object!
|
||||
# User teams
|
||||
teams: [UserTeamInfo!]!
|
||||
|
||||
@since(version: "24.2.3")
|
||||
isAnonymous: Boolean!
|
||||
}
|
||||
|
||||
type UserTeamInfo {
|
||||
|
||||
+8
@@ -69,6 +69,9 @@ public class WebUserInfo {
|
||||
|
||||
@Property
|
||||
public List<String> getLinkedAuthProviders() throws DBWebException {
|
||||
if (isAnonymous()) {
|
||||
return List.of();
|
||||
}
|
||||
if (linkedProviders == null) {
|
||||
try {
|
||||
linkedProviders = session.getSecurityController().getCurrentUserLinkedProviders();
|
||||
@@ -104,4 +107,9 @@ public class WebUserInfo {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
|
||||
@Property
|
||||
public boolean isAnonymous() {
|
||||
return session.getUser() == null;
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -21,6 +21,7 @@ import io.cloudbeaver.WebServiceUtils;
|
||||
import io.cloudbeaver.auth.SMSignOutLinkProvider;
|
||||
import io.cloudbeaver.auth.provider.local.LocalAuthProvider;
|
||||
import io.cloudbeaver.model.WebPropertyInfo;
|
||||
import io.cloudbeaver.model.app.WebAppConfiguration;
|
||||
import io.cloudbeaver.model.session.WebAuthInfo;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.model.session.WebSessionAuthProcessor;
|
||||
@@ -189,7 +190,12 @@ public class WebServiceAuthImpl implements DBWServiceAuth {
|
||||
@Override
|
||||
public WebUserInfo activeUser(@NotNull WebSession webSession) throws DBWebException {
|
||||
if (webSession.getUser() == null) {
|
||||
return null;
|
||||
WebAppConfiguration appConfiguration = webSession.getApplication().getAppConfiguration();
|
||||
if (!appConfiguration.isAnonymousAccessEnabled()) {
|
||||
return null;
|
||||
}
|
||||
SMUser anonymous = new SMUser("anonymous", true, null);
|
||||
return new WebUserInfo(webSession, new WebUser(anonymous));
|
||||
}
|
||||
try {
|
||||
// Read user from security controller. It will also read meta parameters
|
||||
|
||||
@@ -16,9 +16,7 @@ import { UserInfoResource } from './UserInfoResource.js';
|
||||
@injectable()
|
||||
export class AppAuthService extends Bootstrap {
|
||||
get authenticated(): boolean {
|
||||
const user = this.userInfoResource.data;
|
||||
|
||||
return this.serverConfigResource.anonymousAccessEnabled || this.serverConfigResource.configurationMode || user !== null;
|
||||
return this.serverConfigResource.configurationMode || this.userInfoResource.hasAccess();
|
||||
}
|
||||
|
||||
get loaders(): ILoadableState[] {
|
||||
@@ -56,9 +54,9 @@ export class AppAuthService extends Bootstrap {
|
||||
throw new Error("Can't configure Authentication");
|
||||
}
|
||||
|
||||
const user = await this.userInfoResource.load();
|
||||
await this.userInfoResource.load();
|
||||
|
||||
return !this.serverConfigResource.configurationMode && !this.serverConfigResource.anonymousAccessEnabled && user === null;
|
||||
return !this.serverConfigResource.configurationMode && !this.userInfoResource.hasAccess();
|
||||
}
|
||||
|
||||
async authUser(): Promise<boolean> {
|
||||
|
||||
@@ -25,10 +25,6 @@ export class AuthInfoService {
|
||||
return this.userInfoResource.data;
|
||||
}
|
||||
|
||||
get isAnonymous(): boolean {
|
||||
return !this.userInfoResource.data;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly userInfoResource: UserInfoResource,
|
||||
private readonly authProvidersResource: AuthProvidersResource,
|
||||
|
||||
@@ -76,6 +76,18 @@ export class UserInfoResource extends CachedDataResource<UserInfo | null, void,
|
||||
});
|
||||
}
|
||||
|
||||
isAnonymous(): this is { data: UserInfo } {
|
||||
return this.data?.isAnonymous === true;
|
||||
}
|
||||
|
||||
isAuthenticated(): this is { data: UserInfo } {
|
||||
return !!this.data && !this.isAnonymous();
|
||||
}
|
||||
|
||||
hasAccess(): this is { data: UserInfo } {
|
||||
return this.isAnonymous() || this.isAuthenticated();
|
||||
}
|
||||
|
||||
isLinked(provideId: string): boolean {
|
||||
return this.data?.linkedAuthProviders.includes(provideId) || false;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
import { computed, makeObservable } from 'mobx';
|
||||
|
||||
import { ANONYMOUS_USER_ID, UserDataService, UserInfoResource } from '@cloudbeaver/core-authentication';
|
||||
import { UserDataService, UserInfoResource } from '@cloudbeaver/core-authentication';
|
||||
import { Dependency, injectable } from '@cloudbeaver/core-di';
|
||||
import { Executor, ExecutorInterrupter, type IExecutor, type ISyncExecutor, SyncExecutor } from '@cloudbeaver/core-executor';
|
||||
import { CachedMapAllKey, resourceKeyList, ResourceKeyUtils } from '@cloudbeaver/core-resource';
|
||||
@@ -35,8 +35,6 @@ export class ProjectsService extends Dependency {
|
||||
|
||||
if (this.userInfoResource.data) {
|
||||
project = this.projectInfoResource.getUserProject(this.userInfoResource.data.userId);
|
||||
} else {
|
||||
project = this.projectInfoResource.get(ANONYMOUS_USER_ID);
|
||||
}
|
||||
|
||||
return project;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
query getActiveUser($includeConfigurationParameters: Boolean!) {
|
||||
user: activeUser {
|
||||
userId
|
||||
isAnonymous
|
||||
displayName
|
||||
authRole
|
||||
linkedAuthProviders
|
||||
|
||||
@@ -65,7 +65,7 @@ export class UserSettingsService extends SettingsSource {
|
||||
}
|
||||
|
||||
async save() {
|
||||
if (this.userInfoResource.data) {
|
||||
if (this.userInfoResource.isAuthenticated()) {
|
||||
await this.userInfoResource.updatePreferences(Object.fromEntries(this.changes));
|
||||
} else {
|
||||
this.update(() => {
|
||||
@@ -94,7 +94,7 @@ export class UserSettingsService extends SettingsSource {
|
||||
|
||||
private refreshConfig() {
|
||||
this.update(() => {
|
||||
if (!this.userInfoResource.data) {
|
||||
if (!this.userInfoResource.isAuthenticated()) {
|
||||
this.clear();
|
||||
this.lastConfig = null;
|
||||
return;
|
||||
@@ -121,7 +121,7 @@ export class UserSettingsService extends SettingsSource {
|
||||
}
|
||||
|
||||
private getSource() {
|
||||
if (this.userInfoResource.data) {
|
||||
if (this.userInfoResource.isAuthenticated()) {
|
||||
return this.settings;
|
||||
}
|
||||
|
||||
|
||||
@@ -83,11 +83,13 @@
|
||||
box-shadow: 0 0 0 50px $input-background-readonly inset;
|
||||
}
|
||||
}
|
||||
&:not([data-select='true'])[disabled] {
|
||||
|
||||
&[disabled] {
|
||||
@include mdc-theme-prop(color, input-color-readonly, false);
|
||||
@include mdc-theme-prop(border-color, input-border-readonly, false);
|
||||
@include mdc-theme-prop(background-color, input-background-readonly, false);
|
||||
pointer-events: all;
|
||||
opacity: 1;
|
||||
&:-internal-autofill-selected,
|
||||
&:-internal-autofill-previewed {
|
||||
box-shadow: 0 0 0 50px $input-background-readonly inset;
|
||||
|
||||
@@ -67,7 +67,7 @@ export class AuthenticationService extends Bootstrap {
|
||||
this.onLogin = new Executor();
|
||||
|
||||
this.onLogout.before(this.navigationService.navigationTask);
|
||||
this.onLogin.before(this.navigationService.navigationTask, undefined, () => authInfoService.isAnonymous);
|
||||
this.onLogin.before(this.navigationService.navigationTask, undefined, () => userInfoResource.isAnonymous());
|
||||
|
||||
this.authPromise = null;
|
||||
this.configureAuthProvider = null;
|
||||
|
||||
@@ -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 { AuthInfoService } from '@cloudbeaver/core-authentication';
|
||||
import { UserInfoResource } from '@cloudbeaver/core-authentication';
|
||||
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
|
||||
import { ServerConfigResource } from '@cloudbeaver/core-root';
|
||||
import { MenuBaseItem, MenuService } from '@cloudbeaver/core-view';
|
||||
@@ -18,7 +18,7 @@ export class PluginBootstrap extends Bootstrap {
|
||||
constructor(
|
||||
private readonly serverConfigResource: ServerConfigResource,
|
||||
private readonly authenticationService: AuthenticationService,
|
||||
private readonly authInfoService: AuthInfoService,
|
||||
private readonly userInfoResource: UserInfoResource,
|
||||
private readonly menuService: MenuService,
|
||||
) {
|
||||
super();
|
||||
@@ -28,7 +28,7 @@ export class PluginBootstrap extends Bootstrap {
|
||||
this.menuService.addCreator({
|
||||
menus: [TOP_NAV_BAR_SETTINGS_MENU],
|
||||
getItems: (context, items) => {
|
||||
if (this.serverConfigResource.enabledAuthProviders.length > 0 && !this.authInfoService.userInfo) {
|
||||
if (this.serverConfigResource.enabledAuthProviders.length > 0 && this.userInfoResource.isAnonymous()) {
|
||||
return [
|
||||
...items,
|
||||
new MenuBaseItem(
|
||||
@@ -42,7 +42,7 @@ export class PluginBootstrap extends Bootstrap {
|
||||
];
|
||||
}
|
||||
|
||||
if (this.authInfoService.userInfo) {
|
||||
if (this.userInfoResource.isAuthenticated()) {
|
||||
return [
|
||||
...items,
|
||||
new MenuBaseItem(
|
||||
|
||||
@@ -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 { AuthInfoService, AuthProviderService } from '@cloudbeaver/core-authentication';
|
||||
import { AuthProviderService, UserInfoResource } from '@cloudbeaver/core-authentication';
|
||||
import { importLazyComponent } from '@cloudbeaver/core-blocks';
|
||||
import {
|
||||
type Connection,
|
||||
@@ -28,7 +28,7 @@ export class ConnectionAuthService extends Dependency {
|
||||
private readonly connectionInfoResource: ConnectionInfoResource,
|
||||
private readonly commonDialogService: CommonDialogService,
|
||||
private readonly authProviderService: AuthProviderService,
|
||||
private readonly authInfoService: AuthInfoService,
|
||||
private readonly userInfoResource: UserInfoResource,
|
||||
private readonly connectionsManagerService: ConnectionsManagerService,
|
||||
private readonly authenticationService: AuthenticationService,
|
||||
) {
|
||||
@@ -41,7 +41,7 @@ export class ConnectionAuthService extends Dependency {
|
||||
connections: connectionInfoResource.values.filter(connection => connection.connected).map(createConnectionParam),
|
||||
state,
|
||||
}),
|
||||
state => state === 'before' && authInfoService.isAnonymous,
|
||||
state => state === 'before' && userInfoResource.isAnonymous(),
|
||||
);
|
||||
this.authenticationService.onLogout.before(
|
||||
connectionsManagerService.onDisconnect,
|
||||
|
||||
@@ -126,7 +126,7 @@ export class ConnectionFoldersBootstrap extends Bootstrap {
|
||||
isActionApplicable: (context, action) => {
|
||||
const tree = context.get(DATA_CONTEXT_ELEMENTS_TREE)!;
|
||||
|
||||
if (action !== ACTION_NEW_FOLDER || !this.userInfoResource.data || tree.baseRoot !== ROOT_NODE_PATH) {
|
||||
if (action !== ACTION_NEW_FOLDER || !this.userInfoResource.isAuthenticated() || tree.baseRoot !== ROOT_NODE_PATH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -60,8 +60,9 @@ export class PublicConnectionFormService {
|
||||
executorHandlerFilter(
|
||||
() => !!this.formState && this.optionsPanelService.isOpen(formGetter),
|
||||
async (event, context) => {
|
||||
if (event === 'before' && this.userInfoResource.data === null) {
|
||||
if (event === 'before' && this.userInfoResource.isAnonymous()) {
|
||||
const confirmed = await this.showUnsavedChangesDialog();
|
||||
|
||||
if (!confirmed) {
|
||||
ExecutorInterrupter.interrupt(context);
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ export class ResourceFoldersBootstrap extends Bootstrap {
|
||||
isActionApplicable: context => {
|
||||
const tree = context.get(DATA_CONTEXT_ELEMENTS_TREE);
|
||||
|
||||
if (!tree?.baseRoot.startsWith(RESOURCES_NODE_PATH) || !this.userInfoResource.data) {
|
||||
if (!tree?.baseRoot.startsWith(RESOURCES_NODE_PATH) || !this.userInfoResource.isAuthenticated()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
import { computed, makeObservable } from 'mobx';
|
||||
|
||||
import { AuthInfoService } from '@cloudbeaver/core-authentication';
|
||||
import { UserInfoResource } from '@cloudbeaver/core-authentication';
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
import type { ProjectInfo } from '@cloudbeaver/core-projects';
|
||||
import { ServerConfigResource } from '@cloudbeaver/core-root';
|
||||
@@ -15,11 +15,11 @@ import { ServerConfigResource } from '@cloudbeaver/core-root';
|
||||
@injectable()
|
||||
export class ResourceManagerService {
|
||||
get enabled() {
|
||||
return !!this.serverConfigResource.data?.resourceManagerEnabled && !!this.authInfoService.userInfo;
|
||||
return !!this.serverConfigResource.data?.resourceManagerEnabled && this.userInfoResource.isAuthenticated();
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly authInfoService: AuthInfoService,
|
||||
private readonly userInfoResource: UserInfoResource,
|
||||
private readonly serverConfigResource: ServerConfigResource,
|
||||
) {
|
||||
makeObservable(this, {
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ export class SessionExpireWarningDialogBootstrap extends Bootstrap {
|
||||
}
|
||||
|
||||
private handleSessionResourceDataUpdate(isValid?: boolean, remainingTime?: number) {
|
||||
if (!this.serverConfigResource.anonymousAccessEnabled && !this.userInfoResource.data && !this.serverConfigResource.configurationMode) {
|
||||
if (!this.serverConfigResource.configurationMode && !this.userInfoResource.hasAccess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
import { observer } from 'mobx-react-lite';
|
||||
|
||||
import { AuthInfoService, DATA_CONTEXT_USER } from '@cloudbeaver/core-authentication';
|
||||
import { DATA_CONTEXT_USER, UserInfoResource } from '@cloudbeaver/core-authentication';
|
||||
import { Icon, Loader, s, useS } from '@cloudbeaver/core-blocks';
|
||||
import { useDataContextLink } from '@cloudbeaver/core-data-context';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
@@ -20,21 +20,20 @@ import style from './UserMenu.module.css';
|
||||
|
||||
export const UserMenu = observer(function UserMenu() {
|
||||
const styles = useS(style);
|
||||
const authInfoService = useService(AuthInfoService);
|
||||
const userInfoResource = useService(UserInfoResource);
|
||||
const menu = useMenu({ menu: MENU_USER_PROFILE });
|
||||
const userInfo = authInfoService.userInfo;
|
||||
|
||||
useDataContextLink(menu.context, (context, id) => {
|
||||
context.set(DATA_CONTEXT_USER, userInfo, id);
|
||||
context.set(DATA_CONTEXT_USER, userInfoResource.data, id);
|
||||
});
|
||||
|
||||
if (!userInfo) {
|
||||
if (!userInfoResource.isAuthenticated()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Loader suspense inline>
|
||||
<UserInfo info={userInfo} />
|
||||
<UserInfo info={userInfoResource.data} />
|
||||
<ContextMenu className={s(styles, { contextMenu: true })} menu={menu} modal>
|
||||
<Icon className={s(styles, { icon: true })} name="angle" viewBox="0 0 15 8" />
|
||||
</ContextMenu>
|
||||
|
||||
@@ -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 { AuthInfoService } from '@cloudbeaver/core-authentication';
|
||||
import { UserInfoResource } from '@cloudbeaver/core-authentication';
|
||||
import { importLazyComponent } from '@cloudbeaver/core-blocks';
|
||||
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
|
||||
|
||||
@@ -17,7 +17,7 @@ const UserProfileFormPanel = importLazyComponent(() => import('./UserProfileForm
|
||||
export class UserProfileFormBootstrap extends Bootstrap {
|
||||
constructor(
|
||||
private readonly userProfileTabsService: UserProfileTabsService,
|
||||
private readonly authInfoService: AuthInfoService,
|
||||
private readonly userInfoResource: UserInfoResource,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@@ -27,7 +27,7 @@ export class UserProfileFormBootstrap extends Bootstrap {
|
||||
key: 'account',
|
||||
name: 'plugin_user_profile_account_title',
|
||||
order: 1,
|
||||
isHidden: () => this.authInfoService.isAnonymous,
|
||||
isHidden: () => this.userInfoResource.isAnonymous(),
|
||||
panel: () => UserProfileFormPanel,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ export class UserProfileOptionsPanelService {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.userInfoResource.data === null) {
|
||||
if (!this.userInfoResource.hasAccess()) {
|
||||
this.close(true);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user