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