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