Search in sources :

Example 1 with Description

use of org.springframework.extensions.webscripts.Description in project records-management by Alfresco.

the class BaseWebScriptUnitTest method getMockedDescription.

/**
 * Helper method to get mocked description class
 *
 * @return  {@link DescriptionExtension}    mocked description class
 */
protected Description getMockedDescription() {
    Description mockedDescription = mock(Description.class);
    doReturn(mock(RequiredCache.class)).when(mockedDescription).getRequiredCache();
    return mockedDescription;
}
Also used : RequiredCache(org.springframework.extensions.webscripts.Description.RequiredCache) Description(org.springframework.extensions.webscripts.Description)

Example 2 with Description

use of org.springframework.extensions.webscripts.Description in project alfresco-remote-api by Alfresco.

the class RepositoryContainer method executeScriptInternal.

protected void executeScriptInternal(WebScriptRequest scriptReq, WebScriptResponse scriptRes, final Authenticator auth) throws IOException {
    final WebScript script = scriptReq.getServiceMatch().getWebScript();
    final Description desc = script.getDescription();
    final boolean debug = logger.isDebugEnabled();
    // Escalate the webscript declared level of authentication to the container required authentication
    // eg. must be guest if MT is enabled unless credentials are empty
    RequiredAuthentication containerRequiredAuthentication = getRequiredAuthentication();
    final RequiredAuthentication required = (desc.getRequiredAuthentication().compareTo(containerRequiredAuthentication) < 0 && !auth.emptyCredentials() ? containerRequiredAuthentication : desc.getRequiredAuthentication());
    final boolean isGuest = scriptReq.isGuest();
    if (required == RequiredAuthentication.none) {
        // TODO revisit - cleared here, in-lieu of WebClient clear
        // AuthenticationUtil.clearCurrentSecurityContext();
        transactionedExecuteAs(script, scriptReq, scriptRes);
    } else if ((required == RequiredAuthentication.user || required == RequiredAuthentication.admin) && isGuest) {
        throw new WebScriptException(HttpServletResponse.SC_UNAUTHORIZED, "Web Script " + desc.getId() + " requires user authentication; however, a guest has attempted access.");
    } else {
        try {
            AuthenticationUtil.pushAuthentication();
            // 
            if (debug) {
                String currentUser = AuthenticationUtil.getFullyAuthenticatedUser();
                logger.debug("Current authentication: " + (currentUser == null ? "unauthenticated" : "authenticated as " + currentUser));
                logger.debug("Authentication required: " + required);
                logger.debug("Guest login requested: " + isGuest);
            }
            // 
            // Apply appropriate authentication to Web Script invocation
            // 
            RetryingTransactionCallback<Boolean> authWork = new RetryingTransactionCallback<Boolean>() {

                public Boolean execute() throws Exception {
                    if (auth == null || auth.authenticate(required, isGuest)) {
                        // Check to see if they supplied HTTP Auth or Ticket as guest, on a script that needs more
                        if (required == RequiredAuthentication.user || required == RequiredAuthentication.admin) {
                            String authenticatedUser = AuthenticationUtil.getFullyAuthenticatedUser();
                            String runAsUser = AuthenticationUtil.getRunAsUser();
                            if ((authenticatedUser == null) || (authenticatedUser.equals(runAsUser) && authorityService.hasGuestAuthority()) || (!authenticatedUser.equals(runAsUser) && authorityService.isGuestAuthority(authenticatedUser))) {
                                throw new WebScriptException(HttpServletResponse.SC_UNAUTHORIZED, "Web Script " + desc.getId() + " requires user authentication; however, a guest has attempted access.");
                            }
                        }
                        // Check to see if they're admin or system on an Admin only script
                        if (required == RequiredAuthentication.admin && !(authorityService.hasAdminAuthority() || AuthenticationUtil.getFullyAuthenticatedUser().equals(AuthenticationUtil.getSystemUserName()))) {
                            throw new WebScriptException(HttpServletResponse.SC_UNAUTHORIZED, "Web Script " + desc.getId() + " requires admin authentication; however, a non-admin has attempted access.");
                        }
                        if (debug) {
                            String currentUser = AuthenticationUtil.getFullyAuthenticatedUser();
                            logger.debug("Authentication: " + (currentUser == null ? "unauthenticated" : "authenticated as " + currentUser));
                        }
                        return true;
                    }
                    return false;
                }
            };
            boolean readOnly = transactionService.isReadOnly();
            boolean requiresNew = !readOnly && AlfrescoTransactionSupport.getTransactionReadState() == TxnReadState.TXN_READ_ONLY;
            if (transactionService.getRetryingTransactionHelper().doInTransaction(authWork, readOnly, requiresNew)) {
                // Execute Web Script if authentication passed
                // The Web Script has its own txn management with potential runAs() user
                transactionedExecuteAs(script, scriptReq, scriptRes);
            } else {
                throw new WebScriptException(HttpServletResponse.SC_UNAUTHORIZED, "Authentication failed for Web Script " + desc.getId());
            }
        } finally {
            // 
            // Reset authentication for current thread
            // 
            AuthenticationUtil.popAuthentication();
            if (debug) {
                String currentUser = AuthenticationUtil.getFullyAuthenticatedUser();
                logger.debug("Authentication reset: " + (currentUser == null ? "unauthenticated" : "authenticated as " + currentUser));
            }
        }
    }
}
Also used : Description(org.springframework.extensions.webscripts.Description) WebScriptException(org.springframework.extensions.webscripts.WebScriptException) RetryingTransactionCallback(org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback) WebScript(org.springframework.extensions.webscripts.WebScript) SocketException(java.net.SocketException) TooBusyException(org.alfresco.repo.transaction.TooBusyException) IOException(java.io.IOException) AlfrescoRuntimeException(org.alfresco.error.AlfrescoRuntimeException) WebScriptException(org.springframework.extensions.webscripts.WebScriptException) RequiredAuthentication(org.springframework.extensions.webscripts.Description.RequiredAuthentication)

