diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProvider.java
index e21a1c5..b99caac 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProvider.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProvider.java
@@ -21,8 +21,10 @@ import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
+import org.keycloak.authorization.attribute.Attributes;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.policy.evaluation.Evaluation;
+import org.keycloak.authorization.policy.evaluation.EvaluationContext;
import org.keycloak.authorization.policy.provider.PolicyProvider;
/**
@@ -30,15 +32,25 @@ import org.keycloak.authorization.policy.provider.PolicyProvider;
*/
public class TimePolicyProvider implements PolicyProvider {
- static String DEFAULT_DATE_PATTERN = "yyyy-MM-dd hh:mm:ss";
+ static String DEFAULT_DATE_PATTERN = "yyyy-MM-dd HH:mm:ss";
+
+ static String CONTEXT_TIME_ENTRY = "kc.time.date_time";
@Override
public void evaluate(Evaluation evaluation) {
Policy policy = evaluation.getPolicy();
SimpleDateFormat dateFormat = new SimpleDateFormat(DEFAULT_DATE_PATTERN);
- Date actualDate = new Date();
-
try {
+ String contextTime = null;
+ EvaluationContext context = evaluation.getContext();
+ if (context.getAttributes() != null && context.getAttributes().exists(CONTEXT_TIME_ENTRY)) {
+ Attributes.Entry contextTimeEntry = context.getAttributes().getValue(CONTEXT_TIME_ENTRY);
+ if (!contextTimeEntry.isEmpty()) {
+ contextTime = contextTimeEntry.asString(0);
+ }
+ }
+ Date actualDate = contextTime == null ? new Date() : dateFormat.parse(contextTime);
+
String notBefore = policy.getConfig().get("nbf");
if (notBefore != null && !"".equals(notBefore)) {
if (actualDate.before(dateFormat.parse(format(notBefore)))) {
diff --git a/services/src/main/java/org/keycloak/authorization/common/DefaultEvaluationContext.java b/services/src/main/java/org/keycloak/authorization/common/DefaultEvaluationContext.java
index 2551639..dec33cd 100644
--- a/services/src/main/java/org/keycloak/authorization/common/DefaultEvaluationContext.java
+++ b/services/src/main/java/org/keycloak/authorization/common/DefaultEvaluationContext.java
@@ -53,7 +53,7 @@ public class DefaultEvaluationContext implements EvaluationContext {
public Map<String, Collection<String>> getBaseAttributes() {
HashMap<String, Collection<String>> attributes = new HashMap<>();
- attributes.put("kc.time.date_time", Arrays.asList(new SimpleDateFormat("MM/dd/yyyy hh:mm:ss").format(new Date())));
+ attributes.put("kc.time.date_time", Arrays.asList(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
attributes.put("kc.client.network.ip_address", Arrays.asList(this.keycloakSession.getContext().getConnection().getRemoteAddr()));
attributes.put("kc.client.network.host", Arrays.asList(this.keycloakSession.getContext().getConnection().getRemoteHost()));
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationTest.java
index 64431fe..bedd4a7 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationTest.java
@@ -16,7 +16,10 @@
*/
package org.keycloak.testsuite.authz;
+import java.text.SimpleDateFormat;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -54,6 +57,7 @@ import org.keycloak.representations.idm.GroupRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.TimePolicyRepresentation;
import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.GroupBuilder;
@@ -124,6 +128,39 @@ public class PolicyEvaluationTest extends AbstractAuthzTest {
}
@Test
+ public void testCheckDateAndTime() {testingClient.server().run(PolicyEvaluationTest::testCheckDateAndTime);}
+
+ public static void testCheckDateAndTime(KeycloakSession session) {
+ session.getContext().setRealm(session.realms().getRealmByName("authz-test"));
+ AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class);
+ ClientModel clientModel = session.realms().getClientByClientId("resource-server-test", session.getContext().getRealm());
+ StoreFactory storeFactory = authorization.getStoreFactory();
+ ResourceServer resourceServer = storeFactory.getResourceServerStore().findById(clientModel.getId());
+ TimePolicyRepresentation policyRepresentation = new TimePolicyRepresentation();
+ policyRepresentation.setName("testCheckDateAndTime");
+
+ // set the notOnOrAfter for 1 hour from now
+ long notOnOrAfter = System.currentTimeMillis() + 3600000;
+ Date notOnOrAfterDate = new Date(notOnOrAfter);
+ policyRepresentation.setNotOnOrAfter(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(notOnOrAfterDate));
+
+ // evaluation should succeed with the default context as it uses the current time as the date to be compared.
+ Policy policy = storeFactory.getPolicyStore().create(policyRepresentation, resourceServer);
+ PolicyProvider provider = authorization.getProvider(policy.getType());
+ DefaultEvaluation evaluation = createEvaluation(session, authorization, resourceServer, policy);
+ provider.evaluate(evaluation);
+ Assert.assertEquals(Effect.PERMIT, evaluation.getEffect());
+
+ // lets now override the context to use a time that exceeds the time that was set in the policy.
+ long contextTime = System.currentTimeMillis() + 5400000;
+ Map<String,Collection<String>> attributes = new HashMap<>();
+ attributes.put("kc.time.date_time", Arrays.asList(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(contextTime))));
+ evaluation = createEvaluation(session, authorization, null, resourceServer, policy, attributes);
+ provider.evaluate(evaluation);
+ Assert.assertEquals(Effect.DENY, evaluation.getEffect());
+ }
+
+ @Test
public void testCheckUserInGroup() {
testingClient.server().run(PolicyEvaluationTest::testCheckUserInGroup);
}
@@ -599,6 +636,12 @@ public class PolicyEvaluationTest extends AbstractAuthzTest {
}
private static DefaultEvaluation createEvaluation(KeycloakSession session, AuthorizationProvider authorization, Resource resource, ResourceServer resourceServer, Policy policy) {
+ return createEvaluation(session, authorization, resource, resourceServer, policy, null);
+ }
+
+ private static DefaultEvaluation createEvaluation(KeycloakSession session, AuthorizationProvider authorization,
+ Resource resource, ResourceServer resourceServer, Policy policy,
+ Map<String, Collection<String>> contextAttributes) {
return new DefaultEvaluation(new ResourcePermission(resource, null, resourceServer), new DefaultEvaluationContext(new Identity() {
@Override
public String getId() {
@@ -609,8 +652,19 @@ public class PolicyEvaluationTest extends AbstractAuthzTest {
public Attributes getAttributes() {
return null;
}
- }, session), policy, policy, evaluation -> {
+ }, session) {
- }, authorization);
+ /*
+ * Allow specific tests to override/add attributes to the context.
+ */
+ @Override
+ public Map<String, Collection<String>> getBaseAttributes() {
+ Map<String, Collection<String>> baseAttributes = super.getBaseAttributes();
+ if (contextAttributes != null) {
+ baseAttributes.putAll(contextAttributes);
+ }
+ return baseAttributes;
+ }
+ }, policy, policy, evaluation -> {}, authorization);
}
}