1   /*
2    * Copyright 2025 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.storage.encryption;
17  
18  import static com.google.common.base.Preconditions.checkArgument;
19  import static java.time.format.DateTimeFormatter.ISO_INSTANT;
20  import static java.util.Objects.requireNonNull;
21  
22  import java.time.Instant;
23  
24  import com.fasterxml.jackson.annotation.JsonCreator;
25  import com.fasterxml.jackson.annotation.JsonProperty;
26  import com.google.common.base.MoreObjects;
27  
28  /**
29   * Details of a wrapped data encryption key (DEK).
30   */
31  public final class WrappedDekDetails {
32  
33      private final String wrappedDek;
34      private final int dekVersion;
35      private final String kekId;
36      private final String projectName;
37      private final String repoName;
38      private final String creation;
39      private final Instant creationInstant;
40  
41      /**
42       * Creates a new instance.
43       */
44      public WrappedDekDetails(String wrappedDek, int dekVersion, String kekId,
45                               String projectName, String repoName) {
46          this(wrappedDek, dekVersion, kekId, Instant.now(), projectName, repoName);
47      }
48  
49      /**
50       * Creates a new instance.
51       */
52      @JsonCreator
53      public WrappedDekDetails(@JsonProperty("wrappedDek") String wrappedDek,
54                               @JsonProperty("dekVersion") int dekVersion,
55                               @JsonProperty("kekId") String kekId,
56                               @JsonProperty("creation") Instant creation,
57                               @JsonProperty("projectName") String projectName,
58                               @JsonProperty("repoName") String repoName) {
59          this.wrappedDek = requireNonNull(wrappedDek, "wrappedDek");
60          this.kekId = requireNonNull(kekId, "kekId");
61          checkArgument(dekVersion > 0, "dekVersion must be positive: %s", dekVersion);
62          this.dekVersion = dekVersion;
63          creationInstant = creation;
64          this.creation = ISO_INSTANT.format(requireNonNull(creation, "creation"));
65          this.projectName = requireNonNull(projectName, "projectName");
66          this.repoName = requireNonNull(repoName, "repoName");
67      }
68  
69      /**
70       * Returns the wrapped data encryption key (DEK), encoded in Base64.
71       */
72      @JsonProperty
73      public String wrappedDek() {
74          return wrappedDek;
75      }
76  
77      /**
78       * Returns the version of the data encryption key (DEK).
79       */
80      @JsonProperty
81      public int dekVersion() {
82          return dekVersion;
83      }
84  
85      /**
86       * Returns the ID of the key encryption key (KEK) used to wrap the DEK.
87       */
88      @JsonProperty
89      public String kekId() {
90          return kekId;
91      }
92  
93      /**
94       * Returns the creation time of the wrapped DEK in ISO-8601 format.
95       */
96      @JsonProperty
97      public String creation() {
98          return creation;
99      }
100 
101     /**
102      * Returns the creation {@link Instant} of the wrapped DEK.
103      */
104     public Instant creationInstant() {
105         return creationInstant;
106     }
107 
108     /**
109      * Returns the project name associated with this wrapped DEK.
110      */
111     @JsonProperty
112     public String projectName() {
113         return projectName;
114     }
115 
116     /**
117      * Returns the repository name associated with this wrapped DEK.
118      */
119     @JsonProperty
120     public String repoName() {
121         return repoName;
122     }
123 
124     @Override
125     public int hashCode() {
126         int result = wrappedDek.hashCode();
127         result = 31 * result + dekVersion;
128         result = 31 * result + kekId.hashCode();
129         result = 31 * result + creation.hashCode();
130         result = 31 * result + projectName.hashCode();
131         result = 31 * result + repoName.hashCode();
132         return result;
133     }
134 
135     @Override
136     public boolean equals(Object obj) {
137         if (this == obj) {
138             return true;
139         }
140         if (!(obj instanceof WrappedDekDetails)) {
141             return false;
142         }
143         final WrappedDekDetails that = (WrappedDekDetails) obj;
144         return dekVersion == that.dekVersion &&
145                wrappedDek.equals(that.wrappedDek) &&
146                kekId.equals(that.kekId) &&
147                creation.equals(that.creation) &&
148                projectName.equals(that.projectName) &&
149                repoName.equals(that.repoName);
150     }
151 
152     @Override
153     public String toString() {
154         return MoreObjects.toStringHelper(this)
155                           .add("wrappedDek", "*****")
156                           .add("dekVersion", dekVersion)
157                           .add("kekId", kekId)
158                           .add("creation", creation)
159                           .add("projectName", projectName)
160                           .add("repoName", repoName)
161                           .toString();
162     }
163 }