Example 3 with Description

use of org.springframework.extensions.webscripts.Description in project alfresco-remote-api by Alfresco.

the class RepositoryContainer method transactionedExecute.

/**
 * Execute script within required level of transaction
 *
 * @param script WebScript
 * @param scriptReq WebScriptRequest
 * @param scriptRes WebScriptResponse
 * @throws IOException
 */
protected void transactionedExecute(final WebScript script, final WebScriptRequest scriptReq, final WebScriptResponse scriptRes) throws IOException {
    try {
        final Description description = script.getDescription();
        if (description.getRequiredTransaction() == RequiredTransaction.none) {
            script.execute(scriptReq, scriptRes);
        } else {
            final BufferedRequest bufferedReq;
            final BufferedResponse bufferedRes;
            RequiredTransactionParameters trxParams = description.getRequiredTransactionParameters();
            if (trxParams.getCapability() == TransactionCapability.readwrite) {
                if (trxParams.getBufferSize() > 0) {
                    if (logger.isDebugEnabled())
                        logger.debug("Creating Transactional Response for ReadWrite transaction; buffersize=" + trxParams.getBufferSize());
                    // create buffered request and response that allow transaction retrying
                    bufferedReq = new BufferedRequest(scriptReq, streamFactory);
                    bufferedRes = new BufferedResponse(scriptRes, trxParams.getBufferSize());
                } else {
                    if (logger.isDebugEnabled())
                        logger.debug("Transactional Response bypassed for ReadWrite - buffersize=0");
                    bufferedReq = null;
                    bufferedRes = null;
                }
            } else {
                bufferedReq = null;
                bufferedRes = null;
            }
            // encapsulate script within transaction
            RetryingTransactionCallback<Object> work = new RetryingTransactionCallback<Object>() {

                public Object execute() throws Exception {
                    try {
                        if (logger.isDebugEnabled())
                            logger.debug("Begin retry transaction block: " + description.getRequiredTransaction() + "," + description.getRequiredTransactionParameters().getCapability());
                        if (bufferedRes == null) {
                            script.execute(scriptReq, scriptRes);
                        } else {
                            // Reset the request and response in case of a transaction retry
                            bufferedReq.reset();
                            bufferedRes.reset();
                            script.execute(bufferedReq, bufferedRes);
                        }
                    } catch (Exception e) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Transaction exception: " + description.getRequiredTransaction() + ": " + e.getMessage());
                            // Note: user transaction shouldn't be null, but just in case inside this exception handler
                            UserTransaction userTrx = RetryingTransactionHelper.getActiveUserTransaction();
                            if (userTrx != null) {
                                logger.debug("Transaction status: " + userTrx.getStatus());
                            }
                        }
                        UserTransaction userTrx = RetryingTransactionHelper.getActiveUserTransaction();
                        if (userTrx != null) {
                            if (userTrx.getStatus() != Status.STATUS_MARKED_ROLLBACK) {
                                if (logger.isDebugEnabled())
                                    logger.debug("Marking web script transaction for rollback");
                                try {
                                    userTrx.setRollbackOnly();
                                } catch (Throwable re) {
                                    if (logger.isDebugEnabled())
                                        logger.debug("Caught and ignoring exception during marking for rollback: " + re.getMessage());
                                }
                            }
                        }
                        // re-throw original exception for retry
                        throw e;
                    } finally {
                        if (logger.isDebugEnabled())
                            logger.debug("End retry transaction block: " + description.getRequiredTransaction() + "," + description.getRequiredTransactionParameters().getCapability());
                    }
                    return null;
                }
            };
            boolean readonly = description.getRequiredTransactionParameters().getCapability() == TransactionCapability.readonly;
            boolean requiresNew = description.getRequiredTransaction() == RequiredTransaction.requiresnew;
            // NOT have any side effects so this scenario as a warning sign something maybe amiss, see ALF-10179.
            if (logger.isDebugEnabled() && !readonly && "GET".equalsIgnoreCase(description.getMethod())) {
                logger.debug("Webscript with URL '" + scriptReq.getURL() + "' is a GET request but it's descriptor has declared a readwrite transaction is required");
            }
            try {
                RetryingTransactionHelper transactionHelper = transactionService.getRetryingTransactionHelper();
                if (script instanceof LoginPost) {
                    // login script requires read-write transaction because of authorization intercepter
                    transactionHelper.setForceWritable(true);
                }
                transactionHelper.doInTransaction(work, readonly, requiresNew);
            } catch (TooBusyException e) {
                // Map TooBusyException to a 503 status code
                throw new WebScriptException(HttpServletResponse.SC_SERVICE_UNAVAILABLE, e.getMessage(), e);
            } finally {
                // Get rid of any temporary files
                if (bufferedReq != null) {
                    bufferedReq.close();
                }
            }
            // Ensure a response is always flushed after successful execution
            if (bufferedRes != null) {
                bufferedRes.writeResponse();
            }
        }
    } catch (IOException ioe) {
        Throwable socketException = ExceptionStackUtil.getCause(ioe, SocketException.class);
        Class<?> clientAbortException = null;
        try {
            clientAbortException = Class.forName("org.apache.catalina.connector.ClientAbortException");
        } catch (ClassNotFoundException e) {
        // do nothing
        }
        // Note: if you need to look for more exceptions in the stack, then create a static array and pass it in
        if ((socketException != null && socketException.getMessage().contains("Broken pipe")) || (clientAbortException != null && ExceptionStackUtil.getCause(ioe, clientAbortException) != null)) {
            if (logger.isDebugEnabled()) {
                logger.warn("Client has cut off communication", ioe);
            } else {
                logger.info("Client has cut off communication");
            }
        } else {
            throw ioe;
        }
    }
}
Also used : UserTransaction(javax.transaction.UserTransaction) SocketException(java.net.SocketException) Description(org.springframework.extensions.webscripts.Description) RetryingTransactionHelper(org.alfresco.repo.transaction.RetryingTransactionHelper) IOException(java.io.IOException) SocketException(java.net.SocketException) TooBusyException(org.alfresco.repo.transaction.TooBusyException) IOException(java.io.IOException) AlfrescoRuntimeException(org.alfresco.error.AlfrescoRuntimeException) WebScriptException(org.springframework.extensions.webscripts.WebScriptException) RequiredTransactionParameters(org.springframework.extensions.webscripts.Description.RequiredTransactionParameters) WebScriptException(org.springframework.extensions.webscripts.WebScriptException) RetryingTransactionCallback(org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback) TooBusyException(org.alfresco.repo.transaction.TooBusyException) LoginPost(org.alfresco.repo.web.scripts.bean.LoginPost)

