keycloak-uncached

[KEYCLOAK-6853] Make TimePolicyProvider use the kc.date.time_date

4/4/2018 2:37:03 PM

Details

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);
     }
 }