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