From e0f753bcf5ae44b9a8e9d1a8eac4c44aee0778f4 Mon Sep 17 00:00:00 2001 From: Pedro Igor Date: Mon, 24 Apr 2017 07:34:02 -0300 Subject: [PATCH] [KEYCLOAK-3135] - More changes to Policy Management API --- .../provider/js/JSPolicyProviderFactory.java | 52 ++++-- .../provider/time/TimePolicyProvider.java | 21 ++- .../time/TimePolicyProviderFactory.java | 108 +++++++++--- .../authorization/JSPolicyRepresentation.java | 23 ++- .../TimePolicyRepresentation.java | 132 ++++++++++++++ .../client/resource/JSPoliciesResource.java | 50 ++++++ .../client/resource/JSPolicyResource.java | 70 ++++++++ .../client/resource/PoliciesResource.java | 6 + .../client/resource/TimePoliciesResource.java | 50 ++++++ .../client/resource/TimePolicyResource.java | 69 ++++++++ .../authorization/JSPolicyManagementTest.java | 117 +++++++++++++ .../TimePolicyManagementTest.java | 165 ++++++++++++++++++ .../resources/js/authz/authz-controller.js | 69 ++++---- .../resource-server-policy-js-detail.html | 2 +- .../resource-server-policy-time-detail.html | 28 +-- 15 files changed, 851 insertions(+), 111 deletions(-) rename authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyAdminResource.java => core/src/main/java/org/keycloak/representations/idm/authorization/JSPolicyRepresentation.java (57%) create mode 100644 core/src/main/java/org/keycloak/representations/idm/authorization/TimePolicyRepresentation.java create mode 100644 integration/admin-client/src/main/java/org/keycloak/admin/client/resource/JSPoliciesResource.java create mode 100644 integration/admin-client/src/main/java/org/keycloak/admin/client/resource/JSPolicyResource.java create mode 100644 integration/admin-client/src/main/java/org/keycloak/admin/client/resource/TimePoliciesResource.java create mode 100644 integration/admin-client/src/main/java/org/keycloak/admin/client/resource/TimePolicyResource.java create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/JSPolicyManagementTest.java create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/TimePolicyManagementTest.java diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java index 617727f7c3..1a1ed34ecc 100644 --- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java +++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java @@ -1,30 +1,27 @@ package org.keycloak.authorization.policy.provider.js; -import java.util.function.Supplier; +import java.util.Map; -import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import org.keycloak.Config; import org.keycloak.authorization.AuthorizationProvider; -import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.authorization.model.Policy; import org.keycloak.authorization.policy.provider.PolicyProvider; -import org.keycloak.authorization.policy.provider.PolicyProviderAdminService; import org.keycloak.authorization.policy.provider.PolicyProviderFactory; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.representations.idm.authorization.JSPolicyRepresentation; +import org.keycloak.representations.idm.authorization.PolicyRepresentation; /** * @author Pedro Igor */ -public class JSPolicyProviderFactory implements PolicyProviderFactory { +public class JSPolicyProviderFactory implements PolicyProviderFactory { - private JSPolicyProvider provider = new JSPolicyProvider(new Supplier() { - @Override - public ScriptEngine get() { - return new ScriptEngineManager().getEngineByName("nashorn"); - } - }); + private static final String ENGINE = "nashorn"; + + private JSPolicyProvider provider = new JSPolicyProvider(() -> new ScriptEngineManager().getEngineByName(ENGINE)); @Override public String getName() { @@ -42,13 +39,40 @@ public class JSPolicyProviderFactory implements PolicyProviderFactory { } @Override - public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) { + public PolicyProvider create(KeycloakSession session) { return null; } @Override - public PolicyProvider create(KeycloakSession session) { - return null; + public JSPolicyRepresentation toRepresentation(Policy policy, JSPolicyRepresentation representation) { + representation.setCode(policy.getConfig().get("code")); + return representation; + } + + @Override + public Class getRepresentationType() { + return JSPolicyRepresentation.class; + } + + @Override + public void onCreate(Policy policy, JSPolicyRepresentation representation, AuthorizationProvider authorization) { + updatePolicy(policy, representation.getCode()); + } + + @Override + public void onUpdate(Policy policy, JSPolicyRepresentation representation, AuthorizationProvider authorization) { + updatePolicy(policy, representation.getCode()); + } + + @Override + public void onImport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) { + updatePolicy(policy, representation.getConfig().get("code")); + } + + private void updatePolicy(Policy policy, String code) { + Map config = policy.getConfig(); + config.put("code", code); + policy.setConfig(config); } @Override 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 7ce4c6ea35..6a383e3ada 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 @@ -33,21 +33,20 @@ public class TimePolicyProvider implements PolicyProvider { static String DEFAULT_DATE_PATTERN = "yyyy-MM-dd hh:mm:ss"; private final SimpleDateFormat dateFormat; - private final Date currentDate; public TimePolicyProvider() { this.dateFormat = new SimpleDateFormat(DEFAULT_DATE_PATTERN); - this.currentDate = new Date(); } @Override public void evaluate(Evaluation evaluation) { Policy policy = evaluation.getPolicy(); + Date actualDate = new Date(); try { String notBefore = policy.getConfig().get("nbf"); if (notBefore != null && !"".equals(notBefore)) { - if (this.currentDate.before(this.dateFormat.parse(format(notBefore)))) { + if (actualDate.before(this.dateFormat.parse(format(notBefore)))) { evaluation.deny(); return; } @@ -55,17 +54,17 @@ public class TimePolicyProvider implements PolicyProvider { String notOnOrAfter = policy.getConfig().get("noa"); if (notOnOrAfter != null && !"".equals(notOnOrAfter)) { - if (this.currentDate.after(this.dateFormat.parse(format(notOnOrAfter)))) { + if (actualDate.after(this.dateFormat.parse(format(notOnOrAfter)))) { evaluation.deny(); return; } } - if (isInvalid(Calendar.DAY_OF_MONTH, "dayMonth", policy) - || isInvalid(Calendar.MONTH, "month", policy) - || isInvalid(Calendar.YEAR, "year", policy) - || isInvalid(Calendar.HOUR_OF_DAY, "hour", policy) - || isInvalid(Calendar.MINUTE, "minute", policy)) { + if (isInvalid(actualDate, Calendar.DAY_OF_MONTH, "dayMonth", policy) + || isInvalid(actualDate, Calendar.MONTH, "month", policy) + || isInvalid(actualDate, Calendar.YEAR, "year", policy) + || isInvalid(actualDate, Calendar.HOUR_OF_DAY, "hour", policy) + || isInvalid(actualDate, Calendar.MINUTE, "minute", policy)) { evaluation.deny(); return; } @@ -76,10 +75,10 @@ public class TimePolicyProvider implements PolicyProvider { } } - private boolean isInvalid(int timeConstant, String configName, Policy policy) { + private boolean isInvalid(Date actualDate, int timeConstant, String configName, Policy policy) { Calendar calendar = Calendar.getInstance(); - calendar.setTime(this.currentDate); + calendar.setTime(actualDate); int dateField = calendar.get(timeConstant); diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java index 920cf45e5c..a3958b9202 100644 --- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java +++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java @@ -1,22 +1,22 @@ package org.keycloak.authorization.policy.provider.time; import java.text.SimpleDateFormat; +import java.util.Map; import org.keycloak.Config; import org.keycloak.authorization.AuthorizationProvider; import org.keycloak.authorization.model.Policy; -import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.policy.provider.PolicyProvider; -import org.keycloak.authorization.policy.provider.PolicyProviderAdminService; import org.keycloak.authorization.policy.provider.PolicyProviderFactory; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.representations.idm.authorization.PolicyRepresentation; +import org.keycloak.representations.idm.authorization.TimePolicyRepresentation; /** * @author Pedro Igor */ -public class TimePolicyProviderFactory implements PolicyProviderFactory { +public class TimePolicyProviderFactory implements PolicyProviderFactory { private TimePolicyProvider provider = new TimePolicyProvider(); @@ -35,46 +35,58 @@ public class TimePolicyProviderFactory implements PolicyProviderFactory getRepresentationType() { + return TimePolicyRepresentation.class; + } + + @Override + public TimePolicyRepresentation toRepresentation(Policy policy, TimePolicyRepresentation representation) { + Map config = policy.getConfig(); + + representation.setDayMonth(config.get("dayMonth")); + representation.setDayMonthEnd(config.get("dayMonthEnd")); + + representation.setMonth(config.get("month")); + representation.setMonthEnd(config.get("monthEnd")); + + representation.setYear(config.get("year")); + representation.setYearEnd(config.get("yearEnd")); + + representation.setHour(config.get("hour")); + representation.setHourEnd(config.get("hourEnd")); + + representation.setMinute(config.get("minute")); + representation.setMinuteEnd(config.get("minuteEnd")); + + representation.setNotBefore(config.get("nbf")); + representation.setNotOnOrAfter(config.get("noa")); + + return representation; } @Override @@ -96,4 +108,44 @@ public class TimePolicyProviderFactory implements PolicyProviderFactory config = policy.getConfig(); + + config.compute("nbf", (s, s2) -> nbf != null ? nbf : null); + config.compute("noa", (s, s2) -> noa != null ? noa : null); + + config.compute("dayMonth", (s, s2) -> representation.getDayMonth() != null ? representation.getDayMonth() : null); + config.compute("dayMonthEnd", (s, s2) -> representation.getDayMonthEnd() != null ? representation.getDayMonthEnd() : null); + + config.compute("month", (s, s2) -> representation.getMonth() != null ? representation.getMonth() : null); + config.compute("monthEnd", (s, s2) -> representation.getMonthEnd() != null ? representation.getMonthEnd() : null); + + config.compute("year", (s, s2) -> representation.getYear() != null ? representation.getYear() : null); + config.compute("yearEnd", (s, s2) -> representation.getYearEnd() != null ? representation.getYearEnd() : null); + + config.compute("hour", (s, s2) -> representation.getHour() != null ? representation.getHour() : null); + config.compute("hourEnd", (s, s2) -> representation.getHourEnd() != null ? representation.getHourEnd() : null); + + config.compute("minute", (s, s2) -> representation.getMinute() != null ? representation.getMinute() : null); + config.compute("minuteEnd", (s, s2) -> representation.getMinuteEnd() != null ? representation.getMinuteEnd() : null); + + policy.setConfig(config); + } + + private void validateFormat(String date) { + try { + new SimpleDateFormat(TimePolicyProvider.DEFAULT_DATE_PATTERN).parse(TimePolicyProvider.format(date)); + } catch (Exception e) { + throw new RuntimeException("Could not parse a date using format [" + date + "]"); + } + } } diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyAdminResource.java b/core/src/main/java/org/keycloak/representations/idm/authorization/JSPolicyRepresentation.java similarity index 57% rename from authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyAdminResource.java rename to core/src/main/java/org/keycloak/representations/idm/authorization/JSPolicyRepresentation.java index 7c2206af93..ed37a6d2a3 100644 --- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyAdminResource.java +++ b/core/src/main/java/org/keycloak/representations/idm/authorization/JSPolicyRepresentation.java @@ -1,13 +1,12 @@ /* - * JBoss, Home of Professional Open Source. - * Copyright 2016 Red Hat, Inc., and individual contributors - * as indicated by the @author tags. + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -15,14 +14,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package org.keycloak.authorization.policy.provider.time; - -import org.keycloak.authorization.policy.provider.PolicyProviderAdminService; +package org.keycloak.representations.idm.authorization; /** * @author Pedro Igor */ -public class TimePolicyAdminResource implements PolicyProviderAdminService { +public class JSPolicyRepresentation extends AbstractPolicyRepresentation { + private String code; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } } diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/TimePolicyRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/TimePolicyRepresentation.java new file mode 100644 index 0000000000..e4115e19dc --- /dev/null +++ b/core/src/main/java/org/keycloak/representations/idm/authorization/TimePolicyRepresentation.java @@ -0,0 +1,132 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.representations.idm.authorization; + +/** + * @author Pedro Igor + */ +public class TimePolicyRepresentation extends AbstractPolicyRepresentation { + + private String notBefore; + private String notOnOrAfter; + private String dayMonth; + private String dayMonthEnd; + private String month; + private String monthEnd; + private String year; + private String yearEnd; + private String hour; + private String hourEnd; + private String minute; + private String minuteEnd; + + public String getNotBefore() { + return notBefore; + } + + public void setNotBefore(String notBefore) { + this.notBefore = notBefore; + } + + public String getNotOnOrAfter() { + return notOnOrAfter; + } + + public void setNotOnOrAfter(String notOnOrAfter) { + this.notOnOrAfter = notOnOrAfter; + } + + public String getDayMonth() { + return dayMonth; + } + + public void setDayMonth(String dayMonth) { + this.dayMonth = dayMonth; + } + + public String getDayMonthEnd() { + return dayMonthEnd; + } + + public void setDayMonthEnd(String dayMonthEnd) { + this.dayMonthEnd = dayMonthEnd; + } + + public String getMonth() { + return month; + } + + public void setMonth(String month) { + this.month = month; + } + + public String getMonthEnd() { + return monthEnd; + } + + public void setMonthEnd(String monthEnd) { + this.monthEnd = monthEnd; + } + + public String getYear() { + return year; + } + + public void setYear(String year) { + this.year = year; + } + + public String getYearEnd() { + return yearEnd; + } + + public void setYearEnd(String yearEnd) { + this.yearEnd = yearEnd; + } + + public String getHour() { + return hour; + } + + public void setHour(String hour) { + this.hour = hour; + } + + public String getHourEnd() { + return hourEnd; + } + + public void setHourEnd(String hourEnd) { + this.hourEnd = hourEnd; + } + + public String getMinute() { + return minute; + } + + public void setMinute(String minute) { + this.minute = minute; + } + + public String getMinuteEnd() { + return minuteEnd; + } + + public void setMinuteEnd(String minuteEnd) { + this.minuteEnd = minuteEnd; + } +} diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/JSPoliciesResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/JSPoliciesResource.java new file mode 100644 index 0000000000..a6788eb001 --- /dev/null +++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/JSPoliciesResource.java @@ -0,0 +1,50 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.admin.client.resource; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.jboss.resteasy.annotations.cache.NoCache; +import org.keycloak.representations.idm.authorization.JSPolicyRepresentation; + +/** + * @author Pedro Igor + */ +public interface JSPoliciesResource { + + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + Response create(JSPolicyRepresentation representation); + + @Path("{id}") + JSPolicyResource findById(@PathParam("id") String id); + + @Path("/search") + @GET + @Produces(MediaType.APPLICATION_JSON) + @NoCache + JSPolicyRepresentation findByName(@QueryParam("name") String name); +} diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/JSPolicyResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/JSPolicyResource.java new file mode 100644 index 0000000000..433d71eefb --- /dev/null +++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/JSPolicyResource.java @@ -0,0 +1,70 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.admin.client.resource; + +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.jboss.resteasy.annotations.cache.NoCache; +import org.keycloak.representations.idm.authorization.JSPolicyRepresentation; +import org.keycloak.representations.idm.authorization.PolicyRepresentation; +import org.keycloak.representations.idm.authorization.ResourceRepresentation; +import org.keycloak.representations.idm.authorization.RolePolicyRepresentation; + +/** + * @author Pedro Igor + */ +public interface JSPolicyResource { + + @GET + @Produces(MediaType.APPLICATION_JSON) + @NoCache + JSPolicyRepresentation toRepresentation(); + + @PUT + @Consumes(MediaType.APPLICATION_JSON) + void update(JSPolicyRepresentation representation); + + @DELETE + void remove(); + + @Path("/associatedPolicies") + @GET + @Produces(MediaType.APPLICATION_JSON) + @NoCache + List associatedPolicies(); + + @Path("/dependentPolicies") + @GET + @Produces(MediaType.APPLICATION_JSON) + @NoCache + List dependentPolicies(); + + @Path("/resources") + @GET + @Produces("application/json") + @NoCache + List resources(); + +} diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PoliciesResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PoliciesResource.java index 433a1125ad..e98165221f 100644 --- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PoliciesResource.java +++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PoliciesResource.java @@ -74,4 +74,10 @@ public interface PoliciesResource { @Path("user") UserPoliciesResource users(); + + @Path("js") + JSPoliciesResource js(); + + @Path("time") + TimePoliciesResource time(); } diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/TimePoliciesResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/TimePoliciesResource.java new file mode 100644 index 0000000000..326bebe405 --- /dev/null +++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/TimePoliciesResource.java @@ -0,0 +1,50 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.admin.client.resource; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.jboss.resteasy.annotations.cache.NoCache; +import org.keycloak.representations.idm.authorization.TimePolicyRepresentation; + +/** + * @author Pedro Igor + */ +public interface TimePoliciesResource { + + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + Response create(TimePolicyRepresentation representation); + + @Path("{id}") + TimePolicyResource findById(@PathParam("id") String id); + + @Path("/search") + @GET + @Produces(MediaType.APPLICATION_JSON) + @NoCache + TimePolicyRepresentation findByName(@QueryParam("name") String name); +} diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/TimePolicyResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/TimePolicyResource.java new file mode 100644 index 0000000000..560c06a09c --- /dev/null +++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/TimePolicyResource.java @@ -0,0 +1,69 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.admin.client.resource; + +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.jboss.resteasy.annotations.cache.NoCache; +import org.keycloak.representations.idm.authorization.PolicyRepresentation; +import org.keycloak.representations.idm.authorization.ResourceRepresentation; +import org.keycloak.representations.idm.authorization.TimePolicyRepresentation; + +/** + * @author Pedro Igor + */ +public interface TimePolicyResource { + + @GET + @Produces(MediaType.APPLICATION_JSON) + @NoCache + TimePolicyRepresentation toRepresentation(); + + @PUT + @Consumes(MediaType.APPLICATION_JSON) + void update(TimePolicyRepresentation representation); + + @DELETE + void remove(); + + @Path("/associatedPolicies") + @GET + @Produces(MediaType.APPLICATION_JSON) + @NoCache + List associatedPolicies(); + + @Path("/dependentPolicies") + @GET + @Produces(MediaType.APPLICATION_JSON) + @NoCache + List dependentPolicies(); + + @Path("/resources") + @GET + @Produces("application/json") + @NoCache + List resources(); + +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/JSPolicyManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/JSPolicyManagementTest.java new file mode 100644 index 0000000000..f69ed2d819 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/JSPolicyManagementTest.java @@ -0,0 +1,117 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.admin.client.authorization; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.util.Collections; + +import javax.ws.rs.NotFoundException; +import javax.ws.rs.core.Response; + +import org.junit.Test; +import org.keycloak.admin.client.resource.AuthorizationResource; +import org.keycloak.admin.client.resource.JSPoliciesResource; +import org.keycloak.admin.client.resource.JSPolicyResource; +import org.keycloak.representations.idm.authorization.DecisionStrategy; +import org.keycloak.representations.idm.authorization.JSPolicyRepresentation; +import org.keycloak.representations.idm.authorization.Logic; + +/** + * @author Pedro Igor + */ +public class JSPolicyManagementTest extends AbstractPermissionManagementTest { + + @Test + public void testCreate() { + AuthorizationResource authorization = getClient().authorization(); + JSPolicyRepresentation representation = new JSPolicyRepresentation(); + + representation.setName("JS Policy"); + representation.setDescription("description"); + representation.setDecisionStrategy(DecisionStrategy.CONSENSUS); + representation.setLogic(Logic.NEGATIVE); + representation.setCode("$evaluation.grant();"); + + assertCreated(authorization, representation); + } + + @Test + public void testUpdate() { + AuthorizationResource authorization = getClient().authorization(); + JSPolicyRepresentation representation = new JSPolicyRepresentation(); + + representation.setName("Update JS Policy"); + representation.setDescription("description"); + representation.setDecisionStrategy(DecisionStrategy.CONSENSUS); + representation.setLogic(Logic.NEGATIVE); + representation.setCode("$evaluation.grant();"); + + assertCreated(authorization, representation); + + representation.setName("changed"); + representation.setDescription("changed"); + representation.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE); + representation.setLogic(Logic.POSITIVE); + representation.setCode("$evaluation.deny()"); + + JSPoliciesResource policies = authorization.policies().js(); + JSPolicyResource permission = policies.findById(representation.getId()); + + permission.update(representation); + assertRepresentation(representation, permission); + } + + @Test + public void testDelete() { + AuthorizationResource authorization = getClient().authorization(); + JSPolicyRepresentation representation = new JSPolicyRepresentation(); + + representation.setName("Test Delete Policy"); + representation.setCode("$evaluation.grant()"); + + JSPoliciesResource policies = authorization.policies().js(); + Response response = policies.create(representation); + JSPolicyRepresentation created = response.readEntity(JSPolicyRepresentation.class); + + policies.findById(created.getId()).remove(); + + JSPolicyResource removed = policies.findById(created.getId()); + + try { + removed.toRepresentation(); + fail("Permission not removed"); + } catch (NotFoundException ignore) { + + } + } + + private void assertCreated(AuthorizationResource authorization, JSPolicyRepresentation representation) { + JSPoliciesResource permissions = authorization.policies().js(); + Response response = permissions.create(representation); + JSPolicyRepresentation created = response.readEntity(JSPolicyRepresentation.class); + JSPolicyResource permission = permissions.findById(created.getId()); + assertRepresentation(representation, permission); + } + + private void assertRepresentation(JSPolicyRepresentation representation, JSPolicyResource permission) { + JSPolicyRepresentation actual = permission.toRepresentation(); + assertRepresentation(representation, actual, () -> permission.resources(), () -> Collections.emptyList(), () -> permission.associatedPolicies()); + assertEquals(representation.getCode(), actual.getCode()); + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/TimePolicyManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/TimePolicyManagementTest.java new file mode 100644 index 0000000000..0c34705364 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/TimePolicyManagementTest.java @@ -0,0 +1,165 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.admin.client.authorization; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.util.Collections; + +import javax.ws.rs.NotFoundException; +import javax.ws.rs.core.Response; + +import org.junit.Test; +import org.keycloak.admin.client.resource.AuthorizationResource; +import org.keycloak.admin.client.resource.TimePoliciesResource; +import org.keycloak.admin.client.resource.TimePolicyResource; +import org.keycloak.representations.idm.authorization.DecisionStrategy; +import org.keycloak.representations.idm.authorization.TimePolicyRepresentation; +import org.keycloak.representations.idm.authorization.Logic; + +/** + * @author Pedro Igor + */ +public class TimePolicyManagementTest extends AbstractPermissionManagementTest { + + @Test + public void testCreate() { + AuthorizationResource authorization = getClient().authorization(); + assertCreated(authorization, createRepresentation("Time Policy")); + } + + @Test + public void testUpdate() { + AuthorizationResource authorization = getClient().authorization(); + TimePolicyRepresentation representation = createRepresentation("Update Time Policy"); + + assertCreated(authorization, representation); + + representation.setName("changed"); + representation.setDescription("changed"); + representation.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE); + representation.setLogic(Logic.POSITIVE); + representation.setDayMonth("11"); + representation.setDayMonthEnd("22"); + representation.setMonth("7"); + representation.setMonthEnd("9"); + representation.setYear("2019"); + representation.setYearEnd("2030"); + representation.setHour("15"); + representation.setHourEnd("23"); + representation.setMinute("55"); + representation.setMinuteEnd("58"); + representation.setNotBefore("2019-01-01 00:00:00"); + representation.setNotOnOrAfter("2019-02-03 00:00:00"); + + TimePoliciesResource policies = authorization.policies().time(); + TimePolicyResource permission = policies.findById(representation.getId()); + + permission.update(representation); + assertRepresentation(representation, permission); + + representation.setDayMonth(null); + representation.setDayMonthEnd(null); + representation.setMonth(null); + representation.setMonthEnd(null); + representation.setYear(null); + representation.setYearEnd(null); + representation.setHour(null); + representation.setHourEnd(null); + representation.setMinute(null); + representation.setMinuteEnd(null); + representation.setNotBefore(null); + representation.setNotOnOrAfter("2019-02-03 00:00:00"); + + permission.update(representation); + assertRepresentation(representation, permission); + + representation.setNotOnOrAfter(null); + representation.setHour("2"); + + permission.update(representation); + assertRepresentation(representation, permission); + } + + @Test + public void testDelete() { + AuthorizationResource authorization = getClient().authorization(); + TimePolicyRepresentation representation = createRepresentation("Test Delete Policy"); + TimePoliciesResource policies = authorization.policies().time(); + Response response = policies.create(representation); + TimePolicyRepresentation created = response.readEntity(TimePolicyRepresentation.class); + + policies.findById(created.getId()).remove(); + + TimePolicyResource removed = policies.findById(created.getId()); + + try { + removed.toRepresentation(); + fail("Permission not removed"); + } catch (NotFoundException ignore) { + + } + } + + private TimePolicyRepresentation createRepresentation(String name) { + TimePolicyRepresentation representation = new TimePolicyRepresentation(); + + representation.setName(name); + representation.setDescription("description"); + representation.setDecisionStrategy(DecisionStrategy.CONSENSUS); + representation.setLogic(Logic.NEGATIVE); + representation.setDayMonth("1"); + representation.setDayMonthEnd("2"); + representation.setMonth("3"); + representation.setMonthEnd("4"); + representation.setYear("5"); + representation.setYearEnd("6"); + representation.setHour("7"); + representation.setHourEnd("8"); + representation.setMinute("9"); + representation.setMinuteEnd("10"); + representation.setNotBefore("2017-01-01 00:00:00"); + representation.setNotOnOrAfter("2017-02-01 00:00:00"); + return representation; + } + + private void assertCreated(AuthorizationResource authorization, TimePolicyRepresentation representation) { + TimePoliciesResource permissions = authorization.policies().time(); + Response response = permissions.create(representation); + TimePolicyRepresentation created = response.readEntity(TimePolicyRepresentation.class); + TimePolicyResource permission = permissions.findById(created.getId()); + assertRepresentation(representation, permission); + } + + private void assertRepresentation(TimePolicyRepresentation representation, TimePolicyResource permission) { + TimePolicyRepresentation actual = permission.toRepresentation(); + assertRepresentation(representation, actual, () -> permission.resources(), () -> Collections.emptyList(), () -> permission.associatedPolicies()); + assertEquals(representation.getDayMonth(), actual.getDayMonth()); + assertEquals(representation.getDayMonthEnd(), actual.getDayMonthEnd()); + assertEquals(representation.getMonth(), actual.getMonth()); + assertEquals(representation.getMonthEnd(), actual.getMonthEnd()); + assertEquals(representation.getYear(), actual.getYear()); + assertEquals(representation.getYearEnd(), actual.getYearEnd()); + assertEquals(representation.getHour(), actual.getHour()); + assertEquals(representation.getHourEnd(), actual.getHourEnd()); + assertEquals(representation.getMinute(), actual.getMinute()); + assertEquals(representation.getMinuteEnd(), actual.getMinuteEnd()); + assertEquals(representation.getNotBefore(), actual.getNotBefore()); + assertEquals(representation.getNotOnOrAfter(), actual.getNotOnOrAfter()); + } +} diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js index ed5620ca5a..a032243e23 100644 --- a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js @@ -1636,7 +1636,6 @@ module.controller('ResourceServerPolicyJSDetailCtrl', function($scope, $route, $ $scope.initEditor = function(editor){ editor.$blockScrolling = Infinity; var session = editor.getSession(); - session.setMode('ace/mode/javascript'); }; }, @@ -1646,15 +1645,14 @@ module.controller('ResourceServerPolicyJSDetailCtrl', function($scope, $route, $ }, onUpdate : function() { - + delete $scope.policy.config; }, onInitCreate : function(newPolicy) { - newPolicy.config = {}; }, onCreate : function() { - + delete $scope.policy.config; } }, realm, client, $scope); }); @@ -1669,60 +1667,63 @@ module.controller('ResourceServerPolicyTimeDetailCtrl', function($scope, $route, }, onInitUpdate : function(policy) { - if (policy.config.dayMonth) { - policy.config.dayMonth = parseInt(policy.config.dayMonth); + if (policy.dayMonth) { + policy.dayMonth = parseInt(policy.dayMonth); } - if (policy.config.dayMonthEnd) { - policy.config.dayMonthEnd = parseInt(policy.config.dayMonthEnd); + if (policy.dayMonthEnd) { + policy.dayMonthEnd = parseInt(policy.dayMonthEnd); } - if (policy.config.month) { - policy.config.month = parseInt(policy.config.month); + if (policy.month) { + policy.month = parseInt(policy.month); } - if (policy.config.monthEnd) { - policy.config.monthEnd = parseInt(policy.config.monthEnd); + if (policy.monthEnd) { + policy.monthEnd = parseInt(policy.monthEnd); } - if (policy.config.year) { - policy.config.year = parseInt(policy.config.year); + if (policy.year) { + policy.year = parseInt(policy.year); } - if (policy.config.yearEnd) { - policy.config.yearEnd = parseInt(policy.config.yearEnd); + if (policy.yearEnd) { + policy.yearEnd = parseInt(policy.yearEnd); } - if (policy.config.hour) { - policy.config.hour = parseInt(policy.config.hour); + if (policy.hour) { + policy.hour = parseInt(policy.hour); } - if (policy.config.hourEnd) { - policy.config.hourEnd = parseInt(policy.config.hourEnd); + if (policy.hourEnd) { + policy.hourEnd = parseInt(policy.hourEnd); } - if (policy.config.minute) { - policy.config.minute = parseInt(policy.config.minute); + if (policy.minute) { + policy.minute = parseInt(policy.minute); } - if (policy.config.minuteEnd) { - policy.config.minuteEnd = parseInt(policy.config.minuteEnd); + if (policy.minuteEnd) { + policy.minuteEnd = parseInt(policy.minuteEnd); } }, onUpdate : function() { - + delete $scope.policy.config; }, onInitCreate : function(newPolicy) { - newPolicy.config.expirationTime = 1; - newPolicy.config.expirationUnit = 'Minutes'; }, onCreate : function() { - + delete $scope.policy.config; } }, realm, client, $scope); $scope.isRequired = function () { var policy = $scope.policy; - if (policy.config.noa || policy.config.nbf - || policy.config.dayMonth - || policy.config.month - || policy.config.year - || policy.config.hour - || policy.config.minute) { + + if (!policy) { + return true; + } + + if (policy.notOnOrAfter || policy.notBefore + || policy.dayMonth + || policy.month + || policy.year + || policy.hour + || policy.minute) { return false; } return true; diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-js-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-js-detail.html index fb2fbc49cb..6e3951526d 100644 --- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-js-detail.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-js-detail.html @@ -35,7 +35,7 @@
-
+
{{:: 'authz-policy-js-code.tooltip' | translate}}
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-time-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-time-detail.html index 517773470f..e265804bd7 100644 --- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-time-detail.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-time-detail.html @@ -34,58 +34,58 @@ {{:: 'authz-policy-description.tooltip' | translate}}
- +
- +
{{:: 'authz-policy-time-not-before.tooltip' | translate}}
- +
- +
{{:: 'authz-policy-time-not-on-after.tooltip' | translate}}
- +
-   to   +   to  
{{:: 'authz-policy-time-day-month.tooltip' | translate}}
- +
-   to   +   to  
{{:: 'authz-policy-time-month.tooltip' | translate}}
- +
-   to   +   to  
{{:: 'authz-policy-time-year.tooltip' | translate}}
- +
-   to   +   to  
{{:: 'authz-policy-time-hour.tooltip' | translate}}
- +
-   to   +   to  
{{:: 'authz-policy-time-minute.tooltip' | translate}}