Example 4 with Description

use of org.springframework.extensions.webscripts.Description in project alfresco-remote-api by Alfresco.

the class XssVulnerabilityTest method testXssVulnerability.

public void testXssVulnerability() throws Throwable {
    webscriptsRegistry.reset();
    final int scriptsSize = webscriptsRegistry.getWebScripts().size();
    int i = 0, successCount = 0, wserrcount = 0, vulnCount = 0;
    LinkedList<String> vulnerabileURLS = new LinkedList<String>();
    for (WebScript ws : webscriptsRegistry.getWebScripts()) {
        if (getLogger().isDebugEnabled()) {
            getLogger().debug("progress: " + ++i + "/" + scriptsSize);
        }
        Description wsDesc = ws.getDescription();
        if (SKIP_WEBSCRIPT_CHECK_ID_SET.contains(wsDesc.getId())) {
            // skip
            continue;
        }
        boolean isMethodCheck = METHODS_TO_CHECK_SET.contains(wsDesc.getMethod());
        boolean isFormatCheck = FORMATS_TO_CHECK_SET.contains(wsDesc.getDefaultFormat());
        if (isMethodCheck && isFormatCheck) {
            for (String malArg : MALICIOUS_ARGS) {
                String[] uris = wsDesc.getURIs();
                for (String uri : uris) {
                    if (isUriSkip(uri)) {
                        continue;
                    }
                    // always parse url because we cannot rely on getArguments():
                    // - sometimes getArguments() returns null although URI has arguments
                    // - sometimes getArguments() returns set of args that does not contain args from url
                    List<String> parsedArgs = parseArgsFromURI(uri);
                    if (0 == parsedArgs.size()) {
                        // no arguments in uri, skip
                        continue;
                    }
                    String url = substituteMaliciousArgInURI(uri, parsedArgs, malArg);
                    Response resp;
                    try {
                        resp = sendRequest(createRequest(wsDesc.getMethod(), url), -1);
                    } catch (WebScriptException e) {
                        // skip webscript errors
                        ++wserrcount;
                        continue;
                    }
                    String respString = resp.getContentAsString();
                    if (resp.getStatus() == Status.STATUS_OK) {
                        ++successCount;
                    }
                    // do case insensitive check because argument can be converted to lowercase on page
                    if (respString.toLowerCase().contains(malArg.toLowerCase())) {
                        vulnerabileURLS.add(wsDesc.getMethod() + " " + url);
                        vulnCount++;
                    }
                }
            }
        }
    }
    if (getLogger().isDebugEnabled()) {
        getLogger().debug("OK html responses count: " + successCount);
        getLogger().debug("Webscript errors count: " + wserrcount);
        getLogger().debug("Vulnerabile URLs count: " + vulnCount);
    }
    for (String url : vulnerabileURLS) {
        getLogger().warn("Vulnerabile URL: " + url);
    }
    assertTrue("Vulnerabile URLs found: " + vulnerabileURLS, vulnerabileURLS.size() == 0);
}
Also used : Response(org.springframework.extensions.webscripts.TestWebScriptServer.Response) Description(org.springframework.extensions.webscripts.Description) WebScriptException(org.springframework.extensions.webscripts.WebScriptException) WebScript(org.springframework.extensions.webscripts.WebScript) LinkedList(java.util.LinkedList)

