1   /*
2    * Copyright 2017 LINE Corporation
3    *
4    * LINE Corporation licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  
17  package com.linecorp.centraldogma.server;
18  
19  import static com.linecorp.centraldogma.server.auth.AuthConfig.DEFAULT_SESSION_CACHE_SPEC;
20  import static com.linecorp.centraldogma.server.auth.AuthConfig.DEFAULT_SESSION_TIMEOUT_MILLIS;
21  import static com.linecorp.centraldogma.server.auth.AuthConfig.DEFAULT_SESSION_VALIDATION_SCHEDULE;
22  import static com.linecorp.centraldogma.server.internal.storage.repository.RepositoryCache.validateCacheSpec;
23  import static java.util.Objects.requireNonNull;
24  
25  import java.io.File;
26  import java.net.InetSocketAddress;
27  import java.time.Duration;
28  import java.util.ArrayList;
29  import java.util.Collections;
30  import java.util.List;
31  import java.util.Set;
32  
33  import javax.annotation.Nullable;
34  
35  import org.slf4j.Logger;
36  import org.slf4j.LoggerFactory;
37  
38  import com.github.benmanes.caffeine.cache.CaffeineSpec;
39  import com.google.common.collect.ImmutableList;
40  import com.google.common.collect.ImmutableSet;
41  import com.google.common.collect.ImmutableSet.Builder;
42  
43  import com.linecorp.armeria.common.Flags;
44  import com.linecorp.armeria.common.SessionProtocol;
45  import com.linecorp.armeria.server.ServerPort;
46  import com.linecorp.centraldogma.internal.Jackson;
47  import com.linecorp.centraldogma.server.auth.AuthConfig;
48  import com.linecorp.centraldogma.server.auth.AuthProvider;
49  import com.linecorp.centraldogma.server.auth.AuthProviderFactory;
50  import com.linecorp.centraldogma.server.auth.Session;
51  import com.linecorp.centraldogma.server.plugin.Plugin;
52  import com.linecorp.centraldogma.server.plugin.PluginConfig;
53  import com.linecorp.centraldogma.server.storage.repository.Repository;
54  
55  import io.micrometer.core.instrument.MeterRegistry;
56  
57  /**
58   * Builds a {@link CentralDogma} server.
59   *
60   * <pre>{@code
61   * CentralDogmaBuilder builder = new CentralDogmaBuilder(new File("/tmp/dogma"));
62   * builder.numRepositoryWorkers(32);
63   * builder...;
64   * CentralDogma dogma = builder.build();
65   * dogma.start();
66   * }</pre>
67   */
68  public final class CentralDogmaBuilder {
69      private static final Logger logger = LoggerFactory.getLogger(CentralDogmaBuilder.class);
70  
71      // You get 36462 if you map 'dogma' to T9 phone dialer layout.
72      private static final ServerPort DEFAULT_PORT = new ServerPort(36462, SessionProtocol.HTTP);
73  
74      static final int DEFAULT_NUM_REPOSITORY_WORKERS = 16;
75      static final long DEFAULT_MAX_REMOVED_REPOSITORY_AGE_MILLIS = 604_800_000;  // 7 days
76  
77      public static final String DEFAULT_REPOSITORY_CACHE_SPEC =
78              "maximumWeight=268435456," + // Cache up to apx. 256-megachars.
79              "expireAfterAccess=5m";      // Expire on 5 minutes of inactivity.
80  
81      // Armeria properties
82      // Note that we use nullable types here for optional properties.
83      // When a property is null, the default value will be used implicitly.
84      private final List<ServerPort> ports = new ArrayList<>(2);
85      @Nullable
86      private TlsConfig tls;
87      private final List<String> trustedProxyAddresses = new ArrayList<>();
88      private final List<String> clientAddressSources = new ArrayList<>();
89      @Nullable
90      private Integer numWorkers;
91      @Nullable
92      private Integer maxNumConnections;
93      @Nullable
94      private Long requestTimeoutMillis;
95      @Nullable
96      private Long idleTimeoutMillis;
97      @Nullable
98      private Integer maxFrameLength;
99  
100     // Central Dogma properties
101     private final File dataDir;
102     private int numRepositoryWorkers = DEFAULT_NUM_REPOSITORY_WORKERS;
103     private long maxRemovedRepositoryAgeMillis = DEFAULT_MAX_REMOVED_REPOSITORY_AGE_MILLIS;
104 
105     @Nullable
106     private String repositoryCacheSpec = DEFAULT_REPOSITORY_CACHE_SPEC;
107     private boolean webAppEnabled = true;
108     @Nullable
109     private String webAppTitle;
110 
111     @Nullable
112     private GracefulShutdownTimeout gracefulShutdownTimeout;
113     private ReplicationConfig replicationConfig = ReplicationConfig.NONE;
114     @Nullable
115     private String accessLogFormat;
116 
117     // AuthConfig properties
118     @Nullable
119     private AuthProviderFactory authProviderFactory;
120     private final ImmutableSet.Builder<String> systemAdministrators = new Builder<>();
121     private boolean caseSensitiveLoginNames;
122     private String sessionCacheSpec = DEFAULT_SESSION_CACHE_SPEC;
123     private long sessionTimeoutMillis = DEFAULT_SESSION_TIMEOUT_MILLIS;
124     private String sessionValidationSchedule = DEFAULT_SESSION_VALIDATION_SCHEDULE;
125     @Nullable
126     private Object authProviderProperties;
127     private MeterRegistry meterRegistry = Flags.meterRegistry();
128 
129     @Nullable
130     private CorsConfig corsConfig;
131 
132     private final List<PluginConfig> pluginConfigs = new ArrayList<>();
133     private final List<Plugin> plugins = new ArrayList<>();
134     @Nullable
135     private ManagementConfig managementConfig;
136     @Nullable
137     private ZoneConfig zoneConfig;
138 
139     /**
140      * Creates a new builder with the specified data directory.
141      */
142     public CentralDogmaBuilder(File dataDir) {
143         this.dataDir = requireNonNull(dataDir, "dataDir");
144         if (dataDir.exists() && !dataDir.isDirectory()) {
145             throw new IllegalArgumentException("dataDir: " + dataDir + " (not a directory)");
146         }
147     }
148 
149     /**
150      * Adds a port that serves the HTTP requests. If unspecified, cleartext HTTP on port 36462 is used.
151      *
152      * @param port the TCP/IP port number
153      * @param protocol {@link SessionProtocol#HTTP} or {@link SessionProtocol#HTTPS}
154      */
155     public CentralDogmaBuilder port(int port, SessionProtocol protocol) {
156         return port(new ServerPort(port, protocol));
157     }
158 
159     /**
160      * Adds a port that serves the HTTP requests. If unspecified, cleartext HTTP on port 36462 is used.
161      *
162      * @param localAddress the TCP/IP load address to bind
163      * @param protocol {@link SessionProtocol#HTTP} or {@link SessionProtocol#HTTPS}
164      */
165     public CentralDogmaBuilder port(InetSocketAddress localAddress, SessionProtocol protocol) {
166         return port(new ServerPort(localAddress, protocol));
167     }
168 
169     /**
170      * Adds a port that serves the HTTP requests. If unspecified, cleartext HTTP on port 36462 is used.
171      */
172     public CentralDogmaBuilder port(ServerPort port) {
173         ports.add(requireNonNull(port, "port"));
174         return this;
175     }
176 
177     /**
178      * Sets a {@link TlsConfig} for supporting TLS on the server.
179      */
180     public CentralDogmaBuilder tls(TlsConfig tls) {
181         this.tls = requireNonNull(tls, "tls");
182         return this;
183     }
184 
185     /**
186      * Adds addresses or ranges of <a href="https://tools.ietf.org/html/rfc4632">
187      * Classless Inter-domain Routing (CIDR)</a> blocks of trusted proxy servers.
188      *
189      * @param exactOrCidrAddresses a list of addresses and CIDR blocks, e.g. {@code 10.0.0.1} for a single
190      *                             address or {@code 10.0.0.0/8} for a CIDR block
191      */
192     public CentralDogmaBuilder trustedProxyAddresses(String... exactOrCidrAddresses) {
193         requireNonNull(exactOrCidrAddresses, "exactOrCidrAddresses");
194         trustedProxyAddresses.addAll(ImmutableList.copyOf(exactOrCidrAddresses));
195         return this;
196     }
197 
198     /**
199      * Adds addresses or ranges of <a href="https://tools.ietf.org/html/rfc4632">
200      * Classless Inter-domain Routing (CIDR)</a> blocks of trusted proxy servers.
201      *
202      * @param exactOrCidrAddresses a list of addresses and CIDR blocks, e.g. {@code 10.0.0.1} for a single
203      *                             address or {@code 10.0.0.0/8} for a CIDR block
204      */
205     public CentralDogmaBuilder trustedProxyAddresses(Iterable<String> exactOrCidrAddresses) {
206         requireNonNull(exactOrCidrAddresses, "exactOrCidrAddresses");
207         trustedProxyAddresses.addAll(ImmutableList.copyOf(exactOrCidrAddresses));
208         return this;
209     }
210 
211     /**
212      * Adds the HTTP header names to be used for retrieving a client address.
213      *
214      * <p>Note that {@code "PROXY_PROTOCOL"} indicates the source address specified in a
215      * <a href="https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt">PROXY protocol</a> message.
216      *
217      * <p>Also note that if you configured trusted proxy addresses, {@code "forwarded"},
218      * {@code "x-forwarded-for"} and {@code "PROXY_PROTOCOL"} will be used as client address sources by default.
219      *
220      * @param clientAddressSources the HTTP header names or {@code "PROXY_PROTOCOL"} to be used for
221      *                             retrieving a client address
222      */
223     public CentralDogmaBuilder clientAddressSources(String... clientAddressSources) {
224         requireNonNull(clientAddressSources, "clientAddressSources");
225         this.clientAddressSources.addAll(ImmutableList.copyOf(clientAddressSources));
226         return this;
227     }
228 
229     /**
230      * Adds the HTTP header names to be used for retrieving a client address.
231      *
232      * <p>Note that {@code "PROXY_PROTOCOL"} indicates the source address specified in a
233      * <a href="https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt">PROXY protocol</a> message.
234      *
235      * <p>Also note that if you configured trusted proxy addresses, {@code "forwarded"},
236      * {@code "x-forwarded-for"} and {@code "PROXY_PROTOCOL"} will be used as client address sources by default.
237      *
238      * @param clientAddressSources the HTTP header names or {@code "PROXY_PROTOCOL"} to be used for
239      *                             retrieving a client address
240      */
241     public CentralDogmaBuilder clientAddressSources(Iterable<String> clientAddressSources) {
242         requireNonNull(clientAddressSources, "clientAddressSources");
243         this.clientAddressSources.addAll(ImmutableList.copyOf(clientAddressSources));
244         return this;
245     }
246 
247     /**
248      * Sets the number of I/O worker threads. <a href="https://line.github.io/armeria/">Armeria</a> default is
249      * used if unspecified.
250      */
251     public CentralDogmaBuilder numWorkers(int numWorkers) {
252         this.numWorkers = numWorkers;
253         return this;
254     }
255 
256     /**
257      * Sets the maximum allowed number of TCP/IP connections. If unspecified, no limit is enforced.
258      */
259     public CentralDogmaBuilder maxNumConnections(int maxNumConnections) {
260         this.maxNumConnections = maxNumConnections;
261         return this;
262     }
263 
264     /**
265      * Sets the timeout for handling an incoming request. If it takes more than the specified timeout to
266      * handle a request, the server may respond with '503 Service Unavailable' or fail to respond.
267      * <a href="https://line.github.io/armeria/">Armeria</a> default is used if unspecified.
268      */
269     public CentralDogmaBuilder requestTimeout(Duration requestTimeout) {
270         return requestTimeoutMillis(requireNonNull(requestTimeout, "requestTimeout").toMillis());
271     }
272 
273     /**
274      * Sets the timeout for handling an incoming request, in milliseconds. If it takes more than
275      * the specified timeout to handle a request, the server may respond with '503 Service Unavailable' or
276      * fail to respond. <a href="https://line.github.io/armeria/">Armeria</a> default is used if unspecified.
277      */
278     public CentralDogmaBuilder requestTimeoutMillis(long requestTimeoutMillis) {
279         this.requestTimeoutMillis = requestTimeoutMillis;
280         return this;
281     }
282 
283     /**
284      * Sets the timeout for keeping an idle connection. A connection is automatically closed when it stays idle
285      * without any requests in progress for more than the specified timeout.
286      * <a href="https://line.github.io/armeria/">Armeria</a> default is used if unspecified.
287      */
288     public CentralDogmaBuilder idleTimeout(Duration idleTimeout) {
289         return idleTimeoutMillis(requireNonNull(idleTimeout, "idleTimeout").toMillis());
290     }
291 
292     /**
293      * Sets the timeout for keeping an idle connection, in milliseconds. A connection is automatically closed
294      * when it stays idle without any requests in progress for more than the specified timeout.
295      */
296     public CentralDogmaBuilder idleTimeoutMillis(long idleTimeoutMillis) {
297         this.idleTimeoutMillis = idleTimeoutMillis;
298         return this;
299     }
300 
301     /**
302      * Sets the maximum allowed content length of an incoming request.
303      */
304     public CentralDogmaBuilder maxFrameLength(int maxFrameLength) {
305         this.maxFrameLength = maxFrameLength;
306         return this;
307     }
308 
309     /**
310      * Sets the number of worker threads dedicated to repository access.
311      * If unspecified, {@value #DEFAULT_NUM_REPOSITORY_WORKERS} threads are created at maximum.
312      */
313     public CentralDogmaBuilder numRepositoryWorkers(int numRepositoryWorkers) {
314         this.numRepositoryWorkers = numRepositoryWorkers;
315         return this;
316     }
317 
318     /**
319      * Sets the maximum allowed age of removed projects and repositories before they are purged.
320      * Set {@code 0} to disable automatic purge.
321      * If unspecified, the default of {@value #DEFAULT_MAX_REMOVED_REPOSITORY_AGE_MILLIS} milliseconds is used.
322      */
323     public CentralDogmaBuilder maxRemovedRepositoryAge(Duration maxRemovedRepositoryAge) {
324         maxRemovedRepositoryAgeMillis(
325                 requireNonNull(maxRemovedRepositoryAge, "maxRemovedRepositoryAge").toMillis());
326         return this;
327     }
328 
329     /**
330      * Sets the maximum allowed age, in milliseconds of removed projects and repositories
331      * before they are purged.
332      * Set {@code 0} to disable automatic purge.
333      * If unspecified, the default of {@value #DEFAULT_MAX_REMOVED_REPOSITORY_AGE_MILLIS} milliseconds is used.
334      */
335     public CentralDogmaBuilder maxRemovedRepositoryAgeMillis(long maxRemovedRepositoryAgeMillis) {
336         this.maxRemovedRepositoryAgeMillis = maxRemovedRepositoryAgeMillis;
337         return this;
338     }
339 
340     /**
341      * Sets the cache specification which determines the capacity and behavior of the cache for the return
342      * values of methods in {@link Repository} of the server. See {@link CaffeineSpec} for the syntax
343      * of the spec. If unspecified, the default cache spec of {@value #DEFAULT_REPOSITORY_CACHE_SPEC} is used.
344      *
345      * @deprecated Use {@link #repositoryCacheSpec(String)}.
346      */
347     @Deprecated
348     public CentralDogmaBuilder cacheSpec(String cacheSpec) {
349         repositoryCacheSpec = validateCacheSpec(cacheSpec);
350         return this;
351     }
352 
353     /**
354      * Sets the cache specification which determines the capacity and behavior of the cache for the return
355      * values of methods in {@link Repository} of the server. See {@link CaffeineSpec} for the syntax
356      * of the spec. If unspecified, the default cache spec of {@value #DEFAULT_REPOSITORY_CACHE_SPEC} is used.
357      */
358     public CentralDogmaBuilder repositoryCacheSpec(String repositoryCacheSpec) {
359         this.repositoryCacheSpec = validateCacheSpec(repositoryCacheSpec);
360         return this;
361     }
362 
363     /**
364      * Sets whether administrative web application is enabled or not.
365      * If unspecified, the administrative web application is enabled.
366      */
367     public CentralDogmaBuilder webAppEnabled(boolean webAppEnabled) {
368         this.webAppEnabled = webAppEnabled;
369         return this;
370     }
371 
372     /**
373      * Sets the title text which is displayed on the navigation bar of the administrative web application.
374      */
375     public CentralDogmaBuilder webAppTitle(String webAppTitle) {
376         this.webAppTitle = requireNonNull(webAppTitle, "webAppTitle");
377         return this;
378     }
379 
380     /**
381      * Sets the graceful shutdown timeout. If unspecified, graceful shutdown is disabled.
382      */
383     public CentralDogmaBuilder gracefulShutdownTimeout(GracefulShutdownTimeout gracefulShutdownTimeout) {
384         this.gracefulShutdownTimeout = gracefulShutdownTimeout;
385         return this;
386     }
387 
388     /**
389      * Configures the replication.
390      * If unspecified or {@link ReplicationConfig#NONE} is specified, replication is disabled.
391      */
392     public CentralDogmaBuilder replication(ReplicationConfig replicationConfig) {
393         this.replicationConfig = requireNonNull(replicationConfig, "replicationConfig");
394         return this;
395     }
396 
397     /**
398      * Configures a format of an access log. It will work only if any logging framework is configured.
399      * Read the <a href="https://line.github.io/armeria/docs/server-access-log">Writing an access log</a>
400      * document for more information.
401      */
402     public CentralDogmaBuilder accessLogFormat(String accessLogFormat) {
403         this.accessLogFormat = requireNonNull(accessLogFormat, "accessLogFormat");
404         return this;
405     }
406 
407     /**
408      * Sets an {@link AuthProviderFactory} instance which is used to create a new {@link AuthProvider}.
409      */
410     public CentralDogmaBuilder authProviderFactory(AuthProviderFactory authProviderFactory) {
411         this.authProviderFactory = requireNonNull(authProviderFactory, "authProviderFactory");
412         return this;
413     }
414 
415     /**
416      * Adds system administrators to the set.
417      */
418     public CentralDogmaBuilder systemAdministrators(String... systemAdministrators) {
419         requireNonNull(systemAdministrators, "systemAdministrators");
420         for (final String systemAdministrator : systemAdministrators) {
421             this.systemAdministrators.add(systemAdministrator);
422         }
423         return this;
424     }
425 
426     /**
427      * Adds system administrators to the set.
428      */
429     public CentralDogmaBuilder systemAdministrators(Iterable<String> systemAdministrators) {
430         requireNonNull(systemAdministrators, "systemAdministrators");
431         this.systemAdministrators.addAll(systemAdministrators);
432         return this;
433     }
434 
435     /**
436      * Sets whether case-sensitive matching is performed when login names are compared.
437      */
438     public CentralDogmaBuilder caseSensitiveLoginNames(boolean caseSensitiveLoginNames) {
439         this.caseSensitiveLoginNames = caseSensitiveLoginNames;
440         return this;
441     }
442 
443     /**
444      * Sets the cache specification which determines the capacity and behavior of the cache for
445      * {@link Session} of the server. See {@link CaffeineSpec} for the syntax of the spec.
446      * If unspecified, the default cache spec of {@value AuthConfig#DEFAULT_SESSION_CACHE_SPEC}
447      * is used.
448      */
449     public CentralDogmaBuilder sessionCacheSpec(String sessionCacheSpec) {
450         this.sessionCacheSpec = validateCacheSpec(sessionCacheSpec);
451         return this;
452     }
453 
454     /**
455      * Sets the session timeout for administrative web application, in milliseconds.
456      * If unspecified, {@value AuthConfig#DEFAULT_SESSION_TIMEOUT_MILLIS} is used.
457      */
458     public CentralDogmaBuilder sessionTimeoutMillis(long sessionTimeoutMillis) {
459         this.sessionTimeoutMillis = sessionTimeoutMillis;
460         return this;
461     }
462 
463     /**
464      * Sets the session timeout for administrative web application.
465      * If unspecified, {@value AuthConfig#DEFAULT_SESSION_TIMEOUT_MILLIS} is used.
466      */
467     public CentralDogmaBuilder sessionTimeout(Duration sessionTimeout) {
468         return sessionTimeoutMillis(
469                 requireNonNull(sessionTimeout, "sessionTimeout").toMillis());
470     }
471 
472     /**
473      * Sets a schedule for validating sessions.
474      * If unspecified, {@value AuthConfig#DEFAULT_SESSION_VALIDATION_SCHEDULE} is used.
475      */
476     public CentralDogmaBuilder sessionValidationSchedule(String sessionValidationSchedule) {
477         this.sessionValidationSchedule =
478                 requireNonNull(sessionValidationSchedule, "sessionValidationSchedule");
479         return this;
480     }
481 
482     /**
483      * Sets an additional properties for an {@link AuthProviderFactory}.
484      */
485     public CentralDogmaBuilder authProviderProperties(Object authProviderProperties) {
486         this.authProviderProperties = requireNonNull(authProviderProperties, "authProviderProperties");
487         return this;
488     }
489 
490     /**
491      * Sets the {@link MeterRegistry} used to collect metrics.
492      */
493     public CentralDogmaBuilder meterRegistry(MeterRegistry meterRegistry) {
494         this.meterRegistry = requireNonNull(meterRegistry, "meterRegistry");
495         return this;
496     }
497 
498     /**
499      * Enables CORS with the specified allowed origins.
500      */
501     public CentralDogmaBuilder cors(String... allowedOrigins) {
502         requireNonNull(allowedOrigins, "allowedOrigins");
503         corsConfig = new CorsConfig(ImmutableList.copyOf(allowedOrigins), null);
504         return this;
505     }
506 
507     /**
508      * Enables CORS with the specified {@link CorsConfig}.
509      */
510     public CentralDogmaBuilder cors(CorsConfig corsConfig) {
511         this.corsConfig = requireNonNull(corsConfig, "corsConfig");
512         return this;
513     }
514 
515     /**
516      * Adds the {@link PluginConfig}s.
517      */
518     public CentralDogmaBuilder pluginConfigs(PluginConfig... pluginConfigs) {
519         requireNonNull(pluginConfigs, "pluginConfigs");
520         this.pluginConfigs.addAll(ImmutableList.copyOf(pluginConfigs));
521         return this;
522     }
523 
524     /**
525      * Returns the {@link PluginConfig}s that have been added.
526      */
527     public List<PluginConfig> pluginConfigs() {
528         return pluginConfigs;
529     }
530 
531     /**
532      * Adds the {@link Plugin}s.
533      */
534     public CentralDogmaBuilder plugins(Plugin... plugins) {
535         requireNonNull(plugins, "plugins");
536         return plugins(ImmutableList.copyOf(plugins));
537     }
538 
539     /**
540      * Adds the {@link Plugin}s.
541      */
542     public CentralDogmaBuilder plugins(Iterable<? extends Plugin> plugins) {
543         requireNonNull(plugins, "plugins");
544         this.plugins.addAll(ImmutableList.copyOf(plugins));
545         return this;
546     }
547 
548     /**
549      * Enables a management service with the specified {@link ManagementConfig}.
550      */
551     public CentralDogmaBuilder management(ManagementConfig managementConfig) {
552         requireNonNull(managementConfig, "managementConfig");
553         this.managementConfig = managementConfig;
554         return this;
555     }
556 
557     /**
558      * Specifies the {@link ZoneConfig} of the server.
559      */
560     public CentralDogmaBuilder zone(ZoneConfig zoneConfig) {
561         requireNonNull(zoneConfig, "zoneConfig");
562         this.zoneConfig = zoneConfig;
563         return this;
564     }
565 
566     /**
567      * Returns a newly-created {@link CentralDogma} server.
568      */
569     public CentralDogma build() {
570         return new CentralDogma(buildConfig(), meterRegistry, ImmutableList.copyOf(plugins));
571     }
572 
573     private CentralDogmaConfig buildConfig() {
574         final List<ServerPort> ports = !this.ports.isEmpty() ? this.ports
575                                                              : Collections.singletonList(DEFAULT_PORT);
576         final Set<String> systemAdminSet = systemAdministrators.build();
577         final AuthConfig authCfg;
578         if (authProviderFactory != null) {
579             authCfg = new AuthConfig(
580                     authProviderFactory, systemAdminSet, caseSensitiveLoginNames,
581                     sessionCacheSpec, sessionTimeoutMillis, sessionValidationSchedule,
582                     authProviderProperties != null ? Jackson.valueToTree(authProviderProperties) : null);
583         } else {
584             authCfg = null;
585             logger.info("{} is not specified, so {} will not be configured.",
586                         AuthProviderFactory.class.getSimpleName(),
587                         AuthConfig.class.getSimpleName());
588         }
589 
590         return new CentralDogmaConfig(dataDir, ports, tls, trustedProxyAddresses, clientAddressSources,
591                                       numWorkers, maxNumConnections,
592                                       requestTimeoutMillis, idleTimeoutMillis, maxFrameLength,
593                                       numRepositoryWorkers, repositoryCacheSpec,
594                                       maxRemovedRepositoryAgeMillis, gracefulShutdownTimeout,
595                                       webAppEnabled, webAppTitle, replicationConfig,
596                                       null, accessLogFormat, authCfg,
597                                       corsConfig, pluginConfigs, managementConfig, zoneConfig);
598     }
599 }