1   /*
2    * Copyright 2023 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  package com.linecorp.centraldogma.server;
17  
18  import java.util.List;
19  
20  import javax.annotation.Nullable;
21  
22  import com.fasterxml.jackson.annotation.JsonCreator;
23  import com.fasterxml.jackson.annotation.JsonProperty;
24  import com.google.common.base.MoreObjects;
25  import com.google.common.collect.ImmutableList;
26  import com.google.common.collect.Streams;
27  
28  /**
29   * CORS configuration.
30   */
31  public final class CorsConfig {
32  
33      private static final int DEFAULT_MAX_AGE = 7200;
34  
35      private final List<String> allowedOrigins;
36      private final int maxAgeSeconds;
37  
38      /**
39       * Creates an instance with the specified {@code allowedOrigins} and
40       * {@code maxAgeSeconds}.
41       */
42      @JsonCreator
43      public CorsConfig(@JsonProperty("allowedOrigins") Object allowedOrigins,
44                        @JsonProperty("maxAgeSeconds") @Nullable Integer maxAgeSeconds) {
45          if (allowedOrigins instanceof Iterable &&
46              Streams.stream((Iterable<?>) allowedOrigins).allMatch(String.class::isInstance)) {
47              this.allowedOrigins = ImmutableList.copyOf((Iterable<String>) allowedOrigins);
48          } else if (allowedOrigins instanceof String) {
49              final String origin = (String) allowedOrigins;
50              this.allowedOrigins = ImmutableList.of(origin);
51          } else {
52              throw new IllegalArgumentException(
53                      "allowedOrigins: " + allowedOrigins +
54                      " (expected: either a string or an array of strings)");
55          }
56          if (this.allowedOrigins.isEmpty()) {
57              throw new IllegalArgumentException(
58                      "allowedOrigins: " + allowedOrigins +
59                      " (expected: the list of origins must not be empty)");
60          }
61  
62          if (maxAgeSeconds == null) {
63              maxAgeSeconds = DEFAULT_MAX_AGE;
64          }
65          if (maxAgeSeconds <= 0) {
66              throw new IllegalArgumentException(
67                      "maxAgeSeconds: " + maxAgeSeconds +
68                      " (expected: maxAgeSeconds must be positive)");
69          }
70          this.maxAgeSeconds = maxAgeSeconds;
71      }
72  
73      /**
74       * Returns the list of origins which are allowed a CORS policy.
75       */
76      @JsonProperty
77      public List<String> allowedOrigins() {
78          return allowedOrigins;
79      }
80  
81      /**
82       * Returns how long in seconds the results of a preflight request can be cached.
83       * If unspecified, the default of {@code 7200} seconds is returned.
84       */
85      @JsonProperty
86      public int maxAgeSeconds() {
87          return maxAgeSeconds;
88      }
89  
90      @Override
91      public String toString() {
92          return MoreObjects.toStringHelper(this)
93                            .add("allowedOrigins", allowedOrigins)
94                            .add("maxAgeSeconds", maxAgeSeconds)
95                            .toString();
96      }
97  }