Example 5 with Description

use of org.springframework.extensions.webscripts.Description in project records-management by Alfresco.

the class BaseWebScriptUnitTest method getMockedContainer.

/**
 * Helper method to get mocked container object.
 *
 * @param template              classpath location of webscripts ftl template
 * @return {@link Container}    mocked container
 */
protected Container getMockedContainer(String template) throws Exception {
    FormatRegistry mockedFormatRegistry = mock(FormatRegistry.class);
    doReturn("application/json").when(mockedFormatRegistry).getMimeType(anyString(), anyString());
    ScriptProcessorRegistry mockedScriptProcessorRegistry = mock(ScriptProcessorRegistry.class);
    doReturn(null).when(mockedScriptProcessorRegistry).findValidScriptPath(anyString());
    TemplateProcessorRegistry mockedTemplateProcessorRegistry = mock(TemplateProcessorRegistry.class);
    doReturn(template).when(mockedTemplateProcessorRegistry).findValidTemplatePath(anyString());
    FTLTemplateProcessor ftlTemplateProcessor = new FTLTemplateProcessor() {

        @Override
        protected TemplateLoader getTemplateLoader() {
            return new ClassTemplateLoader(getClass(), "/");
        }
    };
    ftlTemplateProcessor.init();
    doReturn(ftlTemplateProcessor).when(mockedTemplateProcessorRegistry).getTemplateProcessor(anyString());
    Container mockedContainer = mock(Container.class);
    doReturn(mockedFormatRegistry).when(mockedContainer).getFormatRegistry();
    doReturn(mockedScriptProcessorRegistry).when(mockedContainer).getScriptProcessorRegistry();
    doReturn(mockedTemplateProcessorRegistry).when(mockedContainer).getTemplateProcessorRegistry();
    Map<String, Object> containerTemplateParameters = new HashMap<>(5);
    containerTemplateParameters.put("jsonUtils", new JSONUtils());
    containerTemplateParameters.put("people", getMockedPeopleObject());
    doReturn(containerTemplateParameters).when(mockedContainer).getTemplateParameters();
    SearchPath mockedSearchPath = mock(SearchPath.class);
    doReturn(false).when(mockedSearchPath).hasDocument(anyString());
    doReturn(mockedSearchPath).when(mockedContainer).getSearchPath();
    // setup description
    Description mockDescription = mock(Description.class);
    doReturn(mock(RequiredCache.class)).when(mockDescription).getRequiredCache();
    return mockedContainer;
}
Also used : ScriptProcessorRegistry(org.springframework.extensions.webscripts.ScriptProcessorRegistry) FTLTemplateProcessor(org.springframework.extensions.webscripts.processor.FTLTemplateProcessor) RequiredCache(org.springframework.extensions.webscripts.Description.RequiredCache) Description(org.springframework.extensions.webscripts.Description) HashMap(java.util.HashMap) ClassTemplateLoader(freemarker.cache.ClassTemplateLoader) FormatRegistry(org.springframework.extensions.webscripts.FormatRegistry) Matchers.anyString(org.mockito.Matchers.anyString) Container(org.springframework.extensions.webscripts.Container) JSONObject(org.json.JSONObject) SearchPath(org.springframework.extensions.webscripts.SearchPath) TemplateProcessorRegistry(org.springframework.extensions.webscripts.TemplateProcessorRegistry) JSONUtils(org.springframework.extensions.webscripts.json.JSONUtils)

