001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2006-2008 Sun Microsystems, Inc. 015 * Portions Copyright 2014-2016 ForgeRock AS. 016 */ 017package org.opends.server.core; 018 019import static org.opends.messages.ConfigMessages.*; 020import static org.opends.server.util.ServerConstants.*; 021import static org.opends.server.util.StaticUtils.*; 022 023import java.util.ArrayList; 024import java.util.LinkedHashMap; 025import java.util.List; 026import java.util.concurrent.atomic.AtomicReference; 027 028import org.forgerock.i18n.LocalizableMessage; 029import org.forgerock.i18n.slf4j.LocalizedLogger; 030import org.forgerock.opendj.config.ClassPropertyDefinition; 031import org.forgerock.opendj.config.server.ConfigChangeResult; 032import org.forgerock.opendj.config.server.ConfigException; 033import org.forgerock.opendj.config.server.ConfigurationChangeListener; 034import org.forgerock.opendj.ldap.DN; 035import org.forgerock.opendj.ldap.ResultCode; 036import org.forgerock.opendj.server.config.meta.AccessControlHandlerCfgDefn; 037import org.forgerock.opendj.server.config.server.AccessControlHandlerCfg; 038import org.forgerock.util.Utils; 039import org.opends.server.api.AccessControlHandler; 040import org.opends.server.api.AlertGenerator; 041import org.opends.server.types.InitializationException; 042 043/** 044 * This class manages the application-wide access-control configuration. 045 * <p> 046 * When access control is disabled a default "permissive" access control 047 * implementation is used, which permits all operations regardless of the 048 * identity of the user. 049 */ 050public final class AccessControlConfigManager 051 implements AlertGenerator , 052 ConfigurationChangeListener<AccessControlHandlerCfg> 053{ 054 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 055 056 private static final String CLASS_NAME = 057 "org.opends.server.core.AccessControlConfigManager"; 058 059 /** The single application-wide instance. */ 060 private static AccessControlConfigManager instance; 061 062 /** The active access control implementation. */ 063 private AtomicReference<AccessControlHandler<?>> accessControlHandler; 064 065 /** The current configuration. */ 066 private AccessControlHandlerCfg currentConfiguration; 067 068 private ServerContext serverContext; 069 070 /** Creates a new instance of this access control configuration manager. */ 071 private AccessControlConfigManager() 072 { 073 this.accessControlHandler = new AtomicReference<AccessControlHandler<?>>(new DefaultAccessControlHandler()); 074 this.currentConfiguration = null; 075 } 076 077 /** 078 * Get the single application-wide access control manager instance. 079 * 080 * @return The access control manager. 081 */ 082 public static AccessControlConfigManager getInstance() 083 { 084 if (instance == null) 085 { 086 instance = new AccessControlConfigManager(); 087 } 088 089 return instance; 090 } 091 092 /** 093 * Determine if access control is enabled according to the current 094 * configuration. 095 * 096 * @return {@code true} if access control is enabled, {@code false} 097 * otherwise. 098 */ 099 public boolean isAccessControlEnabled() 100 { 101 return currentConfiguration.isEnabled(); 102 } 103 104 /** 105 * Get the active access control handler. 106 * <p> 107 * When access control is disabled, this method returns a default access 108 * control implementation which permits all operations. 109 * 110 * @return The active access control handler (never {@code null}). 111 */ 112 public AccessControlHandler<?> getAccessControlHandler() 113 { 114 return accessControlHandler.get(); 115 } 116 117 /** 118 * Initializes the access control sub-system. This should only be called at 119 * Directory Server startup. If an error occurs then an exception will be 120 * thrown and the Directory Server will fail to start (this prevents 121 * accidental exposure of user data due to misconfiguration). 122 * 123 * @param serverContext 124 * The server context. 125 * @throws ConfigException 126 * If an access control configuration error is detected. 127 * @throws InitializationException 128 * If a problem occurs while initializing the access control handler 129 * that is not related to the Directory Server configuration. 130 */ 131 public void initializeAccessControl(ServerContext serverContext) 132 throws ConfigException, InitializationException 133 { 134 this.serverContext = serverContext; 135 136 // Don't register as an add and delete listener with the root configuration 137 // as we can have only one object at a given time. 138 139 // Initialize the current Access control. 140 AccessControlHandlerCfg accessControlConfiguration = serverContext.getRootConfig().getAccessControlHandler(); 141 142 // We have a valid usable entry, so register a change listener in 143 // order to handle configuration changes. 144 accessControlConfiguration.addChangeListener(this); 145 146 //This makes TestCaseUtils.reStartServer happy. 147 currentConfiguration=null; 148 149 // The configuration looks valid, so install it. 150 updateConfiguration(accessControlConfiguration); 151 } 152 153 /** 154 * Updates the access control configuration based on the contents of a 155 * valid configuration entry. 156 * 157 * @param newConfiguration The new configuration object. 158 * 159 * @throws ConfigException If the access control configuration is invalid. 160 * 161 * @throws InitializationException If the access control handler provider 162 * could not be instantiated. 163 */ 164 165 private void updateConfiguration(AccessControlHandlerCfg newConfiguration) 166 throws ConfigException, InitializationException 167 { 168 String newHandlerClass = null; 169 boolean enabledOld = false, enabledNew = newConfiguration.isEnabled(); 170 171 if (currentConfiguration == null) 172 { 173 // Initialization phase. 174 if (enabledNew) 175 { 176 newHandlerClass = newConfiguration.getJavaClass(); 177 } 178 else 179 { 180 newHandlerClass = DefaultAccessControlHandler.class.getName(); 181 } 182 //Get a new handler, initialize it and make it the current handler. 183 accessControlHandler.getAndSet(getHandler(newHandlerClass, 184 newConfiguration, true, false)); 185 } else { 186 enabledOld = currentConfiguration.isEnabled(); 187 if(enabledNew) { 188 //Access control is either being enabled or a attribute in the 189 //configuration has changed such as class name or a global ACI. 190 newHandlerClass = newConfiguration.getJavaClass(); 191 String oldHandlerClass = currentConfiguration.getJavaClass(); 192 //Check if moving from not enabled to enabled state. 193 if(!enabledOld) { 194 AccessControlHandler<?> oldHandler = 195 accessControlHandler.getAndSet(getHandler(newHandlerClass, 196 newConfiguration, true, 197 true)); 198 oldHandler.finalizeAccessControlHandler(); 199 } else { 200 //Check if the class name is being changed. 201 if(!newHandlerClass.equals(oldHandlerClass)) { 202 AccessControlHandler<?> oldHandler = 203 accessControlHandler.getAndSet(getHandler(newHandlerClass, newConfiguration, true, true)); 204 oldHandler.finalizeAccessControlHandler(); 205 } else { 206 //Some other attribute has changed, try to get a new non-initialized 207 //handler, but keep the old handler. 208 getHandler(newHandlerClass,newConfiguration, false, false); 209 } 210 } 211 } else if (enabledOld && !enabledNew) { 212 //Access control has been disabled, switch to the default handler and 213 //finalize the old handler. 214 newHandlerClass = DefaultAccessControlHandler.class.getName(); 215 AccessControlHandler<?> oldHandler = 216 accessControlHandler.getAndSet(getHandler(newHandlerClass, newConfiguration, false, true)); 217 oldHandler.finalizeAccessControlHandler(); 218 } 219 } 220 // Switch in the local configuration. 221 currentConfiguration = newConfiguration; 222 } 223 224 /** 225 * Instantiates a new Access Control Handler using the specified class name, 226 * configuration. 227 * 228 * @param handlerClassName The name of the handler to instantiate. 229 * @param config The configuration to use when instantiating a new handler. 230 * @param initHandler <code>True</code> if the new handler should be 231 * initialized. 232 * @param logMessage <code>True</code> if an error message should be logged 233 * and an alert should be sent. 234 * @return The newly instantiated handler. 235 * 236 * @throws InitializationException If an error occurs instantiating the 237 * the new handler. 238 */ 239 AccessControlHandler<? extends AccessControlHandlerCfg> 240 getHandler(String handlerClassName, AccessControlHandlerCfg config, 241 boolean initHandler, boolean logMessage) 242 throws InitializationException { 243 AccessControlHandler<? extends AccessControlHandlerCfg> newHandler; 244 try { 245 if(handlerClassName.equals(DefaultAccessControlHandler.class.getName())) { 246 newHandler = new DefaultAccessControlHandler(); 247 newHandler.initializeAccessControlHandler(null); 248 if(logMessage) { 249 LocalizableMessage message = WARN_CONFIG_AUTHZ_DISABLED.get(); 250 logger.warn(message); 251 if (currentConfiguration != null) { 252 DirectoryServer.sendAlertNotification(this, 253 ALERT_TYPE_ACCESS_CONTROL_DISABLED, message); 254 } 255 } 256 } else { 257 newHandler = loadHandler(handlerClassName, config, initHandler); 258 if(logMessage) { 259 LocalizableMessage message = NOTE_CONFIG_AUTHZ_ENABLED.get(handlerClassName); 260 logger.info(message); 261 if (currentConfiguration != null) { 262 DirectoryServer.sendAlertNotification(this, 263 ALERT_TYPE_ACCESS_CONTROL_ENABLED, message); 264 } 265 } 266 } 267 } catch (Exception e) { 268 logger.traceException(e); 269 LocalizableMessage message = ERR_CONFIG_AUTHZ_UNABLE_TO_INSTANTIATE_HANDLER. 270 get(handlerClassName, config.dn(), stackTraceToSingleLineString(e)); 271 throw new InitializationException(message, e); 272 } 273 return newHandler; 274 } 275 276 @Override 277 public boolean isConfigurationChangeAcceptable( 278 AccessControlHandlerCfg configuration, 279 List<LocalizableMessage> unacceptableReasons) 280 { 281 try 282 { 283 // If the access control handler is disabled, we don't care about the 284 // configuration. If it is enabled, then all we care about is whether we 285 // can load the access control handler class. 286 if (configuration.isEnabled()) 287 { 288 loadHandler(configuration.getJavaClass(), configuration, false); 289 } 290 } 291 catch (InitializationException e) 292 { 293 unacceptableReasons.add(e.getMessageObject()); 294 return false; 295 } 296 297 return true; 298 } 299 300 @Override 301 public ConfigChangeResult applyConfigurationChange( 302 AccessControlHandlerCfg configuration) 303 { 304 final ConfigChangeResult ccr = new ConfigChangeResult(); 305 306 try 307 { 308 // Attempt to install the new configuration. 309 updateConfiguration(configuration); 310 } 311 catch (ConfigException e) 312 { 313 ccr.addMessage(e.getMessageObject()); 314 ccr.setResultCode(ResultCode.CONSTRAINT_VIOLATION); 315 } 316 catch (InitializationException e) 317 { 318 ccr.addMessage(e.getMessageObject()); 319 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 320 } 321 322 return ccr; 323 } 324 325 @Override 326 public DN getComponentEntryDN() 327 { 328 return currentConfiguration.dn(); 329 } 330 331 @Override 332 public String getClassName() 333 { 334 return CLASS_NAME; 335 } 336 337 @Override 338 public LinkedHashMap<String,String> getAlerts() 339 { 340 LinkedHashMap<String,String> alerts = new LinkedHashMap<>(); 341 342 alerts.put(ALERT_TYPE_ACCESS_CONTROL_DISABLED, 343 ALERT_DESCRIPTION_ACCESS_CONTROL_DISABLED); 344 alerts.put(ALERT_TYPE_ACCESS_CONTROL_ENABLED, 345 ALERT_DESCRIPTION_ACCESS_CONTROL_ENABLED); 346 347 return alerts; 348 } 349 350 /** 351 * Loads the specified class, instantiates it as a AccessControlHandler, and 352 * optionally initializes that instance. 353 * 354 * @param className The fully-qualified name of the Access Control 355 * provider class to load, instantiate, and initialize. 356 * @param configuration The configuration to use to initialize the 357 * Access Control Handler. It must not be 358 * {@code null}. 359 * @param initialize Indicates whether the access control handler 360 * instance should be initialized. 361 * 362 * @return The possibly initialized Access Control Handler. 363 * 364 * @throws InitializationException If a problem occurred while attempting to 365 * initialize the Access Control Handler. 366 */ 367 private <T extends AccessControlHandlerCfg> AccessControlHandler<T> 368 loadHandler(String className, 369 T configuration, 370 boolean initialize) 371 throws InitializationException 372 { 373 try 374 { 375 AccessControlHandlerCfgDefn definition = 376 AccessControlHandlerCfgDefn.getInstance(); 377 ClassPropertyDefinition propertyDefinition = 378 definition.getJavaClassPropertyDefinition(); 379 Class<? extends AccessControlHandler> providerClass = 380 propertyDefinition.loadClass(className, AccessControlHandler.class); 381 AccessControlHandler<T> provider = providerClass.newInstance(); 382 383 if (configuration != null) 384 { 385 if(initialize) { 386 provider.initializeAccessControlHandler(configuration); 387 } 388 } 389 else 390 { 391 List<LocalizableMessage> unacceptableReasons = new ArrayList<>(); 392 if (!provider.isConfigurationAcceptable(configuration, unacceptableReasons)) 393 { 394 String reasons = Utils.joinAsString(". ", unacceptableReasons); 395 // Bug: we are in a section where configuration is null 396 throw new InitializationException(ERR_CONFIG_AUTHZ_CONFIG_NOT_ACCEPTABLE.get( 397 null /* WAS: configuration.dn() */, reasons)); 398 } 399 } 400 401 return provider; 402 } 403 catch (Exception e) 404 { 405 LocalizableMessage message = ERR_CONFIG_AUTHZ_UNABLE_TO_INSTANTIATE_HANDLER. 406 get(className, configuration.dn(), stackTraceToSingleLineString(e)); 407 throw new InitializationException(message, e); 408 } 409 } 410}