diff --git a/server/bundles/io.cloudbeaver.connect.provider.sample/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.connect.provider.sample/META-INF/MANIFEST.MF index 4584ff7e72..bb5b609afd 100644 --- a/server/bundles/io.cloudbeaver.connect.provider.sample/META-INF/MANIFEST.MF +++ b/server/bundles/io.cloudbeaver.connect.provider.sample/META-INF/MANIFEST.MF @@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2 Bundle-Vendor: %Bundle-Vendor Bundle-Name: %Bundle-Name Bundle-SymbolicName: io.cloudbeaver.connect.provider.sample;singleton:=true -Bundle-Version: 1.0.23.qualifier -Bundle-Release-Date: 20210301 +Bundle-Version: 1.0.24.qualifier +Bundle-Release-Date: 20210322 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-ClassPath: . diff --git a/server/bundles/io.cloudbeaver.connect.provider.sample/pom.xml b/server/bundles/io.cloudbeaver.connect.provider.sample/pom.xml index 77cdaff994..ad4d275461 100644 --- a/server/bundles/io.cloudbeaver.connect.provider.sample/pom.xml +++ b/server/bundles/io.cloudbeaver.connect.provider.sample/pom.xml @@ -10,7 +10,7 @@ ../ io.cloudbeaver.connect.provider.sample - 1.0.23-SNAPSHOT + 1.0.24-SNAPSHOT eclipse-plugin diff --git a/server/bundles/io.cloudbeaver.product.ce/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.product.ce/META-INF/MANIFEST.MF index 123687ecf8..805213a842 100644 --- a/server/bundles/io.cloudbeaver.product.ce/META-INF/MANIFEST.MF +++ b/server/bundles/io.cloudbeaver.product.ce/META-INF/MANIFEST.MF @@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2 Bundle-Vendor: %Bundle-Vendor Bundle-Name: %Bundle-Name Bundle-SymbolicName: io.cloudbeaver.product.ce;singleton:=true -Bundle-Version: 20.0.4.qualifier -Bundle-Release-Date: 20210301 +Bundle-Version: 20.0.5.qualifier +Bundle-Release-Date: 20210322 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-ClassPath: . diff --git a/server/bundles/io.cloudbeaver.product.ce/pom.xml b/server/bundles/io.cloudbeaver.product.ce/pom.xml index afe543248e..7ff2fe0dd4 100644 --- a/server/bundles/io.cloudbeaver.product.ce/pom.xml +++ b/server/bundles/io.cloudbeaver.product.ce/pom.xml @@ -10,7 +10,7 @@ ../ io.cloudbeaver.product.ce - 20.0.4-SNAPSHOT + 20.0.5-SNAPSHOT eclipse-plugin diff --git a/server/bundles/io.cloudbeaver.resources.drivers.base/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.resources.drivers.base/META-INF/MANIFEST.MF index afe99dc848..b5d38eb0be 100644 --- a/server/bundles/io.cloudbeaver.resources.drivers.base/META-INF/MANIFEST.MF +++ b/server/bundles/io.cloudbeaver.resources.drivers.base/META-INF/MANIFEST.MF @@ -2,8 +2,8 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Base JDBC drivers Bundle-SymbolicName: io.cloudbeaver.resources.drivers.base;singleton:=true -Bundle-Version: 1.0.20.qualifier -Bundle-Release-Date: 20210301 +Bundle-Version: 1.0.21.qualifier +Bundle-Release-Date: 20210322 Bundle-Vendor: DBeaver Corp Bundle-ActivationPolicy: lazy Automatic-Module-Name: io.cloudbeaver.resources.drivers.base diff --git a/server/bundles/io.cloudbeaver.resources.drivers.base/pom.xml b/server/bundles/io.cloudbeaver.resources.drivers.base/pom.xml index c1e1a6413a..5ce61b1cc1 100644 --- a/server/bundles/io.cloudbeaver.resources.drivers.base/pom.xml +++ b/server/bundles/io.cloudbeaver.resources.drivers.base/pom.xml @@ -9,6 +9,6 @@ ../ io.cloudbeaver.resources.drivers.base - 1.0.20-SNAPSHOT + 1.0.21-SNAPSHOT eclipse-plugin diff --git a/server/bundles/io.cloudbeaver.server/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.server/META-INF/MANIFEST.MF index 961229c43f..2876e771fb 100644 --- a/server/bundles/io.cloudbeaver.server/META-INF/MANIFEST.MF +++ b/server/bundles/io.cloudbeaver.server/META-INF/MANIFEST.MF @@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2 Bundle-Vendor: %Bundle-Vendor Bundle-Name: %Bundle-Name Bundle-SymbolicName: io.cloudbeaver.server;singleton:=true -Bundle-Version: 20.0.4.qualifier -Bundle-Release-Date: 20210301 +Bundle-Version: 20.0.5.qualifier +Bundle-Release-Date: 20210322 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Activator: io.cloudbeaver.server.WebPlatformActivator diff --git a/server/bundles/io.cloudbeaver.server/pom.xml b/server/bundles/io.cloudbeaver.server/pom.xml index 59511cfd2e..1c2775b202 100644 --- a/server/bundles/io.cloudbeaver.server/pom.xml +++ b/server/bundles/io.cloudbeaver.server/pom.xml @@ -10,7 +10,7 @@ ../ io.cloudbeaver.server - 20.0.4-SNAPSHOT + 20.0.5-SNAPSHOT eclipse-plugin diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWAuthProviderExternal.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWAuthProviderExternal.java index a343e7a2af..f781b2e3b0 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWAuthProviderExternal.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWAuthProviderExternal.java @@ -16,6 +16,7 @@ */ package io.cloudbeaver; +import io.cloudbeaver.model.session.WebSession; import io.cloudbeaver.model.user.WebUser; import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; @@ -49,7 +50,8 @@ public interface DBWAuthProviderExternal extend @NotNull DBRProgressMonitor monitor, @NotNull DBWSecurityController securityController, @NotNull Map providerConfig, - @NotNull Map credentials) throws DBException; + @NotNull Map credentials, + @Nullable WebUser activeUser) throws DBException; @Nullable String getUserDisplayName( @@ -60,7 +62,8 @@ public interface DBWAuthProviderExternal extend @Nullable DBPObject getUserDetails( @NotNull DBRProgressMonitor monitor, - AUTH_SESSION session, + @NotNull WebSession webSession, + @NotNull AUTH_SESSION session, @NotNull WebUser user) throws DBException; } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/session/WebSession.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/session/WebSession.java index b61f7267f2..56f74fab8e 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/session/WebSession.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/session/WebSession.java @@ -97,7 +97,8 @@ public class WebSession implements DBASession, DBAAuthCredentialsProvider, IAdap private final Map asyncTasks = new HashMap<>(); private final Map attributes = new HashMap<>(); private final Map> attributeDisposers = new HashMap<>(); - private WebAuthInfo authInfo; + // Map of auth tokens. Key is authentication provdier + private final List authTokens = new ArrayList<>(); private DBNModel navigatorModel; private DBRProgressMonitor progressMonitor = new SessionProgressMonitor(); @@ -477,10 +478,7 @@ public class WebSession implements DBASession, DBAAuthCredentialsProvider, IAdap } catch (Throwable e) { log.error(e); } - if (this.authInfo != null) { - this.authInfo.closeAuth(); - this.authInfo = null; - } + clearAuthTokens(); this.user = null; if (this.sessionProject != null) { @@ -489,6 +487,16 @@ public class WebSession implements DBASession, DBAAuthCredentialsProvider, IAdap } } + private void clearAuthTokens() { + ArrayList tokensCopy; + synchronized (authTokens) { + tokensCopy = new ArrayList<>(this.authTokens); + } + for (WebAuthInfo ai : tokensCopy) { + removeAuthInfo(ai); + } + } + public DBRProgressMonitor getProgressMonitor() { return progressMonitor; } @@ -622,30 +630,69 @@ public class WebSession implements DBASession, DBAAuthCredentialsProvider, IAdap } } - public WebAuthInfo getAuthInfo() { - return authInfo; + public WebAuthInfo getAuthInfo(@Nullable String providerID) { + synchronized (authTokens) { + + if (providerID != null) { + for (WebAuthInfo ai : authTokens) { + if (ai.getAuthProvider().getId().equals(providerID)) { + return ai; + } + } + return null; + } + return authTokens.isEmpty() ? null : authTokens.get(0); + } } - public void setAuthInfo(@Nullable WebAuthInfo authInfo) { - WebUser newUser = authInfo == null ? null : authInfo.getUser(); - if (CommonUtils.equalObjects(this.user, newUser)) { - return; + public List getAllAuthInfo() { + synchronized (authTokens) { + return new ArrayList<>(authTokens); } - forceUserRefresh(newUser); + } - if (this.authInfo != null) { - DBASession oldAuthSession = this.authInfo.getAuthSession(); - if (oldAuthSession != null) { - sessionProject.getSessionContext().removeSession(oldAuthSession); - } - this.authInfo.closeAuth(); + public void addAuthInfo(@NotNull WebAuthInfo authInfo) throws DBException { + WebUser newUser = authInfo.getUser(); + if (this.user == null && newUser != null) { + forceUserRefresh(newUser); + } else if (!CommonUtils.equalObjects(this.user, newUser)) { + throw new DBException("Can't authorize different users in the single session"); } - this.authInfo = authInfo; - if (authInfo != null) { - DBASession authSession = authInfo.getAuthSession(); - if (authSession != null) { - this.sessionProject.getSessionContext().addSession(authSession); - } + + + WebAuthInfo oldAuthInfo = getAuthInfo(authInfo.getAuthProvider().getId()); + if (oldAuthInfo != null) { + removeAuthInfo(oldAuthInfo); + } + + synchronized (authTokens) { + authTokens.add(authInfo); + } + DBASession authSession = authInfo.getAuthSession(); + if (authSession != null) { + this.sessionProject.getSessionContext().addSession(authSession); + } + } + + private void removeAuthInfo(WebAuthInfo oldAuthInfo) { + DBASession oldAuthSession = oldAuthInfo.getAuthSession(); + if (oldAuthSession != null && sessionProject != null) { + sessionProject.getSessionContext().removeSession(oldAuthSession); + } + oldAuthInfo.closeAuth(); + synchronized (authTokens) { + authTokens.remove(oldAuthInfo); + } + } + + public void removeAuthInfo(String providerId) { + if (providerId == null) { + clearAuthTokens(); + } else { + removeAuthInfo(getAuthInfo(providerId)); + } + if (authTokens.isEmpty()) { + forceUserRefresh(null); } } @@ -705,9 +752,13 @@ public class WebSession implements DBASession, DBAAuthCredentialsProvider, IAdap // May be called to extract auth information from session @Override public T getAdapter(Class adapter) { - if (authInfo != null && authInfo.getAuthSession() != null) { - if (adapter.isInstance(authInfo.getAuthSession())) { - return adapter.cast(authInfo.getAuthSession()); + synchronized (authTokens) { + for (WebAuthInfo authInfo : authTokens) { + if (authInfo != null && authInfo.getAuthSession() != null) { + if (adapter.isInstance(authInfo.getAuthSession())) { + return adapter.cast(authInfo.getAuthSession()); + } + } } } return null; diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebUserOriginInfo.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebUserOriginInfo.java index caf9aeeade..b291dcab21 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebUserOriginInfo.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/user/WebUserOriginInfo.java @@ -23,10 +23,12 @@ import io.cloudbeaver.WebServiceUtils; import io.cloudbeaver.auth.provider.local.LocalAuthProvider; import io.cloudbeaver.model.WebObjectOrigin; import io.cloudbeaver.model.WebPropertyInfo; +import io.cloudbeaver.model.session.WebAuthInfo; import io.cloudbeaver.model.session.WebSession; import io.cloudbeaver.registry.WebAuthProviderDescriptor; import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; +import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.DBPObject; import org.jkiss.dbeaver.model.access.DBASession; @@ -91,13 +93,18 @@ public class WebUserOriginInfo implements WebObjectOrigin { return new WebPropertyInfo[0]; } try { - DBASession authSession = session.getAuthInfo().getAuthSession(); + WebAuthInfo authInfo = session.getAuthInfo( + LocalAuthProvider.PROVIDER_ID.equals(authProvider.getId()) ? null : authProvider.getId()); + if (authInfo == null) { + throw new DBException("Session not authorized in auth provider '" + authProvider.getId() + "'"); + } + DBASession authSession = authInfo.getAuthSession(); DBWAuthProvider authProvider = this.authProvider.getInstance(); if (authSession != null && authProvider instanceof DBWAuthProviderExternal) { if (!isValidSessionType(authSession, authProvider)) { return new WebPropertyInfo[0]; } - DBPObject userDetails = ((DBWAuthProviderExternal) authProvider).getUserDetails(session.getProgressMonitor(), authSession, user); + DBPObject userDetails = ((DBWAuthProviderExternal) authProvider).getUserDetails(session.getProgressMonitor(), session, authSession, user); if (userDetails != null) { return WebServiceUtils.getObjectProperties(session, userDetails); } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java index 8e910da128..2deca20080 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/CBApplication.java @@ -224,7 +224,7 @@ public class CBApplication extends BaseApplicationImpl { log.debug("\tContent root: " + new File(contentRoot).getAbsolutePath()); log.debug("\tDrivers storage: " + new File(driversLocation).getAbsolutePath()); //log.debug("\tDrivers root: " + driversLocation); - log.debug("\tProduct details: " + application.getInfoDetails()); + //log.debug("\tProduct details: " + application.getInfoDetails()); log.debug("\tBase port: " + serverPort); log.debug("\tBase URI: " + servicesURI); if (develMode) { diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceBindingBase.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceBindingBase.java index e666cce5a9..547a746ab1 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceBindingBase.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceBindingBase.java @@ -25,6 +25,7 @@ import io.cloudbeaver.model.session.WebSession; import io.cloudbeaver.server.CBApplication; import io.cloudbeaver.server.CBPlatform; import io.cloudbeaver.server.graphql.GraphQLEndpoint; +import org.jkiss.code.NotNull; import org.jkiss.utils.ArrayUtils; import org.jkiss.utils.CommonUtils; @@ -107,8 +108,14 @@ public abstract class WebServiceBindingBase impleme getServletRequest(env)); } + @NotNull protected static WebConnectionInfo getWebConnection(DataFetchingEnvironment env) throws DBWebException { - return getWebSession(env).getWebConnectionInfo(env.getArgument("connectionId")); + return getWebConnection(getWebSession(env), env.getArgument("connectionId")); + } + + @NotNull + public static WebConnectionInfo getWebConnection(WebSession session, String connectionId) throws DBWebException { + return session.getWebConnectionInfo(connectionId); } private class ServiceInvocationHandler implements InvocationHandler { diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceServletBase.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceServletBase.java new file mode 100644 index 0000000000..58f68f9f4a --- /dev/null +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/WebServiceServletBase.java @@ -0,0 +1,46 @@ +package io.cloudbeaver.service; + +import io.cloudbeaver.model.session.WebSession; +import io.cloudbeaver.server.CBApplication; +import io.cloudbeaver.server.CBPlatform; +import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.Log; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public abstract class WebServiceServletBase extends HttpServlet { + + private static final Log log = Log.getLog(WebServiceServletBase.class); + + private final CBApplication application; + + public WebServiceServletBase(CBApplication application) { + this.application = application; + } + + public CBApplication getApplication() { + return application; + } + + @Override + protected final void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + WebSession webSession = CBPlatform.getInstance().getSessionManager().findWebSession(request); + if (webSession == null) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Web session not found"); + return; + } + try { + processServiceRequest(webSession, request, response); + } catch (Exception e) { + log.error(e); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Error processing request: " + e.getMessage()); + } + } + + protected abstract void processServiceRequest(WebSession session, HttpServletRequest request, HttpServletResponse response) throws DBException, IOException; + +} \ No newline at end of file diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLResultServlet.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLResultServlet.java new file mode 100644 index 0000000000..8c8bbbddb9 --- /dev/null +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLResultServlet.java @@ -0,0 +1,72 @@ +package io.cloudbeaver.service.sql; + +import io.cloudbeaver.DBWebException; +import io.cloudbeaver.model.session.WebSession; +import io.cloudbeaver.server.CBApplication; +import io.cloudbeaver.service.WebServiceServletBase; +import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.Log; +import org.jkiss.utils.CommonUtils; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class WebSQLResultServlet extends WebServiceServletBase { + + private static final Log log = Log.getLog(WebSQLResultServlet.class); + + // context-id/result-id/row-number/attribute-name + private static final Pattern URL_PATTERN = Pattern.compile("/?([\\w]+)/([0-9]+)/([0-9]+)/([0-9]+)/(.+)[/\\?]?"); + + private final DBWServiceSQL sqlService; + + public WebSQLResultServlet(CBApplication application, DBWServiceSQL sqlService) { + super(application); + this.sqlService = sqlService; + } + + @Override + protected void processServiceRequest(WebSession session, HttpServletRequest request, HttpServletResponse response) throws DBException, IOException { + + String valuePath = request.getPathInfo(); + if (CommonUtils.isEmpty(valuePath)) { + throw new DBWebException("Result value ID not specified"); + } + Matcher urlMatcher = URL_PATTERN.matcher(valuePath); + if (!urlMatcher.matches()) { + throw new DBWebException("Invalid URI format"); + } + + String connectionId = urlMatcher.group(1); + String contextId = urlMatcher.group(2); + String resultsId = urlMatcher.group(3); + String rowNum = urlMatcher.group(4); + String attrName = urlMatcher.group(5); + + WebSQLProcessor sqlProcessor = WebServiceBindingSQL.getSQLProcessor( + WebServiceBindingSQL.getWebConnection(session, connectionId)); + WebSQLContextInfo sqlContext = WebServiceBindingSQL.getSQLContext(sqlProcessor, contextId); + WebSQLResultsInfo sqlResults = sqlContext.getResults(resultsId); + + + throw new DBWebException("Not implemented yet"); +/* + response.setHeader("Content-Type", processor.getContentType()); + response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); + response.setHeader("Content-Length", String.valueOf(dataFile.length())); + + try (InputStream is = new FileInputStream(dataFile)) { + IOUtils.copyStream(is, response.getOutputStream()); + } + + // TODO: cleanup export files ASAP? + if (false) { + dtConfig.removeTask(taskInfo); + } +*/ + } + +} \ No newline at end of file diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLUtils.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLUtils.java index 9ed47f73be..e78e2e7d23 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLUtils.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLUtils.java @@ -18,6 +18,7 @@ package io.cloudbeaver.service.sql; import io.cloudbeaver.model.session.WebSession; import io.cloudbeaver.server.CBConstants; +import org.jkiss.code.NotNull; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.data.*; import org.jkiss.dbeaver.model.exec.DBCException; @@ -25,14 +26,12 @@ import org.jkiss.dbeaver.model.gis.DBGeometry; import org.jkiss.dbeaver.model.struct.DBSAttributeBase; import org.jkiss.dbeaver.model.struct.DBSTypedObject; import org.jkiss.dbeaver.utils.ContentUtils; +import org.jkiss.dbeaver.utils.GeneralUtils; import org.jkiss.utils.CommonUtils; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.Date; -import java.util.LinkedHashMap; -import java.util.Map; +import java.util.*; /** * Web SQL utils. @@ -41,6 +40,8 @@ public class WebSQLUtils { private static final Log log = Log.getLog(WebSQLUtils.class); + public static final int BINARY_PREVIEW_LENGTH = 255; + public static Object makeWebCellValue(WebSession session, DBSTypedObject type, Object cellValue, WebDataFormat dataFormat) throws DBCException { if (cellValue instanceof Date) { return CBConstants.ISO_DATE_FORMAT.format(cellValue); @@ -51,11 +52,7 @@ public class WebSQLUtils { return null; } else if (dbValue instanceof DBDDocument) { - if (dataFormat != WebDataFormat.document) { - return serializeDocumentValue((DBDDocument) dbValue); - } else { - return serializeDocumentValue(session, (DBDDocument) dbValue); - } + return serializeDocumentValue(session, (DBDDocument) dbValue); } else if (dbValue instanceof DBDComplexValue) { return serializeComplexValue(session, (DBDComplexValue)dbValue, dataFormat); } else if (dbValue instanceof DBGeometry) { @@ -75,18 +72,31 @@ public class WebSQLUtils { for (int i = 0; i < size; i++) { items[i] = makeWebCellValue(session, collection.getComponentType(), collection.getItem(i), dataFormat); } - return items; + + Map map = createMapOfType("collection"); + map.put("value", items); + return map; } else if (value instanceof DBDComposite) { DBDComposite composite = (DBDComposite)value; - Map map = new LinkedHashMap<>(); + Map struct = new LinkedHashMap<>(); for (DBSAttributeBase attr : composite.getAttributes()) { - map.put(attr.getName(), makeWebCellValue(session, attr, composite.getAttributeValue(attr), dataFormat)); + struct.put(attr.getName(), makeWebCellValue(session, attr, composite.getAttributeValue(attr), dataFormat)); } + + Map map = createMapOfType("map"); + map.put("value", struct); return map; } return value.toString(); } + @NotNull + private static Map createMapOfType(String type) { + Map map = new LinkedHashMap<>(); + map.put("$type", type); + return map; + } + private static Map serializeDocumentValue(WebSession session, DBDDocument document) throws DBCException { String documentData; try { @@ -97,7 +107,7 @@ public class WebSQLUtils { throw new DBCException("Error serializing document", e); } - Map map = new LinkedHashMap<>(); + Map map = createMapOfType("document"); map.put("id", CommonUtils.toString(document.getDocumentId())); map.put("contentType", document.getDocumentContentType()); map.put("properties", Collections.emptyMap()); @@ -106,15 +116,34 @@ public class WebSQLUtils { } private static Object serializeContentValue(WebSession session, DBDContent value) throws DBCException { - return ContentUtils.getContentStringValue(session.getProgressMonitor(), value); - } - private static Object serializeDocumentValue(DBDDocument value) { - return value; + Map map = createMapOfType("content"); + if (ContentUtils.isTextContent(value)) { + String stringValue = ContentUtils.getContentStringValue(session.getProgressMonitor(), value); + map.put("text", stringValue); + } else { + map.put("binary", true); + byte[] binaryValue = ContentUtils.getContentBinaryValue(session.getProgressMonitor(), value); + if (binaryValue != null) { + if (binaryValue.length > BINARY_PREVIEW_LENGTH) { + binaryValue = Arrays.copyOf(binaryValue, BINARY_PREVIEW_LENGTH); + } + map.put("text", GeneralUtils.convertToString(binaryValue, 0, binaryValue.length)); + } else { + map.put("text", null); + } + } + map.put("contentType", value.getContentType()); + map.put("contentLength", value.getContentLength()); + return map; } private static Object serializeGeometryValue(DBGeometry value) { - return value.toString(); + Map map = createMapOfType("geometry"); + map.put("srid", value.getSRID()); + map.put("text", value.toString()); + map.put("properties", value.getProperties()); + return map; } } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java index 87d4b7d2d6..652205c81b 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java @@ -20,9 +20,14 @@ import graphql.schema.DataFetchingEnvironment; import io.cloudbeaver.DBWebException; import io.cloudbeaver.model.WebConnectionInfo; import io.cloudbeaver.model.session.WebSession; +import io.cloudbeaver.server.CBApplication; import io.cloudbeaver.service.DBWBindingContext; +import io.cloudbeaver.service.DBWServiceBindingServlet; import io.cloudbeaver.service.WebServiceBindingBase; import io.cloudbeaver.service.sql.impl.WebServiceSQL; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.jkiss.code.NotNull; import org.jkiss.dbeaver.DBException; import org.jkiss.utils.CommonUtils; @@ -34,7 +39,7 @@ import java.util.stream.Collectors; /** * Web service implementation */ -public class WebServiceBindingSQL extends WebServiceBindingBase { +public class WebServiceBindingSQL extends WebServiceBindingBase implements DBWServiceBindingServlet { public WebServiceBindingSQL() { super(DBWServiceSQL.class, new WebServiceSQL(), "schema/service.sql.graphqls"); @@ -109,23 +114,37 @@ public class WebServiceBindingSQL extends WebServiceBindingBase { )); } + @NotNull private WebDataFormat getDataFormat(DataFetchingEnvironment env) { String dataFormat = env.getArgument("dataFormat"); return CommonUtils.valueOf(WebDataFormat.class, dataFormat, WebDataFormat.resultset); } + @NotNull public static WebSQLConfiguration getSQLConfiguration(WebSession webSession) { return webSession.getAttribute("sqlConfiguration", cfg -> new WebSQLConfiguration(), WebSQLConfiguration::dispose); } + @NotNull public static WebSQLProcessor getSQLProcessor(DataFetchingEnvironment env) throws DBWebException { WebConnectionInfo connectionInfo = getWebConnection(env); + return getSQLProcessor(connectionInfo); + } + + @NotNull + public static WebSQLProcessor getSQLProcessor(WebConnectionInfo connectionInfo) throws DBWebException { return getSQLConfiguration(connectionInfo.getSession()).getSQLProcessor(connectionInfo); } + @NotNull public static WebSQLContextInfo getSQLContext(DataFetchingEnvironment env) throws DBWebException { WebSQLProcessor processor = getSQLProcessor(env); String contextId = env.getArgument("contextId"); + return getSQLContext(processor, contextId); + } + + @NotNull + public static WebSQLContextInfo getSQLContext(WebSQLProcessor processor, String contextId) throws DBWebException { WebSQLContextInfo context = processor.getContext(contextId); if (context == null) { throw new DBWebException("SQL context '" + contextId + "' not found"); @@ -133,6 +152,13 @@ public class WebServiceBindingSQL extends WebServiceBindingBase { return context; } + @Override + public void addServlets(CBApplication application, ServletContextHandler servletContextHandler) { + servletContextHandler.addServlet( + new ServletHolder("sqlResultValueViewer", new WebSQLResultServlet(application, getServiceImpl())), + application.getServicesURI() + "sql-result-value/*"); + } + private static class WebSQLConfiguration { private final Map processors = new HashMap<>(); diff --git a/server/bundles/io.cloudbeaver.service.admin/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.service.admin/META-INF/MANIFEST.MF index 3509b87bec..2e032529fd 100644 --- a/server/bundles/io.cloudbeaver.service.admin/META-INF/MANIFEST.MF +++ b/server/bundles/io.cloudbeaver.service.admin/META-INF/MANIFEST.MF @@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2 Bundle-Vendor: %Bundle-Vendor Bundle-Name: %Bundle-Name Bundle-SymbolicName: io.cloudbeaver.service.admin;singleton:=true -Bundle-Version: 1.0.19.qualifier -Bundle-Release-Date: 20210301 +Bundle-Version: 1.0.20.qualifier +Bundle-Release-Date: 20210322 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-ClassPath: . diff --git a/server/bundles/io.cloudbeaver.service.admin/pom.xml b/server/bundles/io.cloudbeaver.service.admin/pom.xml index 78521e4429..0b698c3de0 100644 --- a/server/bundles/io.cloudbeaver.service.admin/pom.xml +++ b/server/bundles/io.cloudbeaver.service.admin/pom.xml @@ -10,7 +10,7 @@ ../ io.cloudbeaver.service.admin - 1.0.19-SNAPSHOT + 1.0.20-SNAPSHOT eclipse-plugin diff --git a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/WebAdminLogsServlet.java b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/WebAdminLogsServlet.java index e2bc27b89c..afc10e854d 100644 --- a/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/WebAdminLogsServlet.java +++ b/server/bundles/io.cloudbeaver.service.admin/src/io/cloudbeaver/service/admin/impl/WebAdminLogsServlet.java @@ -4,15 +4,14 @@ import io.cloudbeaver.DBWConstants; import io.cloudbeaver.DBWebException; import io.cloudbeaver.model.session.WebSession; import io.cloudbeaver.server.CBApplication; -import io.cloudbeaver.server.CBPlatform; +import io.cloudbeaver.service.WebServiceServletBase; +import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.DBConstants; import org.jkiss.dbeaver.utils.GeneralUtils; import org.jkiss.dbeaver.utils.MimeTypes; import org.jkiss.utils.IOUtils; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; @@ -20,55 +19,43 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -public class WebAdminLogsServlet extends HttpServlet { +public class WebAdminLogsServlet extends WebServiceServletBase { private static final Log log = Log.getLog(WebAdminLogsServlet.class); - private final CBApplication application; - public WebAdminLogsServlet(CBApplication application) { - this.application = application; + super(application); } @Override - protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - try { - WebSession webSession = CBPlatform.getInstance().getSessionManager().findWebSession(request); - if (webSession == null) { - throw new DBWebException("No active session"); - } - if (!webSession.hasPermission(DBWConstants.PERMISSION_ADMIN)) { - response.sendError(HttpServletResponse.SC_FORBIDDEN, "Logs accessible for admins only"); - return; - } - - String logId = request.getPathInfo(); - String logFileName = ".log"; - if ("debug".equals(logId)) { - logFileName = DBConstants.DEBUG_LOG_FILE_NAME; - } - if (logFileName.contains("/") || logFileName.contains("\\")) { - throw new DBWebException("Bad log file name"); - } - File logFile = new File(GeneralUtils.getMetadataFolder(), logFileName); - if (!logFile.exists()) { - throw new DBWebException("Log file '" + logFileName + "' not found"); - } - - response.setHeader("Content-Type", MimeTypes.TEXT_PLAIN); - if (logFileName.equals(".log")) { - logFileName = "server.log"; - } - response.setHeader("Content-Disposition", "attachment; filename=\"" + logFileName + "\""); - - try (InputStream is = new FileInputStream(logFile)) { - IOUtils.copyStream(is, response.getOutputStream()); - } - } catch (Exception e) { - log.error(e); - response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Error reading log: " + e.getMessage()); + protected void processServiceRequest(WebSession session, HttpServletRequest request, HttpServletResponse response) throws DBException, IOException { + if (!session.hasPermission(DBWConstants.PERMISSION_ADMIN)) { + response.sendError(HttpServletResponse.SC_FORBIDDEN, "Logs accessible for admins only"); return; } + + String logId = request.getPathInfo(); + String logFileName = ".log"; + if ("debug".equals(logId)) { + logFileName = DBConstants.DEBUG_LOG_FILE_NAME; + } + if (logFileName.contains("/") || logFileName.contains("\\")) { + throw new DBWebException("Bad log file name"); + } + File logFile = new File(GeneralUtils.getMetadataFolder(), logFileName); + if (!logFile.exists()) { + throw new DBWebException("Log file '" + logFileName + "' not found"); + } + + response.setHeader("Content-Type", MimeTypes.TEXT_PLAIN); + if (logFileName.equals(".log")) { + logFileName = "server.log"; + } + response.setHeader("Content-Disposition", "attachment; filename=\"" + logFileName + "\""); + + try (InputStream is = new FileInputStream(logFile)) { + IOUtils.copyStream(is, response.getOutputStream()); + } } } \ No newline at end of file diff --git a/server/bundles/io.cloudbeaver.service.auth/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.service.auth/META-INF/MANIFEST.MF index ba24feafdb..82ca4ac4cd 100644 --- a/server/bundles/io.cloudbeaver.service.auth/META-INF/MANIFEST.MF +++ b/server/bundles/io.cloudbeaver.service.auth/META-INF/MANIFEST.MF @@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2 Bundle-Vendor: %Bundle-Vendor Bundle-Name: %Bundle-Name Bundle-SymbolicName: io.cloudbeaver.service.auth;singleton:=true -Bundle-Version: 1.0.19.qualifier -Bundle-Release-Date: 20210301 +Bundle-Version: 1.0.20.qualifier +Bundle-Release-Date: 20210322 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-ClassPath: . diff --git a/server/bundles/io.cloudbeaver.service.auth/pom.xml b/server/bundles/io.cloudbeaver.service.auth/pom.xml index 899c247313..82e2c67f69 100644 --- a/server/bundles/io.cloudbeaver.service.auth/pom.xml +++ b/server/bundles/io.cloudbeaver.service.auth/pom.xml @@ -10,7 +10,7 @@ ../ io.cloudbeaver.service.auth - 1.0.19-SNAPSHOT + 1.0.20-SNAPSHOT eclipse-plugin diff --git a/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls b/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls index e40e3f0a50..817468382b 100644 --- a/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls +++ b/server/bundles/io.cloudbeaver.service.auth/schema/service.auth.graphqls @@ -30,29 +30,56 @@ type AuthProviderInfo { credentialParameters: [AuthCredentialInfo!]! } -type UserAuthInfo { - # User unique identifier - userId: String! - # Human readable display name. May be null - displayName: String - # Auth provider ID - authProvider: String! +type UserAuthToken { + # Auth provider used for autgorization + provider: ID! + # Authorization time loginTime: DateTime! # Optional login message message: String + # Auth origin origin: ObjectOrigin! } +type UserAuthInfo { + # User unique identifier + userId: String! @deprecated + # Human readable display name. May be null + displayName: String @deprecated + # Auth provider ID + authProvider: String! @deprecated + + loginTime: DateTime! @deprecated + + # Optional login message + message: String @deprecated + + origin: ObjectOrigin! @deprecated +} + +type UserInfo { + # User unique identifier + userId: String! + # Human readable display name. May be null + displayName: String + + authTokens: [UserAuthToken!]! +} + extend type Query { + # Authorize user using specified auth provider + authLogin(provider: ID!, credentials: Object!): UserAuthInfo! + # Logouts user. If provider not specified then all authorizations are revoked from session. + authLogout(provider: ID): Boolean - authLogout: Boolean + activeUser: UserInfo - sessionUser: UserAuthInfo + sessionUser: UserAuthInfo @deprecated authProviders: [AuthProviderInfo!]! diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/DBWServiceAuth.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/DBWServiceAuth.java index 52d42e04a0..e6cea14af4 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/DBWServiceAuth.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/DBWServiceAuth.java @@ -22,6 +22,8 @@ import io.cloudbeaver.WebAction; import io.cloudbeaver.model.session.WebAuthInfo; import io.cloudbeaver.model.session.WebSession; import io.cloudbeaver.model.user.WebAuthProviderInfo; +import org.jkiss.code.NotNull; +import org.jkiss.code.Nullable; import java.util.Map; @@ -31,14 +33,18 @@ import java.util.Map; public interface DBWServiceAuth extends DBWService { @WebAction(requirePermissions = {} ) - WebAuthInfo authLogin(WebSession webSession, String providerId, Map credentials) throws DBWebException; + WebAuthInfo authLogin(@NotNull WebSession webSession, @NotNull String providerId, @NotNull Map credentials) throws DBWebException; @WebAction(requirePermissions = {} ) - void authLogout(WebSession webSession) throws DBWebException; + void authLogout(@NotNull WebSession webSession, @Nullable String providerId) throws DBWebException; @WebAction(requirePermissions = {}) - WebAuthInfo sessionUser(WebSession webSession) throws DBWebException; + WebUserInfo activeUser(@NotNull WebSession webSession) throws DBWebException; + + @Deprecated + @WebAction(requirePermissions = {}) + WebAuthInfo sessionUser(@NotNull WebSession webSession) throws DBWebException; @WebAction(requirePermissions = {}) - WebAuthProviderInfo[] getAuthProviders(WebSession webSession); + WebAuthProviderInfo[] getAuthProviders(@NotNull WebSession webSession); } diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java index 7e412882b7..87a3ba7e9b 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java @@ -40,9 +40,10 @@ public class WebServiceBindingAuth extends WebServiceBindingBase env.getArgument("provider"), env.getArgument("credentials"))) .dataFetcher("authLogout", env -> { - getService(env).authLogout(getWebSession(env)); + getService(env).authLogout(getWebSession(env), env.getArgument("provider")); return true; }) + .dataFetcher("activeUser", env -> getService(env).activeUser(getWebSession(env, false))) .dataFetcher("sessionUser", env -> getService(env).sessionUser(getWebSession(env, false))) .dataFetcher("authProviders", env -> getService(env).getAuthProviders(getWebSession(env))) ; diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebUserInfo.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebUserInfo.java new file mode 100644 index 0000000000..53aec5fc71 --- /dev/null +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebUserInfo.java @@ -0,0 +1,53 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2021 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.cloudbeaver.service.auth; + +import io.cloudbeaver.model.session.WebAuthInfo; +import io.cloudbeaver.model.session.WebSession; +import io.cloudbeaver.model.user.WebUser; +import org.jkiss.dbeaver.Log; + +import java.util.List; + +/** + * WebUserInfo + */ +public class WebUserInfo { + + private static final Log log = Log.getLog(WebUserInfo.class); + + private final WebSession session; + private final WebUser user; + + public WebUserInfo(WebSession session, WebUser user) { + this.session = session; + this.user = user; + } + + public String getUserId() { + return user.getUserId(); + } + + public String getDisplayName() { + return user.getDisplayName(); + } + + public List getAuthTokens() { + return session.getAllAuthInfo(); + } + +} diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java index 4d0b51d82b..18e315e983 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java @@ -26,7 +26,11 @@ import io.cloudbeaver.registry.WebServiceRegistry; import io.cloudbeaver.server.CBApplication; import io.cloudbeaver.server.CBPlatform; import io.cloudbeaver.service.auth.DBWServiceAuth; +import io.cloudbeaver.service.auth.WebUserInfo; +import org.jkiss.code.NotNull; +import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.access.DBASession; import org.jkiss.dbeaver.model.exec.DBCException; import org.jkiss.utils.CommonUtils; @@ -40,8 +44,10 @@ import java.util.Map; */ public class WebServiceAuthImpl implements DBWServiceAuth { + private static final Log log = Log.getLog(WebServiceAuthImpl.class); + @Override - public WebAuthInfo authLogin(WebSession webSession, String providerId, Map authParameters) throws DBWebException { + public WebAuthInfo authLogin(@NotNull WebSession webSession, @NotNull String providerId, @NotNull Map authParameters) throws DBWebException { DBWSecurityController serverController = CBPlatform.getInstance().getApplication().getSecurityController(); if (CommonUtils.isEmpty(providerId)) { @@ -61,12 +67,12 @@ public class WebServiceAuthImpl implements DBWServiceAuth { authParameters = authProviderExternal.readExternalCredentials(webSession.getProgressMonitor(), providerConfig, authParameters); } - WebUser user = null; + WebUser user = webSession.getUser(); String userId = serverController.getUserByCredentials(authProvider, authParameters); if (userId == null) { // User doesn't exist. We can create new user automatically if auth provider supports this if (authProviderExternal != null) { - user = authProviderExternal.registerNewUser(webSession.getProgressMonitor(), serverController, providerConfig, authParameters); + user = authProviderExternal.registerNewUser(webSession.getProgressMonitor(), serverController, providerConfig, authParameters, user); userId = user.getUserId(); } @@ -74,6 +80,10 @@ public class WebServiceAuthImpl implements DBWServiceAuth { throw new DBCException("Invalid user credentials"); } } + if (user != null && !user.getUserId().equals(userId)) { + log.debug("Attempt to authorize user '" + userId + "' while user '" + user.getUserId() + "' already authorized"); + throw new DBCException("You cannot authorize with different users credentials"); + } // Check for auth enabled. Auth is always enabled for admins if (!CBApplication.getInstance().getAppConfiguration().isAuthenticationEnabled()) { @@ -102,7 +112,7 @@ public class WebServiceAuthImpl implements DBWServiceAuth { authInfo.setAuthProvider(authProvider); authInfo.setAuthSession(authSession); authInfo.setMessage("Authenticated with " + authProvider.getLabel() + " provider"); - webSession.setAuthInfo(authInfo); + webSession.addAuthInfo(authInfo); return authInfo; } catch (DBException e) { @@ -111,23 +121,31 @@ public class WebServiceAuthImpl implements DBWServiceAuth { } @Override - public void authLogout(WebSession webSession) throws DBWebException { + public void authLogout(@NotNull WebSession webSession, @Nullable String providerId) throws DBWebException { if (webSession.getUser() == null) { throw new DBWebException("Not logged in"); } - webSession.setAuthInfo(null); + webSession.removeAuthInfo(providerId); } @Override - public WebAuthInfo sessionUser(WebSession webSession) throws DBWebException { + public WebUserInfo activeUser(@NotNull WebSession webSession) throws DBWebException { if (webSession.getUser() == null) { return null; } - return webSession.getAuthInfo(); + return new WebUserInfo(webSession, webSession.getUser()); } @Override - public WebAuthProviderInfo[] getAuthProviders(WebSession webSession) { + public WebAuthInfo sessionUser(@NotNull WebSession webSession) throws DBWebException { + if (webSession.getUser() == null) { + return null; + } + return webSession.getAuthInfo(null); + } + + @Override + public WebAuthProviderInfo[] getAuthProviders(@NotNull WebSession webSession) { return WebServiceRegistry.getInstance().getAuthProviders() .stream().map(WebAuthProviderInfo::new) .toArray(WebAuthProviderInfo[]::new); diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.service.data.transfer/META-INF/MANIFEST.MF index f412b5cc2d..2241e38607 100644 --- a/server/bundles/io.cloudbeaver.service.data.transfer/META-INF/MANIFEST.MF +++ b/server/bundles/io.cloudbeaver.service.data.transfer/META-INF/MANIFEST.MF @@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2 Bundle-Vendor: %Bundle-Vendor Bundle-Name: %Bundle-Name Bundle-SymbolicName: io.cloudbeaver.service.data.transfer;singleton:=true -Bundle-Version: 1.0.20.qualifier -Bundle-Release-Date: 20210301 +Bundle-Version: 1.0.21.qualifier +Bundle-Release-Date: 20210322 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-ClassPath: . diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/pom.xml b/server/bundles/io.cloudbeaver.service.data.transfer/pom.xml index ecb650de81..91009b675d 100644 --- a/server/bundles/io.cloudbeaver.service.data.transfer/pom.xml +++ b/server/bundles/io.cloudbeaver.service.data.transfer/pom.xml @@ -10,7 +10,7 @@ ../ io.cloudbeaver.service.data.transfer - 1.0.20-SNAPSHOT + 1.0.21-SNAPSHOT eclipse-plugin diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferServlet.java b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferServlet.java index ba9d730409..f787740570 100644 --- a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferServlet.java +++ b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferServlet.java @@ -1,18 +1,17 @@ package io.cloudbeaver.service.data.transfer.impl; import io.cloudbeaver.DBWebException; -import io.cloudbeaver.server.CBPlatform; -import io.cloudbeaver.server.CBApplication; import io.cloudbeaver.model.session.WebSession; +import io.cloudbeaver.server.CBApplication; +import io.cloudbeaver.service.WebServiceServletBase; import io.cloudbeaver.service.data.transfer.DBWServiceDataTransfer; +import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.tools.transfer.registry.DataTransferProcessorDescriptor; import org.jkiss.dbeaver.tools.transfer.registry.DataTransferRegistry; import org.jkiss.utils.CommonUtils; import org.jkiss.utils.IOUtils; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; @@ -20,69 +19,56 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -public class WebDataTransferServlet extends HttpServlet { +public class WebDataTransferServlet extends WebServiceServletBase { private static final Log log = Log.getLog(WebDataTransferServlet.class); - private final CBApplication application; private final DBWServiceDataTransfer dtManager; public WebDataTransferServlet(CBApplication application, DBWServiceDataTransfer dtManager) { - this.application = application; + super(application); this.dtManager = dtManager; } @Override - protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - try { - String dataFileId = request.getPathInfo(); - if (CommonUtils.isEmpty(dataFileId)) { - throw new DBWebException("Data ID not specified"); - } - while (dataFileId.startsWith("/")) { - dataFileId = dataFileId.substring(1); - } + protected void processServiceRequest(WebSession session, HttpServletRequest request, HttpServletResponse response) throws DBException, IOException { + String dataFileId = request.getPathInfo(); + if (CommonUtils.isEmpty(dataFileId)) { + throw new DBWebException("Data ID not specified"); + } + while (dataFileId.startsWith("/")) { + dataFileId = dataFileId.substring(1); + } - WebSession webSession = CBPlatform.getInstance().getSessionManager().findWebSession(request); - if (webSession == null) { - throw new DBWebException("No active session"); - } - WebDataTransferSessionConfig dtConfig = WebDataTransferUtils.getSessionDataTransferConfig(webSession); - WebDataTransferTaskConfig taskInfo = dtConfig.getTask(dataFileId); - if (taskInfo == null) { - throw new DBWebException("Session task '" + dataFileId + "' not found"); - } - DataTransferProcessorDescriptor processor = DataTransferRegistry.getInstance().getProcessor(taskInfo.getParameters().getProcessorId()); - if (processor == null) { - throw new DBWebException("Wrong data processor '" + taskInfo.getParameters().getProcessorId() + "'"); - } - String fileName = taskInfo.getExportFileName(); - if (!CommonUtils.isEmpty(fileName)) { - fileName += "." + WebDataTransferUtils.getProcessorFileExtension(processor); - } else { - fileName = taskInfo.getDataFileId(); - } + WebDataTransferSessionConfig dtConfig = WebDataTransferUtils.getSessionDataTransferConfig(session); + WebDataTransferTaskConfig taskInfo = dtConfig.getTask(dataFileId); + if (taskInfo == null) { + throw new DBWebException("Session task '" + dataFileId + "' not found"); + } + DataTransferProcessorDescriptor processor = DataTransferRegistry.getInstance().getProcessor(taskInfo.getParameters().getProcessorId()); + if (processor == null) { + throw new DBWebException("Wrong data processor '" + taskInfo.getParameters().getProcessorId() + "'"); + } + String fileName = taskInfo.getExportFileName(); + if (!CommonUtils.isEmpty(fileName)) { + fileName += "." + WebDataTransferUtils.getProcessorFileExtension(processor); + } else { + fileName = taskInfo.getDataFileId(); + } - File dataFile = taskInfo.getDataFile(); + File dataFile = taskInfo.getDataFile(); - response.setHeader("Content-Type", processor.getContentType()); - response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); - response.setHeader("Content-Length", String.valueOf(dataFile.length())); + response.setHeader("Content-Type", processor.getContentType()); + response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); + response.setHeader("Content-Length", String.valueOf(dataFile.length())); - try (InputStream is = new FileInputStream(dataFile)) { - IOUtils.copyStream(is, response.getOutputStream()); - } + try (InputStream is = new FileInputStream(dataFile)) { + IOUtils.copyStream(is, response.getOutputStream()); + } - // TODO: cleanup export files ASAP? - if (false) { - dtConfig.removeTask(taskInfo); - } - - //response.ok(HttpServletResponse.SC_BAD_REQUEST, "Not implemented"); - } catch (Exception e) { - log.error(e); - response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Error reading data: " + e.getMessage()); - return; + // TODO: cleanup export files ASAP? + if (false) { + dtConfig.removeTask(taskInfo); } } diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferUtils.java b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferUtils.java index 79c88ef71f..40641112cf 100644 --- a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferUtils.java +++ b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferUtils.java @@ -6,9 +6,7 @@ import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor; import org.jkiss.dbeaver.tools.transfer.registry.DataTransferProcessorDescriptor; import org.jkiss.utils.CommonUtils; -import javax.servlet.http.HttpServlet; - -public class WebDataTransferUtils extends HttpServlet { +class WebDataTransferUtils { private static final Log log = Log.getLog(WebDataTransferUtils.class); diff --git a/server/bundles/io.cloudbeaver.service.metadata/META-INF/MANIFEST.MF b/server/bundles/io.cloudbeaver.service.metadata/META-INF/MANIFEST.MF index 549db24974..de411c7e2f 100644 --- a/server/bundles/io.cloudbeaver.service.metadata/META-INF/MANIFEST.MF +++ b/server/bundles/io.cloudbeaver.service.metadata/META-INF/MANIFEST.MF @@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2 Bundle-Vendor: %Bundle-Vendor Bundle-Name: %Bundle-Name Bundle-SymbolicName: io.cloudbeaver.service.metadata;singleton:=true -Bundle-Version: 1.0.23.qualifier -Bundle-Release-Date: 20210301 +Bundle-Version: 1.0.24.qualifier +Bundle-Release-Date: 20210322 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-ClassPath: . diff --git a/server/bundles/io.cloudbeaver.service.metadata/pom.xml b/server/bundles/io.cloudbeaver.service.metadata/pom.xml index 7e8815d452..1a13cfa685 100644 --- a/server/bundles/io.cloudbeaver.service.metadata/pom.xml +++ b/server/bundles/io.cloudbeaver.service.metadata/pom.xml @@ -10,7 +10,7 @@ ../ io.cloudbeaver.service.metadata - 1.0.23-SNAPSHOT + 1.0.24-SNAPSHOT eclipse-plugin diff --git a/server/features/io.cloudbeaver.ce.drivers.feature/feature.xml b/server/features/io.cloudbeaver.ce.drivers.feature/feature.xml index af41c0fac8..1fd636359b 100644 --- a/server/features/io.cloudbeaver.ce.drivers.feature/feature.xml +++ b/server/features/io.cloudbeaver.ce.drivers.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/server/features/io.cloudbeaver.ce.drivers.feature/pom.xml b/server/features/io.cloudbeaver.ce.drivers.feature/pom.xml index 2c1800e07d..dd3880deed 100644 --- a/server/features/io.cloudbeaver.ce.drivers.feature/pom.xml +++ b/server/features/io.cloudbeaver.ce.drivers.feature/pom.xml @@ -9,6 +9,6 @@ ../ io.cloudbeaver.ce.drivers.feature - 1.0.43-SNAPSHOT + 1.0.44-SNAPSHOT eclipse-feature diff --git a/server/features/io.cloudbeaver.product.ce.feature/feature.xml b/server/features/io.cloudbeaver.product.ce.feature/feature.xml index ad22197b8f..3bc4178b47 100644 --- a/server/features/io.cloudbeaver.product.ce.feature/feature.xml +++ b/server/features/io.cloudbeaver.product.ce.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/server/features/io.cloudbeaver.product.ce.feature/pom.xml b/server/features/io.cloudbeaver.product.ce.feature/pom.xml index a9268986cd..1b400dadee 100644 --- a/server/features/io.cloudbeaver.product.ce.feature/pom.xml +++ b/server/features/io.cloudbeaver.product.ce.feature/pom.xml @@ -10,7 +10,7 @@ ../ io.cloudbeaver.product.ce.feature - 20.0.4-SNAPSHOT + 20.0.5-SNAPSHOT eclipse-feature diff --git a/server/features/io.cloudbeaver.sample.feature/feature.xml b/server/features/io.cloudbeaver.sample.feature/feature.xml index 726eadfca5..b1283e5bb8 100644 --- a/server/features/io.cloudbeaver.sample.feature/feature.xml +++ b/server/features/io.cloudbeaver.sample.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/server/features/io.cloudbeaver.sample.feature/pom.xml b/server/features/io.cloudbeaver.sample.feature/pom.xml index 0e1e0ebc17..e12ff598a6 100644 --- a/server/features/io.cloudbeaver.sample.feature/pom.xml +++ b/server/features/io.cloudbeaver.sample.feature/pom.xml @@ -9,6 +9,6 @@ ../ io.cloudbeaver.sample.feature - 1.0.23-SNAPSHOT + 1.0.24-SNAPSHOT eclipse-feature diff --git a/server/features/io.cloudbeaver.server.feature/feature.xml b/server/features/io.cloudbeaver.server.feature/feature.xml index 1eb306241d..f7c355255c 100644 --- a/server/features/io.cloudbeaver.server.feature/feature.xml +++ b/server/features/io.cloudbeaver.server.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/server/features/io.cloudbeaver.server.feature/pom.xml b/server/features/io.cloudbeaver.server.feature/pom.xml index 9abe26a146..8a6c966665 100644 --- a/server/features/io.cloudbeaver.server.feature/pom.xml +++ b/server/features/io.cloudbeaver.server.feature/pom.xml @@ -10,6 +10,6 @@ ../ io.cloudbeaver.server.feature - 20.0.4-SNAPSHOT + 20.0.5-SNAPSHOT eclipse-feature diff --git a/server/pom.xml b/server/pom.xml index a04d099b0e..e0a851729e 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -19,7 +19,7 @@ CloudBeaver CE - 20.0.4 + 20.0.5 diff --git a/server/product/provided-sample/DBeaverWebProvidedSample.product b/server/product/provided-sample/DBeaverWebProvidedSample.product index 538d0515d2..0871c94737 100644 --- a/server/product/provided-sample/DBeaverWebProvidedSample.product +++ b/server/product/provided-sample/DBeaverWebProvidedSample.product @@ -1,7 +1,7 @@ - + diff --git a/server/product/provided-sample/pom.xml b/server/product/provided-sample/pom.xml index 00a643e0f0..fe2cdb58f7 100644 --- a/server/product/provided-sample/pom.xml +++ b/server/product/provided-sample/pom.xml @@ -9,7 +9,7 @@ 1.0.0-SNAPSHOT ../../ - 20.0.4-SNAPSHOT + 20.0.5-SNAPSHOT provided-sample eclipse-repository Cloudbeaver Provided Sample Product diff --git a/server/product/web-server/CloudbeaverServer.product b/server/product/web-server/CloudbeaverServer.product index 6c27e09d11..87d48d544e 100644 --- a/server/product/web-server/CloudbeaverServer.product +++ b/server/product/web-server/CloudbeaverServer.product @@ -2,7 +2,7 @@ diff --git a/server/product/web-server/pom.xml b/server/product/web-server/pom.xml index 282b6bac41..0074d49057 100644 --- a/server/product/web-server/pom.xml +++ b/server/product/web-server/pom.xml @@ -9,7 +9,7 @@ 1.0.0-SNAPSHOT ../../ - 20.0.4-SNAPSHOT + 20.0.5-SNAPSHOT web-server eclipse-repository Cloudbeaver Server Product diff --git a/webapp/package.json b/webapp/package.json index f1d2558599..c0379fba06 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -45,7 +45,7 @@ "reakit": "^1.3.4", "reflect-metadata": "^0.1.13", "reshadow": "^0.0.1-alpha.78", - "typescript": "^4.1.3" + "typescript": "^4.2.3" }, "dependencies": {} } \ No newline at end of file diff --git a/webapp/packages/core-blocks/package.json b/webapp/packages/core-blocks/package.json index 7ab2888983..d09851ae2e 100644 --- a/webapp/packages/core-blocks/package.json +++ b/webapp/packages/core-blocks/package.json @@ -15,7 +15,7 @@ "lint-fix": "eslint ./src/ --ext .ts,.tsx --fix" }, "dependencies": { - "go-split": "^1.x.x", + "go-split": "^2.x.x", "sanitize-html": "^1.x.x", "react-sanitized-html": "^2.x.x" }, diff --git a/webapp/packages/core-blocks/src/Containers/BASE_CONTAINERS_STYLES.ts b/webapp/packages/core-blocks/src/Containers/BASE_CONTAINERS_STYLES.ts index 35aa33a23c..932fc8e0a9 100644 --- a/webapp/packages/core-blocks/src/Containers/BASE_CONTAINERS_STYLES.ts +++ b/webapp/packages/core-blocks/src/Containers/BASE_CONTAINERS_STYLES.ts @@ -13,10 +13,10 @@ import { composes } from '@cloudbeaver/core-theming'; export const BASE_CONTAINERS_STYLES = composes( css` Group { - composes: theme-background-surface from global; + composes: theme-background-surface theme-text-on-surface from global; } ColoredContainer { - composes: theme-background-secondary from global; + composes: theme-background-secondary theme-text-on-secondary from global; } `, css` diff --git a/webapp/packages/core-blocks/src/FormControls/TextareaNew.tsx b/webapp/packages/core-blocks/src/FormControls/TextareaNew.tsx index 9fa6016c80..baa60739bc 100644 --- a/webapp/packages/core-blocks/src/FormControls/TextareaNew.tsx +++ b/webapp/packages/core-blocks/src/FormControls/TextareaNew.tsx @@ -10,7 +10,7 @@ import { observer } from 'mobx-react-lite'; import { useCallback, useContext } from 'react'; import styled, { css, use } from 'reshadow'; -import { useStyles } from '@cloudbeaver/core-theming'; +import { ComponentStyle, useStyles } from '@cloudbeaver/core-theming'; import type { ILayoutSizeProps } from '../Containers/LayoutProps'; import { baseFormControlStylesNew } from './baseFormControlStylesNew'; @@ -20,16 +20,33 @@ const styles = css` textarea { line-height: 19px; } + field[|embedded] { + height: 100%; + display: flex; + flex-direction: column; + + & textarea { + border-radius: 0 !important; + height: 100%; + resize: none !important; + } + } field-label { display: block; padding-bottom: 10px; composes: theme-typography--body1 from global; font-weight: 500; + + &:empty { + display: none; + } } `; -type BaseProps = Omit, 'onChange'> & ILayoutSizeProps & { +type BaseProps = Omit, 'onChange' | 'style'> & ILayoutSizeProps & { mod?: 'surface'; + style?: ComponentStyle; + embedded?: boolean; }; type ControlledProps = BaseProps & { @@ -53,6 +70,7 @@ interface TextareaType { export const TextareaNew: TextareaType = observer(function TextareaNew({ name, + style, value: controlledValue, state, children, @@ -61,6 +79,7 @@ export const TextareaNew: TextareaType = observer(function TextareaNew({ medium, large, mod, + embedded, onChange = () => {}, ...rest }: ControlledProps | ObjectProps) { @@ -80,8 +99,8 @@ export const TextareaNew: TextareaType = observer(function TextareaNew({ const value = state ? state[name] : controlledValue; - return styled(useStyles(baseFormControlStylesNew, styles))( - + return styled(useStyles(baseFormControlStylesNew, styles, style))( + {children}