From e3830fef640c36cfb04232de48d7dee32d399b74 Mon Sep 17 00:00:00 2001 From: serge-rider Date: Fri, 1 May 2020 20:13:39 +0300 Subject: [PATCH] #8 Permission check --- .../src/io/cloudbeaver/DBWebException.java | 5 ++ .../DBWebExceptionAccessDenied.java | 34 +++++++++++++ .../src/io/cloudbeaver/WebAction.java | 7 +++ .../cloudbeaver/model/session/WebSession.java | 17 ++++--- .../model/session/WebSessionManager.java | 27 +++------- .../service/WebServiceBindingBase.java | 49 +++++++++++++++++-- .../transfer/impl/WebDataTransferServlet.java | 2 +- 7 files changed, 110 insertions(+), 31 deletions(-) create mode 100644 server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWebExceptionAccessDenied.java diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWebException.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWebException.java index 5335f240ae..5a8fb394c3 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWebException.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWebException.java @@ -57,6 +57,11 @@ public class DBWebException extends DBException implements GraphQLError { super(makeMessage(message, cause), cause); } + public DBWebException(String message, String errorCode, Throwable cause) { + this(message, cause); + this.webErrorCode = errorCode; + } + public DBWebException(Throwable cause, DBPDataSource dataSource) { super(cause, dataSource); } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWebExceptionAccessDenied.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWebExceptionAccessDenied.java new file mode 100644 index 0000000000..b7d62705c0 --- /dev/null +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/DBWebExceptionAccessDenied.java @@ -0,0 +1,34 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2020 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; + +/** + * The activator class controls the plug-in life cycle + */ +public class DBWebExceptionAccessDenied extends DBWebException { + + public static final String ERROR_CODE_ACCESS_DENIED = "accessDenied"; + + public DBWebExceptionAccessDenied(String message) { + super(message, ERROR_CODE_ACCESS_DENIED); + } + + public DBWebExceptionAccessDenied(String message, Throwable cause) { + super(message, ERROR_CODE_ACCESS_DENIED, cause); + + } +} diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/WebAction.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/WebAction.java index 49b7e17f58..8535514c57 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/WebAction.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/WebAction.java @@ -17,9 +17,16 @@ package io.cloudbeaver; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + /** * Object association annotation */ +@Target(value = {ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) public @interface WebAction { String[] requirePermissions() default { DBWConstants.PERMISSION_PUBLIC }; 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 3fcf0454b6..efa77b8025 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 @@ -66,8 +66,8 @@ public class WebSession { private static final AtomicInteger TASK_ID = new AtomicInteger(); - private String id; - private long createTime; + private final String id; + private final long createTime; private long lastAccessTime; private WebUser user; @@ -122,23 +122,26 @@ public class WebSession { } @Property - public String getLastAccessTime() { + public synchronized String getLastAccessTime() { return CBConstants.ISO_DATE_FORMAT.format(lastAccessTime); } - public long getLastAccessTimeMillis() { + public synchronized long getLastAccessTimeMillis() { return lastAccessTime; } - public WebUser getUser() { + public synchronized WebUser getUser() { return user; } - public Set getSessionPermissions() { + public synchronized Set getSessionPermissions() throws DBCException { + if (sessionPermissions == null) { + refreshSessionAuth(); + } return sessionPermissions; } - public void setUser(WebUser user) { + public synchronized void setUser(WebUser user) { if (CommonUtils.equalObjects(this.user, user)) { return; } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/session/WebSessionManager.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/session/WebSessionManager.java index 1e3359187f..fafaaf4c2e 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/session/WebSessionManager.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/model/session/WebSessionManager.java @@ -17,9 +17,9 @@ package io.cloudbeaver.model.session; import io.cloudbeaver.DBWebException; -import io.cloudbeaver.server.CBPlatform; import io.cloudbeaver.model.WebConnectionConfig; import io.cloudbeaver.model.WebConnectionInfo; +import io.cloudbeaver.server.CBPlatform; import org.jkiss.code.NotNull; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.runtime.DBWorkbench; @@ -49,11 +49,6 @@ public class WebSessionManager { public WebSessionManager() { } - public WebSession getWebSession(@NotNull HttpServletRequest request, boolean errorOnNoFound) throws DBWebException { - HttpSession session = getOrCreateHttpSession(request); - return getWebSession(session, true, errorOnNoFound); - } - public boolean closeSession(@NotNull HttpServletRequest request) { HttpSession session = request.getSession(); if (session != null) { @@ -80,20 +75,12 @@ public class WebSessionManager { return getWebSession(request, true); } - private HttpSession getOrCreateHttpSession(@NotNull HttpServletRequest request) { - HttpSession session = request.getSession(); - if (session == null) { - session = request.getSession(true); - log.debug("New session: " + session.getId()); - } - return session; + public WebSession getWebSession(@NotNull HttpServletRequest request, boolean errorOnNoFound) throws DBWebException { + return getWebSession(request, true, errorOnNoFound); } public WebSession getWebSession(HttpServletRequest request, boolean updateInfo, boolean errorOnNoFound) throws DBWebException { - return getWebSession(getOrCreateHttpSession(request), updateInfo, errorOnNoFound); - } - - public WebSession getWebSession(HttpSession httpSession, boolean updateInfo, boolean errorOnNoFound) throws DBWebException { + HttpSession httpSession = request.getSession(true); String sessionId = httpSession.getId(); WebSession webSession; synchronized (sessionMap) { @@ -113,11 +100,11 @@ public class WebSessionManager { } } } - return webSession; + return webSession; } - public WebSession tryGetWebSession(HttpSession httpSession) { - String sessionId = httpSession.getId(); + public WebSession findWebSession(HttpServletRequest request) { + String sessionId = request.getSession().getId(); synchronized (sessionMap) { return sessionMap.get(sessionId); } 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 709a556383..461d059625 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 @@ -21,9 +21,13 @@ import graphql.schema.idl.SchemaParser; import graphql.schema.idl.TypeDefinitionRegistry; import io.cloudbeaver.DBWebException; import io.cloudbeaver.DBWService; +import io.cloudbeaver.DBWebExceptionAccessDenied; +import io.cloudbeaver.WebAction; import io.cloudbeaver.model.WebConnectionInfo; import io.cloudbeaver.model.session.WebSession; import io.cloudbeaver.server.graphql.GraphQLEndpoint; +import org.jkiss.dbeaver.model.exec.DBCException; +import org.jkiss.utils.CommonUtils; import javax.servlet.http.HttpServletRequest; import java.io.IOException; @@ -34,6 +38,7 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.util.Set; /** * Web service implementation @@ -63,7 +68,7 @@ public abstract class WebServiceBindingBase impleme * Creates proxy for permission checks and other general API calls validation/logging. */ protected API_TYPE getService(DataFetchingEnvironment env) { - Object proxyImpl = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{apiInterface}, new ServiceInvocationHandler(serviceImpl)); + Object proxyImpl = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{apiInterface}, new ServiceInvocationHandler(serviceImpl, env)); return apiInterface.cast(proxyImpl); } @@ -89,7 +94,13 @@ public abstract class WebServiceBindingBase impleme } protected static WebSession getWebSession(DataFetchingEnvironment env) throws DBWebException { - return getBindingContext(env).getSessionManager().getWebSession(getServletRequest(env)); + return getBindingContext(env).getSessionManager().getWebSession( + getServletRequest(env)); + } + + protected static WebSession findWebSession(DataFetchingEnvironment env) { + return getBindingContext(env).getSessionManager().findWebSession( + getServletRequest(env)); } protected static WebConnectionInfo getWebConnection(DataFetchingEnvironment env) throws DBWebException { @@ -98,18 +109,50 @@ public abstract class WebServiceBindingBase impleme private class ServiceInvocationHandler implements InvocationHandler { private final API_TYPE impl; + private final DataFetchingEnvironment env; - ServiceInvocationHandler(API_TYPE impl) { + ServiceInvocationHandler(API_TYPE impl, DataFetchingEnvironment env) { this.impl = impl; + this.env = env; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { + WebAction webAction = method.getAnnotation(WebAction.class); + if (webAction != null) { + checkPermissions(webAction); + } return method.invoke(impl, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } } + + private void checkPermissions(WebAction webAction) throws DBWebExceptionAccessDenied { + String[] reqPermissions = webAction.requirePermissions(); + if (reqPermissions.length == 0) { + return; + } + WebSession session = findWebSession(env); + if (session == null) { + throw new DBWebExceptionAccessDenied("Anonymous access restricted"); + } + Set sessionPermissions; + try { + sessionPermissions = session.getSessionPermissions(); + } catch (DBCException e) { + throw new DBWebExceptionAccessDenied("Can't retrieve session permissions", e); + } + if (CommonUtils.isEmpty(sessionPermissions)) { + throw new DBWebExceptionAccessDenied("Anonymous access restricted"); + } + for (String rp : reqPermissions) { + if (!sessionPermissions.contains(rp)) { + throw new DBWebExceptionAccessDenied("Access denied"); + } + } + } + } } 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 23d23b3567..ba9d730409 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 @@ -43,7 +43,7 @@ public class WebDataTransferServlet extends HttpServlet { dataFileId = dataFileId.substring(1); } - WebSession webSession = CBPlatform.getInstance().getSessionManager().tryGetWebSession(request.getSession()); + WebSession webSession = CBPlatform.getInstance().getSessionManager().findWebSession(request); if (webSession == null) { throw new DBWebException("No active session"); }