default-compile
diff --git a/src/main/docker/.dockerignore b/src/main/docker/.dockerignore
new file mode 100644
index 00000000..28e6b138
--- /dev/null
+++ b/src/main/docker/.dockerignore
@@ -0,0 +1,5 @@
+# https://docs.docker.com/engine/reference/builder/#dockerignore-file
+# by default ignore everything except the jar file
+**/*
+!*.jar
+!*.war
diff --git a/src/main/docker/Dockerfile b/src/main/docker/Dockerfile
index d403534f..9d844df9 100644
--- a/src/main/docker/Dockerfile
+++ b/src/main/docker/Dockerfile
@@ -4,10 +4,10 @@ ENV SPRING_OUTPUT_ANSI_ENABLED=ALWAYS \
JHIPSTER_SLEEP=0 \
JAVA_OPTS=""
-# add directly the war
-ADD *.war /app.war
-
-EXPOSE 8090
CMD echo "The application will start in ${JHIPSTER_SLEEP}s..." && \
sleep ${JHIPSTER_SLEEP} && \
java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar /app.war
+
+EXPOSE 8070
+
+ADD *.war /app.war
diff --git a/src/main/docker/elasticsearch.yml b/src/main/docker/elasticsearch.yml
index 94bb13a1..b4a2fc9e 100644
--- a/src/main/docker/elasticsearch.yml
+++ b/src/main/docker/elasticsearch.yml
@@ -1,7 +1,7 @@
version: '2'
services:
greatbigexampleapplication-elasticsearch:
- image: elasticsearch:2.4.1
+ image: elasticsearch:2.4.6
# volumes:
# - ~/volumes/jhipster/GreatBigExampleApplication/elasticsearch/:/usr/share/elasticsearch/data/
ports:
diff --git a/src/main/docker/postgresql.yml b/src/main/docker/postgresql.yml
index 560f8136..754fce87 100644
--- a/src/main/docker/postgresql.yml
+++ b/src/main/docker/postgresql.yml
@@ -1,7 +1,7 @@
version: '2'
services:
greatbigexampleapplication-postgresql:
- image: postgres:9.6.2
+ image: postgres:9.6.5
# volumes:
# - ~/volumes/jhipster/GreatBigExampleApplication/postgresql/:/var/lib/postgresql/data/
environment:
diff --git a/src/main/docker/sonar.yml b/src/main/docker/sonar.yml
index c8e59e02..6cbc05af 100644
--- a/src/main/docker/sonar.yml
+++ b/src/main/docker/sonar.yml
@@ -1,7 +1,7 @@
version: '2'
services:
greatbigexampleapplication-sonar:
- image: sonarqube:6.4-alpine
+ image: sonarqube:6.5-alpine
ports:
- - 9010:9010
+ - 9000:9000
- 9092:9092
diff --git a/src/main/java/org/exampleapps/greatbig/GreatBigExampleApplicationApp.java b/src/main/java/org/exampleapps/greatbig/GreatBigExampleApplicationApp.java
index 96e7330f..4edb753e 100644
--- a/src/main/java/org/exampleapps/greatbig/GreatBigExampleApplicationApp.java
+++ b/src/main/java/org/exampleapps/greatbig/GreatBigExampleApplicationApp.java
@@ -15,9 +15,6 @@
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.env.Environment;
-import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration;
-import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration;
-
import javax.annotation.PostConstruct;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -25,8 +22,7 @@
import java.util.Collection;
@ComponentScan
-@EnableAutoConfiguration(exclude = {ElasticsearchAutoConfiguration.class, ElasticsearchDataAutoConfiguration.class,
- MetricFilterAutoConfiguration.class, MetricRepositoryAutoConfiguration.class})
+@EnableAutoConfiguration(exclude = {MetricFilterAutoConfiguration.class, MetricRepositoryAutoConfiguration.class})
@EnableConfigurationProperties({LiquibaseProperties.class, ApplicationProperties.class})
public class GreatBigExampleApplicationApp {
@@ -43,7 +39,7 @@ public GreatBigExampleApplicationApp(Environment env) {
*
* Spring profiles can be configured with a program arguments --spring.profiles.active=your-active-profile
*
- * You can find more information on how profiles work with JHipster on http://jhipster.github.io/profiles/.
+ * You can find more information on how profiles work with JHipster on http://www.jhipster.tech/profiles/.
*/
@PostConstruct
public void initApplication() {
@@ -53,7 +49,7 @@ public void initApplication() {
"with both the 'dev' and 'prod' profiles at the same time.");
}
if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_CLOUD)) {
- log.error("You have misconfigured your application! It should not" +
+ log.error("You have misconfigured your application! It should not " +
"run with both the 'dev' and 'cloud' profiles at the same time.");
}
}
diff --git a/src/main/java/org/exampleapps/greatbig/config/ApplicationProperties.java b/src/main/java/org/exampleapps/greatbig/config/ApplicationProperties.java
index 11e8a496..f1c663cd 100644
--- a/src/main/java/org/exampleapps/greatbig/config/ApplicationProperties.java
+++ b/src/main/java/org/exampleapps/greatbig/config/ApplicationProperties.java
@@ -3,9 +3,10 @@
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
- * Properties specific to JHipster.
+ * Properties specific to Great Big Example Application.
*
* Properties are configured in the application.yml file.
+ * See {@link io.github.jhipster.config.JHipsterProperties} for a good example.
*/
@ConfigurationProperties(prefix = "application", ignoreUnknownFields = false)
public class ApplicationProperties {
diff --git a/src/main/java/org/exampleapps/greatbig/config/CacheConfiguration.java b/src/main/java/org/exampleapps/greatbig/config/CacheConfiguration.java
index 2eee3270..0abba7a4 100644
--- a/src/main/java/org/exampleapps/greatbig/config/CacheConfiguration.java
+++ b/src/main/java/org/exampleapps/greatbig/config/CacheConfiguration.java
@@ -37,6 +37,8 @@ public CacheConfiguration(JHipsterProperties jHipsterProperties) {
@Bean
public JCacheManagerCustomizer cacheManagerCustomizer() {
return cm -> {
+ cm.createCache(org.exampleapps.greatbig.repository.UserRepository.USERS_BY_LOGIN_CACHE, jcacheConfiguration);
+ cm.createCache(org.exampleapps.greatbig.repository.UserRepository.USERS_BY_EMAIL_CACHE, jcacheConfiguration);
cm.createCache(org.exampleapps.greatbig.domain.User.class.getName(), jcacheConfiguration);
cm.createCache(org.exampleapps.greatbig.domain.User.class.getName() + ".authorities", jcacheConfiguration);
cm.createCache(org.exampleapps.greatbig.domain.Authority.class.getName(), jcacheConfiguration);
diff --git a/src/main/java/org/exampleapps/greatbig/config/Constants.java b/src/main/java/org/exampleapps/greatbig/config/Constants.java
index 2344e9d8..c1fb0f57 100644
--- a/src/main/java/org/exampleapps/greatbig/config/Constants.java
+++ b/src/main/java/org/exampleapps/greatbig/config/Constants.java
@@ -5,12 +5,13 @@
*/
public final class Constants {
- //Regex for acceptable logins
+ // Regex for acceptable logins
public static final String LOGIN_REGEX = "^[_'.@A-Za-z0-9-]*$";
public static final String SYSTEM_ACCOUNT = "system";
public static final String ANONYMOUS_USER = "anonymoususer";
-
+ public static final String DEFAULT_LANGUAGE = "en";
+
private Constants() {
}
}
diff --git a/src/main/java/org/exampleapps/greatbig/config/DatabaseConfiguration.java b/src/main/java/org/exampleapps/greatbig/config/DatabaseConfiguration.java
index 03c93d61..f4ee73d3 100644
--- a/src/main/java/org/exampleapps/greatbig/config/DatabaseConfiguration.java
+++ b/src/main/java/org/exampleapps/greatbig/config/DatabaseConfiguration.java
@@ -4,7 +4,6 @@
import io.github.jhipster.config.liquibase.AsyncSpringLiquibase;
import liquibase.integration.spring.SpringLiquibase;
-import org.h2.tools.Server;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -13,6 +12,7 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.Environment;
+import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
import org.springframework.core.task.TaskExecutor;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
@@ -20,6 +20,8 @@
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.sql.SQLException;
@Configuration
@@ -45,8 +47,31 @@ public DatabaseConfiguration(Environment env) {
*/
@Bean(initMethod = "start", destroyMethod = "stop")
@Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)
- public Server h2TCPServer() throws SQLException {
- return Server.createTcpServer("-tcp","-tcpAllowOthers");
+ public Object h2TCPServer() throws SQLException {
+ try {
+ // We don't want to include H2 when we are packaging for the "prod" profile and won't
+ // actually need it, so we have to load / invoke things at runtime through reflection.
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ Class> serverClass = Class.forName("org.h2.tools.Server", true, loader);
+ Method createServer = serverClass.getMethod("createTcpServer", String[].class);
+ return createServer.invoke(null, new Object[] { new String[] { "-tcp", "-tcpAllowOthers" } });
+
+ } catch (ClassNotFoundException | LinkageError e) {
+ throw new RuntimeException("Failed to load and initialize org.h2.tools.Server", e);
+
+ } catch (SecurityException | NoSuchMethodException e) {
+ throw new RuntimeException("Failed to get method org.h2.tools.Server.createTcpServer()", e);
+
+ } catch (IllegalAccessException | IllegalArgumentException e) {
+ throw new RuntimeException("Failed to invoke org.h2.tools.Server.createTcpServer()", e);
+
+ } catch (InvocationTargetException e) {
+ Throwable t = e.getTargetException();
+ if (t instanceof SQLException) {
+ throw (SQLException) t;
+ }
+ throw new RuntimeException("Unchecked exception in org.h2.tools.Server.createTcpServer()", t);
+ }
}
@Bean
diff --git a/src/main/java/org/exampleapps/greatbig/config/DefaultProfileUtil.java b/src/main/java/org/exampleapps/greatbig/config/DefaultProfileUtil.java
index 73a7856f..777885c0 100644
--- a/src/main/java/org/exampleapps/greatbig/config/DefaultProfileUtil.java
+++ b/src/main/java/org/exampleapps/greatbig/config/DefaultProfileUtil.java
@@ -25,7 +25,7 @@ private DefaultProfileUtil() {
* @param app the Spring application
*/
public static void addDefaultProfile(SpringApplication app) {
- Map defProperties = new HashMap<>();
+ Map defProperties = new HashMap<>();
/*
* The default profile to use when no other profiles are defined
* This cannot be set in the application.yml file.
diff --git a/src/main/java/org/exampleapps/greatbig/config/ElasticsearchConfiguration.java b/src/main/java/org/exampleapps/greatbig/config/ElasticsearchConfiguration.java
index a8bd46bc..2c95a11d 100644
--- a/src/main/java/org/exampleapps/greatbig/config/ElasticsearchConfiguration.java
+++ b/src/main/java/org/exampleapps/greatbig/config/ElasticsearchConfiguration.java
@@ -5,12 +5,13 @@
import org.elasticsearch.client.Client;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-// import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
+import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.EntityMapper;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
+
import com.github.vanroy.springdata.jest.JestElasticsearchTemplate;
import com.github.vanroy.springdata.jest.mapper.DefaultJestResultsMapper;
import io.searchbox.client.JestClient;
@@ -19,10 +20,12 @@
public class ElasticsearchConfiguration {
@Bean
- public JestElasticsearchTemplate elasticsearchTemplate(JestClient client, Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder) {
+ // public ElasticsearchTemplate elasticsearchTemplate(Client client, Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder) {
+ // return new ElasticsearchTemplate(client, new CustomEntityMapper(jackson2ObjectMapperBuilder.createXmlMapper(false).build()));
+ public JestElasticsearchTemplate elasticsearchTemplate(JestClient client, Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder) {
return new JestElasticsearchTemplate(client, new DefaultJestResultsMapper(
new CustomEntityMapper(jackson2ObjectMapperBuilder.createXmlMapper(false).build())));
- }
+ }
public class CustomEntityMapper implements EntityMapper {
diff --git a/src/main/java/org/exampleapps/greatbig/config/JacksonConfiguration.java b/src/main/java/org/exampleapps/greatbig/config/JacksonConfiguration.java
index 29a71fef..e0eae68d 100644
--- a/src/main/java/org/exampleapps/greatbig/config/JacksonConfiguration.java
+++ b/src/main/java/org/exampleapps/greatbig/config/JacksonConfiguration.java
@@ -5,11 +5,13 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.zalando.problem.ProblemModule;
+import org.zalando.problem.validation.ConstraintViolationProblemModule;
@Configuration
public class JacksonConfiguration {
- /**
+ /*
* Support for Hibernate types in Jackson.
*/
@Bean
@@ -17,11 +19,28 @@ public Hibernate5Module hibernate5Module() {
return new Hibernate5Module();
}
- /**
+ /*
* Jackson Afterburner module to speed up serialization/deserialization.
*/
@Bean
public AfterburnerModule afterburnerModule() {
return new AfterburnerModule();
}
+
+ /*
+ * Module for serialization/deserialization of RFC7807 Problem.
+ */
+ @Bean
+ ProblemModule problemModule() {
+ return new ProblemModule();
+ }
+
+ /*
+ * Module for serialization/deserialization of ConstraintViolationProblem.
+ */
+ @Bean
+ ConstraintViolationProblemModule constraintViolationProblemModule() {
+ return new ConstraintViolationProblemModule();
+ }
+
}
diff --git a/src/main/java/org/exampleapps/greatbig/config/LoggingConfiguration.java b/src/main/java/org/exampleapps/greatbig/config/LoggingConfiguration.java
index b115e229..2c87d4ae 100644
--- a/src/main/java/org/exampleapps/greatbig/config/LoggingConfiguration.java
+++ b/src/main/java/org/exampleapps/greatbig/config/LoggingConfiguration.java
@@ -1,13 +1,22 @@
package org.exampleapps.greatbig.config;
+import java.net.InetSocketAddress;
+import java.util.Iterator;
+
import io.github.jhipster.config.JHipsterProperties;
import ch.qos.logback.classic.AsyncAppender;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.boolex.OnMarkerEvaluator;
+import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.LoggerContextListener;
+import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.filter.EvaluatorFilter;
import ch.qos.logback.core.spi.ContextAwareBase;
-import net.logstash.logback.appender.LogstashSocketAppender;
+import ch.qos.logback.core.spi.FilterReply;
+import net.logstash.logback.appender.LogstashTcpSocketAppender;
+import net.logstash.logback.encoder.LogstashEncoder;
import net.logstash.logback.stacktrace.ShortenedThrowableConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -17,6 +26,10 @@
@Configuration
public class LoggingConfiguration {
+ private static final String LOGSTASH_APPENDER_NAME = "LOGSTASH";
+
+ private static final String ASYNC_LOGSTASH_APPENDER_NAME = "ASYNC_LOGSTASH";
+
private final Logger log = LoggerFactory.getLogger(LoggingConfiguration.class);
private LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
@@ -34,39 +47,46 @@ public LoggingConfiguration(@Value("${spring.application.name}") String appName,
this.jHipsterProperties = jHipsterProperties;
if (jHipsterProperties.getLogging().getLogstash().isEnabled()) {
addLogstashAppender(context);
-
- // Add context listener
- LogbackLoggerContextListener loggerContextListener = new LogbackLoggerContextListener();
- loggerContextListener.setContext(context);
- context.addListener(loggerContextListener);
+ addContextListener(context);
+ }
+ if (jHipsterProperties.getMetrics().getLogs().isEnabled()) {
+ setMetricsMarkerLogbackFilter(context);
}
}
- public void addLogstashAppender(LoggerContext context) {
+ private void addContextListener(LoggerContext context) {
+ LogbackLoggerContextListener loggerContextListener = new LogbackLoggerContextListener();
+ loggerContextListener.setContext(context);
+ context.addListener(loggerContextListener);
+ }
+
+ private void addLogstashAppender(LoggerContext context) {
log.info("Initializing Logstash logging");
- LogstashSocketAppender logstashAppender = new LogstashSocketAppender();
- logstashAppender.setName("LOGSTASH");
+ LogstashTcpSocketAppender logstashAppender = new LogstashTcpSocketAppender();
+ logstashAppender.setName(LOGSTASH_APPENDER_NAME);
logstashAppender.setContext(context);
String customFields = "{\"app_name\":\"" + appName + "\",\"app_port\":\"" + serverPort + "\"}";
+ // More documentation is available at: https://github.com/logstash/logstash-logback-encoder
+ LogstashEncoder logstashEncoder=new LogstashEncoder();
+ // Set the Logstash appender config from JHipster properties
+ logstashEncoder.setCustomFields(customFields);
// Set the Logstash appender config from JHipster properties
- logstashAppender.setSyslogHost(jHipsterProperties.getLogging().getLogstash().getHost());
- logstashAppender.setPort(jHipsterProperties.getLogging().getLogstash().getPort());
- logstashAppender.setCustomFields(customFields);
+ logstashAppender.addDestinations(new InetSocketAddress(jHipsterProperties.getLogging().getLogstash().getHost(),jHipsterProperties.getLogging().getLogstash().getPort()));
- // Limit the maximum length of the forwarded stacktrace so that it won't exceed the 8KB UDP limit of logstash
ShortenedThrowableConverter throwableConverter = new ShortenedThrowableConverter();
- throwableConverter.setMaxLength(7500);
throwableConverter.setRootCauseFirst(true);
- logstashAppender.setThrowableConverter(throwableConverter);
+ logstashEncoder.setThrowableConverter(throwableConverter);
+ logstashEncoder.setCustomFields(customFields);
+ logstashAppender.setEncoder(logstashEncoder);
logstashAppender.start();
// Wrap the appender in an Async appender for performance
AsyncAppender asyncLogstashAppender = new AsyncAppender();
asyncLogstashAppender.setContext(context);
- asyncLogstashAppender.setName("ASYNC_LOGSTASH");
+ asyncLogstashAppender.setName(ASYNC_LOGSTASH_APPENDER_NAME);
asyncLogstashAppender.setQueueSize(jHipsterProperties.getLogging().getLogstash().getQueueSize());
asyncLogstashAppender.addAppender(logstashAppender);
asyncLogstashAppender.start();
@@ -74,6 +94,32 @@ public void addLogstashAppender(LoggerContext context) {
context.getLogger("ROOT").addAppender(asyncLogstashAppender);
}
+ // Configure a log filter to remove "metrics" logs from all appenders except the "LOGSTASH" appender
+ private void setMetricsMarkerLogbackFilter(LoggerContext context) {
+ log.info("Filtering metrics logs from all appenders except the {} appender", LOGSTASH_APPENDER_NAME);
+ OnMarkerEvaluator onMarkerMetricsEvaluator = new OnMarkerEvaluator();
+ onMarkerMetricsEvaluator.setContext(context);
+ onMarkerMetricsEvaluator.addMarker("metrics");
+ onMarkerMetricsEvaluator.start();
+ EvaluatorFilter metricsFilter = new EvaluatorFilter<>();
+ metricsFilter.setContext(context);
+ metricsFilter.setEvaluator(onMarkerMetricsEvaluator);
+ metricsFilter.setOnMatch(FilterReply.DENY);
+ metricsFilter.start();
+
+ for (ch.qos.logback.classic.Logger logger : context.getLoggerList()) {
+ for (Iterator> it = logger.iteratorForAppenders(); it.hasNext();) {
+ Appender appender = it.next();
+ if (!appender.getName().equals(ASYNC_LOGSTASH_APPENDER_NAME)) {
+ log.debug("Filter metrics logs from the {} appender", appender.getName());
+ appender.setContext(context);
+ appender.addFilter(metricsFilter);
+ appender.start();
+ }
+ }
+ }
+ }
+
/**
* Logback configuration is achieved by configuration file and API.
* When configuration file change is detected, the configuration is reset.
diff --git a/src/main/java/org/exampleapps/greatbig/config/MetricsConfiguration.java b/src/main/java/org/exampleapps/greatbig/config/MetricsConfiguration.java
index e687a29b..a0f7cc33 100644
--- a/src/main/java/org/exampleapps/greatbig/config/MetricsConfiguration.java
+++ b/src/main/java/org/exampleapps/greatbig/config/MetricsConfiguration.java
@@ -3,6 +3,7 @@
import io.github.jhipster.config.JHipsterProperties;
import com.codahale.metrics.JmxReporter;
+import com.codahale.metrics.JvmAttributeGaugeSet;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Slf4jReporter;
import com.codahale.metrics.health.HealthCheckRegistry;
@@ -13,6 +14,8 @@
import com.zaxxer.hikari.HikariDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.slf4j.Marker;
+import org.slf4j.MarkerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
@@ -29,8 +32,10 @@ public class MetricsConfiguration extends MetricsConfigurerAdapter {
private static final String PROP_METRIC_REG_JVM_THREADS = "jvm.threads";
private static final String PROP_METRIC_REG_JVM_FILES = "jvm.files";
private static final String PROP_METRIC_REG_JVM_BUFFERS = "jvm.buffers";
+ private static final String PROP_METRIC_REG_JVM_ATTRIBUTE_SET = "jvm.attributes";
private static final String PROP_METRIC_REG_JCACHE_STATISTICS = "jcache.statistics";
+
private final Logger log = LoggerFactory.getLogger(MetricsConfiguration.class);
private MetricRegistry metricRegistry = new MetricRegistry();
@@ -70,7 +75,7 @@ public void init() {
metricRegistry.register(PROP_METRIC_REG_JVM_THREADS, new ThreadStatesGaugeSet());
metricRegistry.register(PROP_METRIC_REG_JVM_FILES, new FileDescriptorRatioGauge());
metricRegistry.register(PROP_METRIC_REG_JVM_BUFFERS, new BufferPoolMetricSet(ManagementFactory.getPlatformMBeanServer()));
-
+ metricRegistry.register(PROP_METRIC_REG_JVM_ATTRIBUTE_SET, new JvmAttributeGaugeSet());
metricRegistry.register(PROP_METRIC_REG_JCACHE_STATISTICS, new JCacheGaugeSet());
if (hikariDataSource != null) {
log.debug("Monitoring the datasource");
@@ -83,8 +88,10 @@ public void init() {
}
if (jHipsterProperties.getMetrics().getLogs().isEnabled()) {
log.info("Initializing Metrics Log reporting");
+ Marker metricsMarker = MarkerFactory.getMarker("metrics");
final Slf4jReporter reporter = Slf4jReporter.forRegistry(metricRegistry)
.outputTo(LoggerFactory.getLogger("metrics"))
+ .markWith(metricsMarker)
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build();
diff --git a/src/main/java/org/exampleapps/greatbig/config/SecurityConfiguration.java b/src/main/java/org/exampleapps/greatbig/config/SecurityConfiguration.java
index 9f936059..93535a9d 100644
--- a/src/main/java/org/exampleapps/greatbig/config/SecurityConfiguration.java
+++ b/src/main/java/org/exampleapps/greatbig/config/SecurityConfiguration.java
@@ -3,11 +3,10 @@
import org.exampleapps.greatbig.security.*;
import org.exampleapps.greatbig.security.jwt.*;
-import io.github.jhipster.security.*;
-
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@@ -19,13 +18,14 @@
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
-import org.springframework.security.data.repository.query.SecurityEvaluationContextExtension;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.filter.CorsFilter;
+import org.zalando.problem.spring.web.advice.security.SecurityProblemSupport;
import javax.annotation.PostConstruct;
@Configuration
+@Import(SecurityProblemSupport.class)
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@@ -38,14 +38,14 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final CorsFilter corsFilter;
- public SecurityConfiguration(AuthenticationManagerBuilder authenticationManagerBuilder, UserDetailsService userDetailsService,
- TokenProvider tokenProvider,
- CorsFilter corsFilter) {
+ private final SecurityProblemSupport problemSupport;
+ public SecurityConfiguration(AuthenticationManagerBuilder authenticationManagerBuilder, UserDetailsService userDetailsService,TokenProvider tokenProvider,CorsFilter corsFilter, SecurityProblemSupport problemSupport) {
this.authenticationManagerBuilder = authenticationManagerBuilder;
this.userDetailsService = userDetailsService;
this.tokenProvider = tokenProvider;
this.corsFilter = corsFilter;
+ this.problemSupport = problemSupport;
}
@PostConstruct
@@ -53,17 +53,12 @@ public void init() {
try {
authenticationManagerBuilder
.userDetailsService(userDetailsService)
- .passwordEncoder(passwordEncoder());
+ .passwordEncoder(passwordEncoder());
} catch (Exception e) {
throw new BeanInitializationException("Security configuration failed", e);
}
}
- @Bean
- public Http401UnauthorizedEntryPoint http401UnauthorizedEntryPoint() {
- return new Http401UnauthorizedEntryPoint();
- }
-
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
@@ -86,7 +81,8 @@ protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling()
- .authenticationEntryPoint(http401UnauthorizedEntryPoint())
+ .authenticationEntryPoint(problemSupport)
+ .accessDeniedHandler(problemSupport)
.and()
.csrf()
.disable()
@@ -101,11 +97,9 @@ protected void configure(HttpSecurity http) throws Exception {
.antMatchers("/api/register").permitAll()
.antMatchers("/api/activate").permitAll()
.antMatchers("/api/authenticate").permitAll()
- .antMatchers("/api/account/reset_password/init").permitAll()
- .antMatchers("/api/account/reset_password/finish").permitAll()
+ .antMatchers("/api/account/reset-password/init").permitAll()
+ .antMatchers("/api/account/reset-password/finish").permitAll()
.antMatchers("/api/profile-info").permitAll()
- .antMatchers("/api/profiles/*/follow").authenticated()
- .antMatchers("/api/profiles/**").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/websocket/tracker").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/websocket/**").permitAll()
@@ -123,8 +117,4 @@ private JWTConfigurer securityConfigurerAdapter() {
return new JWTConfigurer(tokenProvider);
}
- @Bean
- public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
- return new SecurityEvaluationContextExtension();
- }
}
diff --git a/src/main/java/org/exampleapps/greatbig/config/WebConfigurer.java b/src/main/java/org/exampleapps/greatbig/config/WebConfigurer.java
index b34e033d..12d1b8c4 100644
--- a/src/main/java/org/exampleapps/greatbig/config/WebConfigurer.java
+++ b/src/main/java/org/exampleapps/greatbig/config/WebConfigurer.java
@@ -21,6 +21,7 @@
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
+import org.springframework.http.MediaType;
import java.io.File;
import java.nio.file.Paths;
@@ -70,9 +71,9 @@ public void onStartup(ServletContext servletContext) throws ServletException {
public void customize(ConfigurableEmbeddedServletContainer container) {
MimeMappings mappings = new MimeMappings(MimeMappings.DEFAULT);
// IE issue, see https://github.com/jhipster/generator-jhipster/pull/711
- mappings.add("html", "text/html;charset=utf-8");
+ mappings.add("html", MediaType.TEXT_HTML_VALUE + ";charset=utf-8");
// CloudFoundry issue, see https://github.com/cloudfoundry/gorouter/issues/64
- mappings.add("json", "text/html;charset=utf-8");
+ mappings.add("json", MediaType.TEXT_HTML_VALUE + ";charset=utf-8");
container.setMimeMappings(mappings);
// When running in an IDE or with ./mvnw spring-boot:run, set location of the static web assets.
setLocationForStaticAssets(container);
@@ -102,14 +103,14 @@ private void setLocationForStaticAssets(ConfigurableEmbeddedServletContainer con
}
/**
- * Resolve path prefix to static resources.
+ * Resolve path prefix to static resources.
*/
private String resolvePathPrefix() {
String fullExecutablePath = this.getClass().getResource("").getPath();
String rootPath = Paths.get(".").toUri().normalize().getPath();
String extractedPath = fullExecutablePath.replace(rootPath, "");
int extractionEndIndex = extractedPath.indexOf("target/");
- if(extractionEndIndex <= 0) {
+ if (extractionEndIndex <= 0) {
return "";
}
return extractedPath.substring(0, extractionEndIndex);
@@ -163,6 +164,7 @@ public CorsFilter corsFilter() {
if (config.getAllowedOrigins() != null && !config.getAllowedOrigins().isEmpty()) {
log.debug("Registering CORS filter");
source.registerCorsConfiguration("/api/**", config);
+ source.registerCorsConfiguration("/management/**", config);
source.registerCorsConfiguration("/v2/api-docs", config);
}
return new CorsFilter(source);
@@ -173,10 +175,24 @@ public CorsFilter corsFilter() {
*/
private void initH2Console(ServletContext servletContext) {
log.debug("Initialize H2 console");
- ServletRegistration.Dynamic h2ConsoleServlet = servletContext.addServlet("H2Console", new org.h2.server.web.WebServlet());
- h2ConsoleServlet.addMapping("/h2-console/*");
- h2ConsoleServlet.setInitParameter("-properties", "src/main/resources/");
- h2ConsoleServlet.setLoadOnStartup(1);
+ try {
+ // We don't want to include H2 when we are packaging for the "prod" profile and won't
+ // actually need it, so we have to load / invoke things at runtime through reflection.
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ Class> servletClass = Class.forName("org.h2.server.web.WebServlet", true, loader);
+ Servlet servlet = (Servlet) servletClass.newInstance();
+
+ ServletRegistration.Dynamic h2ConsoleServlet = servletContext.addServlet("H2Console", servlet);
+ h2ConsoleServlet.addMapping("/h2-console/*");
+ h2ConsoleServlet.setInitParameter("-properties", "src/main/resources/");
+ h2ConsoleServlet.setLoadOnStartup(1);
+
+ } catch (ClassNotFoundException | LinkageError e) {
+ throw new RuntimeException("Failed to load and initialize org.h2.server.web.WebServlet", e);
+
+ } catch (IllegalAccessException | InstantiationException e) {
+ throw new RuntimeException("Failed to instantiate org.h2.server.web.WebServlet", e);
+ }
}
@Autowired(required = false)
diff --git a/src/main/java/org/exampleapps/greatbig/config/WebsocketSecurityConfiguration.java b/src/main/java/org/exampleapps/greatbig/config/WebsocketSecurityConfiguration.java
index 08363922..0d6be4a4 100644
--- a/src/main/java/org/exampleapps/greatbig/config/WebsocketSecurityConfiguration.java
+++ b/src/main/java/org/exampleapps/greatbig/config/WebsocketSecurityConfiguration.java
@@ -12,17 +12,13 @@ public class WebsocketSecurityConfiguration extends AbstractSecurityWebSocketMes
@Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
- .nullDestMatcher().authenticated().simpDestMatchers("/topic/tracker")
- .hasAuthority(AuthoritiesConstants.ADMIN).simpDestMatchers("/topic/message")
- .hasAuthority(AuthoritiesConstants.ADMIN)
+ .nullDestMatcher().authenticated()
+ .simpDestMatchers("/topic/tracker").hasAuthority(AuthoritiesConstants.ADMIN)
// matches any destination that starts with /topic/
// (i.e. cannot send messages directly to /topic/)
// (i.e. cannot subscribe to /topic/messages/* to get messages sent to
// /topic/messages-user)
.simpDestMatchers("/topic/**").authenticated()
- // matches any destination that starts with /chat/private and /chat/*
- .simpDestMatchers("/chat/private*").hasAuthority(AuthoritiesConstants.USER)
- .simpDestMatchers("/chat/**").permitAll()
// message types other than MESSAGE and SUBSCRIBE
.simpTypeMatchers(SimpMessageType.MESSAGE, SimpMessageType.SUBSCRIBE).denyAll()
// catch all
diff --git a/src/main/java/org/exampleapps/greatbig/config/audit/AuditEventConverter.java b/src/main/java/org/exampleapps/greatbig/config/audit/AuditEventConverter.java
index 120925e6..2184c85c 100644
--- a/src/main/java/org/exampleapps/greatbig/config/audit/AuditEventConverter.java
+++ b/src/main/java/org/exampleapps/greatbig/config/audit/AuditEventConverter.java
@@ -35,6 +35,9 @@ public List convertToAuditEvent(Iterable persi
* @return the converted list.
*/
public AuditEvent convertToAuditEvent(PersistentAuditEvent persistentAuditEvent) {
+ if (persistentAuditEvent == null) {
+ return null;
+ }
return new AuditEvent(Date.from(persistentAuditEvent.getAuditEventDate()), persistentAuditEvent.getPrincipal(),
persistentAuditEvent.getAuditEventType(), convertDataToObjects(persistentAuditEvent.getData()));
}
diff --git a/src/main/java/org/exampleapps/greatbig/domain/Authority.java b/src/main/java/org/exampleapps/greatbig/domain/Authority.java
index ba3192ec..74cbd64e 100644
--- a/src/main/java/org/exampleapps/greatbig/domain/Authority.java
+++ b/src/main/java/org/exampleapps/greatbig/domain/Authority.java
@@ -16,12 +16,13 @@
@Entity
@Table(name = "jhi_authority")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
+
public class Authority implements Serializable {
private static final long serialVersionUID = 1L;
@NotNull
- @Size(min = 0, max = 50)
+ @Size(max = 50)
@Id
@Column(length = 50)
private String name;
diff --git a/src/main/java/org/exampleapps/greatbig/domain/PersistentAuditEvent.java b/src/main/java/org/exampleapps/greatbig/domain/PersistentAuditEvent.java
index 638d8ec2..01e9ec73 100644
--- a/src/main/java/org/exampleapps/greatbig/domain/PersistentAuditEvent.java
+++ b/src/main/java/org/exampleapps/greatbig/domain/PersistentAuditEvent.java
@@ -8,7 +8,8 @@
import java.util.Map;
/**
- * Persist AuditEvent managed by the Spring Boot actuator
+ * Persist AuditEvent managed by the Spring Boot actuator.
+ *
* @see org.springframework.boot.actuate.audit.AuditEvent
*/
@Entity
@@ -27,6 +28,7 @@ public class PersistentAuditEvent implements Serializable {
@Column(name = "event_date")
private Instant auditEventDate;
+
@Column(name = "event_type")
private String auditEventType;
diff --git a/src/main/java/org/exampleapps/greatbig/domain/SocialUserConnection.java b/src/main/java/org/exampleapps/greatbig/domain/SocialUserConnection.java
index 9eb11011..0af40e06 100644
--- a/src/main/java/org/exampleapps/greatbig/domain/SocialUserConnection.java
+++ b/src/main/java/org/exampleapps/greatbig/domain/SocialUserConnection.java
@@ -14,6 +14,7 @@
@Entity
@Table(name = "jhi_social_user_connection")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
+
public class SocialUserConnection implements Serializable {
private static final long serialVersionUID = 1L;
diff --git a/src/main/java/org/exampleapps/greatbig/domain/User.java b/src/main/java/org/exampleapps/greatbig/domain/User.java
index a87c4dd2..423f13b7 100644
--- a/src/main/java/org/exampleapps/greatbig/domain/User.java
+++ b/src/main/java/org/exampleapps/greatbig/domain/User.java
@@ -1,7 +1,6 @@
package org.exampleapps.greatbig.domain;
import org.exampleapps.greatbig.config.Constants;
-import org.exampleapps.greatbig.domain.Author;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.apache.commons.lang3.StringUtils;
@@ -9,7 +8,6 @@
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.validator.constraints.Email;
-import org.springframework.data.elasticsearch.annotations.Document;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
@@ -28,7 +26,7 @@
@Entity
@Table(name = "jhi_user")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
-@Document(indexName = "user")
+@org.springframework.data.elasticsearch.annotations.Document(indexName = "user")
public class User extends AbstractAuditingEntity implements Serializable {
private static final long serialVersionUID = 1L;
@@ -67,8 +65,8 @@ public class User extends AbstractAuditingEntity implements Serializable {
@Column(nullable = false)
private boolean activated = false;
- @Size(min = 2, max = 5)
- @Column(name = "lang_key", length = 5)
+ @Size(min = 2, max = 6)
+ @Column(name = "lang_key", length = 6)
private String langKey;
@Size(max = 256)
@@ -90,9 +88,10 @@ public class User extends AbstractAuditingEntity implements Serializable {
@JsonIgnore
@ManyToMany
- @JoinTable(name = "jhi_user_authority", joinColumns = {
- @JoinColumn(name = "user_id", referencedColumnName = "id") }, inverseJoinColumns = {
- @JoinColumn(name = "authority_name", referencedColumnName = "name") })
+ @JoinTable(
+ name = "jhi_user_authority",
+ joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
+ inverseJoinColumns = {@JoinColumn(name = "authority_name", referencedColumnName = "name")})
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@BatchSize(size = 20)
private Set authorities = new HashSet<>();
@@ -109,7 +108,7 @@ public String getLogin() {
return login;
}
- //Lowercase the login before saving it in database
+ // Lowercase the login before saving it in database
public void setLogin(String login) {
this.login = StringUtils.lowerCase(login, Locale.ENGLISH);
}
@@ -222,8 +221,15 @@ public int hashCode() {
@Override
public String toString() {
- return "User{" + "login='" + login + '\'' + ", firstName='" + firstName + '\'' + ", lastName='" + lastName
- + '\'' + ", email='" + email + '\'' + ", imageUrl='" + imageUrl + '\'' + ", activated='" + activated
- + '\'' + ", langKey='" + langKey + '\'' + ", activationKey='" + activationKey + '\'' + "}";
+ return "User{" +
+ "login='" + login + '\'' +
+ ", firstName='" + firstName + '\'' +
+ ", lastName='" + lastName + '\'' +
+ ", email='" + email + '\'' +
+ ", imageUrl='" + imageUrl + '\'' +
+ ", activated='" + activated + '\'' +
+ ", langKey='" + langKey + '\'' +
+ ", activationKey='" + activationKey + '\'' +
+ "}";
}
}
diff --git a/src/main/java/org/exampleapps/greatbig/repository/CustomAuditEventRepository.java b/src/main/java/org/exampleapps/greatbig/repository/CustomAuditEventRepository.java
index 8c423159..28326496 100644
--- a/src/main/java/org/exampleapps/greatbig/repository/CustomAuditEventRepository.java
+++ b/src/main/java/org/exampleapps/greatbig/repository/CustomAuditEventRepository.java
@@ -4,6 +4,8 @@
import org.exampleapps.greatbig.config.audit.AuditEventConverter;
import org.exampleapps.greatbig.domain.PersistentAuditEvent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.boot.actuate.audit.AuditEventRepository;
import org.springframework.stereotype.Repository;
@@ -11,7 +13,9 @@
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* An implementation of Spring Boot's AuditEventRepository.
@@ -21,10 +25,17 @@ public class CustomAuditEventRepository implements AuditEventRepository {
private static final String AUTHORIZATION_FAILURE = "AUTHORIZATION_FAILURE";
+ /**
+ * Should be the same as in Liquibase migration.
+ */
+ protected static final int EVENT_DATA_COLUMN_MAX_LENGTH = 255;
+
private final PersistenceAuditEventRepository persistenceAuditEventRepository;
private final AuditEventConverter auditEventConverter;
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
public CustomAuditEventRepository(PersistenceAuditEventRepository persistenceAuditEventRepository,
AuditEventConverter auditEventConverter) {
@@ -70,8 +81,32 @@ public void add(AuditEvent event) {
persistentAuditEvent.setPrincipal(event.getPrincipal());
persistentAuditEvent.setAuditEventType(event.getType());
persistentAuditEvent.setAuditEventDate(event.getTimestamp().toInstant());
- persistentAuditEvent.setData(auditEventConverter.convertDataToStrings(event.getData()));
+ Map eventData = auditEventConverter.convertDataToStrings(event.getData());
+ persistentAuditEvent.setData(truncate(eventData));
persistenceAuditEventRepository.save(persistentAuditEvent);
}
}
+
+ /**
+ * Truncate event data that might exceed column length.
+ */
+ private Map truncate(Map data) {
+ Map results = new HashMap<>();
+
+ if (data != null) {
+ for (Map.Entry entry : data.entrySet()) {
+ String value = entry.getValue();
+ if (value != null) {
+ int length = value.length();
+ if (length > EVENT_DATA_COLUMN_MAX_LENGTH) {
+ value = value.substring(0, EVENT_DATA_COLUMN_MAX_LENGTH);
+ log.warn("Event data for {} too long ({}) has been truncated to {}. Consider increasing column width.",
+ entry.getKey(), length, EVENT_DATA_COLUMN_MAX_LENGTH);
+ }
+ }
+ results.put(entry.getKey(), value);
+ }
+ }
+ return results;
+ }
}
diff --git a/src/main/java/org/exampleapps/greatbig/repository/CustomSocialConnectionRepository.java b/src/main/java/org/exampleapps/greatbig/repository/CustomSocialConnectionRepository.java
index f517bba0..3ddad9cf 100644
--- a/src/main/java/org/exampleapps/greatbig/repository/CustomSocialConnectionRepository.java
+++ b/src/main/java/org/exampleapps/greatbig/repository/CustomSocialConnectionRepository.java
@@ -118,9 +118,9 @@ public void addConnection(Connection> connection) {
public void updateConnection(Connection> connection) {
SocialUserConnection socialUserConnection = socialUserConnectionRepository.findOneByUserIdAndProviderIdAndProviderUserId(userId, connection.getKey().getProviderId(), connection.getKey().getProviderUserId());
if (socialUserConnection != null) {
- SocialUserConnection socialUserConnectionToUdpate = connectionToUserSocialConnection(connection, socialUserConnection.getRank());
- socialUserConnectionToUdpate.setId(socialUserConnection.getId());
- socialUserConnectionRepository.save(socialUserConnectionToUdpate);
+ SocialUserConnection socialUserConnectionToUpdate = connectionToUserSocialConnection(connection, socialUserConnection.getRank());
+ socialUserConnectionToUpdate.setId(socialUserConnection.getId());
+ socialUserConnectionRepository.save(socialUserConnectionToUpdate);
}
}
diff --git a/src/main/java/org/exampleapps/greatbig/repository/UserRepository.java b/src/main/java/org/exampleapps/greatbig/repository/UserRepository.java
index b7ae1996..763c4979 100644
--- a/src/main/java/org/exampleapps/greatbig/repository/UserRepository.java
+++ b/src/main/java/org/exampleapps/greatbig/repository/UserRepository.java
@@ -1,6 +1,8 @@
package org.exampleapps.greatbig.repository;
import org.exampleapps.greatbig.domain.User;
+
+import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.EntityGraph;
@@ -16,21 +18,30 @@
@Repository
public interface UserRepository extends JpaRepository {
+ String USERS_BY_LOGIN_CACHE = "usersByLogin";
+
+ String USERS_BY_EMAIL_CACHE = "usersByEmail";
+
Optional findOneByActivationKey(String activationKey);
List findAllByActivatedIsFalseAndCreatedDateBefore(Instant dateTime);
Optional findOneByResetKey(String resetKey);
- Optional findOneByEmail(String email);
+ Optional findOneByEmailIgnoreCase(String email);
Optional findOneByLogin(String login);
@EntityGraph(attributePaths = "authorities")
- User findOneWithAuthoritiesById(Long id);
+ Optional findOneWithAuthoritiesById(Long id);
@EntityGraph(attributePaths = "authorities")
+ @Cacheable(cacheNames = USERS_BY_LOGIN_CACHE)
Optional findOneWithAuthoritiesByLogin(String login);
+ @EntityGraph(attributePaths = "authorities")
+ @Cacheable(cacheNames = USERS_BY_EMAIL_CACHE)
+ Optional findOneWithAuthoritiesByEmail(String email);
+
Page findAllByLoginNot(Pageable pageable, String login);
}
diff --git a/src/main/java/org/exampleapps/greatbig/security/DomainUserDetailsService.java b/src/main/java/org/exampleapps/greatbig/security/DomainUserDetailsService.java
index 210fcb9d..1677954b 100644
--- a/src/main/java/org/exampleapps/greatbig/security/DomainUserDetailsService.java
+++ b/src/main/java/org/exampleapps/greatbig/security/DomainUserDetailsService.java
@@ -34,18 +34,24 @@ public DomainUserDetailsService(UserRepository userRepository) {
public UserDetails loadUserByUsername(final String login) {
log.debug("Authenticating {}", login);
String lowercaseLogin = login.toLowerCase(Locale.ENGLISH);
- Optional userFromDatabase = userRepository.findOneWithAuthoritiesByLogin(lowercaseLogin);
- return userFromDatabase.map(user -> {
- if (!user.getActivated()) {
- throw new UserNotActivatedException("User " + lowercaseLogin + " was not activated");
- }
- List grantedAuthorities = user.getAuthorities().stream()
- .map(authority -> new SimpleGrantedAuthority(authority.getName()))
- .collect(Collectors.toList());
- return new org.springframework.security.core.userdetails.User(lowercaseLogin,
- user.getPassword(),
- grantedAuthorities);
- }).orElseThrow(() -> new UsernameNotFoundException("User " + lowercaseLogin + " was not found in the " +
- "database"));
+ Optional userByEmailFromDatabase = userRepository.findOneWithAuthoritiesByEmail(lowercaseLogin);
+ return userByEmailFromDatabase.map(user -> createSpringSecurityUser(lowercaseLogin, user)).orElseGet(() -> {
+ Optional userByLoginFromDatabase = userRepository.findOneWithAuthoritiesByLogin(lowercaseLogin);
+ return userByLoginFromDatabase.map(user -> createSpringSecurityUser(lowercaseLogin, user))
+ .orElseThrow(() -> new UsernameNotFoundException("User " + lowercaseLogin + " was not found in the " +
+ "database"));
+ });
+ }
+
+ private org.springframework.security.core.userdetails.User createSpringSecurityUser(String lowercaseLogin, User user) {
+ if (!user.getActivated()) {
+ throw new UserNotActivatedException("User " + lowercaseLogin + " was not activated");
+ }
+ List grantedAuthorities = user.getAuthorities().stream()
+ .map(authority -> new SimpleGrantedAuthority(authority.getName()))
+ .collect(Collectors.toList());
+ return new org.springframework.security.core.userdetails.User(user.getLogin(),
+ user.getPassword(),
+ grantedAuthorities);
}
}
diff --git a/src/main/java/org/exampleapps/greatbig/security/SecurityUtils.java b/src/main/java/org/exampleapps/greatbig/security/SecurityUtils.java
index 07273a69..6c059ba0 100644
--- a/src/main/java/org/exampleapps/greatbig/security/SecurityUtils.java
+++ b/src/main/java/org/exampleapps/greatbig/security/SecurityUtils.java
@@ -1,10 +1,11 @@
package org.exampleapps.greatbig.security;
-import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
+import java.util.Optional;
+
/**
* Utility class for Spring Security.
*/
@@ -18,19 +19,18 @@ private SecurityUtils() {
*
* @return the login of the current user
*/
- public static String getCurrentUserLogin() {
+ public static Optional getCurrentUserLogin() {
SecurityContext securityContext = SecurityContextHolder.getContext();
- Authentication authentication = securityContext.getAuthentication();
- String userName = null;
- if (authentication != null) {
- if (authentication.getPrincipal() instanceof UserDetails) {
- UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
- userName = springSecurityUser.getUsername();
- } else if (authentication.getPrincipal() instanceof String) {
- userName = (String) authentication.getPrincipal();
- }
- }
- return userName;
+ return Optional.ofNullable(securityContext.getAuthentication())
+ .map(authentication -> {
+ if (authentication.getPrincipal() instanceof UserDetails) {
+ UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
+ return springSecurityUser.getUsername();
+ } else if (authentication.getPrincipal() instanceof String) {
+ return (String) authentication.getPrincipal();
+ }
+ return null;
+ });
}
/**
@@ -38,13 +38,11 @@ public static String getCurrentUserLogin() {
*
* @return the JWT of the current user
*/
- public static String getCurrentUserJWT() {
+ public static Optional getCurrentUserJWT() {
SecurityContext securityContext = SecurityContextHolder.getContext();
- Authentication authentication = securityContext.getAuthentication();
- if (authentication != null && authentication.getCredentials() instanceof String) {
- return (String) authentication.getCredentials();
- }
- return null;
+ return Optional.ofNullable(securityContext.getAuthentication())
+ .filter(authentication -> authentication.getCredentials() instanceof String)
+ .map(authentication -> (String) authentication.getCredentials());
}
/**
@@ -54,12 +52,10 @@ public static String getCurrentUserJWT() {
*/
public static boolean isAuthenticated() {
SecurityContext securityContext = SecurityContextHolder.getContext();
- Authentication authentication = securityContext.getAuthentication();
- if (authentication != null) {
- return authentication.getAuthorities().stream()
- .noneMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(AuthoritiesConstants.ANONYMOUS));
- }
- return false;
+ return Optional.ofNullable(securityContext.getAuthentication())
+ .map(authentication -> authentication.getAuthorities().stream()
+ .noneMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(AuthoritiesConstants.ANONYMOUS)))
+ .orElse(false);
}
/**
@@ -72,11 +68,9 @@ public static boolean isAuthenticated() {
*/
public static boolean isCurrentUserInRole(String authority) {
SecurityContext securityContext = SecurityContextHolder.getContext();
- Authentication authentication = securityContext.getAuthentication();
- if (authentication != null) {
- return authentication.getAuthorities().stream()
- .anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(authority));
- }
- return false;
+ return Optional.ofNullable(securityContext.getAuthentication())
+ .map(authentication -> authentication.getAuthorities().stream()
+ .anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(authority)))
+ .orElse(false);
}
}
diff --git a/src/main/java/org/exampleapps/greatbig/security/SpringSecurityAuditorAware.java b/src/main/java/org/exampleapps/greatbig/security/SpringSecurityAuditorAware.java
index 74bf32be..ad9757a1 100644
--- a/src/main/java/org/exampleapps/greatbig/security/SpringSecurityAuditorAware.java
+++ b/src/main/java/org/exampleapps/greatbig/security/SpringSecurityAuditorAware.java
@@ -13,7 +13,6 @@ public class SpringSecurityAuditorAware implements AuditorAware {
@Override
public String getCurrentAuditor() {
- String userName = SecurityUtils.getCurrentUserLogin();
- return userName != null ? userName : Constants.SYSTEM_ACCOUNT;
+ return SecurityUtils.getCurrentUserLogin().orElse(Constants.SYSTEM_ACCOUNT);
}
}
diff --git a/src/main/java/org/exampleapps/greatbig/security/jwt/TokenProvider.java b/src/main/java/org/exampleapps/greatbig/security/jwt/TokenProvider.java
index aa25a0cc..8178121f 100644
--- a/src/main/java/org/exampleapps/greatbig/security/jwt/TokenProvider.java
+++ b/src/main/java/org/exampleapps/greatbig/security/jwt/TokenProvider.java
@@ -47,7 +47,7 @@ public void init() {
1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSecondsForRememberMe();
}
- public String createToken(Authentication authentication, Boolean rememberMe) {
+ public String createToken(Authentication authentication, boolean rememberMe) {
String authorities = authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(","));
diff --git a/src/main/java/org/exampleapps/greatbig/service/MailService.java b/src/main/java/org/exampleapps/greatbig/service/MailService.java
index 67f2426b..22bfe83a 100644
--- a/src/main/java/org/exampleapps/greatbig/service/MailService.java
+++ b/src/main/java/org/exampleapps/greatbig/service/MailService.java
@@ -110,6 +110,7 @@ public void sendSocialRegistrationValidationEmail(User user, String provider) {
Locale locale = Locale.forLanguageTag(user.getLangKey());
Context context = new Context(locale);
context.setVariable(USER, user);
+ context.setVariable(BASE_URL, jHipsterProperties.getMail().getBaseUrl());
context.setVariable("provider", StringUtils.capitalize(provider));
String content = templateEngine.process("socialRegistrationValidationEmail", context);
String subject = messageSource.getMessage("email.social.registration.title", null, locale);
diff --git a/src/main/java/org/exampleapps/greatbig/service/SocialService.java b/src/main/java/org/exampleapps/greatbig/service/SocialService.java
index 15ed90b0..b8b29d7e 100644
--- a/src/main/java/org/exampleapps/greatbig/service/SocialService.java
+++ b/src/main/java/org/exampleapps/greatbig/service/SocialService.java
@@ -4,6 +4,7 @@
import org.exampleapps.greatbig.domain.User;
import org.exampleapps.greatbig.repository.AuthorityRepository;
import org.exampleapps.greatbig.repository.UserRepository;
+import org.exampleapps.greatbig.security.AuthoritiesConstants;
import org.exampleapps.greatbig.repository.search.UserSearchRepository;
import org.apache.commons.lang3.RandomStringUtils;
@@ -88,7 +89,7 @@ private User createUserIfNotExist(UserProfile userProfile, String langKey, Strin
throw new IllegalArgumentException("Email cannot be null with an existing login");
}
if (!StringUtils.isBlank(email)) {
- Optional user = userRepository.findOneByEmail(email);
+ Optional user = userRepository.findOneByEmailIgnoreCase(email);
if (user.isPresent()) {
log.info("User already exist associate the connection to this account");
return user.get();
@@ -98,7 +99,7 @@ private User createUserIfNotExist(UserProfile userProfile, String langKey, Strin
String login = getLoginDependingOnProviderId(userProfile, providerId);
String encryptedPassword = passwordEncoder.encode(RandomStringUtils.random(10));
Set authorities = new HashSet<>(1);
- authorities.add(authorityRepository.findOne("ROLE_USER"));
+ authorities.add(authorityRepository.findOne(AuthoritiesConstants.USER));
User newUser = new User();
newUser.setLogin(login);
@@ -116,7 +117,7 @@ private User createUserIfNotExist(UserProfile userProfile, String langKey, Strin
}
/**
- * @return login if provider manage a login like Twitter or Github otherwise email address.
+ * @return login if provider manage a login like Twitter or GitHub otherwise email address.
* Because provider like Google or Facebook didn't provide login or login like "12099388847393"
*/
private String getLoginDependingOnProviderId(UserProfile userProfile, String providerId) {
@@ -124,7 +125,7 @@ private String getLoginDependingOnProviderId(UserProfile userProfile, String pro
case "twitter":
return userProfile.getUsername().toLowerCase();
default:
- return userProfile.getEmail();
+ return userProfile.getFirstName().toLowerCase() + "_" + userProfile.getLastName().toLowerCase();
}
}
diff --git a/src/main/java/org/exampleapps/greatbig/service/UserService.java b/src/main/java/org/exampleapps/greatbig/service/UserService.java
index 099b9757..84b10b6f 100644
--- a/src/main/java/org/exampleapps/greatbig/service/UserService.java
+++ b/src/main/java/org/exampleapps/greatbig/service/UserService.java
@@ -1,5 +1,6 @@
package org.exampleapps.greatbig.service;
+import org.exampleapps.greatbig.config.CacheConfiguration;
import org.exampleapps.greatbig.domain.Authority;
import org.exampleapps.greatbig.domain.User;
import org.exampleapps.greatbig.repository.AuthorityRepository;
@@ -13,6 +14,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.cache.CacheManager;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.annotation.Scheduled;
@@ -44,12 +46,15 @@ public class UserService {
private final AuthorityRepository authorityRepository;
- public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, SocialService socialService, UserSearchRepository userSearchRepository, AuthorityRepository authorityRepository) {
+ private final CacheManager cacheManager;
+
+ public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, SocialService socialService, UserSearchRepository userSearchRepository, AuthorityRepository authorityRepository, CacheManager cacheManager) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.socialService = socialService;
this.userSearchRepository = userSearchRepository;
this.authorityRepository = authorityRepository;
+ this.cacheManager = cacheManager;
}
public Optional activateRegistration(String key) {
@@ -60,6 +65,8 @@ public Optional activateRegistration(String key) {
user.setActivated(true);
user.setActivationKey(null);
userSearchRepository.save(user);
+ cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).evict(user.getLogin());
+ cacheManager.getCache(UserRepository.USERS_BY_EMAIL_CACHE).evict(user.getEmail());
log.debug("Activated user: {}", user);
return user;
});
@@ -74,35 +81,38 @@ public Optional completePasswordReset(String newPassword, String key) {
user.setPassword(passwordEncoder.encode(newPassword));
user.setResetKey(null);
user.setResetDate(null);
+ cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).evict(user.getLogin());
+ cacheManager.getCache(UserRepository.USERS_BY_EMAIL_CACHE).evict(user.getEmail());
return user;
});
}
public Optional requestPasswordReset(String mail) {
- return userRepository.findOneByEmail(mail)
+ return userRepository.findOneByEmailIgnoreCase(mail)
.filter(User::getActivated)
.map(user -> {
user.setResetKey(RandomUtil.generateResetKey());
user.setResetDate(Instant.now());
+ cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).evict(user.getLogin());
+ cacheManager.getCache(UserRepository.USERS_BY_EMAIL_CACHE).evict(user.getEmail());
return user;
});
}
- public User createUser(String login, String password, String firstName, String lastName, String email,
- String imageUrl, String langKey) {
+ public User registerUser(UserDTO userDTO, String password) {
User newUser = new User();
Authority authority = authorityRepository.findOne(AuthoritiesConstants.USER);
Set authorities = new HashSet<>();
String encryptedPassword = passwordEncoder.encode(password);
- newUser.setLogin(login);
+ newUser.setLogin(userDTO.getLogin());
// new user gets initially a generated password
newUser.setPassword(encryptedPassword);
- newUser.setFirstName(firstName);
- newUser.setLastName(lastName);
- newUser.setEmail(email);
- newUser.setImageUrl(imageUrl);
- newUser.setLangKey(langKey);
+ newUser.setFirstName(userDTO.getFirstName());
+ newUser.setLastName(userDTO.getLastName());
+ newUser.setEmail(userDTO.getEmail());
+ newUser.setImageUrl(userDTO.getImageUrl());
+ newUser.setLangKey(userDTO.getLangKey());
// new user is not active
newUser.setActivated(false);
// new user gets registration key
@@ -111,6 +121,8 @@ public User createUser(String login, String password, String firstName, String l
newUser.setAuthorities(authorities);
userRepository.save(newUser);
userSearchRepository.save(newUser);
+ cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).evict(newUser.getLogin());
+ cacheManager.getCache(UserRepository.USERS_BY_EMAIL_CACHE).evict(newUser.getEmail());
log.debug("Created Information for User: {}", newUser);
return newUser;
}
@@ -123,15 +135,14 @@ public User createUser(UserDTO userDTO) {
user.setEmail(userDTO.getEmail());
user.setImageUrl(userDTO.getImageUrl());
if (userDTO.getLangKey() == null) {
- user.setLangKey("en"); // default language
+ user.setLangKey(Constants.DEFAULT_LANGUAGE); // default language
} else {
user.setLangKey(userDTO.getLangKey());
}
if (userDTO.getAuthorities() != null) {
- Set authorities = new HashSet<>();
- userDTO.getAuthorities().forEach(
- authority -> authorities.add(authorityRepository.findOne(authority))
- );
+ Set authorities = userDTO.getAuthorities().stream()
+ .map(authorityRepository::findOne)
+ .collect(Collectors.toSet());
user.setAuthorities(authorities);
}
String encryptedPassword = passwordEncoder.encode(RandomUtil.generatePassword());
@@ -141,6 +152,8 @@ public User createUser(UserDTO userDTO) {
user.setActivated(true);
userRepository.save(user);
userSearchRepository.save(user);
+ cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).evict(user.getLogin());
+ cacheManager.getCache(UserRepository.USERS_BY_EMAIL_CACHE).evict(user.getEmail());
log.debug("Created Information for User: {}", user);
return user;
}
@@ -155,15 +168,19 @@ public User createUser(UserDTO userDTO) {
* @param imageUrl image URL of user
*/
public void updateUser(String firstName, String lastName, String email, String langKey, String imageUrl) {
- userRepository.findOneByLogin(SecurityUtils.getCurrentUserLogin()).ifPresent(user -> {
- user.setFirstName(firstName);
- user.setLastName(lastName);
- user.setEmail(email);
- user.setLangKey(langKey);
- user.setImageUrl(imageUrl);
- userSearchRepository.save(user);
- log.debug("Changed Information for User: {}", user);
- });
+ SecurityUtils.getCurrentUserLogin()
+ .flatMap(userRepository::findOneByLogin)
+ .ifPresent(user -> {
+ user.setFirstName(firstName);
+ user.setLastName(lastName);
+ user.setEmail(email);
+ user.setLangKey(langKey);
+ user.setImageUrl(imageUrl);
+ userSearchRepository.save(user);
+ cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).evict(user.getLogin());
+ cacheManager.getCache(UserRepository.USERS_BY_EMAIL_CACHE).evict(user.getEmail());
+ log.debug("Changed Information for User: {}", user);
+ });
}
/**
@@ -189,6 +206,8 @@ public Optional updateUser(UserDTO userDTO) {
.map(authorityRepository::findOne)
.forEach(managedAuthorities::add);
userSearchRepository.save(user);
+ cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).evict(user.getLogin());
+ cacheManager.getCache(UserRepository.USERS_BY_EMAIL_CACHE).evict(user.getEmail());
log.debug("Changed Information for User: {}", user);
return user;
})
@@ -200,16 +219,22 @@ public void deleteUser(String login) {
socialService.deleteUserSocialConnection(user.getLogin());
userRepository.delete(user);
userSearchRepository.delete(user);
+ cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).evict(user.getLogin());
+ cacheManager.getCache(UserRepository.USERS_BY_EMAIL_CACHE).evict(user.getEmail());
log.debug("Deleted User: {}", user);
});
}
public void changePassword(String password) {
- userRepository.findOneByLogin(SecurityUtils.getCurrentUserLogin()).ifPresent(user -> {
- String encryptedPassword = passwordEncoder.encode(password);
- user.setPassword(encryptedPassword);
- log.debug("Changed password for User: {}", user);
- });
+ SecurityUtils.getCurrentUserLogin()
+ .flatMap(userRepository::findOneByLogin)
+ .ifPresent(user -> {
+ String encryptedPassword = passwordEncoder.encode(password);
+ user.setPassword(encryptedPassword);
+ cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).evict(user.getLogin());
+ cacheManager.getCache(UserRepository.USERS_BY_EMAIL_CACHE).evict(user.getEmail());
+ log.debug("Changed password for User: {}", user);
+ });
}
@Transactional(readOnly = true)
@@ -223,16 +248,15 @@ public Optional getUserWithAuthoritiesByLogin(String login) {
}
@Transactional(readOnly = true)
- public User getUserWithAuthorities(Long id) {
+ public Optional getUserWithAuthorities(Long id) {
return userRepository.findOneWithAuthoritiesById(id);
}
@Transactional(readOnly = true)
- public User getUserWithAuthorities() {
- return userRepository.findOneWithAuthoritiesByLogin(SecurityUtils.getCurrentUserLogin()).orElse(null);
+ public Optional getUserWithAuthorities() {
+ return SecurityUtils.getCurrentUserLogin().flatMap(userRepository::findOneWithAuthoritiesByLogin);
}
-
/**
* Not activated users should be automatically deleted after 3 days.
*
@@ -245,6 +269,8 @@ public void removeNotActivatedUsers() {
log.debug("Deleting not activated user {}", user.getLogin());
userRepository.delete(user);
userSearchRepository.delete(user);
+ cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).evict(user.getLogin());
+ cacheManager.getCache(UserRepository.USERS_BY_EMAIL_CACHE).evict(user.getEmail());
}
}
@@ -280,4 +306,5 @@ public void setCurrentUser(User user) {
*/
public void clearCurrentUser() {
}
+
}
diff --git a/src/main/java/org/exampleapps/greatbig/service/dto/UserDTO.java b/src/main/java/org/exampleapps/greatbig/service/dto/UserDTO.java
index 3a15ab04..82b6b74d 100644
--- a/src/main/java/org/exampleapps/greatbig/service/dto/UserDTO.java
+++ b/src/main/java/org/exampleapps/greatbig/service/dto/UserDTO.java
@@ -40,7 +40,7 @@ public class UserDTO {
private boolean activated = false;
- @Size(min = 2, max = 5)
+ @Size(min = 2, max = 6)
private String langKey;
private String createdBy;
@@ -58,31 +58,21 @@ public UserDTO() {
}
public UserDTO(User user) {
- this(user.getId(), user.getLogin(), user.getFirstName(), user.getLastName(),
- user.getEmail(), user.getActivated(), user.getImageUrl(), user.getLangKey(),
- user.getCreatedBy(), user.getCreatedDate(), user.getLastModifiedBy(), user.getLastModifiedDate(),
- user.getAuthorities().stream().map(Authority::getName)
- .collect(Collectors.toSet()));
- }
-
- public UserDTO(Long id, String login, String firstName, String lastName,
- String email, boolean activated, String imageUrl, String langKey,
- String createdBy, Instant createdDate, String lastModifiedBy, Instant lastModifiedDate,
- Set authorities) {
-
- this.id = id;
- this.login = login;
- this.firstName = firstName;
- this.lastName = lastName;
- this.email = email;
- this.activated = activated;
- this.imageUrl = imageUrl;
- this.langKey = langKey;
- this.createdBy = createdBy;
- this.createdDate = createdDate;
- this.lastModifiedBy = lastModifiedBy;
- this.lastModifiedDate = lastModifiedDate;
- this.authorities = authorities;
+ this.id = user.getId();
+ this.login = user.getLogin();
+ this.firstName = user.getFirstName();
+ this.lastName = user.getLastName();
+ this.email = user.getEmail();
+ this.activated = user.getActivated();
+ this.imageUrl = user.getImageUrl();
+ this.langKey = user.getLangKey();
+ this.createdBy = user.getCreatedBy();
+ this.createdDate = user.getCreatedDate();
+ this.lastModifiedBy = user.getLastModifiedBy();
+ this.lastModifiedDate = user.getLastModifiedDate();
+ this.authorities = user.getAuthorities().stream()
+ .map(Authority::getName)
+ .collect(Collectors.toSet());
}
public Long getId() {
@@ -105,38 +95,74 @@ public String getFirstName() {
return firstName;
}
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
public String getLastName() {
return lastName;
}
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
public String getEmail() {
return email;
}
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
public String getImageUrl() {
return imageUrl;
}
+ public void setImageUrl(String imageUrl) {
+ this.imageUrl = imageUrl;
+ }
+
public boolean isActivated() {
return activated;
}
+ public void setActivated(boolean activated) {
+ this.activated = activated;
+ }
+
public String getLangKey() {
return langKey;
}
+ public void setLangKey(String langKey) {
+ this.langKey = langKey;
+ }
+
public String getCreatedBy() {
return createdBy;
}
+ public void setCreatedBy(String createdBy) {
+ this.createdBy = createdBy;
+ }
+
public Instant getCreatedDate() {
return createdDate;
}
+ public void setCreatedDate(Instant createdDate) {
+ this.createdDate = createdDate;
+ }
+
public String getLastModifiedBy() {
return lastModifiedBy;
}
+ public void setLastModifiedBy(String lastModifiedBy) {
+ this.lastModifiedBy = lastModifiedBy;
+ }
+
public Instant getLastModifiedDate() {
return lastModifiedDate;
}
@@ -149,6 +175,10 @@ public Set getAuthorities() {
return authorities;
}
+ public void setAuthorities(Set authorities) {
+ this.authorities = authorities;
+ }
+
@Override
public String toString() {
return "UserDTO{" +
diff --git a/src/main/java/org/exampleapps/greatbig/service/mapper/UserMapper.java b/src/main/java/org/exampleapps/greatbig/service/mapper/UserMapper.java
index 67488e7b..e931bc06 100644
--- a/src/main/java/org/exampleapps/greatbig/service/mapper/UserMapper.java
+++ b/src/main/java/org/exampleapps/greatbig/service/mapper/UserMapper.java
@@ -43,7 +43,7 @@ public User userDTOToUser(UserDTO userDTO) {
user.setActivated(userDTO.isActivated());
user.setLangKey(userDTO.getLangKey());
Set authorities = this.authoritiesFromStrings(userDTO.getAuthorities());
- if(authorities != null) {
+ if (authorities != null) {
user.setAuthorities(authorities);
}
return user;
diff --git a/src/main/java/org/exampleapps/greatbig/service/util/RandomUtil.java b/src/main/java/org/exampleapps/greatbig/service/util/RandomUtil.java
index 0d5b45d4..10198a84 100644
--- a/src/main/java/org/exampleapps/greatbig/service/util/RandomUtil.java
+++ b/src/main/java/org/exampleapps/greatbig/service/util/RandomUtil.java
@@ -31,10 +31,10 @@ public static String generateActivationKey() {
}
/**
- * Generate a reset key.
- *
- * @return the generated reset key
- */
+ * Generate a reset key.
+ *
+ * @return the generated reset key
+ */
public static String generateResetKey() {
return RandomStringUtils.randomNumeric(DEF_COUNT);
}
diff --git a/src/main/java/org/exampleapps/greatbig/web/rest/AccountResource.java b/src/main/java/org/exampleapps/greatbig/web/rest/AccountResource.java
index 148539ef..c56b95c0 100644
--- a/src/main/java/org/exampleapps/greatbig/web/rest/AccountResource.java
+++ b/src/main/java/org/exampleapps/greatbig/web/rest/AccountResource.java
@@ -8,17 +8,14 @@
import org.exampleapps.greatbig.service.MailService;
import org.exampleapps.greatbig.service.UserService;
import org.exampleapps.greatbig.service.dto.UserDTO;
+import org.exampleapps.greatbig.web.rest.errors.*;
import org.exampleapps.greatbig.web.rest.vm.KeyAndPasswordVM;
import org.exampleapps.greatbig.web.rest.vm.ManagedUserVM;
-import org.exampleapps.greatbig.web.rest.util.HeaderUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
@@ -40,10 +37,7 @@ public class AccountResource {
private final MailService mailService;
- private static final String CHECK_ERROR_MESSAGE = "Incorrect password";
-
- public AccountResource(UserRepository userRepository, UserService userService,
- MailService mailService) {
+ public AccountResource(UserRepository userRepository, UserService userService, MailService mailService) {
this.userRepository = userRepository;
this.userService = userService;
@@ -54,47 +48,36 @@ public AccountResource(UserRepository userRepository, UserService userService,
* POST /register : register the user.
*
* @param managedUserVM the managed user View Model
- * @return the ResponseEntity with status 201 (Created) if the user is registered or 400 (Bad Request) if the login or email is already in use
+ * @throws InvalidPasswordException 400 (Bad Request) if the password is incorrect
+ * @throws EmailAlreadyUsedException 400 (Bad Request) if the email is already used
+ * @throws LoginAlreadyUsedException 400 (Bad Request) if the login is already used
*/
- @PostMapping(path = "/register",
- produces={MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_PLAIN_VALUE})
+ @PostMapping("/register")
@Timed
- public ResponseEntity registerAccount(@Valid @RequestBody ManagedUserVM managedUserVM) {
-
- HttpHeaders textPlainHeaders = new HttpHeaders();
- textPlainHeaders.setContentType(MediaType.TEXT_PLAIN);
+ @ResponseStatus(HttpStatus.CREATED)
+ public void registerAccount(@Valid @RequestBody ManagedUserVM managedUserVM) {
if (!checkPasswordLength(managedUserVM.getPassword())) {
- return new ResponseEntity<>(CHECK_ERROR_MESSAGE, HttpStatus.BAD_REQUEST);
+ throw new InvalidPasswordException();
}
- return userRepository.findOneByLogin(managedUserVM.getLogin().toLowerCase())
- .map(user -> new ResponseEntity<>("login already in use", textPlainHeaders, HttpStatus.BAD_REQUEST))
- .orElseGet(() -> userRepository.findOneByEmail(managedUserVM.getEmail())
- .map(user -> new ResponseEntity<>("email address already in use", textPlainHeaders, HttpStatus.BAD_REQUEST))
- .orElseGet(() -> {
- User user = userService
- .createUser(managedUserVM.getLogin(), managedUserVM.getPassword(),
- managedUserVM.getFirstName(), managedUserVM.getLastName(),
- managedUserVM.getEmail().toLowerCase(), managedUserVM.getImageUrl(),
- managedUserVM.getLangKey());
-
- mailService.sendActivationEmail(user);
- return new ResponseEntity<>(HttpStatus.CREATED);
- })
- );
+ userRepository.findOneByLogin(managedUserVM.getLogin().toLowerCase()).ifPresent(u -> {throw new LoginAlreadyUsedException();});
+ userRepository.findOneByEmailIgnoreCase(managedUserVM.getEmail()).ifPresent(u -> {throw new EmailAlreadyUsedException();});
+ User user = userService.registerUser(managedUserVM, managedUserVM.getPassword());
+ mailService.sendActivationEmail(user);
}
/**
* GET /activate : activate the registered user.
*
* @param key the activation key
- * @return the ResponseEntity with status 200 (OK) and the activated user in body, or status 500 (Internal Server Error) if the user couldn't be activated
+ * @throws RuntimeException 500 (Internal Server Error) if the user couldn't be activated
*/
@GetMapping("/activate")
@Timed
- public ResponseEntity activateAccount(@RequestParam(value = "key") String key) {
- return userService.activateRegistration(key)
- .map(user -> new ResponseEntity(HttpStatus.OK))
- .orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
+ public void activateAccount(@RequestParam(value = "key") String key) {
+ Optional user = userService.activateRegistration(key);
+ if (!user.isPresent()) {
+ throw new InternalServerErrorException("No user was found for this reset key");
+ }
}
/**
@@ -113,94 +96,92 @@ public String isAuthenticated(HttpServletRequest request) {
/**
* GET /account : get the current user.
*
- * @return the ResponseEntity with status 200 (OK) and the current user in body, or status 500 (Internal Server Error) if the user couldn't be returned
+ * @return the current user
+ * @throws RuntimeException 500 (Internal Server Error) if the user couldn't be returned
*/
@GetMapping("/account")
@Timed
- public ResponseEntity getAccount() {
- return Optional.ofNullable(userService.getUserWithAuthorities())
- .map(user -> new ResponseEntity<>(new UserDTO(user), HttpStatus.OK))
- .orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
+ public UserDTO getAccount() {
+ return userService.getUserWithAuthorities()
+ .map(UserDTO::new)
+ .orElseThrow(() -> new InternalServerErrorException("User could not be found"));
}
/**
* POST /account : update the current user information.
*
* @param userDTO the current user information
- * @return the ResponseEntity with status 200 (OK), or status 400 (Bad Request) or 500 (Internal Server Error) if the user couldn't be updated
+ * @throws EmailAlreadyUsedException 400 (Bad Request) if the email is already used
+ * @throws RuntimeException 500 (Internal Server Error) if the user login wasn't found
*/
@PostMapping("/account")
@Timed
- public ResponseEntity saveAccount(@Valid @RequestBody UserDTO userDTO) {
- final String userLogin = SecurityUtils.getCurrentUserLogin();
- Optional existingUser = userRepository.findOneByEmail(userDTO.getEmail());
+ public void saveAccount(@Valid @RequestBody UserDTO userDTO) {
+ final String userLogin = SecurityUtils.getCurrentUserLogin().orElseThrow(() -> new InternalServerErrorException("Current user login not found"));
+ Optional existingUser = userRepository.findOneByEmailIgnoreCase(userDTO.getEmail());
if (existingUser.isPresent() && (!existingUser.get().getLogin().equalsIgnoreCase(userLogin))) {
- return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("user-management", "emailexists", "Email already in use")).body(null);
+ throw new EmailAlreadyUsedException();
}
- return userRepository
- .findOneByLogin(userLogin)
- .map(u -> {
- userService.updateUser(userDTO.getFirstName(), userDTO.getLastName(), userDTO.getEmail(),
- userDTO.getLangKey(), userDTO.getImageUrl());
- return new ResponseEntity(HttpStatus.OK);
- })
- .orElseGet(() -> new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
- }
+ Optional user = userRepository.findOneByLogin(userLogin);
+ if (!user.isPresent()) {
+ throw new InternalServerErrorException("User could not be found");
+ }
+ userService.updateUser(userDTO.getFirstName(), userDTO.getLastName(), userDTO.getEmail(),
+ userDTO.getLangKey(), userDTO.getImageUrl());
+ }
/**
- * POST /account/change_password : changes the current user's password
+ * POST /account/change-password : changes the current user's password
*
* @param password the new password
- * @return the ResponseEntity with status 200 (OK), or status 400 (Bad Request) if the new password is not strong enough
+ * @throws InvalidPasswordException 400 (Bad Request) if the new password is incorrect
*/
- @PostMapping(path = "/account/change_password",
- produces = MediaType.TEXT_PLAIN_VALUE)
+ @PostMapping(path = "/account/change-password")
@Timed
- public ResponseEntity changePassword(@RequestBody String password) {
+ public void changePassword(@RequestBody String password) {
if (!checkPasswordLength(password)) {
- return new ResponseEntity<>(CHECK_ERROR_MESSAGE, HttpStatus.BAD_REQUEST);
+ throw new InvalidPasswordException();
}
userService.changePassword(password);
- return new ResponseEntity<>(HttpStatus.OK);
- }
+ }
/**
- * POST /account/reset_password/init : Send an email to reset the password of the user
+ * POST /account/reset-password/init : Send an email to reset the password of the user
*
* @param mail the mail of the user
- * @return the ResponseEntity with status 200 (OK) if the email was sent, or status 400 (Bad Request) if the email address is not registered
+ * @throws EmailNotFoundException 400 (Bad Request) if the email address is not registered
*/
- @PostMapping(path = "/account/reset_password/init",
- produces = MediaType.TEXT_PLAIN_VALUE)
+ @PostMapping(path = "/account/reset-password/init")
@Timed
- public ResponseEntity requestPasswordReset(@RequestBody String mail) {
- return userService.requestPasswordReset(mail)
- .map(user -> {
- mailService.sendPasswordResetMail(user);
- return new ResponseEntity<>("email was sent", HttpStatus.OK);
- }).orElse(new ResponseEntity<>("email address not registered", HttpStatus.BAD_REQUEST));
+ public void requestPasswordReset(@RequestBody String mail) {
+ mailService.sendPasswordResetMail(
+ userService.requestPasswordReset(mail)
+ .orElseThrow(EmailNotFoundException::new)
+ );
}
/**
- * POST /account/reset_password/finish : Finish to reset the password of the user
+ * POST /account/reset-password/finish : Finish to reset the password of the user
*
* @param keyAndPassword the generated key and the new password
- * @return the ResponseEntity with status 200 (OK) if the password has been reset,
- * or status 400 (Bad Request) or 500 (Internal Server Error) if the password could not be reset
+ * @throws InvalidPasswordException 400 (Bad Request) if the password is incorrect
+ * @throws RuntimeException 500 (Internal Server Error) if the password could not be reset
*/
- @PostMapping(path = "/account/reset_password/finish",
- produces = MediaType.TEXT_PLAIN_VALUE)
+ @PostMapping(path = "/account/reset-password/finish")
@Timed
- public ResponseEntity finishPasswordReset(@RequestBody KeyAndPasswordVM keyAndPassword) {
+ public void finishPasswordReset(@RequestBody KeyAndPasswordVM keyAndPassword) {
if (!checkPasswordLength(keyAndPassword.getNewPassword())) {
- return new ResponseEntity<>(CHECK_ERROR_MESSAGE, HttpStatus.BAD_REQUEST);
+ throw new InvalidPasswordException();
+ }
+ Optional user =
+ userService.completePasswordReset(keyAndPassword.getNewPassword(), keyAndPassword.getKey());
+
+ if (!user.isPresent()) {
+ throw new InternalServerErrorException("No user was found for this reset key");
}
- return userService.completePasswordReset(keyAndPassword.getNewPassword(), keyAndPassword.getKey())
- .map(user -> new ResponseEntity(HttpStatus.OK))
- .orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
}
- private boolean checkPasswordLength(String password) {
+ private static boolean checkPasswordLength(String password) {
return !StringUtils.isEmpty(password) &&
password.length() >= ManagedUserVM.PASSWORD_MIN_LENGTH &&
password.length() <= ManagedUserVM.PASSWORD_MAX_LENGTH;
diff --git a/src/main/java/org/exampleapps/greatbig/web/rest/AuditResource.java b/src/main/java/org/exampleapps/greatbig/web/rest/AuditResource.java
index d322f035..465dcfc8 100644
--- a/src/main/java/org/exampleapps/greatbig/web/rest/AuditResource.java
+++ b/src/main/java/org/exampleapps/greatbig/web/rest/AuditResource.java
@@ -4,7 +4,6 @@
import org.exampleapps.greatbig.web.rest.util.PaginationUtil;
import io.github.jhipster.web.util.ResponseUtil;
-import io.swagger.annotations.ApiParam;
import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -31,13 +30,13 @@ public AuditResource(AuditEventService auditEventService) {
}
/**
- * GET /audits : get a page of AuditEvents.
+ * GET /audits : get a page of AuditEvents.
*
* @param pageable the pagination information
* @return the ResponseEntity with status 200 (OK) and the list of AuditEvents in body
*/
@GetMapping
- public ResponseEntity> getAll(@ApiParam Pageable pageable) {
+ public ResponseEntity> getAll(Pageable pageable) {
Page page = auditEventService.findAll(pageable);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/management/audits");
return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
@@ -55,7 +54,7 @@ public ResponseEntity> getAll(@ApiParam Pageable pageable) {
public ResponseEntity> getByDates(
@RequestParam(value = "fromDate") LocalDate fromDate,
@RequestParam(value = "toDate") LocalDate toDate,
- @ApiParam Pageable pageable) {
+ Pageable pageable) {
Page page = auditEventService.findByDates(
fromDate.atStartOfDay(ZoneId.systemDefault()).toInstant(),
diff --git a/src/main/java/org/exampleapps/greatbig/web/rest/SocialController.java b/src/main/java/org/exampleapps/greatbig/web/rest/SocialController.java
index 5a2265c8..fa29eb78 100644
--- a/src/main/java/org/exampleapps/greatbig/web/rest/SocialController.java
+++ b/src/main/java/org/exampleapps/greatbig/web/rest/SocialController.java
@@ -1,5 +1,6 @@
package org.exampleapps.greatbig.web.rest;
+import org.exampleapps.greatbig.config.Constants;
import org.exampleapps.greatbig.service.SocialService;
import org.slf4j.Logger;
@@ -28,7 +29,7 @@ public SocialController(SocialService socialService, ProviderSignInUtils provide
}
@GetMapping("/signup")
- public RedirectView signUp(WebRequest webRequest, @CookieValue(name = "NG_TRANSLATE_LANG_KEY", required = false, defaultValue = "\"en\"") String langKey) {
+ public RedirectView signUp(WebRequest webRequest, @CookieValue(name = "NG_TRANSLATE_LANG_KEY", required = false, defaultValue = Constants.DEFAULT_LANGUAGE) String langKey) {
try {
Connection> connection = providerSignInUtils.getConnectionFromSession(webRequest);
socialService.createSocialUser(connection, langKey.replace("\"", ""));
diff --git a/src/main/java/org/exampleapps/greatbig/web/rest/UserJWTController.java b/src/main/java/org/exampleapps/greatbig/web/rest/UserJWTController.java
index 1ce117be..825703fa 100644
--- a/src/main/java/org/exampleapps/greatbig/web/rest/UserJWTController.java
+++ b/src/main/java/org/exampleapps/greatbig/web/rest/UserJWTController.java
@@ -7,20 +7,16 @@
import com.codahale.metrics.annotation.Timed;
import com.fasterxml.jackson.annotation.JsonProperty;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
+import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
-import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
-import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
-import java.util.Collections;
/**
* Controller to authenticate users.
@@ -29,8 +25,6 @@
@RequestMapping("/api")
public class UserJWTController {
- private final Logger log = LoggerFactory.getLogger(UserJWTController.class);
-
private final TokenProvider tokenProvider;
private final AuthenticationManager authenticationManager;
@@ -42,23 +36,18 @@ public UserJWTController(TokenProvider tokenProvider, AuthenticationManager auth
@PostMapping("/authenticate")
@Timed
- public ResponseEntity authorize(@Valid @RequestBody LoginVM loginVM, HttpServletResponse response) {
+ public ResponseEntity authorize(@Valid @RequestBody LoginVM loginVM) {
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(loginVM.getUsername(), loginVM.getPassword());
- try {
- Authentication authentication = this.authenticationManager.authenticate(authenticationToken);
- SecurityContextHolder.getContext().setAuthentication(authentication);
- boolean rememberMe = (loginVM.isRememberMe() == null) ? false : loginVM.isRememberMe();
- String jwt = tokenProvider.createToken(authentication, rememberMe);
- response.addHeader(JWTConfigurer.AUTHORIZATION_HEADER, "Bearer " + jwt);
- return ResponseEntity.ok(new JWTToken(jwt));
- } catch (AuthenticationException ae) {
- log.trace("Authentication exception trace: {}", ae);
- return new ResponseEntity<>(Collections.singletonMap("AuthenticationException",
- ae.getLocalizedMessage()), HttpStatus.UNAUTHORIZED);
- }
+ Authentication authentication = this.authenticationManager.authenticate(authenticationToken);
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ boolean rememberMe = (loginVM.isRememberMe() == null) ? false : loginVM.isRememberMe();
+ String jwt = tokenProvider.createToken(authentication, rememberMe);
+ HttpHeaders httpHeaders = new HttpHeaders();
+ httpHeaders.add(JWTConfigurer.AUTHORIZATION_HEADER, "Bearer " + jwt);
+ return new ResponseEntity<>(new JWTToken(jwt), httpHeaders, HttpStatus.OK);
}
/**
diff --git a/src/main/java/org/exampleapps/greatbig/web/rest/UserResource.java b/src/main/java/org/exampleapps/greatbig/web/rest/UserResource.java
index 6df637b8..559060b1 100644
--- a/src/main/java/org/exampleapps/greatbig/web/rest/UserResource.java
+++ b/src/main/java/org/exampleapps/greatbig/web/rest/UserResource.java
@@ -9,11 +9,12 @@
import org.exampleapps.greatbig.service.MailService;
import org.exampleapps.greatbig.service.UserService;
import org.exampleapps.greatbig.service.dto.UserDTO;
-import org.exampleapps.greatbig.web.rest.vm.ManagedUserVM;
+import org.exampleapps.greatbig.web.rest.errors.BadRequestAlertException;
+import org.exampleapps.greatbig.web.rest.errors.EmailAlreadyUsedException;
+import org.exampleapps.greatbig.web.rest.errors.LoginAlreadyUsedException;
import org.exampleapps.greatbig.web.rest.util.HeaderUtil;
import org.exampleapps.greatbig.web.rest.util.PaginationUtil;
import io.github.jhipster.web.util.ResponseUtil;
-import io.swagger.annotations.ApiParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -64,22 +65,19 @@ public class UserResource {
private final Logger log = LoggerFactory.getLogger(UserResource.class);
- private static final String ENTITY_NAME = "userManagement";
-
private final UserRepository userRepository;
- private final MailService mailService;
-
private final UserService userService;
+ private final MailService mailService;
+
private final UserSearchRepository userSearchRepository;
- public UserResource(UserRepository userRepository, MailService mailService,
- UserService userService, UserSearchRepository userSearchRepository) {
+ public UserResource(UserRepository userRepository, UserService userService, MailService mailService, UserSearchRepository userSearchRepository) {
this.userRepository = userRepository;
- this.mailService = mailService;
this.userService = userService;
+ this.mailService = mailService;
this.userSearchRepository = userSearchRepository;
}
@@ -90,31 +88,26 @@ public UserResource(UserRepository userRepository, MailService mailService,
* mail with an activation link.
* The user needs to be activated on creation.
*
- * @param managedUserVM the user to create
+ * @param userDTO the user to create
* @return the ResponseEntity with status 201 (Created) and with body the new user, or with status 400 (Bad Request) if the login or email is already in use
* @throws URISyntaxException if the Location URI syntax is incorrect
+ * @throws BadRequestAlertException 400 (Bad Request) if the login or email is already in use
*/
@PostMapping("/users")
@Timed
@Secured(AuthoritiesConstants.ADMIN)
- public ResponseEntity createUser(@Valid @RequestBody ManagedUserVM managedUserVM) throws URISyntaxException {
- log.debug("REST request to save User : {}", managedUserVM);
-
- if (managedUserVM.getId() != null) {
- return ResponseEntity.badRequest()
- .headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "idexists", "A new user cannot already have an ID"))
- .body(null);
- // Lowercase the user login before comparing with database
- } else if (userRepository.findOneByLogin(managedUserVM.getLogin().toLowerCase()).isPresent()) {
- return ResponseEntity.badRequest()
- .headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "userexists", "Login already in use"))
- .body(null);
- } else if (userRepository.findOneByEmail(managedUserVM.getEmail()).isPresent()) {
- return ResponseEntity.badRequest()
- .headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "emailexists", "Email already in use"))
- .body(null);
+ public ResponseEntity createUser(@Valid @RequestBody UserDTO userDTO) throws URISyntaxException {
+ log.debug("REST request to save User : {}", userDTO);
+
+ if (userDTO.getId() != null) {
+ throw new BadRequestAlertException("A new user cannot already have an ID", "userManagement", "idexists");
+ // Lowercase the user login before comparing with database
+ } else if (userRepository.findOneByLogin(userDTO.getLogin().toLowerCase()).isPresent()) {
+ throw new LoginAlreadyUsedException();
+ } else if (userRepository.findOneByEmailIgnoreCase(userDTO.getEmail()).isPresent()) {
+ throw new EmailAlreadyUsedException();
} else {
- User newUser = userService.createUser(managedUserVM);
+ User newUser = userService.createUser(userDTO);
mailService.sendCreationEmail(newUser);
return ResponseEntity.created(new URI("/api/users/" + newUser.getLogin()))
.headers(HeaderUtil.createAlert( "userManagement.created", newUser.getLogin()))
@@ -123,41 +116,41 @@ public ResponseEntity createUser(@Valid @RequestBody ManagedUserVM managedUserVM
}
/**
- * PUT /users : Updates an existing User.
+ * PUT /users : Updates an existing User.
*
- * @param managedUserVM the user to update
- * @return the ResponseEntity with status 200 (OK) and with body the updated user,
- * or with status 400 (Bad Request) if the login or email is already in use,
- * or with status 500 (Internal Server Error) if the user couldn't be updated
+ * @param userDTO the user to update
+ * @return the ResponseEntity with status 200 (OK) and with body the updated user
+ * @throws EmailAlreadyUsedException 400 (Bad Request) if the email is already in use
+ * @throws LoginAlreadyUsedException 400 (Bad Request) if the login is already in use
*/
@PutMapping("/users")
@Timed
@Secured(AuthoritiesConstants.ADMIN)
- public ResponseEntity updateUser(@Valid @RequestBody ManagedUserVM managedUserVM) {
- log.debug("REST request to update User : {}", managedUserVM);
- Optional existingUser = userRepository.findOneByEmail(managedUserVM.getEmail());
- if (existingUser.isPresent() && (!existingUser.get().getId().equals(managedUserVM.getId()))) {
- return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "emailexists", "Email already in use")).body(null);
+ public ResponseEntity updateUser(@Valid @RequestBody UserDTO userDTO) {
+ log.debug("REST request to update User : {}", userDTO);
+ Optional existingUser = userRepository.findOneByEmailIgnoreCase(userDTO.getEmail());
+ if (existingUser.isPresent() && (!existingUser.get().getId().equals(userDTO.getId()))) {
+ throw new EmailAlreadyUsedException();
}
- existingUser = userRepository.findOneByLogin(managedUserVM.getLogin().toLowerCase());
- if (existingUser.isPresent() && (!existingUser.get().getId().equals(managedUserVM.getId()))) {
- return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "userexists", "Login already in use")).body(null);
+ existingUser = userRepository.findOneByLogin(userDTO.getLogin().toLowerCase());
+ if (existingUser.isPresent() && (!existingUser.get().getId().equals(userDTO.getId()))) {
+ throw new LoginAlreadyUsedException();
}
- Optional updatedUser = userService.updateUser(managedUserVM);
+ Optional updatedUser = userService.updateUser(userDTO);
return ResponseUtil.wrapOrNotFound(updatedUser,
- HeaderUtil.createAlert("userManagement.updated", managedUserVM.getLogin()));
+ HeaderUtil.createAlert("userManagement.updated", userDTO.getLogin()));
}
/**
- * GET /users : get all users.
+ * GET /users : get all users.
*
* @param pageable the pagination information
* @return the ResponseEntity with status 200 (OK) and with body all users
*/
@GetMapping("/users")
@Timed
- public ResponseEntity> getAllUsers(@ApiParam Pageable pageable) {
+ public ResponseEntity> getAllUsers(Pageable pageable) {
final Page page = userService.getAllManagedUsers(pageable);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/users");
return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
@@ -174,7 +167,7 @@ public List getAuthorities() {
}
/**
- * GET /users/:login : get the "login" user.
+ * GET /users/:login : get the "login" user.
*
* @param login the login of the user to find
* @return the ResponseEntity with status 200 (OK) and with body the "login" user, or with status 404 (Not Found)
@@ -204,7 +197,7 @@ public ResponseEntity deleteUser(@PathVariable String login) {
}
/**
- * SEARCH /_search/users/:query : search for the User corresponding
+ * SEARCH /_search/users/:query : search for the User corresponding
* to the query.
*
* @param query the query to search
diff --git a/src/main/java/org/exampleapps/greatbig/web/rest/errors/BadRequestAlertException.java b/src/main/java/org/exampleapps/greatbig/web/rest/errors/BadRequestAlertException.java
new file mode 100644
index 00000000..649b8be3
--- /dev/null
+++ b/src/main/java/org/exampleapps/greatbig/web/rest/errors/BadRequestAlertException.java
@@ -0,0 +1,40 @@
+package org.exampleapps.greatbig.web.rest.errors;
+
+import org.zalando.problem.AbstractThrowableProblem;
+import org.zalando.problem.Status;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+
+public class BadRequestAlertException extends AbstractThrowableProblem {
+
+ private final String entityName;
+
+ private final String errorKey;
+
+ public BadRequestAlertException(String defaultMessage, String entityName, String errorKey) {
+ this(ErrorConstants.DEFAULT_TYPE, defaultMessage, entityName, errorKey);
+ }
+
+ public BadRequestAlertException(URI type, String defaultMessage, String entityName, String errorKey) {
+ super(type, defaultMessage, Status.BAD_REQUEST, null, null, null, getAlertParameters(entityName, errorKey));
+ this.entityName = entityName;
+ this.errorKey = errorKey;
+ }
+
+ public String getEntityName() {
+ return entityName;
+ }
+
+ public String getErrorKey() {
+ return errorKey;
+ }
+
+ private static Map getAlertParameters(String entityName, String errorKey) {
+ Map parameters = new HashMap<>();
+ parameters.put("message", "error." + errorKey);
+ parameters.put("params", entityName);
+ return parameters;
+ }
+}
diff --git a/src/main/java/org/exampleapps/greatbig/web/rest/errors/CustomParameterizedException.java b/src/main/java/org/exampleapps/greatbig/web/rest/errors/CustomParameterizedException.java
index 6742125d..4945a4c2 100644
--- a/src/main/java/org/exampleapps/greatbig/web/rest/errors/CustomParameterizedException.java
+++ b/src/main/java/org/exampleapps/greatbig/web/rest/errors/CustomParameterizedException.java
@@ -1,8 +1,12 @@
package org.exampleapps.greatbig.web.rest.errors;
+import org.zalando.problem.AbstractThrowableProblem;
+
import java.util.HashMap;
import java.util.Map;
+import static org.zalando.problem.Status.BAD_REQUEST;
+
/**
* Custom, parameterized exception, which can be translated on the client side.
* For example:
@@ -17,33 +21,34 @@
* "error.myCustomError" : "The server says {{param0}} to {{param1}}"
*
*/
-public class CustomParameterizedException extends RuntimeException {
+public class CustomParameterizedException extends AbstractThrowableProblem {
private static final long serialVersionUID = 1L;
private static final String PARAM = "param";
- private final String message;
+ public CustomParameterizedException(String message, String... params) {
+ this(message, toParamMap(params));
+ }
- private final Map paramMap = new HashMap<>();
+ public CustomParameterizedException(String message, Map paramMap) {
+ super(ErrorConstants.PARAMETERIZED_TYPE, "Parameterized Exception", BAD_REQUEST, null, null, null, toProblemParameters(message, paramMap));
+ }
- public CustomParameterizedException(String message, String... params) {
- super(message);
- this.message = message;
+ public static Map toParamMap(String... params) {
+ Map paramMap = new HashMap<>();
if (params != null && params.length > 0) {
for (int i = 0; i < params.length; i++) {
paramMap.put(PARAM + i, params[i]);
}
}
+ return paramMap;
}
- public CustomParameterizedException(String message, Map paramMap) {
- super(message);
- this.message = message;
- this.paramMap.putAll(paramMap);
- }
-
- public ParameterizedErrorVM getErrorVM() {
- return new ParameterizedErrorVM(message, paramMap);
+ public static Map toProblemParameters(String message, Map paramMap) {
+ Map parameters = new HashMap<>();
+ parameters.put("message", message);
+ parameters.put("params", paramMap);
+ return parameters;
}
}
diff --git a/src/main/java/org/exampleapps/greatbig/web/rest/errors/EmailAlreadyUsedException.java b/src/main/java/org/exampleapps/greatbig/web/rest/errors/EmailAlreadyUsedException.java
new file mode 100644
index 00000000..8f20e72a
--- /dev/null
+++ b/src/main/java/org/exampleapps/greatbig/web/rest/errors/EmailAlreadyUsedException.java
@@ -0,0 +1,8 @@
+package org.exampleapps.greatbig.web.rest.errors;
+
+public class EmailAlreadyUsedException extends BadRequestAlertException {
+
+ public EmailAlreadyUsedException() {
+ super(ErrorConstants.EMAIL_ALREADY_USED_TYPE, "Email address already in use", "userManagement", "emailexists");
+ }
+}
diff --git a/src/main/java/org/exampleapps/greatbig/web/rest/errors/EmailNotFoundException.java b/src/main/java/org/exampleapps/greatbig/web/rest/errors/EmailNotFoundException.java
new file mode 100644
index 00000000..fe127e7d
--- /dev/null
+++ b/src/main/java/org/exampleapps/greatbig/web/rest/errors/EmailNotFoundException.java
@@ -0,0 +1,11 @@
+package org.exampleapps.greatbig.web.rest.errors;
+
+import org.zalando.problem.AbstractThrowableProblem;
+import org.zalando.problem.Status;
+
+public class EmailNotFoundException extends AbstractThrowableProblem {
+
+ public EmailNotFoundException() {
+ super(ErrorConstants.EMAIL_NOT_FOUND_TYPE, "Email address not registered", Status.BAD_REQUEST);
+ }
+}
diff --git a/src/main/java/org/exampleapps/greatbig/web/rest/errors/ErrorConstants.java b/src/main/java/org/exampleapps/greatbig/web/rest/errors/ErrorConstants.java
index e4c379d6..1e991d54 100644
--- a/src/main/java/org/exampleapps/greatbig/web/rest/errors/ErrorConstants.java
+++ b/src/main/java/org/exampleapps/greatbig/web/rest/errors/ErrorConstants.java
@@ -1,14 +1,20 @@
package org.exampleapps.greatbig.web.rest.errors;
+import java.net.URI;
+
public final class ErrorConstants {
public static final String ERR_CONCURRENCY_FAILURE = "error.concurrencyFailure";
- public static final String ERR_ACCESS_DENIED = "error.accessDenied";
public static final String ERR_VALIDATION = "error.validation";
- public static final String ERR_METHOD_NOT_SUPPORTED = "error.methodNotSupported";
- public static final String ERR_INTERNAL_SERVER_ERROR = "error.internalServerError";
+ public static final String PROBLEM_BASE_URL = "http://www.jhipster.tech/problem";
+ public static final URI DEFAULT_TYPE = URI.create(PROBLEM_BASE_URL + "/problem-with-message");
+ public static final URI CONSTRAINT_VIOLATION_TYPE = URI.create(PROBLEM_BASE_URL + "/constraint-violation");
+ public static final URI PARAMETERIZED_TYPE = URI.create(PROBLEM_BASE_URL + "/parameterized");
+ public static final URI INVALID_PASSWORD_TYPE = URI.create(PROBLEM_BASE_URL + "/invalid-password");
+ public static final URI EMAIL_ALREADY_USED_TYPE = URI.create(PROBLEM_BASE_URL + "/email-already-used");
+ public static final URI LOGIN_ALREADY_USED_TYPE = URI.create(PROBLEM_BASE_URL + "/login-already-used");
+ public static final URI EMAIL_NOT_FOUND_TYPE = URI.create(PROBLEM_BASE_URL + "/email-not-found");
private ErrorConstants() {
}
-
}
diff --git a/src/main/java/org/exampleapps/greatbig/web/rest/errors/ExceptionTranslator.java b/src/main/java/org/exampleapps/greatbig/web/rest/errors/ExceptionTranslator.java
index 3dd1f02d..e0291ebe 100644
--- a/src/main/java/org/exampleapps/greatbig/web/rest/errors/ExceptionTranslator.java
+++ b/src/main/java/org/exampleapps/greatbig/web/rest/errors/ExceptionTranslator.java
@@ -1,87 +1,98 @@
package org.exampleapps.greatbig.web.rest.errors;
-import java.util.List;
+import org.exampleapps.greatbig.web.rest.util.HeaderUtil;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.dao.ConcurrencyFailureException;
-import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
-import org.springframework.http.ResponseEntity.BodyBuilder;
-import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindingResult;
-import org.springframework.validation.FieldError;
-import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.zalando.problem.DefaultProblem;
+import org.zalando.problem.Problem;
+import org.zalando.problem.ProblemBuilder;
+import org.zalando.problem.Status;
+import org.zalando.problem.spring.web.advice.ProblemHandling;
+import org.zalando.problem.spring.web.advice.validation.ConstraintViolationProblem;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.stream.Collectors;
/**
* Controller advice to translate the server side exceptions to client-friendly json structures.
+ * The error response follows RFC7807 - Problem Details for HTTP APIs (https://tools.ietf.org/html/rfc7807)
*/
@ControllerAdvice
-public class ExceptionTranslator {
+public class ExceptionTranslator implements ProblemHandling {
- private final Logger log = LoggerFactory.getLogger(ExceptionTranslator.class);
-
- @ExceptionHandler(ConcurrencyFailureException.class)
- @ResponseStatus(HttpStatus.CONFLICT)
- @ResponseBody
- public ErrorVM processConcurrencyError(ConcurrencyFailureException ex) {
- return new ErrorVM(ErrorConstants.ERR_CONCURRENCY_FAILURE);
- }
+ /**
+ * Post-process Problem payload to add the message key for front-end if needed
+ */
+ @Override
+ public ResponseEntity process(@Nullable ResponseEntity entity, NativeWebRequest request) {
+ if (entity == null || entity.getBody() == null) {
+ return entity;
+ }
+ Problem problem = entity.getBody();
+ if (!(problem instanceof ConstraintViolationProblem || problem instanceof DefaultProblem)) {
+ return entity;
+ }
+ ProblemBuilder builder = Problem.builder()
+ .withType(Problem.DEFAULT_TYPE.equals(problem.getType()) ? ErrorConstants.DEFAULT_TYPE : problem.getType())
+ .withStatus(problem.getStatus())
+ .withTitle(problem.getTitle())
+ .with("path", request.getNativeRequest(HttpServletRequest.class).getRequestURI());
- @ExceptionHandler(MethodArgumentNotValidException.class)
- @ResponseStatus(HttpStatus.BAD_REQUEST)
- @ResponseBody
- public ErrorVM processValidationError(MethodArgumentNotValidException ex) {
- BindingResult result = ex.getBindingResult();
- List fieldErrors = result.getFieldErrors();
- ErrorVM dto = new ErrorVM(ErrorConstants.ERR_VALIDATION);
- for (FieldError fieldError : fieldErrors) {
- dto.add(fieldError.getObjectName(), fieldError.getField(), fieldError.getCode());
+ if (problem instanceof ConstraintViolationProblem) {
+ builder
+ .with("violations", ((ConstraintViolationProblem) problem).getViolations())
+ .with("message", ErrorConstants.ERR_VALIDATION);
+ return new ResponseEntity<>(builder.build(), entity.getHeaders(), entity.getStatusCode());
+ } else {
+ builder
+ .withCause(((DefaultProblem) problem).getCause())
+ .withDetail(problem.getDetail())
+ .withInstance(problem.getInstance());
+ problem.getParameters().forEach(builder::with);
+ if (!problem.getParameters().containsKey("message") && problem.getStatus() != null) {
+ builder.with("message", "error.http." + problem.getStatus().getStatusCode());
+ }
+ return new ResponseEntity<>(builder.build(), entity.getHeaders(), entity.getStatusCode());
}
- return dto;
}
- @ExceptionHandler(CustomParameterizedException.class)
- @ResponseStatus(HttpStatus.BAD_REQUEST)
- @ResponseBody
- public ParameterizedErrorVM processParameterizedValidationError(CustomParameterizedException ex) {
- return ex.getErrorVM();
- }
+ @Override
+ public ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, @Nonnull NativeWebRequest request) {
+ BindingResult result = ex.getBindingResult();
+ List fieldErrors = result.getFieldErrors().stream()
+ .map(f -> new FieldErrorVM(f.getObjectName(), f.getField(), f.getCode()))
+ .collect(Collectors.toList());
- @ExceptionHandler(AccessDeniedException.class)
- @ResponseStatus(HttpStatus.FORBIDDEN)
- @ResponseBody
- public ErrorVM processAccessDeniedException(AccessDeniedException e) {
- return new ErrorVM(ErrorConstants.ERR_ACCESS_DENIED, e.getMessage());
+ Problem problem = Problem.builder()
+ .withType(ErrorConstants.CONSTRAINT_VIOLATION_TYPE)
+ .withTitle("Method argument not valid")
+ .withStatus(defaultConstraintViolationStatus())
+ .with("message", ErrorConstants.ERR_VALIDATION)
+ .with("fieldErrors", fieldErrors)
+ .build();
+ return create(ex, problem, request);
}
- @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
- @ResponseBody
- @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
- public ErrorVM processMethodNotSupportedException(HttpRequestMethodNotSupportedException exception) {
- return new ErrorVM(ErrorConstants.ERR_METHOD_NOT_SUPPORTED, exception.getMessage());
+ @ExceptionHandler(BadRequestAlertException.class)
+ public ResponseEntity handleBadRequestAlertException(BadRequestAlertException ex, NativeWebRequest request) {
+ return create(ex, request, HeaderUtil.createFailureAlert(ex.getEntityName(), ex.getErrorKey(), ex.getMessage()));
}
- @ExceptionHandler(Exception.class)
- public ResponseEntity processException(Exception ex) {
- if (log.isDebugEnabled()) {
- log.debug("An unexpected error occurred: {}", ex.getMessage(), ex);
- } else {
- log.error("An unexpected error occurred: {}", ex.getMessage());
- }
- BodyBuilder builder;
- ErrorVM errorVM;
- ResponseStatus responseStatus = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);
- if (responseStatus != null) {
- builder = ResponseEntity.status(responseStatus.value());
- errorVM = new ErrorVM("error." + responseStatus.value().value(), responseStatus.reason());
- } else {
- builder = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
- errorVM = new ErrorVM(ErrorConstants.ERR_INTERNAL_SERVER_ERROR, "Internal server error");
- }
- return builder.body(errorVM);
+ @ExceptionHandler(ConcurrencyFailureException.class)
+ public ResponseEntity handleConcurrencyFailure(ConcurrencyFailureException ex, NativeWebRequest request) {
+ Problem problem = Problem.builder()
+ .withStatus(Status.CONFLICT)
+ .with("message", ErrorConstants.ERR_CONCURRENCY_FAILURE)
+ .build();
+ return create(ex, problem, request);
}
}
diff --git a/src/main/java/org/exampleapps/greatbig/web/rest/errors/InternalServerErrorException.java b/src/main/java/org/exampleapps/greatbig/web/rest/errors/InternalServerErrorException.java
new file mode 100644
index 00000000..9f0af529
--- /dev/null
+++ b/src/main/java/org/exampleapps/greatbig/web/rest/errors/InternalServerErrorException.java
@@ -0,0 +1,14 @@
+package org.exampleapps.greatbig.web.rest.errors;
+
+import org.zalando.problem.AbstractThrowableProblem;
+import org.zalando.problem.Status;
+
+/**
+ * Simple exception with a message, that returns an Internal Server Error code.
+ */
+public class InternalServerErrorException extends AbstractThrowableProblem {
+
+ public InternalServerErrorException(String message) {
+ super(ErrorConstants.DEFAULT_TYPE, message, Status.INTERNAL_SERVER_ERROR);
+ }
+}
diff --git a/src/main/java/org/exampleapps/greatbig/web/rest/errors/InvalidPasswordException.java b/src/main/java/org/exampleapps/greatbig/web/rest/errors/InvalidPasswordException.java
new file mode 100644
index 00000000..c9068398
--- /dev/null
+++ b/src/main/java/org/exampleapps/greatbig/web/rest/errors/InvalidPasswordException.java
@@ -0,0 +1,11 @@
+package org.exampleapps.greatbig.web.rest.errors;
+
+import org.zalando.problem.AbstractThrowableProblem;
+import org.zalando.problem.Status;
+
+public class InvalidPasswordException extends AbstractThrowableProblem {
+
+ public InvalidPasswordException() {
+ super(ErrorConstants.INVALID_PASSWORD_TYPE, "Incorrect password", Status.BAD_REQUEST);
+ }
+}
diff --git a/src/main/java/org/exampleapps/greatbig/web/rest/errors/LoginAlreadyUsedException.java b/src/main/java/org/exampleapps/greatbig/web/rest/errors/LoginAlreadyUsedException.java
new file mode 100644
index 00000000..e70e23df
--- /dev/null
+++ b/src/main/java/org/exampleapps/greatbig/web/rest/errors/LoginAlreadyUsedException.java
@@ -0,0 +1,8 @@
+package org.exampleapps.greatbig.web.rest.errors;
+
+public class LoginAlreadyUsedException extends BadRequestAlertException {
+
+ public LoginAlreadyUsedException() {
+ super(ErrorConstants.LOGIN_ALREADY_USED_TYPE, "Login already in use", "userManagement", "userexists");
+ }
+}
diff --git a/src/main/java/org/exampleapps/greatbig/web/rest/errors/package-info.java b/src/main/java/org/exampleapps/greatbig/web/rest/errors/package-info.java
new file mode 100644
index 00000000..82b902b2
--- /dev/null
+++ b/src/main/java/org/exampleapps/greatbig/web/rest/errors/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * Specific errors used with Zalando's "problem-spring-web" library.
+ *
+ * More information on https://github.com/zalando/problem-spring-web
+ */
+package org.exampleapps.greatbig.web.rest.errors;
diff --git a/src/main/java/org/exampleapps/greatbig/web/rest/util/HeaderUtil.java b/src/main/java/org/exampleapps/greatbig/web/rest/util/HeaderUtil.java
index dec7a217..26cb828c 100644
--- a/src/main/java/org/exampleapps/greatbig/web/rest/util/HeaderUtil.java
+++ b/src/main/java/org/exampleapps/greatbig/web/rest/util/HeaderUtil.java
@@ -3,6 +3,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
+
/**
* Utility class for HTTP headers creation.
*/
diff --git a/src/main/java/org/exampleapps/greatbig/web/rest/util/PaginationUtil.java b/src/main/java/org/exampleapps/greatbig/web/rest/util/PaginationUtil.java
index 45bf3cba..b0158209 100644
--- a/src/main/java/org/exampleapps/greatbig/web/rest/util/PaginationUtil.java
+++ b/src/main/java/org/exampleapps/greatbig/web/rest/util/PaginationUtil.java
@@ -11,7 +11,7 @@
* Utility class for handling pagination.
*
*
- * Pagination uses the same principles as the Github API,
+ * Pagination uses the same principles as the GitHub API,
* and follow RFC 5988 (Link header).
*/
public final class PaginationUtil {
diff --git a/src/main/java/org/exampleapps/greatbig/web/rest/vm/LoginVM.java b/src/main/java/org/exampleapps/greatbig/web/rest/vm/LoginVM.java
index f6c89031..cd15a954 100644
--- a/src/main/java/org/exampleapps/greatbig/web/rest/vm/LoginVM.java
+++ b/src/main/java/org/exampleapps/greatbig/web/rest/vm/LoginVM.java
@@ -1,8 +1,6 @@
package org.exampleapps.greatbig.web.rest.vm;
-import org.exampleapps.greatbig.config.Constants;
import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
/**
@@ -10,7 +8,6 @@
*/
public class LoginVM {
- @Pattern(regexp = Constants.LOGIN_REGEX)
@NotNull
@Size(min = 1, max = 50)
private String username;
diff --git a/src/main/java/org/exampleapps/greatbig/web/rest/vm/ManagedUserVM.java b/src/main/java/org/exampleapps/greatbig/web/rest/vm/ManagedUserVM.java
index f2b33e1f..5c9c31dd 100644
--- a/src/main/java/org/exampleapps/greatbig/web/rest/vm/ManagedUserVM.java
+++ b/src/main/java/org/exampleapps/greatbig/web/rest/vm/ManagedUserVM.java
@@ -3,9 +3,6 @@
import org.exampleapps.greatbig.service.dto.UserDTO;
import javax.validation.constraints.Size;
-import java.time.Instant;
-import java.util.Set;
-
/**
* View Model extending the UserDTO, which is meant to be used in the user management UI.
*/
@@ -18,31 +15,18 @@ public class ManagedUserVM extends UserDTO {
@Size(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH)
private String password;
- private String bio;
-
- public String getBio() {
- return bio;
- }
-
public ManagedUserVM() {
// Empty constructor needed for Jackson.
}
- public ManagedUserVM(Long id, String login, String password, String firstName, String lastName,
- String email, boolean activated, String imageUrl, String langKey,
- String createdBy, Instant createdDate, String lastModifiedBy, Instant lastModifiedDate,
- Set authorities) {
-
- super(id, login, firstName, lastName, email, activated, imageUrl, langKey,
- createdBy, createdDate, lastModifiedBy, lastModifiedDate, authorities);
-
- this.password = password;
- }
-
public String getPassword() {
return password;
}
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
@Override
public String toString() {
return "ManagedUserVM{" +
diff --git a/src/main/java/org/exampleapps/greatbig/web/websocket/ChatService.java b/src/main/java/org/exampleapps/greatbig/web/websocket/ChatService.java
index 6372541b..dca8ec97 100644
--- a/src/main/java/org/exampleapps/greatbig/web/websocket/ChatService.java
+++ b/src/main/java/org/exampleapps/greatbig/web/websocket/ChatService.java
@@ -45,7 +45,7 @@ public ChatService(SimpMessageSendingOperations messagingTemplate, MessageReposi
@SubscribeMapping("/chat/public")
public void subscribe(StompHeaderAccessor stompHeaderAccessor, Principal principal) {
- String login = SecurityUtils.getCurrentUserLogin();
+ String login = SecurityUtils.getCurrentUserLogin().get();
String ipAddress = stompHeaderAccessor.getSessionAttributes().get(IP_ADDRESS).toString();
log.debug("User {} subscribed to Chat from IP {}", login, ipAddress);
MessageDTO messageDTO = new MessageDTO();
diff --git a/src/main/kotlin/org/exampleapps/greatbig/web/rest/ArticleHandler.kt b/src/main/kotlin/org/exampleapps/greatbig/web/rest/ArticleHandler.kt
index e8e9b7ee..0b572d66 100644
--- a/src/main/kotlin/org/exampleapps/greatbig/web/rest/ArticleHandler.kt
+++ b/src/main/kotlin/org/exampleapps/greatbig/web/rest/ArticleHandler.kt
@@ -67,7 +67,7 @@ class ArticleHandler(val repository: ArticleRepository,
if (favorited != "") userRepository.findOneByLogin(favorited).get() else null
), p)
val currentUser = userService.getUserWithAuthorities()
- val currentAuthor = authorRepository.findById(currentUser.getId())
+ val currentAuthor = authorRepository.findById(currentUser.get().getId())
val headers = PaginationUtil.generatePaginationHttpHeaders(articles, "/api/articles")
@@ -83,7 +83,7 @@ class ArticleHandler(val repository: ArticleRepository,
log.debug("\n\nREST request to get article feed")
val currentUser = userService.getUserWithAuthorities()
- val currentAuthor = authorRepository.findById(currentUser.getId())
+ val currentAuthor = authorRepository.findById(currentUser.get().getId())
val articles = repository.findByAuthorIdInOrderByCreatedAtDesc(currentAuthor.followers.map { it.id },
PageRequest(offset/limit, limit))
@@ -100,7 +100,7 @@ class ArticleHandler(val repository: ArticleRepository,
log.debug("\n\nREST request to get Article for : ", slug)
val currentUser = userService.getUserWithAuthorities()
- val currentAuthor = authorRepository.findById(currentUser.getId())
+ val currentAuthor = authorRepository.findById(currentUser.get().getId())
repository.findBySlug(slug)?.let {
return articleView(it, currentAuthor)
}
@@ -123,7 +123,7 @@ class ArticleHandler(val repository: ArticleRepository,
}
val currentUser = userService.getUserWithAuthorities()
- val currentAuthor = authorRepository.findById(currentUser.getId())
+ val currentAuthor = authorRepository.findById(currentUser.get().getId())
// search for tags
val tags = newArticle.tags.map {
@@ -146,8 +146,8 @@ class ArticleHandler(val repository: ArticleRepository,
repository.findBySlug(slug)?.let {
val currentUser = userService.getUserWithAuthorities()
- val currentAuthor = authorRepository.findById(currentUser.getId())
- if (it.author.id != currentUser.id)
+ val currentAuthor = authorRepository.findById(currentUser.get().getId())
+ if (it.author.id != currentUser.get().getId())
throw ForbiddenRequestException()
// check for errors
@@ -200,7 +200,7 @@ class ArticleHandler(val repository: ArticleRepository,
log.debug("\n\nREST request to delete Article for: ", slug)
repository.findBySlug(slug)?.let {
- if (it.author.id != userService.getUserWithAuthorities().id)
+ if (it.author.id != userService.getUserWithAuthorities().get().getId())
throw ForbiddenRequestException()
// commentRepository.deleteAll(commentRepository.findByArticle(it))
@@ -218,7 +218,7 @@ class ArticleHandler(val repository: ArticleRepository,
repository.findBySlug(slug)?.let {
val currentUser = userService.getUserWithAuthorities()
- val currentAuthor = authorRepository.findById(currentUser.getId())
+ val currentAuthor = authorRepository.findById(currentUser.get().getId())
return commentsView(commentRepository.findByArticleOrderByCreatedAtDesc(it), currentAuthor)
}
throw NotFoundException()
@@ -235,7 +235,7 @@ class ArticleHandler(val repository: ArticleRepository,
repository.findBySlug(slug)?.let {
val currentUser = userService.getUserWithAuthorities()
- val currentAuthor = authorRepository.findById(currentUser.getId())
+ val currentAuthor = authorRepository.findById(currentUser.get().getId())
val newComment = Comment(body = comment.body!!, article = it, author = currentAuthor)
return commentView(commentRepository.save(newComment), currentAuthor)
}
@@ -258,7 +258,7 @@ class ArticleHandler(val repository: ArticleRepository,
}
if (comment.article.id != it.id)
throw ForbiddenRequestException()
- if (comment.author.id != currentUser.id)
+ if (comment.author.id != currentUser.get().getId())
throw ForbiddenRequestException()
return commentRepository.delete(comment)
@@ -275,7 +275,7 @@ class ArticleHandler(val repository: ArticleRepository,
repository.findBySlug(slug)?.let {
val currentUser = userService.getUserWithAuthorities()
- val currentAuthor = authorRepository.findById(currentUser.getId())
+ val currentAuthor = authorRepository.findById(currentUser.get().getId())
if (!it.favorited.contains(currentAuthor)) {
it.favorited.add(currentAuthor)
return articleView(repository.save(it), currentAuthor)
@@ -294,7 +294,7 @@ class ArticleHandler(val repository: ArticleRepository,
repository.findBySlug(slug)?.let {
val currentUser = userService.getUserWithAuthorities()
- val currentAuthor = authorRepository.findById(currentUser.getId())
+ val currentAuthor = authorRepository.findById(currentUser.get().getId())
if (it.favorited.contains(currentAuthor)) {
it.favorited.remove(currentAuthor)
return articleView(repository.save(it), currentAuthor)
diff --git a/src/main/kotlin/org/exampleapps/greatbig/web/rest/ProfileHandler.kt b/src/main/kotlin/org/exampleapps/greatbig/web/rest/ProfileHandler.kt
index 89889ad6..26bbc677 100644
--- a/src/main/kotlin/org/exampleapps/greatbig/web/rest/ProfileHandler.kt
+++ b/src/main/kotlin/org/exampleapps/greatbig/web/rest/ProfileHandler.kt
@@ -23,7 +23,7 @@ class ProfileHandler(val userRepository: UserRepository,
val user = userRepository.findOneByLogin(username);
authorRepository.findById(user.get().getId())?.let {
val currentUser = userService.getUserWithAuthorities()
- val currentAuthor = authorRepository.findById(currentUser.getId())
+ val currentAuthor = authorRepository.findById(currentUser.get().getId())
return view(it, currentAuthor)
}
throw NotFoundException()
@@ -36,7 +36,7 @@ class ProfileHandler(val userRepository: UserRepository,
val user = userRepository.findOneByLogin(username);
authorRepository.findById(user.get().getId())?.let {
var currentUser = userService.getUserWithAuthorities()
- val currentAuthor = authorRepository.findById(currentUser.getId())
+ val currentAuthor = authorRepository.findById(currentUser.get().getId())
if (!currentAuthor.followers.contains(it)) {
currentAuthor.followers.add(it)
// currentAuthor = userService.setCurrentUser(userRepository.save(currentUser))
@@ -53,7 +53,7 @@ class ProfileHandler(val userRepository: UserRepository,
val user = userRepository.findOneByLogin(username);
authorRepository.findById(user.get().getId())?.let {
var currentUser = userService.getUserWithAuthorities()
- val currentAuthor = authorRepository.findById(currentUser.getId())
+ val currentAuthor = authorRepository.findById(currentUser.get().getId())
if (currentAuthor.followers.contains(it)) {
currentAuthor.followers.remove(it)
// currentAuthor = userService.setCurrentUser(userRepository.save(currentUser))
diff --git a/src/main/resources/.h2.server.properties b/src/main/resources/.h2.server.properties
index 816f35da..520078d3 100644
--- a/src/main/resources/.h2.server.properties
+++ b/src/main/resources/.h2.server.properties
@@ -1,6 +1,5 @@
#H2 Server Properties
-# 0=JHipster H2 (Disk)|org.h2.Driver|jdbc\:h2\:file\:./target/h2db/db/greatbigexampleapplication|GreatBigExampleApplication
-0=JHipster H2 (Disk)|org.h2.Driver|jdbc\:h2\:mem\:greatBigExampleApplication|greatBigExampleApplication
+0=JHipster H2 (Disk)|org.h2.Driver|jdbc\:h2\:file\:./target/h2db/db/greatbigexampleapplication|GreatBigExampleApplication
webAllowOthers=true
webPort=8082
webSSL=false
diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt
index c3d8cf72..db090f7e 100644
--- a/src/main/resources/banner.txt
+++ b/src/main/resources/banner.txt
@@ -7,4 +7,4 @@
${AnsiColor.GREEN} ╚═════╝ ${AnsiColor.RED} ╚═╝ ╚═╝ ╚═══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══════╝ ╚═╝ ╚═╝
${AnsiColor.BRIGHT_BLUE}:: JHipster 🤓 :: Running Spring Boot ${spring-boot.version} ::
-:: http://jhipster.github.io ::${AnsiColor.DEFAULT}
+:: http://www.jhipster.tech ::${AnsiColor.DEFAULT}
diff --git a/src/main/resources/config/application-dev.yml b/src/main/resources/config/application-dev.yml
index 7d49f315..1c7e988f 100644
--- a/src/main/resources/config/application-dev.yml
+++ b/src/main/resources/config/application-dev.yml
@@ -3,8 +3,8 @@
#
# This configuration overrides the application.yml file.
#
-# More information on profiles: https://jhipster.github.io/profiles/
-# More information on configuration properties: https://jhipster.github.io/common-application-properties/
+# More information on profiles: http://www.jhipster.tech/profiles/
+# More information on configuration properties: http://www.jhipster.tech/common-application-properties/
# ===================================================================
# ===================================================================
@@ -13,6 +13,12 @@
# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
# ===================================================================
+logging:
+ level:
+ ROOT: DEBUG
+ org.exampleapps.greatbig: DEBUG
+ io.github.jhipster: DEBUG
+
spring:
profiles:
active: dev
@@ -26,7 +32,7 @@ spring:
serialization.indent_output: true
datasource:
type: com.zaxxer.hikari.HikariDataSource
- url: jdbc:h2:mem:greatbigexampleapplication;DB_CLOSE_DELAY=-1
+ url: jdbc:h2:file:./target/h2db/db/greatbigexampleapplication;DB_CLOSE_DELAY=-1
username: GreatBigExampleApplication
password:
h2:
@@ -51,11 +57,6 @@ spring:
home: target/elasticsearch
logs: target/elasticsearch/log
data: target/elasticsearch/data
- transport:
- tcp:
- connect_timeout: 120s
- jest:
- readTimeout: 10000
mail:
host: localhost
port: 25
@@ -83,8 +84,8 @@ liquibase:
# ssl:
# key-store: keystore.p12
# key-store-password:
-# keyStoreType: PKCS12
-# keyAlias: GreatBigExampleApplication
+# key-store-type: PKCS12
+# key-alias: GreatBigExampleApplication
# ===================================================================
server:
port: 8070
@@ -92,7 +93,7 @@ server:
# ===================================================================
# JHipster specific properties
#
-# Full reference is available at: https://jhipster.github.io/common-application-properties/
+# Full reference is available at: http://www.jhipster.tech/common-application-properties/
# ===================================================================
jhipster:
@@ -105,9 +106,9 @@ jhipster:
# CORS is only enabled by default with the "dev" profile, so BrowserSync can access the API
cors:
allowed-origins: "*"
- allowed-methods: GET, PUT, POST, DELETE, OPTIONS
+ allowed-methods: "*"
allowed-headers: "*"
- exposed-headers:
+ exposed-headers: "Authorization,Link,X-Total-Count"
allow-credentials: true
max-age: 1800
security:
@@ -140,13 +141,14 @@ jhipster:
port: 5000
queue-size: 512
+
# ===================================================================
# Application specific properties
# Add your own application properties here, see the ApplicationProperties class
# to have type-safe configuration, like in the JHipsterProperties above
#
# More documentation is available at:
-# https://jhipster.github.io/common-application-properties/
+# http://www.jhipster.tech/common-application-properties/
# ===================================================================
application:
diff --git a/src/main/resources/config/application-prod.yml b/src/main/resources/config/application-prod.yml
index 73e445af..51fbe83b 100644
--- a/src/main/resources/config/application-prod.yml
+++ b/src/main/resources/config/application-prod.yml
@@ -3,8 +3,8 @@
#
# This configuration overrides the application.yml file.
#
-# More information on profiles: https://jhipster.github.io/profiles/
-# More information on configuration properties: https://jhipster.github.io/common-application-properties/
+# More information on profiles: http://www.jhipster.tech/profiles/
+# More information on configuration properties: http://www.jhipster.tech/common-application-properties/
# ===================================================================
# ===================================================================
@@ -13,6 +13,12 @@
# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
# ===================================================================
+logging:
+ level:
+ ROOT: INFO
+ org.exampleapps.greatbig: INFO
+ io.github.jhipster: INFO
+
spring:
devtools:
restart:
@@ -22,8 +28,8 @@ spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:postgresql://localhost:5432/GreatBigExampleApplication
- username: GreatBigExampleUser
- password: password
+ username: GreatBigExampleApplication
+ password:
jpa:
database-platform: io.github.jhipster.domain.util.FixedPostgreSQL82Dialect
database: POSTGRESQL
@@ -38,15 +44,6 @@ spring:
elasticsearch:
cluster-name:
cluster-nodes: localhost:9300
- properties:
- path:
- home: target/elasticsearch
- transport:
- tcp:
- connect_timeout: 120s
- jest:
- readTimeout: 10000
- # uri: ${SEARCHBOX_SSL_URL}
mail:
host: localhost
port: 25
@@ -72,8 +69,10 @@ liquibase:
# ssl:
# key-store: keystore.p12
# key-store-password:
-# keyStoreType: PKCS12
-# keyAlias: GreatBigExampleApplication
+# key-store-type: PKCS12
+# key-alias: GreatBigExampleApplication
+# # The ciphers suite enforce the security by deactivating some old and deprecated SSL cipher, this list was tested against SSL Labs (https://www.ssllabs.com/ssltest/)
+# ciphers: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ,TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 ,TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,TLS_RSA_WITH_CAMELLIA_128_CBC_SHA
# ===================================================================
server:
port: 8090
@@ -85,7 +84,7 @@ server:
# ===================================================================
# JHipster specific properties
#
-# Full reference is available at: https://jhipster.github.io/common-application-properties/
+# Full reference is available at: http://www.jhipster.tech/common-application-properties/
# ===================================================================
jhipster:
@@ -127,13 +126,14 @@ jhipster:
port: 5000
queue-size: 512
+
# ===================================================================
# Application specific properties
# Add your own application properties here, see the ApplicationProperties class
# to have type-safe configuration, like in the JHipsterProperties above
#
# More documentation is available at:
-# https://jhipster.github.io/common-application-properties/
+# http://www.jhipster.tech/common-application-properties/
# ===================================================================
application:
diff --git a/src/main/resources/config/application.yml b/src/main/resources/config/application.yml
index c8a4f86a..e7826c06 100644
--- a/src/main/resources/config/application.yml
+++ b/src/main/resources/config/application.yml
@@ -4,8 +4,8 @@
# This configuration will be overridden by the Spring profile you use,
# for example application-dev.yml if you use the "dev" profile.
#
-# More information on profiles: https://jhipster.github.io/profiles/
-# More information on configuration properties: https://jhipster.github.io/common-application-properties/
+# More information on profiles: http://www.jhipster.tech/profiles/
+# More information on configuration properties: http://www.jhipster.tech/common-application-properties/
# ===================================================================
# ===================================================================
@@ -18,6 +18,9 @@ management:
security:
roles: ADMIN
context-path: /management
+ info:
+ git:
+ mode: full
health:
mail:
enabled: false # When using the MailService, configure an SMTP server and set this to true
@@ -62,7 +65,6 @@ spring:
client-secret: xxx
# jhipster-needle-add-social-configuration
-
security:
basic:
enabled: false
@@ -79,7 +81,7 @@ info:
# ===================================================================
# JHipster specific properties
#
-# Full reference is available at: https://jhipster.github.io/common-application-properties/
+# Full reference is available at: http://www.jhipster.tech/common-application-properties/
# ===================================================================
jhipster:
@@ -90,9 +92,9 @@ jhipster:
# By default CORS is disabled. Uncomment to enable.
#cors:
#allowed-origins: "*"
- #allowed-methods: GET, PUT, POST, DELETE, OPTIONS
+ #allowed-methods: "*"
#allowed-headers: "*"
- #exposed-headers:
+ #exposed-headers: "Authorization,Link,X-Total-Count"
#allow-credentials: true
#max-age: 1800
mail:
@@ -119,7 +121,7 @@ jhipster:
# to have type-safe configuration, like in the JHipsterProperties above
#
# More documentation is available at:
-# https://jhipster.github.io/common-application-properties/
+# http://www.jhipster.tech/common-application-properties/
# ===================================================================
application:
diff --git a/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml b/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml
index 61a18ca9..64d93ec5 100644
--- a/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml
+++ b/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml
@@ -3,7 +3,7 @@
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd
+ xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
@@ -23,19 +23,19 @@
-
+
-
+
-
+
@@ -83,7 +83,6 @@
constraintName="fk_authority_name"
referencedColumnNames="name"
referencedTableName="jhi_authority"/>
-
-
-
diff --git a/src/main/resources/i18n/messages_fr.properties b/src/main/resources/i18n/messages_fr.properties
index 226e0518..a6804026 100644
--- a/src/main/resources/i18n/messages_fr.properties
+++ b/src/main/resources/i18n/messages_fr.properties
@@ -12,7 +12,7 @@ email.activation.text2=Cordialement,
email.signature=GreatBigExampleApplication.
# Creation email
-email.creation.text1=Your GreatBigExampleApplication account has been created, please click on the URL below to access it:
+email.creation.text1=Votre compte GreatBigExampleApplication a été créé, merci de cliquer sur le lien ci-dessous pour y accéder :
# Reset email
email.reset.title=GreatBigExampleApplication Réinitialisation de mot de passe
diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml
index 0bd49303..fcc8c8dc 100644
--- a/src/main/resources/logback-spring.xml
+++ b/src/main/resources/logback-spring.xml
@@ -22,10 +22,6 @@
-->
-
-
-
-
@@ -56,15 +52,11 @@
+
+
+
true
-
-
-
-
-