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