Skip to content

Commit

Permalink
Merge branch 'devel' into CB-5578-dm-ability-to-remove-custom-certifi…
Browse files Browse the repository at this point in the history
…cate
  • Loading branch information
sergeyteleshev committed Oct 10, 2024
2 parents 727027d + 2e5b71e commit 823d513
Show file tree
Hide file tree
Showing 44 changed files with 228 additions and 213 deletions.
4 changes: 2 additions & 2 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@
"label": "Build CE",
"type": "shell",
"windows": {
"command": "./build-sqlite.bat"
"command": "./build.bat"
},
"osx": {
"command": "./build-sqlite.sh"
"command": "./build.sh"
},
"options": {
"cwd": "${workspaceFolder}/deploy"
Expand Down
7 changes: 4 additions & 3 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ currently being supported with security updates.
| ------- | --------- |
| 22.x | yes |
| 23.x | yes |
| 24.x | yes |

## Reporting a Vulnerability

Please report (suspected) security vulnerabilities to [email protected].
You will receive a response from us within 48 hours.
If the issue is confirmed, we will release a patch as soon as possible depending on complexity but historically within a few days.
Please report (suspected) security vulnerabilities to [email protected].
You will receive a response from us within 48 hours.
If the issue is confirmed, we will release a patch as soon as possible, depending on complexity, but historically, within a few days.
24 changes: 2 additions & 22 deletions config/GlobalConfiguration/.dbeaver/data-sources.json
Original file line number Diff line number Diff line change
@@ -1,24 +1,4 @@
{
"folders": {},
"connections": {
"postgresql-template-1": {
"provider": "postgresql",
"driver": "postgres-jdbc",
"name": "PostgreSQL (Template)",
"save-password": false,
"show-system-objects": false,
"read-only": true,
"template": true,
"configuration": {
"host": "localhost",
"port": "5432",
"database": "postgres",
"url": "jdbc:postgresql://localhost:5432/postgres",
"type": "dev",
"provider-properties": {
"@dbeaver-show-non-default-db@": "false"
}
}
}
}
"folders": {},
"connections": {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public List<BaseProjectImpl> getProjects() {
@Nullable
@Override
public BaseProjectImpl getProject(@NotNull String projectName) {
if (globalProject.getId().equals(projectName)) {
if (globalProject != null && globalProject.getId().equals(projectName)) {
return globalProject;
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,100 +19,162 @@
import io.cloudbeaver.WebSessionGlobalProjectImpl;
import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBPlatform;
import io.cloudbeaver.service.security.SMUtils;
import io.cloudbeaver.utils.WebAppUtils;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.security.SMObjectType;
import org.jkiss.dbeaver.model.security.SMAdminController;
import org.jkiss.dbeaver.model.security.SMObjectPermissionsGrant;
import org.jkiss.dbeaver.model.websocket.event.WSEventType;
import org.jkiss.dbeaver.model.websocket.event.WSProjectUpdateEvent;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
import org.jkiss.dbeaver.model.websocket.event.permissions.WSObjectPermissionEvent;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;

public class WSObjectPermissionUpdatedEventHandler extends WSDefaultEventHandler<WSObjectPermissionEvent> {
private static final Log log = Log.getLog(WSObjectPermissionUpdatedEventHandler.class);

@Override
protected void updateSessionData(@NotNull BaseWebSession activeUserSession, @NotNull WSObjectPermissionEvent event) {
try {
public void handleEvent(@NotNull WSObjectPermissionEvent event) {
String objectId = event.getObjectId();
Consumer<BaseWebSession> runnable = switch (event.getSmObjectType()) {
case project:
yield getUpdateUserProjectsInfoConsumer(event, objectId);
case datasource:
try {
SMAdminController smController = CBApplication.getInstance().getSecurityController();
Set<String> dataSourcePermissions = smController.getObjectPermissionGrants(event.getObjectId(), event.getSmObjectType())
.stream()
.map(SMObjectPermissionsGrant::getSubjectId).collect(Collectors.toSet());
yield getUpdateUserDataSourcesInfoConsumer(event, objectId, dataSourcePermissions);
} catch (DBException e) {
log.error("Error getting permissions for data source " + objectId, e);
yield null;
}
};
if (runnable == null) {
return;
}
log.debug(event.getTopicId() + " event handled");
Collection<BaseWebSession> allSessions = CBPlatform.getInstance().getSessionManager().getAllActiveSessions();
for (var activeUserSession : allSessions) {
if (!isAcceptableInSession(activeUserSession, event)) {
log.debug("Cannot handle %s event '%s' in session %s".formatted(
event.getTopicId(),
event.getId(),
activeUserSession.getSessionId()
));
continue;
}
log.debug("%s event '%s' handled".formatted(event.getTopicId(), event.getId()));
runnable.accept(activeUserSession);
}
}

@NotNull
private Consumer<BaseWebSession> getUpdateUserDataSourcesInfoConsumer(
@NotNull WSObjectPermissionEvent event,
@NotNull String dataSourceId,
@NotNull Set<String> dataSourcePermissions
) {
return (activeUserSession) -> {
// we have accessible data sources only in web session
if (event.getSmObjectType() == SMObjectType.datasource && !(activeUserSession instanceof WebSession)) {
// admins already have access for all shared connections
if (!(activeUserSession instanceof WebSession webSession) || SMUtils.isAdmin(webSession)) {
return;
}
var objectId = event.getObjectId();

boolean isAccessibleNow;
switch (event.getSmObjectType()) {
case project:
if (WSEventType.OBJECT_PERMISSIONS_UPDATED.getEventId().equals(event.getId())) {
var accessibleProjectIds = activeUserSession.getUserContext().getAccessibleProjectIds();
if (accessibleProjectIds.contains(event.getObjectId())) {
return;
}
activeUserSession.addSessionProject(objectId);
activeUserSession.addSessionEvent(
WSProjectUpdateEvent.create(
event.getSessionId(),
event.getUserId(),
objectId
)
);
} else if (WSEventType.OBJECT_PERMISSIONS_DELETED.getEventId().equals(event.getId())) {
activeUserSession.removeSessionProject(objectId);
activeUserSession.addSessionEvent(
WSProjectUpdateEvent.delete(
event.getSessionId(),
event.getUserId(),
objectId
)
);
}
break;
case datasource:
var webSession = (WebSession) activeUserSession;
var dataSources = List.of(objectId);
if (!isAcceptableInSession(webSession, event)) {
return;
}
var user = activeUserSession.getUserContext().getUser();
var userSubjects = new HashSet<>(Set.of(user.getTeams()));
userSubjects.add(user.getUserId());
boolean shouldBeAccessible = dataSourcePermissions.stream().anyMatch(userSubjects::contains);
List<String> dataSources = List.of(dataSourceId);
WebSessionGlobalProjectImpl project = webSession.getGlobalProject();
if (project == null) {
log.error("Project " + WebAppUtils.getGlobalProjectId() +
" is not found in session " + activeUserSession.getSessionId());
return;
}
boolean isAccessibleNow = project.findWebConnectionInfo(dataSourceId) != null;
if (WSEventType.OBJECT_PERMISSIONS_UPDATED.getEventId().equals(event.getId())) {
if (isAccessibleNow || !shouldBeAccessible) {
return;
}
project.addAccessibleConnectionToCache(dataSourceId);
webSession.addSessionEvent(
WSDataSourceEvent.create(
event.getSessionId(),
event.getUserId(),
project.getId(),
dataSources,
WSDataSourceProperty.CONFIGURATION
)
);
} else if (WSEventType.OBJECT_PERMISSIONS_DELETED.getEventId().equals(event.getId())) {
if (!isAccessibleNow || shouldBeAccessible) {
return;
}
project.removeAccessibleConnectionFromCache(dataSourceId);
webSession.addSessionEvent(
WSDataSourceEvent.delete(
event.getSessionId(),
event.getUserId(),
project.getId(),
dataSources,
WSDataSourceProperty.CONFIGURATION
)
);
}
};
}

WebSessionGlobalProjectImpl project = webSession.getGlobalProject();
if (project == null) {
log.error("Project " + WebAppUtils.getGlobalProjectId() +
" is not found in session " + activeUserSession.getSessionId());
@NotNull
private Consumer<BaseWebSession> getUpdateUserProjectsInfoConsumer(
@NotNull WSObjectPermissionEvent event,
@NotNull String projectId
) {
return (activeUserSession) -> {
try {
if (WSEventType.OBJECT_PERMISSIONS_UPDATED.getEventId().equals(event.getId())) {
var accessibleProjectIds = activeUserSession.getUserContext().getAccessibleProjectIds();
if (accessibleProjectIds.contains(event.getObjectId())) {
return;
}
if (WSEventType.OBJECT_PERMISSIONS_UPDATED.getEventId().equals(event.getId())) {
isAccessibleNow = project.findWebConnectionInfo(objectId) != null;
if (isAccessibleNow) {
return;
}
project.addAccessibleConnectionToCache(objectId);
webSession.addSessionEvent(
WSDataSourceEvent.create(
event.getSessionId(),
event.getUserId(),
project.getId(),
dataSources,
WSDataSourceProperty.CONFIGURATION
)
);
} else if (WSEventType.OBJECT_PERMISSIONS_DELETED.getEventId().equals(event.getId())) {
project.removeAccessibleConnectionFromCache(objectId);
webSession.addSessionEvent(
WSDataSourceEvent.delete(
event.getSessionId(),
event.getUserId(),
project.getId(),
dataSources,
WSDataSourceProperty.CONFIGURATION
)
);
}
activeUserSession.addSessionProject(projectId);
activeUserSession.addSessionEvent(
WSProjectUpdateEvent.create(
event.getSessionId(),
event.getUserId(),
projectId
)
);
} else if (WSEventType.OBJECT_PERMISSIONS_DELETED.getEventId().equals(event.getId())) {
activeUserSession.removeSessionProject(projectId);
activeUserSession.addSessionEvent(
WSProjectUpdateEvent.delete(
event.getSessionId(),
event.getUserId(),
projectId
)
);
}
} catch (DBException e) {
log.error("Error on changing permissions for project " +
event.getObjectId() + " in session " + activeUserSession.getSessionId(), e);
}
} catch (DBException e) {
log.error("Error on changing permissions for project " +
event.getObjectId() + " in session " + activeUserSession.getSessionId(), e);
}
};
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,7 @@ private void getObjectFeatures(DBSObject object, List<String> features) {
features.add(OBJECT_FEATURE_OBJECT_CONTAINER);
try {
Class<? extends DBSObject> childType = objectContainer.getPrimaryChildType(null);
Collection<? extends DBSObject> childrenCollection = objectContainer.getChildren(session.getProgressMonitor());
if (DBSTable.class.isAssignableFrom(childType) && childrenCollection != null) {
if (DBSTable.class.isAssignableFrom(childType)) {
features.add(OBJECT_FEATURE_ENTITY_CONTAINER);
}
} catch (Exception e) {
Expand Down
7 changes: 0 additions & 7 deletions webapp/packages/core-blocks/src/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,6 @@ export const Button = observer<ButtonProps>(function Button({
['click'],
);

function handleEnter(event: React.KeyboardEvent<HTMLElement>) {
if (event.key === 'Enter') {
event.currentTarget.click();
}
}

loading = state.loading || loading;

if (loading) {
Expand All @@ -89,7 +83,6 @@ export const Button = observer<ButtonProps>(function Button({
<Button
role="button"
tabIndex={0}
onKeyDown={handleEnter}
{...rest}
type={type}
disabled={disabled}
Expand Down
2 changes: 1 addition & 1 deletion webapp/packages/core-blocks/src/FormControls/Filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export const Filter = observer<ControlledProps | ObjectsProps<any, any>>(functio
autoComplete="off"
className={s(styles, { inputField: true })}
placeholder={placeholder}
disabled={disabled}
readOnly={disabled}
name={name}
value={value}
onChange={handleChange}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,8 @@
align-items: center;
justify-content: center;
}

.input[disabled] + .iconContainer {
cursor: auto;
opacity: 0.8;
}
.input:not(:only-child) {
padding-right: 32px !important;
Expand Down
7 changes: 7 additions & 0 deletions webapp/packages/core-blocks/src/InfoItem.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,15 @@
align-items: center;
flex: 0 0 auto;
}

.iconOrImage {
width: 24px;
height: 24px;
margin-right: 16px;

&.compact {
width: 18px;
height: 18px;
margin-right: 8px;
}
}
5 changes: 3 additions & 2 deletions webapp/packages/core-blocks/src/InfoItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,20 @@ import { useS } from './useS.js';
export interface IInfoItem {
info: TLocalizationToken;
icon?: string;
compact?: boolean;
}

interface Props extends IInfoItem {
className?: string;
}

export const InfoItem = observer<Props>(function InfoItem({ info, icon = '/icons/info_icon.svg', className }) {
export const InfoItem = observer<Props>(function InfoItem({ info, compact, icon = '/icons/info_icon.svg', className }) {
const styles = useS(style);

const translate = useTranslate();
return (
<div className={s(styles, { infoItem: true }, className)}>
<IconOrImage className={s(styles, { iconOrImage: true })} icon={icon} />
<IconOrImage className={s(styles, { iconOrImage: true, compact })} icon={icon} />
{translate(info)}
</div>
);
Expand Down
Loading

0 comments on commit 823d513

Please sign in to comment.