1   /*
2    * Copyright 2018 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.auth;
17  
18  import static com.google.common.base.MoreObjects.firstNonNull;
19  import static com.google.common.base.Preconditions.checkArgument;
20  import static com.linecorp.centraldogma.server.internal.storage.repository.RepositoryCache.validateCacheSpec;
21  import static java.util.Objects.requireNonNull;
22  
23  import java.text.ParseException;
24  import java.util.Set;
25  import java.util.function.Function;
26  
27  import javax.annotation.Nullable;
28  
29  import org.quartz.CronExpression;
30  
31  import com.fasterxml.jackson.annotation.JsonCreator;
32  import com.fasterxml.jackson.annotation.JsonProperty;
33  import com.fasterxml.jackson.core.JsonProcessingException;
34  import com.fasterxml.jackson.databind.JsonNode;
35  import com.google.common.base.Ascii;
36  import com.google.common.collect.ImmutableSet;
37  
38  import com.linecorp.centraldogma.internal.Jackson;
39  
40  /**
41   * An authentication configuration for the Central Dogma server.
42   */
43  public final class AuthConfig {
44      /**
45       * A default session timeout in milliseconds.
46       */
47      public static final long DEFAULT_SESSION_TIMEOUT_MILLIS = 604800000;   // 7 days
48  
49      /**
50       * A default specification for a session cache.
51       */
52      public static final String DEFAULT_SESSION_CACHE_SPEC =
53              // Expire after the duration of session timeout.
54              "maximumSize=8192,expireAfterWrite=" + (DEFAULT_SESSION_TIMEOUT_MILLIS / 1000) + 's';
55  
56      /**
57       * A default schedule for validating sessions at 0:30, 4:30, 8:30, 12:30, 16:30 and 20:30 for every day.
58       */
59      public static final String DEFAULT_SESSION_VALIDATION_SCHEDULE = "0 30 */4 ? * *";
60  
61      private final AuthProviderFactory factory;
62  
63      private final Set<String> administrators;
64      private final boolean caseSensitiveLoginNames;
65  
66      private final String sessionCacheSpec;
67      private final long sessionTimeoutMillis;
68      private final String sessionValidationSchedule;
69  
70      @Nullable
71      private final JsonNode properties;
72  
73      /**
74       * Creates a new instance.
75       *
76       * @param factoryClassName the fully-qualified class name of the {@link AuthProviderFactory}
77       * @param administrators the login names of the administrators
78       * @param caseSensitiveLoginNames the flag whether case-sensitive matching is performed when login names
79       *                                are compared
80       * @param sessionCacheSpec the cache specification which determines the capacity and behavior of
81       *                         the cache for {@link Session} of the server
82       * @param sessionTimeoutMillis the timeout for {@link Session}s of the server
83       * @param sessionValidationSchedule the schedule for validating sessions
84       * @param properties the additional properties which are used in the factory
85       */
86      @JsonCreator
87      public AuthConfig(
88              @JsonProperty("factoryClassName") String factoryClassName,
89              @JsonProperty("administrators") @Nullable Set<String> administrators,
90              @JsonProperty("caseSensitiveLoginNames") @Nullable Boolean caseSensitiveLoginNames,
91              @JsonProperty("sessionCacheSpec") @Nullable String sessionCacheSpec,
92              @JsonProperty("sessionTimeoutMillis") @Nullable Long sessionTimeoutMillis,
93              @JsonProperty("sessionValidationSchedule") @Nullable String sessionValidationSchedule,
94              @JsonProperty("properties") @Nullable JsonNode properties) throws Exception {
95          this((AuthProviderFactory) AuthConfig.class
96                       .getClassLoader()
97                       .loadClass(requireNonNull(factoryClassName, "factoryClassName"))
98                       .getDeclaredConstructor().newInstance(),
99               administrators != null ? ImmutableSet.copyOf(administrators) : ImmutableSet.of(),
100              firstNonNull(caseSensitiveLoginNames, false),
101              firstNonNull(sessionCacheSpec, DEFAULT_SESSION_CACHE_SPEC),
102              firstNonNull(sessionTimeoutMillis, DEFAULT_SESSION_TIMEOUT_MILLIS),
103              firstNonNull(sessionValidationSchedule, DEFAULT_SESSION_VALIDATION_SCHEDULE),
104              properties);
105     }
106 
107     /**
108      * Creates a new instance.
109      *
110      * @param factory the {@link AuthProviderFactory} instance
111      * @param administrators the login names of the administrators
112      * @param caseSensitiveLoginNames the flag whether case-sensitive matching is performed when login names
113      *                                are compared
114      * @param sessionCacheSpec the cache specification which determines the capacity and behavior of
115      *                         the cache for {@link Session} of the server
116      * @param sessionTimeoutMillis the timeout for {@link Session}s of the server
117      * @param sessionValidationSchedule the schedule for validating sessions
118      * @param properties the additional properties which are used in the factory
119      */
120     public AuthConfig(AuthProviderFactory factory,
121                       Set<String> administrators,
122                       boolean caseSensitiveLoginNames,
123                       String sessionCacheSpec,
124                       long sessionTimeoutMillis,
125                       String sessionValidationSchedule,
126                       @Nullable JsonNode properties) {
127         this.factory = requireNonNull(factory, "factory");
128         this.administrators = requireNonNull(administrators, "administrators");
129         this.caseSensitiveLoginNames = caseSensitiveLoginNames;
130         this.sessionCacheSpec = validateCacheSpec(requireNonNull(sessionCacheSpec, "sessionCacheSpec"));
131         checkArgument(sessionTimeoutMillis > 0,
132                       "sessionTimeoutMillis: %s (expected: > 0)", sessionTimeoutMillis);
133         this.sessionTimeoutMillis = sessionTimeoutMillis;
134         this.sessionValidationSchedule = validateSchedule(
135                 requireNonNull(sessionValidationSchedule, "sessionValidationSchedule"));
136         this.properties = properties;
137     }
138 
139     /**
140      * Returns the {@link AuthProviderFactory}.
141      */
142     public AuthProviderFactory factory() {
143         return factory;
144     }
145 
146     /**
147      * Returns the class name of the {@link AuthProviderFactory}.
148      */
149     @JsonProperty
150     public String factoryClassName() {
151         return factory.getClass().getName();
152     }
153 
154     /**
155      * Returns the usernames of the users with administrator rights.
156      */
157     @JsonProperty
158     public Set<String> administrators() {
159         return administrators;
160     }
161 
162     /**
163      * Returns whether login names are case-sensitive.
164      */
165     @JsonProperty
166     public boolean caseSensitiveLoginNames() {
167         return caseSensitiveLoginNames;
168     }
169 
170     /**
171      * Returns the spec of the session cache.
172      */
173     @JsonProperty
174     public String sessionCacheSpec() {
175         return sessionCacheSpec;
176     }
177 
178     /**
179      * Returns the timeout of an inactive session in milliseconds.
180      */
181     @JsonProperty
182     public long sessionTimeoutMillis() {
183         return sessionTimeoutMillis;
184     }
185 
186     /**
187      * Returns the cron expression that describes how often session validation task should run.
188      */
189     @JsonProperty
190     public String sessionValidationSchedule() {
191         return sessionValidationSchedule;
192     }
193 
194     /**
195      * Returns the additional properties given to the {@link AuthProviderFactory}.
196      */
197     @Nullable
198     @JsonProperty
199     public JsonNode properties() {
200         return properties;
201     }
202 
203     /**
204      * Returns the additional properties, converted to {@code T}.
205      */
206     @Nullable
207     public <T> T properties(Class<T> clazz) throws JsonProcessingException {
208         return properties != null ? Jackson.treeToValue(properties, clazz) : null;
209     }
210 
211     /**
212      * Returns a {@link Function} which normalizes a login name based on the
213      * {@link AuthConfig#caseSensitiveLoginNames()} property.
214      */
215     public Function<String, String> loginNameNormalizer() {
216         return caseSensitiveLoginNames() ? Function.identity() : Ascii::toLowerCase;
217     }
218 
219     @Override
220     public String toString() {
221         try {
222             return Jackson.writeValueAsPrettyString(this);
223         } catch (JsonProcessingException e) {
224             throw new IllegalStateException(e);
225         }
226     }
227 
228     private static String validateSchedule(String sessionValidationSchedule) {
229         try {
230             CronExpression.validateExpression(sessionValidationSchedule);
231             return sessionValidationSchedule;
232         } catch (ParseException e) {
233             throw new IllegalArgumentException("Invalid session validation schedule", e);
234         }
235     }
236 }