/******************************************************************************* * Copyright (c) 2007 Cambridge Semantics Incorporated. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * File: $Source$ * Created by: Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com </a>) * Created on: Nov 23, 2007 * Revision: $Id$ * * Contributors: * Cambridge Semantics Incorporated - initial API and implementation *******************************************************************************/ package org.openanzo.cache; import java.io.Writer; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantLock; import org.openanzo.exceptions.AnzoException; import org.openanzo.exceptions.ExceptionConstants; import org.openanzo.rdf.Constants; import org.openanzo.rdf.URI; import org.openanzo.rdf.Constants.OSGI; import org.openanzo.rdf.utils.SerializationConstants; import org.openanzo.services.AnzoPrincipal; import org.openanzo.services.IAuthenticationService; import org.openanzo.services.IOperationContext; import org.openanzo.services.serialization.CommonSerializationUtils; import org.osgi.service.event.Event; import org.osgi.service.event.EventHandler; /** * A cached authorization service caches the acls and results from requests, and updates caches based on updates. * * @author Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com</a>) * */ public class CachedAuthenticationService implements org.openanzo.services.IAuthenticationService, EventHandler { /** EventAdmin topic for user credentials changing */ public static final String USER_CREDENTIALS_CHANGED_TOPIC = "org/openanzo/cache/userCredentials"; /** EventAdmin topic for user roles changing */ public static final String USER_ROLES_CHANGED_TOPIC = "org/openanzo/cache/userRoles"; private final CachedAuthenticationServiceStats stats; private ICache<String, Map<String, Boolean>> passwordCache; private ICache<String, AnzoPrincipal> principalCache; private final IAuthenticationService parentService; private final ReentrantLock lock = new ReentrantLock(); /** * Create a new CacheAuthenticationService wrapping another authentication service * * @param parentService * service to wrap * @param provider * cache provider */ public CachedAuthenticationService(IAuthenticationService parentService, ICacheProvider provider) { this.parentService = parentService; this.stats = new CachedAuthenticationServiceStats(); this.stats.setEnabled(true); if (provider != null) { passwordCache = provider.<String, Map<String, Boolean>> openCache("AuthenticationPasswordCache", 20000, true); principalCache = provider.<String, AnzoPrincipal> openCache("AuthenticationPrincipalCache", 20000, true); } } public org.openanzo.services.DynamicServiceStats getStatistics() { return stats; } public String getName() { return "cached_" + parentService.getName(); } public String getDescription() { return "Cached Authentication Service"; } public AnzoPrincipal authenticateUser(IOperationContext context, String userName, String password) throws AnzoException { AnzoPrincipal result = null; long start = 0; if (stats.isEnabled()) { start = System.currentTimeMillis(); } lock.lock(); try { Boolean ok = null; Map<String, Boolean> userMap = passwordCache.get(userName); if (userMap != null) { ok = userMap.get(password); } if (ok != null) { if (ok) { if (stats.isEnabled()) stats.getAuthenticateUserPasswordCacheHit().increment(); result = principalCache.get(userName); } else { throw new AnzoException(ExceptionConstants.SERVER.UNKNOWN_USER_ERROR, userName); } } else { if (stats.isEnabled()) stats.getAuthenticateUserPasswordCacheMiss().increment(); } if (result == null) { if (stats.isEnabled()) stats.getAuthenticateUserPrincipalCacheMiss().increment(); try { userMap = passwordCache.get(userName); if (userMap == null) { userMap = new HashMap<String, Boolean>(); passwordCache.put(userName, userMap); } result = parentService.authenticateUser(context, userName, password); if (result != null) { userMap.put(password, Boolean.TRUE); principalCache.put(userName, result); principalCache.put(result.getUserURI().toString(), result); } else { userMap.put(password, Boolean.FALSE); } } catch (AnzoException ae) { userMap = passwordCache.get(userName); if (userMap == null) { userMap = new HashMap<String, Boolean>(); passwordCache.put(userName, userMap); } userMap.put(password, Boolean.FALSE); throw ae; } } else { if (stats.isEnabled()) stats.getAuthenticateUserPrincipalCacheHit().increment(); } return result; } finally { lock.unlock(); if (stats.isEnabled()) { stats.use("authenticateUser", (System.currentTimeMillis() - start)); } } } public void authenticateUser(IOperationContext context, String userName, String password, Writer output, String format) throws AnzoException { AnzoPrincipal result = authenticateUser(context, userName, password); CommonSerializationUtils.writeAnzoPrincipal(result, output, format); } public AnzoPrincipal getUserPrincipal(IOperationContext context, String userName) throws AnzoException { long start = 0; if (stats.isEnabled()) { start = System.currentTimeMillis(); } try { AnzoPrincipal result = principalCache.get(userName); if (result == null) { if (stats.isEnabled()) stats.getGetUserPrincipalCacheMiss().increment(); result = parentService.getUserPrincipal(context, userName); if (result != null) { principalCache.put(userName, result); principalCache.put(result.getUserURI().toString(), result); } } else { if (stats.isEnabled()) stats.getGetUserPrincipalCacheHit().increment(); } return result; } finally { if (stats.isEnabled()) { stats.use("getUserPrincipal", (System.currentTimeMillis() - start)); } } } public void getUserPrincipal(IOperationContext context, String userName, Writer output, String format) throws AnzoException { AnzoPrincipal result = getUserPrincipal(context, userName); if (result != null) { CommonSerializationUtils.writeAnzoPrincipal(result, output, format); } } public void handleEvent(Event event) { if (event.getTopic().equals(USER_CREDENTIALS_CHANGED_TOPIC)) { String username = (String) event.getProperty(SerializationConstants.userId); passwordCache.remove(username); } else if (event.getTopic().equals(USER_ROLES_CHANGED_TOPIC)) { String[] username = (String[]) event.getProperty(SerializationConstants.userId); String role = (String) event.getProperty(SerializationConstants.role); boolean added = (Boolean) event.getProperty(SerializationConstants.operation); URI roleURI = Constants.valueFactory.createURI(role); for (String name : username) { URI userURI = Constants.valueFactory.createURI(name); AnzoPrincipal principal = principalCache.get(userURI.toString()); if (principal != null) { if (added) { principal.getRoles().add(roleURI); } else { principal.getRoles().remove(roleURI); } } } } else if (event.getTopic().equals(OSGI.RESET_TOPIC)) { principalCache.clear(); passwordCache.clear(); } } }