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