You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by hn...@apache.org on 2021/04/19 11:21:14 UTC
[myfaces-tobago] branch tobago-4.x updated:
fix(AuthorizationHelper): RolesAllowed annotation for composite-components
This is an automated email from the ASF dual-hosted git repository.
hnoeth pushed a commit to branch tobago-4.x
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git
The following commit(s) were added to refs/heads/tobago-4.x by this push:
new 38696b5 fix(AuthorizationHelper): RolesAllowed annotation for composite-components
38696b5 is described below
commit 38696b501e812187a1b9c3ed79585c2dc1b3c5ad
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Mon Apr 12 13:50:55 2021 +0200
fix(AuthorizationHelper): RolesAllowed annotation for composite-components
* fix AuthorizationHelper if a controller is set as cc attribute
* example added
* qunit test added
Issue: TOBAGO-2008
---
.../internal/component/AbstractUICommandBase.java | 6 +-
.../tobago/internal/util/AuthorizationHelper.java | 46 ++++++++++--
.../tobago/example/demo/RoleController.java | 12 +++
.../2008-roles-allowed/Roles_Allowed.test.js | 86 ++++++++++++++++++++++
.../2008-roles-allowed/Roles_Allowed.xhtml | 47 ++++++++++++
.../resources/demo-components/rolesTest.xhtml | 31 ++++++++
6 files changed, 217 insertions(+), 11 deletions(-)
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUICommandBase.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUICommandBase.java
index 1045901..9169507 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUICommandBase.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUICommandBase.java
@@ -117,11 +117,7 @@ public abstract class AbstractUICommandBase extends UICommand
final AuthorizationHelper authorizationHelper = AuthorizationHelper.getInstance(facesContext);
final MethodExpression actionExpression = getActionExpression();
if (actionExpression != null) {
- final boolean authorized =
- authorizationHelper.isAuthorized(facesContext, actionExpression.getExpressionString());
- if (!authorized) {
- return false;
- }
+ return authorizationHelper.isAuthorized(facesContext, this, actionExpression.getExpressionString());
}
return true;
}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/AuthorizationHelper.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/AuthorizationHelper.java
index 07b9f97..cb71b00 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/AuthorizationHelper.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/AuthorizationHelper.java
@@ -26,6 +26,7 @@ import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.faces.bean.ManagedBean;
+import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
@@ -49,6 +50,8 @@ public class AuthorizationHelper {
private static final Pattern PATTERN = Pattern.compile("#\\{(\\w+(?:\\.\\w+)*)\\.(\\w+)(?:\\(.*\\))?}");
+ private static final String CC_ATTRS = "cc.attrs.";
+
private static final Annotation NULL_VALUE = new Annotation() {
@Override
public Class<? extends Annotation> annotationType() {
@@ -83,9 +86,9 @@ public class AuthorizationHelper {
facesContext.getELContext().getELResolver().getValue(facesContext.getELContext(), null, AUTHORIZATION_HELPER);
}
- public boolean isAuthorized(final FacesContext facesContext, final String expression) {
+ public boolean isAuthorized(final FacesContext facesContext, final UIComponent component, final String expression) {
- final Annotation securityAnnotation = getSecurityAnnotation(facesContext, expression);
+ final Annotation securityAnnotation = getSecurityAnnotation(facesContext, component, expression);
if (securityAnnotation == null) {
return true;
}
@@ -118,15 +121,16 @@ public class AuthorizationHelper {
return true;
}
- private Annotation getSecurityAnnotation(final FacesContext facesContext, final String expression) {
+ private Annotation getSecurityAnnotation(final FacesContext facesContext, final UIComponent component,
+ final String expression) {
+ Annotation securityAnnotation = null;
+
if (cache.containsKey(expression)) {
final Object obj = cache.get(expression);
if (obj instanceof Annotation) {
- return (Annotation) obj;
+ securityAnnotation = (Annotation) obj;
}
- return null;
} else {
- Annotation securityAnnotation = null;
final Matcher matcher = PATTERN.matcher(expression);
if (matcher.matches()) {
final String beanString = matcher.group(1);
@@ -167,11 +171,41 @@ public class AuthorizationHelper {
if (LOG.isInfoEnabled()) {
LOG.info("Security annotation '{}' saved for expression '{}'", securityAnnotation, expression);
}
+ }
+
+ if (securityAnnotation == NULL_VALUE && expression.contains(CC_ATTRS)) {
+
+ UIComponent compositeComponent = getParentCompositeComponent(component);
+ if (compositeComponent != null) {
+ final int attrNameStart = expression.indexOf(CC_ATTRS) + CC_ATTRS.length();
+ final int attrNameEnd = attrNameStart + expression.substring(attrNameStart).indexOf(".");
+ final String attrName = expression.substring(attrNameStart, attrNameEnd);
+
+ final String ccExpression = compositeComponent.getValueExpression(attrName).getExpressionString();
+ final int bracketStart = ccExpression.indexOf('{');
+ final int bracketEnd = ccExpression.indexOf("}");
+ final String trimmedCcExpression = ccExpression.substring(bracketStart + 1, bracketEnd).trim();
+ return getSecurityAnnotation(facesContext, component,
+ expression.replace(CC_ATTRS + attrName, trimmedCcExpression));
+ } else {
+ return securityAnnotation;
+ }
+ } else {
return securityAnnotation;
}
}
+ private UIComponent getParentCompositeComponent(final UIComponent component) {
+ if (component == null) {
+ return null;
+ } else if (UIComponent.isCompositeComponent(component)) {
+ return component;
+ } else {
+ return getParentCompositeComponent(component.getParent());
+ }
+ }
+
private Annotation getSecurityAnnotations(final AnnotatedElement annotatedElement) {
Annotation annotation = annotatedElement.getAnnotation(RolesAllowed.class);
if (annotation != null) {
diff --git a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/RoleController.java b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/RoleController.java
index 6d6fb60..a121b44 100644
--- a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/RoleController.java
+++ b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/RoleController.java
@@ -23,6 +23,7 @@ import javax.annotation.security.RolesAllowed;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
import java.io.Serializable;
+import java.time.LocalTime;
@SessionScoped
@Named
@@ -30,6 +31,7 @@ public class RoleController implements Serializable {
private String text;
private static final String OUTCOME_ADMIN = "admin";
+ private String time;
public String getText() {
return text;
@@ -53,4 +55,14 @@ public class RoleController implements Serializable {
public String admin() {
return OUTCOME_ADMIN;
}
+
+ public String getTime() {
+ return time;
+ }
+
+ @RolesAllowed({"demo-admin", "demo-guest"})
+ public String refreshTime() {
+ time = LocalTime.now().toString();
+ return null;
+ }
}
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/40-test/5500-compositeComponent/2008-roles-allowed/Roles_Allowed.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/40-test/5500-compositeComponent/2008-roles-allowed/Roles_Allowed.test.js
new file mode 100644
index 0000000..bad09d3
--- /dev/null
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/40-test/5500-compositeComponent/2008-roles-allowed/Roles_Allowed.test.js
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+QUnit.test("RolesAllowed as 'guest'", function (assert) {
+ var userFn = jQueryFrameFn("#page\\:mainForm\\:username\\:\\:field");
+ var passwordFn = jQueryFrameFn("#page\\:mainForm\\:password\\:\\:field");
+ var loginFn = jQueryFrameFn("#page\\:mainForm\\:login");
+ var logoutFn = jQueryFrameFn("#page\\:mainForm\\:logout");
+ var outputLabelFn = jQueryFrameFn("#page\\:mainForm\\:output label");
+ var outputValueFn = jQueryFrameFn("#page\\:mainForm\\:output .tobago-out");
+ var buttonFn = jQueryFrameFn("#page\\:mainForm\\:button");
+ var ccOutputLabelFn = jQueryFrameFn("#page\\:mainForm\\:ccRolesTest\\:out label");
+ var ccOutputValueFn = jQueryFrameFn("#page\\:mainForm\\:ccRolesTest\\:out .tobago-out");
+ var ccButtonFn = jQueryFrameFn("#page\\:mainForm\\:ccRolesTest\\:submit");
+
+ var url = document.getElementById("page:testframe").contentWindow.location.href;
+ var time;
+
+ assert.expect(5);
+ var TTT = new TobagoTestTool(assert);
+ TTT.asserts(5, function () {
+ assert.equal(loginFn().length, 1, "user must be logged out");
+ assert.equal(outputLabelFn().text(), "Output label");
+ assert.equal(buttonFn().attr("disabled"), "disabled");
+ assert.equal(ccOutputLabelFn().text(), "Label");
+ assert.equal(ccButtonFn().attr("disabled"), "disabled");
+ });
+ TTT.action(function () {
+ userFn().val("guest");
+ passwordFn().val("guest");
+ loginFn().click();
+ });
+ TTT.waitForResponse();
+ TTT.action(function () {
+ document.getElementById("page:testframe").contentWindow.location.href = url;
+ });
+ TTT.waitForResponse();
+ TTT.waitMs(2000);
+ TTT.asserts(5, function () {
+ assert.equal(logoutFn().length, 1, "user must be logged in");
+ assert.equal(outputLabelFn().text(), "Output label");
+ assert.equal(buttonFn().attr("disabled"), undefined);
+ assert.equal(ccOutputLabelFn().text(), "Label");
+ assert.equal(ccButtonFn().attr("disabled"), undefined);
+ });
+ TTT.action(function () {
+ time = outputValueFn().text();
+ buttonFn().click();
+ });
+ TTT.waitForResponse();
+ TTT.asserts(2, function () {
+ assert.notEqual(outputValueFn().text(), time);
+ assert.notEqual(ccOutputValueFn().text(), time);
+ });
+ TTT.action(function () {
+ time = ccOutputValueFn().text();
+ ccButtonFn().click();
+ });
+ TTT.waitForResponse();
+ TTT.asserts(2, function () {
+ assert.notEqual(outputValueFn().text(), time);
+ assert.notEqual(ccOutputValueFn().text(), time);
+ });
+ TTT.action(function () {
+ logoutFn().click();
+ });
+ TTT.waitForResponse();
+ TTT.asserts(1, function () {
+ assert.equal(loginFn().length, 1, "user must be logged out");
+ });
+ TTT.startTest();
+});
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/40-test/5500-compositeComponent/2008-roles-allowed/Roles_Allowed.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/40-test/5500-compositeComponent/2008-roles-allowed/Roles_Allowed.xhtml
new file mode 100644
index 0000000..83d86a1
--- /dev/null
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/40-test/5500-compositeComponent/2008-roles-allowed/Roles_Allowed.xhtml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+-->
+
+<ui:composition template="/main.xhtml"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:dc="http://java.sun.com/jsf/composite/demo-components"
+ xmlns:tc="http://myfaces.apache.org/tobago/component"
+ xmlns:ui="http://java.sun.com/jsf/facelets">
+ <ui:param name="title" value="Composite Components"/>
+
+ <tc:box label="Login">
+ <tc:panel rendered="#{request.userPrincipal == null}">
+ <tc:in id="username" value="#{loginController.username}" label="User" required="true"/>
+ <tc:in id="password" value="#{loginController.password}" password="true" label="Password" required="true"/>
+ <tc:button id="login" action="#{loginController.login}" label="Login"/>
+ </tc:panel>
+ <tc:panel rendered="#{request.userPrincipal != null}">
+ <tc:out value="logged in as '#{request.userPrincipal.name}'"/>
+ <tc:button id="logout" label="Logout" action="#{loginController.logout}"/>
+ </tc:panel>
+ </tc:box>
+
+ <tc:section label="No Composite Component">
+ <tc:out id="output" label="Output label" value="#{roleController.time}"/>
+ <tc:button id="button" label="Reload" action="#{roleController.refreshTime}"/>
+ </tc:section>
+
+ <tc:section label="RolesTest Composite Component">
+ <dc:rolesTest id="ccRolesTest" label="Label" controller="#{roleController}"/>
+ </tc:section>
+</ui:composition>
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/resources/demo-components/rolesTest.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/resources/demo-components/rolesTest.xhtml
new file mode 100644
index 0000000..e0e0cb9
--- /dev/null
+++ b/tobago-example/tobago-example-demo/src/main/webapp/resources/demo-components/rolesTest.xhtml
@@ -0,0 +1,31 @@
+<?xml version = "1.0" encoding = "UTF-8"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+-->
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns:cc="http://java.sun.com/jsf/composite"
+ xmlns:tc="http://myfaces.apache.org/tobago/component">
+<cc:interface>
+ <cc:attribute name="label"/>
+ <cc:attribute name="controller" type="RoleController"/>
+</cc:interface>
+<cc:implementation>
+ <tc:out id="out" label="#{cc.attrs.label}" value="#{cc.attrs.controller.time}"/>
+ <tc:button id="submit" label="Reload" action="#{cc.attrs.controller.refreshTime}"/>
+</cc:implementation>
+</html>