details: https://code.openbravo.com/erp/devel/pi/rev/b59cceedafd3 changeset: 30637:b59cceedafd3 user: Martin Taal <martin.taal <at> openbravo.com> date: Mon Nov 14 12:22:05 2016 +0100 summary: Fixes issue 34486: Support stateless requests Various code changes to prevent http session creation in some cases and set the correct attribute in the http request object to track that a request is stateless.
Summary of changes: - JsonUtils: prevent creation of a session when generating error message - AuthenticationManager: define the stateless parameter and annotation, some common utility methods, prevent setting dbsession record when stateless request, and prevent creating session when clearing the session - DefaultAuthenticationManager: when stateless use the webservice login approach - HttpSecureAppServlet: small change to prevent accidental creation of http session, move common code to method, use it to check license restrictions when doing stateless request - BaseWebServiceServlet: set the stateless request attribute if this is a stateless request - WebServiceServlet: moved code to parent class for re-use and checking if a webservice is stateless diffstat: modules/org.openbravo.service.json/src/org/openbravo/service/json/JsonUtils.java | 29 +++- src/org/openbravo/authentication/AuthenticationManager.java | 65 +++++++++- src/org/openbravo/authentication/basic/DefaultAuthenticationManager.java | 4 + src/org/openbravo/base/secureApp/HttpSecureAppServlet.java | 44 ++++-- src/org/openbravo/service/web/BaseWebServiceServlet.java | 25 +++ src/org/openbravo/service/web/WebServiceServlet.java | 17 +-- 6 files changed, 145 insertions(+), 39 deletions(-) diffs (truncated from 416 to 300 lines): diff -r 285226a2a73b -r b59cceedafd3 modules/org.openbravo.service.json/src/org/openbravo/service/json/JsonUtils.java --- a/modules/org.openbravo.service.json/src/org/openbravo/service/json/JsonUtils.java Mon Nov 14 10:30:16 2016 +0100 +++ b/modules/org.openbravo.service.json/src/org/openbravo/service/json/JsonUtils.java Mon Nov 14 12:22:05 2016 +0100 @@ -31,6 +31,7 @@ import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONObject; import org.hibernate.QueryTimeoutException; +import org.openbravo.authentication.AuthenticationManager; import org.openbravo.base.exception.OBException; import org.openbravo.base.exception.OBSecurityException; import org.openbravo.base.model.Entity; @@ -216,9 +217,19 @@ log.error(e.getMessage(), e); } - final VariablesSecureApp vars = RequestContext.get().getVariablesSecureApp(); - final OBError obError = Utility.translateError(new DalConnectionProvider(), vars, OBContext - .getOBContext().getLanguage().getLanguage(), localThrowable.getMessage()); + OBError obError; + VariablesSecureApp vars = null; + + // in case of stateless then prevent creation of a http session when an error is reported + if (AuthenticationManager.isStatelessRequest(RequestContext.get().getRequest())) { + obError = new OBError(); + obError.setType("Error"); + obError.setMessage(throwable.getMessage()); + } else { + vars = RequestContext.get().getVariablesSecureApp(); + obError = Utility.translateError(new DalConnectionProvider(), vars, OBContext + .getOBContext().getLanguage().getLanguage(), localThrowable.getMessage()); + } if (localThrowable instanceof OBSecurityException) { final JSONObject error = new JSONObject(); @@ -230,10 +241,14 @@ || (localThrowable.getCause() instanceof PSQLException && PG_QUERY_CANCELED .equals(((PSQLException) localThrowable.getCause()).getSQLState()))) { final JSONObject error = new JSONObject(); - error.put( - "message", - Utility.messageBD(new DalConnectionProvider(false), "OBUIAPP_QueryTimeOut", - vars.getLanguage())); + if (vars != null) { + error.put( + "message", + Utility.messageBD(new DalConnectionProvider(false), "OBUIAPP_QueryTimeOut", + vars.getLanguage())); + } else { + error.put("message", "OBUIAPP_QueryTimeOut"); + } error.put("messageType", obError.getType()); error.put("title", obError.getTitle()); jsonResponse.put(JsonConstants.RESPONSE_ERROR, error); diff -r 285226a2a73b -r b59cceedafd3 src/org/openbravo/authentication/AuthenticationManager.java --- a/src/org/openbravo/authentication/AuthenticationManager.java Mon Nov 14 10:30:16 2016 +0100 +++ b/src/org/openbravo/authentication/AuthenticationManager.java Mon Nov 14 12:22:05 2016 +0100 @@ -16,6 +16,10 @@ import java.io.IOException; import java.io.PrintWriter; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -54,6 +58,7 @@ private static final Logger log4j = Logger.getLogger(AuthenticationManager.class); private static final String DEFAULT_AUTH_CLASS = "org.openbravo.authentication.basic.DefaultAuthenticationManager"; + public static final String STATELESS_REQUEST_PARAMETER = "stateless"; private static final String SUCCESS_SESSION_WEB_SERVICE = "WS"; private static final String REJECTED_SESSION_WEB_SERVICE = "WSR"; private static final String SUCCESS_SESSION_CONNECTOR = "WSC"; @@ -65,6 +70,29 @@ protected String username = ""; /** + * Is used to determine if the request is a stateless request. A stateless request does not create + * a httpsession. This also means that no preferences or other information is present. Stateless + * requests should normally be used only for relatively simple logic. + * + * A request is stateless if it has a parameter stateless=true or an attribute stateless with the + * string value "true". + * + * @param request + * @return true if this is a stateless request + */ + public static boolean isStatelessRequest(HttpServletRequest request) { + return "true".equals(request.getParameter(STATELESS_REQUEST_PARAMETER)) + || "true".equals(request.getAttribute(STATELESS_REQUEST_PARAMETER)); + } + + /** + * Returns true if the passed class has the Stateless annotation + */ + public static boolean isStatelessService(Class<?> clz) { + return null != clz.getAnnotation(Stateless.class); + } + + /** * Returns an instance of AuthenticationManager subclass, based on the authentication.class * property in Openbravo.properties */ @@ -142,6 +170,10 @@ final String userId = doAuthenticate(request, response); + if (AuthenticationManager.isStatelessRequest(request)) { + return userId; + } + final VariablesSecureApp vars = new VariablesSecureApp(request, false); if (StringUtils.isEmpty(vars.getSessionValue("#AD_SESSION_ID"))) { setDBSession(request, userId, SUCCESS_SESSION_STANDARD, true); @@ -186,7 +218,10 @@ throws AuthenticationException { final String userId = doWebServiceAuthenticate(request); - final String dbSessionId = setDBSession(request, userId, SUCCESS_SESSION_WEB_SERVICE, false); + String dbSessionId = null; + if (!AuthenticationManager.isStatelessRequest(request)) { + dbSessionId = setDBSession(request, userId, SUCCESS_SESSION_WEB_SERVICE, false); + } return webServicePostAuthenticate(userId, dbSessionId); } @@ -227,15 +262,21 @@ log4j.warn("Number of webservice calls exceeded today."); return userId; case EXCEEDED_MAX_WS_CALLS: - updateDBSession(dbSessionId, false, REJECTED_SESSION_WEB_SERVICE); + if (dbSessionId != null) { + updateDBSession(dbSessionId, false, REJECTED_SESSION_WEB_SERVICE); + } log4j.warn("Cannot use WS, exceeded number of calls"); throw new AuthenticationException("Exceeded maximum number of allowed calls to web services."); case EXPIRED: - updateDBSession(dbSessionId, false, REJECTED_SESSION_WEB_SERVICE); + if (dbSessionId != null) { + updateDBSession(dbSessionId, false, REJECTED_SESSION_WEB_SERVICE); + } log4j.warn("Cannot use WS, license expired"); throw new AuthenticationException("Exceeded maximum number of allowed calls to web services."); case EXPIRED_MODULES: - updateDBSession(dbSessionId, false, REJECTED_SESSION_WEB_SERVICE); + if (dbSessionId != null) { + updateDBSession(dbSessionId, false, REJECTED_SESSION_WEB_SERVICE); + } log4j.warn("Cannot use WS, expired modules"); throw new AuthenticationException("There are expired modules"); } @@ -310,8 +351,10 @@ public final void logout(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - VariablesBase vars = new VariablesBase(request); - vars.clearSession(true); + if (request.getSession(false) != null) { + VariablesBase vars = new VariablesBase(request); + vars.clearSession(true); + } doLogout(request, response); } @@ -452,4 +495,14 @@ } } + /** + * To annotate a certain webservice/service as being stateless, i.e. not creating or keeping a + * http session on the server + * + * @author mtaal + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public static @interface Stateless { + } } diff -r 285226a2a73b -r b59cceedafd3 src/org/openbravo/authentication/basic/DefaultAuthenticationManager.java --- a/src/org/openbravo/authentication/basic/DefaultAuthenticationManager.java Mon Nov 14 10:30:16 2016 +0100 +++ b/src/org/openbravo/authentication/basic/DefaultAuthenticationManager.java Mon Nov 14 12:22:05 2016 +0100 @@ -57,6 +57,10 @@ protected String doAuthenticate(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, ServletException, IOException { + if (request.getSession(false) == null && AuthenticationManager.isStatelessRequest(request)) { + return webServiceAuthenticate(request); + } + final VariablesSecureApp vars = new VariablesSecureApp(request, false); final Boolean resetPassword = Boolean.parseBoolean(vars.getStringParameter("resetPassword")); final String sUserId; diff -r 285226a2a73b -r b59cceedafd3 src/org/openbravo/base/secureApp/HttpSecureAppServlet.java --- a/src/org/openbravo/base/secureApp/HttpSecureAppServlet.java Mon Nov 14 10:30:16 2016 +0100 +++ b/src/org/openbravo/base/secureApp/HttpSecureAppServlet.java Mon Nov 14 12:22:05 2016 +0100 @@ -32,10 +32,9 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import net.sf.jasperreports.engine.JRDataSource; - import org.codehaus.jettison.json.JSONObject; import org.hibernate.criterion.Restrictions; +import org.openbravo.authentication.AuthenticationException; import org.openbravo.authentication.AuthenticationManager; import org.openbravo.base.HttpBaseServlet; import org.openbravo.base.HttpBaseUtils; @@ -72,6 +71,8 @@ import org.openbravo.utils.Replace; import org.openbravo.xmlEngine.XmlDocument; +import net.sf.jasperreports.engine.JRDataSource; + public class HttpSecureAppServlet extends HttpBaseServlet { private static final long serialVersionUID = 1L; protected boolean boolHist = true; @@ -194,7 +195,8 @@ OBContext.setAdminMode(); strUserAuth = m_AuthManager.authenticate(request, response); - if (strUserAuth == null && "Y".equals(request.getSession().getAttribute("forceLogin"))) { + if (strUserAuth == null && request.getSession(false) != null + && "Y".equals(request.getSession().getAttribute("forceLogin"))) { strUserAuth = "0"; variables.loggingIn = "Y"; } @@ -204,6 +206,15 @@ return; } + // if stateless then stop here, the remaining logic uses the httpsession + if (AuthenticationManager.isStatelessRequest(request)) { + if (areThereLicenseRestrictions(null)) { + throw new AuthenticationException("No valid license"); + } + super.serviceInitialized(request, response); + return; + } + variables = new Variables(request); // Rebuild variable, auth-mgr could set the role boolean loggedOK = false; @@ -214,7 +225,8 @@ // log in process is completed, check whether the session in db is still active loggedOK = SeguridadData.loggedOK(this, variables.getDBSession()); if (!loggedOK) { - if ("Y".equals(request.getSession().getAttribute("forceLogin"))) { + if (request.getSession(false) != null + && "Y".equals(request.getSession().getAttribute("forceLogin"))) { variables.loggingIn = "Y"; loggedOK = true; } else { @@ -241,18 +253,10 @@ final VariablesSecureApp vars = new VariablesSecureApp(request, false); boolean onlySystemAdminAvailable = "Y".equals(vars .getSessionValue("onlySystemAdminRoleShouldBeAvailableInErp")); - LicenseRestriction limitation = ActivationKey.getInstance().checkOPSLimitations( - variables.getDBSession()); // We check if there is a Openbravo Professional Subscription restriction in the license, // or if the last rebuild didn't go well. If any of these are true, then the user is // allowed to login only as system administrator - if (limitation == LicenseRestriction.OPS_INSTANCE_NOT_ACTIVE - || limitation == LicenseRestriction.NUMBER_OF_CONCURRENT_USERS_REACHED - || limitation == LicenseRestriction.MODULE_EXPIRED - || limitation == LicenseRestriction.NOT_MATCHED_INSTANCE - || limitation == LicenseRestriction.HB_NOT_ACTIVE - || limitation == LicenseRestriction.ON_DEMAND_OFF_PLATFORM - || limitation == LicenseRestriction.POS_TERMINALS_EXCEEDED || !correctSystemStatus + if (areThereLicenseRestrictions(variables.getDBSession()) || !correctSystemStatus || onlySystemAdminAvailable) { // it is only allowed to log as system administrator strRole = DefaultOptionsData.getDefaultSystemRole(this, strUserAuth); @@ -480,6 +484,20 @@ } } + // We check if there is a Openbravo Professional Subscription restriction in the license, + // or if the last rebuild didn't go well. If any of these are true, then the user is + // allowed to login only as system administrator + private boolean areThereLicenseRestrictions(String sessionId) { + LicenseRestriction limitation = ActivationKey.getInstance().checkOPSLimitations(sessionId); + return limitation == LicenseRestriction.OPS_INSTANCE_NOT_ACTIVE + || limitation == LicenseRestriction.NUMBER_OF_CONCURRENT_USERS_REACHED + || limitation == LicenseRestriction.MODULE_EXPIRED + || limitation == LicenseRestriction.NOT_MATCHED_INSTANCE + || limitation == LicenseRestriction.HB_NOT_ACTIVE + || limitation == LicenseRestriction.ON_DEMAND_OFF_PLATFORM + || limitation == LicenseRestriction.POS_TERMINALS_EXCEEDED; + } + /** * Cheks access passing all the parameters * diff -r 285226a2a73b -r b59cceedafd3 src/org/openbravo/service/web/BaseWebServiceServlet.java --- a/src/org/openbravo/service/web/BaseWebServiceServlet.java Mon Nov 14 10:30:16 2016 +0100 +++ b/src/org/openbravo/service/web/BaseWebServiceServlet.java Mon Nov 14 12:22:05 2016 +0100 @@ -31,8 +31,10 @@ ------------------------------------------------------------------------------ Developer Access Program for Intel Xeon Phi Processors Access to Intel Xeon Phi processor-based developer platforms. With one year of Intel Parallel Studio XE. Training and support from Colfax. Order your platform today. http://sdm.link/xeonphi _______________________________________________ Openbravo-commits mailing list Openbravo-commits@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openbravo-commits