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: package org.apache.lenya.cms.ac.usecase.impl;
020:
021: import java.io.File;
022: import java.util.Arrays;
023: import java.util.HashMap;
024: import java.util.List;
025: import java.util.Map;
026:
027: import org.apache.avalon.framework.activity.Disposable;
028: import org.apache.avalon.framework.configuration.Configuration;
029: import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
030: import org.apache.avalon.framework.logger.AbstractLogEnabled;
031: import org.apache.avalon.framework.parameters.ParameterException;
032: import org.apache.avalon.framework.parameters.Parameterizable;
033: import org.apache.avalon.framework.parameters.Parameters;
034: import org.apache.avalon.framework.service.ServiceException;
035: import org.apache.avalon.framework.service.ServiceManager;
036: import org.apache.avalon.framework.service.Serviceable;
037: import org.apache.cocoon.environment.Request;
038: import org.apache.lenya.ac.AccessControlException;
039: import org.apache.lenya.ac.Role;
040: import org.apache.lenya.ac.cache.BuildException;
041: import org.apache.lenya.ac.cache.CachingException;
042: import org.apache.lenya.ac.cache.SourceCache;
043: import org.apache.lenya.cms.ac.PolicyUtil;
044: import org.apache.lenya.cms.ac.usecase.UsecaseAuthorizer;
045: import org.apache.lenya.cms.publication.Publication;
046: import org.apache.lenya.cms.publication.PublicationException;
047: import org.apache.lenya.cms.publication.PublicationUtil;
048:
049: /**
050: * Authorizer for usecases.
051: * <p>
052: * Supported parameters via {@link Parameterizable}:
053: * </p>
054: * <ul>
055: * <li> {@link #PARAMETER_CONFIGURATION} - location of the usecase policies file
056: * (parameterizable for testing purposes) </li>
057: * </ul>
058: * @version $Id: UsecaseAuthorizer.java 392449 2006-04-07 23:20:38Z michi $
059: */
060: public class UsecaseAuthorizerImpl extends AbstractLogEnabled implements
061: UsecaseAuthorizer, Serviceable, Disposable, Parameterizable {
062:
063: /**
064: * The name of the pseudo-usecase that governs access to pages.
065: */
066: public static final String VISIT_USECASE = "ac.visit";
067:
068: protected static final String PARAMETER_CONFIGURATION = "configuration";
069: protected static final String TYPE = "usecase";
070: protected static final String USECASE_PARAMETER = "lenya.usecase";
071: private static final String AC_CONFIGURATION_FILE = "config/access-control/access-control.xml"
072: .replace('/', File.separatorChar);
073:
074: private SourceCache cache;
075: /**
076: * the configuration URI for this component
077: */
078: private String configurationUri;
079: private ServiceManager manager;
080:
081: /**
082: * Maps publication IDs to their configuration URIs. This is a persistent
083: * map to avoid unnecessary publication lookups. Whenever an authorization
084: * request for a new publication is dealt with, the publication's
085: * configuration URI is stored, to be re-used on later occasions (for the
086: * lifetime of the component).
087: */
088: private Map pubId2configUri = new HashMap();
089:
090: /**
091: * @see org.apache.lenya.cms.ac.usecase.UsecaseAuthorizer#authorizeUsecase(java.lang.String,
092: * org.apache.lenya.ac.Role[],
093: * org.apache.lenya.cms.publication.Publication)
094: */
095: public boolean authorizeUsecase(String usecase, Role[] roles,
096: Publication publication) throws AccessControlException {
097: return authorizeUsecase(usecase, roles,
098: getConfigurationURI(publication));
099: }
100:
101: private boolean authorizeUsecase(String usecase, Role[] roles,
102: String _configurationUri) throws AccessControlException {
103: getLogger().debug("Authorizing usecase [" + usecase + "]");
104: boolean authorized = false;
105:
106: UsecaseRoles usecaseRoles = getUsecaseRoles(_configurationUri);
107:
108: if (usecaseRoles == null) {
109: throw new AccessControlException(
110: "Usecase policies configuration not found at ["
111: + _configurationUri + "]");
112: }
113:
114: if (usecaseRoles.hasRoles(usecase)) {
115: getLogger().debug("Roles for usecase found.");
116:
117: List usecaseRoleIds = Arrays.asList(usecaseRoles
118: .getRoles(usecase));
119:
120: int i = 0;
121: while (!authorized && i < roles.length) {
122: authorized = usecaseRoleIds.contains(roles[i].getId());
123: getLogger().debug(
124: "Authorization for role [" + roles[i].getId()
125: + "] is [" + authorized + "]");
126: i++;
127: }
128: } else {
129: getLogger().debug(
130: "No roles for usecase [" + usecase
131: + "] found. Denying access.");
132: }
133: return authorized;
134: }
135:
136: /**
137: * @see org.apache.lenya.cms.ac.usecase.UsecaseAuthorizer#isPermitted(java.lang.String,
138: * org.apache.lenya.cms.publication.Publication,
139: * org.apache.lenya.ac.Role)
140: */
141: public boolean isPermitted(String usecase, Publication publication,
142: Role role) throws AccessControlException {
143: String configUri = getConfigurationURI(publication);
144: UsecaseRoles usecaseRoles = getUsecaseRoles(configUri);
145: String[] roles = usecaseRoles.getRoles(usecase);
146: return Arrays.asList(roles).contains(role.getId());
147: }
148:
149: /**
150: * @see org.apache.lenya.cms.ac.usecase.UsecaseAuthorizer#setPermission(java.lang.String,
151: * org.apache.lenya.cms.publication.Publication,
152: * org.apache.lenya.ac.Role, boolean)
153: */
154: public void setPermission(String usecase, Publication publication,
155: Role role, boolean granted) throws AccessControlException {
156: String configUri = getConfigurationURI(publication);
157: if (configUri.startsWith("aggregate-")) {
158: configUri = configUri.substring("aggregate-".length());
159: }
160: UsecaseRoles usecaseRoles = getUsecaseRoles(configUri);
161: List roles = Arrays.asList(usecaseRoles.getRoles(usecase));
162: String roleId = role.getId();
163: if (granted) {
164: if (!roles.contains(roleId)) {
165: usecaseRoles.addRole(usecase, roleId);
166: }
167: } else {
168: if (roles.contains(roleId)) {
169: usecaseRoles.removeRole(usecase, roleId);
170: }
171: }
172: UsecaseRolesBuilder builder = new UsecaseRolesBuilder();
173: try {
174: builder.save(usecaseRoles, configUri, this .manager);
175: } catch (BuildException e) {
176: throw new AccessControlException(e);
177: }
178: }
179:
180: /**
181: * This method will substitute VISIT_USECASE if no USECASE_PARAMETER is set,
182: * so that it can be used to authorize plain page access as well.
183: * @see org.apache.lenya.ac.Authorizer#authorize(org.apache.cocoon.environment.Request)
184: */
185: public boolean authorize(Request request)
186: throws AccessControlException {
187:
188: String usecase = request.getParameter(USECASE_PARAMETER);
189: if (usecase == null || "".equals(usecase)) {
190: usecase = VISIT_USECASE;
191: }
192:
193: boolean authorized = false;
194:
195: try {
196: String _configurationUri;
197: // Check if the service has been parameterized with a
198: // configuration URI. This can be used for testing purposes etc.
199: if (getConfigurationURI() != null) {
200: _configurationUri = getConfigurationURI();
201: } else {
202: Publication publication = PublicationUtil
203: .getPublication(this .manager, request);
204: _configurationUri = getConfigurationURI(publication);
205: }
206:
207: Role[] roles = PolicyUtil.getRoles(request);
208: authorized = authorizeUsecase(usecase, roles,
209: _configurationUri);
210: } catch (final PublicationException e) {
211: throw new AccessControlException(e);
212: } catch (final AccessControlException e) {
213: throw new AccessControlException(e);
214: }
215:
216: return authorized;
217: }
218:
219: /**
220: * Returns the configuration source cache.
221: * @return A source cache.
222: */
223: private SourceCache getCache() {
224: return this .cache;
225: }
226:
227: /**
228: * Returns the source URI of the usecase role configuration file for a
229: * certain publication.
230: *
231: * @param publication The publication.
232: * @return A string representing a URI.
233: */
234: protected String getConfigurationURI(Publication publication) {
235:
236: String configURI = (String) this .pubId2configUri
237: .get(publication.getId());
238: if (configURI == null) {
239: try {
240: Configuration config = getConfiguration(publication);
241: Configuration[] authorizerConfigs = config
242: .getChildren("authorizer");
243: for (int i = 0; i < authorizerConfigs.length; i++) {
244: if (authorizerConfigs[i].getAttribute("type")
245: .equals("usecase")) {
246: Configuration paraConfig = authorizerConfigs[i]
247: .getChild("parameter");
248: configURI = paraConfig.getAttribute("value");
249: this .pubId2configUri.put(publication.getId(),
250: configURI);
251: }
252: }
253: } catch (Exception e) {
254: getLogger().error(e.getMessage(), e);
255: }
256: }
257: return configURI;
258: }
259:
260: protected UsecaseRoles getUsecaseRoles(String _configurationUri)
261: throws AccessControlException {
262: UsecaseRolesBuilder builder = new UsecaseRolesBuilder();
263: UsecaseRoles usecaseRoles;
264: try {
265: usecaseRoles = (UsecaseRoles) getCache().get(
266: _configurationUri, builder);
267: } catch (CachingException e) {
268: throw new AccessControlException(e);
269: }
270: return usecaseRoles;
271: }
272:
273: /**
274: * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
275: */
276: public void service(ServiceManager _manager)
277: throws ServiceException {
278: getLogger().debug("Servicing [" + getClass().getName() + "]");
279: this .manager = _manager;
280: this .cache = (SourceCache) _manager.lookup(SourceCache.ROLE);
281: }
282:
283: /**
284: * @see org.apache.avalon.framework.activity.Disposable#dispose()
285: */
286: public void dispose() {
287: if (getCache() != null) {
288: this .manager.release(getCache());
289: }
290: }
291:
292: public void parameterize(Parameters parameters)
293: throws ParameterException {
294: if (parameters.isParameter(PARAMETER_CONFIGURATION)) {
295: this .configurationUri = parameters
296: .getParameter(PARAMETER_CONFIGURATION);
297: }
298: }
299:
300: private String getConfigurationURI() {
301: return this .configurationUri;
302: }
303:
304: /**
305: * Retrieves access control configuration of a specific publication.
306: * @param publication The publication.
307: * @return Configuration
308: * @throws AccessControlException when something went wrong.
309: */
310: private Configuration getConfiguration(Publication publication)
311: throws AccessControlException {
312: File configurationFile = new File(publication.getDirectory(),
313: AC_CONFIGURATION_FILE);
314:
315: if (configurationFile.isFile()) {
316: try {
317: Configuration configuration = new DefaultConfigurationBuilder()
318: .buildFromFile(configurationFile);
319: return configuration;
320: } catch (Exception e) {
321: throw new AccessControlException(e);
322: }
323: } else {
324: throw new AccessControlException(
325: "No such file or directory: " + configurationFile);
326: }
327: }
328:
329: }
|