diff --git a/libraries-security/pom.xml b/libraries-security/pom.xml
index 0031b7bc06..46e12eb655 100644
--- a/libraries-security/pom.xml
+++ b/libraries-security/pom.xml
@@ -78,6 +78,29 @@
sshd-core
${apache-mina.version}
+
+ org.xacml4j
+ xacml-core
+ ${xacml4j.version}
+
+
+ org.slf4j
+ slf4j-log4j12
+
+
+
+
+ org.xacml4j
+ xacml-test
+ ${xacml4j.version}
+ test
+
+
+ org.slf4j
+ slf4j-log4j12
+
+
+
@@ -90,6 +113,7 @@
0.1.55
2.5.1
2.4.0.RELEASE
+ 1.4.0
\ No newline at end of file
diff --git a/libraries-security/src/main/java/com/baeldung/scribejava/ScribejavaApplication.java b/libraries-security/src/main/java/com/baeldung/scribejava/ScribejavaApplication.java
index bb86c497b0..5b18567b2d 100644
--- a/libraries-security/src/main/java/com/baeldung/scribejava/ScribejavaApplication.java
+++ b/libraries-security/src/main/java/com/baeldung/scribejava/ScribejavaApplication.java
@@ -2,9 +2,11 @@ package com.baeldung.scribejava;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.web.servlet.ServletComponentScan;
@SpringBootApplication
+@ServletComponentScan
public class ScribejavaApplication {
public static void main(String[] args) {
diff --git a/libraries-security/src/main/java/com/baeldung/scribejava/controller/RBACController.java b/libraries-security/src/main/java/com/baeldung/scribejava/controller/RBACController.java
new file mode 100644
index 0000000000..785f6228e8
--- /dev/null
+++ b/libraries-security/src/main/java/com/baeldung/scribejava/controller/RBACController.java
@@ -0,0 +1,27 @@
+package com.baeldung.scribejava.controller;
+
+import java.io.IOException;
+
+import javax.annotation.security.DeclareRoles;
+import javax.annotation.security.RolesAllowed;
+import javax.servlet.ServletException;
+import javax.servlet.annotation.HttpConstraint;
+import javax.servlet.annotation.ServletSecurity;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@WebServlet(name="rbac", urlPatterns = {"/protected"})
+@DeclareRoles("USER")
+@ServletSecurity(
+ @HttpConstraint(rolesAllowed = "USER")
+)
+public class RBACController extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ resp.getWriter().println("Hello, USER");
+ }
+}
diff --git a/libraries-security/src/test/java/com/baeldung/xacml4j/NightlyWithdrawalPolicyUnitTest.java b/libraries-security/src/test/java/com/baeldung/xacml4j/NightlyWithdrawalPolicyUnitTest.java
new file mode 100644
index 0000000000..013c78370f
--- /dev/null
+++ b/libraries-security/src/test/java/com/baeldung/xacml4j/NightlyWithdrawalPolicyUnitTest.java
@@ -0,0 +1,233 @@
+package com.baeldung.xacml4j;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+import org.xacml4j.v20.Xacml20TestUtility;
+import org.xacml4j.v30.Attribute;
+import org.xacml4j.v30.Categories;
+import org.xacml4j.v30.Category;
+import org.xacml4j.v30.CompositeDecisionRule;
+import org.xacml4j.v30.Decision;
+import org.xacml4j.v30.Entity;
+import org.xacml4j.v30.RequestContext;
+import org.xacml4j.v30.ResponseContext;
+import org.xacml4j.v30.Result;
+import org.xacml4j.v30.XacmlPolicyTestSupport;
+import org.xacml4j.v30.pdp.PolicyDecisionPoint;
+import org.xacml4j.v30.pdp.PolicyDecisionPointBuilder;
+import org.xacml4j.v30.spi.combine.DecisionCombiningAlgorithmProviderBuilder;
+import org.xacml4j.v30.spi.function.FunctionProviderBuilder;
+import org.xacml4j.v30.spi.pip.PolicyInformationPointBuilder;
+import org.xacml4j.v30.spi.repository.InMemoryPolicyRepository;
+import org.xacml4j.v30.spi.repository.PolicyRepository;
+import org.xacml4j.v30.types.DoubleExp;
+import org.xacml4j.v30.types.StringExp;
+import org.xacml4j.v30.types.TimeExp;
+
+public class NightlyWithdrawalPolicyUnitTest extends XacmlPolicyTestSupport {
+
+ private static final String POLICY_SET = "xacml4j/NightlyWithdrawalsPolicy.xml";
+
+ @Test
+ public void testWhenNightlyWithdrawalOver500_thenFail() throws Exception {
+
+ PolicyDecisionPoint pdp = buildPDP(POLICY_SET);
+
+ // Action category
+ Attribute actionAttribute = Attribute.builder("urn:oasis:names:tc:xacml:1.0:action:action-id")
+ .value(StringExp.of("withdrawal"))
+ .build();
+ Entity actionEntity = Entity.builder()
+ .attribute(actionAttribute)
+ .build();
+ Category actionCategory = Category.builder(Categories.ACTION)
+ .entity(actionEntity)
+ .build();
+
+ // Environment Category
+ Attribute timeAttribute = Attribute.builder("urn:oasis:names:tc:xacml:1.0:environment:current-time")
+ .includeInResult(false)
+ .value(TimeExp.of("21:00:00"))
+ .build();
+ Entity timeEntity = Entity.builder()
+ .attribute(timeAttribute)
+ .build();
+
+ Category environmentCategory = Category.builder(Categories.ENVIRONMENT)
+ .entity(timeEntity)
+ .build();
+
+ // ATM category
+ Attribute amountAttribute = Attribute.builder("urn:baeldung:atm:withdrawal:amount")
+ .value(DoubleExp.of("1200.00"))
+ .build();
+ Entity atmEntity = Entity.builder()
+ .attribute(amountAttribute)
+ .build();
+
+ Category atmCategory = Category.builder(Categories.parse("urn:baeldung:atm:withdrawal"))
+ .entity(atmEntity)
+ .build();
+
+ RequestContext request = RequestContext.builder()
+ .attributes(actionCategory, environmentCategory, atmCategory)
+ .build();
+
+ ResponseContext response = pdp.decide(request);
+ assertNotNull(response);
+ assertTrue("Shoud have at least one result", response.getResults() != null && !response.getResults()
+ .isEmpty());
+
+ Result result = response.getResults()
+ .iterator()
+ .next();
+ assertTrue("Evaluation should succeed", result.getStatus()
+ .isSuccess());
+ assertEquals("Should DENY withdrawal", Decision.DENY, result.getDecision());
+
+ }
+
+ @Test
+ public void testWhenNightlyWithdrawalUnder500_thenSuccess() throws Exception {
+
+ PolicyDecisionPoint pdp = buildPDP(POLICY_SET);
+
+ // Action category
+ Attribute actionAttribute = Attribute.builder("urn:oasis:names:tc:xacml:1.0:action:action-id")
+ .includeInResult(false)
+ .value(StringExp.of("withdrawal"))
+ .build();
+ Entity actionEntity = Entity.builder()
+ .attribute(actionAttribute)
+ .build();
+ Category actionCategory = Category.builder(Categories.ACTION)
+ .entity(actionEntity)
+ .build();
+
+ // Environment Category
+ Attribute timeAttribute = Attribute.builder("urn:oasis:names:tc:xacml:1.0:environment:current-time")
+ .includeInResult(false)
+ .value(TimeExp.of("21:00:00"))
+ .build();
+ Entity timeEntity = Entity.builder()
+ .attribute(timeAttribute)
+ .build();
+ Category environmentCategory = Category.builder(Categories.ENVIRONMENT)
+ .entity(timeEntity)
+ .build();
+
+ // ATM category
+ Attribute amountAttribute = Attribute.builder("urn:baeldung:atm:withdrawal:amount")
+ .value(DoubleExp.of("499.00"))
+ .build();
+ Entity atmEntity = Entity.builder()
+ .attribute(amountAttribute)
+ .build();
+ Category atmCategory = Category.builder(Categories.parse("urn:baeldung:atm:withdrawal"))
+ .entity(atmEntity)
+ .build();
+
+ RequestContext request = RequestContext.builder()
+ .attributes(actionCategory, environmentCategory, atmCategory)
+ .build();
+
+ ResponseContext response = pdp.decide(request);
+ assertNotNull(response);
+ assertTrue("Shoud have at least one result",
+ response.getResults() != null && !response.getResults().isEmpty());
+
+ Result result = response.getResults().iterator().next();
+ assertTrue("Evaluation should succeed", result.getStatus().isSuccess());
+ assertEquals("Should PERMIT withdrawal", Decision.PERMIT, result.getDecision());
+
+ }
+
+ @Test
+ public void testWhenBusinessHoursWithdrawalOver500_thenSuccess() throws Exception {
+
+ PolicyDecisionPoint pdp = buildPDP(POLICY_SET);
+
+ // Action category
+ Attribute actionAttribute = Attribute.builder("urn:oasis:names:tc:xacml:1.0:action:action-id")
+ .includeInResult(false)
+ .value(StringExp.of("withdrawal"))
+ .build();
+ Entity actionEntity = Entity.builder()
+ .attribute(actionAttribute)
+ .build();
+ Category actionCategory = Category.builder(Categories.ACTION)
+ .entity(actionEntity)
+ .build();
+
+ // Environment Category
+ Attribute timeAttribute = Attribute.builder("urn:oasis:names:tc:xacml:1.0:environment:current-time")
+ .includeInResult(false)
+ .value(TimeExp.of("12:00:00"))
+ .build();
+ Entity timeEntity = Entity.builder()
+ .attribute(timeAttribute)
+ .build();
+ Category environmentCategory = Category.builder(Categories.ENVIRONMENT)
+ .entity(timeEntity)
+ .build();
+
+ // ATM category
+ Attribute amountAttribute = Attribute.builder("urn:baeldung:atm:withdrawal:amount")
+ .value(DoubleExp.of("2000.00"))
+ .build();
+ Entity atmEntity = Entity.builder()
+ .attribute(amountAttribute)
+ .build();
+
+ Category atmCategory = Category.builder(Categories.parse("urn:baeldung:atm:withdrawal"))
+ .entity(atmEntity)
+ .build();
+
+ RequestContext request = RequestContext.builder()
+ .attributes(actionCategory, environmentCategory, atmCategory)
+ .build();
+
+ ResponseContext response = pdp.decide(request);
+ assertNotNull(response);
+ assertTrue("Shoud have at least one result", response.getResults() != null && !response.getResults()
+ .isEmpty());
+
+ Result result = response.getResults()
+ .iterator()
+ .next();
+ assertTrue("Evaluation should succeed", result.getStatus().isSuccess());
+ assertEquals("Should PERMIT withdrawal", Decision.PERMIT, result.getDecision());
+
+ }
+
+ private PolicyDecisionPoint buildPDP(String... policyResources) throws Exception {
+ PolicyRepository repository = new InMemoryPolicyRepository("tes-repository", FunctionProviderBuilder.builder()
+ .defaultFunctions()
+ .build(),
+ DecisionCombiningAlgorithmProviderBuilder.builder()
+ .withDefaultAlgorithms()
+ .create());
+
+ List policies = new ArrayList(policyResources.length);
+ for (String policyResource : policyResources) {
+ CompositeDecisionRule policy = repository.importPolicy(Xacml20TestUtility.getClasspathResource(policyResource));
+ log.info("Policy: {}", policy);
+ policies.add(policy);
+ }
+
+ return PolicyDecisionPointBuilder.builder("testPdp")
+ .policyRepository(repository)
+ .pip(PolicyInformationPointBuilder.builder("testPip")
+ .defaultResolvers()
+ .build())
+ .rootPolicy(policies.get(0))
+ .build();
+ }
+
+}
diff --git a/libraries-security/src/test/resources/xacml4j/NightlyWithdrawalsPolicy.xml b/libraries-security/src/test/resources/xacml4j/NightlyWithdrawalsPolicy.xml
new file mode 100644
index 0000000000..163df47f36
--- /dev/null
+++ b/libraries-security/src/test/resources/xacml4j/NightlyWithdrawalsPolicy.xml
@@ -0,0 +1,137 @@
+
+
+
+ Withdrawal policy example
+
+
+
+
+Deny withdrawals over $500 between 20:00 and 08:00
+
+
+
+
+
+ withdrawal
+
+
+
+
+
+
+
+
+
+
+
+
+ 08:00:00
+ 20:00:00
+
+
+
+
+
+
+ 500.00
+
+
+
+
+
+
+Permit withdrawals under $500 between 20:00 and 08:00
+
+
+
+
+
+ withdrawal
+
+
+
+
+
+
+
+
+
+
+
+
+ 08:00:00
+ 20:00:00
+
+
+
+
+
+
+ 500.00
+
+
+
+
+
+
+Permit withdrawals of any value between 08:00 and 20:00
+
+
+
+
+
+ withdrawal
+
+
+
+
+
+
+
+
+
+
+ 08:00:00
+ 20:00:00
+
+
+
+
+
diff --git a/libraries-security/src/test/resources/xacml4j/Request.xml b/libraries-security/src/test/resources/xacml4j/Request.xml
new file mode 100644
index 0000000000..105c7ad02f
--- /dev/null
+++ b/libraries-security/src/test/resources/xacml4j/Request.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+ withdrawal
+
+
+
+
+ 21:00:00
+
+
+
+
+ 1200
+
+
+
\ No newline at end of file
diff --git a/libraries-security/src/test/resources/xacml4j/Response.xml b/libraries-security/src/test/resources/xacml4j/Response.xml
new file mode 100644
index 0000000000..f82b16080d
--- /dev/null
+++ b/libraries-security/src/test/resources/xacml4j/Response.xml
@@ -0,0 +1,9 @@
+
+
+
+ NotApplicable
+
+
+
+
+
diff --git a/maven-modules/maven-parent-pom-resolution/README.md b/maven-modules/maven-parent-pom-resolution/README.md
new file mode 100644
index 0000000000..6f72b5e70b
--- /dev/null
+++ b/maven-modules/maven-parent-pom-resolution/README.md
@@ -0,0 +1,3 @@
+### Relevant Articles:
+
+- [Understanding the "relativePath" Tag - Maven Parent POM Resolution At A Glance](https://www.baeldung.com/maven-relativepath)
diff --git a/maven-modules/maven-parent-pom-resolution/pom.xml b/maven-modules/maven-parent-pom-resolution/pom.xml
new file mode 100644
index 0000000000..62e3946723
--- /dev/null
+++ b/maven-modules/maven-parent-pom-resolution/pom.xml
@@ -0,0 +1,28 @@
+
+
+ 4.0.0
+ com.baeldung
+ maven-parent-pom-resolution
+ 1.0-SNAPSHOT
+ pom
+
+
+ project-a
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ 3.2.0
+
+
+
+
+
+
diff --git a/maven-modules/maven-parent-pom-resolution/project-a/pom.xml b/maven-modules/maven-parent-pom-resolution/project-a/pom.xml
new file mode 100644
index 0000000000..2d53a36d84
--- /dev/null
+++ b/maven-modules/maven-parent-pom-resolution/project-a/pom.xml
@@ -0,0 +1,20 @@
+
+
+ 4.0.0
+ project-a
+
+ com.baeldung
+ maven-parent-pom-resolution
+ 1.0-SNAPSHOT
+
+
+ pom
+
+
+ project-b
+ project-c
+
+
+
diff --git a/maven-modules/maven-parent-pom-resolution/project-a/project-b/pom.xml b/maven-modules/maven-parent-pom-resolution/project-a/project-b/pom.xml
new file mode 100644
index 0000000000..e3f5239efe
--- /dev/null
+++ b/maven-modules/maven-parent-pom-resolution/project-a/project-b/pom.xml
@@ -0,0 +1,15 @@
+
+
+ 4.0.0
+ project-b
+
+ com.baeldung
+ project-a
+ 1.0-SNAPSHOT
+
+
+ pom
+
+
diff --git a/maven-modules/maven-parent-pom-resolution/project-a/project-c/pom.xml b/maven-modules/maven-parent-pom-resolution/project-a/project-c/pom.xml
new file mode 100644
index 0000000000..3c7f70ae20
--- /dev/null
+++ b/maven-modules/maven-parent-pom-resolution/project-a/project-c/pom.xml
@@ -0,0 +1,20 @@
+
+
+ 4.0.0
+ project-c
+
+ com.baeldung
+ project-b
+ 1.0-SNAPSHOT
+ ../project-b/pom.xml
+
+
+ pom
+
+
+ project-d
+
+
+
diff --git a/maven-modules/maven-parent-pom-resolution/project-a/project-c/project-d/pom.xml b/maven-modules/maven-parent-pom-resolution/project-a/project-c/project-d/pom.xml
new file mode 100644
index 0000000000..973e44eb1f
--- /dev/null
+++ b/maven-modules/maven-parent-pom-resolution/project-a/project-c/project-d/pom.xml
@@ -0,0 +1,17 @@
+
+
+ 4.0.0
+ project-d
+
+ com.baeldung
+ project-a
+ 1.0-SNAPSHOT
+
+
+ ../../pom.xml
+
+ pom
+
+
diff --git a/maven-modules/pom.xml b/maven-modules/pom.xml
index a0c45234d2..fe3bbd2653 100644
--- a/maven-modules/pom.xml
+++ b/maven-modules/pom.xml
@@ -36,6 +36,7 @@
host-maven-repo-example
plugin-management
maven-surefire-plugin
+ maven-parent-pom-resolution