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, WITHOUT
013: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
014: * License for the specific language governing permissions and limitations under
015: * the License.
016: *
017: */
018:
019: package org.apache.lenya.ac.impl;
020:
021: import java.util.ArrayList;
022: import java.util.Arrays;
023: import java.util.HashMap;
024: import java.util.List;
025: import java.util.Map;
026: import java.util.regex.*;
027:
028: import org.apache.avalon.framework.activity.Disposable;
029: import org.apache.avalon.framework.component.Component;
030: import org.apache.avalon.framework.configuration.Configurable;
031: import org.apache.avalon.framework.configuration.Configuration;
032: import org.apache.avalon.framework.configuration.ConfigurationException;
033: import org.apache.avalon.framework.logger.AbstractLogEnabled;
034: import org.apache.avalon.framework.parameters.ParameterException;
035: import org.apache.avalon.framework.parameters.Parameterizable;
036: import org.apache.avalon.framework.parameters.Parameters;
037: import org.apache.avalon.framework.service.ServiceException;
038: import org.apache.avalon.framework.service.ServiceManager;
039: import org.apache.avalon.framework.service.ServiceSelector;
040: import org.apache.avalon.framework.service.Serviceable;
041: import org.apache.cocoon.environment.Request;
042: import org.apache.cocoon.environment.Session;
043: import org.apache.lenya.ac.AccessControlException;
044: import org.apache.lenya.ac.AccessController;
045: import org.apache.lenya.ac.Accreditable;
046: import org.apache.lenya.ac.AccreditableManager;
047: import org.apache.lenya.ac.AccreditableManagerFactory;
048: import org.apache.lenya.ac.Authenticator;
049: import org.apache.lenya.ac.Authorizer;
050: import org.apache.lenya.ac.IPRange;
051: import org.apache.lenya.ac.Identity;
052: import org.apache.lenya.ac.Item;
053: import org.apache.lenya.ac.ItemManagerListener;
054: import org.apache.lenya.ac.Machine;
055: import org.apache.lenya.ac.PolicyManager;
056: import org.apache.lenya.ac.Role;
057: import org.apache.lenya.util.ServletHelper;
058:
059: /**
060: * Default access controller implementation.
061: * @version $Id: DefaultAccessController.java 563459 2007-08-07 12:00:20Z
062: * nettings $
063: */
064: public class DefaultAccessController extends AbstractLogEnabled
065: implements AccessController, Configurable, Serviceable,
066: Disposable, ItemManagerListener {
067:
068: protected static final String AUTHORIZER_ELEMENT = "authorizer";
069: protected static final String TYPE_ATTRIBUTE = "type";
070: protected static final String ACCREDITABLE_MANAGER_ELEMENT = "accreditable-manager";
071: protected static final String POLICY_MANAGER_ELEMENT = "policy-manager";
072:
073: private static final String VALID_IP = "([0-9]{1,3}\\.){3}[0-9]{1,3}";
074: private ServiceManager manager;
075: private ServiceSelector authorizerSelector;
076: private ServiceSelector policyManagerSelector;
077: private AccreditableManager accreditableManager;
078: private PolicyManager policyManager;
079: private Map authorizers = new HashMap();
080: private List authorizerKeys = new ArrayList();
081: private Authenticator authenticator;
082:
083: /**
084: * @see org.apache.lenya.ac.AccessController#authenticate(org.apache.cocoon.environment.Request)
085: */
086: public boolean authenticate(Request request)
087: throws AccessControlException {
088:
089: assert request != null;
090: boolean authenticated = getAuthenticator().authenticate(
091: getAccreditableManager(), request);
092:
093: return authenticated;
094: }
095:
096: /**
097: * @see org.apache.lenya.ac.AccessController#authorize(org.apache.cocoon.environment.Request)
098: */
099: public boolean authorize(Request request)
100: throws AccessControlException {
101: assert request != null;
102:
103: boolean authorized = false;
104:
105: getLogger()
106: .debug(
107: "=========================================================");
108: getLogger().debug("Beginning authorization.");
109:
110: resolveRoles(request);
111:
112: if (hasAuthorizers()) {
113: Authorizer[] _authorizers = getAuthorizers();
114: int i = 0;
115: authorized = true;
116:
117: while ((i < _authorizers.length) && authorized) {
118:
119: if (getLogger().isDebugEnabled()) {
120: getLogger()
121: .debug(
122: "---------------------------------------------------------");
123: getLogger().debug(
124: "Invoking authorizer [" + _authorizers[i]
125: + "]");
126: }
127:
128: authorized = authorized
129: && _authorizers[i].authorize(request);
130:
131: if (getLogger().isDebugEnabled()) {
132: getLogger()
133: .debug(
134: "Authorizer [" + _authorizers[i]
135: + "] returned ["
136: + authorized + "]");
137: }
138:
139: i++;
140: }
141: }
142:
143: if (getLogger().isDebugEnabled()) {
144: getLogger()
145: .debug(
146: "=========================================================");
147: getLogger().debug(
148: "Authorization complete, result: [" + authorized
149: + "]");
150: getLogger()
151: .debug(
152: "=========================================================");
153: }
154:
155: return authorized;
156: }
157:
158: protected void resolveRoles(Request request)
159: throws AccessControlException {
160: String webappUrl = ServletHelper.getWebappURI(request);
161: Session session = request.getSession(true);
162: Identity identity = (Identity) session
163: .getAttribute(Identity.class.getName());
164:
165: Role[] roles;
166: if (identity.belongsTo(this .accreditableManager)) {
167: roles = this .policyManager.getGrantedRoles(
168: this .accreditableManager, identity, webappUrl);
169: } else {
170: roles = new Role[0];
171: getLogger()
172: .debug(
173: "No roles resolved for identity ["
174: + identity
175: + "] - belongs to wrong accreditable manager.");
176: }
177: saveRoles(request, roles);
178: }
179:
180: /**
181: * Saves the roles of the current identity to the request.
182: * @param request The request.
183: * @param roles The roles.
184: */
185: protected void saveRoles(Request request, Role[] roles) {
186: if (getLogger().isDebugEnabled()) {
187: StringBuffer rolesBuffer = new StringBuffer();
188: for (int i = 0; i < roles.length; i++) {
189: rolesBuffer.append(" ").append(roles[i]);
190: }
191: getLogger().debug(
192: "Adding roles [" + rolesBuffer + " ] to request ["
193: + request + "]");
194: }
195: request
196: .setAttribute(Role.class.getName(), Arrays
197: .asList(roles));
198: }
199:
200: /**
201: * Configures or parameterizes a component, depending on the implementation
202: * as Configurable or Parameterizable.
203: * @param component The component.
204: * @param configuration The configuration to use.
205: * @throws ConfigurationException when an error occurs during configuration.
206: * @throws ParameterException when an error occurs during parameterization.
207: */
208: public static void configureOrParameterize(Component component,
209: Configuration configuration) throws ConfigurationException,
210: ParameterException {
211: if (component instanceof Configurable) {
212: ((Configurable) component).configure(configuration);
213: }
214: if (component instanceof Parameterizable) {
215: Parameters parameters = Parameters
216: .fromConfiguration(configuration);
217: ((Parameterizable) component).parameterize(parameters);
218: }
219: }
220:
221: /**
222: * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
223: */
224: public void configure(Configuration conf)
225: throws ConfigurationException {
226:
227: try {
228: setupAccreditableManager(conf);
229: setupAuthorizers(conf);
230: setupPolicyManager(conf);
231: setupAuthenticator();
232: } catch (ConfigurationException e) {
233: throw e;
234: } catch (Exception e) {
235: throw new ConfigurationException("Configuration failed: ",
236: e);
237: }
238: }
239:
240: /**
241: * Creates the accreditable manager.
242: *
243: * @param configuration The access controller configuration.
244: * @throws ConfigurationException when the configuration failed.
245: * @throws ServiceException when something went wrong.
246: * @throws ParameterException when something went wrong.
247: */
248: protected void setupAccreditableManager(Configuration configuration)
249: throws ConfigurationException, ServiceException,
250: ParameterException {
251: Configuration config = configuration.getChild(
252: ACCREDITABLE_MANAGER_ELEMENT, false);
253: if (config != null) {
254: AccreditableManagerFactory factory = null;
255: try {
256: factory = (AccreditableManagerFactory) this .manager
257: .lookup(AccreditableManagerFactory.ROLE);
258: this .accreditableManager = factory
259: .getAccreditableManager(config);
260: this .accreditableManager.addItemManagerListener(this );
261: } finally {
262: if (factory != null) {
263: this .manager.release(factory);
264: }
265: }
266: }
267: }
268:
269: /**
270: * Creates the authorizers.
271: *
272: * @param configuration The access controller configuration.
273: * @throws ConfigurationException when the configuration failed.
274: * @throws ServiceException when something went wrong.
275: * @throws ParameterException when something went wrong.
276: */
277: protected void setupAuthorizers(Configuration configuration)
278: throws ServiceException, ConfigurationException,
279: ParameterException {
280: Configuration[] authorizerConfigurations = configuration
281: .getChildren(AUTHORIZER_ELEMENT);
282: if (authorizerConfigurations.length > 0) {
283: this .authorizerSelector = (ServiceSelector) this .manager
284: .lookup(Authorizer.ROLE + "Selector");
285:
286: for (int i = 0; i < authorizerConfigurations.length; i++) {
287: String type = authorizerConfigurations[i]
288: .getAttribute(TYPE_ATTRIBUTE);
289: if (getLogger().isDebugEnabled()) {
290: getLogger().debug(
291: "Adding authorizer [" + type + "]");
292: }
293:
294: Authorizer authorizer = (Authorizer) this .authorizerSelector
295: .select(type);
296: this .authorizerKeys.add(type);
297: this .authorizers.put(type, authorizer);
298: configureOrParameterize(authorizer,
299: authorizerConfigurations[i]);
300: }
301: }
302: }
303:
304: /**
305: * Creates the policy manager.
306: *
307: * @param configuration The access controller configuration.
308: * @throws ConfigurationException when the configuration failed.
309: * @throws ServiceException when something went wrong.
310: * @throws ParameterException when something went wrong.
311: */
312: protected void setupPolicyManager(Configuration configuration)
313: throws ServiceException, ConfigurationException,
314: ParameterException {
315: Configuration policyManagerConfiguration = configuration
316: .getChild(POLICY_MANAGER_ELEMENT, false);
317: if (policyManagerConfiguration != null) {
318: String policyManagerType = policyManagerConfiguration
319: .getAttribute(TYPE_ATTRIBUTE);
320: if (getLogger().isDebugEnabled()) {
321: getLogger().debug(
322: "Adding policy manager type: ["
323: + policyManagerType + "]");
324: }
325: this .policyManagerSelector = (ServiceSelector) this .manager
326: .lookup(PolicyManager.ROLE + "Selector");
327: this .policyManager = (PolicyManager) this .policyManagerSelector
328: .select(policyManagerType);
329: configureOrParameterize(this .policyManager,
330: policyManagerConfiguration);
331: }
332: }
333:
334: /**
335: * Sets up the authenticator.
336: * @throws ServiceException when something went wrong.
337: */
338: protected void setupAuthenticator() throws ServiceException {
339: this .authenticator = (Authenticator) this .manager
340: .lookup(Authenticator.ROLE);
341: }
342:
343: /**
344: * Set the global component manager.
345: *
346: * @param _manager The global component manager
347: * @throws ServiceException when something went wrong.
348: */
349: public void service(ServiceManager _manager)
350: throws ServiceException {
351: this .manager = _manager;
352: }
353:
354: /**
355: * Returns the service manager.
356: * @return A service manager.
357: */
358: protected ServiceManager getManager() {
359: return this .manager;
360: }
361:
362: /**
363: * Returns the authorizers of this action.
364: * @return An array of authorizers.
365: */
366: public Authorizer[] getAuthorizers() {
367:
368: Authorizer[] authorizerArray = new Authorizer[this .authorizers
369: .size()];
370: for (int i = 0; i < this .authorizers.size(); i++) {
371: String key = (String) this .authorizerKeys.get(i);
372: authorizerArray[i] = (Authorizer) this .authorizers.get(key);
373: }
374: return authorizerArray;
375: }
376:
377: /**
378: * Returns if this action has authorizers.
379: * @return A boolean value.
380: */
381: protected boolean hasAuthorizers() {
382: return !this .authorizers.isEmpty();
383: }
384:
385: /**
386: * @see org.apache.avalon.framework.activity.Disposable#dispose()
387: */
388: public void dispose() {
389:
390: if (this .accreditableManager != null) {
391: this .accreditableManager.removeItemManagerListener(this );
392: }
393:
394: if (this .policyManagerSelector != null) {
395: if (this .policyManager != null) {
396: this .policyManagerSelector.release(this .policyManager);
397: }
398: getManager().release(this .policyManagerSelector);
399: }
400:
401: if (this .authorizerSelector != null) {
402: Authorizer[] _authorizers = getAuthorizers();
403: for (int i = 0; i < _authorizers.length; i++) {
404: this .authorizerSelector.release(_authorizers[i]);
405: }
406: getManager().release(this .authorizerSelector);
407: }
408:
409: if (this .authenticator != null) {
410: getManager().release(this .authenticator);
411: }
412:
413: if (getLogger().isDebugEnabled()) {
414: getLogger().debug("Disposing [" + this + "]");
415: }
416: }
417:
418: /**
419: * Returns the accreditable manager.
420: * @return An accreditable manager.
421: */
422: public AccreditableManager getAccreditableManager() {
423: return this .accreditableManager;
424: }
425:
426: /**
427: * Returns the policy manager.
428: * @return A policy manager.
429: */
430: public PolicyManager getPolicyManager() {
431: return this .policyManager;
432: }
433:
434: /**
435: * Returns the authenticator.
436: * @return The authenticator.
437: */
438: public Authenticator getAuthenticator() {
439: return this .authenticator;
440: }
441:
442: /**
443: * Checks if this identity was initialized by this access controller.
444: *
445: * @param identity An identity.
446: * @return A boolean value.
447: * @throws AccessControlException when something went wrong.
448: */
449: public boolean ownsIdenity(Identity identity)
450: throws AccessControlException {
451: return identity.belongsTo(getAccreditableManager());
452: }
453:
454: /**
455: * @see org.apache.lenya.ac.AccessController#setupIdentity(org.apache.cocoon.environment.Request)
456: */
457: public void setupIdentity(Request request)
458: throws AccessControlException {
459: Session session = request.getSession(true);
460: if (!hasValidIdentity(session)) {
461: Identity identity = new Identity(getLogger());
462: identity.initialize();
463: String remoteAddress = request.getRemoteAddr();
464: String clientAddress = request.getHeader("x-forwarded-for");
465:
466: if (clientAddress != null) {
467: Pattern p = Pattern.compile(VALID_IP);
468: Matcher m = p.matcher(clientAddress);
469:
470: if (m.find()) {
471: remoteAddress = m.group();
472: }
473: }
474:
475: getLogger().info(
476: "Remote Address to use: [" + remoteAddress + "]");
477:
478: Machine machine = new Machine(remoteAddress);
479: IPRange[] ranges = this .accreditableManager
480: .getIPRangeManager().getIPRanges();
481: for (int i = 0; i < ranges.length; i++) {
482: if (ranges[i].contains(machine)) {
483: machine.addIPRange(ranges[i]);
484: }
485: }
486:
487: identity.addIdentifiable(machine);
488: session.setAttribute(Identity.class.getName(), identity);
489: }
490: }
491:
492: /**
493: * Checks if the session contains an identity that is not null and belongs
494: * to the used access controller.
495: *
496: * @param session The current session.
497: * @return A boolean value.
498: * @throws AccessControlException when something went wrong.
499: */
500: protected boolean hasValidIdentity(Session session)
501: throws AccessControlException {
502: boolean valid = true;
503: Identity identity = (Identity) session
504: .getAttribute(Identity.class.getName());
505: if (identity == null || !ownsIdenity(identity)) {
506: valid = false;
507: }
508: return valid;
509: }
510:
511: /**
512: * @see org.apache.lenya.ac.ItemManagerListener#itemAdded(org.apache.lenya.ac.Item)
513: */
514: public void itemAdded(Item item) throws AccessControlException {
515: if (getLogger().isDebugEnabled()) {
516: getLogger().debug("Item was added: [" + item + "]");
517: getLogger().debug("Notifying policy manager");
518: }
519: if (item instanceof Accreditable) {
520: getPolicyManager().accreditableAdded(
521: getAccreditableManager(), (Accreditable) item);
522: }
523: }
524:
525: /**
526: * @see org.apache.lenya.ac.ItemManagerListener#itemRemoved(org.apache.lenya.ac.Item)
527: */
528: public void itemRemoved(Item item) throws AccessControlException {
529: if (getLogger().isDebugEnabled()) {
530: getLogger().debug("Item was removed: [" + item + "]");
531: getLogger().debug("Notifying policy manager");
532: }
533:
534: if (!(item instanceof Role)) {
535: getPolicyManager().accreditableRemoved(
536: getAccreditableManager(), (Accreditable) item);
537: }
538: }
539:
540: }
|