diff --git a/aws-modules/aws-s3/README.md b/aws-modules/aws-s3/README.md index cb039b7c61..9b862c8685 100644 --- a/aws-modules/aws-s3/README.md +++ b/aws-modules/aws-s3/README.md @@ -4,7 +4,7 @@ This module contains articles about Simple Storage Service (S3) on AWS ### Relevant articles -- [AWS S3 with Java](https://www.baeldung.com/aws-s3-java) +- [AWS S3 with Java](https://www.baeldung.com/java-aws-s3) - [Multipart Uploads in Amazon S3 with Java](https://www.baeldung.com/aws-s3-multipart-upload) - [Using the JetS3t Java Client With Amazon S3](https://www.baeldung.com/jets3t-amazon-s3) - [Check if a Specified Key Exists in a Given S3 Bucket Using Java](https://www.baeldung.com/java-aws-s3-check-specified-key-exists) diff --git a/core-java-modules/core-java-21/README.md b/core-java-modules/core-java-21/README.md index c63f3b360b..2e7130c906 100644 --- a/core-java-modules/core-java-21/README.md +++ b/core-java-modules/core-java-21/README.md @@ -1 +1,2 @@ -## Relevant Articles \ No newline at end of file +## Relevant Articles +- [Sequenced Collections in Java 21](https://www.baeldung.com/java-21-sequenced-collections) diff --git a/core-java-modules/core-java-8-datetime-2/README.md b/core-java-modules/core-java-8-datetime-2/README.md index b860ca979d..ac1e1ca81f 100644 --- a/core-java-modules/core-java-8-datetime-2/README.md +++ b/core-java-modules/core-java-8-datetime-2/README.md @@ -5,4 +5,5 @@ - [Parsing Date Strings with Varying Formats](https://www.baeldung.com/java-parsing-dates-many-formats) - [How Many Days Are There in a Particular Month of a Given Year?](https://www.baeldung.com/days-particular-month-given-year) - [Difference Between Instant and LocalDateTime](https://www.baeldung.com/java-instant-vs-localdatetime) +- [Add Minutes to a Time String in Java](https://www.baeldung.com/java-string-time-add-mins) - [[<-- Prev]](/core-java-modules/core-java-datetime-java8-1) diff --git a/core-java-modules/core-java-8-datetime/src/test/java/com/baeldung/dateapi/JavaDurationUnitTest.java b/core-java-modules/core-java-8-datetime/src/test/java/com/baeldung/dateapi/JavaDurationUnitTest.java index be43fb609a..d1bd77776a 100644 --- a/core-java-modules/core-java-8-datetime/src/test/java/com/baeldung/dateapi/JavaDurationUnitTest.java +++ b/core-java-modules/core-java-8-datetime/src/test/java/com/baeldung/dateapi/JavaDurationUnitTest.java @@ -3,9 +3,11 @@ package com.baeldung.dateapi; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.Duration; import java.time.Instant; +import java.time.LocalDateTime; import java.time.LocalTime; import java.time.temporal.ChronoUnit; @@ -19,7 +21,7 @@ public class JavaDurationUnitTest { LocalTime finalTime = initialTime.plus(Duration.ofSeconds(30)); long seconds = Duration.between(initialTime, finalTime) - .getSeconds(); + .getSeconds(); assertThat(seconds).isEqualTo(30); } @@ -34,6 +36,22 @@ public class JavaDurationUnitTest { assertThat(seconds).isEqualTo(30); } + @Test + public void givenADuration_whenCallingisZeroAndisNegative_thenGetExpectedResult() { + LocalDateTime start = LocalDateTime.parse("2020-01-01T08:00:00"); + LocalDateTime end = LocalDateTime.parse("2020-01-01T12:00:00"); + assertFalse(Duration.between(start, end) + .isNegative()); + assertTrue(Duration.between(end, start) + .isNegative()); + + LocalDateTime theTime = LocalDateTime.parse("2023-09-09T08:00:00"); + assertTrue(Duration.between(theTime, theTime) + .isZero()); + assertFalse(Duration.between(theTime, theTime) + .isNegative()); + } + @Test public void test2() { Instant start = Instant.parse("2017-10-03T10:15:30.00Z"); @@ -53,17 +71,17 @@ public class JavaDurationUnitTest { assertEquals(1, fromMinutes.toHours()); assertEquals(120, duration.plusSeconds(60) - .getSeconds()); + .getSeconds()); assertEquals(30, duration.minusSeconds(30) - .getSeconds()); + .getSeconds()); assertEquals(120, duration.plus(60, ChronoUnit.SECONDS) - .getSeconds()); + .getSeconds()); assertEquals(30, duration.minus(30, ChronoUnit.SECONDS) - .getSeconds()); + .getSeconds()); Duration fromChar1 = Duration.parse("P1DT1H10M10.5S"); Duration fromChar2 = Duration.parse("PT10M"); } -} +} \ No newline at end of file diff --git a/core-java-modules/core-java-collections-set-2/README.md b/core-java-modules/core-java-collections-set-2/README.md index feeffed1c4..ee41908faf 100644 --- a/core-java-modules/core-java-collections-set-2/README.md +++ b/core-java-modules/core-java-collections-set-2/README.md @@ -5,4 +5,5 @@ - [Sorting a HashSet in Java](https://www.baeldung.com/java-sort-hashset) - [How to Get First Item From a Java Set](https://www.baeldung.com/first-item-set) - [Cartesian Product of Any Number of Sets in Java](https://www.baeldung.com/java-cartesian-product-sets) +- [How to Get Index of an Item in Java Set](https://www.baeldung.com/java-set-element-find-index) - More articles: [[<-- prev]](/core-java-modules/core-java-collections-set) diff --git a/core-java-modules/core-java-concurrency-collections-2/README.md b/core-java-modules/core-java-concurrency-collections-2/README.md index 2d65cf88e7..fdb7af5936 100644 --- a/core-java-modules/core-java-concurrency-collections-2/README.md +++ b/core-java-modules/core-java-concurrency-collections-2/README.md @@ -5,4 +5,5 @@ - [Java Concurrent HashSet Equivalent to ConcurrentHashMap](https://www.baeldung.com/java-concurrent-hashset-concurrenthashmap) - [Reading and Writing With a ConcurrentHashMap](https://www.baeldung.com/concurrenthashmap-reading-and-writing) - [ArrayBlockingQueue vs. LinkedBlockingQueue](https://www.baeldung.com/java-arrayblockingqueue-vs-linkedblockingqueue) +- [Difference Between Hashtable and ConcurrentHashMap in Java](https://www.baeldung.com/java-hashtable-vs-concurrenthashmap) - [[<-- Prev]](/core-java-modules/core-java-concurrency-collections) diff --git a/core-java-modules/core-java-lang-oop-inheritance/README.md b/core-java-modules/core-java-lang-oop-inheritance/README.md index 430f88e717..6091b15024 100644 --- a/core-java-modules/core-java-lang-oop-inheritance/README.md +++ b/core-java-modules/core-java-lang-oop-inheritance/README.md @@ -12,4 +12,4 @@ This module contains articles about inheritance in Java - [Guide to Inheritance in Java](https://www.baeldung.com/java-inheritance) - [Object Type Casting in Java](https://www.baeldung.com/java-type-casting) - [Variable and Method Hiding in Java](https://www.baeldung.com/java-variable-method-hiding) -- [Inner Classes Vs. Subclasses in Java](https://www.baeldung.com/java-inner-classes-vs-subclasses) +- [Inner Classes vs. Subclasses in Java](https://www.baeldung.com/java-inner-classes-vs-subclasses) diff --git a/core-java-modules/core-java-streams-5/README.md b/core-java-modules/core-java-streams-5/README.md index 885949d937..dec4a7a0cb 100644 --- a/core-java-modules/core-java-streams-5/README.md +++ b/core-java-modules/core-java-streams-5/README.md @@ -2,3 +2,4 @@ - [Difference Between parallelStream() and stream().parallel() in Java](https://www.baeldung.com/java-parallelstream-vs-stream-parallel) - [Working With Empty Stream in Java](https://www.baeldung.com/java-empty-stream) - [Aggregate Runtime Exceptions in Java Streams](https://www.baeldung.com/java-streams-aggregate-exceptions) +- [Streams vs. Loops in Java](https://www.baeldung.com/java-streams-vs-loops) diff --git a/core-java-modules/core-java-string-operations-5/README.md b/core-java-modules/core-java-string-operations-5/README.md index 21ba1bf985..fda7f39654 100644 --- a/core-java-modules/core-java-string-operations-5/README.md +++ b/core-java-modules/core-java-string-operations-5/README.md @@ -10,4 +10,5 @@ - [Guide to Splitting a String by Whitespace in Java](https://www.baeldung.com/java-splitting-a-string-by-whitespace) - [Check if the First Letter of a String Is a Number](https://www.baeldung.com/java-check-if-string-starts-with-number) - [Print “” Quotes Around a String in Java](https://www.baeldung.com/java-string-print-quotes) -- [Remove Punctuation From a String in Java](https://www.baeldung.com/java-remove-punctuation-from-string) \ No newline at end of file +- [Remove Punctuation From a String in Java](https://www.baeldung.com/java-remove-punctuation-from-string) +- [Replacing Single Quote with \’ in Java String](https://www.baeldung.com/java-replacing-single-quote-string) diff --git a/gradle-modules/gradle-5/README.md b/gradle-modules/gradle-5/README.md index e37c100534..7871c0e822 100644 --- a/gradle-modules/gradle-5/README.md +++ b/gradle-modules/gradle-5/README.md @@ -2,3 +2,4 @@ - [Run a Java main Method Using Gradle](https://www.baeldung.com/gradle-run-java-main) - [Finding Unused Gradle Dependencies](https://www.baeldung.com/gradle-finding-unused-dependencies) +- [Intro to Gradle Lint Plugin](https://www.baeldung.com/java-gradle-lint-intro) diff --git a/persistence-modules/pom.xml b/persistence-modules/pom.xml index ec02b0f37c..73c6b96bba 100644 --- a/persistence-modules/pom.xml +++ b/persistence-modules/pom.xml @@ -114,7 +114,7 @@ java-mongodb questdb neo4j - rethinkdb + scylladb spring-data-cassandra-2 spring-data-jpa-repo-3 diff --git a/persistence-modules/spring-data-cassandra-2/README.md b/persistence-modules/spring-data-cassandra-2/README.md index 3f49d6ae93..0578dcc429 100644 --- a/persistence-modules/spring-data-cassandra-2/README.md +++ b/persistence-modules/spring-data-cassandra-2/README.md @@ -2,3 +2,4 @@ - [Using Test Containers With Spring Data Cassandra](https://www.baeldung.com/spring-data-cassandra-test-containers) - [Cassandra – Object Mapping with DataStax Java Driver](https://www.baeldung.com/cassandra-object-mapping-datastax-java-driver) +- [Query With IN Clause in Spring Data Cassandra](https://www.baeldung.com/spring-cassandra-query-in-clause) diff --git a/persistence-modules/spring-data-couchbase-2/src/test/docker/Dockerfile b/persistence-modules/spring-data-couchbase-2/src/test/docker/Dockerfile new file mode 100644 index 0000000000..1dffcb31f1 --- /dev/null +++ b/persistence-modules/spring-data-couchbase-2/src/test/docker/Dockerfile @@ -0,0 +1,4 @@ +FROM couchbase/server:community-5.0.1 + +COPY configure.sh /configure.sh +CMD ["/configure.sh"] \ No newline at end of file diff --git a/persistence-modules/spring-data-couchbase-2/src/test/docker/configure.sh b/persistence-modules/spring-data-couchbase-2/src/test/docker/configure.sh new file mode 100644 index 0000000000..c2b62dba7c --- /dev/null +++ b/persistence-modules/spring-data-couchbase-2/src/test/docker/configure.sh @@ -0,0 +1,106 @@ +#!/bin/bash + +function retry() { + for i in $(seq 1 10); do + $1 "$2" + if [[ $? == 0 ]]; then + return 0 + fi + sleep 1 + done + return 1 +} + +function bucketCreate(){ + couchbase-cli bucket-create -c localhost -u Administrator -p password \ + --bucket="$1" \ + --bucket-type=couchbase \ + --bucket-ramsize=512 \ + --bucket-replica=1 \ + --wait + if [[ $? != 0 ]]; then + return 1 + fi +} + +function userCreate(){ + createOutput=$(couchbase-cli user-manage -c localhost -u Administrator -p password \ + --set --rbac-username "$1" --rbac-password "$1" \ + --roles admin --auth-domain local) + if [[ $? != 0 ]]; then + echo $createOutput >&2 + return 1 + fi +} + +function clusterUp(){ + # wait for service to come up + until $(curl --output /dev/null --silent --head --fail http://localhost:8091); do + printf '.' + sleep 1 + done + + # initialize cluster + initOutput=$(couchbase-cli cluster-init -c localhost \ + --cluster-username=Administrator \ + --cluster-password=password \ + --cluster-port=8091 \ + --services=data,index,query,fts \ + --cluster-ramsize=1024 \ + --cluster-index-ramsize=256 \ + --cluster-fts-ramsize=256 \ + --index-storage-setting=default) + if [[ $? != 0 ]]; then + echo $initOutput >&2 + return 1 + fi +} + +function main(){ + set -ex + echo "Couchbase UI :8091" + echo "Couchbase logs /opt/couchbase/var/lib/couchbase/logs" + ./entrypoint.sh couchbase-server & + if [[ $? != 0 ]]; then + echo "Couchbase startup failed. Exiting." >&2 + exit 1 + fi + + clusterUp + if [[ $? != 0 ]]; then + echo "Cluster init failed. Exiting." >&2 + exit 1 + fi + + retry userCreate baeldung + if [[ $? != 0 ]]; then + echo "User create failed. Exiting." >&2 + exit 1 + fi + + retry userCreate baeldung2 + if [[ $? != 0 ]]; then + echo "User create failed. Exiting." >&2 + exit 1 + fi + + retry bucketCreate baeldung + if [[ $? != 0 ]]; then + echo "Bucket create failed. Exiting." >&2 + exit 1 + fi + + retry bucketCreate baeldung2 + if [[ $? != 0 ]]; then + echo "Bucket create failed. Exiting." >&2 + exit 1 + fi + + set +ex + + # entrypoint.sh launches the server but since config.sh is pid 1 we keep it + # running so that the docker container does not exit. + wait +} + +main \ No newline at end of file diff --git a/persistence-modules/spring-data-couchbase-2/src/test/docker/dockerbuild.sh b/persistence-modules/spring-data-couchbase-2/src/test/docker/dockerbuild.sh new file mode 100644 index 0000000000..37add165c6 --- /dev/null +++ b/persistence-modules/spring-data-couchbase-2/src/test/docker/dockerbuild.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# setup +set -ex +docker rm couchbase_container -f + +# main +docker build -t couchbase_image . + +# cleanup +set +ex + +# run +docker run -d --name couchbase_container -p 8091-8096:8091-8096 -p 11210-11211:11210-11211 couchbase_image diff --git a/persistence-modules/spring-data-couchbase-2/src/test/java/com/baeldung/SpringContextLiveTest.java b/persistence-modules/spring-data-couchbase-2/src/test/java/com/baeldung/SpringContextLiveTest.java index 553520e6e6..74dccbadd0 100644 --- a/persistence-modules/spring-data-couchbase-2/src/test/java/com/baeldung/SpringContextLiveTest.java +++ b/persistence-modules/spring-data-couchbase-2/src/test/java/com/baeldung/SpringContextLiveTest.java @@ -12,16 +12,10 @@ import org.springframework.test.context.support.DependencyInjectionTestExecution /** * This LiveTest requires: * - * 1- Couchbase instance running (e.g. with `docker run -d --name db -p 8091-8096:8091-8096 -p 11210-11211:11210-11211 couchbase`) - * - * - * 2- Couchbase configured with (we can use the console in localhost:8091): - * - * 2.1- Buckets: named 'baeldung' and 'baeldung2' - * - * 2.2- Security: users 'baeldung' and 'baeldung2'. Note: in newer versions an empty password is not allowed, then we have to change the passwords in the project) - * - * 2.3- Spacial View: Add new spacial view (in Index tab) in document 'campus_spatial', view 'byLocation' with the following function: + * 1- Couchbase 5 instance Running and Configured. + * It's enough to execute the "dockerbuild.sh" script in the test/docker folder. + * + * 2.1- Spacial View: Add new spacial view (in Index tab) in document 'campus_spatial', view 'byLocation' with the following function: * {@code * function (doc) { * if (doc.location && @@ -30,8 +24,9 @@ import org.springframework.test.context.support.DependencyInjectionTestExecution * } * }} * - * 2.4- MapReduce Views: Add new views in document 'campus': - * 2.4.1- view 'all' with function: + * 2.2- MapReduce Views: Add new views in document 'campus': + * + * 2.2.1- view 'all' with function: * {@code * function (doc, meta) { * if(doc._class == "com.baeldung.spring.data.couchbase.model.Campus") { @@ -39,7 +34,7 @@ import org.springframework.test.context.support.DependencyInjectionTestExecution * } * }} * - * 2.4.2- view 'byName' with function: + * 2.2.2- view 'byName' with function: * {@code * function (doc, meta) { * if(doc._class == "com.baeldung.spring.data.couchbase.model.Campus" && diff --git a/persistence-modules/spring-data-couchbase-2/src/test/java/com/baeldung/spring/data/couchbase/CustomTypeKeyCouchbaseConfig.java b/persistence-modules/spring-data-couchbase-2/src/test/java/com/baeldung/spring/data/couchbase/CustomTypeKeyCouchbaseConfig.java deleted file mode 100644 index eacecb4e1e..0000000000 --- a/persistence-modules/spring-data-couchbase-2/src/test/java/com/baeldung/spring/data/couchbase/CustomTypeKeyCouchbaseConfig.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.baeldung.spring.data.couchbase; - -import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter; - -public class CustomTypeKeyCouchbaseConfig extends MyCouchbaseConfig { - - @Override - public String getConnectionString() { - return NODE_LIST; - } - - @Override - public String getUserName() { - return BUCKET_USERNAME; - } - - @Override - public String getPassword() { - return BUCKET_PASSWORD; - } - - @Override - public String typeKey() { - return MappingCouchbaseConverter.TYPEKEY_SYNCGATEWAY_COMPATIBLE; - } -} diff --git a/persistence-modules/spring-data-couchbase-2/src/test/java/com/baeldung/spring/data/couchbase/ReadYourOwnWritesCouchbaseConfig.java b/persistence-modules/spring-data-couchbase-2/src/test/java/com/baeldung/spring/data/couchbase/ReadYourOwnWritesCouchbaseConfig.java deleted file mode 100644 index 33a6c4f091..0000000000 --- a/persistence-modules/spring-data-couchbase-2/src/test/java/com/baeldung/spring/data/couchbase/ReadYourOwnWritesCouchbaseConfig.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.baeldung.spring.data.couchbase; - -import com.couchbase.client.java.query.QueryScanConsistency; - -public class ReadYourOwnWritesCouchbaseConfig extends MyCouchbaseConfig { - - @Override - public QueryScanConsistency getDefaultConsistency() { - return QueryScanConsistency.REQUEST_PLUS; - } -} diff --git a/spring-aop-2/README.md b/spring-aop-2/README.md index a9694ac236..0aa20d32f0 100644 --- a/spring-aop-2/README.md +++ b/spring-aop-2/README.md @@ -7,4 +7,5 @@ This module contains articles about Spring aspect oriented programming (AOP) - [Spring Performance Logging](https://www.baeldung.com/spring-performance-logging) - [When Does Java Throw UndeclaredThrowableException?](https://www.baeldung.com/java-undeclaredthrowableexception) - [Get Advised Method Info in Spring AOP](https://www.baeldung.com/spring-aop-get-advised-method-info) -- More articles: [[<-- prev]](/spring-aop) \ No newline at end of file +- [Invoke Spring @Cacheable from Another Method of Same Bean](https://www.baeldung.com/spring-invoke-cacheable-other-method-same-bean) +- More articles: [[<-- prev]](/spring-aop) diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/permitallanonymous/SecuredEcommerceApplication.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/permitallanonymous/SecuredEcommerceApplication.java new file mode 100644 index 0000000000..150fe399d1 --- /dev/null +++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/permitallanonymous/SecuredEcommerceApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.permitallanonymous; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan("com.baeldung.permitallanonymous.*") +public class SecuredEcommerceApplication { + public static void main(String[] args) { + SpringApplication.run(SecuredEcommerceApplication.class, args); + } +} diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/permitallanonymous/controller/EcommerceController.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/permitallanonymous/controller/EcommerceController.java new file mode 100644 index 0000000000..8f20baee10 --- /dev/null +++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/permitallanonymous/controller/EcommerceController.java @@ -0,0 +1,29 @@ +package com.baeldung.permitallanonymous.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class EcommerceController { + + //can be accessed by only logged-in users + @GetMapping("/private/showCart") + public @ResponseBody String showCart() { + return "Show Cart"; + } + + //can we accessed by both anonymous and authenticated users + @GetMapping("/public/showProducts") + public @ResponseBody String listProducts() { + return "List Products"; + } + + //can be access by only anonymous users not by authenticated users + @GetMapping("/public/registerUser") + public @ResponseBody String registerUser() { + return "Register User"; + } + +} + diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/permitallanonymous/filter/AuditInterceptor.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/permitallanonymous/filter/AuditInterceptor.java new file mode 100644 index 0000000000..c0a5f6972f --- /dev/null +++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/permitallanonymous/filter/AuditInterceptor.java @@ -0,0 +1,33 @@ +package com.baeldung.permitallanonymous.filter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class AuditInterceptor extends OncePerRequestFilter { + private final Logger logger = LoggerFactory.getLogger(AuditInterceptor.class); + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication instanceof AnonymousAuthenticationToken) { + logger.info("Audit anonymous user"); + } + if (authentication instanceof UsernamePasswordAuthenticationToken) { + logger.info("Audit registered user"); + } + filterChain.doFilter(request, response); + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/permitallanonymous/security/EcommerceWebSecurityConfig.java b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/permitallanonymous/security/EcommerceWebSecurityConfig.java new file mode 100644 index 0000000000..566ec49e42 --- /dev/null +++ b/spring-boot-modules/spring-boot-security/src/main/java/com/baeldung/permitallanonymous/security/EcommerceWebSecurityConfig.java @@ -0,0 +1,46 @@ +package com.baeldung.permitallanonymous.security; + +import com.baeldung.permitallanonymous.filter.AuditInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.AnonymousAuthenticationFilter; + +@Configuration +@EnableWebSecurity +public class EcommerceWebSecurityConfig { + @Bean + public InMemoryUserDetailsManager userDetailsService(PasswordEncoder passwordEncoder) { + UserDetails user = User.withUsername("spring") + .password(passwordEncoder.encode("secret")) + .roles("USER") + .build(); + + return new InMemoryUserDetailsManager(user); + } + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.addFilterAfter(new AuditInterceptor(), AnonymousAuthenticationFilter.class) + .authorizeRequests() + .antMatchers("/private/**").authenticated().and().httpBasic() + .and().authorizeRequests() + .antMatchers("/public/showProducts").permitAll() + .antMatchers("/public/registerUser").anonymous(); + + return http.build(); + } + + @Bean + public BCryptPasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-security/src/test/java/com/baeldung/permitallanonymous/SecureEcommerceApplicationUnitTest.java b/spring-boot-modules/spring-boot-security/src/test/java/com/baeldung/permitallanonymous/SecureEcommerceApplicationUnitTest.java new file mode 100644 index 0000000000..3c73caf1fd --- /dev/null +++ b/spring-boot-modules/spring-boot-security/src/test/java/com/baeldung/permitallanonymous/SecureEcommerceApplicationUnitTest.java @@ -0,0 +1,70 @@ +package com.baeldung.permitallanonymous; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.test.context.support.WithAnonymousUser; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SecuredEcommerceApplication.class) +@AutoConfigureMockMvc +public class SecureEcommerceApplicationUnitTest { + @Autowired + private MockMvc mockMvc; + private static final Logger logger = LoggerFactory.getLogger(SecureEcommerceApplicationUnitTest.class); + + @WithAnonymousUser + @Test + public void givenAnonymousUser_whenAccessToUserRegisterPage_thenAllowAccess() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/public/registerUser")) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.content().string("Register User")); + } + + @WithMockUser(username = "spring", password = "secret") + @Test + public void givenAuthenticatedUser_whenAccessToUserRegisterPage_thenDenyAccess() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/public/registerUser")) + .andExpect(MockMvcResultMatchers.status().isForbidden()); + } + + @WithMockUser(username = "spring", password = "secret") + @Test + public void givenAuthenticatedUser_whenAccessToProductLinePage_thenAllowAccess() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/public/showProducts")) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.content().string("List Products")); + } + + @WithAnonymousUser + @Test + public void givenAnonymousUser_whenAccessToProductLinePage_thenAllowAccess() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/public/showProducts")) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.content().string("List Products")); + } + + @WithMockUser(username = "spring", password = "secret") + @Test + public void givenAuthenticatedUser_whenAccessToCartPage_thenAllowAccess() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/private/showCart")) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.content().string("Show Cart")); + } + + @WithAnonymousUser + @Test + public void givenAnonymousUser_whenAccessToCartPage_thenDenyAccess() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/private/showCart")) + .andExpect(MockMvcResultMatchers.status().isUnauthorized()); + } +} diff --git a/spring-boot-modules/spring-boot-ssl-bundles/README.md b/spring-boot-modules/spring-boot-ssl-bundles/README.md index 5616cce48b..840cc21583 100644 --- a/spring-boot-modules/spring-boot-ssl-bundles/README.md +++ b/spring-boot-modules/spring-boot-ssl-bundles/README.md @@ -1 +1,2 @@ ## Relevant Articles +- [Securing Spring Boot 3 Applications With SSL Bundles](https://www.baeldung.com/spring-boot-security-ssl-bundles) diff --git a/spring-vault/README.md b/spring-vault/README.md index 9e1d14ba6b..22fb0a7ff2 100644 --- a/spring-vault/README.md +++ b/spring-vault/README.md @@ -5,3 +5,4 @@ This module contains articles about Spring Vault ### Relevant Articles: - [Spring Vault](https://www.baeldung.com/spring-vault) +- [Secure Kubernetes Secrets with Vault](https://www.baeldung.com/spring-vault-kubernetes-secrets) diff --git a/testing-modules/junit-5-basics-2/README.md b/testing-modules/junit-5-basics-2/README.md index f5e2558332..0e0faedb4b 100644 --- a/testing-modules/junit-5-basics-2/README.md +++ b/testing-modules/junit-5-basics-2/README.md @@ -1,2 +1,2 @@ ### Relevant Articles: -- [Test Main Method with JUnit](http://www.baeldung.com/junit-5) +- [Test Main Method with JUnit](https://www.baeldung.com/junit-test-main-method) diff --git a/testing-modules/mockito-2/README.md b/testing-modules/mockito-2/README.md index 82c97305f6..b60885c6d5 100644 --- a/testing-modules/mockito-2/README.md +++ b/testing-modules/mockito-2/README.md @@ -7,3 +7,4 @@ This module contains articles about Mockito - [Resolving Mockito Exception: Wanted But Not Invoked](https://www.baeldung.com/mockito-exception-wanted-but-not-invoked) - [Matching Null With Mockito](https://www.baeldung.com/mockito-match-null) - [Mock Same Method with Different Parameters](https://www.baeldung.com/java-mock-same-method-other-parameters) +- [How to Mock Constructors for Unit Testing using Mockito](https://www.baeldung.com/java-mockito-constructors-unit-testing) diff --git a/testing-modules/testing-assertions/README.md b/testing-modules/testing-assertions/README.md index 37de3d4929..8f94277791 100644 --- a/testing-modules/testing-assertions/README.md +++ b/testing-modules/testing-assertions/README.md @@ -5,3 +5,4 @@ - [Assert That a Java Optional Has a Certain Value](https://www.baeldung.com/java-optional-assert-value) - [Assert That an Object Is From a Specific Type](https://www.baeldung.com/java-assert-object-of-type) - [Asserting Equality on Two Classes Without an equals() Method](https://www.baeldung.com/java-assert-equality-no-equals) +- [Assert Regex Matches in JUnit](https://www.baeldung.com/junit-assert-regex-matches)