BAEL-137 Intro do JHipster (#1427)

* refactor: Reorder tests without lambda

Moves inner implementations of Answer and ArgumentMatcher to the top of
the test classes.
Also changes the lambda expression to a regular "pre java 8" expression
in one of the tests.

Resolves: BAEL-632

* feat: Create basic Monolithic JHipster project

Commit just after creating a JHipster project, before making any modifications.

Resolves: BAEL-137

* chore: Change the artifactId and name of the project

From baeldung to jhipster-monolithic and JHipster Monolithic Application

Relates to: BAEL-137

* feat: Create entities Post and Comment

Relates to: BAEL-137

* feat: Fix Gatling configuration in pom.xml

Relates to: BAEL-137

* feat: Add files for Continuous Integration

Relates to: BAEL-137

* feat: Change pom.xml to conform to Baeldung standards

- moved the <properties> element to the bottom of the file
- excluded integration tests in the default surefire configuration
- added a new profile, called integration, and added the integration tests there
- added Java 8 in the <source> and <target> tags, under maven-compiler

solves: BAEL-137

* chore: Add jhipster module to parent pom
This commit is contained in:
Felipe Reis
2017-03-21 08:23:41 -03:00
committed by pedja4
parent 07c0e84bf4
commit 78f87104d6
361 changed files with 20698 additions and 21 deletions
+13
View File
@@ -0,0 +1,13 @@
FROM openjdk:8-jre-alpine
ENV SPRING_OUTPUT_ANSI_ENABLED=ALWAYS \
JHIPSTER_SLEEP=0
# add directly the war
ADD *.war /app.war
VOLUME /tmp
EXPOSE 8080
CMD echo "The application will start in ${JHIPSTER_SLEEP}s..." && \
sleep ${JHIPSTER_SLEEP} && \
java -Djava.security.egd=file:/dev/./urandom -jar /app.war
+14
View File
@@ -0,0 +1,14 @@
version: '2'
services:
baeldung-app:
image: baeldung
environment:
- SPRING_PROFILES_ACTIVE=prod,swagger
- SPRING_DATASOURCE_URL=jdbc:mysql://baeldung-mysql:3306/baeldung?useUnicode=true&characterEncoding=utf8&useSSL=false
- JHIPSTER_SLEEP=10 # gives time for the database to boot before the application
ports:
- 8080:8080
baeldung-mysql:
extends:
file: mysql.yml
service: baeldung-mysql
+13
View File
@@ -0,0 +1,13 @@
version: '2'
services:
baeldung-mysql:
image: mysql:5.7.13
# volumes:
# - ~/volumes/jhipster/baeldung/mysql/:/var/lib/mysql/
environment:
- MYSQL_USER=root
- MYSQL_ALLOW_EMPTY_PASSWORD=yes
- MYSQL_DATABASE=baeldung
ports:
- 3306:3306
command: mysqld --lower_case_table_names=1 --skip-ssl --character_set_server=utf8
+7
View File
@@ -0,0 +1,7 @@
version: '2'
services:
baeldung-sonar:
image: sonarqube:6.2-alpine
ports:
- 9000:9000
- 9092:9092
@@ -0,0 +1,21 @@
package com.baeldung;
import com.baeldung.config.DefaultProfileUtil;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
/**
* This is a helper Java class that provides an alternative to creating a web.xml.
* This will be invoked only when the application is deployed to a servlet container like Tomcat, JBoss etc.
*/
public class ApplicationWebXml extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
/**
* set a default to use when no profile is configured.
*/
DefaultProfileUtil.addDefaultProfile(application.application());
return application.sources(BaeldungApp.class);
}
}
@@ -0,0 +1,84 @@
package com.baeldung;
import com.baeldung.config.ApplicationProperties;
import com.baeldung.config.DefaultProfileUtil;
import io.github.jhipster.config.JHipsterConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.*;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.env.Environment;
import javax.annotation.PostConstruct;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collection;
@ComponentScan
@EnableAutoConfiguration(exclude = {MetricFilterAutoConfiguration.class, MetricRepositoryAutoConfiguration.class})
@EnableConfigurationProperties({LiquibaseProperties.class, ApplicationProperties.class})
public class BaeldungApp {
private static final Logger log = LoggerFactory.getLogger(BaeldungApp.class);
private final Environment env;
public BaeldungApp(Environment env) {
this.env = env;
}
/**
* Initializes baeldung.
* <p>
* Spring profiles can be configured with a program arguments --spring.profiles.active=your-active-profile
* <p>
* You can find more information on how profiles work with JHipster on <a href="http://jhipster.github.io/profiles/">http://jhipster.github.io/profiles/</a>.
*/
@PostConstruct
public void initApplication() {
Collection<String> activeProfiles = Arrays.asList(env.getActiveProfiles());
if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) {
log.error("You have misconfigured your application! It should not run " +
"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" +
"run with both the 'dev' and 'cloud' profiles at the same time.");
}
}
/**
* Main method, used to run the application.
*
* @param args the command line arguments
* @throws UnknownHostException if the local host name could not be resolved into an address
*/
public static void main(String[] args) throws UnknownHostException {
SpringApplication app = new SpringApplication(BaeldungApp.class);
DefaultProfileUtil.addDefaultProfile(app);
Environment env = app.run(args).getEnvironment();
String protocol = "http";
if (env.getProperty("server.ssl.key-store") != null) {
protocol = "https";
}
log.info("\n----------------------------------------------------------\n\t" +
"Application '{}' is running! Access URLs:\n\t" +
"Local: \t\t{}://localhost:{}\n\t" +
"External: \t{}://{}:{}\n\t" +
"Profile(s): \t{}\n----------------------------------------------------------",
env.getProperty("spring.application.name"),
protocol,
env.getProperty("server.port"),
protocol,
InetAddress.getLocalHost().getHostAddress(),
env.getProperty("server.port"),
env.getActiveProfiles());
}
}
@@ -0,0 +1,79 @@
package com.baeldung.aop.logging;
import io.github.jhipster.config.JHipsterConstants;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
import java.util.Arrays;
/**
* Aspect for logging execution of service and repository Spring components.
*
* By default, it only runs with the "dev" profile.
*/
@Aspect
public class LoggingAspect {
private final Logger log = LoggerFactory.getLogger(this.getClass());
private final Environment env;
public LoggingAspect(Environment env) {
this.env = env;
}
/**
* Pointcut that matches all repositories, services and Web REST endpoints.
*/
@Pointcut("within(com.baeldung.repository..*) || within(com.baeldung.service..*) || within(com.baeldung.web.rest..*)")
public void loggingPointcut() {
// Method is empty as this is just a Pointcut, the implementations are in the advices.
}
/**
* Advice that logs methods throwing exceptions.
*/
@AfterThrowing(pointcut = "loggingPointcut()", throwing = "e")
public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)) {
log.error("Exception in {}.{}() with cause = \'{}\' and exception = \'{}\'", joinPoint.getSignature().getDeclaringTypeName(),
joinPoint.getSignature().getName(), e.getCause() != null? e.getCause() : "NULL", e.getMessage(), e);
} else {
log.error("Exception in {}.{}() with cause = {}", joinPoint.getSignature().getDeclaringTypeName(),
joinPoint.getSignature().getName(), e.getCause() != null? e.getCause() : "NULL");
}
}
/**
* Advice that logs when a method is entered and exited.
*/
@Around("loggingPointcut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
if (log.isDebugEnabled()) {
log.debug("Enter: {}.{}() with argument[s] = {}", joinPoint.getSignature().getDeclaringTypeName(),
joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()));
}
try {
Object result = joinPoint.proceed();
if (log.isDebugEnabled()) {
log.debug("Exit: {}.{}() with result = {}", joinPoint.getSignature().getDeclaringTypeName(),
joinPoint.getSignature().getName(), result);
}
return result;
} catch (IllegalArgumentException e) {
log.error("Illegal argument: {} in {}.{}()", Arrays.toString(joinPoint.getArgs()),
joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
throw e;
}
}
}
@@ -0,0 +1,15 @@
package com.baeldung.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Properties specific to JHipster.
*
* <p>
* Properties are configured in the application.yml file.
* </p>
*/
@ConfigurationProperties(prefix = "application", ignoreUnknownFields = false)
public class ApplicationProperties {
}
@@ -0,0 +1,46 @@
package com.baeldung.config;
import io.github.jhipster.async.ExceptionHandlingAsyncTaskExecutor;
import io.github.jhipster.config.JHipsterProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.*;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration
@EnableAsync
@EnableScheduling
public class AsyncConfiguration implements AsyncConfigurer {
private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class);
private final JHipsterProperties jHipsterProperties;
public AsyncConfiguration(JHipsterProperties jHipsterProperties) {
this.jHipsterProperties = jHipsterProperties;
}
@Override
@Bean(name = "taskExecutor")
public Executor getAsyncExecutor() {
log.debug("Creating Async Task Executor");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(jHipsterProperties.getAsync().getCorePoolSize());
executor.setMaxPoolSize(jHipsterProperties.getAsync().getMaxPoolSize());
executor.setQueueCapacity(jHipsterProperties.getAsync().getQueueCapacity());
executor.setThreadNamePrefix("baeldung-Executor-");
return new ExceptionHandlingAsyncTaskExecutor(executor);
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}
@@ -0,0 +1,48 @@
package com.baeldung.config;
import io.github.jhipster.config.JHipsterProperties;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.expiry.Duration;
import org.ehcache.expiry.Expirations;
import org.ehcache.jsr107.Eh107Configuration;
import java.util.concurrent.TimeUnit;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.*;
@Configuration
@EnableCaching
@AutoConfigureAfter(value = { MetricsConfiguration.class })
@AutoConfigureBefore(value = { WebConfigurer.class, DatabaseConfiguration.class })
public class CacheConfiguration {
private final javax.cache.configuration.Configuration<Object, Object> jcacheConfiguration;
public CacheConfiguration(JHipsterProperties jHipsterProperties) {
JHipsterProperties.Cache.Ehcache ehcache =
jHipsterProperties.getCache().getEhcache();
jcacheConfiguration = Eh107Configuration.fromEhcacheCacheConfiguration(
CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class,
ResourcePoolsBuilder.heap(ehcache.getMaxEntries()))
.withExpiry(Expirations.timeToLiveExpiration(Duration.of(ehcache.getTimeToLiveSeconds(), TimeUnit.SECONDS)))
.build());
}
@Bean
public JCacheManagerCustomizer cacheManagerCustomizer() {
return cm -> {
cm.createCache(com.baeldung.domain.User.class.getName(), jcacheConfiguration);
cm.createCache(com.baeldung.domain.Authority.class.getName(), jcacheConfiguration);
cm.createCache(com.baeldung.domain.User.class.getName() + ".authorities", jcacheConfiguration);
cm.createCache(com.baeldung.domain.Post.class.getName(), jcacheConfiguration);
cm.createCache(com.baeldung.domain.Comment.class.getName(), jcacheConfiguration);
// jhipster-needle-ehcache-add-entry
};
}
}
@@ -0,0 +1,23 @@
package com.baeldung.config;
import io.github.jhipster.config.JHipsterConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.config.java.AbstractCloudConfig;
import org.springframework.context.annotation.*;
import javax.sql.DataSource;
@Configuration
@Profile(JHipsterConstants.SPRING_PROFILE_CLOUD)
public class CloudDatabaseConfiguration extends AbstractCloudConfig {
private final Logger log = LoggerFactory.getLogger(CloudDatabaseConfiguration.class);
@Bean
public DataSource dataSource() {
log.info("Configuring JDBC datasource from a cloud provider");
return connectionFactory().dataSource();
}
}
@@ -0,0 +1,16 @@
package com.baeldung.config;
/**
* Application constants.
*/
public final class Constants {
//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";
private Constants() {
}
}
@@ -0,0 +1,75 @@
package com.baeldung.config;
import io.github.jhipster.config.JHipsterConstants;
import io.github.jhipster.config.liquibase.AsyncSpringLiquibase;
import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module;
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;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.Environment;
import org.springframework.core.task.TaskExecutor;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.sql.SQLException;
@Configuration
@EnableJpaRepositories("com.baeldung.repository")
@EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware")
@EnableTransactionManagement
public class DatabaseConfiguration {
private final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class);
private final Environment env;
public DatabaseConfiguration(Environment env) {
this.env = env;
}
/**
* Open the TCP port for the H2 database, so it is available remotely.
*
* @return the H2 database TCP server
* @throws SQLException if the server failed to start
*/
@Bean(initMethod = "start", destroyMethod = "stop")
@Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)
public Server h2TCPServer() throws SQLException {
return Server.createTcpServer("-tcp","-tcpAllowOthers");
}
@Bean
public SpringLiquibase liquibase(@Qualifier("taskExecutor") TaskExecutor taskExecutor,
DataSource dataSource, LiquibaseProperties liquibaseProperties) {
// Use liquibase.integration.spring.SpringLiquibase if you don't want Liquibase to start asynchronously
SpringLiquibase liquibase = new AsyncSpringLiquibase(taskExecutor, env);
liquibase.setDataSource(dataSource);
liquibase.setChangeLog("classpath:config/liquibase/master.xml");
liquibase.setContexts(liquibaseProperties.getContexts());
liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema());
liquibase.setDropFirst(liquibaseProperties.isDropFirst());
if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_NO_LIQUIBASE)) {
liquibase.setShouldRun(false);
} else {
liquibase.setShouldRun(liquibaseProperties.isEnabled());
log.debug("Configuring Liquibase");
}
return liquibase;
}
@Bean
public Hibernate5Module hibernate5Module() {
return new Hibernate5Module();
}
}
@@ -0,0 +1,17 @@
package com.baeldung.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class DateTimeFormatConfiguration extends WebMvcConfigurerAdapter {
@Override
public void addFormatters(FormatterRegistry registry) {
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
registrar.setUseIsoFormat(true);
registrar.registerFormatters(registry);
}
}
@@ -0,0 +1,48 @@
package com.baeldung.config;
import io.github.jhipster.config.JHipsterConstants;
import org.springframework.boot.SpringApplication;
import org.springframework.core.env.Environment;
import java.util.*;
/**
* Utility class to load a Spring profile to be used as default
* when there is no <code>spring.profiles.active</code> set in the environment or as command line argument.
* If the value is not available in <code>application.yml</code> then <code>dev</code> profile will be used as default.
*/
public final class DefaultProfileUtil {
private static final String SPRING_PROFILE_DEFAULT = "spring.profiles.default";
private DefaultProfileUtil() {
}
/**
* Set a default to use when no profile is configured.
*
* @param app the Spring application
*/
public static void addDefaultProfile(SpringApplication app) {
Map<String, Object> defProperties = new HashMap<>();
/*
* The default profile to use when no other profiles are defined
* This cannot be set in the <code>application.yml</code> file.
* See https://github.com/spring-projects/spring-boot/issues/1219
*/
defProperties.put(SPRING_PROFILE_DEFAULT, JHipsterConstants.SPRING_PROFILE_DEVELOPMENT);
app.setDefaultProperties(defProperties);
}
/**
* Get the profiles that are applied else get default profiles.
*/
public static String[] getActiveProfiles(Environment env) {
String[] profiles = env.getActiveProfiles();
if (profiles.length == 0) {
return env.getDefaultProfiles();
}
return profiles;
}
}
@@ -0,0 +1,35 @@
package com.baeldung.config;
import io.github.jhipster.config.locale.AngularCookieLocaleResolver;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
@Configuration
public class LocaleConfiguration extends WebMvcConfigurerAdapter implements EnvironmentAware {
@Override
public void setEnvironment(Environment environment) {
// unused
}
@Bean(name = "localeResolver")
public LocaleResolver localeResolver() {
AngularCookieLocaleResolver cookieLocaleResolver = new AngularCookieLocaleResolver();
cookieLocaleResolver.setCookieName("NG_TRANSLATE_LANG_KEY");
return cookieLocaleResolver;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("language");
registry.addInterceptor(localeChangeInterceptor);
}
}
@@ -0,0 +1,19 @@
package com.baeldung.config;
import com.baeldung.aop.logging.LoggingAspect;
import io.github.jhipster.config.JHipsterConstants;
import org.springframework.context.annotation.*;
import org.springframework.core.env.Environment;
@Configuration
@EnableAspectJAutoProxy
public class LoggingAspectConfiguration {
@Bean
@Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)
public LoggingAspect loggingAspect(Environment env) {
return new LoggingAspect(env);
}
}
@@ -0,0 +1,109 @@
package com.baeldung.config;
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.spi.LoggerContextListener;
import ch.qos.logback.core.spi.ContextAwareBase;
import net.logstash.logback.appender.LogstashSocketAppender;
import net.logstash.logback.stacktrace.ShortenedThrowableConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
public class LoggingConfiguration {
private final Logger log = LoggerFactory.getLogger(LoggingConfiguration.class);
private LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
@Value("${spring.application.name}")
private String appName;
@Value("${server.port}")
private String serverPort;
private final JHipsterProperties jHipsterProperties;
public LoggingConfiguration(JHipsterProperties jHipsterProperties) {
this.jHipsterProperties = jHipsterProperties;
if (jHipsterProperties.getLogging().getLogstash().isEnabled()) {
addLogstashAppender(context);
// Add context listener
LogbackLoggerContextListener loggerContextListener = new LogbackLoggerContextListener();
loggerContextListener.setContext(context);
context.addListener(loggerContextListener);
}
}
public void addLogstashAppender(LoggerContext context) {
log.info("Initializing Logstash logging");
LogstashSocketAppender logstashAppender = new LogstashSocketAppender();
logstashAppender.setName("LOGSTASH");
logstashAppender.setContext(context);
String customFields = "{\"app_name\":\"" + appName + "\",\"app_port\":\"" + serverPort + "\"}";
// Set the Logstash appender config from JHipster properties
logstashAppender.setSyslogHost(jHipsterProperties.getLogging().getLogstash().getHost());
logstashAppender.setPort(jHipsterProperties.getLogging().getLogstash().getPort());
logstashAppender.setCustomFields(customFields);
// 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);
logstashAppender.start();
// Wrap the appender in an Async appender for performance
AsyncAppender asyncLogstashAppender = new AsyncAppender();
asyncLogstashAppender.setContext(context);
asyncLogstashAppender.setName("ASYNC_LOGSTASH");
asyncLogstashAppender.setQueueSize(jHipsterProperties.getLogging().getLogstash().getQueueSize());
asyncLogstashAppender.addAppender(logstashAppender);
asyncLogstashAppender.start();
context.getLogger("ROOT").addAppender(asyncLogstashAppender);
}
/**
* Logback configuration is achieved by configuration file and API.
* When configuration file change is detected, the configuration is reset.
* This listener ensures that the programmatic configuration is also re-applied after reset.
*/
class LogbackLoggerContextListener extends ContextAwareBase implements LoggerContextListener {
@Override
public boolean isResetResistant() {
return true;
}
@Override
public void onStart(LoggerContext context) {
addLogstashAppender(context);
}
@Override
public void onReset(LoggerContext context) {
addLogstashAppender(context);
}
@Override
public void onStop(LoggerContext context) {
// Nothing to do.
}
@Override
public void onLevelChange(ch.qos.logback.classic.Logger logger, Level level) {
// Nothing to do.
}
}
}
@@ -0,0 +1,94 @@
package com.baeldung.config;
import io.github.jhipster.config.JHipsterProperties;
import io.github.jhipster.config.jcache.JCacheGaugeSet;
import com.codahale.metrics.JmxReporter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Slf4jReporter;
import com.codahale.metrics.health.HealthCheckRegistry;
import com.codahale.metrics.jvm.*;
import com.ryantenney.metrics.spring.config.annotation.EnableMetrics;
import com.ryantenney.metrics.spring.config.annotation.MetricsConfigurerAdapter;
import com.zaxxer.hikari.HikariDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import javax.annotation.PostConstruct;
import java.lang.management.ManagementFactory;
import java.util.concurrent.TimeUnit;
@Configuration
@EnableMetrics(proxyTargetClass = true)
public class MetricsConfiguration extends MetricsConfigurerAdapter {
private static final String PROP_METRIC_REG_JVM_MEMORY = "jvm.memory";
private static final String PROP_METRIC_REG_JVM_GARBAGE = "jvm.garbage";
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_JCACHE_STATISTICS = "jcache.statistics";
private final Logger log = LoggerFactory.getLogger(MetricsConfiguration.class);
private MetricRegistry metricRegistry = new MetricRegistry();
private HealthCheckRegistry healthCheckRegistry = new HealthCheckRegistry();
private final JHipsterProperties jHipsterProperties;
private HikariDataSource hikariDataSource;
public MetricsConfiguration(JHipsterProperties jHipsterProperties) {
this.jHipsterProperties = jHipsterProperties;
}
@Autowired(required = false)
public void setHikariDataSource(HikariDataSource hikariDataSource) {
this.hikariDataSource = hikariDataSource;
}
@Override
@Bean
public MetricRegistry getMetricRegistry() {
return metricRegistry;
}
@Override
@Bean
public HealthCheckRegistry getHealthCheckRegistry() {
return healthCheckRegistry;
}
@PostConstruct
public void init() {
log.debug("Registering JVM gauges");
metricRegistry.register(PROP_METRIC_REG_JVM_MEMORY, new MemoryUsageGaugeSet());
metricRegistry.register(PROP_METRIC_REG_JVM_GARBAGE, new GarbageCollectorMetricSet());
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_JCACHE_STATISTICS, new JCacheGaugeSet());
if (hikariDataSource != null) {
log.debug("Monitoring the datasource");
hikariDataSource.setMetricRegistry(metricRegistry);
}
if (jHipsterProperties.getMetrics().getJmx().isEnabled()) {
log.debug("Initializing Metrics JMX reporting");
JmxReporter jmxReporter = JmxReporter.forRegistry(metricRegistry).build();
jmxReporter.start();
}
if (jHipsterProperties.getMetrics().getLogs().isEnabled()) {
log.info("Initializing Metrics Log reporting");
final Slf4jReporter reporter = Slf4jReporter.forRegistry(metricRegistry)
.outputTo(LoggerFactory.getLogger("metrics"))
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build();
reporter.start(jHipsterProperties.getMetrics().getLogs().getReportFrequency(), TimeUnit.SECONDS);
}
}
}
@@ -0,0 +1,127 @@
package com.baeldung.config;
import com.baeldung.security.*;
import com.baeldung.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.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.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 javax.annotation.PostConstruct;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final AuthenticationManagerBuilder authenticationManagerBuilder;
private final UserDetailsService userDetailsService;
private final TokenProvider tokenProvider;
private final CorsFilter corsFilter;
public SecurityConfiguration(AuthenticationManagerBuilder authenticationManagerBuilder, UserDetailsService userDetailsService,
TokenProvider tokenProvider,
CorsFilter corsFilter) {
this.authenticationManagerBuilder = authenticationManagerBuilder;
this.userDetailsService = userDetailsService;
this.tokenProvider = tokenProvider;
this.corsFilter = corsFilter;
}
@PostConstruct
public void init() {
try {
authenticationManagerBuilder
.userDetailsService(userDetailsService)
.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();
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/app/**/*.{js,html}")
.antMatchers("/bower_components/**")
.antMatchers("/i18n/**")
.antMatchers("/content/**")
.antMatchers("/swagger-ui/index.html")
.antMatchers("/test/**")
.antMatchers("/h2-console/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(http401UnauthorizedEntryPoint())
.and()
.csrf()
.disable()
.headers()
.frameOptions()
.disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.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/profile-info").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/v2/api-docs/**").permitAll()
.antMatchers("/swagger-resources/configuration/ui").permitAll()
.antMatchers("/swagger-ui/index.html").hasAuthority(AuthoritiesConstants.ADMIN)
.and()
.apply(securityConfigurerAdapter());
}
private JWTConfigurer securityConfigurerAdapter() {
return new JWTConfigurer(tokenProvider);
}
@Bean
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
return new SecurityEvaluationContextExtension();
}
}
@@ -0,0 +1,26 @@
package com.baeldung.config;
import org.apache.commons.lang3.CharEncoding;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.*;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
@Configuration
public class ThymeleafConfiguration {
@SuppressWarnings("unused")
private final Logger log = LoggerFactory.getLogger(ThymeleafConfiguration.class);
@Bean
@Description("Thymeleaf template resolver serving HTML 5 emails")
public ClassLoaderTemplateResolver emailTemplateResolver() {
ClassLoaderTemplateResolver emailTemplateResolver = new ClassLoaderTemplateResolver();
emailTemplateResolver.setPrefix("mails/");
emailTemplateResolver.setSuffix(".html");
emailTemplateResolver.setTemplateMode("HTML5");
emailTemplateResolver.setCharacterEncoding(CharEncoding.UTF_8);
emailTemplateResolver.setOrder(1);
return emailTemplateResolver;
}
}
@@ -0,0 +1,186 @@
package com.baeldung.config;
import io.github.jhipster.config.JHipsterConstants;
import io.github.jhipster.config.JHipsterProperties;
import io.github.jhipster.web.filter.CachingHttpHeadersFilter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.servlet.InstrumentedFilter;
import com.codahale.metrics.servlets.MetricsServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.embedded.*;
import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory;
import io.undertow.UndertowOptions;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.io.File;
import java.nio.file.Paths;
import java.util.*;
import javax.servlet.*;
/**
* Configuration of web application with Servlet 3.0 APIs.
*/
@Configuration
public class WebConfigurer implements ServletContextInitializer, EmbeddedServletContainerCustomizer {
private final Logger log = LoggerFactory.getLogger(WebConfigurer.class);
private final Environment env;
private final JHipsterProperties jHipsterProperties;
private MetricRegistry metricRegistry;
public WebConfigurer(Environment env, JHipsterProperties jHipsterProperties) {
this.env = env;
this.jHipsterProperties = jHipsterProperties;
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
if (env.getActiveProfiles().length != 0) {
log.info("Web application configuration, using profiles: {}", (Object[]) env.getActiveProfiles());
}
EnumSet<DispatcherType> disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC);
initMetrics(servletContext, disps);
if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) {
initCachingHttpHeadersFilter(servletContext, disps);
}
if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)) {
initH2Console(servletContext);
}
log.info("Web application fully configured");
}
/**
* Customize the Servlet engine: Mime types, the document root, the cache.
*/
@Override
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");
// CloudFoundry issue, see https://github.com/cloudfoundry/gorouter/issues/64
mappings.add("json", "text/html;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);
/*
* Enable HTTP/2 for Undertow - https://twitter.com/ankinson/status/829256167700492288
* HTTP/2 requires HTTPS, so HTTP requests will fallback to HTTP/1.1.
* See the JHipsterProperties class and your application-*.yml configuration files
* for more information.
*/
if (jHipsterProperties.getHttp().getVersion().equals(JHipsterProperties.Http.Version.V_2_0) &&
container instanceof UndertowEmbeddedServletContainerFactory) {
((UndertowEmbeddedServletContainerFactory) container)
.addBuilderCustomizers(builder ->
builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true));
}
}
private void setLocationForStaticAssets(ConfigurableEmbeddedServletContainer container) {
File root;
String prefixPath = resolvePathPrefix();
root = new File(prefixPath + "target/www/");
if (root.exists() && root.isDirectory()) {
container.setDocumentRoot(root);
}
}
/**
* 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) {
return "";
}
return extractedPath.substring(0, extractionEndIndex);
}
/**
* Initializes the caching HTTP Headers Filter.
*/
private void initCachingHttpHeadersFilter(ServletContext servletContext,
EnumSet<DispatcherType> disps) {
log.debug("Registering Caching HTTP Headers Filter");
FilterRegistration.Dynamic cachingHttpHeadersFilter =
servletContext.addFilter("cachingHttpHeadersFilter",
new CachingHttpHeadersFilter(jHipsterProperties));
cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/content/*");
cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/app/*");
cachingHttpHeadersFilter.setAsyncSupported(true);
}
/**
* Initializes Metrics.
*/
private void initMetrics(ServletContext servletContext, EnumSet<DispatcherType> disps) {
log.debug("Initializing Metrics registries");
servletContext.setAttribute(InstrumentedFilter.REGISTRY_ATTRIBUTE,
metricRegistry);
servletContext.setAttribute(MetricsServlet.METRICS_REGISTRY,
metricRegistry);
log.debug("Registering Metrics Filter");
FilterRegistration.Dynamic metricsFilter = servletContext.addFilter("webappMetricsFilter",
new InstrumentedFilter());
metricsFilter.addMappingForUrlPatterns(disps, true, "/*");
metricsFilter.setAsyncSupported(true);
log.debug("Registering Metrics Servlet");
ServletRegistration.Dynamic metricsAdminServlet =
servletContext.addServlet("metricsServlet", new MetricsServlet());
metricsAdminServlet.addMapping("/management/metrics/*");
metricsAdminServlet.setAsyncSupported(true);
metricsAdminServlet.setLoadOnStartup(2);
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = jHipsterProperties.getCors();
if (config.getAllowedOrigins() != null && !config.getAllowedOrigins().isEmpty()) {
log.debug("Registering CORS filter");
source.registerCorsConfiguration("/api/**", config);
source.registerCorsConfiguration("/v2/api-docs", config);
}
return new CorsFilter(source);
}
/**
* Initializes H2 console.
*/
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);
}
@Autowired(required = false)
public void setMetricRegistry(MetricRegistry metricRegistry) {
this.metricRegistry = metricRegistry;
}
}
@@ -0,0 +1,91 @@
package com.baeldung.config.audit;
import com.baeldung.domain.PersistentAuditEvent;
import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.time.ZoneId;
import java.util.*;
@Component
public class AuditEventConverter {
/**
* Convert a list of PersistentAuditEvent to a list of AuditEvent
*
* @param persistentAuditEvents the list to convert
* @return the converted list.
*/
public List<AuditEvent> convertToAuditEvent(Iterable<PersistentAuditEvent> persistentAuditEvents) {
if (persistentAuditEvents == null) {
return Collections.emptyList();
}
List<AuditEvent> auditEvents = new ArrayList<>();
for (PersistentAuditEvent persistentAuditEvent : persistentAuditEvents) {
auditEvents.add(convertToAuditEvent(persistentAuditEvent));
}
return auditEvents;
}
/**
* Convert a PersistentAuditEvent to an AuditEvent
*
* @param persistentAuditEvent the event to convert
* @return the converted list.
*/
public AuditEvent convertToAuditEvent(PersistentAuditEvent persistentAuditEvent) {
Instant instant = persistentAuditEvent.getAuditEventDate().atZone(ZoneId.systemDefault()).toInstant();
return new AuditEvent(Date.from(instant), persistentAuditEvent.getPrincipal(),
persistentAuditEvent.getAuditEventType(), convertDataToObjects(persistentAuditEvent.getData()));
}
/**
* Internal conversion. This is needed to support the current SpringBoot actuator AuditEventRepository interface
*
* @param data the data to convert
* @return a map of String, Object
*/
public Map<String, Object> convertDataToObjects(Map<String, String> data) {
Map<String, Object> results = new HashMap<>();
if (data != null) {
for (Map.Entry<String, String> entry : data.entrySet()) {
results.put(entry.getKey(), entry.getValue());
}
}
return results;
}
/**
* Internal conversion. This method will allow to save additional data.
* By default, it will save the object as string
*
* @param data the data to convert
* @return a map of String, String
*/
public Map<String, String> convertDataToStrings(Map<String, Object> data) {
Map<String, String> results = new HashMap<>();
if (data != null) {
for (Map.Entry<String, Object> entry : data.entrySet()) {
Object object = entry.getValue();
// Extract the data that will be saved.
if (object instanceof WebAuthenticationDetails) {
WebAuthenticationDetails authenticationDetails = (WebAuthenticationDetails) object;
results.put("remoteAddress", authenticationDetails.getRemoteAddress());
results.put("sessionId", authenticationDetails.getSessionId());
} else if (object != null) {
results.put(entry.getKey(), object.toString());
} else {
results.put(entry.getKey(), "null");
}
}
}
return results;
}
}
@@ -0,0 +1,4 @@
/**
* Audit specific code.
*/
package com.baeldung.config.audit;
@@ -0,0 +1,4 @@
/**
* Spring Framework configuration files.
*/
package com.baeldung.config;
@@ -0,0 +1,80 @@
package com.baeldung.domain;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.hibernate.envers.Audited;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import java.time.ZonedDateTime;
import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
/**
* Base abstract class for entities which will hold definitions for created, last modified by and created,
* last modified by date.
*/
@MappedSuperclass
@Audited
@EntityListeners(AuditingEntityListener.class)
public abstract class AbstractAuditingEntity implements Serializable {
private static final long serialVersionUID = 1L;
@CreatedBy
@Column(name = "created_by", nullable = false, length = 50, updatable = false)
@JsonIgnore
private String createdBy;
@CreatedDate
@Column(name = "created_date", nullable = false)
@JsonIgnore
private ZonedDateTime createdDate = ZonedDateTime.now();
@LastModifiedBy
@Column(name = "last_modified_by", length = 50)
@JsonIgnore
private String lastModifiedBy;
@LastModifiedDate
@Column(name = "last_modified_date")
@JsonIgnore
private ZonedDateTime lastModifiedDate = ZonedDateTime.now();
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public ZonedDateTime getCreatedDate() {
return createdDate;
}
public void setCreatedDate(ZonedDateTime createdDate) {
this.createdDate = createdDate;
}
public String getLastModifiedBy() {
return lastModifiedBy;
}
public void setLastModifiedBy(String lastModifiedBy) {
this.lastModifiedBy = lastModifiedBy;
}
public ZonedDateTime getLastModifiedDate() {
return lastModifiedDate;
}
public void setLastModifiedDate(ZonedDateTime lastModifiedDate) {
this.lastModifiedDate = lastModifiedDate;
}
}
@@ -0,0 +1,66 @@
package com.baeldung.domain;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Column;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
/**
* An authority (a security role) used by Spring Security.
*/
@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)
@Id
@Column(length = 50)
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Authority authority = (Authority) o;
if (name != null ? !name.equals(authority.name) : authority.name != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
return name != null ? name.hashCode() : 0;
}
@Override
public String toString() {
return "Authority{" +
"name='" + name + '\'' +
"}";
}
}
@@ -0,0 +1,114 @@
package com.baeldung.domain;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import javax.persistence.*;
import javax.validation.constraints.*;
import java.io.Serializable;
import java.time.LocalDate;
import java.util.Objects;
/**
* A Comment.
*/
@Entity
@Table(name = "comment")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Comment implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotNull
@Size(min = 10, max = 100)
@Column(name = "text", length = 100, nullable = false)
private String text;
@NotNull
@Column(name = "creation_date", nullable = false)
private LocalDate creationDate;
@ManyToOne(optional = false)
@NotNull
private Post post;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getText() {
return text;
}
public Comment text(String text) {
this.text = text;
return this;
}
public void setText(String text) {
this.text = text;
}
public LocalDate getCreationDate() {
return creationDate;
}
public Comment creationDate(LocalDate creationDate) {
this.creationDate = creationDate;
return this;
}
public void setCreationDate(LocalDate creationDate) {
this.creationDate = creationDate;
}
public Post getPost() {
return post;
}
public Comment post(Post post) {
this.post = post;
return this;
}
public void setPost(Post post) {
this.post = post;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Comment comment = (Comment) o;
if (comment.id == null || id == null) {
return false;
}
return Objects.equals(id, comment.id);
}
@Override
public int hashCode() {
return Objects.hashCode(id);
}
@Override
public String toString() {
return "Comment{" +
"id=" + id +
", text='" + text + "'" +
", creationDate='" + creationDate + "'" +
'}';
}
}
@@ -0,0 +1,78 @@
package com.baeldung.domain;
import java.io.Serializable;
import java.time.LocalDateTime;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.Map;
/**
* Persist AuditEvent managed by the Spring Boot actuator
* @see org.springframework.boot.actuate.audit.AuditEvent
*/
@Entity
@Table(name = "jhi_persistent_audit_event")
public class PersistentAuditEvent implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "event_id")
private Long id;
@NotNull
@Column(nullable = false)
private String principal;
@Column(name = "event_date")
private LocalDateTime auditEventDate;
@Column(name = "event_type")
private String auditEventType;
@ElementCollection
@MapKeyColumn(name = "name")
@Column(name = "value")
@CollectionTable(name = "jhi_persistent_audit_evt_data", joinColumns=@JoinColumn(name="event_id"))
private Map<String, String> data = new HashMap<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getPrincipal() {
return principal;
}
public void setPrincipal(String principal) {
this.principal = principal;
}
public LocalDateTime getAuditEventDate() {
return auditEventDate;
}
public void setAuditEventDate(LocalDateTime auditEventDate) {
this.auditEventDate = auditEventDate;
}
public String getAuditEventType() {
return auditEventType;
}
public void setAuditEventType(String auditEventType) {
this.auditEventType = auditEventType;
}
public Map<String, String> getData() {
return data;
}
public void setData(Map<String, String> data) {
this.data = data;
}
}
@@ -0,0 +1,133 @@
package com.baeldung.domain;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import javax.persistence.*;
import javax.validation.constraints.*;
import java.io.Serializable;
import java.time.LocalDate;
import java.util.Objects;
/**
* A Post.
*/
@Entity
@Table(name = "post")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Post implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotNull
@Size(min = 10, max = 100)
@Column(name = "title", length = 100, nullable = false)
private String title;
@NotNull
@Size(min = 10, max = 1000)
@Column(name = "content", length = 1000, nullable = false)
private String content;
@NotNull
@Column(name = "creation_date", nullable = false)
private LocalDate creationDate;
@ManyToOne(optional = false)
@NotNull
private User creator;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public Post title(String title) {
this.title = title;
return this;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public Post content(String content) {
this.content = content;
return this;
}
public void setContent(String content) {
this.content = content;
}
public LocalDate getCreationDate() {
return creationDate;
}
public Post creationDate(LocalDate creationDate) {
this.creationDate = creationDate;
return this;
}
public void setCreationDate(LocalDate creationDate) {
this.creationDate = creationDate;
}
public User getCreator() {
return creator;
}
public Post creator(User user) {
this.creator = user;
return this;
}
public void setCreator(User user) {
this.creator = user;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Post post = (Post) o;
if (post.id == null || id == null) {
return false;
}
return Objects.equals(id, post.id);
}
@Override
public int hashCode() {
return Objects.hashCode(id);
}
@Override
public String toString() {
return "Post{" +
"id=" + id +
", title='" + title + "'" +
", content='" + content + "'" +
", creationDate='" + creationDate + "'" +
'}';
}
}
@@ -0,0 +1,235 @@
package com.baeldung.domain;
import com.baeldung.config.Constants;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.validator.constraints.Email;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.time.ZonedDateTime;
/**
* A user.
*/
@Entity
@Table(name = "jhi_user")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class User extends AbstractAuditingEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotNull
@Pattern(regexp = Constants.LOGIN_REGEX)
@Size(min = 1, max = 50)
@Column(length = 50, unique = true, nullable = false)
private String login;
@JsonIgnore
@NotNull
@Size(min = 60, max = 60)
@Column(name = "password_hash",length = 60)
private String password;
@Size(max = 50)
@Column(name = "first_name", length = 50)
private String firstName;
@Size(max = 50)
@Column(name = "last_name", length = 50)
private String lastName;
@Email
@Size(max = 100)
@Column(length = 100, unique = true)
private String email;
@NotNull
@Column(nullable = false)
private boolean activated = false;
@Size(min = 2, max = 5)
@Column(name = "lang_key", length = 5)
private String langKey;
@Size(max = 256)
@Column(name = "image_url", length = 256)
private String imageUrl;
@Size(max = 20)
@Column(name = "activation_key", length = 20)
@JsonIgnore
private String activationKey;
@Size(max = 20)
@Column(name = "reset_key", length = 20)
private String resetKey;
@Column(name = "reset_date")
private ZonedDateTime resetDate = null;
@JsonIgnore
@ManyToMany
@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<Authority> authorities = new HashSet<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getLogin() {
return login;
}
//Lowercase the login before saving it in database
public void setLogin(String login) {
this.login = login.toLowerCase(Locale.ENGLISH);
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
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 getActivated() {
return activated;
}
public void setActivated(boolean activated) {
this.activated = activated;
}
public String getActivationKey() {
return activationKey;
}
public void setActivationKey(String activationKey) {
this.activationKey = activationKey;
}
public String getResetKey() {
return resetKey;
}
public void setResetKey(String resetKey) {
this.resetKey = resetKey;
}
public ZonedDateTime getResetDate() {
return resetDate;
}
public void setResetDate(ZonedDateTime resetDate) {
this.resetDate = resetDate;
}
public String getLangKey() {
return langKey;
}
public void setLangKey(String langKey) {
this.langKey = langKey;
}
public Set<Authority> getAuthorities() {
return authorities;
}
public void setAuthorities(Set<Authority> authorities) {
this.authorities = authorities;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
User user = (User) o;
if (!login.equals(user.login)) {
return false;
}
return true;
}
@Override
public int hashCode() {
return login.hashCode();
}
@Override
public String toString() {
return "User{" +
"login='" + login + '\'' +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", imageUrl='" + imageUrl + '\'' +
", activated='" + activated + '\'' +
", langKey='" + langKey + '\'' +
", activationKey='" + activationKey + '\'' +
"}";
}
}
@@ -0,0 +1,4 @@
/**
* JPA domain objects.
*/
package com.baeldung.domain;
@@ -0,0 +1,11 @@
package com.baeldung.repository;
import com.baeldung.domain.Authority;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* Spring Data JPA repository for the Authority entity.
*/
public interface AuthorityRepository extends JpaRepository<Authority, String> {
}
@@ -0,0 +1,15 @@
package com.baeldung.repository;
import com.baeldung.domain.Comment;
import org.springframework.data.jpa.repository.*;
import java.util.List;
/**
* Spring Data JPA repository for the Comment entity.
*/
@SuppressWarnings("unused")
public interface CommentRepository extends JpaRepository<Comment,Long> {
}
@@ -0,0 +1,81 @@
package com.baeldung.repository;
import com.baeldung.config.Constants;
import com.baeldung.config.audit.AuditEventConverter;
import com.baeldung.domain.PersistentAuditEvent;
import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.boot.actuate.audit.AuditEventRepository;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
/**
* An implementation of Spring Boot's AuditEventRepository.
*/
@Repository
public class CustomAuditEventRepository implements AuditEventRepository {
private static final String AUTHORIZATION_FAILURE = "AUTHORIZATION_FAILURE";
private final PersistenceAuditEventRepository persistenceAuditEventRepository;
private final AuditEventConverter auditEventConverter;
public CustomAuditEventRepository(PersistenceAuditEventRepository persistenceAuditEventRepository,
AuditEventConverter auditEventConverter) {
this.persistenceAuditEventRepository = persistenceAuditEventRepository;
this.auditEventConverter = auditEventConverter;
}
@Override
public List<AuditEvent> find(Date after) {
Iterable<PersistentAuditEvent> persistentAuditEvents =
persistenceAuditEventRepository.findByAuditEventDateAfter(LocalDateTime.from(after.toInstant()));
return auditEventConverter.convertToAuditEvent(persistentAuditEvents);
}
@Override
public List<AuditEvent> find(String principal, Date after) {
Iterable<PersistentAuditEvent> persistentAuditEvents;
if (principal == null && after == null) {
persistentAuditEvents = persistenceAuditEventRepository.findAll();
} else if (after == null) {
persistentAuditEvents = persistenceAuditEventRepository.findByPrincipal(principal);
} else {
persistentAuditEvents =
persistenceAuditEventRepository.findByPrincipalAndAuditEventDateAfter(principal, LocalDateTime.from(after.toInstant()));
}
return auditEventConverter.convertToAuditEvent(persistentAuditEvents);
}
@Override
public List<AuditEvent> find(String principal, Date after, String type) {
Iterable<PersistentAuditEvent> persistentAuditEvents =
persistenceAuditEventRepository.findByPrincipalAndAuditEventDateAfterAndAuditEventType(principal, LocalDateTime.from(after.toInstant()), type);
return auditEventConverter.convertToAuditEvent(persistentAuditEvents);
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void add(AuditEvent event) {
if (!AUTHORIZATION_FAILURE.equals(event.getType()) &&
!Constants.ANONYMOUS_USER.equals(event.getPrincipal())) {
PersistentAuditEvent persistentAuditEvent = new PersistentAuditEvent();
persistentAuditEvent.setPrincipal(event.getPrincipal());
persistentAuditEvent.setAuditEventType(event.getType());
Instant instant = Instant.ofEpochMilli(event.getTimestamp().getTime());
persistentAuditEvent.setAuditEventDate(LocalDateTime.ofInstant(instant, ZoneId.systemDefault()));
persistentAuditEvent.setData(auditEventConverter.convertDataToStrings(event.getData()));
persistenceAuditEventRepository.save(persistentAuditEvent);
}
}
}
@@ -0,0 +1,26 @@
package com.baeldung.repository;
import com.baeldung.domain.PersistentAuditEvent;
import java.time.LocalDateTime;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.List;
/**
* Spring Data JPA repository for the PersistentAuditEvent entity.
*/
public interface PersistenceAuditEventRepository extends JpaRepository<PersistentAuditEvent, Long> {
List<PersistentAuditEvent> findByPrincipal(String principal);
List<PersistentAuditEvent> findByAuditEventDateAfter(LocalDateTime after);
List<PersistentAuditEvent> findByPrincipalAndAuditEventDateAfter(String principal, LocalDateTime after);
List<PersistentAuditEvent> findByPrincipalAndAuditEventDateAfterAndAuditEventType(String principle, LocalDateTime after, String type);
Page<PersistentAuditEvent> findAllByAuditEventDateBetween(LocalDateTime fromDate, LocalDateTime toDate, Pageable pageable);
}
@@ -0,0 +1,18 @@
package com.baeldung.repository;
import com.baeldung.domain.Post;
import org.springframework.data.jpa.repository.*;
import java.util.List;
/**
* Spring Data JPA repository for the Post entity.
*/
@SuppressWarnings("unused")
public interface PostRepository extends JpaRepository<Post,Long> {
@Query("select post from Post post where post.creator.login = ?#{principal.username}")
List<Post> findByCreatorIsCurrentUser();
}
@@ -0,0 +1,37 @@
package com.baeldung.repository;
import com.baeldung.domain.User;
import java.time.ZonedDateTime;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
/**
* Spring Data JPA repository for the User entity.
*/
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findOneByActivationKey(String activationKey);
List<User> findAllByActivatedIsFalseAndCreatedDateBefore(ZonedDateTime dateTime);
Optional<User> findOneByResetKey(String resetKey);
Optional<User> findOneByEmail(String email);
Optional<User> findOneByLogin(String login);
@EntityGraph(attributePaths = "authorities")
User findOneWithAuthoritiesById(Long id);
@EntityGraph(attributePaths = "authorities")
Optional<User> findOneWithAuthoritiesByLogin(String login);
Page<User> findAllByLoginNot(Pageable pageable, String login);
}
@@ -0,0 +1,4 @@
/**
* Spring Data JPA repositories.
*/
package com.baeldung.repository;
@@ -0,0 +1,16 @@
package com.baeldung.security;
/**
* Constants for Spring Security authorities.
*/
public final class AuthoritiesConstants {
public static final String ADMIN = "ROLE_ADMIN";
public static final String USER = "ROLE_USER";
public static final String ANONYMOUS = "ROLE_ANONYMOUS";
private AuthoritiesConstants() {
}
}
@@ -0,0 +1,51 @@
package com.baeldung.security;
import com.baeldung.domain.User;
import com.baeldung.repository.UserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.stream.Collectors;
/**
* Authenticate a user from the database.
*/
@Component("userDetailsService")
public class DomainUserDetailsService implements UserDetailsService {
private final Logger log = LoggerFactory.getLogger(DomainUserDetailsService.class);
private final UserRepository userRepository;
public DomainUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
@Transactional
public UserDetails loadUserByUsername(final String login) {
log.debug("Authenticating {}", login);
String lowercaseLogin = login.toLowerCase(Locale.ENGLISH);
Optional<User> userFromDatabase = userRepository.findOneWithAuthoritiesByLogin(lowercaseLogin);
return userFromDatabase.map(user -> {
if (!user.getActivated()) {
throw new UserNotActivatedException("User " + lowercaseLogin + " was not activated");
}
List<GrantedAuthority> 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"));
}
}
@@ -0,0 +1,68 @@
package com.baeldung.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;
/**
* Utility class for Spring Security.
*/
public final class SecurityUtils {
private SecurityUtils() {
}
/**
* Get the login of the current user.
*
* @return the login of the current user
*/
public static String 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;
}
/**
* Check if a user is authenticated.
*
* @return true if the user is authenticated, false otherwise
*/
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;
}
/**
* If the current user has a specific authority (security role).
*
* <p>The name of this method comes from the isUserInRole() method in the Servlet API</p>
*
* @param authority the authority to check
* @return true if the current user has the authority, false otherwise
*/
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;
}
}
@@ -0,0 +1,19 @@
package com.baeldung.security;
import com.baeldung.config.Constants;
import org.springframework.data.domain.AuditorAware;
import org.springframework.stereotype.Component;
/**
* Implementation of AuditorAware based on Spring Security.
*/
@Component
public class SpringSecurityAuditorAware implements AuditorAware<String> {
@Override
public String getCurrentAuditor() {
String userName = SecurityUtils.getCurrentUserLogin();
return userName != null ? userName : Constants.SYSTEM_ACCOUNT;
}
}
@@ -0,0 +1,19 @@
package com.baeldung.security;
import org.springframework.security.core.AuthenticationException;
/**
* This exception is thrown in case of a not activated user trying to authenticate.
*/
public class UserNotActivatedException extends AuthenticationException {
private static final long serialVersionUID = 1L;
public UserNotActivatedException(String message) {
super(message);
}
public UserNotActivatedException(String message, Throwable t) {
super(message, t);
}
}
@@ -0,0 +1,23 @@
package com.baeldung.security.jwt;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
public class JWTConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
public static final String AUTHORIZATION_HEADER = "Authorization";
private TokenProvider tokenProvider;
public JWTConfigurer(TokenProvider tokenProvider) {
this.tokenProvider = tokenProvider;
}
@Override
public void configure(HttpSecurity http) throws Exception {
JWTFilter customFilter = new JWTFilter(tokenProvider);
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
}
}
@@ -0,0 +1,58 @@
package com.baeldung.security.jwt;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.GenericFilterBean;
import io.jsonwebtoken.ExpiredJwtException;
/**
* Filters incoming requests and installs a Spring Security principal if a header corresponding to a valid user is
* found.
*/
public class JWTFilter extends GenericFilterBean {
private final Logger log = LoggerFactory.getLogger(JWTFilter.class);
private TokenProvider tokenProvider;
public JWTFilter(TokenProvider tokenProvider) {
this.tokenProvider = tokenProvider;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
try {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String jwt = resolveToken(httpServletRequest);
if (StringUtils.hasText(jwt) && this.tokenProvider.validateToken(jwt)) {
Authentication authentication = this.tokenProvider.getAuthentication(jwt);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(servletRequest, servletResponse);
} catch (ExpiredJwtException eje) {
log.info("Security exception for user {} - {}",
eje.getClaims().getSubject(), eje.getMessage());
log.trace("Security exception trace: {}", eje);
((HttpServletResponse) servletResponse).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
private String resolveToken(HttpServletRequest request){
String bearerToken = request.getHeader(JWTConfigurer.AUTHORIZATION_HEADER);
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7, bearerToken.length());
}
return null;
}
}
@@ -0,0 +1,109 @@
package com.baeldung.security.jwt;
import io.github.jhipster.config.JHipsterProperties;
import java.util.*;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.*;
@Component
public class TokenProvider {
private final Logger log = LoggerFactory.getLogger(TokenProvider.class);
private static final String AUTHORITIES_KEY = "auth";
private String secretKey;
private long tokenValidityInMilliseconds;
private long tokenValidityInMillisecondsForRememberMe;
private final JHipsterProperties jHipsterProperties;
public TokenProvider(JHipsterProperties jHipsterProperties) {
this.jHipsterProperties = jHipsterProperties;
}
@PostConstruct
public void init() {
this.secretKey =
jHipsterProperties.getSecurity().getAuthentication().getJwt().getSecret();
this.tokenValidityInMilliseconds =
1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSeconds();
this.tokenValidityInMillisecondsForRememberMe =
1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSecondsForRememberMe();
}
public String createToken(Authentication authentication, Boolean rememberMe) {
String authorities = authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(","));
long now = (new Date()).getTime();
Date validity;
if (rememberMe) {
validity = new Date(now + this.tokenValidityInMillisecondsForRememberMe);
} else {
validity = new Date(now + this.tokenValidityInMilliseconds);
}
return Jwts.builder()
.setSubject(authentication.getName())
.claim(AUTHORITIES_KEY, authorities)
.signWith(SignatureAlgorithm.HS512, secretKey)
.setExpiration(validity)
.compact();
}
public Authentication getAuthentication(String token) {
Claims claims = Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token)
.getBody();
Collection<? extends GrantedAuthority> authorities =
Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
User principal = new User(claims.getSubject(), "", authorities);
return new UsernamePasswordAuthenticationToken(principal, "", authorities);
}
public boolean validateToken(String authToken) {
try {
Jwts.parser().setSigningKey(secretKey).parseClaimsJws(authToken);
return true;
} catch (SignatureException e) {
log.info("Invalid JWT signature.");
log.trace("Invalid JWT signature trace: {}", e);
} catch (MalformedJwtException e) {
log.info("Invalid JWT token.");
log.trace("Invalid JWT token trace: {}", e);
} catch (ExpiredJwtException e) {
log.info("Expired JWT token.");
log.trace("Expired JWT token trace: {}", e);
} catch (UnsupportedJwtException e) {
log.info("Unsupported JWT token.");
log.trace("Unsupported JWT token trace: {}", e);
} catch (IllegalArgumentException e) {
log.info("JWT token compact of handler are invalid.");
log.trace("JWT token compact of handler are invalid trace: {}", e);
}
return false;
}
}
@@ -0,0 +1,4 @@
/**
* Spring Security configuration.
*/
package com.baeldung.security;
@@ -0,0 +1,50 @@
package com.baeldung.service;
import com.baeldung.config.audit.AuditEventConverter;
import com.baeldung.repository.PersistenceAuditEventRepository;
import java.time.LocalDateTime;
import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
/**
* Service for managing audit events.
* <p>
* This is the default implementation to support SpringBoot Actuator AuditEventRepository
* </p>
*/
@Service
@Transactional
public class AuditEventService {
private final PersistenceAuditEventRepository persistenceAuditEventRepository;
private final AuditEventConverter auditEventConverter;
public AuditEventService(
PersistenceAuditEventRepository persistenceAuditEventRepository,
AuditEventConverter auditEventConverter) {
this.persistenceAuditEventRepository = persistenceAuditEventRepository;
this.auditEventConverter = auditEventConverter;
}
public Page<AuditEvent> findAll(Pageable pageable) {
return persistenceAuditEventRepository.findAll(pageable)
.map(auditEventConverter::convertToAuditEvent);
}
public Page<AuditEvent> findByDates(LocalDateTime fromDate, LocalDateTime toDate, Pageable pageable) {
return persistenceAuditEventRepository.findAllByAuditEventDateBetween(fromDate, toDate, pageable)
.map(auditEventConverter::convertToAuditEvent);
}
public Optional<AuditEvent> find(Long id) {
return Optional.ofNullable(persistenceAuditEventRepository.findOne(id)).map
(auditEventConverter::convertToAuditEvent);
}
}
@@ -0,0 +1,108 @@
package com.baeldung.service;
import com.baeldung.domain.User;
import io.github.jhipster.config.JHipsterProperties;
import org.apache.commons.lang3.CharEncoding;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring4.SpringTemplateEngine;
import javax.mail.internet.MimeMessage;
import java.util.Locale;
/**
* Service for sending e-mails.
* <p>
* We use the @Async annotation to send e-mails asynchronously.
* </p>
*/
@Service
public class MailService {
private final Logger log = LoggerFactory.getLogger(MailService.class);
private static final String USER = "user";
private static final String BASE_URL = "baseUrl";
private final JHipsterProperties jHipsterProperties;
private final JavaMailSender javaMailSender;
private final MessageSource messageSource;
private final SpringTemplateEngine templateEngine;
public MailService(JHipsterProperties jHipsterProperties, JavaMailSender javaMailSender,
MessageSource messageSource, SpringTemplateEngine templateEngine) {
this.jHipsterProperties = jHipsterProperties;
this.javaMailSender = javaMailSender;
this.messageSource = messageSource;
this.templateEngine = templateEngine;
}
@Async
public void sendEmail(String to, String subject, String content, boolean isMultipart, boolean isHtml) {
log.debug("Send e-mail[multipart '{}' and html '{}'] to '{}' with subject '{}' and content={}",
isMultipart, isHtml, to, subject, content);
// Prepare message using a Spring helper
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
try {
MimeMessageHelper message = new MimeMessageHelper(mimeMessage, isMultipart, CharEncoding.UTF_8);
message.setTo(to);
message.setFrom(jHipsterProperties.getMail().getFrom());
message.setSubject(subject);
message.setText(content, isHtml);
javaMailSender.send(mimeMessage);
log.debug("Sent e-mail to User '{}'", to);
} catch (Exception e) {
log.warn("E-mail could not be sent to user '{}'", to, e);
}
}
@Async
public void sendActivationEmail(User user) {
log.debug("Sending activation e-mail to '{}'", user.getEmail());
Locale locale = Locale.forLanguageTag(user.getLangKey());
Context context = new Context(locale);
context.setVariable(USER, user);
context.setVariable(BASE_URL, jHipsterProperties.getMail().getBaseUrl());
String content = templateEngine.process("activationEmail", context);
String subject = messageSource.getMessage("email.activation.title", null, locale);
sendEmail(user.getEmail(), subject, content, false, true);
}
@Async
public void sendCreationEmail(User user) {
log.debug("Sending creation e-mail to '{}'", user.getEmail());
Locale locale = Locale.forLanguageTag(user.getLangKey());
Context context = new Context(locale);
context.setVariable(USER, user);
context.setVariable(BASE_URL, jHipsterProperties.getMail().getBaseUrl());
String content = templateEngine.process("creationEmail", context);
String subject = messageSource.getMessage("email.activation.title", null, locale);
sendEmail(user.getEmail(), subject, content, false, true);
}
@Async
public void sendPasswordResetMail(User user) {
log.debug("Sending password reset e-mail to '{}'", user.getEmail());
Locale locale = Locale.forLanguageTag(user.getLangKey());
Context context = new Context(locale);
context.setVariable(USER, user);
context.setVariable(BASE_URL, jHipsterProperties.getMail().getBaseUrl());
String content = templateEngine.process("passwordResetEmail", context);
String subject = messageSource.getMessage("email.reset.title", null, locale);
sendEmail(user.getEmail(), subject, content, false, true);
}
}
@@ -0,0 +1,228 @@
package com.baeldung.service;
import com.baeldung.domain.Authority;
import com.baeldung.domain.User;
import com.baeldung.repository.AuthorityRepository;
import com.baeldung.config.Constants;
import com.baeldung.repository.UserRepository;
import com.baeldung.security.AuthoritiesConstants;
import com.baeldung.security.SecurityUtils;
import com.baeldung.service.util.RandomUtil;
import com.baeldung.service.dto.UserDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.ZonedDateTime;
import java.util.*;
/**
* Service class for managing users.
*/
@Service
@Transactional
public class UserService {
private final Logger log = LoggerFactory.getLogger(UserService.class);
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final AuthorityRepository authorityRepository;
public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, AuthorityRepository authorityRepository) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.authorityRepository = authorityRepository;
}
public Optional<User> activateRegistration(String key) {
log.debug("Activating user for activation key {}", key);
return userRepository.findOneByActivationKey(key)
.map(user -> {
// activate given user for the registration key.
user.setActivated(true);
user.setActivationKey(null);
log.debug("Activated user: {}", user);
return user;
});
}
public Optional<User> completePasswordReset(String newPassword, String key) {
log.debug("Reset user password for reset key {}", key);
return userRepository.findOneByResetKey(key)
.filter(user -> {
ZonedDateTime oneDayAgo = ZonedDateTime.now().minusHours(24);
return user.getResetDate().isAfter(oneDayAgo);
})
.map(user -> {
user.setPassword(passwordEncoder.encode(newPassword));
user.setResetKey(null);
user.setResetDate(null);
return user;
});
}
public Optional<User> requestPasswordReset(String mail) {
return userRepository.findOneByEmail(mail)
.filter(User::getActivated)
.map(user -> {
user.setResetKey(RandomUtil.generateResetKey());
user.setResetDate(ZonedDateTime.now());
return user;
});
}
public User createUser(String login, String password, String firstName, String lastName, String email,
String imageUrl, String langKey) {
User newUser = new User();
Authority authority = authorityRepository.findOne(AuthoritiesConstants.USER);
Set<Authority> authorities = new HashSet<>();
String encryptedPassword = passwordEncoder.encode(password);
newUser.setLogin(login);
// 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);
// new user is not active
newUser.setActivated(false);
// new user gets registration key
newUser.setActivationKey(RandomUtil.generateActivationKey());
authorities.add(authority);
newUser.setAuthorities(authorities);
userRepository.save(newUser);
log.debug("Created Information for User: {}", newUser);
return newUser;
}
public User createUser(UserDTO userDTO) {
User user = new User();
user.setLogin(userDTO.getLogin());
user.setFirstName(userDTO.getFirstName());
user.setLastName(userDTO.getLastName());
user.setEmail(userDTO.getEmail());
user.setImageUrl(userDTO.getImageUrl());
if (userDTO.getLangKey() == null) {
user.setLangKey("en"); // default language
} else {
user.setLangKey(userDTO.getLangKey());
}
if (userDTO.getAuthorities() != null) {
Set<Authority> authorities = new HashSet<>();
userDTO.getAuthorities().forEach(
authority -> authorities.add(authorityRepository.findOne(authority))
);
user.setAuthorities(authorities);
}
String encryptedPassword = passwordEncoder.encode(RandomUtil.generatePassword());
user.setPassword(encryptedPassword);
user.setResetKey(RandomUtil.generateResetKey());
user.setResetDate(ZonedDateTime.now());
user.setActivated(true);
userRepository.save(user);
log.debug("Created Information for User: {}", user);
return user;
}
/**
* Update basic information (first name, last name, email, language) for the current user.
*/
public void updateUser(String firstName, String lastName, String email, String langKey) {
userRepository.findOneByLogin(SecurityUtils.getCurrentUserLogin()).ifPresent(user -> {
user.setFirstName(firstName);
user.setLastName(lastName);
user.setEmail(email);
user.setLangKey(langKey);
log.debug("Changed Information for User: {}", user);
});
}
/**
* Update all information for a specific user, and return the modified user.
*/
public Optional<UserDTO> updateUser(UserDTO userDTO) {
return Optional.of(userRepository
.findOne(userDTO.getId()))
.map(user -> {
user.setLogin(userDTO.getLogin());
user.setFirstName(userDTO.getFirstName());
user.setLastName(userDTO.getLastName());
user.setEmail(userDTO.getEmail());
user.setImageUrl(userDTO.getImageUrl());
user.setActivated(userDTO.isActivated());
user.setLangKey(userDTO.getLangKey());
Set<Authority> managedAuthorities = user.getAuthorities();
managedAuthorities.clear();
userDTO.getAuthorities().stream()
.map(authorityRepository::findOne)
.forEach(managedAuthorities::add);
log.debug("Changed Information for User: {}", user);
return user;
})
.map(UserDTO::new);
}
public void deleteUser(String login) {
userRepository.findOneByLogin(login).ifPresent(user -> {
userRepository.delete(user);
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);
});
}
@Transactional(readOnly = true)
public Page<UserDTO> getAllManagedUsers(Pageable pageable) {
return userRepository.findAllByLoginNot(pageable, Constants.ANONYMOUS_USER).map(UserDTO::new);
}
@Transactional(readOnly = true)
public Optional<User> getUserWithAuthoritiesByLogin(String login) {
return userRepository.findOneWithAuthoritiesByLogin(login);
}
@Transactional(readOnly = true)
public User getUserWithAuthorities(Long id) {
return userRepository.findOneWithAuthoritiesById(id);
}
@Transactional(readOnly = true)
public User getUserWithAuthorities() {
return userRepository.findOneWithAuthoritiesByLogin(SecurityUtils.getCurrentUserLogin()).orElse(null);
}
/**
* Not activated users should be automatically deleted after 3 days.
* <p>
* This is scheduled to get fired everyday, at 01:00 (am).
* </p>
*/
@Scheduled(cron = "0 0 1 * * ?")
public void removeNotActivatedUsers() {
ZonedDateTime now = ZonedDateTime.now();
List<User> users = userRepository.findAllByActivatedIsFalseAndCreatedDateBefore(now.minusDays(3));
for (User user : users) {
log.debug("Deleting not activated user {}", user.getLogin());
userRepository.delete(user);
}
}
}
@@ -0,0 +1,167 @@
package com.baeldung.service.dto;
import com.baeldung.config.Constants;
import com.baeldung.domain.Authority;
import com.baeldung.domain.User;
import org.hibernate.validator.constraints.Email;
import javax.validation.constraints.*;
import java.time.ZonedDateTime;
import java.util.Set;
import java.util.stream.Collectors;
/**
* A DTO representing a user, with his authorities.
*/
public class UserDTO {
private Long id;
@Pattern(regexp = Constants.LOGIN_REGEX)
@Size(min = 1, max = 50)
private String login;
@Size(max = 50)
private String firstName;
@Size(max = 50)
private String lastName;
@Email
@Size(min = 5, max = 100)
private String email;
@Size(max = 256)
private String imageUrl;
private boolean activated = false;
@Size(min = 2, max = 5)
private String langKey;
private String createdBy;
private ZonedDateTime createdDate;
private String lastModifiedBy;
private ZonedDateTime lastModifiedDate;
private Set<String> authorities;
public UserDTO() {
// Empty constructor needed for MapStruct.
}
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, ZonedDateTime createdDate, String lastModifiedBy, ZonedDateTime lastModifiedDate,
Set<String> 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;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getEmail() {
return email;
}
public String getImageUrl() {
return imageUrl;
}
public boolean isActivated() {
return activated;
}
public String getLangKey() {
return langKey;
}
public String getCreatedBy() {
return createdBy;
}
public ZonedDateTime getCreatedDate() {
return createdDate;
}
public String getLastModifiedBy() {
return lastModifiedBy;
}
public ZonedDateTime getLastModifiedDate() {
return lastModifiedDate;
}
public void setLastModifiedDate(ZonedDateTime lastModifiedDate) {
this.lastModifiedDate = lastModifiedDate;
}
public Set<String> getAuthorities() {
return authorities;
}
@Override
public String toString() {
return "UserDTO{" +
"login='" + login + '\'' +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", imageUrl='" + imageUrl + '\'' +
", activated=" + activated +
", langKey='" + langKey + '\'' +
", createdBy=" + createdBy +
", createdDate=" + createdDate +
", lastModifiedBy='" + lastModifiedBy + '\'' +
", lastModifiedDate=" + lastModifiedDate +
", authorities=" + authorities +
"}";
}
}
@@ -0,0 +1,4 @@
/**
* Data Transfer Objects.
*/
package com.baeldung.service.dto;
@@ -0,0 +1,55 @@
package com.baeldung.service.mapper;
import com.baeldung.domain.Authority;
import com.baeldung.domain.User;
import com.baeldung.service.dto.UserDTO;
import org.mapstruct.*;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Mapper for the entity User and its DTO UserDTO.
*/
@Mapper(componentModel = "spring", uses = {})
public interface UserMapper {
UserDTO userToUserDTO(User user);
List<UserDTO> usersToUserDTOs(List<User> users);
@Mapping(target = "createdBy", ignore = true)
@Mapping(target = "createdDate", ignore = true)
@Mapping(target = "lastModifiedBy", ignore = true)
@Mapping(target = "lastModifiedDate", ignore = true)
@Mapping(target = "activationKey", ignore = true)
@Mapping(target = "resetKey", ignore = true)
@Mapping(target = "resetDate", ignore = true)
@Mapping(target = "password", ignore = true)
User userDTOToUser(UserDTO userDTO);
List<User> userDTOsToUsers(List<UserDTO> userDTOs);
default User userFromId(Long id) {
if (id == null) {
return null;
}
User user = new User();
user.setId(id);
return user;
}
default Set<String> stringsFromAuthorities (Set<Authority> authorities) {
return authorities.stream().map(Authority::getName)
.collect(Collectors.toSet());
}
default Set<Authority> authoritiesFromStrings(Set<String> strings) {
return strings.stream().map(string -> {
Authority auth = new Authority();
auth.setName(string);
return auth;
}).collect(Collectors.toSet());
}
}
@@ -0,0 +1,4 @@
/**
* MapStruct mappers for mapping domain objects and Data Transfer Objects.
*/
package com.baeldung.service.mapper;
@@ -0,0 +1,4 @@
/**
* Service layer beans.
*/
package com.baeldung.service;
@@ -0,0 +1,41 @@
package com.baeldung.service.util;
import org.apache.commons.lang3.RandomStringUtils;
/**
* Utility class for generating random Strings.
*/
public final class RandomUtil {
private static final int DEF_COUNT = 20;
private RandomUtil() {
}
/**
* Generate a password.
*
* @return the generated password
*/
public static String generatePassword() {
return RandomStringUtils.randomAlphanumeric(DEF_COUNT);
}
/**
* Generate an activation key.
*
* @return the generated activation key
*/
public static String generateActivationKey() {
return RandomStringUtils.randomNumeric(DEF_COUNT);
}
/**
* Generate a reset key.
*
* @return the generated reset key
*/
public static String generateResetKey() {
return RandomStringUtils.randomNumeric(DEF_COUNT);
}
}
@@ -0,0 +1,202 @@
package com.baeldung.web.rest;
import com.codahale.metrics.annotation.Timed;
import com.baeldung.domain.User;
import com.baeldung.repository.UserRepository;
import com.baeldung.security.SecurityUtils;
import com.baeldung.service.MailService;
import com.baeldung.service.UserService;
import com.baeldung.service.dto.UserDTO;
import com.baeldung.web.rest.vm.KeyAndPasswordVM;
import com.baeldung.web.rest.vm.ManagedUserVM;
import com.baeldung.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;
import javax.validation.Valid;
import java.util.*;
/**
* REST controller for managing the current user's account.
*/
@RestController
@RequestMapping("/api")
public class AccountResource {
private final Logger log = LoggerFactory.getLogger(AccountResource.class);
private final UserRepository userRepository;
private final UserService userService;
private final MailService mailService;
public AccountResource(UserRepository userRepository, UserService userService,
MailService mailService) {
this.userRepository = userRepository;
this.userService = userService;
this.mailService = mailService;
}
/**
* 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 e-mail is already in use
*/
@PostMapping(path = "/register",
produces={MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_PLAIN_VALUE})
@Timed
public ResponseEntity registerAccount(@Valid @RequestBody ManagedUserVM managedUserVM) {
HttpHeaders textPlainHeaders = new HttpHeaders();
textPlainHeaders.setContentType(MediaType.TEXT_PLAIN);
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<>("e-mail 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);
})
);
}
/**
* 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
*/
@GetMapping("/activate")
@Timed
public ResponseEntity<String> activateAccount(@RequestParam(value = "key") String key) {
return userService.activateRegistration(key)
.map(user -> new ResponseEntity<String>(HttpStatus.OK))
.orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
}
/**
* GET /authenticate : check if the user is authenticated, and return its login.
*
* @param request the HTTP request
* @return the login if the user is authenticated
*/
@GetMapping("/authenticate")
@Timed
public String isAuthenticated(HttpServletRequest request) {
log.debug("REST request to check if the current user is authenticated");
return request.getRemoteUser();
}
/**
* 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
*/
@GetMapping("/account")
@Timed
public ResponseEntity<UserDTO> getAccount() {
return Optional.ofNullable(userService.getUserWithAuthorities())
.map(user -> new ResponseEntity<>(new UserDTO(user), HttpStatus.OK))
.orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
}
/**
* 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
*/
@PostMapping("/account")
@Timed
public ResponseEntity saveAccount(@Valid @RequestBody UserDTO userDTO) {
Optional<User> existingUser = userRepository.findOneByEmail(userDTO.getEmail());
if (existingUser.isPresent() && (!existingUser.get().getLogin().equalsIgnoreCase(userDTO.getLogin()))) {
return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("user-management", "emailexists", "Email already in use")).body(null);
}
return userRepository
.findOneByLogin(SecurityUtils.getCurrentUserLogin())
.map(u -> {
userService.updateUser(userDTO.getFirstName(), userDTO.getLastName(), userDTO.getEmail(),
userDTO.getLangKey());
return new ResponseEntity(HttpStatus.OK);
})
.orElseGet(() -> new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
}
/**
* 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
*/
@PostMapping(path = "/account/change_password",
produces = MediaType.TEXT_PLAIN_VALUE)
@Timed
public ResponseEntity changePassword(@RequestBody String password) {
if (!checkPasswordLength(password)) {
return new ResponseEntity<>("Incorrect password", HttpStatus.BAD_REQUEST);
}
userService.changePassword(password);
return new ResponseEntity<>(HttpStatus.OK);
}
/**
* POST /account/reset_password/init : Send an e-mail to reset the password of the user
*
* @param mail the mail of the user
* @return the ResponseEntity with status 200 (OK) if the e-mail was sent, or status 400 (Bad Request) if the e-mail address is not registered
*/
@PostMapping(path = "/account/reset_password/init",
produces = MediaType.TEXT_PLAIN_VALUE)
@Timed
public ResponseEntity requestPasswordReset(@RequestBody String mail) {
return userService.requestPasswordReset(mail)
.map(user -> {
mailService.sendPasswordResetMail(user);
return new ResponseEntity<>("e-mail was sent", HttpStatus.OK);
}).orElse(new ResponseEntity<>("e-mail address not registered", HttpStatus.BAD_REQUEST));
}
/**
* 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
*/
@PostMapping(path = "/account/reset_password/finish",
produces = MediaType.TEXT_PLAIN_VALUE)
@Timed
public ResponseEntity<String> finishPasswordReset(@RequestBody KeyAndPasswordVM keyAndPassword) {
if (!checkPasswordLength(keyAndPassword.getNewPassword())) {
return new ResponseEntity<>("Incorrect password", HttpStatus.BAD_REQUEST);
}
return userService.completePasswordReset(keyAndPassword.getNewPassword(), keyAndPassword.getKey())
.map(user -> new ResponseEntity<String>(HttpStatus.OK))
.orElse(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
}
private boolean checkPasswordLength(String password) {
return !StringUtils.isEmpty(password) &&
password.length() >= ManagedUserVM.PASSWORD_MIN_LENGTH &&
password.length() <= ManagedUserVM.PASSWORD_MAX_LENGTH;
}
}
@@ -0,0 +1,76 @@
package com.baeldung.web.rest;
import com.baeldung.service.AuditEventService;
import com.baeldung.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;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.net.URISyntaxException;
import java.time.LocalDate;
import java.util.List;
/**
* REST controller for getting the audit events.
*/
@RestController
@RequestMapping("/management/audits")
public class AuditResource {
private final AuditEventService auditEventService;
public AuditResource(AuditEventService auditEventService) {
this.auditEventService = auditEventService;
}
/**
* 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<List<AuditEvent>> getAll(@ApiParam Pageable pageable) {
Page<AuditEvent> page = auditEventService.findAll(pageable);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/management/audits");
return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
}
/**
* GET /audits : get a page of AuditEvents between the fromDate and toDate.
*
* @param fromDate the start of the time period of AuditEvents to get
* @param toDate the end of the time period of AuditEvents to get
* @param pageable the pagination information
* @return the ResponseEntity with status 200 (OK) and the list of AuditEvents in body
*/
@GetMapping(params = {"fromDate", "toDate"})
public ResponseEntity<List<AuditEvent>> getByDates(
@RequestParam(value = "fromDate") LocalDate fromDate,
@RequestParam(value = "toDate") LocalDate toDate,
@ApiParam Pageable pageable) {
Page<AuditEvent> page = auditEventService.findByDates(fromDate.atTime(0, 0), toDate.atTime(23, 59), pageable);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/management/audits");
return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
}
/**
* GET /audits/:id : get an AuditEvent by id.
*
* @param id the id of the entity to get
* @return the ResponseEntity with status 200 (OK) and the AuditEvent in body, or status 404 (Not Found)
*/
@GetMapping("/{id:.+}")
public ResponseEntity<AuditEvent> get(@PathVariable Long id) {
return ResponseUtil.wrapOrNotFound(auditEventService.find(id));
}
}
@@ -0,0 +1,129 @@
package com.baeldung.web.rest;
import com.codahale.metrics.annotation.Timed;
import com.baeldung.domain.Comment;
import com.baeldung.repository.CommentRepository;
import com.baeldung.web.rest.util.HeaderUtil;
import com.baeldung.web.rest.util.PaginationUtil;
import io.swagger.annotations.ApiParam;
import io.github.jhipster.web.util.ResponseUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Optional;
/**
* REST controller for managing Comment.
*/
@RestController
@RequestMapping("/api")
public class CommentResource {
private final Logger log = LoggerFactory.getLogger(CommentResource.class);
private static final String ENTITY_NAME = "comment";
private final CommentRepository commentRepository;
public CommentResource(CommentRepository commentRepository) {
this.commentRepository = commentRepository;
}
/**
* POST /comments : Create a new comment.
*
* @param comment the comment to create
* @return the ResponseEntity with status 201 (Created) and with body the new comment, or with status 400 (Bad Request) if the comment has already an ID
* @throws URISyntaxException if the Location URI syntax is incorrect
*/
@PostMapping("/comments")
@Timed
public ResponseEntity<Comment> createComment(@Valid @RequestBody Comment comment) throws URISyntaxException {
log.debug("REST request to save Comment : {}", comment);
if (comment.getId() != null) {
return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "idexists", "A new comment cannot already have an ID")).body(null);
}
Comment result = commentRepository.save(comment);
return ResponseEntity.created(new URI("/api/comments/" + result.getId()))
.headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString()))
.body(result);
}
/**
* PUT /comments : Updates an existing comment.
*
* @param comment the comment to update
* @return the ResponseEntity with status 200 (OK) and with body the updated comment,
* or with status 400 (Bad Request) if the comment is not valid,
* or with status 500 (Internal Server Error) if the comment couldnt be updated
* @throws URISyntaxException if the Location URI syntax is incorrect
*/
@PutMapping("/comments")
@Timed
public ResponseEntity<Comment> updateComment(@Valid @RequestBody Comment comment) throws URISyntaxException {
log.debug("REST request to update Comment : {}", comment);
if (comment.getId() == null) {
return createComment(comment);
}
Comment result = commentRepository.save(comment);
return ResponseEntity.ok()
.headers(HeaderUtil.createEntityUpdateAlert(ENTITY_NAME, comment.getId().toString()))
.body(result);
}
/**
* GET /comments : get all the comments.
*
* @param pageable the pagination information
* @return the ResponseEntity with status 200 (OK) and the list of comments in body
* @throws URISyntaxException if there is an error to generate the pagination HTTP headers
*/
@GetMapping("/comments")
@Timed
public ResponseEntity<List<Comment>> getAllComments(@ApiParam Pageable pageable) {
log.debug("REST request to get a page of Comments");
Page<Comment> page = commentRepository.findAll(pageable);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/comments");
return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
}
/**
* GET /comments/:id : get the "id" comment.
*
* @param id the id of the comment to retrieve
* @return the ResponseEntity with status 200 (OK) and with body the comment, or with status 404 (Not Found)
*/
@GetMapping("/comments/{id}")
@Timed
public ResponseEntity<Comment> getComment(@PathVariable Long id) {
log.debug("REST request to get Comment : {}", id);
Comment comment = commentRepository.findOne(id);
return ResponseUtil.wrapOrNotFound(Optional.ofNullable(comment));
}
/**
* DELETE /comments/:id : delete the "id" comment.
*
* @param id the id of the comment to delete
* @return the ResponseEntity with status 200 (OK)
*/
@DeleteMapping("/comments/{id}")
@Timed
public ResponseEntity<Void> deleteComment(@PathVariable Long id) {
log.debug("REST request to delete Comment : {}", id);
commentRepository.delete(id);
return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert(ENTITY_NAME, id.toString())).build();
}
}
@@ -0,0 +1,24 @@
package com.baeldung.web.rest;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Object to return as body in JWT Authentication.
*/
public class JWTToken {
private String idToken;
public JWTToken(String idToken) {
this.idToken = idToken;
}
@JsonProperty("id_token")
public String getIdToken() {
return idToken;
}
public void setIdToken(String idToken) {
this.idToken = idToken;
}
}
@@ -0,0 +1,39 @@
package com.baeldung.web.rest;
import com.baeldung.web.rest.vm.LoggerVM;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import com.codahale.metrics.annotation.Timed;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.stream.Collectors;
/**
* Controller for view and managing Log Level at runtime.
*/
@RestController
@RequestMapping("/management")
public class LogsResource {
@GetMapping("/logs")
@Timed
public List<LoggerVM> getList() {
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
return context.getLoggerList()
.stream()
.map(LoggerVM::new)
.collect(Collectors.toList());
}
@PutMapping("/logs")
@ResponseStatus(HttpStatus.NO_CONTENT)
@Timed
public void changeLevel(@RequestBody LoggerVM jsonLogger) {
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
context.getLogger(jsonLogger.getName()).setLevel(Level.valueOf(jsonLogger.getLevel()));
}
}
@@ -0,0 +1,129 @@
package com.baeldung.web.rest;
import com.codahale.metrics.annotation.Timed;
import com.baeldung.domain.Post;
import com.baeldung.repository.PostRepository;
import com.baeldung.web.rest.util.HeaderUtil;
import com.baeldung.web.rest.util.PaginationUtil;
import io.swagger.annotations.ApiParam;
import io.github.jhipster.web.util.ResponseUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Optional;
/**
* REST controller for managing Post.
*/
@RestController
@RequestMapping("/api")
public class PostResource {
private final Logger log = LoggerFactory.getLogger(PostResource.class);
private static final String ENTITY_NAME = "post";
private final PostRepository postRepository;
public PostResource(PostRepository postRepository) {
this.postRepository = postRepository;
}
/**
* POST /posts : Create a new post.
*
* @param post the post to create
* @return the ResponseEntity with status 201 (Created) and with body the new post, or with status 400 (Bad Request) if the post has already an ID
* @throws URISyntaxException if the Location URI syntax is incorrect
*/
@PostMapping("/posts")
@Timed
public ResponseEntity<Post> createPost(@Valid @RequestBody Post post) throws URISyntaxException {
log.debug("REST request to save Post : {}", post);
if (post.getId() != null) {
return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "idexists", "A new post cannot already have an ID")).body(null);
}
Post result = postRepository.save(post);
return ResponseEntity.created(new URI("/api/posts/" + result.getId()))
.headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString()))
.body(result);
}
/**
* PUT /posts : Updates an existing post.
*
* @param post the post to update
* @return the ResponseEntity with status 200 (OK) and with body the updated post,
* or with status 400 (Bad Request) if the post is not valid,
* or with status 500 (Internal Server Error) if the post couldnt be updated
* @throws URISyntaxException if the Location URI syntax is incorrect
*/
@PutMapping("/posts")
@Timed
public ResponseEntity<Post> updatePost(@Valid @RequestBody Post post) throws URISyntaxException {
log.debug("REST request to update Post : {}", post);
if (post.getId() == null) {
return createPost(post);
}
Post result = postRepository.save(post);
return ResponseEntity.ok()
.headers(HeaderUtil.createEntityUpdateAlert(ENTITY_NAME, post.getId().toString()))
.body(result);
}
/**
* GET /posts : get all the posts.
*
* @param pageable the pagination information
* @return the ResponseEntity with status 200 (OK) and the list of posts in body
* @throws URISyntaxException if there is an error to generate the pagination HTTP headers
*/
@GetMapping("/posts")
@Timed
public ResponseEntity<List<Post>> getAllPosts(@ApiParam Pageable pageable) {
log.debug("REST request to get a page of Posts");
Page<Post> page = postRepository.findAll(pageable);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/posts");
return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
}
/**
* GET /posts/:id : get the "id" post.
*
* @param id the id of the post to retrieve
* @return the ResponseEntity with status 200 (OK) and with body the post, or with status 404 (Not Found)
*/
@GetMapping("/posts/{id}")
@Timed
public ResponseEntity<Post> getPost(@PathVariable Long id) {
log.debug("REST request to get Post : {}", id);
Post post = postRepository.findOne(id);
return ResponseUtil.wrapOrNotFound(Optional.ofNullable(post));
}
/**
* DELETE /posts/:id : delete the "id" post.
*
* @param id the id of the post to delete
* @return the ResponseEntity with status 200 (OK)
*/
@DeleteMapping("/posts/{id}")
@Timed
public ResponseEntity<Void> deletePost(@PathVariable Long id) {
log.debug("REST request to delete Post : {}", id);
postRepository.delete(id);
return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert(ENTITY_NAME, id.toString())).build();
}
}
@@ -0,0 +1,69 @@
package com.baeldung.web.rest;
import com.baeldung.config.DefaultProfileUtil;
import io.github.jhipster.config.JHipsterProperties;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Resource to return information about the currently running Spring profiles.
*/
@RestController
@RequestMapping("/api")
public class ProfileInfoResource {
private final Environment env;
private final JHipsterProperties jHipsterProperties;
public ProfileInfoResource(Environment env, JHipsterProperties jHipsterProperties) {
this.env = env;
this.jHipsterProperties = jHipsterProperties;
}
@GetMapping("/profile-info")
public ProfileInfoVM getActiveProfiles() {
String[] activeProfiles = DefaultProfileUtil.getActiveProfiles(env);
return new ProfileInfoVM(activeProfiles, getRibbonEnv(activeProfiles));
}
private String getRibbonEnv(String[] activeProfiles) {
String[] displayOnActiveProfiles = jHipsterProperties.getRibbon().getDisplayOnActiveProfiles();
if (displayOnActiveProfiles == null) {
return null;
}
List<String> ribbonProfiles = new ArrayList<>(Arrays.asList(displayOnActiveProfiles));
List<String> springBootProfiles = Arrays.asList(activeProfiles);
ribbonProfiles.retainAll(springBootProfiles);
if (!ribbonProfiles.isEmpty()) {
return ribbonProfiles.get(0);
}
return null;
}
class ProfileInfoVM {
private String[] activeProfiles;
private String ribbonEnv;
ProfileInfoVM(String[] activeProfiles, String ribbonEnv) {
this.activeProfiles = activeProfiles;
this.ribbonEnv = ribbonEnv;
}
public String[] getActiveProfiles() {
return activeProfiles;
}
public String getRibbonEnv() {
return ribbonEnv;
}
}
}
@@ -0,0 +1,60 @@
package com.baeldung.web.rest;
import com.baeldung.security.jwt.JWTConfigurer;
import com.baeldung.security.jwt.TokenProvider;
import com.baeldung.web.rest.vm.LoginVM;
import java.util.Collections;
import com.codahale.metrics.annotation.Timed;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
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;
@RestController
@RequestMapping("/api")
public class UserJWTController {
private final Logger log = LoggerFactory.getLogger(UserJWTController.class);
private final TokenProvider tokenProvider;
private final AuthenticationManager authenticationManager;
public UserJWTController(TokenProvider tokenProvider, AuthenticationManager authenticationManager) {
this.tokenProvider = tokenProvider;
this.authenticationManager = authenticationManager;
}
@PostMapping("/authenticate")
@Timed
public ResponseEntity authorize(@Valid @RequestBody LoginVM loginVM, HttpServletResponse response) {
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);
}
}
}
@@ -0,0 +1,187 @@
package com.baeldung.web.rest;
import com.baeldung.config.Constants;
import com.codahale.metrics.annotation.Timed;
import com.baeldung.domain.User;
import com.baeldung.repository.UserRepository;
import com.baeldung.security.AuthoritiesConstants;
import com.baeldung.service.MailService;
import com.baeldung.service.UserService;
import com.baeldung.service.dto.UserDTO;
import com.baeldung.web.rest.vm.ManagedUserVM;
import com.baeldung.web.rest.util.HeaderUtil;
import com.baeldung.web.rest.util.PaginationUtil;
import io.github.jhipster.web.util.ResponseUtil;
import io.swagger.annotations.ApiParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
/**
* REST controller for managing users.
*
* <p>This class accesses the User entity, and needs to fetch its collection of authorities.</p>
* <p>
* For a normal use-case, it would be better to have an eager relationship between User and Authority,
* and send everything to the client side: there would be no View Model and DTO, a lot less code, and an outer-join
* which would be good for performance.
* </p>
* <p>
* We use a View Model and a DTO for 3 reasons:
* <ul>
* <li>We want to keep a lazy association between the user and the authorities, because people will
* quite often do relationships with the user, and we don't want them to get the authorities all
* the time for nothing (for performance reasons). This is the #1 goal: we should not impact our users'
* application because of this use-case.</li>
* <li> Not having an outer join causes n+1 requests to the database. This is not a real issue as
* we have by default a second-level cache. This means on the first HTTP call we do the n+1 requests,
* but then all authorities come from the cache, so in fact it's much better than doing an outer join
* (which will get lots of data from the database, for each HTTP call).</li>
* <li> As this manages users, for security reasons, we'd rather have a DTO layer.</li>
* </ul>
* <p>Another option would be to have a specific JPA entity graph to handle this case.</p>
*/
@RestController
@RequestMapping("/api")
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;
public UserResource(UserRepository userRepository, MailService mailService,
UserService userService) {
this.userRepository = userRepository;
this.mailService = mailService;
this.userService = userService;
}
/**
* POST /users : Creates a new user.
* <p>
* Creates a new user if the login and email are not already used, and sends an
* mail with an activation link.
* The user needs to be activated on creation.
* </p>
*
* @param managedUserVM 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
*/
@PostMapping("/users")
@Timed
@Secured(AuthoritiesConstants.ADMIN)
public ResponseEntity createUser(@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);
} else {
User newUser = userService.createUser(managedUserVM);
mailService.sendCreationEmail(newUser);
return ResponseEntity.created(new URI("/api/users/" + newUser.getLogin()))
.headers(HeaderUtil.createAlert( "userManagement.created", newUser.getLogin()))
.body(newUser);
}
}
/**
* 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
*/
@PutMapping("/users")
@Timed
@Secured(AuthoritiesConstants.ADMIN)
public ResponseEntity<UserDTO> updateUser(@RequestBody ManagedUserVM managedUserVM) {
log.debug("REST request to update User : {}", managedUserVM);
Optional<User> existingUser = userRepository.findOneByEmail(managedUserVM.getEmail());
if (existingUser.isPresent() && (!existingUser.get().getId().equals(managedUserVM.getId()))) {
return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "emailexists", "E-mail already in use")).body(null);
}
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);
}
Optional<UserDTO> updatedUser = userService.updateUser(managedUserVM);
return ResponseUtil.wrapOrNotFound(updatedUser,
HeaderUtil.createAlert("userManagement.updated", managedUserVM.getLogin()));
}
/**
* 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<List<UserDTO>> getAllUsers(@ApiParam Pageable pageable) {
final Page<UserDTO> page = userService.getAllManagedUsers(pageable);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/users");
return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
}
/**
* 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)
*/
@GetMapping("/users/{login:" + Constants.LOGIN_REGEX + "}")
@Timed
public ResponseEntity<UserDTO> getUser(@PathVariable String login) {
log.debug("REST request to get User : {}", login);
return ResponseUtil.wrapOrNotFound(
userService.getUserWithAuthoritiesByLogin(login)
.map(UserDTO::new));
}
/**
* DELETE /users/:login : delete the "login" User.
*
* @param login the login of the user to delete
* @return the ResponseEntity with status 200 (OK)
*/
@DeleteMapping("/users/{login:" + Constants.LOGIN_REGEX + "}")
@Timed
@Secured(AuthoritiesConstants.ADMIN)
public ResponseEntity<Void> deleteUser(@PathVariable String login) {
log.debug("REST request to delete User: {}", login);
userService.deleteUser(login);
return ResponseEntity.ok().headers(HeaderUtil.createAlert( "userManagement.deleted", login)).build();
}
}
@@ -0,0 +1,34 @@
package com.baeldung.web.rest.errors;
/**
* Custom, parameterized exception, which can be translated on the client side.
* For example:
*
* <pre>
* throw new CustomParameterizedException(&quot;myCustomError&quot;, &quot;hello&quot;, &quot;world&quot;);
* </pre>
*
* Can be translated with:
*
* <pre>
* "error.myCustomError" : "The server says {{params[0]}} to {{params[1]}}"
* </pre>
*/
public class CustomParameterizedException extends RuntimeException {
private static final long serialVersionUID = 1L;
private final String message;
private final String[] params;
public CustomParameterizedException(String message, String... params) {
super(message);
this.message = message;
this.params = params;
}
public ParameterizedErrorVM getErrorVM() {
return new ParameterizedErrorVM(message, params);
}
}
@@ -0,0 +1,14 @@
package com.baeldung.web.rest.errors;
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";
private ErrorConstants() {
}
}
@@ -0,0 +1,52 @@
package com.baeldung.web.rest.errors;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* View Model for transferring error message with a list of field errors.
*/
public class ErrorVM implements Serializable {
private static final long serialVersionUID = 1L;
private final String message;
private final String description;
private List<FieldErrorVM> fieldErrors;
public ErrorVM(String message) {
this(message, null);
}
public ErrorVM(String message, String description) {
this.message = message;
this.description = description;
}
public ErrorVM(String message, String description, List<FieldErrorVM> fieldErrors) {
this.message = message;
this.description = description;
this.fieldErrors = fieldErrors;
}
public void add(String objectName, String field, String message) {
if (fieldErrors == null) {
fieldErrors = new ArrayList<>();
}
fieldErrors.add(new FieldErrorVM(objectName, field, message));
}
public String getMessage() {
return message;
}
public String getDescription() {
return description;
}
public List<FieldErrorVM> getFieldErrors() {
return fieldErrors;
}
}
@@ -0,0 +1,85 @@
package com.baeldung.web.rest.errors;
import java.util.List;
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.*;
/**
* Controller advice to translate the server side exceptions to client-friendly json structures.
*/
@ControllerAdvice
public class ExceptionTranslator {
@ExceptionHandler(ConcurrencyFailureException.class)
@ResponseStatus(HttpStatus.CONFLICT)
@ResponseBody
public ErrorVM processConcurrencyError(ConcurrencyFailureException ex) {
return new ErrorVM(ErrorConstants.ERR_CONCURRENCY_FAILURE);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public ErrorVM processValidationError(MethodArgumentNotValidException ex) {
BindingResult result = ex.getBindingResult();
List<FieldError> fieldErrors = result.getFieldErrors();
return processFieldErrors(fieldErrors);
}
@ExceptionHandler(CustomParameterizedException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public ParameterizedErrorVM processParameterizedValidationError(CustomParameterizedException ex) {
return ex.getErrorVM();
}
@ExceptionHandler(AccessDeniedException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
@ResponseBody
public ErrorVM processAccessDeniedException(AccessDeniedException e) {
return new ErrorVM(ErrorConstants.ERR_ACCESS_DENIED, e.getMessage());
}
private ErrorVM processFieldErrors(List<FieldError> fieldErrors) {
ErrorVM dto = new ErrorVM(ErrorConstants.ERR_VALIDATION);
for (FieldError fieldError : fieldErrors) {
dto.add(fieldError.getObjectName(), fieldError.getField(), fieldError.getCode());
}
return dto;
}
@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(Exception.class)
public ResponseEntity<ErrorVM> processRuntimeException(Exception ex) {
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);
}
}
@@ -0,0 +1,33 @@
package com.baeldung.web.rest.errors;
import java.io.Serializable;
public class FieldErrorVM implements Serializable {
private static final long serialVersionUID = 1L;
private final String objectName;
private final String field;
private final String message;
public FieldErrorVM(String dto, String field, String message) {
this.objectName = dto;
this.field = field;
this.message = message;
}
public String getObjectName() {
return objectName;
}
public String getField() {
return field;
}
public String getMessage() {
return message;
}
}
@@ -0,0 +1,27 @@
package com.baeldung.web.rest.errors;
import java.io.Serializable;
/**
* View Model for sending a parameterized error message.
*/
public class ParameterizedErrorVM implements Serializable {
private static final long serialVersionUID = 1L;
private final String message;
private final String[] params;
public ParameterizedErrorVM(String message, String... params) {
this.message = message;
this.params = params;
}
public String getMessage() {
return message;
}
public String[] getParams() {
return params;
}
}
@@ -0,0 +1,4 @@
/**
* Spring MVC REST controllers.
*/
package com.baeldung.web.rest;
@@ -0,0 +1,45 @@
package com.baeldung.web.rest.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
/**
* Utility class for HTTP headers creation.
*/
public final class HeaderUtil {
private static final Logger log = LoggerFactory.getLogger(HeaderUtil.class);
private static final String APPLICATION_NAME = "baeldungApp";
private HeaderUtil() {
}
public static HttpHeaders createAlert(String message, String param) {
HttpHeaders headers = new HttpHeaders();
headers.add("X-baeldungApp-alert", message);
headers.add("X-baeldungApp-params", param);
return headers;
}
public static HttpHeaders createEntityCreationAlert(String entityName, String param) {
return createAlert(APPLICATION_NAME + "." + entityName + ".created", param);
}
public static HttpHeaders createEntityUpdateAlert(String entityName, String param) {
return createAlert(APPLICATION_NAME + "." + entityName + ".updated", param);
}
public static HttpHeaders createEntityDeletionAlert(String entityName, String param) {
return createAlert(APPLICATION_NAME + "." + entityName + ".deleted", param);
}
public static HttpHeaders createFailureAlert(String entityName, String errorKey, String defaultMessage) {
log.error("Entity creation failed, {}", defaultMessage);
HttpHeaders headers = new HttpHeaders();
headers.add("X-baeldungApp-error", "error." + errorKey);
headers.add("X-baeldungApp-params", entityName);
return headers;
}
}
@@ -0,0 +1,47 @@
package com.baeldung.web.rest.util;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpHeaders;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URISyntaxException;
/**
* Utility class for handling pagination.
*
* <p>
* Pagination uses the same principles as the <a href="https://developer.github.com/v3/#pagination">Github API</a>,
* and follow <a href="http://tools.ietf.org/html/rfc5988">RFC 5988 (Link header)</a>.
*/
public final class PaginationUtil {
private PaginationUtil() {
}
public static HttpHeaders generatePaginationHttpHeaders(Page page, String baseUrl) {
HttpHeaders headers = new HttpHeaders();
headers.add("X-Total-Count", "" + Long.toString(page.getTotalElements()));
String link = "";
if ((page.getNumber() + 1) < page.getTotalPages()) {
link = "<" + generateUri(baseUrl, page.getNumber() + 1, page.getSize()) + ">; rel=\"next\",";
}
// prev link
if ((page.getNumber()) > 0) {
link += "<" + generateUri(baseUrl, page.getNumber() - 1, page.getSize()) + ">; rel=\"prev\",";
}
// last and first link
int lastPage = 0;
if (page.getTotalPages() > 0) {
lastPage = page.getTotalPages() - 1;
}
link += "<" + generateUri(baseUrl, lastPage, page.getSize()) + ">; rel=\"last\",";
link += "<" + generateUri(baseUrl, 0, page.getSize()) + ">; rel=\"first\"";
headers.add(HttpHeaders.LINK, link);
return headers;
}
private static String generateUri(String baseUrl, int page, int size) {
return UriComponentsBuilder.fromUriString(baseUrl).queryParam("page", page).queryParam("size", size).toUriString();
}
}
@@ -0,0 +1,27 @@
package com.baeldung.web.rest.vm;
/**
* View Model object for storing the user's key and password.
*/
public class KeyAndPasswordVM {
private String key;
private String newPassword;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getNewPassword() {
return newPassword;
}
public void setNewPassword(String newPassword) {
this.newPassword = newPassword;
}
}
@@ -0,0 +1,48 @@
package com.baeldung.web.rest.vm;
import ch.qos.logback.classic.Logger;
import com.fasterxml.jackson.annotation.JsonCreator;
/**
* View Model object for storing a Logback logger.
*/
public class LoggerVM {
private String name;
private String level;
public LoggerVM(Logger logger) {
this.name = logger.getName();
this.level = logger.getEffectiveLevel().toString();
}
@JsonCreator
public LoggerVM() {
// Empty public constructor used by Jackson.
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
@Override
public String toString() {
return "LoggerVM{" +
"name='" + name + '\'' +
", level='" + level + '\'' +
'}';
}
}
@@ -0,0 +1,55 @@
package com.baeldung.web.rest.vm;
import com.baeldung.config.Constants;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
/**
* View Model object for storing a user's credentials.
*/
public class LoginVM {
@Pattern(regexp = Constants.LOGIN_REGEX)
@NotNull
@Size(min = 1, max = 50)
private String username;
@NotNull
@Size(min = ManagedUserVM.PASSWORD_MIN_LENGTH, max = ManagedUserVM.PASSWORD_MAX_LENGTH)
private String password;
private Boolean rememberMe;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Boolean isRememberMe() {
return rememberMe;
}
public void setRememberMe(Boolean rememberMe) {
this.rememberMe = rememberMe;
}
@Override
public String toString() {
return "LoginVM{" +
"username='" + username + '\'' +
", rememberMe=" + rememberMe +
'}';
}
}
@@ -0,0 +1,45 @@
package com.baeldung.web.rest.vm;
import com.baeldung.service.dto.UserDTO;
import javax.validation.constraints.Size;
import java.time.ZonedDateTime;
import java.util.Set;
/**
* View Model extending the UserDTO, which is meant to be used in the user management UI.
*/
public class ManagedUserVM extends UserDTO {
public static final int PASSWORD_MIN_LENGTH = 4;
public static final int PASSWORD_MAX_LENGTH = 100;
@Size(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH)
private String password;
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, ZonedDateTime createdDate, String lastModifiedBy, ZonedDateTime lastModifiedDate,
Set<String> authorities) {
super(id, login, firstName, lastName, email, activated, imageUrl, langKey,
createdBy, createdDate, lastModifiedBy, lastModifiedDate, authorities);
this.password = password;
}
public String getPassword() {
return password;
}
@Override
public String toString() {
return "ManagedUserVM{" +
"} " + super.toString();
}
}
@@ -0,0 +1,4 @@
/**
* View Models used by Spring MVC REST controllers.
*/
package com.baeldung.web.rest.vm;
@@ -0,0 +1,5 @@
#H2 Server Properties
0=JHipster H2 (Disk)|org.h2.Driver|jdbc\:h2\:file\:./target/h2db/db/baeldung|baeldung
webAllowOthers=true
webPort=8082
webSSL=false
+10
View File
@@ -0,0 +1,10 @@
${AnsiColor.GREEN} ██╗${AnsiColor.RED} ██╗ ██╗ ████████╗ ███████╗ ██████╗ ████████╗ ████████╗ ███████╗
${AnsiColor.GREEN} ██║${AnsiColor.RED} ██║ ██║ ╚══██╔══╝ ██╔═══██╗ ██╔════╝ ╚══██╔══╝ ██╔═════╝ ██╔═══██╗
${AnsiColor.GREEN} ██║${AnsiColor.RED} ████████║ ██║ ███████╔╝ ╚█████╗ ██║ ██████╗ ███████╔╝
${AnsiColor.GREEN}██╗ ██║${AnsiColor.RED} ██╔═══██║ ██║ ██╔════╝ ╚═══██╗ ██║ ██╔═══╝ ██╔══██║
${AnsiColor.GREEN}╚██████╔╝${AnsiColor.RED} ██║ ██║ ████████╗ ██║ ██████╔╝ ██║ ████████╗ ██║ ╚██╗
${AnsiColor.GREEN} ╚═════╝ ${AnsiColor.RED} ╚═╝ ╚═╝ ╚═══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══════╝ ╚═╝ ╚═╝
${AnsiColor.BRIGHT_BLUE}:: JHipster 🤓 :: Running Spring Boot ${spring-boot.version} ::
:: http://jhipster.github.io ::${AnsiColor.DEFAULT}
@@ -0,0 +1,130 @@
# ===================================================================
# Spring Boot configuration for the "dev" profile.
#
# 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/
# ===================================================================
# ===================================================================
# Standard Spring Boot properties.
# Full reference is available at:
# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
# ===================================================================
spring:
profiles:
active: dev
include: swagger
devtools:
restart:
enabled: true
livereload:
enabled: false # we use gulp + BrowserSync for livereload
jackson:
serialization.indent_output: true
datasource:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:h2:file:./target/h2db/db/baeldung;DB_CLOSE_DELAY=-1
username: baeldung
password:
h2:
console:
enabled: false
jpa:
database-platform: io.github.jhipster.domain.util.FixedH2Dialect
database: H2
show-sql: true
properties:
hibernate.id.new_generator_mappings: true
hibernate.cache.use_second_level_cache: true
hibernate.cache.use_query_cache: false
hibernate.generate_statistics: true
hibernate.cache.region.factory_class: io.github.jhipster.config.jcache.NoDefaultJCacheRegionFactory
mail:
host: localhost
port: 25
username:
password:
messages:
cache-seconds: 1
thymeleaf:
cache: false
liquibase:
contexts: dev
# ===================================================================
# To enable SSL, generate a certificate using:
# keytool -genkey -alias baeldung -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650
#
# You can also use Let's Encrypt:
# https://maximilian-boehm.com/hp2121/Create-a-Java-Keystore-JKS-from-Let-s-Encrypt-Certificates.htm
#
# Then, modify the server.ssl properties so your "server" configuration looks like:
#
# server:
# port: 8443
# ssl:
# key-store: keystore.p12
# key-store-password: <your-password>
# keyStoreType: PKCS12
# keyAlias: baeldung
# ===================================================================
server:
port: 8080
# ===================================================================
# JHipster specific properties
#
# Full reference is available at: https://jhipster.github.io/common-application-properties/
# ===================================================================
jhipster:
http:
version: V_1_1 # To use HTTP/2 you will need SSL support (see above the "server.ssl" configuration)
cache: # Cache configuration
ehcache: # Ehcache configuration
time-to-live-seconds: 3600 # By default objects stay 1 hour in the cache
max-entries: 100 # Number of objects in each cache entry
security:
authentication:
jwt:
secret: my-secret-token-to-change-in-production
# Token is valid 24 hours
token-validity-in-seconds: 86400
token-validity-in-seconds-for-remember-me: 2592000
mail: # specific JHipster mail property, for standard properties see MailProperties
from: baeldung@localhost
base-url: http://127.0.0.1:8080
metrics: # DropWizard Metrics configuration, used by MetricsConfiguration
jmx.enabled: true
graphite: # Use the "graphite" Maven profile to have the Graphite dependencies
enabled: false
host: localhost
port: 2003
prefix: baeldung
prometheus: # Use the "prometheus" Maven profile to have the Prometheus dependencies
enabled: false
endpoint: /prometheusMetrics
logs: # Reports Dropwizard metrics in the logs
enabled: false
reportFrequency: 60 # in seconds
logging:
logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration
enabled: false
host: localhost
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/
# ===================================================================
application:
@@ -0,0 +1,132 @@
# ===================================================================
# Spring Boot configuration for the "prod" profile.
#
# 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/
# ===================================================================
# ===================================================================
# Standard Spring Boot properties.
# Full reference is available at:
# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
# ===================================================================
spring:
devtools:
restart:
enabled: false
livereload:
enabled: false
datasource:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://localhost:3306/baeldung?useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password:
hikari:
data-source-properties:
cachePrepStmts: true
prepStmtCacheSize: 250
prepStmtCacheSqlLimit: 2048
useServerPrepStmts: true
jpa:
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
database: MYSQL
show-sql: false
properties:
hibernate.id.new_generator_mappings: true
hibernate.cache.use_second_level_cache: true
hibernate.cache.use_query_cache: false
hibernate.generate_statistics: false
hibernate.cache.region.factory_class: io.github.jhipster.config.jcache.NoDefaultJCacheRegionFactory
mail:
host: localhost
port: 25
username:
password:
thymeleaf:
cache: true
liquibase:
contexts: prod
# ===================================================================
# To enable SSL, generate a certificate using:
# keytool -genkey -alias baeldung -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650
#
# You can also use Let's Encrypt:
# https://maximilian-boehm.com/hp2121/Create-a-Java-Keystore-JKS-from-Let-s-Encrypt-Certificates.htm
#
# Then, modify the server.ssl properties so your "server" configuration looks like:
#
# server:
# port: 443
# ssl:
# key-store: keystore.p12
# key-store-password: <your-password>
# keyStoreType: PKCS12
# keyAlias: baeldung
# ===================================================================
server:
port: 8080
compression:
enabled: true
mime-types: text/html,text/xml,text/plain,text/css, application/javascript, application/json
min-response-size: 1024
# ===================================================================
# JHipster specific properties
#
# Full reference is available at: https://jhipster.github.io/common-application-properties/
# ===================================================================
jhipster:
http:
version: V_1_1 # To use HTTP/2 you will need SSL support (see above the "server.ssl" configuration)
cache: # Used by the CachingHttpHeadersFilter
timeToLiveInDays: 1461
cache: # Cache configuration
ehcache: # Ehcache configuration
time-to-live-seconds: 3600 # By default objects stay 1 hour in the cache
max-entries: 1000 # Number of objects in each cache entry
security:
authentication:
jwt:
secret: e1d4b69d3f953e3fa622121e882e6f459ca20ca4
# Token is valid 24 hours
token-validity-in-seconds: 86400
token-validity-in-seconds-for-remember-me: 2592000
mail: # specific JHipster mail property, for standard properties see MailProperties
from: baeldung@localhost
base-url: http://my-server-url-to-change # Modify according to your server's URL
metrics: # DropWizard Metrics configuration, used by MetricsConfiguration
jmx.enabled: true
graphite:
enabled: false
host: localhost
port: 2003
prefix: baeldung
prometheus:
enabled: false
endpoint: /prometheusMetrics
logs: # Reports Dropwizard metrics in the logs
enabled: false
reportFrequency: 60 # in seconds
logging:
logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration
enabled: false
host: localhost
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/
# ===================================================================
application:
@@ -0,0 +1,106 @@
# ===================================================================
# Spring Boot configuration.
#
# This configuration will be overriden 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/
# ===================================================================
# ===================================================================
# Standard Spring Boot properties.
# Full reference is available at:
# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
# ===================================================================
management:
security:
roles: ADMIN
context-path: /management
health:
mail:
enabled: false # When using the MailService, configure an SMTP server and set this to true
spring:
application:
name: baeldung
profiles:
# The commented value for `active` can be replaced with valid Spring profiles to load.
# Otherwise, it will be filled in by maven when building the WAR file
# Either way, it can be overridden by `--spring.profiles.active` value passed in the commandline or `-Dspring.profiles.active` set in `JAVA_OPTS`
active: #spring.profiles.active#
jackson:
serialization.write_dates_as_timestamps: false
jpa:
open-in-view: false
hibernate:
ddl-auto: none
naming:
physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
messages:
basename: i18n/messages
mvc:
favicon:
enabled: false
thymeleaf:
mode: XHTML
security:
basic:
enabled: false
server:
session:
cookie:
http-only: true
info:
project:
version: #project.version#
# ===================================================================
# JHipster specific properties
#
# Full reference is available at: https://jhipster.github.io/common-application-properties/
# ===================================================================
jhipster:
async:
core-pool-size: 2
max-pool-size: 50
queue-capacity: 10000
# By default CORS is disabled. Uncomment to enable.
#cors:
#allowed-origins: "*"
#allowed-methods: GET, PUT, POST, DELETE, OPTIONS
#allowed-headers: "*"
#exposed-headers:
#allow-credentials: true
#max-age: 1800
mail:
from: baeldung@localhost
swagger:
default-include-pattern: /api/.*
title: baeldung API
description: baeldung API documentation
version: 0.0.1
terms-of-service-url:
contact-name:
contact-url:
contact-email:
license:
license-url:
ribbon:
display-on-active-profiles: dev
# ===================================================================
# 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/
# ===================================================================
application:
@@ -0,0 +1,3 @@
name
ROLE_ADMIN
ROLE_USER
1 name
2 ROLE_ADMIN
3 ROLE_USER
@@ -0,0 +1,149 @@
<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
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
http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
<property name="now" value="now()" dbms="h2"/>
<property name="now" value="now()" dbms="mysql"/>
<property name="autoIncrement" value="true"/>
<!--
JHipster core tables.
The initial schema has the '00000000000001' id, so that it is over-written if we re-generate it.
-->
<changeSet id="00000000000001" author="jhipster">
<createTable tableName="jhi_user">
<column name="id" type="bigint" autoIncrement="${autoIncrement}">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="login" type="varchar(50)">
<constraints unique="true" nullable="false"/>
</column>
<column name="password_hash" type="varchar(60)"/>
<column name="first_name" type="varchar(50)"/>
<column name="last_name" type="varchar(50)"/>
<column name="email" type="varchar(100)">
<constraints unique="true" nullable="true"/>
</column>
<column name="image_url" type="varchar(256)"/>
<column name="activated" type="boolean" valueBoolean="false">
<constraints nullable="false" />
</column>
<column name="lang_key" type="varchar(5)"/>
<column name="activation_key" type="varchar(20)"/>
<column name="reset_key" type="varchar(20)"/>
<column name="created_by" type="varchar(50)">
<constraints nullable="false"/>
</column>
<column name="created_date" type="timestamp" defaultValueDate="${now}">
<constraints nullable="false"/>
</column>
<column name="reset_date" type="timestamp">
<constraints nullable="true"/>
</column>
<column name="last_modified_by" type="varchar(50)"/>
<column name="last_modified_date" type="timestamp"/>
</createTable>
<createIndex indexName="idx_user_login"
tableName="jhi_user"
unique="true">
<column name="login" type="varchar(50)"/>
</createIndex>
<createIndex indexName="idx_user_email"
tableName="jhi_user"
unique="true">
<column name="email" type="varchar(100)"/>
</createIndex>
<createTable tableName="jhi_authority">
<column name="name" type="varchar(50)">
<constraints primaryKey="true" nullable="false"/>
</column>
</createTable>
<createTable tableName="jhi_user_authority">
<column name="user_id" type="bigint">
<constraints nullable="false"/>
</column>
<column name="authority_name" type="varchar(50)">
<constraints nullable="false"/>
</column>
</createTable>
<addPrimaryKey columnNames="user_id, authority_name" tableName="jhi_user_authority"/>
<addForeignKeyConstraint baseColumnNames="authority_name"
baseTableName="jhi_user_authority"
constraintName="fk_authority_name"
referencedColumnNames="name"
referencedTableName="jhi_authority"/>
<addForeignKeyConstraint baseColumnNames="user_id"
baseTableName="jhi_user_authority"
constraintName="fk_user_id"
referencedColumnNames="id"
referencedTableName="jhi_user"/>
<loadData encoding="UTF-8"
file="config/liquibase/users.csv"
separator=";"
tableName="jhi_user">
<column name="activated" type="boolean"/>
<column name="created_date" type="timestamp"/>
</loadData>
<dropDefaultValue tableName="jhi_user" columnName="created_date" columnDataType="datetime"/>
<loadData encoding="UTF-8"
file="config/liquibase/authorities.csv"
separator=";"
tableName="jhi_authority"/>
<loadData encoding="UTF-8"
file="config/liquibase/users_authorities.csv"
separator=";"
tableName="jhi_user_authority"/>
<createTable tableName="jhi_persistent_audit_event">
<column name="event_id" type="bigint" autoIncrement="${autoIncrement}">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="principal" type="varchar(50)">
<constraints nullable="false" />
</column>
<column name="event_date" type="timestamp"/>
<column name="event_type" type="varchar(255)"/>
</createTable>
<createTable tableName="jhi_persistent_audit_evt_data">
<column name="event_id" type="bigint">
<constraints nullable="false"/>
</column>
<column name="name" type="varchar(150)">
<constraints nullable="false"/>
</column>
<column name="value" type="varchar(255)"/>
</createTable>
<addPrimaryKey columnNames="event_id, name" tableName="jhi_persistent_audit_evt_data"/>
<createIndex indexName="idx_persistent_audit_event"
tableName="jhi_persistent_audit_event"
unique="false">
<column name="principal" type="varchar(50)"/>
<column name="event_date" type="timestamp"/>
</createIndex>
<createIndex indexName="idx_persistent_audit_evt_data"
tableName="jhi_persistent_audit_evt_data"
unique="false">
<column name="event_id" type="bigint"/>
</createIndex>
<addForeignKeyConstraint baseColumnNames="event_id"
baseTableName="jhi_persistent_audit_evt_data"
constraintName="fk_evt_pers_audit_evt_data"
referencedColumnNames="event_id"
referencedTableName="jhi_persistent_audit_event"/>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
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
http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
<property name="now" value="now()" dbms="h2"/>
<property name="now" value="now()" dbms="mysql"/>
<property name="autoIncrement" value="true"/>
<property name="floatType" value="float4" dbms="postgresql, h2"/>
<property name="floatType" value="float" dbms="mysql, oracle, mssql"/>
<!--
Added the entity Post.
-->
<changeSet id="20170316223211-1" author="jhipster">
<createTable tableName="post">
<column name="id" type="bigint" autoIncrement="${autoIncrement}">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="title" type="varchar(100)">
<constraints nullable="false" />
</column>
<column name="content" type="varchar(1000)">
<constraints nullable="false" />
</column>
<column name="creation_date" type="date">
<constraints nullable="false" />
</column>
<column name="creator_id" type="bigint">
<constraints nullable="false" />
</column>
<!-- jhipster-needle-liquibase-add-column - JHipster will add columns here, do not remove-->
</createTable>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
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">
<!--
Added the constraints for entity Post.
-->
<changeSet id="20170316223211-2" author="jhipster">
<addForeignKeyConstraint baseColumnNames="creator_id"
baseTableName="post"
constraintName="fk_post_creator_id"
referencedColumnNames="id"
referencedTableName="jhi_user"/>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
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
http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
<property name="now" value="now()" dbms="h2"/>
<property name="now" value="now()" dbms="mysql"/>
<property name="autoIncrement" value="true"/>
<property name="floatType" value="float4" dbms="postgresql, h2"/>
<property name="floatType" value="float" dbms="mysql, oracle, mssql"/>
<!--
Added the entity Comment.
-->
<changeSet id="20170316224021-1" author="jhipster">
<createTable tableName="comment">
<column name="id" type="bigint" autoIncrement="${autoIncrement}">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="text" type="varchar(100)">
<constraints nullable="false" />
</column>
<column name="creation_date" type="date">
<constraints nullable="false" />
</column>
<column name="post_id" type="bigint">
<constraints nullable="false" />
</column>
<!-- jhipster-needle-liquibase-add-column - JHipster will add columns here, do not remove-->
</createTable>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
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">
<!--
Added the constraints for entity Comment.
-->
<changeSet id="20170316224021-2" author="jhipster">
<addForeignKeyConstraint baseColumnNames="post_id"
baseTableName="comment"
constraintName="fk_comment_post_id"
referencedColumnNames="id"
referencedTableName="post"/>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
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">
<include file="classpath:config/liquibase/changelog/00000000000000_initial_schema.xml" relativeToChangelogFile="false"/>
<include file="classpath:config/liquibase/changelog/20170316223211_added_entity_Post.xml" relativeToChangelogFile="false"/>
<include file="classpath:config/liquibase/changelog/20170316224021_added_entity_Comment.xml" relativeToChangelogFile="false"/>
<!-- jhipster-needle-liquibase-add-changelog - JHipster will add liquibase changelogs here -->
<include file="classpath:config/liquibase/changelog/20170316223211_added_entity_constraints_Post.xml" relativeToChangelogFile="false"/>
<include file="classpath:config/liquibase/changelog/20170316224021_added_entity_constraints_Comment.xml" relativeToChangelogFile="false"/>
<!-- jhipster-needle-liquibase-add-constraints-changelog - JHipster will add liquibase constraints changelogs here -->
</databaseChangeLog>
@@ -0,0 +1,5 @@
id;login;password_hash;first_name;last_name;email;image_url;activated;lang_key;created_by;last_modified_by
1;system;$2a$10$mE.qmcV0mFU5NcKh73TZx.z4ueI/.bDWbj0T1BYyqP481kGGarKLG;System;System;system@localhost;;true;en;system;system
2;anonymoususer;$2a$10$j8S5d7Sr7.8VTOYNviDPOeWX8KcYILUVJBsYV83Y5NtECayypx9lO;Anonymous;User;anonymous@localhost;;true;en;system;system
3;admin;$2a$10$gSAhZrxMllrbgj/kkK9UceBPpChGWJA7SYIb1Mqo.n5aNLq1/oRrC;Administrator;Administrator;admin@localhost;;true;en;system;system
4;user;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;User;User;user@localhost;;true;en;system;system
1 id login password_hash first_name last_name email image_url activated lang_key created_by last_modified_by
2 1 system $2a$10$mE.qmcV0mFU5NcKh73TZx.z4ueI/.bDWbj0T1BYyqP481kGGarKLG System System system@localhost true en system system
3 2 anonymoususer $2a$10$j8S5d7Sr7.8VTOYNviDPOeWX8KcYILUVJBsYV83Y5NtECayypx9lO Anonymous User anonymous@localhost true en system system
4 3 admin $2a$10$gSAhZrxMllrbgj/kkK9UceBPpChGWJA7SYIb1Mqo.n5aNLq1/oRrC Administrator Administrator admin@localhost true en system system
5 4 user $2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K User User user@localhost true en system system
@@ -0,0 +1,6 @@
user_id;authority_name
1;ROLE_ADMIN
1;ROLE_USER
3;ROLE_ADMIN
3;ROLE_USER
4;ROLE_USER
1 user_id authority_name
2 1 ROLE_ADMIN
3 1 ROLE_USER
4 3 ROLE_ADMIN
5 3 ROLE_USER
6 4 ROLE_USER
@@ -0,0 +1,22 @@
# Error page
error.title=Your request cannot be processed
error.subtitle=Sorry, an error has occurred.
error.status=Status:
error.message=Message:
# Activation e-mail
email.activation.title=baeldung account activation
email.activation.greeting=Dear {0}
email.activation.text1=Your baeldung account has been created, please click on the URL below to activate it:
email.activation.text2=Regards,
email.signature=baeldung Team.
# Creation email
email.creation.text1=Your baeldung account has been created, please click on the URL below to access it:
# Reset e-mail
email.reset.title=baeldung password reset
email.reset.greeting=Dear {0}
email.reset.text1=For your baeldung account a password reset was requested, please click on the URL below to reset it:
email.reset.text2=Regards,
@@ -0,0 +1,22 @@
# Error page
error.title=Your request cannot be processed
error.subtitle=Sorry, an error has occurred.
error.status=Status:
error.message=Message:
# Activation e-mail
email.activation.title=baeldung account activation
email.activation.greeting=Dear {0}
email.activation.text1=Your baeldung account has been created, please click on the URL below to activate it:
email.activation.text2=Regards,
email.signature=baeldung Team.
# Creation email
email.creation.text1=Your baeldung account has been created, please click on the URL below to access it:
# Reset e-mail
email.reset.title=baeldung password reset
email.reset.greeting=Dear {0}
email.reset.text1=For your baeldung account a password reset was requested, please click on the URL below to reset it:
email.reset.text2=Regards,
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true">
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<!-- The FILE and ASYNC appenders are here as examples for a production configuration -->
<!--
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>90</maxHistory>
</rollingPolicy>
<encoder>
<charset>utf-8</charset>
<Pattern>%d %-5level [%thread] %logger{0}: %msg%n</Pattern>
</encoder>
</appender>
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<queueSize>512</queueSize>
<appender-ref ref="FILE"/>
</appender>
-->
<logger name="com.baeldung" level="#logback.loglevel#"/>
<logger name="io.github.jhipster" level="DEBUG"/>
<logger name="javax.activation" level="WARN"/>
<logger name="javax.mail" level="WARN"/>
<logger name="javax.xml.bind" level="WARN"/>
<logger name="ch.qos.logback" level="WARN"/>
<logger name="com.codahale.metrics" level="WARN"/>
<logger name="com.ryantenney" level="WARN"/>
<logger name="com.sun" level="WARN"/>
<logger name="com.zaxxer" level="WARN"/>
<logger name="io.undertow" level="WARN"/>
<logger name="io.undertow.websockets.jsr" level="ERROR"/>
<logger name="org.ehcache" level="WARN"/>
<logger name="org.apache" level="WARN"/>
<logger name="org.apache.catalina.startup.DigesterFactory" level="OFF"/>
<logger name="org.bson" level="WARN"/>
<logger name="org.hibernate.validator" level="WARN"/>
<logger name="org.hibernate" level="WARN"/>
<logger name="org.hibernate.ejb.HibernatePersistence" level="OFF"/>
<logger name="org.springframework" level="WARN"/>
<logger name="org.springframework.web" level="WARN"/>
<logger name="org.springframework.security" level="WARN"/>
<logger name="org.springframework.cache" level="WARN"/>
<logger name="org.thymeleaf" level="WARN"/>
<logger name="org.xnio" level="WARN"/>
<logger name="springfox" level="WARN"/>
<logger name="sun.rmi" level="WARN"/>
<logger name="liquibase" level="WARN"/>
<logger name="LiquibaseSchemaResolver" level="INFO"/>
<logger name="sun.rmi.transport" level="WARN"/>
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
<resetJUL>true</resetJUL>
</contextListener>
<root level="#logback.loglevel#">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:text="#{email.activation.title}">JHipster activation</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text="#{email.activation.greeting(${user.login})}">
Dear
</p>
<p th:text="#{email.activation.text1}">
Your JHipster account has been created, please click on the URL below to activate it:
</p>
<p>
<a th:href="@{|${baseUrl}/#/activate?key=${user.activationKey}|}"
th:text="|${user.login}|">Activation Link</a>
</p>
<p>
<span th:text="#{email.activation.text2}">Regards, </span>
<br/>
<em th:text="#{email.signature}">JHipster.</em>
</p>
</body>
</html>
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:text="#{email.activation.title}">JHipster creation</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text="#{email.activation.greeting(${user.login})}">
Dear
</p>
<p th:text="#{email.creation.text1}">
Your JHipster account has been created, please click on the URL below to access it:
</p>
<p>
<a th:href="@{|${baseUrl}/#/reset/finish?key=${user.resetKey}|}"
th:text="|${user.login}|">login</a>
</p>
<p>
<span th:text="#{email.activation.text2}">Regards, </span>
<br/>
<em th:text="#{email.signature}">JHipster.</em>
</p>
</body>
</html>
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:text="#{email.reset.title}">JHipster password reset</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text="#{email.reset.greeting(${user.login})}">
Dear
</p>
<p th:text="#{email.reset.text1}">
For your JHipster account a password reset was requested, please click on the URL below to reset it:
</p>
<p>
<a th:href="@{|${baseUrl}/#/reset/finish?key=${user.resetKey}|}"
th:text="|${user.login}|">Reset Link</a>
</p>
<p>
<span th:text="#{email.reset.text2}">Regards, </span>
<br/>
<em th:text="#{email.signature}">JHipster.</em>
</p>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More