1   /*
2    * Copyright 2019 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.metadata;
18  
19  import static com.google.common.base.MoreObjects.firstNonNull;
20  import static java.util.Objects.requireNonNull;
21  
22  import javax.annotation.Nullable;
23  
24  import com.fasterxml.jackson.annotation.JsonCreator;
25  import com.fasterxml.jackson.annotation.JsonIgnore;
26  import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
27  import com.fasterxml.jackson.annotation.JsonInclude;
28  import com.fasterxml.jackson.annotation.JsonInclude.Include;
29  import com.fasterxml.jackson.annotation.JsonProperty;
30  import com.google.common.base.MoreObjects;
31  
32  import com.linecorp.centraldogma.common.ProjectRole;
33  import com.linecorp.centraldogma.internal.Util;
34  
35  /**
36   * Specifies details of an application token.
37   */
38  @JsonIgnoreProperties(ignoreUnknown = true)
39  @JsonInclude(Include.NON_NULL)
40  public final class Token implements Identifiable {
41  
42      /**
43       * An application identifier.
44       */
45      private final String appId;
46  
47      /**
48       * A secret which is used to access an HTTP API.
49       */
50      @Nullable
51      private final String secret;
52  
53      /**
54       * Specifies whether this token is for system administrators.
55       */
56      private final boolean isSystemAdmin;
57  
58      private final boolean allowGuestAccess;
59  
60      /**
61       * Specifies when this token is created by whom.
62       */
63      private final UserAndTimestamp creation;
64  
65      /**
66       * Specifies when this repository is removed by whom.
67       */
68      @Nullable
69      private final UserAndTimestamp deactivation;
70  
71      @Nullable
72      private final UserAndTimestamp deletion;
73  
74      Token(String appId, String secret, boolean isSystemAdmin, boolean allowGuestAccess,
75            UserAndTimestamp creation) {
76          this(appId, secret, null, isSystemAdmin, allowGuestAccess, creation, null, null);
77      }
78  
79      /**
80       * Creates a new instance.
81       */
82      @JsonCreator
83      public Token(@JsonProperty("appId") String appId,
84                   @JsonProperty("secret") String secret,
85                   // TODO(minwoox): Remove admin field after all tokens are migrated.
86                   @JsonProperty("admin") @Nullable Boolean isAdmin,
87                   @JsonProperty("systemAdmin") @Nullable Boolean isSystemAdmin,
88                   @JsonProperty("allowGuestAccess") @Nullable Boolean allowGuestAccess,
89                   @JsonProperty("creation") UserAndTimestamp creation,
90                   @JsonProperty("deactivation") @Nullable UserAndTimestamp deactivation,
91                   @JsonProperty("deletion") @Nullable UserAndTimestamp deletion) {
92          assert isAdmin != null || isSystemAdmin != null;
93          this.appId = Util.validateFileName(appId, "appId");
94          this.secret = Util.validateFileName(secret, "secret");
95          this.isSystemAdmin = isSystemAdmin != null ? isSystemAdmin : isAdmin;
96          // Allow guest access by default for backward compatibility.
97          this.allowGuestAccess = firstNonNull(allowGuestAccess, true);
98          this.creation = requireNonNull(creation, "creation");
99          this.deactivation = deactivation;
100         this.deletion = deletion;
101     }
102 
103     private Token(String appId, boolean isSystemAdmin, boolean allowGuestAccess, UserAndTimestamp creation,
104                   @Nullable UserAndTimestamp deactivation, @Nullable UserAndTimestamp deletion) {
105         this.appId = Util.validateFileName(appId, "appId");
106         this.isSystemAdmin = isSystemAdmin;
107         this.allowGuestAccess = allowGuestAccess;
108         this.creation = requireNonNull(creation, "creation");
109         this.deactivation = deactivation;
110         this.deletion = deletion;
111         secret = null;
112     }
113 
114     @Override
115     public String id() {
116         return appId;
117     }
118 
119     /**
120      * Returns the ID of the application.
121      */
122     @JsonProperty
123     public String appId() {
124         return appId;
125     }
126 
127     /**
128      * Returns the secret.
129      */
130     @Nullable
131     @JsonProperty
132     public String secret() {
133         return secret;
134     }
135 
136     /**
137      * Returns whether this token has system administrative privileges.
138      */
139     @JsonProperty
140     public boolean isSystemAdmin() {
141         return isSystemAdmin;
142     }
143 
144     /**
145      * Returns whether this token allows {@link ProjectRole#GUEST} access.
146      */
147     @JsonProperty
148     public boolean allowGuestAccess() {
149         return allowGuestAccess;
150     }
151 
152     /**
153      * Returns who created this token when.
154      */
155     @JsonProperty
156     public UserAndTimestamp creation() {
157         return creation;
158     }
159 
160     /**
161      * Returns who deactivated this token when.
162      */
163     @Nullable
164     @JsonProperty
165     public UserAndTimestamp deactivation() {
166         return deactivation;
167     }
168 
169     /**
170      * Returns who deleted this token when.
171      */
172     @Nullable
173     @JsonProperty
174     public UserAndTimestamp deletion() {
175         return deletion;
176     }
177 
178     /**
179      * Returns whether this token is activated.
180      */
181     @JsonIgnore
182     public boolean isActive() {
183         return deactivation == null && deletion == null;
184     }
185 
186     /**
187      * Returns whether this token is deleted.
188      */
189     @JsonIgnore
190     public boolean isDeleted() {
191         return deletion != null;
192     }
193 
194     @Override
195     public String toString() {
196         // Do not add "secret" to prevent it from logging.
197         return MoreObjects.toStringHelper(this).omitNullValues()
198                           .add("appId", appId())
199                           .add("isSystemAdmin", isSystemAdmin())
200                           .add("allowGuestAccess", allowGuestAccess())
201                           .add("creation", creation())
202                           .add("deactivation", deactivation())
203                           .add("deletion", deletion())
204                           .toString();
205     }
206 
207     /**
208      * Returns a new {@link Token} instance without its secret.
209      */
210     public Token withoutSecret() {
211         return new Token(appId(), isSystemAdmin(), allowGuestAccess(), creation(), deactivation(), deletion());
212     }
213 
214     /**
215      * Returns a new {@link Token} instance without its secret.
216      * This method must be called by the token whose secret is not null.
217      */
218     public Token withSystemAdmin(boolean isSystemAdmin) {
219         if (isSystemAdmin == isSystemAdmin()) {
220             return this;
221         }
222         final String secret = secret();
223         assert secret != null;
224         return new Token(appId(), secret, null, isSystemAdmin, allowGuestAccess(), creation(),
225                          deactivation(), deletion());
226     }
227 }