/* * 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. */ package org.apache.cocoon.portal.profile.impl; import java.io.Serializable; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import org.apache.avalon.framework.CascadingRuntimeException; import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.avalon.framework.service.ServiceException; import org.apache.avalon.framework.service.ServiceSelector; import org.apache.cocoon.ProcessingException; import org.apache.cocoon.portal.PortalService; import org.apache.cocoon.portal.coplet.CopletFactory; import org.apache.cocoon.portal.coplet.CopletInstanceData; import org.apache.cocoon.portal.coplet.adapter.CopletAdapter; import org.apache.cocoon.portal.layout.Layout; import org.apache.cocoon.portal.layout.LayoutFactory; import org.apache.cocoon.portal.profile.PortalUser; import org.apache.cocoon.portal.profile.ProfileLS; import org.apache.cocoon.portal.util.DeltaApplicableReferencesAdjustable; import org.apache.cocoon.portal.util.ProfileException; import org.apache.cocoon.webapps.authentication.AuthenticationManager; import org.apache.cocoon.webapps.authentication.configuration.ApplicationConfiguration; import org.apache.cocoon.webapps.authentication.user.RequestState; import org.apache.cocoon.webapps.authentication.user.UserHandler; import org.apache.commons.collections.map.LinkedMap; import org.apache.commons.lang.exception.ExceptionUtils; import org.apache.excalibur.source.SourceNotFoundException; import org.apache.excalibur.source.SourceValidity; /** * The profile manager using the authentication framework * * @author <a href="mailto:cziegeler@s-und-n.de">Carsten Ziegeler</a> * @author <a href="mailto:bluetkemeier@s-und-n.de">Björn Lütkemeier</a> * @deprecated Use the {@link org.apache.cocoon.portal.profile.impl.GroupBasedProfileManager} * @version CVS $Id$ */ public class AuthenticationProfileManager extends AbstractUserProfileManager { protected ReadWriteLock lock = new ReadWriteLock(); protected Map attributes = new HashMap(); /** * Get the current authentication state of the user * @return the current authentication state of the user */ protected RequestState getRequestState() { AuthenticationManager authManager = null; try { authManager = (AuthenticationManager)this.manager.lookup(AuthenticationManager.ROLE); return authManager.getState(); } catch (ServiceException ce) { // ignore this here return null; } finally { this.manager.release( authManager ); } } /** * This loads a new profile */ protected Layout loadProfile(String layoutKey, PortalService service, CopletFactory copletFactory, LayoutFactory layoutFactory, ServiceSelector adapterSelector) throws Exception { final RequestState state = this.getRequestState(); final UserHandler handler = state.getHandler(); final ApplicationConfiguration ac = state.getApplicationConfiguration(); if ( ac == null ) { throw new ProcessingException("Configuration for portal not found in application configuration."); } final Configuration appConf = ac.getConfiguration("portal"); if ( appConf == null ) { throw new ProcessingException("Configuration for portal not found in application configuration."); } final Configuration config = appConf.getChild("profiles"); HashMap parameters = new HashMap(); parameters.put("config", config); parameters.put("handler", handler); CopletDataManager copletDataManager = null; try { this.lock.readLock(); // load coplet base data parameters.put("profiletype", "copletbasedata"); parameters.put("objectmap", null); Object[] result = this.getProfile(layoutKey, parameters, null, false, service); CopletBaseDataManager copletBaseDataManager = (CopletBaseDataManager)result[0]; // load coplet data parameters.put("profiletype", "copletdata"); parameters.put("objectmap", copletBaseDataManager.getCopletBaseData()); copletDataManager = (CopletDataManager)this.getDeltaProfile(layoutKey, parameters, service, copletFactory, ((Boolean)result[1]).booleanValue()); service.setAttribute("CopletData:" + layoutKey, copletDataManager); } finally { this.lock.releaseLocks(); } // load coplet instance data parameters.put("profiletype", "copletinstancedata"); parameters.put("objectmap", copletDataManager.getCopletData()); CopletInstanceDataManager copletInstanceDataManager = (CopletInstanceDataManager)this.getOrCreateProfile(layoutKey, parameters, service, copletFactory); service.setAttribute("CopletInstanceData:" + layoutKey, copletInstanceDataManager); // load layout parameters.put("profiletype", "layout"); parameters.put("objectmap", copletInstanceDataManager.getCopletInstanceData()); Layout layout = (Layout)this.getOrCreateProfile(layoutKey, parameters, service, layoutFactory); service.setAttribute("Layout:" + layoutKey, layout); // now invoke login on each instance Iterator iter = copletInstanceDataManager.getCopletInstanceData().values().iterator(); while ( iter.hasNext() ) { CopletInstanceData cid = (CopletInstanceData) iter.next(); CopletAdapter adapter = null; try { adapter = (CopletAdapter) adapterSelector.select(cid.getCopletData().getCopletBaseData().getCopletAdapterName()); adapter.login( cid ); } finally { adapterSelector.release( adapter ); } } return layout; } /* (non-Javadoc) * @see org.apache.cocoon.portal.profile.ProfileManager#saveUserCopletInstanceDatas(java.lang.String) */ public void saveUserCopletInstanceDatas(String layoutKey) { ProfileLS adapter = null; PortalService service = null; try { adapter = (ProfileLS) this.manager.lookup(ProfileLS.ROLE); service = (PortalService) this.manager.lookup(PortalService.ROLE); if (layoutKey == null) { layoutKey = service.getDefaultLayoutKey(); } final RequestState state = this.getRequestState(); final UserHandler handler = state.getHandler(); final HashMap parameters = new HashMap(); parameters.put("type", "user"); parameters.put("config", state.getApplicationConfiguration().getConfiguration("portal").getChild("profiles")); parameters.put("handler", handler); parameters.put("profiletype", "copletinstancedata"); final Map key = this.buildKey(service, parameters, layoutKey, false); final CopletInstanceDataManager profileManager = ((CopletInstanceDataManager) service.getAttribute("CopletInstanceData:" + layoutKey)); adapter.saveProfile(key, parameters, profileManager); } catch (Exception e) { // TODO throw new CascadingRuntimeException("Exception during save profile", e); } finally { this.manager.release(adapter); this.manager.release(service); } } /* (non-Javadoc) * @see org.apache.cocoon.portal.profile.ProfileManager#saveUserLayout(java.lang.String) */ public void saveUserLayout(String layoutKey) { ProfileLS adapter = null; PortalService service = null; try { adapter = (ProfileLS) this.manager.lookup(ProfileLS.ROLE); service = (PortalService) this.manager.lookup(PortalService.ROLE); if ( layoutKey == null ) { layoutKey = service.getDefaultLayoutKey(); } final RequestState state = this.getRequestState(); final UserHandler handler = state.getHandler(); final HashMap parameters = new HashMap(); parameters.put("type", "user"); parameters.put("config", state.getApplicationConfiguration().getConfiguration("portal").getChild("profiles")); parameters.put("handler", handler); parameters.put("profiletype", "layout"); final Map key = this.buildKey(service, parameters, layoutKey, false); final Layout layout = (Layout)service.getAttribute("Layout:" + layoutKey); adapter.saveProfile(key, parameters, layout); } catch (Exception e) { // TODO throw new CascadingRuntimeException("Exception during save profile", e); } finally { this.manager.release(adapter); this.manager.release(service); } } /** * Gets a profile and applies possible user and role deltas to it. */ protected Object getDeltaProfile(String layoutKey, Map parameters, PortalService service, Object factory, boolean forcedLoad) throws Exception { DeltaApplicableReferencesAdjustable result; Object object; parameters.put("type", "global"); Object global = this.getProfile(layoutKey, parameters, factory, forcedLoad, service)[0]; Object key = this.buildKey(service, parameters, layoutKey, true); result = (DeltaApplicableReferencesAdjustable)this.loadProfile(key, parameters, factory); // load role delta parameters.put("type", "role"); try { object = this.getProfile(layoutKey, parameters, factory, forcedLoad, service)[0]; if (object != null) result.applyDelta(object); } catch (Exception e) { if (!isSourceNotFoundException(e)) throw e; } // load user delta parameters.put("type", "user"); try { key = this.buildKey(service, parameters, layoutKey, true); object = this.loadProfile(key, parameters, factory); if (object != null) result.applyDelta(object); } catch (Exception e) { if (!isSourceNotFoundException(e)) throw e; } if (result == null) throw new SourceNotFoundException("Global profile does not exist."); // change references to objects where no delta has been applied result.adjustReferences(global); // FIXME this.attributes.put(key, result); return result; } /** * Gets a user profile and creates it by copying the role or the global profile. */ protected Object getOrCreateProfile(String layoutKey, Map parameters, PortalService service, Object factory) throws Exception { Object result; // load user profile parameters.put("type", "user"); Map keyMap = this.buildKey(service, parameters, layoutKey, true); try { result = this.loadProfile(keyMap, parameters, factory); } catch (Exception e1) { if (!isSourceNotFoundException(e1)) throw e1; // load role profile parameters.put("type", "role"); keyMap = this.buildKey(service, parameters, layoutKey, true); try { result = this.loadProfile(keyMap, parameters, factory); } catch (Exception e2) { if (!isSourceNotFoundException(e2)) throw e2; // load global profile parameters.put("type", "global"); keyMap = this.buildKey(service, parameters, layoutKey, true); result = this.loadProfile(keyMap, parameters, factory); } // save profile as user profile ProfileLS adapter = null; try { adapter = (ProfileLS) this.manager.lookup(ProfileLS.ROLE); parameters.put("type", "user"); keyMap = this.buildKey(service, parameters, layoutKey, false); //adapter.saveProfile(keyMap, parameters, result); } finally { this.manager.release(adapter); } } // FIXME this.attributes.put(keyMap, result); return result; } /** * Gets a profile. * @return result[0] is the profile, result[1] is a Boolean, * which signals whether the profile has been loaded or reused. */ protected Object[] getProfile(String layoutKey, Map parameters, Object factory, boolean forcedLoad, PortalService service) throws Exception { final Map key = this.buildKey(service, parameters, layoutKey, true); ProfileLS adapter = null; try { adapter = (ProfileLS)this.manager.lookup(ProfileLS.ROLE); Object result = this.checkValidity(key, parameters, forcedLoad, adapter); if ( result != null && !(result instanceof SourceValidity)) { return new Object[]{result, Boolean.FALSE}; } SourceValidity newValidity = (SourceValidity)result; this.lock.releaseReadLock(); this.lock.writeLock(); // check validity again in case of another thread has already loaded result = this.checkValidity(key, parameters, forcedLoad, adapter); if ( result != null && !(result instanceof SourceValidity) ) { return new Object[]{result, Boolean.FALSE}; } newValidity = (SourceValidity)result; Object object = adapter.loadProfile(key, parameters); this.prepareObject(object, factory); if (newValidity != null) { this.attributes.put(key, new Object[] {object, newValidity}); } return new Object[] {object, Boolean.TRUE}; } catch (SourceNotFoundException se) { this.getLogger().warn("Unable to locate profile: " + se.getMessage()); throw se; } catch (ProfileException pe) { this.getLogger().error("Error loading profile: " + pe.getMessage(), pe); throw pe; } catch (Exception t) { this.getLogger().error("Error loading profile.", t); throw t; } finally { this.manager.release(adapter); } } /** * If the profile is valid itself is returned, otherwise a newly created SourceValidity object is returned. */ protected Object checkValidity(Object key, Map parameters, boolean forcedLoad, ProfileLS adapter) { Object[] objects = (Object[])this.attributes.get(key); SourceValidity sourceValidity = null; int valid = SourceValidity.INVALID; if (objects != null) { sourceValidity = (SourceValidity) objects[1]; valid = sourceValidity.isValid(); if (!forcedLoad && valid == SourceValidity.VALID) return objects[0]; } SourceValidity newValidity = adapter.getValidity(key, parameters); if (!forcedLoad && valid == SourceValidity.UNKNOWN) { if (sourceValidity.isValid(newValidity) == SourceValidity.VALID) return objects[0]; } return newValidity; } /** * Loads a profile. */ protected Object loadProfile(Object key, Map map, Object factory) throws Exception { ProfileLS adapter = null; try { adapter = (ProfileLS) this.manager.lookup(ProfileLS.ROLE); Object object = adapter.loadProfile(key, map); this.prepareObject(object, factory); return object; } finally { this.manager.release(adapter); } } private boolean isSourceNotFoundException(Throwable t) { while (t != null) { if (t instanceof SourceNotFoundException) { return true; } t = ExceptionUtils.getCause(t); } return false; } protected Map buildKey(PortalService service, Map parameters, String layoutKey, boolean load) throws ProcessingException, ConfigurationException { // TODO Change to KeyManager usage final String type = (String)parameters.get("type"); final Configuration config = (Configuration) parameters.get("config"); final String profileType = (String)parameters.get("profiletype"); final String postFix = (load ? "load" : "save"); final UserHandler handler = (UserHandler)parameters.get("handler"); String uri = null; if (type == null) { uri = config.getChild(profileType + "-" + postFix).getAttribute("uri"); } else if (type.equals("global")) { uri = config.getChild(profileType + "-global-" + postFix).getAttribute("uri"); } else if (type.equals("role")) { uri = config.getChild(profileType + "-role-" + postFix).getAttribute("uri"); } else if (type.equals("user")) { uri = config.getChild(profileType + "-user-" + postFix).getAttribute("uri"); } Map key = new LinkedMap(); key.put("baseuri", uri); key.put("separator", "?"); key.put("portal", service.getPortalName()); key.put("layout", layoutKey); if ( type != null ) { key.put("type", type); if ( "role".equals(type) || "user".equals(type)) { key.put("role", handler.getContext().getContextInfo().get("role")); } if ( "user".equals(type) ) { key.put("user", handler.getUserId()); } } return key; } static class ReadWriteLock { private Thread activeWriter = null; private HashSet activeReaders = new HashSet(); private int waitingWriters = 0; public void readLock() throws InterruptedException { synchronized (ReadWriteLock.this) { while (this.activeWriter != null || this.waitingWriters != 0) { ReadWriteLock.this.wait(); } this.activeReaders.add(Thread.currentThread()); } } public void writeLock() throws InterruptedException { synchronized (ReadWriteLock.this) { Thread current = Thread.currentThread(); if (this.activeWriter != current) { this.waitingWriters++; while (this.activeWriter != null || this.activeReaders.size() != 0) { ReadWriteLock.this.wait(); } this.waitingWriters--; this.activeWriter = current; } } } public void releaseReadLock() { synchronized (ReadWriteLock.this) { Thread current = Thread.currentThread(); this.activeReaders.remove(current); if (this.activeReaders.size() == 0 && this.waitingWriters > 0) { ReadWriteLock.this.notifyAll(); } } } public void releaseLocks() { synchronized (ReadWriteLock.this) { Thread current = Thread.currentThread(); boolean notify = false; if (this.activeWriter == current) { this.activeWriter = null; notify = true; } this.activeReaders.remove(current); if (this.activeReaders.size() == 0 && this.waitingWriters > 0) { notify = true; } if (notify) { ReadWriteLock.this.notifyAll(); } } } } /* (non-Javadoc) * @see org.apache.cocoon.portal.profile.ProfileManager#getUser() */ public PortalUser getUser() { final RequestState state = this.getRequestState(); return new User(state); } protected static final class User implements PortalUser, Serializable { protected final RequestState state; public User(RequestState state) { this.state = state; } /* (non-Javadoc) * @see org.apache.cocoon.portal.profile.PortalUser#getGroup() */ public String getGroup() { // TODO Auto-generated method stub return null; } /* (non-Javadoc) * @see org.apache.cocoon.portal.profile.PortalUser#getUserName() */ public String getUserName() { return this.state.getHandler().getUserId(); } /* (non-Javadoc) * @see org.apache.cocoon.portal.profile.PortalUser#isUserInRole(java.lang.String) */ public boolean isUserInRole(String role) { return this.state.getHandler().isUserInRole(role); } } }