Skip to content

Commit

Permalink
Merge branch 'devel' into CB-5719-easy-config-part
Browse files Browse the repository at this point in the history
  • Loading branch information
devnaumov authored Sep 27, 2024
2 parents 9af11b8 + 9bc6bd0 commit 0fe35bd
Show file tree
Hide file tree
Showing 50 changed files with 325 additions and 126 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,6 @@
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.file.Path;
import java.security.Permission;
import java.security.Policy;
import java.security.ProtectionDomain;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

Expand Down Expand Up @@ -315,16 +312,6 @@ protected void startServer() {
grantPermissionsToConnections();
}

if (getServerConfiguration().isEnableSecurityManager()) {
Policy.setPolicy(new Policy() {
@Override
public boolean implies(ProtectionDomain domain, Permission permission) {
return true;
}
});
System.setSecurityManager(new SecurityManager());
}

eventController.scheduleCheckJob();

runWebServer();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@
*/
package io.cloudbeaver.service.navigator;

import io.cloudbeaver.WebProjectImpl;
import io.cloudbeaver.WebServiceUtils;
import io.cloudbeaver.model.WebPropertyInfo;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.service.security.SMUtils;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.*;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.model.navigator.DBNDataSource;
import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
import org.jkiss.dbeaver.model.rm.RMProjectPermission;
import org.jkiss.dbeaver.model.struct.*;
import org.jkiss.dbeaver.model.struct.rdb.DBSCatalog;
import org.jkiss.dbeaver.model.struct.rdb.DBSSchema;
Expand Down Expand Up @@ -91,9 +93,22 @@ public WebPropertyInfo[] getProperties() {

@Property
public WebPropertyInfo[] filterProperties(@Nullable WebPropertyFilter filter) {
if (object instanceof DBPDataSourceContainer container && !isDataSourceEditable(container)) {
// If user cannot edit a connection, then return only name
filter = new WebPropertyFilter();
filter.setFeatures(List.of(DBConstants.PROP_FEATURE_NAME));
}
return WebServiceUtils.getObjectFilteredProperties(session, object, filter);
}

private boolean isDataSourceEditable(@NotNull DBPDataSourceContainer container) {
WebProjectImpl project = session.getProjectById(container.getProject().getId());
if (project == null) {
return false;
}
return SMUtils.hasProjectPermission(session, project.getRMProject(), RMProjectPermission.DATA_SOURCES_EDIT);
}

///////////////////////////////////
// Advanced

Expand Down
11 changes: 8 additions & 3 deletions webapp/packages/core-authentication/src/AuthProvidersResource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import {
} from '@cloudbeaver/core-resource';
import { ServerConfigResource } from '@cloudbeaver/core-root';
import { type AuthProviderConfigurationInfoFragment, type AuthProviderInfoFragment, GraphQLService } from '@cloudbeaver/core-sdk';
import { isNotNullDefined } from '@cloudbeaver/core-utils';

import { AuthConfigurationsResource } from './AuthConfigurationsResource.js';
import { AuthSettingsService } from './AuthSettingsService.js';

export type AuthProvider = NonNullable<AuthProviderInfoFragment>;
export type AuthProviderConfiguration = NonNullable<AuthProviderConfigurationInfoFragment>;
Expand All @@ -31,8 +31,13 @@ export class AuthProvidersResource extends CachedMapResource<string, AuthProvide
return this.values.filter(provider => provider.configurable);
}

get enabledConfigurableAuthProviders(): AuthProvider[] {
const enabledProviders = new Set(this.serverConfigResource.data?.enabledAuthProviders);

return this.configurable.filter(provider => enabledProviders.has(provider.id));
}

constructor(
private readonly authSettingsService: AuthSettingsService,
private readonly graphQLService: GraphQLService,
private readonly serverConfigResource: ServerConfigResource,
private readonly authConfigurationsResource: AuthConfigurationsResource,
Expand Down Expand Up @@ -64,7 +69,7 @@ export class AuthProvidersResource extends CachedMapResource<string, AuthProvide
}

