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-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2011-2015 ForgeRock AS.
026 */
027package org.opends.server.core;
028
029import java.util.*;
030
031import org.forgerock.i18n.LocalizableMessage;
032import org.forgerock.opendj.config.server.ConfigChangeResult;
033import org.forgerock.opendj.config.server.ConfigException;
034import org.forgerock.opendj.ldap.ResultCode;
035import org.forgerock.opendj.ldap.schema.Schema;
036import org.forgerock.opendj.ldap.schema.SchemaBuilder;
037import org.forgerock.opendj.ldap.schema.SchemaOptions;
038import org.opends.server.admin.server.ConfigurationChangeListener;
039import org.opends.server.admin.server.ServerManagementContext;
040import org.opends.server.admin.std.meta.GlobalCfgDefn;
041import org.opends.server.admin.std.meta.GlobalCfgDefn.DisabledPrivilege;
042import org.opends.server.admin.std.meta.GlobalCfgDefn.InvalidAttributeSyntaxBehavior;
043import org.opends.server.admin.std.meta.GlobalCfgDefn.SingleStructuralObjectclassBehavior;
044import org.opends.server.admin.std.server.GlobalCfg;
045import org.opends.server.admin.std.server.RootCfg;
046import org.opends.server.api.AuthenticationPolicy;
047import org.opends.server.schema.SchemaUpdater;
048import org.opends.server.types.*;
049
050import static org.opends.messages.ConfigMessages.*;
051import static org.opends.server.core.DirectoryServer.*;
052import static org.opends.server.util.ServerConstants.*;
053
054/**
055 * This class defines a utility that will be used to manage the set of core
056 * configuration attributes defined in the Directory Server.  These
057 * configuration attributes appear in the "cn=config" configuration entry.
058 */
059public class CoreConfigManager
060       implements ConfigurationChangeListener<GlobalCfg>
061{
062  private final ServerContext serverContext;
063
064  /**
065   * Creates a new instance of this core config manager.
066   *
067   * @param serverContext
068   *            The server context.
069   */
070  public CoreConfigManager(ServerContext serverContext)
071  {
072    this.serverContext = serverContext;
073  }
074
075  /**
076   * Initializes the Directory Server's core configuration. This should only be
077   * called at server startup.
078   *
079   * @throws ConfigException
080   *           If a configuration problem causes the identity mapper
081   *           initialization process to fail.
082   * @throws InitializationException
083   *           If a problem occurs while initializing the identity mappers that
084   *           is not related to the server configuration.
085   */
086  public void initializeCoreConfig()
087         throws ConfigException, InitializationException
088  {
089    // Get the root configuration object.
090    ServerManagementContext managementContext =
091         ServerManagementContext.getInstance();
092    RootCfg rootConfiguration =
093         managementContext.getRootConfiguration();
094
095
096    // Get the global configuration and register with it as a change listener.
097    GlobalCfg globalConfig = rootConfiguration.getGlobalConfiguration();
098    globalConfig.addChangeListener(this);
099
100
101    // If there are any STMP servers specified, then make sure that if the value
102    // contains a colon that the portion after it is an integer between 1 and
103    // 65535.
104    Set<String> smtpServers = globalConfig.getSMTPServer();
105    if (smtpServers != null)
106    {
107      for (String server : smtpServers)
108      {
109        try
110        {
111          // validate provided string
112          HostPort.valueOf(server);
113        }
114        catch (RuntimeException e)
115        {
116          LocalizableMessage message = ERR_CONFIG_CORE_INVALID_SMTP_SERVER.get(server);
117          throw new ConfigException(message, e);
118        }
119      }
120    }
121
122
123    // Apply the configuration to the server.
124    applyGlobalConfiguration(globalConfig, serverContext);
125  }
126
127
128
129  /**
130   * Applies the settings in the provided configuration to the Directory Server.
131   *
132   * @param  globalConfig  The configuration settings to be applied.
133   */
134  private static void applyGlobalConfiguration(GlobalCfg globalConfig, ServerContext serverContext)
135  {
136    setCheckSchema(globalConfig.isCheckSchema());
137    setDefaultPasswordPolicyDN(globalConfig.getDefaultPasswordPolicyDN());
138    setAddMissingRDNAttributes(globalConfig.isAddMissingRDNAttributes());
139    setAllowAttributeNameExceptions(globalConfig.isAllowAttributeNameExceptions());
140    setSyntaxEnforcementPolicy(convert(globalConfig.getInvalidAttributeSyntaxBehavior()));
141    setServerErrorResultCode(ResultCode.valueOf(globalConfig.getServerErrorResultCode()));
142    setSingleStructuralObjectClassPolicy(convert(globalConfig.getSingleStructuralObjectclassBehavior()));
143
144    setNotifyAbandonedOperations(globalConfig.isNotifyAbandonedOperations());
145    setSizeLimit(globalConfig.getSizeLimit());
146    setTimeLimit((int) globalConfig.getTimeLimit());
147    setProxiedAuthorizationIdentityMapperDN(globalConfig.getProxiedAuthorizationIdentityMapperDN());
148    setWritabilityMode(convert(globalConfig.getWritabilityMode()));
149    setRejectUnauthenticatedRequests(globalConfig.isRejectUnauthenticatedRequests());
150    setBindWithDNRequiresPassword(globalConfig.isBindWithDNRequiresPassword());
151    setLookthroughLimit(globalConfig.getLookthroughLimit());
152
153    setMailServerPropertySets(getMailServerProperties(globalConfig.getSMTPServer()));
154    setAllowedTasks(globalConfig.getAllowedTask());
155    setDisabledPrivileges(convert(globalConfig.getDisabledPrivilege()));
156    setReturnBindErrorMessages(globalConfig.isReturnBindErrorMessages());
157    setIdleTimeLimit(globalConfig.getIdleTimeLimit());
158    setSaveConfigOnSuccessfulStartup(globalConfig.isSaveConfigOnSuccessfulStartup());
159
160    setUseNanoTime(globalConfig.getEtimeResolution() == GlobalCfgDefn.EtimeResolution.NANOSECONDS);
161    setMaxAllowedConnections(globalConfig.getMaxAllowedClientConnections());
162    setMaxPersistentSearchLimit(globalConfig.getMaxPsearches());
163    setMaxInternalBufferSize((int) globalConfig.getMaxInternalBufferSize());
164
165    // Update the "new" schema with configuration changes
166    SchemaUpdater schemaUpdater = serverContext.getSchemaUpdater();
167    SchemaBuilder schemaBuilder = schemaUpdater.getSchemaBuilder();
168    boolean allowMalformedNames = globalConfig.isAllowAttributeNameExceptions();
169    schemaBuilder.setOption(SchemaOptions.ALLOW_MALFORMED_NAMES_AND_OPTIONS, allowMalformedNames);
170    Schema schema = schemaBuilder.toSchema();
171    if (!globalConfig.isCheckSchema())
172    {
173      schema = schema.asNonStrictSchema();
174    }
175    schemaUpdater.updateSchema(schema);
176  }
177
178  private static AcceptRejectWarn convert(InvalidAttributeSyntaxBehavior invalidAttributeSyntaxBehavior)
179  {
180    switch (invalidAttributeSyntaxBehavior)
181    {
182    case ACCEPT:
183      return AcceptRejectWarn.ACCEPT;
184    case WARN:
185      return AcceptRejectWarn.WARN;
186    case REJECT:
187    default:
188      return AcceptRejectWarn.REJECT;
189    }
190  }
191
192  private static AcceptRejectWarn convert(SingleStructuralObjectclassBehavior singleStructuralObjectclassBehavior)
193  {
194    switch (singleStructuralObjectclassBehavior)
195    {
196    case ACCEPT:
197      return AcceptRejectWarn.ACCEPT;
198    case WARN:
199      return AcceptRejectWarn.WARN;
200    case REJECT:
201    default:
202      return AcceptRejectWarn.REJECT;
203    }
204  }
205
206  private static WritabilityMode convert(GlobalCfgDefn.WritabilityMode writabilityMode)
207  {
208    switch (writabilityMode)
209    {
210    case ENABLED:
211      return WritabilityMode.ENABLED;
212    case INTERNAL_ONLY:
213      return WritabilityMode.INTERNAL_ONLY;
214    case DISABLED:
215    default:
216      return WritabilityMode.DISABLED;
217    }
218  }
219
220  private static List<Properties> getMailServerProperties(Set<String> smtpServers)
221  {
222    List<Properties> mailServerProperties = new ArrayList<>();
223    if (smtpServers != null && !smtpServers.isEmpty())
224    {
225      for (String smtpServer : smtpServers)
226      {
227        final Properties properties = new Properties();
228        try
229        {
230          final HostPort hp = HostPort.valueOf(smtpServer);
231
232          properties.setProperty(SMTP_PROPERTY_HOST, hp.getHost());
233          properties.setProperty(SMTP_PROPERTY_PORT,
234              String.valueOf(hp.getPort()));
235          properties.setProperty(SMTP_PROPERTY_CONNECTION_TIMEOUT,
236              SMTP_DEFAULT_TIMEOUT_VALUE);
237          properties.setProperty(SMTP_PROPERTY_IO_TIMEOUT,
238              SMTP_DEFAULT_TIMEOUT_VALUE);
239        }
240        catch (RuntimeException e)
241        {
242          // no valid port provided
243          properties.setProperty(SMTP_PROPERTY_HOST, smtpServer);
244        }
245        mailServerProperties.add(properties);
246      }
247    }
248    return mailServerProperties;
249  }
250
251  private static HashSet<Privilege> convert(Set<DisabledPrivilege> configuredDisabledPrivs)
252  {
253    HashSet<Privilege> disabledPrivileges = new HashSet<>();
254    if (configuredDisabledPrivs != null)
255    {
256      for (DisabledPrivilege p : configuredDisabledPrivs)
257      {
258        final Privilege privilege = convert(p);
259        if (privilege != null)
260        {
261          disabledPrivileges.add(privilege);
262        }
263      }
264    }
265    return disabledPrivileges;
266  }
267
268  private static Privilege convert(DisabledPrivilege privilege)
269  {
270    switch (privilege)
271    {
272      case BACKEND_BACKUP:
273        return Privilege.BACKEND_BACKUP;
274      case BACKEND_RESTORE:
275        return Privilege.BACKEND_RESTORE;
276      case BYPASS_ACL:
277        return Privilege.BYPASS_ACL;
278      case CANCEL_REQUEST:
279        return Privilege.CANCEL_REQUEST;
280      case CONFIG_READ:
281        return Privilege.CONFIG_READ;
282      case CONFIG_WRITE:
283        return Privilege.CONFIG_WRITE;
284      case DATA_SYNC:
285        return Privilege.DATA_SYNC;
286      case DISCONNECT_CLIENT:
287        return Privilege.DISCONNECT_CLIENT;
288      case JMX_NOTIFY:
289        return Privilege.JMX_NOTIFY;
290      case JMX_READ:
291        return Privilege.JMX_READ;
292      case JMX_WRITE:
293        return Privilege.JMX_WRITE;
294      case LDIF_EXPORT:
295        return Privilege.LDIF_EXPORT;
296      case LDIF_IMPORT:
297        return Privilege.LDIF_IMPORT;
298      case MODIFY_ACL:
299        return Privilege.MODIFY_ACL;
300      case PASSWORD_RESET:
301        return Privilege.PASSWORD_RESET;
302      case PRIVILEGE_CHANGE:
303        return Privilege.PRIVILEGE_CHANGE;
304      case PROXIED_AUTH:
305        return Privilege.PROXIED_AUTH;
306      case SERVER_RESTART:
307        return Privilege.SERVER_RESTART;
308      case SERVER_SHUTDOWN:
309        return Privilege.SERVER_SHUTDOWN;
310      case UNINDEXED_SEARCH:
311        return Privilege.UNINDEXED_SEARCH;
312      case UPDATE_SCHEMA:
313        return Privilege.UPDATE_SCHEMA;
314      case SUBENTRY_WRITE:
315        return Privilege.SUBENTRY_WRITE;
316      default:
317        return null;
318    }
319  }
320
321
322  /** {@inheritDoc} */
323  @Override
324  public boolean isConfigurationChangeAcceptable(GlobalCfg configuration,
325                      List<LocalizableMessage> unacceptableReasons)
326  {
327    boolean configAcceptable = true;
328
329    Set<String> smtpServers = configuration.getSMTPServer();
330    if (smtpServers != null)
331    {
332      for (String server : smtpServers)
333      {
334        try
335        {
336          // validate provided string
337          HostPort.valueOf(server);
338        }
339        catch (RuntimeException e)
340        {
341          LocalizableMessage message = ERR_CONFIG_CORE_INVALID_SMTP_SERVER.get(server);
342          unacceptableReasons.add(message);
343          configAcceptable = false;
344        }
345      }
346    }
347
348    // Ensure that the default password policy always points to a password
349    // policy and not another type of authentication policy.
350    DN defaultPasswordPolicyDN = configuration.getDefaultPasswordPolicyDN();
351    AuthenticationPolicy policy = DirectoryServer
352        .getAuthenticationPolicy(defaultPasswordPolicyDN);
353    if (!policy.isPasswordPolicy())
354    {
355      LocalizableMessage message =
356        ERR_CONFIG_PWPOLICY_CANNOT_CHANGE_DEFAULT_POLICY_WRONG_TYPE
357          .get(configuration.getDefaultPasswordPolicy());
358      unacceptableReasons.add(message);
359      configAcceptable = false;
360    }
361
362    return configAcceptable;
363  }
364
365
366
367  /** {@inheritDoc} */
368  @Override
369  public ConfigChangeResult applyConfigurationChange(GlobalCfg configuration)
370  {
371    final ConfigChangeResult ccr = new ConfigChangeResult();
372
373    applyGlobalConfiguration(configuration, serverContext);
374
375    return ccr;
376  }
377}
378