This is an automated email from the ASF dual-hosted git repository. rcordier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 8a20a86ec5b2d94e433c99151f8bdffe02db1b86 Author: Rene Cordier <rcord...@linagora.com> AuthorDate: Wed Mar 11 14:00:54 2020 +0700 JAMES-3078 Cleanup jmap-draft --- .../jmap/draft/AllowAllCrossOriginRequests.java | 57 ------ .../james/jmap/draft/AuthenticationFilter.java | 111 ---------- .../james/jmap/draft/AuthenticationServlet.java | 223 --------------------- .../james/jmap/draft/AuthenticationStrategy.java | 28 --- .../jmap/draft/BypassAuthOnRequestMethod.java | 118 ----------- .../draft/DefaultMailboxesProvisioningFilter.java | 127 ------------ .../apache/james/jmap/draft/DownloadServlet.java | 157 --------------- .../org/apache/james/jmap/draft/JMAPServer.java | 118 ----------- .../org/apache/james/jmap/draft/JMAPServlet.java | 115 ----------- .../java/org/apache/james/jmap/draft/JMAPUrls.java | 28 --- .../org/apache/james/jmap/draft/MDCFilter.java | 56 ------ .../org/apache/james/jmap/draft/UploadHandler.java | 72 ------- .../org/apache/james/jmap/draft/UploadServlet.java | 86 -------- .../james/jmap/draft/UserProvisioningFilter.java | 101 ---------- .../jmap/draft/BypassAuthOnRequestMethodTest.java | 109 ---------- .../DefaultMailboxesProvisioningFilterTest.java | 104 ---------- ...faultMailboxesProvisioningFilterThreadTest.java | 74 ------- .../james/jmap/draft/DownloadServletTest.java | 57 ------ .../apache/james/jmap/draft/JMAPServletTest.java | 142 ------------- .../jmap/draft/UserProvisioningFilterTest.java | 132 ------------ .../draft/UserProvisioningFilterThreadTest.java | 57 ------ .../james/jmap/draft/utils/DownloadPathTest.java | 84 -------- .../utils/HeadersAuthenticationExtractorTest.java | 69 ------- 23 files changed, 2225 deletions(-) diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/AllowAllCrossOriginRequests.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/AllowAllCrossOriginRequests.java deleted file mode 100644 index 2f8f8b9..0000000 --- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/AllowAllCrossOriginRequests.java +++ /dev/null @@ -1,57 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you 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 org.apache.james.jmap.draft; - -import java.io.IOException; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletResponse; - -public class AllowAllCrossOriginRequests implements Filter { - - private final Filter nestedFilter; - - public AllowAllCrossOriginRequests(Filter nestedFilter) { - this.nestedFilter = nestedFilter; - } - - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - - HttpServletResponse servletResponse = (HttpServletResponse) response; - servletResponse.addHeader("Access-Control-Allow-Origin", "*"); - servletResponse.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT"); - servletResponse.addHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Accept"); - nestedFilter.doFilter(request, response, chain); - } - - @Override - public void init(FilterConfig filterConfig) throws ServletException { - - } - - @Override - public void destroy() { - } -} diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/AuthenticationFilter.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/AuthenticationFilter.java deleted file mode 100644 index e14a550..0000000 --- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/AuthenticationFilter.java +++ /dev/null @@ -1,111 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you 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 org.apache.james.jmap.draft; - -import java.io.IOException; -import java.util.List; -import java.util.stream.Stream; - -import javax.inject.Inject; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.james.jmap.draft.exceptions.MailboxSessionCreationException; -import org.apache.james.jmap.draft.exceptions.NoValidAuthHeaderException; -import org.apache.james.jmap.draft.exceptions.UnauthorizedException; -import org.apache.james.mailbox.MailboxSession; -import org.apache.james.metrics.api.MetricFactory; -import org.apache.james.metrics.api.TimeMetric; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.annotations.VisibleForTesting; - -import io.jsonwebtoken.JwtException; - -public class AuthenticationFilter implements Filter { - - private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationFilter.class); - - public static final String MAILBOX_SESSION = "mailboxSession"; - - private final List<AuthenticationStrategy> authMethods; - private final MetricFactory metricFactory; - - @Inject - @VisibleForTesting - AuthenticationFilter(List<AuthenticationStrategy> authMethods, MetricFactory metricFactory) { - this.authMethods = authMethods; - this.metricFactory = metricFactory; - } - - @Override - public void init(FilterConfig filterConfig) throws ServletException { - } - - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - HttpServletRequest httpRequest = (HttpServletRequest) request; - HttpServletResponse httpResponse = (HttpServletResponse) response; - - try { - chain.doFilter(authenticate(httpRequest), response); - } catch (UnauthorizedException | NoValidAuthHeaderException | MailboxSessionCreationException | JwtException e) { - LOGGER.info("Exception occurred during authentication process", e); - httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED); - } - } - - private HttpServletRequest authenticate(HttpServletRequest httpRequest) { - TimeMetric timeMetric = metricFactory.timer("JMAP-authentication-filter"); - try { - return authMethods.stream() - .flatMap(auth -> createSession(auth, httpRequest)) - .findFirst() - .map(mailboxSession -> addSessionToRequest(httpRequest, mailboxSession)) - .orElseThrow(UnauthorizedException::new); - } finally { - timeMetric.stopAndPublish(); - } - } - - private HttpServletRequest addSessionToRequest(HttpServletRequest httpRequest, MailboxSession mailboxSession) { - httpRequest.setAttribute(MAILBOX_SESSION, mailboxSession); - return httpRequest; - } - - private Stream<MailboxSession> createSession(AuthenticationStrategy authenticationMethod, HttpServletRequest httpRequest) { - try { - return Stream.of(authenticationMethod.createMailboxSession(httpRequest)); - } catch (Exception e) { - return Stream.empty(); - } - } - - @Override - public void destroy() { - } - -} diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/AuthenticationServlet.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/AuthenticationServlet.java deleted file mode 100644 index 67ec0d8..0000000 --- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/AuthenticationServlet.java +++ /dev/null @@ -1,223 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you 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 org.apache.james.jmap.draft; - -import static org.apache.james.metrics.api.TimeMetric.ExecutionResult.DEFAULT_100_MS_THRESHOLD; - -import java.io.IOException; - -import javax.inject.Inject; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.james.core.Username; -import org.apache.james.jmap.api.access.AccessToken; -import org.apache.james.jmap.draft.api.AccessTokenManager; -import org.apache.james.jmap.draft.api.SimpleTokenFactory; -import org.apache.james.jmap.draft.api.SimpleTokenManager; -import org.apache.james.jmap.draft.exceptions.BadRequestException; -import org.apache.james.jmap.draft.exceptions.InternalErrorException; -import org.apache.james.jmap.draft.json.MultipleObjectMapperBuilder; -import org.apache.james.jmap.draft.model.AccessTokenRequest; -import org.apache.james.jmap.draft.model.AccessTokenResponse; -import org.apache.james.jmap.draft.model.ContinuationTokenRequest; -import org.apache.james.jmap.draft.model.ContinuationTokenResponse; -import org.apache.james.jmap.draft.model.EndPointsResponse; -import org.apache.james.metrics.api.MetricFactory; -import org.apache.james.metrics.api.TimeMetric; -import org.apache.james.user.api.UsersRepository; -import org.apache.james.user.api.UsersRepositoryException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.annotations.VisibleForTesting; - -public class AuthenticationServlet extends HttpServlet { - - public static final String JSON_CONTENT_TYPE = "application/json"; - public static final String JSON_CONTENT_TYPE_UTF8 = "application/json; charset=UTF-8"; - - private static final Logger LOG = LoggerFactory.getLogger(AuthenticationServlet.class); - - private final ObjectMapper mapper; - private final UsersRepository usersRepository; - private final SimpleTokenManager simpleTokenManager; - private final AccessTokenManager accessTokenManager; - private final SimpleTokenFactory simpleTokenFactory; - private final MetricFactory metricFactory; - - @Inject - @VisibleForTesting AuthenticationServlet(UsersRepository usersRepository, SimpleTokenManager simpleTokenManager, SimpleTokenFactory simpleTokenFactory, AccessTokenManager accessTokenManager, MetricFactory metricFactory) { - this.usersRepository = usersRepository; - this.simpleTokenManager = simpleTokenManager; - this.simpleTokenFactory = simpleTokenFactory; - this.accessTokenManager = accessTokenManager; - this.metricFactory = metricFactory; - this.mapper = new MultipleObjectMapperBuilder() - .registerClass(ContinuationTokenRequest.UNIQUE_JSON_PATH, ContinuationTokenRequest.class) - .registerClass(AccessTokenRequest.UNIQUE_JSON_PATH, AccessTokenRequest.class) - .build(); - } - - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - TimeMetric timeMetric = metricFactory.timer("JMAP-authentication-post"); - try { - assertJsonContentType(req); - assertAcceptJsonOnly(req); - - Object request = deserialize(req); - - if (request instanceof ContinuationTokenRequest) { - handleContinuationTokenRequest((ContinuationTokenRequest)request, resp); - } else if (request instanceof AccessTokenRequest) { - handleAccessTokenRequest((AccessTokenRequest)request, resp); - } - } catch (BadRequestException e) { - LOG.warn("Invalid authentication request received.", e); - resp.sendError(HttpServletResponse.SC_BAD_REQUEST); - } catch (InternalErrorException e) { - LOG.error("Internal error", e); - resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - } finally { - timeMetric.stopAndPublish().logWhenExceedP99(DEFAULT_100_MS_THRESHOLD); - } - } - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - returnEndPointsResponse(resp); - } - - @Override - protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - accessTokenManager.revoke(AccessToken.fromString(req.getHeader("Authorization"))); - resp.setStatus(HttpServletResponse.SC_NO_CONTENT); - } - - private Object deserialize(HttpServletRequest req) throws BadRequestException { - Object request; - try { - request = mapper.readValue(req.getReader(), Object.class); - } catch (IOException e) { - throw new BadRequestException("Request can't be deserialized", e); - } - return request; - } - - private void assertJsonContentType(HttpServletRequest req) { - if (! req.getContentType().equals(JSON_CONTENT_TYPE_UTF8)) { - throw new BadRequestException("Request ContentType header must be set to: " + JSON_CONTENT_TYPE_UTF8); - } - } - - private void assertAcceptJsonOnly(HttpServletRequest req) { - String accept = req.getHeader("Accept"); - if (accept == null || ! accept.contains(JSON_CONTENT_TYPE)) { - throw new BadRequestException("Request Accept header must be set to JSON content type"); - } - } - - private void handleContinuationTokenRequest(ContinuationTokenRequest request, HttpServletResponse resp) throws IOException { - resp.setContentType(JSON_CONTENT_TYPE_UTF8); - try { - ContinuationTokenResponse continuationTokenResponse = ContinuationTokenResponse - .builder() - .continuationToken(simpleTokenFactory.generateContinuationToken(request.getUsername())) - .methods(ContinuationTokenResponse.AuthenticationMethod.PASSWORD) - .build(); - mapper.writeValue(resp.getOutputStream(), continuationTokenResponse); - } catch (Exception e) { - throw new InternalErrorException("Error while responding to continuation token", e); - } - } - - private void handleAccessTokenRequest(AccessTokenRequest request, HttpServletResponse resp) throws IOException { - switch (simpleTokenManager.getValidity(request.getToken())) { - case EXPIRED: - returnRestartAuthentication(resp); - break; - case INVALID: - LOG.warn("Use of an invalid ContinuationToken : {}", request.getToken().serialize()); - returnUnauthorizedResponse(resp); - break; - case OK: - manageAuthenticationResponse(request, resp); - break; - } - } - - private void manageAuthenticationResponse(AccessTokenRequest request, HttpServletResponse resp) throws IOException { - Username username = Username.of(request.getToken().getUsername()); - if (authenticate(request, username)) { - returnAccessTokenResponse(resp, username); - } else { - LOG.info("Authentication failure for {}", username); - returnUnauthorizedResponse(resp); - } - } - - private boolean authenticate(AccessTokenRequest request, Username username) { - boolean authenticated = false; - try { - authenticated = usersRepository.test(username, request.getPassword()); - } catch (UsersRepositoryException e) { - LOG.error("Error while trying to validate authentication for user '{}'", username, e); - } - return authenticated; - } - - private void returnAccessTokenResponse(HttpServletResponse resp, Username username) throws IOException { - resp.setContentType(JSON_CONTENT_TYPE_UTF8); - resp.setStatus(HttpServletResponse.SC_CREATED); - AccessTokenResponse response = AccessTokenResponse - .builder() - .accessToken(accessTokenManager.grantAccessToken(username)) - .api(JMAPUrls.JMAP) - .eventSource("/notImplemented") - .upload(JMAPUrls.UPLOAD) - .download(JMAPUrls.DOWNLOAD) - .build(); - mapper.writeValue(resp.getOutputStream(), response); - } - - private void returnEndPointsResponse(HttpServletResponse resp) throws IOException { - resp.setContentType(JSON_CONTENT_TYPE_UTF8); - resp.setStatus(HttpServletResponse.SC_OK); - EndPointsResponse response = EndPointsResponse - .builder() - .api(JMAPUrls.JMAP) - .eventSource("/notImplemented") - .upload(JMAPUrls.UPLOAD) - .download(JMAPUrls.DOWNLOAD) - .build(); - mapper.writeValue(resp.getOutputStream(), response); - } - - private void returnUnauthorizedResponse(HttpServletResponse resp) throws IOException { - resp.sendError(HttpServletResponse.SC_UNAUTHORIZED); - } - - private void returnRestartAuthentication(HttpServletResponse resp) throws IOException { - resp.sendError(HttpServletResponse.SC_FORBIDDEN); - } -} diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/AuthenticationStrategy.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/AuthenticationStrategy.java deleted file mode 100644 index 4e5443b..0000000 --- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/AuthenticationStrategy.java +++ /dev/null @@ -1,28 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you 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 org.apache.james.jmap.draft; - -import javax.servlet.http.HttpServletRequest; - -import org.apache.james.mailbox.MailboxSession; - -public interface AuthenticationStrategy { - - MailboxSession createMailboxSession(HttpServletRequest httpRequest); -} diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/BypassAuthOnRequestMethod.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/BypassAuthOnRequestMethod.java deleted file mode 100644 index 7e48efa..0000000 --- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/BypassAuthOnRequestMethod.java +++ /dev/null @@ -1,118 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you 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 org.apache.james.jmap.draft; - -import java.io.IOException; -import java.util.List; -import java.util.function.Predicate; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; - -import com.google.common.base.Preconditions; -import com.google.common.base.Strings; -import com.google.common.collect.ImmutableList; - -public class BypassAuthOnRequestMethod implements Filter { - - public static Builder bypass(AuthenticationFilter authenticationFilter) { - return new Builder(authenticationFilter); - } - - public static class Builder { - private final ImmutableList.Builder<Predicate<HttpServletRequest>> reasons = new ImmutableList.Builder<>(); - private final AuthenticationFilter authenticationFilter; - - private Builder(AuthenticationFilter authenticationFilter) { - this.authenticationFilter = authenticationFilter; - } - - public InitializedBuilder on(String requestMethod) { - Preconditions.checkArgument(!Strings.isNullOrEmpty(requestMethod), "'requestMethod' is mandatory"); - String trimmedRequestMethod = requestMethod.trim(); - Preconditions.checkArgument(!Strings.isNullOrEmpty(trimmedRequestMethod), "'requestMethod' is mandatory"); - reasons.add(r -> r.getMethod().equalsIgnoreCase(trimmedRequestMethod)); - return new InitializedBuilder(this); - } - - public static class InitializedBuilder { - private final Builder builder; - - private InitializedBuilder(Builder builder) { - this.builder = builder; - } - - public InitializedBuilder and(String requestMethod) { - return builder.on(requestMethod); - } - - public BypassAuthOnRequestMethod only() { - return new BypassAuthOnRequestMethod(builder.authenticationFilter, builder.reasons.build()); - } - } - } - - - private final AuthenticationFilter authenticationFilter; - private final List<Predicate<HttpServletRequest>> listOfReasonsToBypassAuth; - - private BypassAuthOnRequestMethod(AuthenticationFilter authenticationFilter, List<Predicate<HttpServletRequest>> listOfReasonsToBypassAuth) { - this.authenticationFilter = authenticationFilter; - this.listOfReasonsToBypassAuth = listOfReasonsToBypassAuth; - } - - @Override - public void init(FilterConfig filterConfig) throws ServletException { - } - - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - HttpServletRequest httpRequest = (HttpServletRequest)request; - - if (shouldBypassAuth(httpRequest)) { - bypassAuth(request, response, chain); - } else { - tryAuth(httpRequest, response, chain); - } - } - - private boolean shouldBypassAuth(HttpServletRequest httpRequest) { - return listOfReasonsToBypassAuth.stream() - .anyMatch(r -> r.test(httpRequest)); - } - - @Override - public void destroy() { - } - - private void bypassAuth(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - chain.doFilter(request, response); - } - - private void tryAuth(HttpServletRequest httpRequest, ServletResponse response, FilterChain chain) throws IOException, ServletException { - authenticationFilter.doFilter(httpRequest, response, chain); - } - -} diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/DefaultMailboxesProvisioningFilter.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/DefaultMailboxesProvisioningFilter.java deleted file mode 100644 index 03bfce3..0000000 --- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/DefaultMailboxesProvisioningFilter.java +++ /dev/null @@ -1,127 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you 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 org.apache.james.jmap.draft; - -import java.io.IOException; -import java.util.Optional; -import java.util.function.Function; - -import javax.inject.Inject; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; - -import org.apache.james.core.Username; -import org.apache.james.mailbox.DefaultMailboxes; -import org.apache.james.mailbox.MailboxManager; -import org.apache.james.mailbox.MailboxSession; -import org.apache.james.mailbox.SubscriptionManager; -import org.apache.james.mailbox.exception.MailboxException; -import org.apache.james.mailbox.exception.MailboxExistsException; -import org.apache.james.mailbox.model.MailboxId; -import org.apache.james.mailbox.model.MailboxPath; -import org.apache.james.metrics.api.MetricFactory; -import org.apache.james.metrics.api.TimeMetric; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.annotations.VisibleForTesting; - -public class DefaultMailboxesProvisioningFilter implements Filter { - - private static final Logger LOGGER = LoggerFactory.getLogger(DefaultMailboxesProvisioningFilter.class); - private final MailboxManager mailboxManager; - private final SubscriptionManager subscriptionManager; - private final MetricFactory metricFactory; - - @Inject - @VisibleForTesting - DefaultMailboxesProvisioningFilter(MailboxManager mailboxManager, - SubscriptionManager subscriptionManager, - MetricFactory metricFactory) { - this.mailboxManager = mailboxManager; - this.subscriptionManager = subscriptionManager; - this.metricFactory = metricFactory; - } - - @Override - public void init(FilterConfig filterConfig) { - } - - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - Optional<MailboxSession> session = Optional.ofNullable((MailboxSession)request.getAttribute(AuthenticationFilter.MAILBOX_SESSION)); - session.ifPresent(this::createMailboxesIfNeeded); - chain.doFilter(request, response); - } - - @VisibleForTesting - void createMailboxesIfNeeded(MailboxSession session) { - TimeMetric timeMetric = metricFactory.timer("JMAP-mailboxes-provisioning"); - try { - Username username = session.getUser(); - createDefaultMailboxes(username); - } catch (MailboxException e) { - throw new RuntimeException(e); - } finally { - timeMetric.stopAndPublish(); - } - } - - private void createDefaultMailboxes(Username username) throws MailboxException { - MailboxSession session = mailboxManager.createSystemSession(username); - DefaultMailboxes.DEFAULT_MAILBOXES.stream() - .map(toMailboxPath(session)) - .filter(mailboxPath -> mailboxDoesntExist(mailboxPath, session)) - .forEach(mailboxPath -> createMailbox(mailboxPath, session)); - } - - private boolean mailboxDoesntExist(MailboxPath mailboxPath, MailboxSession session) { - try { - return !mailboxManager.mailboxExists(mailboxPath, session); - } catch (MailboxException e) { - throw new RuntimeException(e); - } - } - - private Function<String, MailboxPath> toMailboxPath(MailboxSession session) { - return mailbox -> MailboxPath.forUser(session.getUser(), mailbox); - } - - private void createMailbox(MailboxPath mailboxPath, MailboxSession session) { - try { - Optional<MailboxId> mailboxId = mailboxManager.createMailbox(mailboxPath, session); - if (mailboxId.isPresent()) { - subscriptionManager.subscribe(session, mailboxPath.getName()); - } - LOGGER.info("Provisioning {}. {} created.", mailboxPath, mailboxId); - } catch (MailboxExistsException e) { - LOGGER.info("Mailbox {} have been created concurrently", mailboxPath); - } catch (MailboxException e) { - throw new RuntimeException(e); - } - } - - @Override - public void destroy() { - } -} diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/DownloadServlet.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/DownloadServlet.java deleted file mode 100644 index 0963871..0000000 --- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/DownloadServlet.java +++ /dev/null @@ -1,157 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you 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 org.apache.james.jmap.draft; - -import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; -import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; -import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND; -import static javax.servlet.http.HttpServletResponse.SC_OK; - -import java.io.IOException; -import java.util.Optional; - -import javax.inject.Inject; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.io.IOUtils; -import org.apache.james.jmap.draft.api.SimpleTokenFactory; -import org.apache.james.jmap.draft.utils.DownloadPath; -import org.apache.james.mailbox.BlobManager; -import org.apache.james.mailbox.MailboxSession; -import org.apache.james.mailbox.exception.BlobNotFoundException; -import org.apache.james.mailbox.exception.MailboxException; -import org.apache.james.mailbox.model.Blob; -import org.apache.james.mailbox.model.BlobId; -import org.apache.james.metrics.api.MetricFactory; -import org.apache.james.metrics.api.TimeMetric; -import org.apache.james.mime4j.codec.EncoderUtil; -import org.apache.james.mime4j.codec.EncoderUtil.Usage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.CharMatcher; - -public class DownloadServlet extends HttpServlet { - - private static final Logger LOGGER = LoggerFactory.getLogger(DownloadServlet.class); - private static final String TEXT_PLAIN_CONTENT_TYPE = "text/plain"; - - private final BlobManager blobManager; - private final SimpleTokenFactory simpleTokenFactory; - private final MetricFactory metricFactory; - - @Inject - @VisibleForTesting DownloadServlet(BlobManager blobManager, SimpleTokenFactory simpleTokenFactory, MetricFactory metricFactory) { - this.blobManager = blobManager; - this.simpleTokenFactory = simpleTokenFactory; - this.metricFactory = metricFactory; - } - - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException { - TimeMetric timeMetric = metricFactory.timer("JMAP-download-post"); - String pathInfo = req.getPathInfo(); - try { - respondAttachmentAccessToken(getMailboxSession(req), DownloadPath.from(pathInfo), resp); - } catch (IllegalArgumentException e) { - LOGGER.error("Error while generating attachment access token '{}'", pathInfo, e); - resp.setStatus(SC_BAD_REQUEST); - } finally { - timeMetric.stopAndPublish(); - } - } - - private void respondAttachmentAccessToken(MailboxSession mailboxSession, DownloadPath downloadPath, HttpServletResponse resp) { - TimeMetric timeMetric = metricFactory.timer("JMAP-download-get"); - String blobId = downloadPath.getBlobId(); - try { - if (! attachmentExists(mailboxSession, blobId)) { - resp.setStatus(SC_NOT_FOUND); - return; - } - resp.setContentType(TEXT_PLAIN_CONTENT_TYPE); - resp.getOutputStream().print(simpleTokenFactory.generateAttachmentAccessToken(mailboxSession.getUser().asString(), blobId).serialize()); - resp.setStatus(SC_OK); - } catch (MailboxException | IOException e) { - LOGGER.error("Error while asking attachment access token", e); - resp.setStatus(SC_INTERNAL_SERVER_ERROR); - } finally { - timeMetric.stopAndPublish(); - } - } - - private boolean attachmentExists(MailboxSession mailboxSession, String blobId) throws MailboxException { - try { - blobManager.retrieve(BlobId.fromString(blobId), mailboxSession); - return true; - } catch (BlobNotFoundException e) { - return false; - } - } - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException { - String pathInfo = req.getPathInfo(); - try { - download(getMailboxSession(req), DownloadPath.from(pathInfo), resp); - } catch (IllegalArgumentException e) { - LOGGER.error("Error while downloading '{}'", pathInfo, e); - resp.setStatus(SC_BAD_REQUEST); - } - } - - @VisibleForTesting void download(MailboxSession mailboxSession, DownloadPath downloadPath, HttpServletResponse resp) { - String blobId = downloadPath.getBlobId(); - try { - Blob blob = blobManager.retrieve(BlobId.fromString(blobId), mailboxSession); - - addContentDispositionHeader(downloadPath.getName(), resp); - resp.setHeader("Content-Length", String.valueOf(blob.getSize())); - resp.setHeader("Content-Type", blob.getContentType()); - resp.setStatus(SC_OK); - IOUtils.copy(blob.getStream(), resp.getOutputStream()); - } catch (BlobNotFoundException e) { - LOGGER.info("Attachment '{}' not found", blobId, e); - resp.setStatus(SC_NOT_FOUND); - } catch (MailboxException | IOException e) { - LOGGER.error("Error while downloading", e); - resp.setStatus(SC_INTERNAL_SERVER_ERROR); - } - } - - private void addContentDispositionHeader(Optional<String> optionalName, HttpServletResponse resp) { - optionalName.ifPresent(name -> addContentDispositionHeaderRegardingEncoding(name, resp)); - } - - private void addContentDispositionHeaderRegardingEncoding(String name, HttpServletResponse resp) { - if (CharMatcher.ascii().matchesAllOf(name)) { - resp.addHeader("Content-Disposition", "attachment; filename=\"" + name + "\""); - } else { - resp.addHeader("Content-Disposition", "attachment; filename*=\"" + EncoderUtil.encodeEncodedWord(name, Usage.TEXT_TOKEN) + "\""); - } - } - - private MailboxSession getMailboxSession(HttpServletRequest req) { - return (MailboxSession) req.getAttribute(AuthenticationFilter.MAILBOX_SESSION); - } -} diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/JMAPServer.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/JMAPServer.java deleted file mode 100644 index c3aabf9..0000000 --- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/JMAPServer.java +++ /dev/null @@ -1,118 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you 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 org.apache.james.jmap.draft; - -import static org.apache.james.jmap.draft.BypassAuthOnRequestMethod.bypass; -import static org.zalando.logbook.HeaderFilters.authorization; - -import java.util.Optional; - -import javax.annotation.PreDestroy; -import javax.inject.Inject; - -import org.apache.james.http.jetty.Configuration; -import org.apache.james.http.jetty.Configuration.Builder; -import org.apache.james.http.jetty.JettyHttpServer; -import org.apache.james.lifecycle.api.Startable; -import org.apache.james.util.Port; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.zalando.logbook.DefaultHttpLogWriter; -import org.zalando.logbook.DefaultHttpLogWriter.Level; -import org.zalando.logbook.Logbook; -import org.zalando.logbook.servlet.LogbookFilter; - -import com.github.fge.lambdas.Throwing; - -public class JMAPServer implements Startable { - - private static final Logger HTTP_JMAP_LOGGER = LoggerFactory.getLogger("http.jmap"); - private final Optional<JettyHttpServer> server; - - @Inject - private JMAPServer(JMAPConfiguration jmapConfiguration, - AuthenticationServlet authenticationServlet, JMAPServlet jmapServlet, DownloadServlet downloadServlet, UploadServlet uploadServlet, - AuthenticationFilter authenticationFilter, UserProvisioningFilter userProvisioningFilter, DefaultMailboxesProvisioningFilter defaultMailboxesProvisioningFilter) { - if (jmapConfiguration.isEnabled()) { - server = Optional.of(JettyHttpServer.create( - configurationBuilderFor(jmapConfiguration) - .serve(JMAPUrls.AUTHENTICATION) - .with(authenticationServlet) - .filter(JMAPUrls.AUTHENTICATION) - .with(new AllowAllCrossOriginRequests(bypass(authenticationFilter).on("POST").and("OPTIONS").only())) - .and(new MDCFilter()) - .only() - .serve(JMAPUrls.JMAP) - .with(jmapServlet) - .filter(JMAPUrls.JMAP) - .with(new AllowAllCrossOriginRequests(bypass(authenticationFilter).on("OPTIONS").only())) - .and(new LogbookFilter(logbook())) - .and(userProvisioningFilter) - .and(defaultMailboxesProvisioningFilter) - .and(new MDCFilter()) - .only() - .serveAsOneLevelTemplate(JMAPUrls.DOWNLOAD) - .with(downloadServlet) - .filterAsOneLevelTemplate(JMAPUrls.DOWNLOAD) - .with(new AllowAllCrossOriginRequests(bypass(authenticationFilter).on("OPTIONS").only())) - .and(new MDCFilter()) - .only() - .serve(JMAPUrls.UPLOAD) - .with(uploadServlet) - .filterAsOneLevelTemplate(JMAPUrls.UPLOAD) - .with(new AllowAllCrossOriginRequests(bypass(authenticationFilter).on("OPTIONS").only())) - .and(new MDCFilter()) - .only() - .build())); - } else { - server = Optional.empty(); - } - } - - private Builder configurationBuilderFor(JMAPConfiguration jmapConfiguration) { - Builder builder = Configuration.builder(); - if (jmapConfiguration.getPort().isPresent()) { - builder.port(jmapConfiguration.getPort().get()); - } else { - builder.randomPort(); - } - return builder; - } - - private Logbook logbook() { - return Logbook.builder() - .headerFilter(authorization()) - .writer(new DefaultHttpLogWriter(HTTP_JMAP_LOGGER, Level.DEBUG)) - .build(); - } - - public void start() { - server.ifPresent(Throwing.consumer(JettyHttpServer::start).sneakyThrow()); - } - - @PreDestroy - public void stop() { - server.ifPresent(Throwing.consumer(JettyHttpServer::stop).sneakyThrow()); - } - - public Port getPort() { - return Port.of(server.map(JettyHttpServer::getPort).orElseThrow(() -> new RuntimeException("JMAP server was disabled. No port bound"))); - } -} diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/JMAPServlet.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/JMAPServlet.java deleted file mode 100644 index c9e2ab7..0000000 --- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/JMAPServlet.java +++ /dev/null @@ -1,115 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you 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 org.apache.james.jmap.draft; - -import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; - -import java.io.IOException; -import java.nio.channels.ClosedChannelException; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import javax.inject.Inject; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.james.jmap.draft.methods.RequestHandler; -import org.apache.james.jmap.draft.model.AuthenticatedRequest; -import org.apache.james.jmap.draft.model.InvocationRequest; -import org.apache.james.jmap.draft.model.InvocationResponse; -import org.apache.james.metrics.api.MetricFactory; -import org.apache.james.metrics.api.TimeMetric; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.core.JsonParser.Feature; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; - -public class JMAPServlet extends HttpServlet { - - public static final Logger LOGGER = LoggerFactory.getLogger(JMAPServlet.class); - public static final String JSON_CONTENT_TYPE = "application/json"; - public static final String JSON_CONTENT_TYPE_UTF8 = "application/json; charset=UTF-8"; - - private final ObjectMapper objectMapper; - private final RequestHandler requestHandler; - private final MetricFactory metricFactory; - - @Inject - public JMAPServlet(RequestHandler requestHandler, MetricFactory metricFactory) { - this.requestHandler = requestHandler; - this.metricFactory = metricFactory; - this.objectMapper = new ObjectMapper(); - objectMapper.configure(Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true); - } - - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException { - TimeMetric timeMetric = metricFactory.timer("JMAP-request"); - try { - List<Object[]> responses = - requestAsJsonStream(req) - .map(InvocationRequest::deserialize) - .map(x -> AuthenticatedRequest.decorate(x, req)) - .flatMap(this::handle) - .map(InvocationResponse::asProtocolSpecification) - .collect(Collectors.toList()); - - resp.setContentType(JSON_CONTENT_TYPE); - sendResponses(resp, responses); - } catch (IOException e) { - LOGGER.warn("Error handling request", e); - resp.setStatus(SC_BAD_REQUEST); - } catch (Exception e) { - LOGGER.error("Error handling request", e); - throw new ServletException(e); - } finally { - timeMetric.stopAndPublish(); - } - } - - private void sendResponses(HttpServletResponse response, List<Object[]> responses) throws IOException { - try { - objectMapper.writeValue(response.getOutputStream(), responses); - } catch (ClosedChannelException e) { - LOGGER.info("Error sending response", e); - response.setStatus(SC_BAD_REQUEST); - } - } - - private Stream<? extends InvocationResponse> handle(AuthenticatedRequest request) { - try { - return requestHandler.handle(request); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private Stream<JsonNode[]> requestAsJsonStream(HttpServletRequest req) throws IOException, JsonParseException, JsonMappingException { - return Arrays.stream( - objectMapper.readValue(req.getInputStream(), JsonNode[][].class)); - } -} diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/JMAPUrls.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/JMAPUrls.java deleted file mode 100644 index 9a8d983..0000000 --- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/JMAPUrls.java +++ /dev/null @@ -1,28 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you 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 org.apache.james.jmap.draft; - -public interface JMAPUrls { - - String JMAP = "/jmap"; - String AUTHENTICATION = "/authentication"; - String DOWNLOAD = "/download"; - String UPLOAD = "/upload"; -} diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/MDCFilter.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/MDCFilter.java deleted file mode 100644 index 83e6614..0000000 --- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/MDCFilter.java +++ /dev/null @@ -1,56 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you 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 org.apache.james.jmap.draft; - -import java.io.Closeable; -import java.io.IOException; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; - -import org.apache.james.util.MDCBuilder; - -public class MDCFilter implements Filter { - @Override - public void init(FilterConfig filterConfig) throws ServletException { - - } - - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - try (Closeable closeable = - MDCBuilder.create() - .addContext(MDCBuilder.PROTOCOL, "JMAP") - .addContext(MDCBuilder.IP, request.getRemoteAddr()) - .addContext(MDCBuilder.HOST, request.getRemoteHost()) - .build()) { - chain.doFilter(request, response); - } - } - - @Override - public void destroy() { - - } -} diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/UploadHandler.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/UploadHandler.java deleted file mode 100644 index 836948f..0000000 --- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/UploadHandler.java +++ /dev/null @@ -1,72 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you 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 org.apache.james.jmap.draft; - -import static javax.servlet.http.HttpServletResponse.SC_CREATED; - -import java.io.IOException; -import java.io.InputStream; - -import javax.inject.Inject; -import javax.servlet.http.HttpServletResponse; - -import org.apache.james.jmap.draft.json.ObjectMapperFactory; -import org.apache.james.jmap.draft.model.UploadResponse; -import org.apache.james.mailbox.AttachmentManager; -import org.apache.james.mailbox.MailboxSession; -import org.apache.james.mailbox.exception.MailboxException; -import org.apache.james.mailbox.model.Attachment; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.io.ByteStreams; - -public class UploadHandler { - private final AttachmentManager attachmentManager; - private final ObjectMapper objectMapper; - - @Inject - private UploadHandler(AttachmentManager attachmentManager, ObjectMapperFactory objectMapperFactory) { - this.attachmentManager = attachmentManager; - this.objectMapper = objectMapperFactory.forWriting(); - } - - public void handle(String contentType, InputStream content, MailboxSession mailboxSession, HttpServletResponse response) throws IOException, MailboxException { - UploadResponse storedContent = uploadContent(contentType, content, mailboxSession); - buildResponse(response, storedContent); - } - - private UploadResponse uploadContent(String contentType, InputStream inputStream, MailboxSession session) throws IOException, MailboxException { - Attachment attachment = Attachment.builder() - .bytes(ByteStreams.toByteArray(inputStream)) - .type(contentType) - .build(); - attachmentManager.storeAttachment(attachment, session); - return UploadResponse.builder() - .blobId(attachment.getAttachmentId().getId()) - .type(attachment.getType()) - .size(attachment.getSize()) - .build(); - } - - private void buildResponse(HttpServletResponse resp, UploadResponse storedContent) throws IOException { - resp.setContentType(JMAPServlet.JSON_CONTENT_TYPE_UTF8); - resp.setStatus(SC_CREATED); - objectMapper.writeValue(resp.getOutputStream(), storedContent); - } -} diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/UploadServlet.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/UploadServlet.java deleted file mode 100644 index c16074a..0000000 --- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/UploadServlet.java +++ /dev/null @@ -1,86 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you 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 org.apache.james.jmap.draft; - -import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; -import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; - -import java.io.EOFException; -import java.io.IOException; - -import javax.inject.Inject; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.james.mailbox.MailboxSession; -import org.apache.james.mailbox.exception.MailboxException; -import org.apache.james.metrics.api.MetricFactory; -import org.apache.james.metrics.api.TimeMetric; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Strings; - -public class UploadServlet extends HttpServlet { - private static final Logger LOGGER = LoggerFactory.getLogger(UploadServlet.class); - - private final UploadHandler uploadHandler; - private final MetricFactory metricFactory; - - @Inject - private UploadServlet(UploadHandler uploadHandler, MetricFactory metricFactory) { - this.uploadHandler = uploadHandler; - this.metricFactory = metricFactory; - } - - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException { - String contentType = req.getContentType(); - if (Strings.isNullOrEmpty(contentType)) { - resp.setStatus(SC_BAD_REQUEST); - } else { - TimeMetric timeMetric = metricFactory.timer("JMAP-upload-post"); - try { - uploadHandler.handle(contentType, req.getInputStream(), getMailboxSession(req), resp); - } catch (IOException e) { - if (e instanceof EOFException) { - LOGGER.info("An upload has been canceled before the end", e); - } else { - internalServerError(resp, e); - } - } catch (MailboxException e) { - internalServerError(resp, e); - } finally { - timeMetric.stopAndPublish(); - } - } - } - - private void internalServerError(HttpServletResponse resp, Exception e) { - LOGGER.error("Error while uploading content", e); - resp.setStatus(SC_INTERNAL_SERVER_ERROR); - } - - private MailboxSession getMailboxSession(HttpServletRequest req) { - return (MailboxSession) req.getAttribute(AuthenticationFilter.MAILBOX_SESSION); - } - -} diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/UserProvisioningFilter.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/UserProvisioningFilter.java deleted file mode 100644 index 573d863..0000000 --- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/UserProvisioningFilter.java +++ /dev/null @@ -1,101 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you 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 org.apache.james.jmap.draft; - -import static org.apache.james.metrics.api.TimeMetric.ExecutionResult.DEFAULT_100_MS_THRESHOLD; - -import java.io.IOException; -import java.util.Optional; -import java.util.UUID; - -import javax.inject.Inject; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; - -import org.apache.james.core.Username; -import org.apache.james.mailbox.MailboxSession; -import org.apache.james.metrics.api.MetricFactory; -import org.apache.james.metrics.api.TimeMetric; -import org.apache.james.user.api.AlreadyExistInUsersRepositoryException; -import org.apache.james.user.api.UsersRepository; -import org.apache.james.user.api.UsersRepositoryException; - -import com.google.common.annotations.VisibleForTesting; - -public class UserProvisioningFilter implements Filter { - - private final UsersRepository usersRepository; - private final MetricFactory metricFactory; - - @Inject - @VisibleForTesting UserProvisioningFilter(UsersRepository usersRepository, MetricFactory metricFactory) { - this.usersRepository = usersRepository; - this.metricFactory = metricFactory; - } - - @Override - public void init(FilterConfig filterConfig) throws ServletException { - } - - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - if (!usersRepository.isReadOnly()) { - Optional<MailboxSession> session = Optional.ofNullable((MailboxSession) request.getAttribute(AuthenticationFilter.MAILBOX_SESSION)); - session.ifPresent(this::createAccountIfNeeded); - } - chain.doFilter(request, response); - } - - @VisibleForTesting - void createAccountIfNeeded(MailboxSession session) { - TimeMetric timeMetric = metricFactory.timer("JMAP-user-provisioning"); - try { - Username username = session.getUser(); - if (needsAccountCreation(username)) { - createAccount(username); - } - } catch (AlreadyExistInUsersRepositoryException e) { - // Ignore - } catch (UsersRepositoryException e) { - throw new RuntimeException(e); - } finally { - timeMetric.stopAndPublish().logWhenExceedP99(DEFAULT_100_MS_THRESHOLD); - } - } - - private void createAccount(Username username) throws UsersRepositoryException { - usersRepository.addUser(username, generatePassword()); - } - - private boolean needsAccountCreation(Username username) throws UsersRepositoryException { - return !usersRepository.contains(username); - } - - private String generatePassword() { - return UUID.randomUUID().toString(); - } - - @Override - public void destroy() { - } -} diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/BypassAuthOnRequestMethodTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/BypassAuthOnRequestMethodTest.java deleted file mode 100644 index e4ca23a..0000000 --- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/BypassAuthOnRequestMethodTest.java +++ /dev/null @@ -1,109 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you 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 org.apache.james.jmap.draft; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import javax.servlet.FilterChain; -import javax.servlet.http.HttpServletRequest; - -import org.apache.james.jmap.draft.AuthenticationFilter; -import org.apache.james.jmap.draft.BypassAuthOnRequestMethod; -import org.junit.Before; -import org.junit.Test; - -public class BypassAuthOnRequestMethodTest { - - private HttpServletRequest mockedRequest; - private AuthenticationFilter nestedFilter; - private BypassAuthOnRequestMethod sut; - private FilterChain filterChain; - - @Before - public void setup() throws Exception { - mockedRequest = mock(HttpServletRequest.class); - nestedFilter = mock(AuthenticationFilter.class); - sut = BypassAuthOnRequestMethod.bypass(nestedFilter).on("POST").and("OPTIONS").only(); - filterChain = mock(FilterChain.class); - } - - @Test - public void filterShouldCallNestedFilterOnGet() throws Exception { - when(mockedRequest.getMethod()) - .thenReturn("GET"); - - sut.doFilter(mockedRequest, null, filterChain); - - verify(nestedFilter).doFilter(mockedRequest, null, filterChain); - } - - @Test - public void filterShouldNotCallDirectlyChainOnGet() throws Exception { - when(mockedRequest.getMethod()) - .thenReturn("GET"); - - sut.doFilter(mockedRequest, null, filterChain); - - verify(filterChain, never()).doFilter(mockedRequest, null); - } - - @Test - public void filterShouldNotCallNestedFilterOnPost() throws Exception { - when(mockedRequest.getMethod()) - .thenReturn("POST"); - - sut.doFilter(mockedRequest, null, filterChain); - - verify(nestedFilter, never()).doFilter(mockedRequest, null, filterChain); - } - - @Test - public void filterShouldCallChainOnPost() throws Exception { - when(mockedRequest.getMethod()) - .thenReturn("POST"); - - sut.doFilter(mockedRequest, null, filterChain); - - verify(filterChain).doFilter(mockedRequest, null); - } - - @Test - public void filterShouldNotCallNestedFilterOnOptions() throws Exception { - when(mockedRequest.getMethod()) - .thenReturn("OPTIONS"); - - sut.doFilter(mockedRequest, null, filterChain); - - verify(nestedFilter, never()).doFilter(mockedRequest, null, filterChain); - } - - @Test - public void filterShouldCallChainOnOptions() throws Exception { - when(mockedRequest.getMethod()) - .thenReturn("OPTIONS"); - - sut.doFilter(mockedRequest, null, filterChain); - - verify(filterChain).doFilter(mockedRequest, null); - } - -} diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/DefaultMailboxesProvisioningFilterTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/DefaultMailboxesProvisioningFilterTest.java deleted file mode 100644 index 218eeac..0000000 --- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/DefaultMailboxesProvisioningFilterTest.java +++ /dev/null @@ -1,104 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you 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 org.apache.james.jmap.draft; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.Duration; - -import org.apache.james.core.Username; -import org.apache.james.mailbox.DefaultMailboxes; -import org.apache.james.mailbox.MailboxSession; -import org.apache.james.mailbox.MailboxSessionUtil; -import org.apache.james.mailbox.inmemory.InMemoryMailboxManager; -import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources; -import org.apache.james.mailbox.model.MailboxPath; -import org.apache.james.mailbox.store.StoreSubscriptionManager; -import org.apache.james.metrics.tests.RecordingMetricFactory; -import org.apache.james.util.concurrency.ConcurrentTestRunner; -import org.junit.Before; -import org.junit.Test; - -import com.github.fge.lambdas.Throwing; -import com.github.steveash.guavate.Guavate; - -public class DefaultMailboxesProvisioningFilterTest { - - public static final Username USERNAME = Username.of("username"); - private DefaultMailboxesProvisioningFilter testee; - private MailboxSession session; - private InMemoryMailboxManager mailboxManager; - private StoreSubscriptionManager subscriptionManager; - - @Before - public void before() throws Exception { - session = MailboxSessionUtil.create(USERNAME); - - mailboxManager = InMemoryIntegrationResources.defaultResources().getMailboxManager(); - subscriptionManager = new StoreSubscriptionManager(mailboxManager.getMapperFactory()); - testee = new DefaultMailboxesProvisioningFilter(mailboxManager, subscriptionManager, new RecordingMetricFactory()); - } - - @Test - public void createMailboxesIfNeededShouldCreateSystemMailboxes() throws Exception { - testee.createMailboxesIfNeeded(session); - - assertThat(mailboxManager.list(session)) - .containsOnlyElementsOf(DefaultMailboxes.DEFAULT_MAILBOXES - .stream() - .map(mailboxName -> MailboxPath.forUser(USERNAME, mailboxName)) - .collect(Guavate.toImmutableList())); - } - - @Test - public void createMailboxesIfNeededShouldCreateSpamWhenOtherSystemMailboxesExist() throws Exception { - DefaultMailboxes.DEFAULT_MAILBOXES - .stream() - .filter(mailbox -> !DefaultMailboxes.SPAM.equals(mailbox)) - .forEach(Throwing.consumer(mailbox -> mailboxManager.createMailbox(MailboxPath.forUser(USERNAME, mailbox), session))); - - testee.createMailboxesIfNeeded(session); - - assertThat(mailboxManager.list(session)).contains(MailboxPath.forUser(USERNAME, DefaultMailboxes.SPAM)); - } - - @Test - public void createMailboxesIfNeededShouldSubscribeMailboxes() throws Exception { - testee.createMailboxesIfNeeded(session); - - assertThat(subscriptionManager.subscriptions(session)) - .containsOnlyElementsOf(DefaultMailboxes.DEFAULT_MAILBOXES); - } - - @Test - public void createMailboxesIfNeededShouldNotGenerateExceptionsInConcurrentEnvironment() throws Exception { - ConcurrentTestRunner.builder() - .operation((threadNumber, step) -> testee.createMailboxesIfNeeded(session)) - .threadCount(10) - .runSuccessfullyWithin(Duration.ofSeconds(10)); - - assertThat(mailboxManager.list(session)) - .containsOnlyElementsOf(DefaultMailboxes.DEFAULT_MAILBOXES - .stream() - .map(mailboxName -> MailboxPath.forUser(USERNAME, mailboxName)) - .collect(Guavate.toImmutableList())); - } - -} - diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/DefaultMailboxesProvisioningFilterThreadTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/DefaultMailboxesProvisioningFilterThreadTest.java deleted file mode 100644 index 5e349aa..0000000 --- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/DefaultMailboxesProvisioningFilterThreadTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you 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 org.apache.james.jmap.draft; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.time.Duration; -import java.util.Optional; - -import org.apache.james.core.Username; -import org.apache.james.mailbox.MailboxManager; -import org.apache.james.mailbox.MailboxSession; -import org.apache.james.mailbox.MailboxSessionUtil; -import org.apache.james.mailbox.SubscriptionManager; -import org.apache.james.mailbox.model.MailboxPath; -import org.apache.james.mailbox.model.TestId; -import org.apache.james.metrics.tests.RecordingMetricFactory; -import org.apache.james.util.concurrency.ConcurrentTestRunner; -import org.junit.Before; -import org.junit.Test; - -public class DefaultMailboxesProvisioningFilterThreadTest { - - private static final Username USERNAME = Username.of("username"); - - private DefaultMailboxesProvisioningFilter sut; - private MailboxSession session; - private MailboxManager mailboxManager; - private SubscriptionManager subscriptionManager; - - @Before - public void before() { - session = MailboxSessionUtil.create(USERNAME); - mailboxManager = mock(MailboxManager.class); - subscriptionManager = mock(SubscriptionManager.class); - sut = new DefaultMailboxesProvisioningFilter(mailboxManager, subscriptionManager, new RecordingMetricFactory()); - } - - @Test - public void testConcurrentAccessToFilterShouldNotThrow() throws Exception { - doNothing().when(subscriptionManager).subscribe(eq(session), anyString()); - - when(mailboxManager.createMailbox(any(MailboxPath.class), eq(session))).thenReturn(Optional.of(TestId.of(18L))); - when(mailboxManager.mailboxExists(any(MailboxPath.class), eq(session))).thenReturn(false); - when(mailboxManager.createSystemSession(USERNAME)).thenReturn(session); - - ConcurrentTestRunner - .builder() - .operation((threadNumber, step) -> sut.createMailboxesIfNeeded(session)) - .threadCount(2) - .runSuccessfullyWithin(Duration.ofMinutes(1)); - } -} diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/DownloadServletTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/DownloadServletTest.java deleted file mode 100644 index e52c47e..0000000 --- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/DownloadServletTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you 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 org.apache.james.jmap.draft; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import javax.servlet.http.HttpServletResponse; - -import org.apache.james.core.Username; -import org.apache.james.jmap.draft.api.SimpleTokenFactory; -import org.apache.james.jmap.draft.utils.DownloadPath; -import org.apache.james.mailbox.BlobManager; -import org.apache.james.mailbox.MailboxSession; -import org.apache.james.mailbox.MailboxSessionUtil; -import org.apache.james.mailbox.exception.MailboxException; -import org.apache.james.metrics.tests.RecordingMetricFactory; -import org.junit.Test; - -public class DownloadServletTest { - - @Test - public void downloadMayFailWhenUnknownErrorOnAttachmentManager() throws Exception { - MailboxSession mailboxSession = MailboxSessionUtil.create(Username.of("User")); - BlobManager mockedBlobManager = mock(BlobManager.class); - when(mockedBlobManager.retrieve(any(), eq(mailboxSession))) - .thenThrow(new MailboxException()); - SimpleTokenFactory nullSimpleTokenFactory = null; - - DownloadServlet testee = new DownloadServlet(mockedBlobManager, nullSimpleTokenFactory, new RecordingMetricFactory()); - - HttpServletResponse resp = mock(HttpServletResponse.class); - testee.download(mailboxSession, DownloadPath.from("/blobId"), resp); - - verify(resp).setStatus(500); - } -} diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/JMAPServletTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/JMAPServletTest.java deleted file mode 100644 index de1a3dd..0000000 --- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/JMAPServletTest.java +++ /dev/null @@ -1,142 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you 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 org.apache.james.jmap.draft; - -import static io.restassured.RestAssured.given; -import static io.restassured.config.EncoderConfig.encoderConfig; -import static io.restassured.config.RestAssuredConfig.newConfig; -import static org.hamcrest.Matchers.equalTo; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.nio.charset.StandardCharsets; -import java.util.stream.Stream; - -import org.apache.james.http.jetty.Configuration; -import org.apache.james.http.jetty.JettyHttpServer; -import org.apache.james.jmap.draft.methods.ErrorResponse; -import org.apache.james.jmap.draft.methods.Method; -import org.apache.james.jmap.draft.methods.RequestHandler; -import org.apache.james.jmap.draft.model.InvocationResponse; -import org.apache.james.jmap.draft.model.MethodCallId; -import org.apache.james.metrics.tests.RecordingMetricFactory; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import com.fasterxml.jackson.databind.node.ObjectNode; - -import io.restassured.RestAssured; -import io.restassured.builder.RequestSpecBuilder; -import io.restassured.http.ContentType; - -public class JMAPServletTest { - - private JettyHttpServer server; - private RequestHandler requestHandler; - - @Before - public void setup() throws Exception { - requestHandler = mock(RequestHandler.class); - JMAPServlet jmapServlet = new JMAPServlet(requestHandler, new RecordingMetricFactory()); - - server = JettyHttpServer.create( - Configuration.builder() - .serve("/*") - .with(jmapServlet) - .randomPort() - .build()); - - server.start(); - - RestAssured.requestSpecification = new RequestSpecBuilder() - .setContentType(ContentType.JSON) - .setAccept(ContentType.JSON) - .setConfig(newConfig().encoderConfig(encoderConfig().defaultContentCharset(StandardCharsets.UTF_8))) - .setPort(server.getPort()) - .build(); - } - - @After - public void teardown() throws Exception { - server.stop(); - } - - @Test - public void mustReturnBadRequestOnMalformedRequest() { - String missingAnOpeningBracket = "[\"getAccounts\", {\"state\":false}, \"#0\"]]"; - - given() - .body(missingAnOpeningBracket) - .when() - .post("/") - .then() - .statusCode(400); - } - - @Test - public void mustReturnInvalidArgumentOnInvalidState() throws Exception { - ObjectNode json = new ObjectNode(new JsonNodeFactory(false)); - json.put("type", "invalidArgument"); - - when(requestHandler.handle(any())) - .thenReturn(Stream.of(new InvocationResponse(ErrorResponse.ERROR_METHOD, json, MethodCallId.of("#0")))); - - given() - .body("[[\"getAccounts\", {\"state\":false}, \"#0\"]]") - .when() - .post("/") - .then() - .statusCode(200) - .body(equalTo("[[\"error\",{\"type\":\"invalidArgument\"},\"#0\"]]")); - } - - @Test - public void mustReturnAccountsOnValidRequest() throws Exception { - ObjectNode json = new ObjectNode(new JsonNodeFactory(false)); - json.put("state", "f6a7e214"); - ArrayNode arrayNode = json.putArray("list"); - ObjectNode list = new ObjectNode(new JsonNodeFactory(false)); - list.put("id", "6asf5"); - list.put("name", "roger@barcamp"); - arrayNode.add(list); - - when(requestHandler.handle(any())) - .thenReturn(Stream.of(new InvocationResponse(Method.Response.name("accounts"), json, MethodCallId.of("#0")))); - - given() - .body("[[\"getAccounts\", {}, \"#0\"]]") - .when() - .post("/") - .then() - .statusCode(200) - .body(equalTo("[[\"accounts\",{" + - "\"state\":\"f6a7e214\"," + - "\"list\":[" + - "{" + - "\"id\":\"6asf5\"," + - "\"name\":\"roger@barcamp\"" + - "}" + - "]" + - "},\"#0\"]]")); - } -} diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/UserProvisioningFilterTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/UserProvisioningFilterTest.java deleted file mode 100644 index 23a2f81..0000000 --- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/UserProvisioningFilterTest.java +++ /dev/null @@ -1,132 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you 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 org.apache.james.jmap.draft; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import java.io.IOException; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.james.core.Username; -import org.apache.james.domainlist.api.DomainList; -import org.apache.james.mailbox.MailboxSession; -import org.apache.james.mailbox.MailboxSessionUtil; -import org.apache.james.metrics.tests.RecordingMetricFactory; -import org.apache.james.user.api.UsersRepository; -import org.apache.james.user.api.UsersRepositoryException; -import org.apache.james.user.memory.MemoryUsersRepository; -import org.junit.Before; -import org.junit.Test; - -public class UserProvisioningFilterTest { - private static final Username USERNAME = Username.of("username"); - private static final Username USERNAME_WITH_DOMAIN = Username.of("usern...@james.org"); - private static final DomainList NO_DOMAIN_LIST = null; - - private UserProvisioningFilter sut; - private MemoryUsersRepository usersRepository; - private HttpServletRequest request; - private HttpServletResponse response; - private FilterChain chain; - - @Before - public void setup() throws Exception { - usersRepository = MemoryUsersRepository.withoutVirtualHosting(NO_DOMAIN_LIST); - sut = new UserProvisioningFilter(usersRepository, new RecordingMetricFactory()); - request = mock(HttpServletRequest.class); - response = mock(HttpServletResponse.class); - chain = mock(FilterChain.class); - } - - @Test - public void filterShouldDoNothingOnNullSession() throws IOException, ServletException, UsersRepositoryException { - sut.doFilter(request, response, chain); - - verify(chain).doFilter(request, response); - assertThat(usersRepository.list()).toIterable() - .isEmpty(); - } - - @Test - public void filterShouldAddUsernameWhenNoVirtualHostingAndMailboxSessionContainsUsername() throws Exception { - usersRepository.setEnableVirtualHosting(false); - MailboxSession mailboxSession = MailboxSessionUtil.create(USERNAME); - when(request.getAttribute(AuthenticationFilter.MAILBOX_SESSION)) - .thenReturn(mailboxSession); - - sut.doFilter(request, response, chain); - - verify(chain).doFilter(request, response); - assertThat(usersRepository.list()).toIterable() - .contains(USERNAME); - } - - @Test - public void filterShouldFailOnInvalidVirtualHosting() { - usersRepository.setEnableVirtualHosting(false); - MailboxSession mailboxSession = MailboxSessionUtil.create(USERNAME_WITH_DOMAIN); - when(request.getAttribute(AuthenticationFilter.MAILBOX_SESSION)) - .thenReturn(mailboxSession); - - assertThatThrownBy(() -> sut.doFilter(request, response, chain)) - .hasCauseInstanceOf(UsersRepositoryException.class); - } - - @Test - public void filterShouldNotTryToAddUserWhenReadOnlyUsersRepository() throws Exception { - UsersRepository usersRepository = mock(UsersRepository.class); - when(usersRepository.isReadOnly()).thenReturn(true); - sut = new UserProvisioningFilter(usersRepository, new RecordingMetricFactory()); - - MailboxSession mailboxSession = MailboxSessionUtil.create(USERNAME_WITH_DOMAIN); - when(request.getAttribute(AuthenticationFilter.MAILBOX_SESSION)) - .thenReturn(mailboxSession); - - sut.doFilter(request, response, chain); - - verify(usersRepository).isReadOnly(); - verifyNoMoreInteractions(usersRepository); - } - - @Test - public void filterShouldChainCallsWhenReadOnlyUsersRepository() throws Exception { - UsersRepository usersRepository = mock(UsersRepository.class); - when(usersRepository.isReadOnly()).thenReturn(true); - sut = new UserProvisioningFilter(usersRepository, new RecordingMetricFactory()); - - MailboxSession mailboxSession = MailboxSessionUtil.create(USERNAME_WITH_DOMAIN); - when(request.getAttribute(AuthenticationFilter.MAILBOX_SESSION)) - .thenReturn(mailboxSession); - - sut.doFilter(request, response, chain); - - verify(chain).doFilter(eq(request), any()); - } -} diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/UserProvisioningFilterThreadTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/UserProvisioningFilterThreadTest.java deleted file mode 100644 index 8ee5474..0000000 --- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/UserProvisioningFilterThreadTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you 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 org.apache.james.jmap.draft; - -import java.time.Duration; -import java.util.concurrent.ExecutionException; - -import org.apache.james.core.Username; -import org.apache.james.domainlist.api.DomainList; -import org.apache.james.mailbox.MailboxSession; -import org.apache.james.mailbox.MailboxSessionUtil; -import org.apache.james.metrics.tests.RecordingMetricFactory; -import org.apache.james.user.memory.MemoryUsersRepository; -import org.apache.james.util.concurrency.ConcurrentTestRunner; -import org.junit.Before; -import org.junit.Test; - -public class UserProvisioningFilterThreadTest { - private static final DomainList NO_DOMAIN_LIST = null; - - private UserProvisioningFilter sut; - private MemoryUsersRepository usersRepository; - private MailboxSession session; - - @Before - public void before() { - usersRepository = MemoryUsersRepository.withoutVirtualHosting(NO_DOMAIN_LIST); - session = MailboxSessionUtil.create(Username.of("username")); - sut = new UserProvisioningFilter(usersRepository, new RecordingMetricFactory()); - } - - @Test - public void testConcurrentAccessToFilterShouldNotThrow() throws ExecutionException, InterruptedException { - ConcurrentTestRunner - .builder() - .operation((threadNumber, step) -> sut.createAccountIfNeeded(session)) - .threadCount(2) - .runSuccessfullyWithin(Duration.ofMinutes(1)); - } -} - diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/DownloadPathTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/DownloadPathTest.java deleted file mode 100644 index c66c625..0000000 --- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/DownloadPathTest.java +++ /dev/null @@ -1,84 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you 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 org.apache.james.jmap.draft.utils; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import org.junit.Test; - -public class DownloadPathTest { - - @Test - public void fromShouldThrowWhenPathIsNull() { - assertThatThrownBy(() -> DownloadPath.from(null)).isInstanceOf(IllegalArgumentException.class); - } - - @Test - public void fromShouldThrowWhenPathIsEmpty() { - assertThatThrownBy(() -> DownloadPath.from("")).isInstanceOf(IllegalArgumentException.class); - } - - @Test - public void fromShouldThrowWhenNoBlobId() { - assertThatThrownBy(() -> DownloadPath.from("/")).isInstanceOf(IllegalArgumentException.class); - } - - @Test - public void fromShouldThrowWhenBlobIdIsEmpty() { - assertThatThrownBy(() -> DownloadPath.from("//")).isInstanceOf(IllegalArgumentException.class); - } - - @Test - public void fromShouldParseWhenBlobId() { - String expectedBlobId = "123456789"; - DownloadPath downloadPath = DownloadPath.from("/" + expectedBlobId); - assertThat(downloadPath.getBlobId()).isEqualTo(expectedBlobId); - } - - @Test - public void nameShouldBeEmptyWhenNone() { - DownloadPath downloadPath = DownloadPath.from("/123456789"); - assertThat(downloadPath.getName()).isEmpty(); - } - - @Test - public void nameShouldBeEmptyWhenNoneButSlash() { - DownloadPath downloadPath = DownloadPath.from("/123456789/"); - assertThat(downloadPath.getName()).isEmpty(); - } - - @Test - public void nameShouldBePresentWhenGiven() { - String expectedName = "myName"; - DownloadPath downloadPath = DownloadPath.from("/123456789/" + expectedName); - assertThat(downloadPath.getName()).hasValue(expectedName); - } - - @Test - public void fromShouldThrowWhenExtraPathVariables() { - assertThatThrownBy(() -> DownloadPath.from("/123456789/myName/132/456/789")).isInstanceOf(IllegalArgumentException.class); - } - - @Test - public void fromShouldThrowWhenExtraPathSeparator() { - assertThatThrownBy(() -> DownloadPath.from("/123456789//myName")).isInstanceOf(IllegalArgumentException.class); - } -} diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/HeadersAuthenticationExtractorTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/HeadersAuthenticationExtractorTest.java deleted file mode 100644 index 56794f6..0000000 --- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/HeadersAuthenticationExtractorTest.java +++ /dev/null @@ -1,69 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you 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 org.apache.james.jmap.draft.utils; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.util.Collections; - -import javax.servlet.http.HttpServletRequest; - -import org.junit.Before; -import org.junit.Test; - -import com.google.common.collect.ImmutableList; - -public class HeadersAuthenticationExtractorTest { - - private HeadersAuthenticationExtractor testee; - - @Before - public void setup() { - testee = new HeadersAuthenticationExtractor(); - } - - @Test - public void authHeadersShouldThrowWhenRequestIsNull() { - assertThatThrownBy(() -> testee.authHeaders(null)) - .isExactlyInstanceOf(IllegalArgumentException.class); - } - - @Test - public void authHeadersShouldReturnEmptyStreamWhenNoAuthorizationHeader() { - HttpServletRequest request = mock(HttpServletRequest.class); - when(request.getHeaders("Authorization")) - .thenReturn(Collections.emptyEnumeration()); - - assertThat(testee.authHeaders(request)).isEmpty(); - } - - @Test - public void authHeadersShouldReturnStreamOfHeadersWhenSome() { - HttpServletRequest request = mock(HttpServletRequest.class); - ImmutableList<String> expectedHeaders = ImmutableList.of("header", "otherHeader"); - when(request.getHeaders("Authorization")) - .thenReturn(Collections.enumeration(expectedHeaders)); - - assertThat(testee.authHeaders(request)).containsOnlyElementsOf(expectedHeaders); - } -} --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org