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