getEnabledProviders(): AuthProvider[] {
return this.get(resourceKeyList(this.serverConfigResource.enabledAuthProviders)) as AuthProvider[];
return this.get(resourceKeyList(this.serverConfigResource.enabledAuthProviders)).filter(isNotNullDefined);
}

isEnabled(id: string): boolean {
Expand Down
2 changes: 1 addition & 1 deletion webapp/packages/core-blocks/src/IconButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const IconButton: React.FC<IconButtonProps> = observer(function IconButto
const Button = tag ?? ReakitButton;

return (
<Button {...rest} className={s(styles, { iconButton: true }, className)}>
<Button tabIndex={0} {...rest} className={s(styles, { iconButton: true }, className)}>
<div className={s(styles, { iconBox: true })}>
{img && <StaticImage className={s(styles, { staticImage: true })} icon={name} />}
{!img && <Icon className={s(styles, { icon: true })} name={name} viewBox={viewBox} />}
Expand Down
2 changes: 2 additions & 0 deletions webapp/packages/core-blocks/src/Menu/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ export const Menu = observer<IMenuProps, HTMLButtonElement>(
<MenuButton
key={relativePosition ? 'link' : 'main'}
ref={combinedRef}
tabIndex={0}
className={s(styles, { menuButton: true }, className)}
{...menu}
visible={menuVisible}
Expand Down Expand Up @@ -168,6 +169,7 @@ export const Menu = observer<IMenuProps, HTMLButtonElement>(
<MenuButton
key={relativePosition ? 'link' : 'main'}
ref={combinedRef}
tabIndex={0}
className={s(styles, { menuButton: true }, className)}
{...menu}
visible={menuVisible}
Expand Down
7 changes: 4 additions & 3 deletions webapp/packages/core-blocks/src/TextPlaceholder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,22 @@
* you may not use this file except in compliance with the License.
*/
import { observer } from 'mobx-react-lite';
import type { HTMLAttributes } from 'react';

import { s } from './s.js';
import style from './TextPlaceholder.module.css';
import { useS } from './useS.js';

interface Props {
interface Props extends HTMLAttributes<HTMLDivElement> {
className?: string;
children?: React.ReactNode;
}

export const TextPlaceholder = observer<Props>(function TextPlaceholder({ className, children }) {
export const TextPlaceholder = observer<Props>(function TextPlaceholder({ className, children, ...rest }) {
const styles = useS(style);

return (
<div className={s(styles, { container: true })}>
<div {...rest} className={s(styles, { container: true })}>
<span className={s(styles, { content: true }, className)}>{children}</span>
</div>
);
Expand Down
10 changes: 8 additions & 2 deletions webapp/packages/core-blocks/src/ToolsPanel/ToolsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@
* you may not use this file except in compliance with the License.
*/
import { observer } from 'mobx-react-lite';
import type { HTMLAttributes } from 'react';

import { s } from '../s.js';
import { useS } from '../useS.js';
import style from './ToolsPanel.module.css';

type TType = 'primary' | 'secondary';
interface Props {
interface Props extends HTMLAttributes<HTMLDivElement> {
className?: string;
type?: TType;
center?: boolean;
Expand All @@ -29,8 +30,13 @@ export const ToolsPanel: React.FC<React.PropsWithChildren<Props>> = observer(fun
minHeight,
type = 'primary',
bottomBorder = false,
...rest
}) {
const styles = useS(style);

return <div className={s(styles, { toolsPanel: true, [type]: true, bottomBorder, minHeight, center, rounded }, className)}>{children}</div>;
return (
<div {...rest} className={s(styles, { toolsPanel: true, [type]: true, bottomBorder, minHeight, center, rounded }, className)}>
{children}
</div>
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,11 @@ export const TreeNodeControl = observer<Props & React.HTMLAttributes<HTMLDivElem
return (
<div
ref={ref}
tabIndex={0}
tabIndex={context.selected ? 0 : -1}
title={title}
aria-selected={context.selected}
className={s(styles, { treeNodeControl: true }, className)}
data-tree-node-control
onClick={handleClick}
onMouseDown={handleMouseDown}
onKeyDown={handleEnter}
Expand Down
1 change: 1 addition & 0 deletions webapp/packages/core-blocks/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ export * from './Snackbars/Snackbar.js';
export * from './Snackbars/ActionSnackbar.js';
export * from './Snackbars/ProcessSnackbar.js';
export * from './useUserData.js';
export * from './useListKeyboardNavigation.js';
export * from './useMergeRefs.js';
export * from './usePasswordValidation.js';
export * from './manifest.js';
Expand Down
100 changes: 100 additions & 0 deletions webapp/packages/core-blocks/src/useListKeyboardNavigation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { useEffect, useState } from 'react';

import { EventContext } from '@cloudbeaver/core-events';

export const EventKeyboardNavigationFlag = EventContext.create('useListKeyboardNavigation');

export function useListKeyboardNavigation<T extends HTMLElement>(elementsSelector = '[tabindex]:not(:disabled)'): (obj: T | null) => void {
const [ref, setRef] = useState<T | null>(null);

useEffect(() => {
if (!ref) {
return;
}

const getFocusableElements = () => {
const allFocusableElements = Array.from(ref.querySelectorAll(elementsSelector)) as HTMLElement[];
return allFocusableElements;
};

// Function to reset tabindex on all elements and set it to 0 on aria-selected="true"
const resetTabindex = () => {
const focusableElements = getFocusableElements();
focusableElements.forEach(el => {
if (el.getAttribute('aria-selected') === 'true') {
el.setAttribute('tabindex', '0');
} else {
el.setAttribute('tabindex', '-1');
}
});
};

const handleKeyDown = (e: KeyboardEvent) => {
if (EventContext.has(e, EventKeyboardNavigationFlag) || !['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown'].includes(e.key)) {
return;
}

const focusableElements = getFocusableElements();
let currentIndex = focusableElements.findIndex(el => el === document.activeElement);

if (document.activeElement !== ref && currentIndex === -1) {
return;
}

let newIndex = currentIndex;

EventContext.set(e, EventKeyboardNavigationFlag);
e.preventDefault();

switch (e.key) {
case 'ArrowRight':
case 'ArrowDown':
newIndex = (currentIndex + 1) % focusableElements.length; // Move to next element

break;
case 'ArrowLeft':
case 'ArrowUp':
if (currentIndex === -1) {
currentIndex = 0;
}
newIndex = (currentIndex - 1 + focusableElements.length) % focusableElements.length; // Move to previous element

break;
default:
return;
}

// // Reset all tabindex to -1
focusableElements.forEach(el => el.setAttribute('tabindex', '-1'));

// Set the new element's tabindex to 0 and focus it
const nextElement = focusableElements[newIndex];
nextElement?.setAttribute('tabindex', '0');
nextElement?.focus();
};

const handleFocusOut = (e: FocusEvent) => {
// Check if the focus moved outside the container
if (!ref.contains(e.relatedTarget as Node)) {
resetTabindex();
}
};

ref.addEventListener('keydown', handleKeyDown);
ref.addEventListener('focusout', handleFocusOut);

return () => {
ref.removeEventListener('keydown', handleKeyDown);
ref.removeEventListener('focusout', handleFocusOut);
};
}, [ref, elementsSelector]);

return setRef;
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,7 @@ export class ResourceOffsetPagination<TKey, TMetadata extends ICachedResourceMet
return undefined;
}

const page = this.metadata.get(key as TKey).offsetPage;

if (!page || !isOffsetPageInRange(page, key.options)) {
return undefined;
}

return page;
return this.metadata.get(key as TKey).offsetPage;
}

hasNextPage(key: ResourceAlias<TKey, Readonly<ICachedResourceOffsetPageOptions>>): boolean {
Expand Down
Loading

0 comments on commit 0fe35bd

Please sign in to comment.