diff options
Diffstat (limited to 'src')
52 files changed, 1223 insertions, 565 deletions
diff --git a/src/main/assets/scripts.js b/src/main/assets/scripts.js index b103810d..cc811808 100644 --- a/src/main/assets/scripts.js +++ b/src/main/assets/scripts.js @@ -417,6 +417,7 @@ function checkUsername() { /******************************************************************************/ function openDialogLogin() { + const token = document.body.getAttribute('data-token'); let html = ` <div class="dialoglogin"> <p>${i18n('loginDialog.pleaseIntroduceYourself')}:</p> @@ -426,6 +427,7 @@ function openDialogLogin() { <a href="/_apple" id="signapple"><img src="https://appleid.cdn-apple.com/appleid/button"></a> <p>${i18n('loginDialog.registeredAlready')}</p> <form action="/login" method="POST"> + <input type="hidden" name="_csrf" value="${token}" /> <input class="signinput" type="text" name="username" placeholder="${i18n('loginDialog.username')}" autocomplete="username" /><br/> <input class="signinput" type="password" name="password" placeholder="${i18n('loginDialog.password')}" autocomplete="current-password" /><br/> <input class="signsubmit Button" type="submit" value="OK"/> diff --git a/src/main/java/com/cliqset/xrd/Alias.java b/src/main/java/com/cliqset/xrd/Alias.java index 49e4052b..3a108f46 100644 --- a/src/main/java/com/cliqset/xrd/Alias.java +++ b/src/main/java/com/cliqset/xrd/Alias.java @@ -20,13 +20,14 @@ import java.net.URI; import java.util.HashMap; import java.util.Map; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAnyAttribute; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.XmlValue; import javax.xml.namespace.QName; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlAnyAttribute; +import jakarta.xml.bind.annotation.XmlType; +import jakarta.xml.bind.annotation.XmlValue; + @XmlType(name="Alias", namespace=XRDConstants.XRD_NAMESPACE) @XmlAccessorType(XmlAccessType.FIELD) public class Alias { diff --git a/src/main/java/com/cliqset/xrd/Expires.java b/src/main/java/com/cliqset/xrd/Expires.java index b4bcdd24..bb6704f8 100644 --- a/src/main/java/com/cliqset/xrd/Expires.java +++ b/src/main/java/com/cliqset/xrd/Expires.java @@ -21,7 +21,7 @@ import java.util.HashMap; import java.util.Map; import javax.xml.namespace.QName; -import javax.xml.bind.annotation.*; +import jakarta.xml.bind.annotation.*; @XmlType(name="Expires", namespace=XRDConstants.XRD_NAMESPACE) @XmlAccessorType(XmlAccessType.FIELD) diff --git a/src/main/java/com/cliqset/xrd/Link.java b/src/main/java/com/cliqset/xrd/Link.java index ec8522f0..020defe6 100644 --- a/src/main/java/com/cliqset/xrd/Link.java +++ b/src/main/java/com/cliqset/xrd/Link.java @@ -22,13 +22,13 @@ import java.util.List; import java.util.Map; import javax.xml.namespace.QName; -import javax.xml.bind.annotation.XmlAnyElement; -import javax.xml.bind.annotation.XmlAnyAttribute; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlAnyElement; +import jakarta.xml.bind.annotation.XmlAnyAttribute; +import jakarta.xml.bind.annotation.XmlType; +import jakarta.xml.bind.annotation.XmlAttribute; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; import org.w3c.dom.Element; diff --git a/src/main/java/com/cliqset/xrd/Property.java b/src/main/java/com/cliqset/xrd/Property.java index 35c7d0cc..f7777c94 100644 --- a/src/main/java/com/cliqset/xrd/Property.java +++ b/src/main/java/com/cliqset/xrd/Property.java @@ -18,12 +18,12 @@ package com.cliqset.xrd; import java.net.URI; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAnyAttribute; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.XmlValue; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlAnyAttribute; +import jakarta.xml.bind.annotation.XmlAttribute; +import jakarta.xml.bind.annotation.XmlType; +import jakarta.xml.bind.annotation.XmlValue; import javax.xml.namespace.QName; import java.util.HashMap; diff --git a/src/main/java/com/cliqset/xrd/Subject.java b/src/main/java/com/cliqset/xrd/Subject.java index f6815317..d2f6b333 100644 --- a/src/main/java/com/cliqset/xrd/Subject.java +++ b/src/main/java/com/cliqset/xrd/Subject.java @@ -16,11 +16,11 @@ package com.cliqset.xrd; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAnyAttribute; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.XmlValue; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlAnyAttribute; +import jakarta.xml.bind.annotation.XmlType; +import jakarta.xml.bind.annotation.XmlValue; import javax.xml.namespace.QName; import java.net.URI; diff --git a/src/main/java/com/cliqset/xrd/Title.java b/src/main/java/com/cliqset/xrd/Title.java index 7d6597bd..4bb43c55 100644 --- a/src/main/java/com/cliqset/xrd/Title.java +++ b/src/main/java/com/cliqset/xrd/Title.java @@ -19,7 +19,7 @@ package com.cliqset.xrd; import java.util.HashMap; import java.util.Map; -import javax.xml.bind.annotation.*; +import jakarta.xml.bind.annotation.*; import javax.xml.namespace.QName; @XmlType(name="Title", namespace=XRDConstants.XRD_NAMESPACE) diff --git a/src/main/java/com/cliqset/xrd/XRD.java b/src/main/java/com/cliqset/xrd/XRD.java index 393e977b..8fc6f7de 100644 --- a/src/main/java/com/cliqset/xrd/XRD.java +++ b/src/main/java/com/cliqset/xrd/XRD.java @@ -17,9 +17,9 @@ package com.cliqset.xrd; import javax.xml.namespace.QName; -import javax.xml.bind.annotation.*; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; +import jakarta.xml.bind.annotation.*; +import jakarta.xml.bind.JAXBContext; +import jakarta.xml.bind.JAXBException; import org.w3c.dom.Element; diff --git a/src/main/java/com/cliqset/xrd/package-info.java b/src/main/java/com/cliqset/xrd/package-info.java index bd8f0146..dfc11f51 100644 --- a/src/main/java/com/cliqset/xrd/package-info.java +++ b/src/main/java/com/cliqset/xrd/package-info.java @@ -26,8 +26,8 @@ package com.cliqset.xrd; import org.apache.commons.lang3.StringUtils; -import javax.xml.bind.annotation.XmlNs; -import javax.xml.bind.annotation.XmlNsForm; -import javax.xml.bind.annotation.XmlSchema; +import jakarta.xml.bind.annotation.XmlNs; +import jakarta.xml.bind.annotation.XmlNsForm; +import jakarta.xml.bind.annotation.XmlSchema; import static com.cliqset.xrd.XRDConstants.XRD_NAMESPACE;
\ No newline at end of file diff --git a/src/main/java/com/juick/ActivityPubManager.java b/src/main/java/com/juick/ActivityPubManager.java index 613de046..7691aa6c 100644 --- a/src/main/java/com/juick/ActivityPubManager.java +++ b/src/main/java/com/juick/ActivityPubManager.java @@ -71,8 +71,8 @@ import com.juick.www.api.activity.model.objects.Image; import com.juick.www.api.activity.model.objects.Mention; import com.juick.www.api.activity.model.objects.Note; import com.juick.www.api.activity.model.objects.Person; -import com.mitchellbosecke.pebble.PebbleEngine; -import com.mitchellbosecke.pebble.template.PebbleTemplate; +import io.pebbletemplates.pebble.PebbleEngine; +import io.pebbletemplates.pebble.template.PebbleTemplate; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; diff --git a/src/main/java/com/juick/EmailManager.java b/src/main/java/com/juick/EmailManager.java index 8ef1a270..a62d31f3 100644 --- a/src/main/java/com/juick/EmailManager.java +++ b/src/main/java/com/juick/EmailManager.java @@ -22,6 +22,16 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.juick.model.Message; import com.juick.model.User; import com.juick.www.api.SystemActivity; + +import jakarta.mail.MessagingException; +import jakarta.mail.Multipart; +import jakarta.mail.Session; +import jakarta.mail.Transport; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeBodyPart; +import jakarta.mail.internet.MimeMessage; +import jakarta.mail.internet.MimeMultipart; + import com.juick.util.HttpBadRequestException; import com.juick.www.WebApp; import com.juick.service.EmailService; @@ -38,14 +48,6 @@ import org.springframework.scheduling.annotation.Async; import javax.annotation.Nonnull; import javax.inject.Inject; -import javax.mail.MessagingException; -import javax.mail.Multipart; -import javax.mail.Session; -import javax.mail.Transport; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeBodyPart; -import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeMultipart; import java.util.*; import static com.juick.util.formatters.PlainTextFormatter.formatPost; @@ -163,7 +165,7 @@ public class EmailManager implements NotificationListener { }; String fromAddress = StringUtils.isNotEmpty(from) ? from : "juick@juick.com"; message.setFrom(fromAddress); - message.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(to)); + message.addRecipient(jakarta.mail.Message.RecipientType.TO, new InternetAddress(to)); message.setSubject(subject); MimeBodyPart textBodyPart = new MimeBodyPart(); textBodyPart.setContent(textPart, "text/plain; charset=UTF-8"); diff --git a/src/main/java/com/juick/TelegramBotManager.java b/src/main/java/com/juick/TelegramBotManager.java index 20f31b1b..1dcc7edb 100644 --- a/src/main/java/com/juick/TelegramBotManager.java +++ b/src/main/java/com/juick/TelegramBotManager.java @@ -54,7 +54,7 @@ import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; +import jakarta.annotation.PostConstruct; import javax.inject.Inject; import java.io.IOException; import java.net.URI; diff --git a/src/main/java/com/juick/XMPPManager.java b/src/main/java/com/juick/XMPPManager.java index 6343378c..0878c450 100644 --- a/src/main/java/com/juick/XMPPManager.java +++ b/src/main/java/com/juick/XMPPManager.java @@ -21,6 +21,10 @@ import com.juick.model.User; import com.juick.util.formatters.PlainTextFormatter; import com.juick.model.CommandResult; import com.juick.www.api.SystemActivity; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; + import com.juick.www.WebApp; import com.juick.util.xmpp.iq.MessageQuery; import com.juick.service.MessagesService; @@ -66,8 +70,6 @@ import rocks.xmpp.extensions.version.SoftwareVersionManager; import rocks.xmpp.extensions.version.model.SoftwareVersion; import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; import javax.inject.Inject; import java.io.IOException; import java.net.MalformedURLException; diff --git a/src/main/java/com/juick/config/ActivityPubClientErrorHandler.java b/src/main/java/com/juick/config/ActivityPubClientErrorHandler.java index ecb81e30..69958bbd 100644 --- a/src/main/java/com/juick/config/ActivityPubClientErrorHandler.java +++ b/src/main/java/com/juick/config/ActivityPubClientErrorHandler.java @@ -28,7 +28,6 @@ import org.springframework.http.client.ClientHttpResponse; import org.springframework.stereotype.Component; import org.springframework.web.client.DefaultResponseErrorHandler; -import javax.annotation.Nonnull; import javax.inject.Inject; import java.io.IOException; import java.net.URI; @@ -41,7 +40,7 @@ public class ActivityPubClientErrorHandler extends DefaultResponseErrorHandler { @Inject private ApplicationEventPublisher applicationEventPublisher; @Override - public void handleError(URI contextUri, HttpMethod method, @Nonnull ClientHttpResponse response) throws IOException { + public void handleError(URI contextUri, HttpMethod method, ClientHttpResponse response) throws IOException { logger.warn("HTTP ERROR {} {} : {}", response.getStatusCode().value(), response.getStatusText(), IOUtils.toString(response.getBody(), StandardCharsets.UTF_8)); if (response.getStatusCode().equals(HttpStatus.GONE)) { diff --git a/src/main/java/com/juick/config/ActivityPubConfig.java b/src/main/java/com/juick/config/ActivityPubConfig.java index 89aacff0..fb204aaa 100644 --- a/src/main/java/com/juick/config/ActivityPubConfig.java +++ b/src/main/java/com/juick/config/ActivityPubConfig.java @@ -20,7 +20,8 @@ package com.juick.config; import com.fasterxml.jackson.databind.ObjectMapper; import com.juick.ActivityPubManager; import com.juick.util.ActivityPubRequestInterceptor; -import org.apache.http.impl.client.CloseableHttpClient; + +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; diff --git a/src/main/java/com/juick/config/HttpClientConfig.java b/src/main/java/com/juick/config/HttpClientConfig.java index 22c06ecb..99351828 100644 --- a/src/main/java/com/juick/config/HttpClientConfig.java +++ b/src/main/java/com/juick/config/HttpClientConfig.java @@ -1,20 +1,19 @@ package com.juick.config; import java.util.concurrent.TimeUnit; - -import org.apache.http.HeaderElement; -import org.apache.http.HeaderElementIterator; -import org.apache.http.HttpResponse; -import org.apache.http.client.config.CookieSpecs; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.conn.ConnectionKeepAliveStrategy; - -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.apache.http.message.BasicHeaderElementIterator; -import org.apache.http.protocol.HTTP; -import org.apache.http.protocol.HttpContext; + +import org.apache.hc.client5.http.ConnectionKeepAliveStrategy; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.cookie.StandardCookieSpec; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; +import org.apache.hc.core5.http.HeaderElement; +import org.apache.hc.core5.http.HttpResponse; +import org.apache.hc.core5.http.message.BasicHeaderElementIterator; +import org.apache.hc.core5.http.protocol.HttpContext; +import org.apache.hc.core5.util.TimeValue; +import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; @@ -55,19 +54,19 @@ public class HttpClientConfig { public ConnectionKeepAliveStrategy connectionKeepAliveStrategy() { return new ConnectionKeepAliveStrategy() { @Override - public long getKeepAliveDuration(HttpResponse response, HttpContext context) { - HeaderElementIterator it = new BasicHeaderElementIterator - (response.headerIterator(HTTP.CONN_KEEP_ALIVE)); + public TimeValue getKeepAliveDuration(HttpResponse response, HttpContext context) { + BasicHeaderElementIterator it = new BasicHeaderElementIterator + (response.headerIterator("Keep-Alive")); while (it.hasNext()) { - HeaderElement he = it.nextElement(); + HeaderElement he = it.next(); String param = he.getName(); String value = he.getValue(); if (value != null && param.equalsIgnoreCase("timeout")) { - return Long.parseLong(value) * 1000; + return TimeValue.of(Long.parseLong(value) * 1000, TimeUnit.MILLISECONDS); } } - return DEFAULT_KEEP_ALIVE_TIME_MILLIS; + return TimeValue.of(DEFAULT_KEEP_ALIVE_TIME_MILLIS, TimeUnit.MILLISECONDS); } }; } @@ -75,10 +74,10 @@ public class HttpClientConfig { @Bean public CloseableHttpClient httpClient() { RequestConfig requestConfig = RequestConfig.custom() - .setCookieSpec(CookieSpecs.IGNORE_COOKIES) - .setConnectionRequestTimeout(REQUEST_TIMEOUT) - .setConnectTimeout(CONNECT_TIMEOUT) - .setSocketTimeout(SOCKET_TIMEOUT).build(); + .setCookieSpec(StandardCookieSpec.IGNORE) + .setConnectionRequestTimeout(Timeout.of(REQUEST_TIMEOUT, TimeUnit.MILLISECONDS)) + .setConnectTimeout(Timeout.of(CONNECT_TIMEOUT, TimeUnit.MILLISECONDS)) + .setResponseTimeout(Timeout.of(SOCKET_TIMEOUT, TimeUnit.MILLISECONDS)).build(); return HttpClients.custom() .setDefaultRequestConfig(requestConfig) @@ -96,8 +95,8 @@ public class HttpClientConfig { try { if (connectionManager != null) { LOGGER.trace("run IdleConnectionMonitor - Closing expired and idle connections..."); - connectionManager.closeExpiredConnections(); - connectionManager.closeIdleConnections(CLOSE_IDLE_CONNECTION_WAIT_TIME_SECS, TimeUnit.SECONDS); + connectionManager.closeExpired(); + connectionManager.closeIdle(TimeValue.of(CLOSE_IDLE_CONNECTION_WAIT_TIME_SECS, TimeUnit.SECONDS)); } else { LOGGER.trace("run IdleConnectionMonitor - Http Client Connection manager is not initialised"); } diff --git a/src/main/java/com/juick/config/SecurityConfig.java b/src/main/java/com/juick/config/SecurityConfig.java index 1907c7c5..b5c93906 100644 --- a/src/main/java/com/juick/config/SecurityConfig.java +++ b/src/main/java/com/juick/config/SecurityConfig.java @@ -23,20 +23,17 @@ import com.juick.service.security.HTTPSignatureAuthenticationFilter; import com.juick.service.security.HashParamAuthenticationFilter; import com.juick.service.security.JuickUserDetailsService; import com.juick.service.security.entities.JuickUser; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.security.web.authentication.NullRememberMeServices; +import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.RememberMeServices; import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices; import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; @@ -46,18 +43,18 @@ import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import javax.annotation.Resource; -import javax.inject.Inject; import java.util.Arrays; import java.util.Collections; -import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; /** * Created by aalexeev on 11/21/16. */ @EnableWebSecurity +@Configuration public class SecurityConfig { - @Resource + @Inject private UserService userService; private static final String COOKIE_NAME = "juick-remember-me"; @@ -66,6 +63,7 @@ public class SecurityConfig { public UserDetailsService userDetailsService() { return new JuickUserDetailsService(userService); } + @Bean static CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); @@ -81,132 +79,106 @@ public class SecurityConfig { return source; } - @Configuration + @Inject + private SignatureManager signatureManager; + + @Bean + public HashParamAuthenticationFilter apiAuthenticationFilter() { + return new HashParamAuthenticationFilter(userService, null); + } + + @Bean + public AuthenticationEntryPoint juickAuthenticationEntryPoint() { + var entryPoint = new BasicAuthenticationEntryPoint(); + entryPoint.setRealmName("Juick"); + return entryPoint; + } + + @Value("${auth_remember_me_key:secret}") + private String rememberMeKey; + @Value("${web_domain:localhost}") + private String webDomain; + + @Bean + public HashParamAuthenticationFilter wwwAuthenticationFilter() { + return new HashParamAuthenticationFilter(userService, hashCookieServices()); + } + + @Bean + public RememberMeServices hashCookieServices() { + TokenBasedRememberMeServices services = new TokenBasedRememberMeServices( + rememberMeKey, userDetailsService()); + + services.setCookieName(COOKIE_NAME); + services.setCookieDomain(webDomain); + services.setAlwaysRemember(true); + services.setTokenValiditySeconds(6 * 30 * 24 * 3600); + services.setUseSecureCookie(false); // TODO set true if https is supports + return services; + } + + @Bean @Order(1) - public static class ApiConfig extends WebSecurityConfigurerAdapter { - @Value("${auth_remember_me_key:secret}") - private String rememberMeKey; - @Resource - private UserService userService; - @Resource - private SignatureManager signatureManager; - ApiConfig() { - super(true); - } - @Bean - RememberMeServices apiTokenServices() { - return new NullRememberMeServices(); - } - @Bean - public HashParamAuthenticationFilter apiAuthenticationFilter() { - return new HashParamAuthenticationFilter(userService, apiTokenServices()); - } - - @Override - protected void configure(HttpSecurity http) throws Exception { - http.antMatcher("/api/**") - .addFilterBefore(apiAuthenticationFilter(), BasicAuthenticationFilter.class) - .addFilterBefore(new HTTPSignatureAuthenticationFilter(signatureManager, userService), BasicAuthenticationFilter.class) - .authorizeRequests() - .antMatchers(HttpMethod.OPTIONS).permitAll() - .antMatchers("/api/", "/api/messages", "/api/avatar", "/api/messages/discussions", - "/api/users", "/api/thread", "/api/tags", "/api/tlgmbtwbhk", "/api/fbwbhk", - "/api/skypebotendpoint", "/api/_fblogin", "/api/_vklogin", "/api/_tglogin", - "/api/_google", "/api/_applelogin", "/api/signup", "/api/inbox", "/api/events", "/api/info/**", - "/api/nodeinfo/2.0").permitAll() - .anyRequest().hasRole("USER") - .and() - .anonymous().principal(JuickUser.ANONYMOUS_USER).authorities(JuickUser.ANONYMOUS_AUTHORITY) - .and() - .httpBasic().authenticationEntryPoint(juickAuthenticationEntryPoint()) - .and().cors().configurationSource(corsConfigurationSource()) - .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) - .and().exceptionHandling().authenticationEntryPoint(juickAuthenticationEntryPoint()) - .and() - .rememberMe() - .alwaysRemember(true) - .tokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(6 * 30)) - .rememberMeServices(apiTokenServices()) - .key(rememberMeKey) - .and() - .headers().defaultsDisabled().cacheControl(); - } - - @Bean - public AuthenticationEntryPoint juickAuthenticationEntryPoint() { - var entryPoint = new BasicAuthenticationEntryPoint(); - entryPoint.setRealmName("Juick"); - return entryPoint; - } + public SecurityFilterChain apiChain(HttpSecurity http) throws Exception { + http.securityMatcher("/api/**") + .addFilterBefore(apiAuthenticationFilter(), BasicAuthenticationFilter.class) + .addFilterBefore(new HTTPSignatureAuthenticationFilter(signatureManager, userService), + BasicAuthenticationFilter.class) + .authorizeHttpRequests(requests -> requests + .requestMatchers(HttpMethod.OPTIONS).permitAll() + .requestMatchers("/api/", "/api/messages", "/api/avatar", "/api/messages/discussions", + "/api/users", "/api/thread", "/api/tags", "/api/tlgmbtwbhk", "/api/fbwbhk", + "/api/skypebotendpoint", "/api/_fblogin", "/api/_vklogin", "/api/_tglogin", + "/api/_google", "/api/_applelogin", "/api/signup", "/api/inbox", "/api/events", + "/api/info/**", + "/api/nodeinfo/2.0") + .permitAll() + .anyRequest().hasRole("USER")) + .anonymous(anonymous -> anonymous.principal(JuickUser.ANONYMOUS_USER) + .authorities(JuickUser.ANONYMOUS_AUTHORITY)) + .httpBasic(httpBasic -> httpBasic.authenticationEntryPoint(juickAuthenticationEntryPoint())) + .cors(cors -> cors.configurationSource(corsConfigurationSource())) + .sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .exceptionHandling(exceptionHandling -> exceptionHandling.authenticationEntryPoint(juickAuthenticationEntryPoint())) + .csrf().disable() + .headers().defaultsDisabled().cacheControl(); + return http.build(); } - @Configuration - public static class WebConfig extends WebSecurityConfigurerAdapter { - @Value("${auth_remember_me_key:secret}") - private String rememberMeKey; - @Value("${web_domain:localhost}") - private String webDomain; - @Resource - private UserService userService; - @Inject - private UserDetailsService userDetailsService; - @Bean - @Qualifier("www") - public HashParamAuthenticationFilter wwwAuthenticationFilter() { - return new HashParamAuthenticationFilter(userService, hashCookieServices()); - } - @Bean - @Qualifier("www") - public RememberMeServices hashCookieServices() { - TokenBasedRememberMeServices services = new TokenBasedRememberMeServices( - rememberMeKey, userDetailsService); - - services.setCookieName(COOKIE_NAME); - services.setCookieDomain(webDomain); - services.setAlwaysRemember(true); - services.setTokenValiditySeconds(6 * 30 * 24 * 3600); - services.setUseSecureCookie(false); // TODO set true if https is supports - - return services; - } - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .addFilterBefore(wwwAuthenticationFilter(), BasicAuthenticationFilter.class) - .authorizeRequests() - .antMatchers("/settings", "/pm/**", "/**/bl", "/_twitter", "/post", "/post2", "/comment") - .authenticated() - .antMatchers("/actuator/**").hasRole("ADMIN") - .anyRequest().permitAll() - .and() - .anonymous().principal(JuickUser.ANONYMOUS_USER).authorities(JuickUser.ANONYMOUS_AUTHORITY) - .and().cors().configurationSource(corsConfigurationSource()) - .and().sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.STATELESS) - .invalidSessionUrl("/") - .and() - .logout() - .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) - .invalidateHttpSession(true) - .logoutUrl("/logout") - .logoutSuccessUrl("/") - .deleteCookies("hash", COOKIE_NAME) - .and() - .formLogin() - .loginPage("/login") - .permitAll() - .defaultSuccessUrl("/") - .loginProcessingUrl("/login") - .usernameParameter("username") - .passwordParameter("password") - .failureUrl("/login?error=1") - .and() - .rememberMe() - .rememberMeCookieDomain(webDomain).key(rememberMeKey) - .rememberMeServices(hashCookieServices()) - .and() - .csrf().disable() - .headers().defaultsDisabled().cacheControl(); - } + @Bean + public SecurityFilterChain wwwChain(HttpSecurity http) throws Exception { + http.addFilterBefore(wwwAuthenticationFilter(), BasicAuthenticationFilter.class) + .authorizeHttpRequests(authorize -> authorize + .requestMatchers("/settings", "/pm/**", "/**/bl", "/_twitter", "/post", "/post2", + "/comment") + .authenticated() + .requestMatchers("/actuator/**").hasRole("ADMIN") + .anyRequest().permitAll()) + .anonymous(anonymous -> anonymous.principal(JuickUser.ANONYMOUS_USER) + .authorities(JuickUser.ANONYMOUS_AUTHORITY)) + .cors(cors -> cors + .configurationSource(corsConfigurationSource())) + .sessionManagement( + sessionManagement -> sessionManagement + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .invalidSessionUrl("/")) + .logout(logout -> logout + .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) + .invalidateHttpSession(true) + .logoutSuccessUrl("/") + .deleteCookies("hash", COOKIE_NAME)) + .formLogin(form -> form.loginPage("/login") + .defaultSuccessUrl("/") + .loginProcessingUrl("/login") + .usernameParameter("username") + .passwordParameter("password") + .failureUrl("/login?error=1") + .permitAll()) + .csrf(csrf -> csrf.ignoringRequestMatchers("/settings/unsubscribe")) + .rememberMe(rememberMe -> rememberMe + .rememberMeCookieDomain(webDomain).key(rememberMeKey) + .rememberMeServices(hashCookieServices())) + .headers().defaultsDisabled().cacheControl(); + return http.build(); } } diff --git a/src/main/java/com/juick/config/WebConfig.java b/src/main/java/com/juick/config/WebConfig.java index 59f9a99c..bfebd77f 100644 --- a/src/main/java/com/juick/config/WebConfig.java +++ b/src/main/java/com/juick/config/WebConfig.java @@ -23,14 +23,16 @@ import com.juick.service.HelpService; import com.juick.service.StorageService; import com.juick.service.FileSystemStorageService; import com.juick.service.UserService; -import com.mitchellbosecke.pebble.PebbleEngine; import com.mitchellbosecke.pebble.extension.FormatterExtension; -import com.mitchellbosecke.pebble.loader.ClasspathLoader; -import com.mitchellbosecke.pebble.loader.Loader; -import com.mitchellbosecke.pebble.spring.extension.SpringExtension; -import com.mitchellbosecke.pebble.spring.servlet.PebbleViewResolver; import com.overzealous.remark.Options; import com.overzealous.remark.Remark; + +import io.pebbletemplates.pebble.PebbleEngine; +import io.pebbletemplates.pebble.loader.ClasspathLoader; +import io.pebbletemplates.pebble.loader.Loader; +import io.pebbletemplates.spring.extension.SpringExtension; +import io.pebbletemplates.spring.servlet.PebbleViewResolver; + import org.apache.commons.codec.CharEncoding; import org.commonmark.ext.autolink.AutolinkExtension; import org.commonmark.node.Link; @@ -175,6 +177,7 @@ public class WebConfig implements WebMvcConfigurer { viewResolver.setPrefix("templates"); viewResolver.setSuffix(".html"); viewResolver.setCharacterEncoding(CharEncoding.UTF_8); + viewResolver.setExposeRequestAttributes(true); return viewResolver; } @Override diff --git a/src/main/java/com/juick/model/Message.java b/src/main/java/com/juick/model/Message.java index 2a5883ac..cfd2582b 100644 --- a/src/main/java/com/juick/model/Message.java +++ b/src/main/java/com/juick/model/Message.java @@ -20,11 +20,17 @@ import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.juick.util.adapters.SimpleDateAdapter; + +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlAttribute; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlTransient; +import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + import org.apache.commons.lang3.builder.ToStringBuilder; import javax.annotation.Nonnull; -import javax.xml.bind.annotation.*; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import java.io.Serializable; import java.net.URI; import java.time.Instant; @@ -120,7 +126,7 @@ public class Message implements Comparable<Message>, Serializable { } @Override - public int compareTo(@Nonnull Message jmsg) throws ClassCastException { + public int compareTo(Message jmsg) throws ClassCastException { if (jmsg == this) return 0; diff --git a/src/main/java/com/juick/model/Tag.java b/src/main/java/com/juick/model/Tag.java index 75266303..e7fcde5d 100644 --- a/src/main/java/com/juick/model/Tag.java +++ b/src/main/java/com/juick/model/Tag.java @@ -18,7 +18,12 @@ package com.juick.model; import com.fasterxml.jackson.annotation.JsonValue; -import javax.xml.bind.annotation.*; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlTransient; +import jakarta.xml.bind.annotation.XmlValue; + import java.io.Serializable; import java.util.Comparator; import java.util.Objects; diff --git a/src/main/java/com/juick/model/User.java b/src/main/java/com/juick/model/User.java index b43257ed..5cc0d125 100644 --- a/src/main/java/com/juick/model/User.java +++ b/src/main/java/com/juick/model/User.java @@ -18,14 +18,16 @@ package com.juick.model; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlAttribute; +import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlTransient; + import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.ToStringBuilder; import javax.annotation.Nonnull; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlTransient; import java.io.Serializable; import java.net.URI; import java.time.Instant; diff --git a/src/main/java/com/juick/model/package-info.java b/src/main/java/com/juick/model/package-info.java index be9e6c2b..9e8d4b34 100644 --- a/src/main/java/com/juick/model/package-info.java +++ b/src/main/java/com/juick/model/package-info.java @@ -30,6 +30,6 @@ package com.juick.model; import org.apache.commons.lang3.StringUtils; -import javax.xml.bind.annotation.XmlNs; -import javax.xml.bind.annotation.XmlNsForm; -import javax.xml.bind.annotation.XmlSchema;
\ No newline at end of file +import jakarta.xml.bind.annotation.XmlNs; +import jakarta.xml.bind.annotation.XmlNsForm; +import jakarta.xml.bind.annotation.XmlSchema;
\ No newline at end of file diff --git a/src/main/java/com/juick/service/UserServiceImpl.java b/src/main/java/com/juick/service/UserServiceImpl.java index 36f37d5e..71f2dc91 100644 --- a/src/main/java/com/juick/service/UserServiceImpl.java +++ b/src/main/java/com/juick/service/UserServiceImpl.java @@ -129,7 +129,7 @@ public class UserServiceImpl extends BaseJdbcService implements UserService { return stmt; }, holder); - } catch (DuplicateKeyException e) { + } catch (DataIntegrityViolationException e) { throw new UsernameTakenException(); } diff --git a/src/main/java/com/juick/service/security/HTTPSignatureAuthenticationFilter.java b/src/main/java/com/juick/service/security/HTTPSignatureAuthenticationFilter.java index 4eeb41b4..723cf576 100644 --- a/src/main/java/com/juick/service/security/HTTPSignatureAuthenticationFilter.java +++ b/src/main/java/com/juick/service/security/HTTPSignatureAuthenticationFilter.java @@ -28,11 +28,10 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.filter.OncePerRequestFilter; -import javax.annotation.Nonnull; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Collections; import java.util.Map; @@ -43,15 +42,16 @@ public class HTTPSignatureAuthenticationFilter extends OncePerRequestFilter { private final SignatureManager signatureManager; private final UserService userService; - public HTTPSignatureAuthenticationFilter( final SignatureManager signatureManager, final UserService userService) { this.signatureManager = signatureManager; this.userService = userService; } + @Override - protected void doFilterInternal(@Nonnull HttpServletRequest request, @Nonnull HttpServletResponse response, @Nonnull FilterChain filterChain) throws IOException, ServletException { + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws IOException, ServletException { if (authenticationIsRequired()) { Map<String, String> headers = Collections.list(request.getHeaderNames()) .stream() @@ -63,7 +63,8 @@ public class HTTPSignatureAuthenticationFilter extends OncePerRequestFilter { if (userUri.length() == 0) { User userWithPassword = userService.getUserByName(user.getName()); userWithPassword.setAuthHash(userService.getHashByUID(userWithPassword.getUid())); - Authentication authentication = new UsernamePasswordAuthenticationToken(userWithPassword.getName(), userWithPassword.getCredentials()); + Authentication authentication = new UsernamePasswordAuthenticationToken( + new JuickUser(user), userWithPassword.getCredentials(), JuickUser.USER_AUTHORITY); SecurityContextHolder.getContext().setAuthentication(authentication); } else { Authentication authentication = new AnonymousAuthenticationToken(userUri, diff --git a/src/main/java/com/juick/service/security/HashParamAuthenticationFilter.java b/src/main/java/com/juick/service/security/HashParamAuthenticationFilter.java index 68ae91ee..0f4ac66f 100644 --- a/src/main/java/com/juick/service/security/HashParamAuthenticationFilter.java +++ b/src/main/java/com/juick/service/security/HashParamAuthenticationFilter.java @@ -21,41 +21,40 @@ import com.juick.model.User; import com.juick.service.UserService; import com.juick.service.security.entities.JuickUser; import org.apache.commons.lang3.StringUtils; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.authentication.RememberMeAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.authentication.NullRememberMeServices; import org.springframework.security.web.authentication.RememberMeServices; import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices; import org.springframework.util.Assert; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.WebUtils; -import javax.annotation.Nonnull; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; /** * Created by aalexeev on 4/5/17. */ public class HashParamAuthenticationFilter extends OncePerRequestFilter { + public static final String PARAM_NAME = "hash"; private final UserService userService; private final RememberMeServices rememberMeServices; - public HashParamAuthenticationFilter( - final UserService userService, - final RememberMeServices rememberMeServices) { + @NonNull final UserService userService, + @Nullable final RememberMeServices rememberMeServices) { Assert.notNull(userService, "userService should not be null"); - Assert.notNull(rememberMeServices, "rememberMeServices should not be null"); this.userService = userService; this.rememberMeServices = rememberMeServices; @@ -63,28 +62,31 @@ public class HashParamAuthenticationFilter extends OncePerRequestFilter { @Override protected void doFilterInternal( - @Nonnull HttpServletRequest request, - @Nonnull HttpServletResponse response, - @Nonnull FilterChain filterChain) throws ServletException, IOException { + HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { String hash = getHashFromRequest(request); - if (hash != null && authenticationIsRequired()) { User user = userService.getUserByHash(hash); - if (!user.isAnonymous()) { User userWithPassword = userService.getUserByName(user.getName()); userWithPassword.setAuthHash(userService.getHashByUID(userWithPassword.getUid())); - Authentication authentication = rememberMeServices instanceof NullRememberMeServices - ? new UsernamePasswordAuthenticationToken(userWithPassword.getName(), - userWithPassword.getCredentials()) - : new RememberMeAuthenticationToken( - ((AbstractRememberMeServices)rememberMeServices).getKey(), - new JuickUser(userWithPassword), JuickUser.USER_AUTHORITY); - - SecurityContextHolder.getContext().setAuthentication(authentication); - - rememberMeServices.loginSuccess(request, response, authentication); + if (rememberMeServices != null) { + // web login should create cookie + var authentication = new RememberMeAuthenticationToken( + ((AbstractRememberMeServices) rememberMeServices).getKey(), + new JuickUser(userWithPassword), JuickUser.USER_AUTHORITY); + SecurityContextHolder.getContext().setAuthentication(authentication); + rememberMeServices.loginSuccess(request, response, authentication); + } else { + Authentication authentication = new UsernamePasswordAuthenticationToken( + new JuickUser(userWithPassword), + userWithPassword.getCredentials(), + JuickUser.USER_AUTHORITY); + SecurityContextHolder.getContext().setAuthentication(authentication); + + } } } diff --git a/src/main/java/com/juick/util/adapters/SimpleDateAdapter.java b/src/main/java/com/juick/util/adapters/SimpleDateAdapter.java index 46d4cc47..664a37a0 100644 --- a/src/main/java/com/juick/util/adapters/SimpleDateAdapter.java +++ b/src/main/java/com/juick/util/adapters/SimpleDateAdapter.java @@ -19,7 +19,8 @@ package com.juick.util.adapters; import com.juick.util.DateFormattersHolder; -import javax.xml.bind.annotation.adapters.XmlAdapter; +import jakarta.xml.bind.annotation.adapters.XmlAdapter; + import java.time.Instant; /** diff --git a/src/main/java/com/juick/util/xmpp/JidConverter.java b/src/main/java/com/juick/util/xmpp/JidConverter.java index f2add967..4f457164 100644 --- a/src/main/java/com/juick/util/xmpp/JidConverter.java +++ b/src/main/java/com/juick/util/xmpp/JidConverter.java @@ -21,12 +21,10 @@ import org.springframework.core.convert.converter.Converter; import org.springframework.lang.Nullable; import rocks.xmpp.addr.Jid; -import javax.annotation.Nonnull; - public class JidConverter implements Converter<String, Jid> { @Nullable @Override - public Jid convert(@Nonnull String jidStr) { + public Jid convert(String jidStr) { return Jid.of(jidStr); } } diff --git a/src/main/java/com/juick/util/xmpp/iq/MessageQuery.java b/src/main/java/com/juick/util/xmpp/iq/MessageQuery.java index 8a8d5614..aa1c1573 100644 --- a/src/main/java/com/juick/util/xmpp/iq/MessageQuery.java +++ b/src/main/java/com/juick/util/xmpp/iq/MessageQuery.java @@ -17,7 +17,7 @@ package com.juick.util.xmpp.iq; -import javax.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "query") public class MessageQuery { diff --git a/src/main/java/com/juick/util/xmpp/iq/package-info.java b/src/main/java/com/juick/util/xmpp/iq/package-info.java index 6d0395b1..cf8dcd4d 100644 --- a/src/main/java/com/juick/util/xmpp/iq/package-info.java +++ b/src/main/java/com/juick/util/xmpp/iq/package-info.java @@ -19,7 +19,7 @@ @XmlSchema(namespace = "http://juick.com/", elementFormDefault = XmlNsForm.QUALIFIED) package com.juick.util.xmpp.iq; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlNsForm; -import javax.xml.bind.annotation.XmlSchema;
\ No newline at end of file +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlNsForm; +import jakarta.xml.bind.annotation.XmlSchema;
\ No newline at end of file diff --git a/src/main/java/com/juick/www/VaryHandler.java b/src/main/java/com/juick/www/VaryHandler.java index 372847a9..6910823d 100644 --- a/src/main/java/com/juick/www/VaryHandler.java +++ b/src/main/java/com/juick/www/VaryHandler.java @@ -20,7 +20,7 @@ package com.juick.www; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ModelAttribute; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponse; @ControllerAdvice public class VaryHandler { diff --git a/src/main/java/com/juick/www/WebApp.java b/src/main/java/com/juick/www/WebApp.java index cebedb9b..a7c5eb8b 100644 --- a/src/main/java/com/juick/www/WebApp.java +++ b/src/main/java/com/juick/www/WebApp.java @@ -27,15 +27,16 @@ import java.util.Map; import java.util.Optional; import java.util.stream.Stream; -import javax.annotation.PostConstruct; import javax.inject.Inject; import com.juick.model.Message; import com.juick.model.Tag; import com.juick.model.User; import com.juick.service.TagService; -import com.mitchellbosecke.pebble.PebbleEngine; -import com.mitchellbosecke.pebble.template.PebbleTemplate; +import io.pebbletemplates.pebble.PebbleEngine; +import io.pebbletemplates.pebble.template.PebbleTemplate; + +import jakarta.annotation.PostConstruct; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; diff --git a/src/main/java/com/juick/www/api/ApiSocialLogin.java b/src/main/java/com/juick/www/api/ApiSocialLogin.java index ccb9f923..38884cd6 100644 --- a/src/main/java/com/juick/www/api/ApiSocialLogin.java +++ b/src/main/java/com/juick/www/api/ApiSocialLogin.java @@ -48,7 +48,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.util.UriComponentsBuilder; -import javax.annotation.PostConstruct; +import jakarta.annotation.PostConstruct; import javax.inject.Inject; import java.io.IOException; import java.security.GeneralSecurityException; diff --git a/src/main/java/com/juick/www/api/Post.java b/src/main/java/com/juick/www/api/Post.java index bced92bf..9faf883a 100644 --- a/src/main/java/com/juick/www/api/Post.java +++ b/src/main/java/com/juick/www/api/Post.java @@ -23,7 +23,7 @@ import java.util.List; import java.util.Optional; import javax.inject.Inject; -import javax.validation.constraints.NotNull; +import jakarta.validation.constraints.NotNull; import com.juick.ActivityPubManager; import com.juick.CommandsManager; diff --git a/src/main/java/com/juick/www/api/Service.java b/src/main/java/com/juick/www/api/Service.java index 81b24a23..3bb760ff 100644 --- a/src/main/java/com/juick/www/api/Service.java +++ b/src/main/java/com/juick/www/api/Service.java @@ -32,6 +32,11 @@ import com.juick.service.UserService; import com.juick.service.component.AccountVerificationEvent; import com.juick.service.security.annotation.Visitor; import io.swagger.v3.oas.annotations.Hidden; +import jakarta.mail.Session; +import jakarta.mail.internet.AddressException; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeMessage; + import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.RandomStringUtils; @@ -52,10 +57,6 @@ import org.springframework.web.context.request.async.AsyncRequestTimeoutExceptio import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import javax.inject.Inject; -import javax.mail.Session; -import javax.mail.internet.AddressException; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeMessage; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; diff --git a/src/main/java/com/juick/www/controllers/Login.java b/src/main/java/com/juick/www/controllers/Login.java index 41d902de..f78ccef0 100644 --- a/src/main/java/com/juick/www/controllers/Login.java +++ b/src/main/java/com/juick/www/controllers/Login.java @@ -20,6 +20,8 @@ import com.juick.model.User; import com.juick.service.UserService; import com.juick.service.security.annotation.Visitor; +import jakarta.servlet.http.HttpSession; + import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.WebAttributes; import org.springframework.stereotype.Controller; @@ -28,7 +30,6 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import javax.inject.Inject; -import javax.servlet.http.HttpSession; /** * @author Ugnich Anton diff --git a/src/main/java/com/juick/www/controllers/Settings.java b/src/main/java/com/juick/www/controllers/Settings.java index 1dc5f1c3..1e40b9d1 100644 --- a/src/main/java/com/juick/www/controllers/Settings.java +++ b/src/main/java/com/juick/www/controllers/Settings.java @@ -25,15 +25,6 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import javax.inject.Inject; -import javax.mail.Message; -import javax.mail.MessagingException; -import javax.mail.Session; -import javax.mail.Transport; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeMessage; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import com.juick.model.NotifyOpts; import com.juick.model.User; @@ -49,6 +40,16 @@ import com.juick.util.HttpBadRequestException; import com.juick.util.HttpUtils; import com.juick.www.WebApp; +import jakarta.mail.Message; +import jakarta.mail.MessagingException; +import jakarta.mail.Session; +import jakarta.mail.Transport; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeMessage; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; diff --git a/src/main/java/com/juick/www/controllers/Site.java b/src/main/java/com/juick/www/controllers/Site.java index 5f034b86..46440b2b 100644 --- a/src/main/java/com/juick/www/controllers/Site.java +++ b/src/main/java/com/juick/www/controllers/Site.java @@ -24,6 +24,9 @@ import com.juick.util.HttpForbiddenException; import com.juick.util.HttpNotFoundException; import com.juick.util.WebUtils; import com.juick.www.WebApp; + +import jakarta.servlet.http.HttpServletRequest; + import com.juick.service.*; import com.juick.service.security.annotation.Visitor; import com.juick.util.MessageUtils; @@ -42,7 +45,6 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestParam; import javax.inject.Inject; -import javax.servlet.http.HttpServletRequest; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; diff --git a/src/main/java/com/juick/www/controllers/SocialLogin.java b/src/main/java/com/juick/www/controllers/SocialLogin.java index f4f8dbfd..dad3d969 100644 --- a/src/main/java/com/juick/www/controllers/SocialLogin.java +++ b/src/main/java/com/juick/www/controllers/SocialLogin.java @@ -29,6 +29,13 @@ import com.juick.service.TelegramService; import com.juick.service.UserService; import com.juick.service.security.annotation.Visitor; import com.juick.util.HttpBadRequestException; + +import jakarta.annotation.PostConstruct; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; + import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.HmacAlgorithms; import org.apache.commons.codec.digest.HmacUtils; @@ -46,12 +53,7 @@ import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.util.UriComponentsBuilder; -import javax.annotation.PostConstruct; import javax.inject.Inject; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; import java.io.IOException; import java.util.List; diff --git a/src/main/java/com/juick/www/filters/AnythingFilter.java b/src/main/java/com/juick/www/filters/AnythingFilter.java index 7f0950a4..a725a66f 100644 --- a/src/main/java/com/juick/www/filters/AnythingFilter.java +++ b/src/main/java/com/juick/www/filters/AnythingFilter.java @@ -19,6 +19,12 @@ package com.juick.www.filters; import com.juick.model.User; import com.juick.util.WebUtils; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + import com.juick.service.MessagesService; import com.juick.service.UserService; import org.apache.commons.lang3.math.NumberUtils; @@ -27,11 +33,7 @@ import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import org.springframework.web.util.UriComponents; -import javax.annotation.Nonnull; import javax.inject.Inject; -import javax.servlet.*; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component @@ -42,9 +44,9 @@ public class AnythingFilter extends OncePerRequestFilter { private UserService userService; @Override - public void doFilterInternal(@Nonnull HttpServletRequest servletRequest, - @Nonnull HttpServletResponse servletResponse, - @Nonnull FilterChain filterChain) throws IOException, ServletException { + public void doFilterInternal(HttpServletRequest servletRequest, + HttpServletResponse servletResponse, + FilterChain filterChain) throws IOException, ServletException { String upgrade = servletRequest.getHeader("Connection"); if (upgrade != null && upgrade.equals("Upgrade")) { filterChain.doFilter(servletRequest, servletResponse); diff --git a/src/main/java/com/juick/www/rss/MessagesView.java b/src/main/java/com/juick/www/rss/MessagesView.java index 2c7d4a7d..dde67d7b 100644 --- a/src/main/java/com/juick/www/rss/MessagesView.java +++ b/src/main/java/com/juick/www/rss/MessagesView.java @@ -26,11 +26,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; import javax.inject.Inject; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import com.juick.model.Attachment; import com.juick.model.Message; @@ -57,6 +53,10 @@ import com.rometools.rome.feed.rss.Guid; import com.rometools.rome.feed.rss.Image; import com.rometools.rome.feed.rss.Item; +import jakarta.annotation.PostConstruct; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -81,8 +81,8 @@ public class MessagesView extends AbstractRssFeedView { @SuppressWarnings("unchecked") @Override - protected List<Item> buildFeedItems(@Nonnull Map<String, Object> model, @Nonnull HttpServletRequest request, - @Nonnull HttpServletResponse response) { + protected List<Item> buildFeedItems(Map<String, Object> model, HttpServletRequest request, + HttpServletResponse response) { List<Message> msgs = (List<Message>) model.get("messages"); return msgs.stream().map(this::createRssItem).collect(Collectors.toList()); } diff --git a/src/main/java/com/juick/www/rss/RepliesView.java b/src/main/java/com/juick/www/rss/RepliesView.java index 9cb98c94..83c8e104 100644 --- a/src/main/java/com/juick/www/rss/RepliesView.java +++ b/src/main/java/com/juick/www/rss/RepliesView.java @@ -30,15 +30,17 @@ import com.rometools.rome.feed.rss.Channel; import com.rometools.rome.feed.rss.Description; import com.rometools.rome.feed.rss.Guid; import com.rometools.rome.feed.rss.Item; + +import jakarta.annotation.PostConstruct; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.servlet.view.feed.AbstractRssFeedView; import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.net.URI; import java.net.URISyntaxException; import java.util.List; @@ -59,9 +61,9 @@ public class RepliesView extends AbstractRssFeedView { @SuppressWarnings("unchecked") @Override - protected @Nonnull List<Item> buildFeedItems(@Nonnull Map<String, Object> model, - @Nonnull HttpServletRequest request, - @Nonnull HttpServletResponse response) { + protected @Nonnull List<Item> buildFeedItems(Map<String, Object> model, + HttpServletRequest request, + HttpServletResponse response) { List<ResponseReply> msgs = (List<ResponseReply>)model.get("messages"); return msgs.stream().map(this::createRssItem).collect(Collectors.toList()); } diff --git a/src/main/java/com/mitchellbosecke/pebble/extension/FormatterExtension.java b/src/main/java/com/mitchellbosecke/pebble/extension/FormatterExtension.java index 9feca15b..bd8ba19d 100644 --- a/src/main/java/com/mitchellbosecke/pebble/extension/FormatterExtension.java +++ b/src/main/java/com/mitchellbosecke/pebble/extension/FormatterExtension.java @@ -19,6 +19,9 @@ package com.mitchellbosecke.pebble.extension; import com.mitchellbosecke.pebble.extension.filters.*; +import io.pebbletemplates.pebble.extension.AbstractExtension; +import io.pebbletemplates.pebble.extension.Filter; + import java.util.HashMap; import java.util.Map; diff --git a/src/main/java/com/mitchellbosecke/pebble/extension/filters/FormatMessageFilter.java b/src/main/java/com/mitchellbosecke/pebble/extension/filters/FormatMessageFilter.java index 41c80691..0f4ba034 100644 --- a/src/main/java/com/mitchellbosecke/pebble/extension/filters/FormatMessageFilter.java +++ b/src/main/java/com/mitchellbosecke/pebble/extension/filters/FormatMessageFilter.java @@ -19,10 +19,10 @@ package com.mitchellbosecke.pebble.extension.filters; import com.juick.model.Message; import com.juick.util.MessageUtils; -import com.mitchellbosecke.pebble.extension.Filter; -import com.mitchellbosecke.pebble.extension.escaper.SafeString; -import com.mitchellbosecke.pebble.template.EvaluationContext; -import com.mitchellbosecke.pebble.template.PebbleTemplate; +import io.pebbletemplates.pebble.extension.Filter; +import io.pebbletemplates.pebble.extension.escaper.SafeString; +import io.pebbletemplates.pebble.template.EvaluationContext; +import io.pebbletemplates.pebble.template.PebbleTemplate; import org.apache.commons.lang3.StringUtils; import java.util.List; diff --git a/src/main/java/com/mitchellbosecke/pebble/extension/filters/PrettyTimeFilter.java b/src/main/java/com/mitchellbosecke/pebble/extension/filters/PrettyTimeFilter.java index 9c79e563..f5d82bfe 100644 --- a/src/main/java/com/mitchellbosecke/pebble/extension/filters/PrettyTimeFilter.java +++ b/src/main/java/com/mitchellbosecke/pebble/extension/filters/PrettyTimeFilter.java @@ -18,9 +18,9 @@ package com.mitchellbosecke.pebble.extension.filters; import com.juick.util.PrettyTimeFormatter; -import com.mitchellbosecke.pebble.extension.Filter; -import com.mitchellbosecke.pebble.template.EvaluationContext; -import com.mitchellbosecke.pebble.template.PebbleTemplate; +import io.pebbletemplates.pebble.extension.Filter; +import io.pebbletemplates.pebble.template.EvaluationContext; +import io.pebbletemplates.pebble.template.PebbleTemplate; import java.time.Instant; import java.util.Date; diff --git a/src/main/java/com/mitchellbosecke/pebble/extension/filters/TagsListFilter.java b/src/main/java/com/mitchellbosecke/pebble/extension/filters/TagsListFilter.java index 62518a09..d3a374a2 100644 --- a/src/main/java/com/mitchellbosecke/pebble/extension/filters/TagsListFilter.java +++ b/src/main/java/com/mitchellbosecke/pebble/extension/filters/TagsListFilter.java @@ -18,9 +18,9 @@ package com.mitchellbosecke.pebble.extension.filters; import com.juick.model.Tag; -import com.mitchellbosecke.pebble.extension.Filter; -import com.mitchellbosecke.pebble.template.EvaluationContext; -import com.mitchellbosecke.pebble.template.PebbleTemplate; +import io.pebbletemplates.pebble.extension.Filter; +import io.pebbletemplates.pebble.template.EvaluationContext; +import io.pebbletemplates.pebble.template.PebbleTemplate; import java.util.Collection; import java.util.List; diff --git a/src/main/java/com/mitchellbosecke/pebble/extension/filters/TimestampFilter.java b/src/main/java/com/mitchellbosecke/pebble/extension/filters/TimestampFilter.java index 5fc93805..dfe93673 100644 --- a/src/main/java/com/mitchellbosecke/pebble/extension/filters/TimestampFilter.java +++ b/src/main/java/com/mitchellbosecke/pebble/extension/filters/TimestampFilter.java @@ -17,9 +17,9 @@ package com.mitchellbosecke.pebble.extension.filters; -import com.mitchellbosecke.pebble.extension.Filter; -import com.mitchellbosecke.pebble.template.EvaluationContext; -import com.mitchellbosecke.pebble.template.PebbleTemplate; +import io.pebbletemplates.pebble.extension.Filter; +import io.pebbletemplates.pebble.template.EvaluationContext; +import io.pebbletemplates.pebble.template.PebbleTemplate; import java.time.Instant; import java.util.Date; diff --git a/src/main/java/org/apache/commons/mail/util/MimeMessageParser.java b/src/main/java/org/apache/commons/mail/util/MimeMessageParser.java new file mode 100644 index 00000000..ec36b7cb --- /dev/null +++ b/src/main/java/org/apache/commons/mail/util/MimeMessageParser.java @@ -0,0 +1,452 @@ +/* + * 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.commons.mail.util; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jakarta.activation.DataHandler; +import jakarta.activation.DataSource; +import jakarta.mail.Message; +import jakarta.mail.MessagingException; +import jakarta.mail.Multipart; +import jakarta.mail.Part; +import jakarta.mail.internet.ContentType; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeBodyPart; +import jakarta.mail.internet.MimeMessage; +import jakarta.mail.internet.MimePart; +import jakarta.mail.internet.MimeUtility; +import jakarta.mail.internet.ParseException; +import jakarta.mail.util.ByteArrayDataSource; + +/** + * Parses a MimeMessage and stores the individual parts such a plain text, + * HTML text and attachments. + * + * @since 1.3 + */ +public class MimeMessageParser +{ + /** The MimeMessage to convert */ + private final MimeMessage mimeMessage; + + /** Plain mail content from MimeMessage */ + private String plainContent; + + /** Html mail content from MimeMessage */ + private String htmlContent; + + /** List of attachments of MimeMessage */ + private final List<DataSource> attachmentList; + + /** Attachments stored by their content-id */ + private final Map<String, DataSource> cidMap; + + /** Is this a Multipart email */ + private boolean isMultiPart; + + /** + * Constructs an instance with the MimeMessage to be extracted. + * + * @param message the message to parse + */ + public MimeMessageParser(final MimeMessage message) + { + attachmentList = new ArrayList<>(); + cidMap = new HashMap<>(); + this.mimeMessage = message; + this.isMultiPart = false; + } + + /** + * Does the actual extraction. + * + * @return this instance + * @throws Exception parsing the mime message failed + */ + public MimeMessageParser parse() throws Exception + { + this.parse(null, mimeMessage); + return this; + } + + /** + * @return the 'to' recipients of the message + * @throws Exception determining the recipients failed + */ + public List<jakarta.mail.Address> getTo() throws Exception + { + final jakarta.mail.Address[] recipients = this.mimeMessage.getRecipients(Message.RecipientType.TO); + return recipients != null ? Arrays.asList(recipients) : new ArrayList<>(); + } + + /** + * @return the 'cc' recipients of the message + * @throws Exception determining the recipients failed + */ + public List<jakarta.mail.Address> getCc() throws Exception + { + final jakarta.mail.Address[] recipients = this.mimeMessage.getRecipients(Message.RecipientType.CC); + return recipients != null ? Arrays.asList(recipients) : new ArrayList<>(); + } + + /** + * @return the 'bcc' recipients of the message + * @throws Exception determining the recipients failed + */ + public List<jakarta.mail.Address> getBcc() throws Exception + { + final jakarta.mail.Address[] recipients = this.mimeMessage.getRecipients(Message.RecipientType.BCC); + return recipients != null ? Arrays.asList(recipients) : new ArrayList<>(); + } + + /** + * @return the 'from' field of the message + * @throws Exception parsing the mime message failed + */ + public String getFrom() throws Exception + { + final jakarta.mail.Address[] addresses = this.mimeMessage.getFrom(); + if (addresses == null || addresses.length == 0) + { + return null; + } + return ((InternetAddress) addresses[0]).getAddress(); + } + + /** + * @return the 'replyTo' address of the email + * @throws Exception parsing the mime message failed + */ + public String getReplyTo() throws Exception + { + final jakarta.mail.Address[] addresses = this.mimeMessage.getReplyTo(); + if (addresses == null || addresses.length == 0) + { + return null; + } + return ((InternetAddress) addresses[0]).getAddress(); + } + + /** + * @return the mail subject + * @throws Exception parsing the mime message failed + */ + public String getSubject() throws Exception + { + return this.mimeMessage.getSubject(); + } + + /** + * Extracts the content of a MimeMessage recursively. + * + * @param parent the parent multi-part + * @param part the current MimePart + * @throws MessagingException parsing the MimeMessage failed + * @throws IOException parsing the MimeMessage failed + */ + protected void parse(final Multipart parent, final MimePart part) + throws MessagingException, IOException + { + if (isMimeType(part, "text/plain") && plainContent == null + && !Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition())) + { + plainContent = (String) part.getContent(); + } + else + { + if (isMimeType(part, "text/html") && htmlContent == null + && !Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition())) + { + htmlContent = (String) part.getContent(); + } + else + { + if (isMimeType(part, "multipart/*")) + { + this.isMultiPart = true; + final Multipart mp = (Multipart) part.getContent(); + final int count = mp.getCount(); + + // iterate over all MimeBodyPart + + for (int i = 0; i < count; i++) + { + parse(mp, (MimeBodyPart) mp.getBodyPart(i)); + } + } + else + { + final String cid = stripContentId(part.getContentID()); + final DataSource ds = createDataSource(parent, part); + if (cid != null) + { + this.cidMap.put(cid, ds); + } + this.attachmentList.add(ds); + } + } + } + } + + /** + * Strips the content id of any whitespace and angle brackets. + * @param contentId the string to strip + * @return a stripped version of the content id + */ + private String stripContentId(final String contentId) + { + if (contentId == null) + { + return null; + } + return contentId.trim().replaceAll("[\\<\\>]", ""); + } + + /** + * Checks whether the MimePart contains an object of the given mime type. + * + * @param part the current MimePart + * @param mimeType the mime type to check + * @return {@code true} if the MimePart matches the given mime type, {@code false} otherwise + * @throws MessagingException parsing the MimeMessage failed + * @throws IOException parsing the MimeMessage failed + */ + private boolean isMimeType(final MimePart part, final String mimeType) + throws MessagingException, IOException + { + // Do not use part.isMimeType(String) as it is broken for MimeBodyPart + // and does not really check the actual content type. + + try + { + final ContentType ct = new ContentType(part.getDataHandler().getContentType()); + return ct.match(mimeType); + } + catch (final ParseException ex) + { + return part.getContentType().equalsIgnoreCase(mimeType); + } + } + + /** + * Parses the MimePart to create a DataSource. + * + * @param parent the parent multi-part + * @param part the current part to be processed + * @return the DataSource + * @throws MessagingException creating the DataSource failed + * @throws IOException creating the DataSource failed + */ + protected DataSource createDataSource(final Multipart parent, final MimePart part) + throws MessagingException, IOException + { + final DataHandler dataHandler = part.getDataHandler(); + final DataSource dataSource = dataHandler.getDataSource(); + final String contentType = getBaseMimeType(dataSource.getContentType()); + byte[] content; + try (InputStream inputStream = dataSource.getInputStream()) + { + content = this.getContent(inputStream); + } + final ByteArrayDataSource result = new ByteArrayDataSource(content, contentType); + final String dataSourceName = getDataSourceName(part, dataSource); + result.setName(dataSourceName); + return result; + } + + /** @return Returns the mimeMessage. */ + public MimeMessage getMimeMessage() + { + return mimeMessage; + } + + /** @return Returns the isMultiPart. */ + public boolean isMultipart() + { + return isMultiPart; + } + + /** @return Returns the plainContent if any */ + public String getPlainContent() + { + return plainContent; + } + + /** @return Returns the attachmentList. */ + public List<DataSource> getAttachmentList() + { + return attachmentList; + } + + /** + * Returns a collection of all content-ids in the parsed message. + * <p> + * The content-ids are stripped of any angle brackets, i.e. "part1" instead + * of "<part1>". + * + * @return the collection of content ids. + * @since 1.3.4 + */ + public Collection<String> getContentIds() + { + return Collections.unmodifiableSet(cidMap.keySet()); + } + + /** @return Returns the htmlContent if any */ + public String getHtmlContent() + { + return htmlContent; + } + + /** @return true if a plain content is available */ + public boolean hasPlainContent() + { + return this.plainContent != null; + } + + /** @return true if HTML content is available */ + public boolean hasHtmlContent() + { + return this.htmlContent != null; + } + + /** @return true if attachments are available */ + public boolean hasAttachments() + { + return !this.attachmentList.isEmpty(); + } + + /** + * Find an attachment using its name. + * + * @param name the name of the attachment + * @return the corresponding datasource or null if nothing was found + */ + public DataSource findAttachmentByName(final String name) + { + DataSource dataSource; + + for (final DataSource element : getAttachmentList()) { + dataSource = element; + if (name.equalsIgnoreCase(dataSource.getName())) + { + return dataSource; + } + } + + return null; + } + + /** + * Find an attachment using its content-id. + * <p> + * The content-id must be stripped of any angle brackets, + * i.e. "part1" instead of "<part1>". + * + * @param cid the content-id of the attachment + * @return the corresponding datasource or null if nothing was found + * @since 1.3.4 + */ + public DataSource findAttachmentByCid(final String cid) + { + final DataSource dataSource = cidMap.get(cid); + return dataSource; + } + + /** + * Determines the name of the data source if it is not already set. + * + * @param part the mail part + * @param dataSource the data source + * @return the name of the data source or {@code null} if no name can be determined + * @throws MessagingException accessing the part failed + * @throws UnsupportedEncodingException decoding the text failed + */ + protected String getDataSourceName(final Part part, final DataSource dataSource) + throws MessagingException, UnsupportedEncodingException + { + String result = dataSource.getName(); + + if (result == null || result.isEmpty()) + { + result = part.getFileName(); + } + + if (result != null && !result.isEmpty()) + { + result = MimeUtility.decodeText(result); + } + else + { + result = null; + } + + return result; + } + + /** + * Read the content of the input stream. + * + * @param is the input stream to process + * @return the content of the input stream + * @throws IOException reading the input stream failed + */ + private byte[] getContent(final InputStream is) + throws IOException + { + final ByteArrayOutputStream os = new ByteArrayOutputStream(); + final BufferedInputStream isReader = new BufferedInputStream(is); + try (BufferedOutputStream osWriter = new BufferedOutputStream(os)) { + int ch; + while ((ch = isReader.read()) != -1) + { + osWriter.write(ch); + } + osWriter.flush(); + return os.toByteArray(); + } + } + + /** + * Parses the mimeType. + * + * @param fullMimeType the mime type from the mail api + * @return the real mime type + */ + private String getBaseMimeType(final String fullMimeType) + { + final int pos = fullMimeType.indexOf(';'); + if (pos >= 0) + { + return fullMimeType.substring(0, pos); + } + return fullMimeType; + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index d176bb5a..df575a3c 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,4 +1,5 @@ server.servlet.session.tracking-modes=cookie +spring.mvc.pathmatch.matching-strategy=ant-path-matcher spring.jackson.default-property-inclusion=non-default spring.jackson.serialization.write-dates-as-timestamps=false spring.jackson.serialization.write-empty-json-arrays=true diff --git a/src/main/resources/templates/layouts/default.html b/src/main/resources/templates/layouts/default.html index a2665364..9167dbfb 100644 --- a/src/main/resources/templates/layouts/default.html +++ b/src/main/resources/templates/layouts/default.html @@ -35,7 +35,7 @@ <link rel="manifest" href="//i.juick.com/manifest.json" /> </head> -<body id="body" {% if visitor.uid > 0 %}data-hash="{{visitor.authHash}}" {% endif %}> +<body id="body" {% if visitor.uid > 0 %}data-hash="{{visitor.authHash}}" {% else %}data-token="{{_csrf.token}}" {% endif %}> <div id="app"> {% include "views/partial/navigation" %} <div id="content_wrapper"> @@ -53,4 +53,4 @@ </div> </body> -</html>
\ No newline at end of file +</html> diff --git a/src/main/resources/templates/layouts/login.html b/src/main/resources/templates/layouts/login.html index e89f6790..86eb45fa 100644 --- a/src/main/resources/templates/layouts/login.html +++ b/src/main/resources/templates/layouts/login.html @@ -266,6 +266,7 @@ <div id="signinform"> <form action="/login" method="POST"> <p class="err">{{ authErrorMessage | default('') }}</p> + <input type="hidden" name="{{_csrf.parameterName}}" value="{{_csrf.token}}" /> <input class="txt" type="text" name="username" placeholder='{{ i18n("messages","label.username") }}' id="nickinput" autocomplete="username" /> <input class="txt" type="password" name="password" @@ -277,4 +278,4 @@ </body> -</html>
\ No newline at end of file +</html> diff --git a/src/main/resources/templates/views/post.html b/src/main/resources/templates/views/post.html index a77fa3bd..f1070d7a 100644 --- a/src/main/resources/templates/views/post.html +++ b/src/main/resources/templates/views/post.html @@ -5,6 +5,7 @@ <form id="postmsg"> <p style="text-align: left;"> <b>Фото:</b> <span id="attachmentfile"> + <input type="hidden" name="{{_csrf.parameterName}}" value="{{_csrf.token}}" /> <input style="width: 100%;" type="file" name="attach" accept="image/jpeg,image/png"/> <i>({{ i18n("messages","postForm.imageFormats") }})</i></span> </p> <p> diff --git a/src/test/java/com/juick/server/tests/ServerTests.java b/src/test/java/com/juick/server/tests/ServerTests.java index da777a92..69622387 100644 --- a/src/test/java/com/juick/server/tests/ServerTests.java +++ b/src/test/java/com/juick/server/tests/ServerTests.java @@ -36,6 +36,7 @@ import static org.hamcrest.collection.IsEmptyCollection.empty; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic; import static org.springframework.test.util.AssertionErrors.assertNotEquals; import static org.springframework.test.web.client.ExpectedCount.times; @@ -91,11 +92,6 @@ import java.util.stream.IntStream; import java.util.stream.StreamSupport; import javax.inject.Inject; -import javax.servlet.http.Cookie; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Marshaller; -import javax.xml.bind.Unmarshaller; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -168,9 +164,9 @@ import com.juick.www.api.activity.model.objects.Note; import com.juick.www.api.activity.model.objects.Person; import com.juick.www.api.webfinger.model.Account; import com.juick.www.api.xnodeinfo2.model.NodeInfo; -import com.mitchellbosecke.pebble.PebbleEngine; -import com.mitchellbosecke.pebble.error.PebbleException; -import com.mitchellbosecke.pebble.template.PebbleTemplate; +import io.pebbletemplates.pebble.PebbleEngine; +import io.pebbletemplates.pebble.error.PebbleException; +import io.pebbletemplates.pebble.template.PebbleTemplate; import com.overzealous.remark.Remark; import org.apache.commons.collections4.CollectionUtils; @@ -232,6 +228,11 @@ import org.xml.sax.SAXException; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jws; import io.jsonwebtoken.Jwts; +import jakarta.servlet.http.Cookie; +import jakarta.xml.bind.JAXBContext; +import jakarta.xml.bind.JAXBException; +import jakarta.xml.bind.Marshaller; +import jakarta.xml.bind.Unmarshaller; import rocks.xmpp.addr.Jid; import rocks.xmpp.core.session.Extension; import rocks.xmpp.core.session.XmppSession; @@ -376,8 +377,10 @@ public class ServerTests { ugnichPassword = "secret"; freefdName = "freefd"; freefdPassword = "MyPassw0rd!"; - ugnich = userService.createUser(ugnichName, ugnichPassword).orElseThrow(IllegalStateException::new); - freefd = userService.createUser(freefdName, freefdPassword).orElseThrow(IllegalStateException::new); + ugnich = userService.createUser(ugnichName, ugnichPassword) + .orElseThrow(IllegalStateException::new); + freefd = userService.createUser(freefdName, freefdPassword) + .orElseThrow(IllegalStateException::new); webClient.getOptions().setJavaScriptEnabled(false); webClient.getOptions().setCssEnabled(false); isSetUp = true; @@ -412,7 +415,8 @@ public class ServerTests { assertThat(messagesService.getMyFeed(freefd.getUid(), mid2, true).get(0), equalTo(mid0)); assertThat(messagesService.recommendMessage(mid0, ugnich.getUid()), equalTo(MessagesService.RecommendStatus.Added)); - assertThat(messagesService.getMessage(mid0).orElseThrow(IllegalStateException::new).getRecommendations().size(), equalTo(1)); + assertThat(messagesService.getMessage(mid0).orElseThrow(IllegalStateException::new).getRecommendations() + .size(), equalTo(1)); assertThat(messagesService.recommendMessage(mid0, ugnich.getUid()), equalTo(MessagesService.RecommendStatus.Deleted)); assertThat(messagesService.getMessage(mid0).get().getRecommendations().size(), equalTo(0)); @@ -424,7 +428,8 @@ public class ServerTests { List<User> subscribers = subscriptionService.getSubscribedUsers(ugnich.getUid(), msg); telegramService.createTelegramUser(12345, "freefd"); - String loginhash = jdbcTemplate.queryForObject("SELECT loginhash FROM telegram where tg_id=?", String.class, + String loginhash = jdbcTemplate.queryForObject("SELECT loginhash FROM telegram where tg_id=?", + String.class, 12345); userService.setTelegramUser(loginhash, freefd.getUid()); @@ -503,7 +508,8 @@ public class ServerTests { subscriptionService.subscribeMessage(msg, ugnich); int reply_id = messagesService.createReply(msg.getMid(), 0, ugnich, "comment", null); assertEquals(1, subscriptionService - .getUsersSubscribedToComments(msg, messagesService.getReply(msg.getMid(), reply_id)).size()); + .getUsersSubscribedToComments(msg, messagesService.getReply(msg.getMid(), reply_id)) + .size()); assertThat(messagesService.getDiscussions(ugnich.getUid(), 0L).get(0), equalTo(msg.getMid())); messagesService.deleteMessage(user.getUid(), mid); messagesService.deleteMessage(user.getUid(), mid2); @@ -511,7 +517,8 @@ public class ServerTests { Tag htmlTag = tagService.getTag(htmlTagName, true); TagStats htmlTagStats = new TagStats(); htmlTagStats.setTag(htmlTag); - String dbTagName = jdbcTemplate.queryForObject("select name from tags where name=?", String.class, htmlTagName); + String dbTagName = jdbcTemplate.queryForObject("select name from tags where name=?", String.class, + htmlTagName); assertEquals(dbTagName, htmlTag.getName()); int mid4 = messagesService.createMessage(user.getUid(), "yoyoyo", null, Set.of()); Message msg4 = messagesService.getMessage(mid4).get(); @@ -564,8 +571,10 @@ public class ServerTests { @Test public void lastEmailShouldNotBeDeleted() { User hugnich3 = userService.createUser("hugnich3", "x").orElseThrow(IllegalStateException::new); - jdbcTemplate.update("INSERT INTO emails(user_id,email) VALUES(?,?)", hugnich3.getUid(), "first@localhost"); - jdbcTemplate.update("INSERT INTO emails(user_id,email) VALUES(?,?)", hugnich3.getUid(), "second@localhost"); + jdbcTemplate.update("INSERT INTO emails(user_id,email) VALUES(?,?)", hugnich3.getUid(), + "first@localhost"); + jdbcTemplate.update("INSERT INTO emails(user_id,email) VALUES(?,?)", hugnich3.getUid(), + "second@localhost"); emailService.addEmail(hugnich3.getUid(), "test@email.example.com"); assertThat(emailService.getNotificationsEmail(hugnich3.getUid()), is("test@email.example.com")); emailService.disableEmail(hugnich3, "test@email.example.com"); @@ -580,11 +589,13 @@ public class ServerTests { User hugnich4 = userService.createUser("hugnich4", "x").orElseThrow(IllegalStateException::new); int mid = messagesService.createMessage(hugnich4.getUid(), "yo", null, Set.of()); Instant ts = jdbcTemplate - .queryForObject("SELECT updated FROM messages WHERE message_id=?", Timestamp.class, mid).toInstant(); + .queryForObject("SELECT updated FROM messages WHERE message_id=?", Timestamp.class, mid) + .toInstant(); Thread.sleep(1000); int rid = messagesService.createReply(mid, 0, ugnich, "people", null); Instant rts = jdbcTemplate - .queryForObject("SELECT updated FROM messages WHERE message_id=?", Timestamp.class, mid).toInstant(); + .queryForObject("SELECT updated FROM messages WHERE message_id=?", Timestamp.class, mid) + .toInstant(); assertThat(rts, greaterThan(ts)); Message msg = messagesService.getReply(mid, rid); assertThat(rts, equalTo(msg.getCreated())); @@ -618,19 +629,23 @@ public class ServerTests { .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$[0].mid", is(msg.getMid()))) .andExpect(jsonPath("$[0].timestamp", - is(DateFormattersHolder.getMessageFormatterInstance().format(msg.getCreated())))) + is(DateFormattersHolder.getMessageFormatterInstance() + .format(msg.getCreated())))) .andExpect(jsonPath("$[0].body", is(msg.getText()))) .andExpect(jsonPath("$[0].attachment.url", is(String.format("https://i.juick.com/p/%d.png", msg.getMid())))) .andExpect(jsonPath("$[0].attachment.small.url", - is(String.format("https://i.juick.com/photos-512/%d.png", msg.getMid())))) - .andExpect(jsonPath("$[0].user.avatar", is(String.format("http://localhost:8080/av-96-%s.png", hash)))); + is(String.format("https://i.juick.com/photos-512/%d.png", + msg.getMid())))) + .andExpect(jsonPath("$[0].user.avatar", + is(String.format("http://localhost:8080/av-96-%s.png", hash)))); } @Test public void homeTestWithMessagesAndRememberMe() throws Exception { String ugnichHash = userService.getHashByUID(ugnich.getUid()); - mockMvc.perform(get("/api/home").with(httpBasic(ugnichName, ugnichPassword))).andExpect(status().isOk()); + mockMvc.perform(get("/api/home").with(httpBasic(ugnichName, ugnichPassword))) + .andExpect(status().isOk()); mockMvc.perform(get("/api/home").param("hash", ugnichHash)).andExpect(status().isOk()); @@ -641,8 +656,10 @@ public class ServerTests { @Test public void homeTestWithMessagesAndSimpleCors() throws Exception { mockMvc.perform( - get("/api/home").with(httpBasic(ugnichName, ugnichPassword)).header("Origin", "http://api.example.net")) - .andExpect(status().isOk()).andExpect(header().string("Access-Control-Allow-Origin", "*")); + get("/api/home").with(httpBasic(ugnichName, ugnichPassword)).header("Origin", + "http://api.example.net")) + .andExpect(status().isOk()) + .andExpect(header().string("Access-Control-Allow-Origin", "*")); mockMvc.perform(get("/u/ugnich").header("Origin", "http://api.example.net")).andExpect(status().isOk()) .andExpect(header().string("Access-Control-Allow-Origin", "*")); } @@ -650,11 +667,15 @@ public class ServerTests { @Test public void homeTestWithPreflightCors() throws Exception { mockMvc.perform(options("/api/home").with(httpBasic(ugnichName, ugnichPassword)) - .header("Origin", "http://api.example.net").header("Access-Control-Request-Method", "POST") - .header("Access-Control-Request-Headers", "X-PINGOTHER, Content-Type")).andExpect(status().isOk()) + .header("Origin", "http://api.example.net") + .header("Access-Control-Request-Method", "POST") + .header("Access-Control-Request-Headers", "X-PINGOTHER, Content-Type")) + .andExpect(status().isOk()) .andExpect(header().string("Access-Control-Allow-Origin", "*")) - .andExpect(header().string("Access-Control-Allow-Methods", "POST,GET,PUT,OPTIONS,DELETE")) - .andExpect(header().string("Access-Control-Allow-Headers", "X-PINGOTHER, Content-Type")); + .andExpect(header().string("Access-Control-Allow-Methods", + "POST,GET,PUT,OPTIONS,DELETE")) + .andExpect(header().string("Access-Control-Allow-Headers", + "X-PINGOTHER, Content-Type")); } @Test @@ -662,8 +683,10 @@ public class ServerTests { mockMvc.perform(get("/api/messages")).andExpect(status().isOk()); - mockMvc.perform(get("/api/users").param("uname", "ugnich").param("uname", "freefd")).andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andExpect(jsonPath("$", hasSize(2))); + mockMvc.perform(get("/api/users").param("uname", "ugnich").param("uname", "freefd")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$", hasSize(2))); } @Test @@ -680,11 +703,14 @@ public class ServerTests { mockMvc.perform(get("/api/messages?" + "hash=" + userIdHash)).andDo(print()).andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect((jsonPath("$[0].reactions[?(@.id == 3)].count", is(Collections.singletonList(1))))) - .andExpect((jsonPath("$[0].reactions[?(@.id == 2)].count", is(Collections.singletonList(2))))); + .andExpect((jsonPath("$[0].reactions[?(@.id == 3)].count", + is(Collections.singletonList(1))))) + .andExpect((jsonPath("$[0].reactions[?(@.id == 2)].count", + is(Collections.singletonList(2))))); mockMvc.perform(get("/api/reactions?hash=" + userIdHash)).andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)).andExpect(jsonPath("$.length()", is(7))); + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.length()", is(7))); } @Test @@ -697,14 +723,16 @@ public class ServerTests { List<TagStats> tagsFromApi = jsonMapper.readValue(result.getResponse().getContentAsString(), new TypeReference<>() { }); - TagStats yoStats = tagsFromApi.stream().filter(t -> t.getTag().getName().equals("yo")).findFirst().get(); + TagStats yoStats = tagsFromApi.stream().filter(t -> t.getTag().getName().equals("yo")).findFirst() + .get(); assertThat(yoStats.getUsageCount(), is(2)); MvcResult result2 = mockMvc.perform(get("/api/tags").param("user_id", String.valueOf(ugnich.getUid()))) .andExpect(status().isOk()).andReturn(); List<TagStats> ugnichTagsFromApi = jsonMapper.readValue(result2.getResponse().getContentAsString(), new TypeReference<>() { }); - TagStats yoUgnichStats = ugnichTagsFromApi.stream().filter(t -> t.getTag().getName().equals("yo")).findFirst() + TagStats yoUgnichStats = ugnichTagsFromApi.stream().filter(t -> t.getTag().getName().equals("yo")) + .findFirst() .get(); assertThat(yoUgnichStats.getUsageCount(), is(1)); } @@ -717,7 +745,8 @@ public class ServerTests { @Test public void threadWithEphemeralNumberShouldReturn404() throws Exception { - mockMvc.perform(get("/api/thread").param("mid", "999999999").with(httpBasic(ugnichName, ugnichPassword))) + mockMvc.perform(get("/api/thread").param("mid", "999999999") + .with(httpBasic(ugnichName, ugnichPassword))) .andExpect(status().is4xxClientError()); } @@ -743,10 +772,13 @@ public class ServerTests { .contentType(MediaType.APPLICATION_JSON) .content(jsonMapper.writeValueAsBytes(Collections.singletonList(registration)))) .andExpect(status().isOk()); - MvcResult result = mockMvc.perform(get("/api/notifications").param("uid", String.valueOf(ugnich.getUid())) - .with(httpBasic(serviceUser.getName(), "password"))).andExpect(status().isOk()).andReturn(); - List<User> users = jsonMapper.readValue(result.getResponse().getContentAsString(), new TypeReference<>() { - }); + MvcResult result = mockMvc + .perform(get("/api/notifications").param("uid", String.valueOf(ugnich.getUid())) + .with(httpBasic(serviceUser.getName(), "password"))) + .andExpect(status().isOk()).andReturn(); + List<User> users = jsonMapper.readValue(result.getResponse().getContentAsString(), + new TypeReference<>() { + }); assertThat(users.size(), is(1)); assertThat(users.get(0).getTokens().size(), is(1)); assertThat(users.get(0).getTokens().get(0).getToken(), equalTo(token)); @@ -754,7 +786,8 @@ public class ServerTests { @Test public void tg2juickLinks() { - UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://juick.com/m/123456#23").build(); + UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://juick.com/m/123456#23") + .build(); assertThat(uriComponents.getPath().substring(3), is("123456")); assertThat(uriComponents.getFragment(), is("23")); } @@ -778,7 +811,8 @@ public class ServerTests { .param("mid", stringValueOfMid).param("uid", String.valueOf(ugnich.getUid()))) .andExpect(status().isOk()); mockMvc.perform( - get("/api/notifications").param("mid", stringValueOfMid).param("uid", String.valueOf(ugnich.getUid()))) + get("/api/notifications").param("mid", stringValueOfMid).param("uid", + String.valueOf(ugnich.getUid()))) .andExpect(status().isUnauthorized()); } @@ -846,10 +880,12 @@ public class ServerTests { @Test public void botCommandsTests() throws Exception { - assertThat(commandsManager.processCommand(AnonymousUser.INSTANCE, "PING", emptyUri).getText(), is("PONG")); + assertThat(commandsManager.processCommand(AnonymousUser.INSTANCE, "PING", emptyUri).getText(), + is("PONG")); // subscription commands have two lines, others have 1 assertThat( - commandsManager.processCommand(AnonymousUser.INSTANCE, "help", emptyUri).getText().split("\n").length, + commandsManager.processCommand(AnonymousUser.INSTANCE, "help", emptyUri).getText() + .split("\n").length, is(32)); } @@ -859,12 +895,14 @@ public class ServerTests { User user = userService.createUser("me", "secret").orElseThrow(IllegalStateException::new); Tag yo = tagService.getTag("yo", true); Message msg = commandsManager - .processCommand(user, "*yo yoyo", URI.create("https://static.juick.com/settings/facebook.png")) + .processCommand(user, "*yo yoyo", + URI.create("https://static.juick.com/settings/facebook.png")) .getNewMessage().get(); assertThat(msg.getAttachmentType(), is("png")); Message msgreply = commandsManager .processCommand(user, "#" + msg.getMid() + " yyy", HttpUtils - .downloadImage(URI.create("https://static.juick.com/settings/xmpp.png").toURL(), tmpDir)) + .downloadImage(URI.create("https://static.juick.com/settings/xmpp.png") + .toURL(), tmpDir)) .getNewMessage().get(); assertThat(msgreply.getAttachmentType(), equalTo("png")); assertEquals("yoyo", messagesService.getMessage(msg.getMid()).get().getText()); @@ -875,28 +913,35 @@ public class ServerTests { assertThat(yoyoMsg.getNewMessage().get().getTags().stream().findFirst().get(), is(yo)); Message msg2 = yoyoMsg.getNewMessage().get(); int mid = msg2.getMid(); - Timestamp last = jdbcTemplate.queryForObject("SELECT lastmessage FROM users WHERE id=?", Timestamp.class, + Timestamp last = jdbcTemplate.queryForObject("SELECT lastmessage FROM users WHERE id=?", + Timestamp.class, user.getUid()); assertThat(last.toInstant(), equalTo(yoyoMsg.getNewMessage().get().getCreated())); assertEquals(true, - commandsManager.processCommand(user, String.format("#%d", mid), emptyUri).getText().startsWith("@me")); - User readerUser = userService.createUser("dummyReader", "dummySecret").orElseThrow(IllegalStateException::new); + commandsManager.processCommand(user, String.format("#%d", mid), emptyUri).getText() + .startsWith("@me")); + User readerUser = userService.createUser("dummyReader", "dummySecret") + .orElseThrow(IllegalStateException::new); assertThat( - commandsManager.processCommand(readerUser, "s", emptyUri).getText().startsWith("You are subscribed to"), + commandsManager.processCommand(readerUser, "s", emptyUri).getText() + .startsWith("You are subscribed to"), is(true)); assertThat( - commandsManager.processCommand(readerUser, "S", emptyUri).getText().startsWith("You are subscribed to"), + commandsManager.processCommand(readerUser, "S", emptyUri).getText() + .startsWith("You are subscribed to"), is(true)); assertEquals("Subscribed", commandsManager.processCommand(readerUser, "S #" + mid, emptyUri).getText()); assertEquals("Message is added to your recommendations", commandsManager.processCommand(readerUser, "! #" + mid, emptyUri).getText()); int rid = messagesService.createReply(mid, 0, user, "comment", null); assertEquals(1, subscriptionService - .getUsersSubscribedToComments(messagesService.getMessage(mid).get(), messagesService.getReply(mid, rid)) + .getUsersSubscribedToComments(messagesService.getMessage(mid).get(), + messagesService.getReply(mid, rid)) .size()); privacyQueriesService.blacklistUser(user, readerUser); assertEquals(0, subscriptionService - .getUsersSubscribedToComments(messagesService.getMessage(mid).get(), messagesService.getReply(mid, rid)) + .getUsersSubscribedToComments(messagesService.getMessage(mid).get(), + messagesService.getReply(mid, rid)) .size()); assertEquals(1, subscriptionService.getUsersSubscribedToComments(messagesService.getMessage(mid).get(), messagesService.getReply(mid, rid), true).size()); @@ -908,13 +953,15 @@ public class ServerTests { String expectedSecondReply = "Reply posted.\n#" + mid + "/2 " + "https://juick.com/m/" + mid + "#2"; String expectedThirdReply = "Reply posted.\n#" + mid + "/3 " + "https://juick.com/m/" + mid + "#3"; assertEquals(expectedSecondReply, commandsManager - .processCommand(user, "#" + mid + " yoyo", URI.create("https://static.juick.com/settings/facebook.png")) + .processCommand(user, "#" + mid + " yoyo", + URI.create("https://static.juick.com/settings/facebook.png")) .getText()); assertEquals(expectedThirdReply, commandsManager.processCommand(user, " \t\n #" + mid + "/2 ", URI.create("https://static.juick.com/settings/facebook.png")).getText()); Message reply = messagesService.getReplies(user, mid).stream().filter(m -> m.getRid() == 3).findFirst() .orElse(new Message()); - Timestamp lastreply = jdbcTemplate.queryForObject("SELECT lastmessage FROM users WHERE id=?", Timestamp.class, + Timestamp lastreply = jdbcTemplate.queryForObject("SELECT lastmessage FROM users WHERE id=?", + Timestamp.class, user.getUid()); assertThat(lastreply.toInstant(), equalTo(reply.getCreated())); assertEquals(2, reply.getReplyto()); @@ -930,10 +977,12 @@ public class ServerTests { assertEquals("Tags are updated", commandsManager.processCommand(user, "#" + mid + " *there", emptyUri).getText()); assertEquals(1, tagService.getMessageTags(mid).size()); - User taggerUser = userService.createUser("dummyTagger", "dummySecret").orElseThrow(IllegalStateException::new); + User taggerUser = userService.createUser("dummyTagger", "dummySecret") + .orElseThrow(IllegalStateException::new); assertEquals("Subscribed", commandsManager.processCommand(taggerUser, "S *yo", emptyUri).getText()); assertEquals(2, subscriptionService.getSubscribedUsers(user.getUid(), msg2).size()); - assertEquals("Unsubscribed from yo", commandsManager.processCommand(taggerUser, "U *yo", emptyUri).getText()); + assertEquals("Unsubscribed from yo", + commandsManager.processCommand(taggerUser, "U *yo", emptyUri).getText()); assertEquals(1, subscriptionService.getSubscribedUsers(user.getUid(), msg2).size()); assertEquals(1, userService.getUserReaders(user.getUid()).size()); String readerFeed = commandsManager.processCommand(readerUser, "#", emptyUri).getText(); @@ -945,7 +994,8 @@ public class ServerTests { assertEquals("Unsubscribed from #" + mid, commandsManager.processCommand(readerUser, "u #" + mid, emptyUri).getText()); assertEquals(0, subscriptionService - .getUsersSubscribedToComments(messagesService.getMessage(mid).get(), messagesService.getReply(mid, rid)) + .getUsersSubscribedToComments(messagesService.getMessage(mid).get(), + messagesService.getReply(mid, rid)) .size()); assertNotEquals("should NOT be deleted", String.format("Message %s deleted", mid), commandsManager.processCommand(readerUser, "D #" + mid, emptyUri).getText()); @@ -954,12 +1004,14 @@ public class ServerTests { String expectedCodeMessage = "some smelly code goes here\n" + "> void main(void** args) {\n" + "> }"; String codeAndTags = "*code\n" + expectedCodeMessage; - Message codeAndTagsMessage = commandsManager.processCommand(user, codeAndTags, emptyUri).getNewMessage().get(); + Message codeAndTagsMessage = commandsManager.processCommand(user, codeAndTags, emptyUri).getNewMessage() + .get(); Set<Tag> codeAndTagsTags = codeAndTagsMessage.getTags(); assertEquals(1, codeAndTagsTags.size()); assertEquals("code", codeAndTagsTags.stream().findFirst().get().getName()); assertEquals(expectedCodeMessage, codeAndTagsMessage.getText()); - CommandResult result = commandsManager.processCommand(user, "*one *two *three *four *five *six test", emptyUri); + CommandResult result = commandsManager.processCommand(user, "*one *two *three *four *five *six test", + emptyUri); assertThat(result.getNewMessage(), is(Optional.empty())); assertThat(result.getText(), is("Sorry, 5 tags maximum.")); result = commandsManager.processCommand(user, @@ -967,12 +1019,14 @@ public class ServerTests { assertThat(result.getNewMessage(), is(Optional.empty())); assertThat(result.getText(), is("Tags are NOT updated (5 tags maximum?)")); result = commandsManager.processCommand(user, - "I'm very smart to post my login url there" + "<https://juick.com/settings?hash=VTYZkKV8FWkmu6g1>", + "I'm very smart to post my login url there" + + "<https://juick.com/settings?hash=VTYZkKV8FWkmu6g1>", emptyUri); assertThat(result.getNewMessage().isPresent(), is(true)); assertFalse(result.getNewMessage().get().getText().contains("VTYZkKV8FWkmu6g1")); result = commandsManager.processCommand(user, - "*корм *juick_ppl *рационализм *? *мюсли а сколько микроморт в дневной порции сверхмюслей?", emptyUri); + "*корм *juick_ppl *рационализм *? *мюсли а сколько микроморт в дневной порции сверхмюслей?", + emptyUri); assertThat(result.getNewMessage().isPresent(), is(true)); String tags = "*Juick *Google *Google Play"; String data = "Вчера отправлял *NSFW постинг в топ :)"; @@ -983,7 +1037,8 @@ public class ServerTests { data = "* \u043c\u0443\u0441\u043e\u0440\\n\u0423 \u043c\u0435\u043d\u044f \u043a\u0430\u0436\u0434\u0443\u044e \u043d\u0435\u0434\u0435\u043b\u044e \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044f \u043f\u043e 4-5 \u0431\u0443\u0442\u044b\u043b\u043e\u043a 1,5\u043b \u041f\u0415\u0422. \u041c\u043d\u0435 \u0433\u0435\u043c\u043e\u0440\u043d\u043e \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c \u043f\u043e \u043a\u0438\u043b\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u0438\u043b\u0438 \u043f\u043e 5\u043a\u0433 \u044d\u0442\u043e\u0433\u043e \u043c\u0443\u0441\u043e\u0440\u0430, \u0447\u0442\u043e\u0431\u044b \u0432\u0435\u0437\u0442\u0438 \u0435\u0433\u043e \u0435\u0449\u0435 \u043a\u0443\u0434\u0430-\u0442\u043e.\\n\u041d\u0435, \u043d\u0443 \u0435\u0441\u0442\u044c \u043b\u044e\u0434\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u043e\u0431\u0438\u0440\u0430\u044e\u0442 \u0432\u0442\u043e\u0440\u0441\u044b\u0440\u044c\u0435 \u043f\u043e \u043c\u0443\u0441\u043e\u0440\u043a\u0430\u043c, \u0441\u0432\u0430\u043b\u043a\u0430\u043c, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0434\u0435\u043d\u044c\u0433\u0438 \u043d\u0443\u0436\u043d\u044b. \u0418 \u0431\u044b\u0432\u0430\u044e\u0442 \u0441\u0442\u043e\u044f\u0442 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b-\u043a\u043b\u0435\u0442\u043a\u0438 \u0434\u043b\u044f \u043f\u043b\u0430\u0441\u0442\u0438\u043a\u0430, \u043d\u043e \u0442\u0430\u043a \u043a\u0430\u043a \u043c\u0443\u0441\u043e\u0440 \u0432\u044b\u0432\u043e\u0437\u044f\u0442 \u043d\u0435 \u0447\u0430\u0441\u0442\u043e \u0438\u043b\u0438 \u043b\u044e\u0434\u0438 \u0432\u043d\u0435\u0437\u0430\u043f\u043d\u043e \u0432\u044b\u043a\u0438\u0434\u044b\u0432\u0430\u044e\u0442 \u043c\u043d\u043e\u0433\u043e \u043c\u0443\u0441\u043e\u0440\u0430, \u0442\u043e \u0432 \u0442\u043e\u0439 \u043a\u043b\u0435\u0442\u043a\u0435 \u0442\u043e\u0442 \u043c\u0443\u0441\u043e\u0440, \u0447\u0442\u043e \u043d\u0435 \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u043b\u0441\u044f \u0432 \u043e\u0431\u044b\u0447\u043d\u044b\u0445 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430\u0445."; result = commandsManager.processCommand(user, String.format("%s %s", tags, data), emptyUri); assertThat(result.getNewMessage().get().getTags().size(), equalTo(2)); - assertThat(result.getNewMessage().get().getTags().stream().findFirst().get().getName(), equalTo("Киев")); + assertThat(result.getNewMessage().get().getTags().stream().findFirst().get().getName(), + equalTo("Киев")); assertThat(result.getNewMessage().get().getText(), equalTo(data)); result = commandsManager.processCommand(user, "S @unknown-user", emptyUri); assertThat(result.getNewMessage(), is(Optional.empty())); @@ -995,28 +1050,37 @@ public class ServerTests { emailService.addEmail(ugnich.getUid(), "ugnich@example.com"); int mid = messagesService.createMessage(ugnich.getUid(), "text", StringUtils.EMPTY, Set.of()); String mail = String.format( - "MIME-Version: 1.0\n" + "Received: by 10.176.0.242 with HTTP; Fri, 16 Mar 2018 05:31:50 -0700 (PDT)\n" + "MIME-Version: 1.0\n" + + "Received: by 10.176.0.242 with HTTP; Fri, 16 Mar 2018 05:31:50 -0700 (PDT)\n" + "In-Reply-To: <%d.0@juick.com>\n" + "References: <%d.0@juick.com>\n" - + "Date: Fri, 16 Mar 2018 15:31:50 +0300\n" + "Delivered-To: ugnich@example.com\n" + + "Date: Fri, 16 Mar 2018 15:31:50 +0300\n" + + "Delivered-To: ugnich@example.com\n" + "Message-ID: <CAF+0zPD_YLVgYovajLqUFwkRAgJT+FzyQ2EzikQsPKsrnfKv-Q@mail.gmail.com>\n" - + "Subject: Re: New reply to TJ\n" + "From: Ugnich <ugnich@example.com>\n" + + "Subject: Re: New reply to TJ\n" + + "From: Ugnich <ugnich@example.com>\n" + "To: Juick <juick@juick.com>\n" - + "Content-Type: multipart/alternative; boundary=\"001a11454886e42be5056786ca70\"\n" + "\n" - + "--001a11454886e42be5056786ca70\n" + "Content-Type: text/plain; charset=\"UTF-8\"\n" + "\n" + + "Content-Type: multipart/alternative; boundary=\"001a11454886e42be5056786ca70\"\n" + + "\n" + + "--001a11454886e42be5056786ca70\n" + + "Content-Type: text/plain; charset=\"UTF-8\"\n" + "\n" + "s2313334\n" + "\n" + "--001a11454886e42be5056786ca70\n" - + "Content-Type: text/html; charset=\"UTF-8\"\n" + "\n" + "<div dir=\"ltr\">s2313334</div>\n" + + "Content-Type: text/html; charset=\"UTF-8\"\n" + "\n" + + "<div dir=\"ltr\">s2313334</div>\n" + "\n" + "--001a11454886e42be5056786ca70--", mid, mid); mockMvc.perform(post("/api/mail").with(httpBasic(serviceUser.getName(), "password")).content(mail)) .andExpect(status().isOk()); - String reply = "Return-Path: <ugnich@example.com>\n" + "Received: from [192.168.88.140] ([91.244.168.38])\n" + String reply = "Return-Path: <ugnich@example.com>\n" + + "Received: from [192.168.88.140] ([91.244.168.38])\n" + " by smtp.gmail.com with ESMTPSA id r84sm3970197lja.54.2019.06.20.08.39.54\n" + " for <juick@juick.com>\n" + " (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);\n" - + " Thu, 20 Jun 2019 08:39:54 -0700 (PDT)\n" + "From: Ugnich <ugnich@example.com>\n" + + " Thu, 20 Jun 2019 08:39:54 -0700 (PDT)\n" + + "From: Ugnich <ugnich@example.com>\n" + "Content-Type: text/plain; charset=utf-8\n" + "Content-Transfer-Encoding: base64\n" + "Mime-Version: 1.0 (1.0)\n" + "Date: Thu, 20 Jun 2019 18:39:54 +0300\n" - + "Subject: Re: New reply to vt\n" + "Message-Id: <40BC3538-0A0C-4BD0-8F11-5408A85CC6EF@gmail.com>\n" + + "Subject: Re: New reply to vt\n" + + "Message-Id: <40BC3538-0A0C-4BD0-8F11-5408A85CC6EF@gmail.com>\n" + "References: <2945559.7@juick.com>\n" + "In-Reply-To: <CAF+0zPD_YLVgYovajLqUFwkRAgJT+FzyQ2EzikQsPKsrnfKv-Q@mail.gmail.com>\n" + "To: juick@juick.com\n" + "X-Mailer: iPhone Mail (16F203)\n" + "\n" @@ -1038,12 +1102,14 @@ public class ServerTests { mockMvc.perform(get("/api/thread?mid=" + mid + "&hash=" + freefdHash)).andExpect(status().isOk()) .andExpect(jsonPath("$[0].recommendations.length()", is(1))) .andExpect(jsonPath("$[0].recommendations[0].uname", is(freefdName))); - mockMvc.perform(post("/api/like?mid=" + freefdMid + "&hash=" + freefdHash)).andExpect(status().isForbidden()); + mockMvc.perform(post("/api/like?mid=" + freefdMid + "&hash=" + freefdHash)) + .andExpect(status().isForbidden()); messagesService.createReply(mid, 0, freefd, "reply", null); mockMvc.perform(get("/api/thread?mid=" + mid + "&hash=" + freefdHash)).andExpect(status().isOk()) .andExpect(jsonPath("$.length()", is(2))) .andExpect(jsonPath("$[0].replies", is(1))); - mockMvc.perform(get("/api/thread?mid=" + mid + "&hash=" + freefdHash + "&showReplies=false")).andExpect(status().isOk()) + mockMvc.perform(get("/api/thread?mid=" + mid + "&hash=" + freefdHash + "&showReplies=false")) + .andExpect(status().isOk()) .andExpect(jsonPath("$.length()", is(1))) .andExpect(jsonPath("$[0].replies", is(1))); } @@ -1060,7 +1126,8 @@ public class ServerTests { Message msg4 = messagesService.getMessage(mid1).get(); assertThat(msg4.getRecommendations().size(), is(0)); assertThat( - messagesService.getMessages(AnonymousUser.INSTANCE, Collections.singletonList(mid1)).get(0).getRecommendations().size(), + messagesService.getMessages(AnonymousUser.INSTANCE, Collections.singletonList(mid1)) + .get(0).getRecommendations().size(), is(0)); assertEquals(1, msg4.getReactions().stream().filter(r -> r.getId() == 2).findFirst() .orElseThrow(IllegalStateException::new).getCount()); @@ -1081,12 +1148,14 @@ public class ServerTests { subscriptionService.subscribeMessage(messagesService.getMessage(mid).get(), ugnich); messagesService.createReply(mid, 0, freefd, "new reply", null); BiFunction<User, Integer, Integer> lastRead = (user, m) -> jdbcTemplate.queryForObject( - "SELECT last_read_rid FROM subscr_messages WHERE suser_id=? AND message_id=?", Integer.class, + "SELECT last_read_rid FROM subscr_messages WHERE suser_id=? AND message_id=?", + Integer.class, user.getUid(), m); assertThat(lastRead.apply(ugnich, mid), is(0)); assertThat(messagesService.getUnread(ugnich).size(), is(1)); assertThat(messagesService.getUnread(ugnich).get(0), is(mid)); - assertThat(messagesService.getMessages(ugnich, Collections.singletonList(mid)).get(0).isUnread(), is(true)); + assertThat(messagesService.getMessages(ugnich, Collections.singletonList(mid)).get(0).isUnread(), + is(true)); messagesService.getReplies(ugnich, mid); assertThat(lastRead.apply(ugnich, mid), is(1)); assertThat(messagesService.getUnread(ugnich).size(), is(0)); @@ -1094,7 +1163,8 @@ public class ServerTests { assertThat(lastRead.apply(ugnich, mid), is(1)); String ugnichHash = userService.getHashByUID(ugnich.getUid()); int freefdrid = messagesService.createReply(mid, 0, freefd, "again", null); - mockMvc.perform(get(String.format("/api/thread/mark_read/%d-%d.gif?hash=%s", mid, freefdrid, ugnichHash))) + mockMvc.perform(get( + String.format("/api/thread/mark_read/%d-%d.gif?hash=%s", mid, freefdrid, ugnichHash))) .andExpect(status().isOk()) .andExpect(content().bytes(IOUtils.toByteArray(invisiblePixel.getInputStream()))); assertThat(lastRead.apply(ugnich, mid), is(freefdrid)); @@ -1109,7 +1179,8 @@ public class ServerTests { privacyQueriesService.blacklistUser(ugnich, freefd); newfreefdrid = messagesService.createReply(mid, 0, freefd, "after ban", null); assertThat(lastRead.apply(ugnich, mid), lessThan(newfreefdrid)); - mockMvc.perform(get(String.format("/api/thread?mid=%d&hash=%s", mid, ugnichHash))).andExpect(status().isOk()); + mockMvc.perform(get(String.format("/api/thread?mid=%d&hash=%s", mid, ugnichHash))) + .andExpect(status().isOk()); assertThat(lastRead.apply(ugnich, mid), is(newfreefdrid)); } @@ -1118,72 +1189,87 @@ public class ServerTests { Tag banned = tagService.getTag("banned", true); int mid = messagesService.createMessage(ugnich.getUid(), "yo", "jpg", Set.of(banned)); privacyQueriesService.blacklistTag(freefd, banned); - assertThat(messagesService.getMessages(AnonymousUser.INSTANCE, messagesService.getAll(freefd.getUid(), 0)) + assertThat(messagesService + .getMessages(AnonymousUser.INSTANCE, messagesService.getAll(freefd.getUid(), 0)) .stream().noneMatch(m -> m.getTags().contains(banned)), is(true)); - assertFalse(messagesService.getMessages(AnonymousUser.INSTANCE, messagesService.getAll(ugnich.getUid(), 0)) + assertFalse(messagesService + .getMessages(AnonymousUser.INSTANCE, messagesService.getAll(ugnich.getUid(), 0)) .stream().noneMatch(m -> m.getTags().contains(banned))); - assertThat(messagesService.getMessages(AnonymousUser.INSTANCE, messagesService.getPhotos(freefd.getUid(), 0)) + assertThat(messagesService + .getMessages(AnonymousUser.INSTANCE, messagesService.getPhotos(freefd.getUid(), 0)) .stream().noneMatch(m -> m.getTags().contains(banned)), is(true)); - assertFalse(messagesService.getMessages(AnonymousUser.INSTANCE, messagesService.getPhotos(ugnich.getUid(), 0)) + assertFalse(messagesService + .getMessages(AnonymousUser.INSTANCE, messagesService.getPhotos(ugnich.getUid(), 0)) .stream().noneMatch(m -> m.getTags().contains(banned))); messagesService.recommendMessage(mid, serviceUser.getUid()); assertThat(messagesService .getMessages(AnonymousUser.INSTANCE, - messagesService.getUserBlogWithRecommendations(serviceUser, freefd, 0, 0)) + messagesService.getUserBlogWithRecommendations(serviceUser, freefd, 0, + 0)) .stream().noneMatch(m -> m.getTags().contains(banned)), is(true)); assertFalse(messagesService .getMessages(AnonymousUser.INSTANCE, - messagesService.getUserBlogWithRecommendations(serviceUser, ugnich, 0, 0)) + messagesService.getUserBlogWithRecommendations(serviceUser, ugnich, 0, + 0)) .stream().noneMatch(m -> m.getTags().contains(banned))); assertThat( - messagesService.getMessages(AnonymousUser.INSTANCE, messagesService.getMyFeed(freefd.getUid(), 0, true)) + messagesService.getMessages(AnonymousUser.INSTANCE, + messagesService.getMyFeed(freefd.getUid(), 0, true)) .stream().noneMatch(m -> m.getTags().contains(banned)), is(true)); User newUser1 = userService.createUser("newUser1", "12345").orElseThrow(IllegalStateException::new); int newMid = messagesService.createMessage(newUser1.getUid(), "people", null, Set.of(banned)); messagesService.recommendMessage(newMid, ugnich.getUid()); assertThat( - messagesService.getMessages(AnonymousUser.INSTANCE, messagesService.getMyFeed(freefd.getUid(), 0, true)) + messagesService.getMessages(AnonymousUser.INSTANCE, + messagesService.getMyFeed(freefd.getUid(), 0, true)) .stream().noneMatch(m -> m.getTags().contains(banned)), is(true)); tagService.updateTags(newMid, Collections.singletonList(banned)); assertThat(messagesService.getMessage(newMid).get().getTags().size(), is(0)); privacyQueriesService.blacklistUser(freefd, newUser1); - assertThat(messagesService.getMyFeed(freefd.getUid(), 0, true).stream().noneMatch(m -> m == newMid), is(true)); - } - - @Test - public void tagsShouldBeDeserializedFromXml() throws JAXBException { - XmppSessionConfiguration configuration = XmppSessionConfiguration.builder() - .extensions(Extension.of(Message.class)).build(); - XmppSession xmpp = new XmppSession("juick.com", configuration) { - @Override - public void connect(Jid from) { - - } - - @Override - public Jid getConnectedResource() { - return null; - } - }; - String tag = "<tag xmlns='http://juick.com/message'>yo</tag>"; - String xml = "<message xmlns='jabber:client' from='juick@juick.com' type='chat'><body>yo</body><juick mid='1' ts='2017-09-14' uid='1' uname='ugnich' xmlns='http://juick.com/message'><body>yo</body><user uid='1' uname='ugnich' xmlns='http://juick.com/user'/><tag>yo</tag><tag>people</tag></juick></message>"; - Unmarshaller unmarshaller = xmpp.createUnmarshaller(); - rocks.xmpp.core.stanza.model.Message xmppMessage = (rocks.xmpp.core.stanza.model.Message) unmarshaller - .unmarshal(new StringReader(xml)); - Tag xmlTag = (Tag) unmarshaller.unmarshal(new StringReader(tag)); - assertThat(xmlTag.getName(), equalTo("yo")); - Message juickMessage = xmppMessage.getExtension(Message.class); - List<Tag> tags = new ArrayList<>(juickMessage.getTags()); - assertThat(tags.get(0).getName(), equalTo("yo")); + assertThat(messagesService.getMyFeed(freefd.getUid(), 0, true).stream().noneMatch(m -> m == newMid), + is(true)); } + /* + * @Test + * public void tagsShouldBeDeserializedFromXml() throws JAXBException { + * XmppSessionConfiguration configuration = XmppSessionConfiguration.builder() + * .extensions(Extension.of(Message.class)).build(); + * XmppSession xmpp = new XmppSession("juick.com", configuration) { + * + * @Override + * public void connect(Jid from) { + * + * } + * + * @Override + * public Jid getConnectedResource() { + * return null; + * } + * }; + * String tag = "<tag xmlns='http://juick.com/message'>yo</tag>"; + * String xml = + * "<message xmlns='jabber:client' from='juick@juick.com' type='chat'><body>yo</body><juick mid='1' ts='2017-09-14' uid='1' uname='ugnich' xmlns='http://juick.com/message'><body>yo</body><user uid='1' uname='ugnich' xmlns='http://juick.com/user'/><tag>yo</tag><tag>people</tag></juick></message>" + * ; + * Unmarshaller unmarshaller = xmpp.createUnmarshaller(); + * rocks.xmpp.core.stanza.model.Message xmppMessage = + * (rocks.xmpp.core.stanza.model.Message) unmarshaller + * .unmarshal(new StringReader(xml)); + * Tag xmlTag = (Tag) unmarshaller.unmarshal(new StringReader(tag)); + * assertThat(xmlTag.getName(), equalTo("yo")); + * Message juickMessage = xmppMessage.getExtension(Message.class); + * List<Tag> tags = new ArrayList<>(juickMessage.getTags()); + * assertThat(tags.get(0).getName(), equalTo("yo")); + * } + */ @Test public void messageParserSerializer() throws Exception { String tagsString = "test test" + (char) 0xA0 + "2 test 3"; Set<Tag> tags = MessageUtils.parseTags(tagsString); - List<Tag> tagList = tags.stream().map(t -> tagService.getTag(t.getName(), true)).collect(Collectors.toList()); + List<Tag> tagList = tags.stream().map(t -> tagService.getTag(t.getName(), true)) + .collect(Collectors.toList()); assertEquals("test", tagList.get(0).getName()); assertEquals("test 3", tagList.get(2).getName()); assertEquals(3, tagList.size()); @@ -1194,7 +1280,8 @@ public class ServerTests { map.add("body", "*test *test 2 *test 3 YO"); map.add("hash", userService.getHashByUID(ugnich.getUid())); - ResponseEntity<CommandResult> result = restTemplate.postForEntity("/api/post", request, CommandResult.class); + ResponseEntity<CommandResult> result = restTemplate.postForEntity("/api/post", request, + CommandResult.class); assertThat(result.getStatusCode(), is(HttpStatus.OK)); Message msg = result.getBody().getNewMessage().orElseThrow(); Instant currentDate = msg.getCreated(); @@ -1218,7 +1305,8 @@ public class ServerTests { MvcResult apiResult = mockMvc.perform(get("/api/thread?mid=" + msg.getMid())).andExpect(status().isOk()) .andReturn(); - List<Message> fromApi = jsonMapper.readValue(apiResult.getResponse().getContentAsString(StandardCharsets.UTF_8), + List<Message> fromApi = jsonMapper.readValue( + apiResult.getResponse().getContentAsString(StandardCharsets.UTF_8), new TypeReference<>() { }); assertThat(fromApi.get(0).getTags(), is(tags)); @@ -1251,7 +1339,8 @@ public class ServerTests { assertThat(userService.getUserReaders(ugnich.getUid()).size(), is(1)); String hash = userService.getHashByUID(ugnich.getUid()); mockMvc.perform(get("/api/me").with(httpBasic(ugnichName, ugnichPassword))) - .andExpect(jsonPath("$.hash", is(hash))).andExpect(jsonPath("$.readers.length()", is(1))) + .andExpect(jsonPath("$.hash", is(hash))) + .andExpect(jsonPath("$.readers.length()", is(1))) .andExpect(jsonPath("$.read.length()", is(1))); } @@ -1262,7 +1351,8 @@ public class ServerTests { int mid = result.getNewMessage().get().getMid(); commandsManager.processCommand(freefd, String.format("#%d ugnich - dick too", mid), emptyUri); commandsManager.processCommand(serviceUser, String.format("#%d/1 ban for a hour!", mid), emptyUri); - commandsManager.processCommand(serviceUser, String.format("#%d freefd is here but it is hidden from you", mid), + commandsManager.processCommand(serviceUser, + String.format("#%d freefd is here but it is hidden from you", mid), emptyUri); assertThat(messagesService.getMessage(mid).get().getReplies(), is(3)); Message reply = messagesService.getReply(mid, 3); @@ -1274,8 +1364,8 @@ public class ServerTests { replies = messagesService.getReplies(ugnich, mid); assertThat(replies.size(), is(1)); mockMvc.perform(get("/api/thread").with(httpBasic(ugnichName, ugnichPassword)).param("mid", - String.valueOf(mid))) - .andExpect(jsonPath("$[0].replies", is(1))); + String.valueOf(mid))) + .andExpect(jsonPath("$[0].replies", is(1))); commandsManager.processCommand(serviceUser, String.format("#%d/4 mmm?!", mid), emptyUri); assertThat(messagesService.getMessage(mid).get().getReplies(), is(5)); replies = messagesService.getReplies(ugnich, mid); @@ -1295,7 +1385,8 @@ public class ServerTests { assertThat(messagesService.getTag(tag.TID, freefd.getUid(), 0, 10).size(), is(1)); assertThat(messagesService.getTag(tag.TID, ugnich.getUid(), 0, 10).size(), is(0)); messagesService.recommendMessage(freefdMsg, serviceUser.getUid()); - assertThat(messagesService.getUserBlogWithRecommendations(serviceUser, ugnich, 0, 0).contains(freefdMsg), + assertThat(messagesService.getUserBlogWithRecommendations(serviceUser, ugnich, 0, 0) + .contains(freefdMsg), is(false)); commandsManager.processCommand(ugnich, "BL @freefd", emptyUri); } @@ -1340,7 +1431,8 @@ public class ServerTests { Files.copy(Paths.get(new ClassPathResource("2915104.jpg").getURI()), tmpFile, StandardCopyOption.REPLACE_EXISTING); assertThat(tmpFile.toFile().exists(), is(true)); - CommandResult postJpgiPhone = commandsManager.processCommand(ugnich, "YO", URI.create("juick://2915104.jpg")); + CommandResult postJpgiPhone = commandsManager.processCommand(ugnich, "YO", + URI.create("juick://2915104.jpg")); assertThat(postJpgiPhone.getNewMessage().isPresent(), is(true)); int mid = postJpgiPhone.getNewMessage().get().getMid(); File originalFile = Paths.get(imgDir, "p", String.format("%d.jpg", mid)).toFile(); @@ -1384,7 +1476,8 @@ public class ServerTests { @Test public void messageEditingSpec() throws Exception { MvcResult result = mockMvc - .perform(post("/api/post").with(httpBasic(ugnichName, ugnichPassword)).param("body", "YO")) + .perform(post("/api/post").with(httpBasic(ugnichName, ugnichPassword)).param("body", + "YO")) .andExpect(status().is2xxSuccessful()).andReturn(); Message original = jsonMapper.readValue(result.getResponse().getContentAsString(), CommandResult.class) .getNewMessage().get(); @@ -1394,7 +1487,8 @@ public class ServerTests { Thread.sleep(1000); result = mockMvc .perform(post("/api/update").with(httpBasic(ugnichName, ugnichPassword)) - .param("mid", String.valueOf(original.getMid())).param("body", "PEOPLE")) + .param("mid", String.valueOf(original.getMid())) + .param("body", "PEOPLE")) .andExpect(status().is2xxSuccessful()).andReturn(); Message edited = jsonMapper.readValue(result.getResponse().getContentAsString(), CommandResult.class) .getNewMessage().get(); @@ -1405,36 +1499,45 @@ public class ServerTests { assertThat(updateEvent.getUser(), is(ugnich)); assertThat(activityPubManager.messageUri(original.getMid(), 0), is(updateEvent.getMessageUri())); mockMvc.perform(post("/api/update").with(httpBasic(freefdName, freefdPassword)) - .param("mid", String.valueOf(original.getMid())).param("body", "PEOPLE")).andExpect(status().is(403)); + .param("mid", String.valueOf(original.getMid())).param("body", "PEOPLE")) + .andExpect(status().is(403)); result = mockMvc .perform(post("/api/comment").with(httpBasic(freefdName, freefdPassword)) .param("mid", String.valueOf(original.getMid())).param("body", "HEY")) .andExpect(status().is2xxSuccessful()).andReturn(); - CommandResult comment = jsonMapper.readValue(result.getResponse().getContentAsString(), CommandResult.class); + CommandResult comment = jsonMapper.readValue(result.getResponse().getContentAsString(), + CommandResult.class); assertThat(comment.getNewMessage().get().getText(), is("HEY")); - assertThat(comment.getNewMessage().get().getUpdatedAt(), is(comment.getNewMessage().get().getCreated())); + assertThat(comment.getNewMessage().get().getUpdatedAt(), + is(comment.getNewMessage().get().getCreated())); // to have updated_at greater than ts Thread.sleep(1000); result = mockMvc .perform(post("/api/update").with(httpBasic(freefdName, freefdPassword)) .param("mid", String.valueOf(comment.getNewMessage().get().getMid())) - .param("rid", String.valueOf(comment.getNewMessage().get().getRid())).param("body", "HEY, JOE")) + .param("rid", String.valueOf(comment.getNewMessage().get().getRid())) + .param("body", "HEY, JOE")) .andExpect(status().is2xxSuccessful()).andReturn(); - Message editedComment = jsonMapper.readValue(result.getResponse().getContentAsString(), CommandResult.class) + Message editedComment = jsonMapper + .readValue(result.getResponse().getContentAsString(), CommandResult.class) .getNewMessage().get(); assertThat(editedComment.getText(), is("HEY, JOE")); assertThat(editedComment.getUpdatedAt(), greaterThan(editedComment.getCreated())); - jdbcTemplate.update("UPDATE replies SET updated_at='1990-05-05 00:00:00' WHERE message_id=? AND reply_id=?", + jdbcTemplate.update( + "UPDATE replies SET updated_at='1990-05-05 00:00:00' WHERE message_id=? AND reply_id=?", editedComment.getMid(), editedComment.getRid()); Message updatedComment = comment.getNewMessage().get(); result = mockMvc .perform(post("/api/update").with(httpBasic(freefdName, freefdPassword)) .param("mid", String.valueOf(updatedComment.getMid())) - .param("rid", String.valueOf(updatedComment.getRid())).param("body", "HEY, JOE AGAIN")) + .param("rid", String.valueOf(updatedComment.getRid())) + .param("body", "HEY, JOE AGAIN")) .andExpect(status().isBadRequest()).andReturn(); - assertThat(messagesService.deleteReply(ugnich.getUid(), updatedComment.getMid(), updatedComment.getRid()), + assertThat(messagesService.deleteReply(ugnich.getUid(), updatedComment.getMid(), + updatedComment.getRid()), is(false)); - assertThat(messagesService.deleteReply(freefd.getUid(), updatedComment.getMid(), updatedComment.getRid()), + assertThat(messagesService.deleteReply(freefd.getUid(), updatedComment.getMid(), + updatedComment.getRid()), is(true)); assertThat(messagesService.getReply(updatedComment.getMid(), updatedComment.getRid()).getUser(), is(archiveUser)); @@ -1451,7 +1554,8 @@ public class ServerTests { @Test public void subscribersToRecommendations() { User reader = userService.createUser("reader", "123456").orElseThrow(IllegalStateException::new); - User recommender = userService.createUser("recommender", "123456").orElseThrow(IllegalStateException::new); + User recommender = userService.createUser("recommender", "123456") + .orElseThrow(IllegalStateException::new); User lateRecommender = userService.createUser("lateRecommender", "123456") .orElseThrow(IllegalStateException::new); User poster = userService.createUser("poster", "123456").orElseThrow(IllegalStateException::new); @@ -1468,20 +1572,25 @@ public class ServerTests { assertThat(recommendSubscribers.size(), is(1)); assertThat(recommendSubscribers.get(0).getUid(), is(reader.getUid())); privacyQueriesService.blacklistUser(reader, poster); - assertThat(subscribers.apply(recommender.getUid(), messagesService.getMessage(posterMid).get()).size(), is(0)); + assertThat(subscribers.apply(recommender.getUid(), messagesService.getMessage(posterMid).get()).size(), + is(0)); privacyQueriesService.blacklistUser(reader, poster); - assertThat(subscribers.apply(recommender.getUid(), messagesService.getMessage(posterMid).get()).size(), is(1)); + assertThat(subscribers.apply(recommender.getUid(), messagesService.getMessage(posterMid).get()).size(), + is(1)); tagService.blacklistTag(reader, sampleTag); - assertThat(subscribers.apply(recommender.getUid(), messagesService.getMessage(posterMid).get()).size(), is(0)); + assertThat(subscribers.apply(recommender.getUid(), messagesService.getMessage(posterMid).get()).size(), + is(0)); tagService.blacklistTag(reader, sampleTag); - assertThat(subscribers.apply(recommender.getUid(), messagesService.getMessage(posterMid).get()).size(), is(1)); + assertThat(subscribers.apply(recommender.getUid(), messagesService.getMessage(posterMid).get()).size(), + is(1)); messagesService.recommendMessage(posterMid, lateRecommender.getUid()); List<User> lateRecommendSubscribers = subscribers.apply(recommender.getUid(), messagesService.getMessage(posterMid).get()); assertThat(lateRecommendSubscribers.size(), is(0)); int readerMid = messagesService.createMessage(reader.getUid(), "PEOPLE", null, Set.of()); messagesService.recommendMessage(readerMid, recommender.getUid()); - assertThat(subscribers.apply(recommender.getUid(), messagesService.getMessage(readerMid).get()).size(), is(0)); + assertThat(subscribers.apply(recommender.getUid(), messagesService.getMessage(readerMid).get()).size(), + is(0)); } @Test @@ -1548,28 +1657,35 @@ public class ServerTests { User isilmine = userService.createUser(userName, userPassword).orElseThrow(IllegalStateException::new); int mid = messagesService.createMessage(isilmine.getUid(), msgText, null, Set.of()); - mockMvc.perform(get(String.format("/api/thread?mid=%d", mid)).with(httpBasic(ugnichName, ugnichPassword))) + mockMvc.perform(get(String.format("/api/thread?mid=%d", mid)) + .with(httpBasic(ugnichName, ugnichPassword))) .andExpect(status().isOk()); jdbcTemplate.update("UPDATE users SET banned=1 WHERE id=?", isilmine.getUid()); - mockMvc.perform(get(String.format("/api/thread?mid=%d", mid)).with(httpBasic(ugnichName, ugnichPassword))) + mockMvc.perform(get(String.format("/api/thread?mid=%d", mid)) + .with(httpBasic(ugnichName, ugnichPassword))) .andExpect(status().isNotFound()); mockMvc.perform(get("/api/messages?uname=isilmine").with(httpBasic(ugnichName, ugnichPassword))) .andExpect(status().isNotFound()); mockMvc.perform(get("/api/info/isilmine").with(httpBasic(ugnichName, ugnichPassword))) .andExpect(status().isNotFound()); - mockMvc.perform(get("/api/info/ugnich").with(httpBasic(ugnichName, ugnichPassword))).andExpect(status().isOk()); + mockMvc.perform(get("/api/info/ugnich").with(httpBasic(ugnichName, ugnichPassword))) + .andExpect(status().isOk()); } @Test public void emptyPasswordMeansUserIsDisabled() throws Exception { String userName = "oldschooluser"; String userPassword = ""; - userService.createUser(userName, userPassword); - - mockMvc.perform(get("/api/auth").with(httpBasic(userName, userPassword))).andExpect(status().isUnauthorized()); - mockMvc.perform(post("/login").param("username", userName).param("password", userPassword)) - .andExpect(status().is3xxRedirection()).andExpect(redirectedUrl("/login?error=1")); + mockMvc.perform(get("/api/auth").with(httpBasic(userName, userPassword))) + .andExpect(status().isUnauthorized()); + mockMvc.perform( + post("/login") + .with(csrf()) + .param("username", userName) + .param("password", userPassword)) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl("/login?error=1")); } @Test @@ -1580,9 +1696,12 @@ public class ServerTests { User pogo = userService.createUser("pogo", "secret").orElseThrow(IllegalStateException::new); User fmap = userService.createUser("fmap", "secret").orElseThrow(IllegalStateException::new); int mid = messagesService.createMessage(monstreek.getUid(), "KURWA", null, Set.of()); - assertThat(messagesService.recommendMessage(mid, ermine.getUid()), is(MessagesService.RecommendStatus.Added)); - assertThat(messagesService.recommendMessage(mid, fmap.getUid()), is(MessagesService.RecommendStatus.Added)); - assertThat(messagesService.recommendMessage(mid, pogo.getUid()), is(MessagesService.RecommendStatus.Added)); + assertThat(messagesService.recommendMessage(mid, ermine.getUid()), + is(MessagesService.RecommendStatus.Added)); + assertThat(messagesService.recommendMessage(mid, fmap.getUid()), + is(MessagesService.RecommendStatus.Added)); + assertThat(messagesService.recommendMessage(mid, pogo.getUid()), + is(MessagesService.RecommendStatus.Added)); jdbcTemplate.update("INSERT INTO favorites(user_id, user_uri, message_id, like_id, ts) " + "values (0, 'http://example.com/u/test', ?, 1, now())", mid); assertThat(messagesService.getMessage(mid).get().getRecommendations().size(), is(4)); @@ -1590,7 +1709,8 @@ public class ServerTests { messagesService.getMessagesRecommendations(Collections.singletonList(mid)).stream() .map(p -> p.getRight()).map(User::getName).collect(Collectors.toList()), Arrays.asList("fmap", "ermine", "pogo", "Anonymous")), is(true)); - privacyQueriesService.blacklistUser(userService.getUserByName("monstreek"), userService.getUserByName("pogo")); + privacyQueriesService.blacklistUser(userService.getUserByName("monstreek"), + userService.getUserByName("pogo")); assertThat(messagesService.getMessage(mid).get().getRecommendations().size(), is(3)); assertThat(CollectionUtils.isEqualCollection( messagesService.getMessagesRecommendations(Collections.singletonList(mid)).stream() @@ -1616,10 +1736,12 @@ public class ServerTests { int casualRid = messagesService.createReply(mid, 0, userService.getUserByName("user"), "DOOR", null); assertThat(messagesService.getReplies(AnonymousUser.INSTANCE, mid).size(), is(1)); assertThat( - messagesService.getMessages(AnonymousUser.INSTANCE, Collections.singletonList(mid)).get(0).getReplies(), + messagesService.getMessages(AnonymousUser.INSTANCE, Collections.singletonList(mid)) + .get(0).getReplies(), is(1)); assertThat(messagesService.getReplies(banned, mid).size(), is(2)); - assertThat(messagesService.getMessages(banned, Collections.singletonList(mid)).get(0).getReplies(), is(2)); + assertThat(messagesService.getMessages(banned, Collections.singletonList(mid)).get(0).getReplies(), + is(2)); } @Test @@ -1628,7 +1750,8 @@ public class ServerTests { .andExpect(jsonPath("$.subject", is("acct:ugnich@localhost"))) .andExpect(jsonPath("$.links", hasSize(1))) .andExpect(jsonPath("$.links[0].href", is("http://localhost:8080/u/ugnich"))); - mockMvc.perform(get("/.well-known/webfinger?resource=acct:durov@localhost")).andExpect(status().isNotFound()); + mockMvc.perform(get("/.well-known/webfinger?resource=acct:durov@localhost")) + .andExpect(status().isNotFound()); } @Test @@ -1636,18 +1759,21 @@ public class ServerTests { ClassPathResource defaultAvatar = new ClassPathResource("static/av-96.png"); String hash = DigestUtils.md5DigestAsHex(IOUtils.toByteArray(defaultAvatar.getInputStream())); mockMvc.perform(get("/u/ugnich").accept(Context.LD_JSON_MEDIA_TYPE)).andExpect(status().isOk()) - .andExpect(jsonPath("$.icon.url", is(String.format("http://localhost:8080/av-96-%s.png", hash)))) + .andExpect(jsonPath("$.icon.url", + is(String.format("http://localhost:8080/av-96-%s.png", hash)))) .andExpect(jsonPath("$.publicKey.publicKeyPem", is(keystoreManager.getPublicKeyPem()))); jdbcTemplate.execute("DELETE FROM messages"); List<Integer> mids = IteratorUtils.toList(IntStream.rangeClosed(1, 30) .mapToObj( - i -> messagesService.createMessage(ugnich.getUid(), String.format("message %d", i), null, Set.of())) + i -> messagesService.createMessage(ugnich.getUid(), + String.format("message %d", i), null, Set.of())) .collect(Collectors.toCollection(ArrayDeque::new)).descendingIterator()); List<Integer> midsPage = mids.stream().limit(20).collect(Collectors.toList()); mockMvc.perform(get("/u/ugnich/blog").accept(Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE)) .andExpect(status().isOk()).andExpect(jsonPath("$.orderedItems", hasSize(20))) .andExpect(jsonPath("$.next", - is("http://localhost:8080/u/ugnich/blog?before=" + midsPage.get(midsPage.size() - 1)))); + is("http://localhost:8080/u/ugnich/blog?before=" + + midsPage.get(midsPage.size() - 1)))); } @Test @@ -1662,12 +1788,15 @@ public class ServerTests { public void repliesList() throws IOException { int mid = messagesService.createMessage(ugnich.getUid(), "hello", null, Set.of()); IntStream.range(1, 15) - .forEach(i -> messagesService.createReply(mid, i - 1, freefd, String.valueOf(i - 1), null)); + .forEach(i -> messagesService.createReply(mid, i - 1, freefd, String.valueOf(i - 1), + null)); HtmlPage threadPage = webClient.getPage(String.format("http://localhost:8080/ugnich/%d", mid)); assertThat(threadPage.getWebResponse().getStatusCode(), equalTo(200)); Long visibleItems = StreamSupport - .stream(threadPage.getHtmlElementById("replies").getChildElements().spliterator(), false).filter(e -> { + .stream(threadPage.getHtmlElementById("replies").getChildElements().spliterator(), + false) + .filter(e -> { StyleElement display = e.getStyleElement("display"); return display == null || !display.getValue().equals("none"); }).count(); @@ -1681,12 +1810,13 @@ public class ServerTests { int mid = messagesService.createMessage(ugnich.getUid(), "freefd bl me", null, Set.of()); messagesService.createReply(mid, 0, ugnich, "yo", null); MvcResult loginResult = mockMvc - .perform(post("/login").param("username", freefdName).param("password", freefdPassword)) + .perform(post("/login").with(csrf()).param("username", freefdName).param("password", freefdPassword)) .andExpect(status().isFound()).andReturn(); Cookie loginCookie = loginResult.getResponse().getCookie("juick-remember-me"); webClient.setCookieManager(new CookieManager()); - webClient.getCookieManager().addCookie(new com.gargoylesoftware.htmlunit.util.Cookie(loginCookie.getDomain(), - loginCookie.getName(), loginCookie.getValue())); + webClient.getCookieManager() + .addCookie(new com.gargoylesoftware.htmlunit.util.Cookie(loginCookie.getDomain(), + loginCookie.getName(), loginCookie.getValue())); HtmlPage threadPage = webClient.getPage(String.format("http://localhost:8080/ugnich/%d", mid)); assertThat(threadPage.getWebResponse().getStatusCode(), equalTo(200)); assertThat(threadPage.querySelectorAll(".msg-comment-target").isEmpty(), equalTo(false)); @@ -1706,7 +1836,8 @@ public class ServerTests { PebbleTemplate template = pebbleEngine.getTemplate("views/test"); Writer writer = new StringWriter(); template.evaluate(writer, - Collections.singletonMap("tagsList", Collections.singletonList(new Tag(">_<").getName()))); + Collections.singletonMap("tagsList", + Collections.singletonList(new Tag(">_<").getName()))); String output = writer.toString().trim(); assertThat(output, equalTo("<a class=\"hashtag\" href=\"/ugnich/?tag=%3E_%3C\">>_<</a>")); } @@ -1714,7 +1845,8 @@ public class ServerTests { public DomElement fetchMeta(String url, String name) throws IOException { HtmlPage page = webClient.getPage(url); DomElement emptyMeta = new DomElement("", "meta", null, null); - return page.getElementsByTagName("meta").stream().filter(t -> t.getAttribute("name").equals(name)).findFirst() + return page.getElementsByTagName("meta").stream().filter(t -> t.getAttribute("name").equals(name)) + .findFirst() .orElse(emptyMeta); } @@ -1743,7 +1875,7 @@ public class ServerTests { .andExpect(content().string(containsString(hash))).andReturn(); Cookie rememberMeFromHash = hashLoginResult.getResponse().getCookie("juick-remember-me"); MvcResult formLoginResult = mockMvc - .perform(post("/login").param("username", ugnichName).param("password", ugnichPassword)) + .perform(post("/login").with(csrf()).param("username", ugnichName).param("password", ugnichPassword)) .andExpect(status().is3xxRedirection()).andReturn(); Cookie rememberMeFromForm = formLoginResult.getResponse().getCookie("juick-remember-me"); mockMvc.perform(get("/?show=my").cookie(rememberMeFromForm)).andExpect(status().isOk()) @@ -1765,12 +1897,13 @@ public class ServerTests { int mid = messagesService.createMessage(ugnich.getUid(), msgText, null, Set.of()); int midNew = messagesService.createMessage(ugnich.getUid(), "Я более новый Угнич", null, Set.of()); MvcResult loginResult = mockMvc - .perform(post("/login").param("username", freefdName).param("password", freefdPassword)) + .perform(post("/login").with(csrf()).param("username", freefdName).param("password", freefdPassword)) .andExpect(status().is3xxRedirection()).andReturn(); Cookie loginCookie = loginResult.getResponse().getCookie("juick-remember-me"); webClient.setCookieManager(new CookieManager()); - webClient.getCookieManager().addCookie(new com.gargoylesoftware.htmlunit.util.Cookie(loginCookie.getDomain(), - loginCookie.getName(), loginCookie.getValue())); + webClient.getCookieManager() + .addCookie(new com.gargoylesoftware.htmlunit.util.Cookie(loginCookie.getDomain(), + loginCookie.getName(), loginCookie.getValue())); String discussionsUrl = "http://localhost:8080/"; HtmlPage discussions = webClient.getPage(discussionsUrl); assertThat(discussions.querySelectorAll("article").size(), is(0)); @@ -1781,13 +1914,15 @@ public class ServerTests { discussions = (HtmlPage) discussions.refresh(); assertThat(discussions.querySelectorAll("article").size(), is(2)); assertThat( - discussions.querySelectorAll("article").get(0).getAttributes().getNamedItem("data-mid").getNodeValue(), + discussions.querySelectorAll("article").get(0).getAttributes().getNamedItem("data-mid") + .getNodeValue(), is(String.valueOf(midNew))); messagesService.createReply(mid, 0, freefd, "I'm replied", null); discussions = (HtmlPage) discussions.refresh(); assertThat(discussions.querySelectorAll("article").size(), is(2)); assertThat( - discussions.querySelectorAll("article").get(0).getAttributes().getNamedItem("data-mid").getNodeValue(), + discussions.querySelectorAll("article").get(0).getAttributes().getNamedItem("data-mid") + .getNodeValue(), is(String.valueOf(mid))); Message msg = messagesService.getMessage(mid).get(); HtmlPage discussionsOld = webClient.getPage(discussionsUrl + "?to=" + msg.getUpdated().toEpochMilli()); @@ -1795,7 +1930,9 @@ public class ServerTests { assertThat(discussionsOld.querySelectorAll("article").get(0).getAttributes().getNamedItem("data-mid") .getNodeValue(), is(String.valueOf(midNew))); List<Integer> newMids = IntStream.rangeClosed(1, 19) - .map(i -> messagesService.createMessage(ugnich.getUid(), String.valueOf(i), null, Set.of())).boxed() + .map(i -> messagesService.createMessage(ugnich.getUid(), String.valueOf(i), null, + Set.of())) + .boxed() .collect(Collectors.toList()); for (Integer m : newMids) { subscriptionService.subscribeMessage(messagesService.getMessage(m).get(), freefd); @@ -1803,12 +1940,14 @@ public class ServerTests { discussions = (HtmlPage) discussions.refresh(); assertThat(discussions.querySelectorAll("article").size(), is(20)); assertThat( - discussions.querySelectorAll("article").get(19).getAttributes().getNamedItem("data-mid").getNodeValue(), + discussions.querySelectorAll("article").get(19).getAttributes().getNamedItem("data-mid") + .getNodeValue(), is(String.valueOf(mid))); messagesService.createReply(midNew, 0, freefd, "I'm replied", null); discussions = (HtmlPage) discussions.refresh(); assertThat( - discussions.querySelectorAll("article").get(0).getAttributes().getNamedItem("data-mid").getNodeValue(), + discussions.querySelectorAll("article").get(0).getAttributes().getNamedItem("data-mid") + .getNodeValue(), is(String.valueOf(midNew))); Message old = messagesService.getMessage(newMids.get(0)).get(); discussionsOld = webClient.getPage(discussionsUrl + "?to=" + old.getUpdated().toEpochMilli()); @@ -1820,12 +1959,13 @@ public class ServerTests { @Test public void redirectParamShouldCorrectlyRedirectLoggedUser() throws Exception { MvcResult formLoginResult = mockMvc - .perform(post("/login").param("username", ugnichName).param("password", ugnichPassword)) + .perform(post("/login").with(csrf()).param("username", ugnichName).param("password", ugnichPassword)) .andExpect(status().isFound()).andReturn(); Cookie rememberMeFromForm = formLoginResult.getResponse().getCookie("juick-remember-me"); mockMvc.perform(get("/login").cookie(rememberMeFromForm)).andExpect(status().is3xxRedirection()) .andExpect(redirectedUrl("/")); - mockMvc.perform(get("/login?redirect=false").cookie(rememberMeFromForm)).andExpect(status().is3xxRedirection()) + mockMvc.perform(get("/login?redirect=false").cookie(rememberMeFromForm)) + .andExpect(status().is3xxRedirection()) .andExpect(redirectedUrl("/login/success")); } @@ -1850,12 +1990,13 @@ public class ServerTests { jdbcTemplate.execute("DELETE FROM subscr_messages"); jdbcTemplate.execute("DELETE FROM bl_users"); MvcResult loginResult = mockMvc - .perform(post("/login").param("username", freefdName).param("password", freefdPassword)) + .perform(post("/login").with(csrf()).param("username", freefdName).param("password", freefdPassword)) .andExpect(status().is3xxRedirection()).andReturn(); Cookie loginCookie = loginResult.getResponse().getCookie("juick-remember-me"); webClient.setCookieManager(new CookieManager()); - webClient.getCookieManager().addCookie(new com.gargoylesoftware.htmlunit.util.Cookie(loginCookie.getDomain(), - loginCookie.getName(), loginCookie.getValue())); + webClient.getCookieManager() + .addCookie(new com.gargoylesoftware.htmlunit.util.Cookie(loginCookie.getDomain(), + loginCookie.getName(), loginCookie.getValue())); int mid = messagesService.createMessage(ugnich.getUid(), "new test", null, Set.of()); subscriptionService.subscribeMessage(messagesService.getMessage(mid).get(), freefd); messagesService.createReply(mid, 0, ugnich, "new reply", null); @@ -1911,7 +2052,8 @@ public class ServerTests { @Test public void oneClickUnsubscribe() throws Exception { - mockMvc.perform(post("/settings/unsubscribe").param("hash", "123456").param("List-Unsubscribe", "One-Click")) + mockMvc.perform(post("/settings/unsubscribe").with(csrf()).param("hash", "123456").param("List-Unsubscribe", + "One-Click")) .andExpect(status().isBadRequest()); mockMvc.perform(post("/settings/unsubscribe").param("hash", userService.getHashByUID(ugnich.getUid())) .param("List-Unsubscribe", "One-Click")).andExpect(status().isOk()); @@ -1920,20 +2062,25 @@ public class ServerTests { @Test @Order(3) public void ActivityDeserialization() throws IOException { - String followJsonStr = IOUtils.toString(new ClassPathResource("follow.json").getURI(), StandardCharsets.UTF_8); + String followJsonStr = IOUtils.toString(new ClassPathResource("follow.json").getURI(), + StandardCharsets.UTF_8); Follow follow = (Follow) jsonMapper.readValue(followJsonStr, Context.class); - String personJsonStr = IOUtils.toString(new ClassPathResource("person.json").getURI(), StandardCharsets.UTF_8); + String personJsonStr = IOUtils.toString(new ClassPathResource("person.json").getURI(), + StandardCharsets.UTF_8); Person person = (Person) jsonMapper.readValue(personJsonStr, Context.class); - String undoJsonStr = IOUtils.toString(new ClassPathResource("undo.json").getURI(), StandardCharsets.UTF_8); + String undoJsonStr = IOUtils.toString(new ClassPathResource("undo.json").getURI(), + StandardCharsets.UTF_8); Undo undo = jsonMapper.readValue(undoJsonStr, Undo.class); assertThat(undo.getObject(), instanceOf(Follow.class)); String undoFollower = undo.getObject().getId(); - String createJsonStr = IOUtils.toString(new ClassPathResource("create.json").getURI(), StandardCharsets.UTF_8); + String createJsonStr = IOUtils.toString(new ClassPathResource("create.json").getURI(), + StandardCharsets.UTF_8); Create create = jsonMapper.readValue(createJsonStr, Create.class); Note note = (Note) create.getObject(); Context attachmentObj = note.getAttachment().get(0); String attachment = attachmentObj != null ? (String) attachmentObj.getUrl() : StringUtils.EMPTY; - String deleteJsonStr = IOUtils.toString(new ClassPathResource("delete.json").getURI(), StandardCharsets.UTF_8); + String deleteJsonStr = IOUtils.toString(new ClassPathResource("delete.json").getURI(), + StandardCharsets.UTF_8); Delete delete = jsonMapper.readValue(deleteJsonStr, Delete.class); int mid = messagesService.createMessage(ugnich.getUid(), "YO", "", Set.of()); User extUser = new User(); @@ -1947,16 +2094,21 @@ public class ServerTests { Message replyToExt = messagesService.getReply(mid, rid2); Note replyNote = activityPubManager.makeNote(replyToExt); assertThat(replyNote.getInReplyTo(), equalTo(extMessageUri)); - String noteStr = IOUtils.toString(new ClassPathResource("mention.json").getURI(), StandardCharsets.UTF_8); + String noteStr = IOUtils.toString(new ClassPathResource("mention.json").getURI(), + StandardCharsets.UTF_8); Note create2 = jsonMapper.readValue(noteStr, Note.class); - jsonMapper.readValue(IOUtils.toString(new ClassPathResource("webfinger.json").getURI(), StandardCharsets.UTF_8), + jsonMapper.readValue( + IOUtils.toString(new ClassPathResource("webfinger.json").getURI(), + StandardCharsets.UTF_8), Account.class); NodeInfo info = jsonMapper.readValue( - IOUtils.toString(new ClassPathResource("xnodeinfo2.json").getURI(), StandardCharsets.UTF_8), + IOUtils.toString(new ClassPathResource("xnodeinfo2.json").getURI(), + StandardCharsets.UTF_8), NodeInfo.class); assertThat(info.getUsage().getUsers().getActiveHalfyear(), is(42)); Like like = jsonMapper.readValue( - IOUtils.toString(new ClassPathResource("like.json").getURI(), StandardCharsets.UTF_8), Like.class); + IOUtils.toString(new ClassPathResource("like.json").getURI(), StandardCharsets.UTF_8), + Like.class); String undoPleromaStr = IOUtils.toString(new ClassPathResource("undo_pleroma.json").getURI(), StandardCharsets.UTF_8); Undo undoPleroma = jsonMapper.readValue(undoPleromaStr, Undo.class); @@ -1970,7 +2122,8 @@ public class ServerTests { public void activitySerialization() throws Exception { Message msgNoTags = commandsManager.processCommand(ugnich, "people", emptyUri).getNewMessage().get(); String json = jsonMapper.writeValueAsString(Context.build(activityPubManager.makeNote(msgNoTags))); - Message msg = commandsManager.processCommand(ugnich, "*NSFW *shit happens", emptyUri).getNewMessage().get(); + Message msg = commandsManager.processCommand(ugnich, "*NSFW *shit happens", emptyUri).getNewMessage() + .get(); Note note = activityPubManager.makeNote(msg); assertThat(note.isSensitive(), is(true)); json = jsonMapper.writeValueAsString(Context.build(note)); @@ -1984,13 +2137,17 @@ public class ServerTests { create.setId(replyNote.getId()); create.setActor("http://localhost:8080/u/freefd"); create.setObject(replyNote); - signatureManager.post((Actor) signatureManager.getContext(URI.create("http://localhost:8080/u/freefd")).get(), - (Actor) signatureManager.getContext(URI.create("http://localhost:8080/u/ugnich")).get(), create); + signatureManager.post( + (Actor) signatureManager.getContext(URI.create("http://localhost:8080/u/freefd")).get(), + (Actor) signatureManager.getContext(URI.create("http://localhost:8080/u/ugnich")).get(), + create); Message replyToExt = commandsManager - .processCommand(ugnich, String.format("#%d/1 PSSH YOBA ETO TI", msg.getMid()), emptyUri).getNewMessage() + .processCommand(ugnich, String.format("#%d/1 PSSH YOBA ETO TI", msg.getMid()), emptyUri) + .getNewMessage() .get(); json = jsonMapper.writeValueAsString(Context.build( - activityPubManager.makeNote(messagesService.getReply(replyToExt.getMid(), replyToExt.getRid())))); + activityPubManager.makeNote( + messagesService.getReply(replyToExt.getMid(), replyToExt.getRid())))); mockMvc.perform(get("/n/2-0")).andExpect(status().isOk()); mockMvc.perform(get("/n/2222-0")).andExpect(status().isNotFound()); mockMvc.perform(get("/n/2-14")).andExpect(status().isNotFound()); @@ -2017,14 +2174,17 @@ public class ServerTests { Actor ugnichPerson = profileController.getUser("ugnich"); now = Instant.now(); requestDate = DateFormattersHolder.getHttpDateFormatter().format(now); - String signatureString = signatureManager.addSignature(ugnichPerson, testHost, "GET", meUri, requestDate, + String signatureString = signatureManager.addSignature(ugnichPerson, testHost, "GET", meUri, + requestDate, StringUtils.EMPTY); MvcResult me = mockMvc.perform(get("/api/me").header("Host", testHost).header("Date", requestDate) .header("Signature", signatureString)).andExpect(status().isOk()).andReturn(); User meUser = jsonMapper.readValue(me.getResponse().getContentAsString(), User.class); assertThat(meUser, is(ugnich)); - String testuserResponseString = IOUtils.toString(testuserResponse.getInputStream(), StandardCharsets.UTF_8); - String testappResponseString = IOUtils.toString(testappResponse.getInputStream(), StandardCharsets.UTF_8); + String testuserResponseString = IOUtils.toString(testuserResponse.getInputStream(), + StandardCharsets.UTF_8); + String testappResponseString = IOUtils.toString(testappResponse.getInputStream(), + StandardCharsets.UTF_8); ClientHttpRequestFactory originalRequestFactory = apClient.getRequestFactory(); URI testuserUri = URI.create("https://example.com/u/testuser"); URI testuserkeyUri = URI.create("https://example.com/u/testuser#main-key"); @@ -2049,13 +2209,16 @@ public class ServerTests { testRequestDate, digestHeader, testKeystoreManager); mockMvc.perform(post(inboxUri).header("Host", testHost).header("Date", testRequestDate) .header("Digest", digestHeader).header("Signature", testSignatureString) - .contentType(Context.LD_JSON_MEDIA_TYPE).content(payload)).andExpect(status().isAccepted()); + .contentType(Context.LD_JSON_MEDIA_TYPE).content(payload)) + .andExpect(status().isAccepted()); mockMvc.perform(post(inboxUri).header("Host", "wronghost").header("Date", testRequestDate) .header("Signature", testSignatureString).contentType(Context.LD_JSON_MEDIA_TYPE) - .content(IOUtils.toByteArray(testfollowRequest.getInputStream()))).andExpect(status().isUnauthorized()); + .content(IOUtils.toByteArray(testfollowRequest.getInputStream()))) + .andExpect(status().isUnauthorized()); // digest required but not present mockMvc.perform(post(inboxUri).header("Host", testHost).header("Date", testRequestDate) - .header("Signature", testSignatureString).contentType(Context.LD_JSON_MEDIA_TYPE).content(payload)) + .header("Signature", testSignatureString).contentType(Context.LD_JSON_MEDIA_TYPE) + .content(payload)) .andExpect(status().isUnauthorized()); // test flagging as application payload = IOUtils.toByteArray(flagPayload.getInputStream()); @@ -2065,11 +2228,13 @@ public class ServerTests { testRequestDate = DateFormattersHolder.getHttpDateFormatter().format(now2); Application testapp = (Application) signatureManager.getContext(testAppUri).get(); assertThat(testapp.getPublicKey().getPublicKeyPem(), is(testKeystoreManager.getPublicKeyPem())); - testSignatureString = signatureManager.addSignature(testapp, "localhost", "POST", inboxUri, testRequestDate, + testSignatureString = signatureManager.addSignature(testapp, "localhost", "POST", inboxUri, + testRequestDate, digestHeader, testKeystoreManager); mockMvc.perform(post(inboxUri).header("Host", testHost).header("Date", testRequestDate) .header("Signature", testSignatureString).header("Digest", digestHeader) - .contentType(Context.LD_JSON_MEDIA_TYPE).content(payload)).andExpect(status().isAccepted()); + .contentType(Context.LD_JSON_MEDIA_TYPE).content(payload)) + .andExpect(status().isAccepted()); apClient.setRequestFactory(originalRequestFactory); } @@ -2082,7 +2247,8 @@ public class ServerTests { @Test public void hostmeta() throws Exception { - MvcResult result = mockMvc.perform(get("/.well-known/host-meta")).andExpect(status().isOk()).andReturn(); + MvcResult result = mockMvc.perform(get("/.well-known/host-meta")).andExpect(status().isOk()) + .andReturn(); String xrd = result.getResponse().getContentAsString(); result = mockMvc.perform(get("/.well-known/x-nodeinfo2")).andExpect(status().isOk()).andReturn(); } @@ -2096,7 +2262,8 @@ public class ServerTests { assertThat(res.getText(), is("Private message sent")); MvcResult result = mockMvc.perform(get("/api/groups_pms").with(httpBasic(freefdName, freefdPassword))) .andExpect(status().isOk()).andReturn(); - PrivateChats chats = jsonMapper.readValue(result.getResponse().getContentAsString(), PrivateChats.class); + PrivateChats chats = jsonMapper.readValue(result.getResponse().getContentAsString(), + PrivateChats.class); assertThat(chats.getUsers().size(), is(1)); } @@ -2114,7 +2281,8 @@ public class ServerTests { public void signupTest() throws Exception { emailService.addVerificationCode(null, "demo@email.com", "123456"); MvcResult result = mockMvc.perform(post("/api/signup").param("username", "testuser") - .param("password", "demopassword").param("verificationCode", "123456")).andExpect(status().isOk()) + .param("password", "demopassword").param("verificationCode", "123456")) + .andExpect(status().isOk()) .andReturn(); User testuser = jsonMapper.readValue(result.getResponse().getContentAsString(), User.class); assertThat(testuser.getName(), is("testuser")); @@ -2125,14 +2293,17 @@ public class ServerTests { assertThat(commandsManager.processCommand(ugnich, "#23213213/2 BAD COMMENT", emptyUri).getText(), is("Message not found")); Message msg = commandsManager.processCommand(ugnich, "YO", emptyUri).getNewMessage().get(); - assertThat(commandsManager.processCommand(ugnich, String.format("#%d/1 BAD COMMENT", msg.getMid()), emptyUri) + assertThat(commandsManager + .processCommand(ugnich, String.format("#%d/1 BAD COMMENT", msg.getMid()), emptyUri) .getText(), is("Reply not found")); - CommandResult result = commandsManager.processCommand(freefd, String.format("#%d *GOOD *COMMENT", msg.getMid()), + CommandResult result = commandsManager.processCommand(freefd, + String.format("#%d *GOOD *COMMENT", msg.getMid()), emptyUri); Message reply = result.getNewMessage().get(); assertThat( commandsManager.processCommand(ugnich, - String.format("#%d/%d *GOOD *BAD", reply.getMid(), reply.getRid()), emptyUri).getText(), + String.format("#%d/%d *GOOD *BAD", reply.getMid(), reply.getRid()), + emptyUri).getText(), startsWith("Reply posted")); } @@ -2140,10 +2311,10 @@ public class ServerTests { public void XMPPSignupIsDisabled() throws Exception { jdbcTemplate.update("INSERT INTO jids(loginhash, jid) VALUES('1', 'test@jid.tld')"); MvcResult formLoginResult = mockMvc - .perform(post("/login").param("username", ugnichName).param("password", ugnichPassword)) + .perform(post("/login").with(csrf()).param("username", ugnichName).param("password", ugnichPassword)) .andExpect(status().is3xxRedirection()).andReturn(); Cookie rememberMeFromForm = formLoginResult.getResponse().getCookie("juick-remember-me"); - mockMvc.perform(post("/signup").cookie(rememberMeFromForm).param("hash", "1").param("type", "xmpp") + mockMvc.perform(post("/signup").with(csrf()).cookie(rememberMeFromForm).param("hash", "1").param("type", "xmpp") .param("action", "link")).andExpect(status().isOk()) .andExpect(content().string(containsString("XMPP support is disabled"))); } @@ -2163,9 +2334,11 @@ public class ServerTests { @Test public void verifiedUsersTest() { assertThat(userService.getUserByName("ugnich").isVerified(), is(false)); - jdbcTemplate.update("INSERT INTO telegram(user_id, tg_id) VALUES(?, ?)", ugnich.getUid(), "100001866137681"); + jdbcTemplate.update("INSERT INTO telegram(user_id, tg_id) VALUES(?, ?)", ugnich.getUid(), + "100001866137681"); assertThat(userService.canDeleteTelegramUser(userService.getUserByName("ugnich")), is(false)); - jdbcTemplate.update("INSERT INTO facebook(user_id, fb_id) VALUES(?, ?)", ugnich.getUid(), "100001866137681"); + jdbcTemplate.update("INSERT INTO facebook(user_id, fb_id) VALUES(?, ?)", ugnich.getUid(), + "100001866137681"); assertThat(userService.getUserByName("ugnich").isVerified(), is(true)); assertThat(userService.canDeleteTelegramUser(userService.getUserByName("ugnich")), is(true)); jdbcTemplate.update("DELETE FROM facebook"); @@ -2194,31 +2367,38 @@ public class ServerTests { String convertedAvatarHash = DigestUtils.md5DigestAsHex(IOUtils.toByteArray(convertedAvatarUri)); mockMvc.perform(get("/api/me").with(httpBasic(freefdName, freefdPassword))).andExpect(status().isOk()) .andExpect(jsonPath("$.avatar", is( - String.format("http://localhost:8080/i/a/%d-%s.png", freefd.getUid(), convertedAvatarHash)))); - mockMvc.perform(post("/api/me").with(httpBasic(ugnichName, ugnichPassword)).param("password", "newPassword")) + String.format("http://localhost:8080/i/a/%d-%s.png", freefd.getUid(), + convertedAvatarHash)))); + mockMvc.perform(post("/api/me").with(httpBasic(ugnichName, ugnichPassword)).param("password", + "newPassword")) .andExpect(status().isOk()); mockMvc.perform(get("/api/me").with(httpBasic(ugnichName, ugnichPassword))) .andExpect(status().isUnauthorized()); - mockMvc.perform(post("/api/me").with(httpBasic(ugnichName, "newPassword")).param("password", ugnichPassword)) + mockMvc.perform(post("/api/me").with(httpBasic(ugnichName, "newPassword")).param("password", + ugnichPassword)) .andExpect(status().isOk()); mockMvc.perform(get("/api/me").with(httpBasic(ugnichName, ugnichPassword))).andExpect(status().isOk()); assertThat(usersController.getMe(ugnich).getJIDs().size(), is(0)); jdbcTemplate.update("INSERT INTO jids(user_id, jid) VALUES(?, ?)", ugnich.getUid(), "test@example.com"); - jdbcTemplate.update("INSERT INTO jids(user_id, jid) VALUES(?, ?)", ugnich.getUid(), "test2@example.com"); + jdbcTemplate.update("INSERT INTO jids(user_id, jid) VALUES(?, ?)", ugnich.getUid(), + "test2@example.com"); assertThat(usersController.getMe(ugnich).getJIDs().size(), is(2)); mockMvc.perform( - post("/api/me").with(httpBasic(ugnichName, ugnichPassword)).param("jid-del", "test@example.com")) + post("/api/me").with(httpBasic(ugnichName, ugnichPassword)).param("jid-del", + "test@example.com")) .andExpect(status().isOk()); assertThat(usersController.getMe(ugnich).getJIDs().size(), is(1)); mockMvc.perform( - post("/api/me").with(httpBasic(ugnichName, ugnichPassword)).param("jid-del", "test2@example.com")) + post("/api/me").with(httpBasic(ugnichName, ugnichPassword)).param("jid-del", + "test2@example.com")) .andExpect(status().isBadRequest()); jdbcTemplate.execute("DELETE FROM jids"); } @Test public void varyMvcResponse() throws Exception { - mockMvc.perform(get("/")).andExpect(status().isOk()).andExpect(header().string("Vary", "Accept-Language")); + mockMvc.perform(get("/")).andExpect(status().isOk()) + .andExpect(header().string("Vary", "Accept-Language")); mockMvc.perform(get("/rss/ugnich/blog")).andExpect(status().isOk()) .andExpect(header().string("Vary", "Accept-Language")); mockMvc.perform(get("/api/messages")).andExpect(status().isOk()) @@ -2251,7 +2431,8 @@ public class ServerTests { }); mockMvc.perform(post("/api/inbox").contentType(ACTIVITY_MEDIA_TYPE).content(deleteJsonStr)) .andExpect(status().isAccepted()); - mockMvc.perform(post("/api/inbox").contentType(ACTIVITY_MEDIA_TYPE).content(deleteJsonStr).header("Signature", + mockMvc.perform(post("/api/inbox").contentType(ACTIVITY_MEDIA_TYPE).content(deleteJsonStr).header( + "Signature", "keyId=\"https://example.com/users/deleted#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"wHoU91JJBsIYcR1W1/57B0oG98t5Aa/TvGPw1B8KQlAp5KhpePnOzD1MZRgivBx7YKO6eYwDx+AX9dn6tjlAvzRLygv21H6UoDZFihWzeE1HM8pY2Pe4EhUgYBN0YuiKUi7W4TS9bDRAJ5vGNPUWATe+2o5Jcbux5cZYXFKKYbLBLD+/IlqPdHA2IXLZ52HFVVfBkPH5sSklV6XJtD/PHLK9R/I9w/mUpj9moUPQu44rR7KvxiGNuHla3vfDtJbkBqLMdScX91EG8373AulXPUiCCF7R2lJB0fFQedm2nSbcwBoJ32GEyOyOPFgPKG5zd9Fd5TfB1pmA8ZIE0sChfA==\"")) .andExpect(status().isAccepted()); apClient.setRequestFactory(originalRequestFactory); @@ -2270,7 +2451,8 @@ public class ServerTests { MockRestServiceServer restServiceServer = MockRestServiceServer.createServer(apClient); restServiceServer.expect(times(2), requestTo(delete.getObject().getId())) .andRespond(withSuccess( - IOUtils.toString(testSuspendedUserResponse.getInputStream(), StandardCharsets.UTF_8), + IOUtils.toString(testSuspendedUserResponse.getInputStream(), + StandardCharsets.UTF_8), MediaType.APPLICATION_JSON)); Person testuser = (Person) signatureManager.getContext(URI.create(delete.getObject().getId())).get(); Instant now = Instant.now(); @@ -2281,7 +2463,8 @@ public class ServerTests { String testSignatureString = signatureManager.addSignature(testuser, "localhost", "POST", inboxUri, testRequestDate, digestHeader, testKeystoreManager); mockMvc.perform(post(inboxUri).contentType(ACTIVITY_MEDIA_TYPE).content(deleteJsonStr) - .header("Host", "localhost").header("Date", testRequestDate).header("Digest", digestHeader) + .header("Host", "localhost").header("Date", testRequestDate) + .header("Digest", digestHeader) .header("Signature", testSignatureString)).andExpect(status().isAccepted()); apClient.setRequestFactory(originalRequestFactory); Mockito.verify(deleteListener, Mockito.times(1)).onApplicationEvent(deleteEventCaptor.capture()); @@ -2300,7 +2483,8 @@ public class ServerTests { restServiceServer.expect(requestTo(delete.getObject().getId())).andRespond(response -> { throw new ResourceAccessException("Connection reset"); }); - mockMvc.perform(post("/api/inbox").contentType(ACTIVITY_MEDIA_TYPE).content(deleteJsonStr).header("Signature", + mockMvc.perform(post("/api/inbox").contentType(ACTIVITY_MEDIA_TYPE).content(deleteJsonStr).header( + "Signature", "keyId=\"https://example.com/users/deleted#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"wHoU91JJBsIYcR1W1/57B0oG98t5Aa/TvGPw1B8KQlAp5KhpePnOzD1MZRgivBx7YKO6eYwDx+AX9dn6tjlAvzRLygv21H6UoDZFihWzeE1HM8pY2Pe4EhUgYBN0YuiKUi7W4TS9bDRAJ5vGNPUWATe+2o5Jcbux5cZYXFKKYbLBLD+/IlqPdHA2IXLZ52HFVVfBkPH5sSklV6XJtD/PHLK9R/I9w/mUpj9moUPQu44rR7KvxiGNuHla3vfDtJbkBqLMdScX91EG8373AulXPUiCCF7R2lJB0fFQedm2nSbcwBoJ32GEyOyOPFgPKG5zd9Fd5TfB1pmA8ZIE0sChfA==\"")) .andExpect(status().isAccepted()); apClient.setRequestFactory(originalRequestFactory); @@ -2322,11 +2506,12 @@ public class ServerTests { Create create = jsonMapper.readValue(noteString, Create.class); Note note = (Note) create.getObject(); String markdown = remarkConverter.convertFragment((String) note.getContent()); - String commandBody = note.getContent() == null ? markdown : note.getAttachment().stream().map(attachment -> { - String attachmentUrl = attachment.getUrl(); - String attachmentName = attachment.getName(); - return PlainTextFormatter.markdownUrl(attachmentUrl, attachmentName); - }).reduce(markdown, (current, next) -> String.format("%s\n%s", current, next)); + String commandBody = note.getContent() == null ? markdown + : note.getAttachment().stream().map(attachment -> { + String attachmentUrl = attachment.getUrl(); + String attachmentName = attachment.getName(); + return PlainTextFormatter.markdownUrl(attachmentUrl, attachmentName); + }).reduce(markdown, (current, next) -> String.format("%s\n%s", current, next)); } @Test @@ -2340,7 +2525,8 @@ public class ServerTests { @Test public void nodeinfo() throws Exception { - MvcResult nodeinfoXRD = mockMvc.perform(get("/.well-known/nodeinfo").contentType(MediaType.APPLICATION_JSON)) + MvcResult nodeinfoXRD = mockMvc + .perform(get("/.well-known/nodeinfo").contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()).andReturn(); JsonNode node = jsonMapper.readTree(nodeinfoXRD.getResponse().getContentAsString()); assertThat(node.get("links"), notNullValue()); @@ -2398,7 +2584,8 @@ public class ServerTests { mockMvc.perform(get("/rss/ugnich/blog").header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;" + "q=0.8,application/signed-exchange;v=b3")) - .andExpect(status().isOk()).andExpect(content().contentType("application/rss+xml;charset=UTF-8")); + .andExpect(status().isOk()) + .andExpect(content().contentType("application/rss+xml;charset=UTF-8")); mockMvc.perform(get("/rss/ugnich/feed").accept(MediaType.TEXT_XML)).andExpect(status().isOk()); mockMvc.perform(get("/rss/ugnich/diary").accept(MediaType.TEXT_XML)).andExpect(status().isNotFound()); } @@ -2407,8 +2594,10 @@ public class ServerTests { public void wsThreadsShouldRedirect() throws Exception { int mid = messagesService.createMessage(ugnich.getUid(), "tst", null, Set.of()); mockMvc.perform(get("/ugnich/" + mid)).andExpect(status().isOk()); - mockMvc.perform(get("/s/" + mid)).andExpect(status().isFound()).andExpect(redirectedUrl("/ugnich/" + mid)); - mockMvc.perform(get("/ws/" + mid)).andExpect(status().isFound()).andExpect(redirectedUrl("/ugnich/" + mid)); + mockMvc.perform(get("/s/" + mid)).andExpect(status().isFound()) + .andExpect(redirectedUrl("/ugnich/" + mid)); + mockMvc.perform(get("/ws/" + mid)).andExpect(status().isFound()) + .andExpect(redirectedUrl("/ugnich/" + mid)); } @MockBean @@ -2432,7 +2621,8 @@ public class ServerTests { public void tagStatsSpec() throws Exception { String newUserName = "tagger"; String newUserSecret = "secret"; - User newUser = userService.createUser(newUserName, newUserSecret).orElseThrow(IllegalStateException::new); + User newUser = userService.createUser(newUserName, newUserSecret) + .orElseThrow(IllegalStateException::new); commandsManager.processCommand(newUser, "*test yo", emptyUri); commandsManager.processCommand(newUser, "*test yo2", emptyUri); commandsManager.processCommand(newUser, "*rare yo3", emptyUri); @@ -2457,12 +2647,14 @@ public class ServerTests { User demo = MockUtils.mockUser(45, ugnichName, ugnichPassword); Message html = MockUtils.mockMessage(56, demo, "yo"); String htmlText = webApp - .renderHtml(MessageUtils.formatHtml(html), PlainTextFormatter.formatUrl(html), html, "12345") + .renderHtml(MessageUtils.formatHtml(html), PlainTextFormatter.formatUrl(html), html, + "12345") .orElseThrow(); assertThat(htmlText, is(getSnapshot(testSubscriptionHtmlEmail))); html.setMid(0); String htmlPM = webApp - .renderHtml(MessageUtils.formatHtml(html), PlainTextFormatter.formatUrl(html), html, "12345") + .renderHtml(MessageUtils.formatHtml(html), PlainTextFormatter.formatUrl(html), html, + "12345") .orElseThrow(); assertThat(htmlPM, is(getSnapshot(testPrivateHtmlEmail))); } @@ -2522,7 +2714,7 @@ public class ServerTests { @Test public void invalidMediaTypeTest() throws Exception { mockMvc.perform(get("/api/messages") - .header("Accept", "application/xml")).andExpect(status().isBadRequest()) + .header("Accept", "application/xml")).andExpect(status().isBadRequest()) .andExpect(content().string("Invalid media type")); } @@ -2540,6 +2732,6 @@ public class ServerTests { get("/api/groups_pms").with(httpBasic(ugnichName, ugnichPassword))) .andExpect(status().isOk()) .andExpect(jsonPath("$.pms", empty())); - + } } |