Aggregations

Description (org.springframework.extensions.webscripts.Description)5 WebScriptException (org.springframework.extensions.webscripts.WebScriptException)3 IOException (java.io.IOException)2 SocketException (java.net.SocketException)2 AlfrescoRuntimeException (org.alfresco.error.AlfrescoRuntimeException)2 RetryingTransactionCallback (org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback)2 TooBusyException (org.alfresco.repo.transaction.TooBusyException)2 RequiredCache (org.springframework.extensions.webscripts.Description.RequiredCache)2 WebScript (org.springframework.extensions.webscripts.WebScript)2 ClassTemplateLoader (freemarker.cache.ClassTemplateLoader)1 HashMap (java.util.HashMap)1 LinkedList (java.util.LinkedList)1 UserTransaction (javax.transaction.UserTransaction)1 RetryingTransactionHelper (org.alfresco.repo.transaction.RetryingTransactionHelper)1 LoginPost (org.alfresco.repo.web.scripts.bean.LoginPost)1 JSONObject (org.json.JSONObject)1 Matchers.anyString (org.mockito.Matchers.anyString)1 Container (org.springframework.extensions.webscripts.Container)1 RequiredAuthentication (org.springframework.extensions.webscripts.Description.RequiredAuthentication)1 RequiredTransactionParameters (org.springframework.extensions.webscripts.Description.RequiredTransactionParameters)1