001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: /* $Id: UsecaseMenuTransformer.java 407248 2006-05-17 13:20:01Z andreas $ */
020:
021: package org.apache.lenya.cms.cocoon.transformation;
022:
023: import java.io.IOException;
024: import java.util.Collections;
025: import java.util.Iterator;
026: import java.util.List;
027: import java.util.Map;
028:
029: import org.apache.avalon.framework.activity.Disposable;
030: import org.apache.avalon.framework.parameters.Parameters;
031: import org.apache.avalon.framework.service.ServiceSelector;
032: import org.apache.cocoon.ProcessingException;
033: import org.apache.cocoon.environment.SourceResolver;
034: import org.apache.cocoon.transformation.AbstractSAXTransformer;
035: import org.apache.lenya.ac.AccessController;
036: import org.apache.lenya.ac.AccessControllerResolver;
037: import org.apache.lenya.ac.Authorizer;
038: import org.apache.lenya.ac.Role;
039: import org.apache.lenya.cms.ac.PolicyUtil;
040: import org.apache.lenya.cms.ac.usecase.UsecaseAuthorizer;
041: import org.apache.lenya.cms.publication.Publication;
042: import org.apache.lenya.cms.publication.PublicationUtil;
043: import org.apache.lenya.cms.usecase.Usecase;
044: import org.apache.lenya.cms.usecase.UsecaseMessage;
045: import org.apache.lenya.cms.usecase.UsecaseResolver;
046: import org.apache.lenya.util.ServletHelper;
047: import org.xml.sax.Attributes;
048: import org.xml.sax.SAXException;
049: import org.xml.sax.helpers.AttributesImpl;
050:
051: /**
052: * This transformer disables menu items (by removing the href attribute) which are not allowed with
053: * respect to the usecase policies.
054: */
055: public class UsecaseMenuTransformer extends AbstractSAXTransformer
056: implements Disposable {
057:
058: /**
059: * <code>MENU_ELEMENT</code> The menu element
060: */
061: public static final String MENU_ELEMENT = "menu";
062: /**
063: * The menu namespace.
064: */
065: public static final String MENU_NAMESPACE = "http://apache.org/cocoon/lenya/menubar/1.0";
066: /**
067: * <code>ITEM_ELEMENT</code> The item element
068: */
069: public static final String ITEM_ELEMENT = "item";
070: /**
071: * <code>USECASE_ATTRIBUTE</code> The usecase attribute
072: */
073: public static final String USECASE_ATTRIBUTE = "usecase";
074: /**
075: * Comment for <code>HREF_ATTRIBUTE</code> The href attribute
076: */
077: public static final String HREF_ATTRIBUTE = "href";
078: /**
079: * <code>NAMESPACE</code> The usecase namespace
080: */
081: public static final String NAMESPACE = "http://apache.org/cocoon/lenya/usecase/1.0";
082:
083: /**
084: * (non-Javadoc)
085: *
086: * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String,
087: * java.lang.String, org.xml.sax.Attributes)
088: */
089: public void startElement(String uri, String localName, String raw,
090: Attributes attr) throws SAXException {
091:
092: Attributes attributes = attr;
093: List messages = null;
094:
095: UsecaseResolver usecaseResolver = null;
096: try {
097: usecaseResolver = (UsecaseResolver) this .manager
098: .lookup(UsecaseResolver.ROLE);
099:
100: if (this .authorizer != null
101: && localName.equals(ITEM_ELEMENT)) {
102: String usecaseName = attr.getValue(NAMESPACE,
103: USECASE_ATTRIBUTE);
104:
105: // filter item if usecase not allowed
106: if (usecaseName != null) {
107: if (getLogger().isDebugEnabled()) {
108: getLogger().debug(
109: "Found usecase [" + usecaseName + "]");
110: }
111: if (!this .authorizer.authorizeUsecase(usecaseName,
112: this .roles, this .publication)) {
113: if (getLogger().isDebugEnabled()) {
114: getLogger().debug("Usecase not authorized");
115: }
116: attributes = removeHrefAttribute(attr);
117: UsecaseMessage message = new UsecaseMessage(
118: "Access denied");
119: messages = Collections.singletonList(message);
120: }
121: }
122:
123: if (usecaseResolver.isRegistered(this .sourceUrl,
124: usecaseName)) {
125: Usecase usecase = null;
126: try {
127: usecase = usecaseResolver.resolve(
128: this .sourceUrl, usecaseName);
129: usecase.setSourceURL(this .sourceUrl);
130: usecase.setName(usecaseName);
131: if (attr.getValue(HREF_ATTRIBUTE) != null) {
132: passRequestParameters(usecase, attr
133: .getValue(HREF_ATTRIBUTE));
134: }
135: usecase.checkPreconditions();
136: if (usecase.hasErrors()) {
137: if (getLogger().isDebugEnabled()) {
138: getLogger()
139: .debug(
140: "Usecase preconditions not complied");
141: }
142:
143: attributes = removeHrefAttribute(attr);
144: messages = usecase.getErrorMessages();
145: }
146: } finally {
147: if (usecase != null) {
148: usecaseResolver.release(usecase);
149: }
150: }
151: }
152: }
153: } catch (final Exception e) {
154: throw new SAXException(e);
155: } finally {
156: if (usecaseResolver != null) {
157: this .manager.release(usecaseResolver);
158: }
159: }
160:
161: super .startElement(uri, localName, raw, attributes);
162:
163: if (messages != null) {
164: addMessages(messages);
165: }
166:
167: }
168:
169: /**
170: * Removes the <code>href</code> attribute.
171: *
172: * @param attr The original attributes.
173: * @return An attributes object.
174: */
175: protected Attributes removeHrefAttribute(Attributes attr) {
176: Attributes attributes = attr;
177: int hrefIndex = attributes.getIndex(HREF_ATTRIBUTE);
178: if (hrefIndex > -1) {
179: attributes = new AttributesImpl(attr);
180: ((AttributesImpl) attributes).removeAttribute(hrefIndex);
181: }
182: return attributes;
183: }
184:
185: protected void addMessages(List messages) throws SAXException {
186:
187: for (Iterator i = messages.iterator(); i.hasNext();) {
188: UsecaseMessage message = (UsecaseMessage) i.next();
189: super .startElement(MENU_NAMESPACE, "message", "message",
190: new AttributesImpl());
191: String messageString = message.getMessage();
192: super .characters(messageString.toCharArray(), 0,
193: messageString.length());
194: if (message.hasParameters()) {
195: String[] parameters = message.getParameters();
196: for (int p = 0; p < parameters.length; p++) {
197: super .startElement(MENU_NAMESPACE, "parameter",
198: "parameter", new AttributesImpl());
199: super .characters(parameters[p].toCharArray(), 0,
200: parameters[p].length());
201: super .endElement(MENU_NAMESPACE, "parameter",
202: "parameter");
203: }
204: }
205: super .endElement(MENU_NAMESPACE, "message", "message");
206: }
207:
208: }
209:
210: /**
211: * Pass the request parameters from the <code>href</code> attribute to the usecase handler.
212: *
213: * @param usecase The usecase handler.
214: * @param href The value of the <code>href</code> attribute.
215: */
216: void passRequestParameters(Usecase usecase, String href) {
217: int questionMarkIndex = href.indexOf("?");
218: if (questionMarkIndex > -1) {
219: String queryString = href.substring(questionMarkIndex + 1);
220: String[] nameValuePairs = queryString.split("&");
221: for (int i = 0; i < nameValuePairs.length; i++) {
222: String[] pair = nameValuePairs[i].split("=");
223: if (pair.length == 2) {
224: String name = pair[0];
225: String value = pair[1];
226: usecase.setParameter(name, value);
227: }
228: }
229: }
230: }
231:
232: private UsecaseAuthorizer authorizer;
233: private ServiceSelector serviceSelector = null;
234: private Role[] roles;
235: private Publication publication;
236: private AccessControllerResolver acResolver;
237: private String sourceUrl;
238:
239: /**
240: * @see org.apache.cocoon.sitemap.SitemapModelComponent#setup(org.apache.cocoon.environment.SourceResolver,
241: * java.util.Map, java.lang.String, org.apache.avalon.framework.parameters.Parameters)
242: */
243: public void setup(SourceResolver _resolver, Map _objectModel,
244: String src, Parameters _parameters)
245: throws ProcessingException, SAXException, IOException {
246:
247: super .setup(_resolver, _objectModel, src, _parameters);
248:
249: getLogger().debug("Setting up transformer");
250:
251: this .serviceSelector = null;
252: this .acResolver = null;
253: this .authorizer = null;
254:
255: this .sourceUrl = ServletHelper.getWebappURI(this .request);
256:
257: try {
258: this .roles = PolicyUtil.getRoles(this .request);
259: this .publication = PublicationUtil.getPublication(
260: this .manager, _objectModel);
261:
262: this .serviceSelector = (ServiceSelector) this .manager
263: .lookup(AccessControllerResolver.ROLE + "Selector");
264: this .acResolver = (AccessControllerResolver) this .serviceSelector
265: .select(AccessControllerResolver.DEFAULT_RESOLVER);
266: getLogger().debug(
267: "Resolved AC resolver [" + this .acResolver + "]");
268:
269: String webappUrl = ServletHelper.getWebappURI(this .request);
270: AccessController accessController = this .acResolver
271: .resolveAccessController(webappUrl);
272:
273: Authorizer[] authorizers = accessController
274: .getAuthorizers();
275: for (int i = 0; i < authorizers.length; i++) {
276: if (authorizers[i] instanceof UsecaseAuthorizer) {
277: this .authorizer = (UsecaseAuthorizer) authorizers[i];
278: }
279: }
280:
281: getLogger().debug(
282: "Using authorizer [" + this .authorizer + "]");
283: } catch (final Exception e) {
284: throw new ProcessingException(e);
285: }
286:
287: }
288:
289: /**
290: * @see org.apache.avalon.framework.activity.Disposable#dispose()
291: */
292: public void dispose() {
293: getLogger().debug("Disposing transformer");
294: if (this.serviceSelector != null) {
295: if (this.acResolver != null) {
296: this.serviceSelector.release(this.acResolver);
297: }
298: this.manager.release(this.serviceSelector);
299: }
300: }
301:
302: }
|