1 /* 2 * Copyright 2016 Google LLC 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.cloud.dns; 18 19 import static com.google.common.base.Preconditions.checkNotNull; 20 import static com.google.common.collect.Lists.transform; 21 22 import com.google.api.client.util.Data; 23 import com.google.api.services.dns.model.DnsKeySpec; 24 import com.google.api.services.dns.model.ManagedZone; 25 import com.google.api.services.dns.model.ManagedZoneDnsSecConfig; 26 import com.google.common.base.Function; 27 import com.google.common.base.MoreObjects; 28 import com.google.common.collect.ImmutableList; 29 import com.google.common.collect.ImmutableSet; 30 import com.google.common.collect.Lists; 31 import com.google.common.collect.Maps; 32 import java.io.Serializable; 33 import java.math.BigInteger; 34 import java.util.List; 35 import java.util.Map; 36 import java.util.Objects; 37 import java.util.Set; 38 import org.threeten.bp.Instant; 39 import org.threeten.bp.ZoneOffset; 40 import org.threeten.bp.format.DateTimeFormatter; 41 42 /** 43 * A {@code Zone} represents a DNS zone hosted by the Google Cloud DNS service. A zone is a subtree 44 * of the DNS namespace under one administrative responsibility. See <a 45 * href="https://cloud.google.com/dns/api/v1/managedZones">Google Cloud DNS documentation</a> for 46 * more information. 47 */ 48 public class ZoneInfo implements Serializable { 49 50 private static final long serialVersionUID = -5313169712036079818L; 51 private static final DateTimeFormatter DATE_TIME_FORMATTER = 52 DateTimeFormatter.ISO_DATE_TIME.withZone(ZoneOffset.UTC); 53 54 private final String name; 55 private final String generatedId; 56 private final Long creationTimeMillis; 57 private final String dnsName; 58 private final String description; 59 private final String nameServerSet; 60 private final List<String> nameServers; 61 private final DnsSecConfig dnsSecConfig; 62 private final Map<String, String> labels; 63 64 /** This class represents the DNS key spec. */ 65 public static class KeySpec { 66 67 private String algorithm; 68 private Long keyLength; 69 private String keyType; 70 71 public static class Builder { 72 private String algorithm; 73 private Long keyLength; 74 private String keyType; 75 Builder()76 private Builder() {} 77 Builder(KeySpec keySpec)78 private Builder(KeySpec keySpec) { 79 this.algorithm = keySpec.algorithm; 80 this.keyLength = keySpec.keyLength; 81 this.keyType = keySpec.getKeyType(); 82 } 83 84 /** Specifies the DNSSEC algorithm of this key. */ setAlgorithm(String algorithm)85 public Builder setAlgorithm(String algorithm) { 86 this.algorithm = algorithm; 87 return this; 88 } 89 90 /** Specifies the length of the keys in bits. */ setKeyLength(Long keyLength)91 public Builder setKeyLength(Long keyLength) { 92 this.keyLength = keyLength; 93 return this; 94 } 95 96 /** 97 * Specifies the key type, Whether this key is a signing key (KSK) or a zone signing key 98 * (ZSK). 99 */ setKeyType(String keyType)100 public Builder setKeyType(String keyType) { 101 this.keyType = keyType; 102 return this; 103 } 104 105 /** Creates a {@code KeySpec} object. */ build()106 public KeySpec build() { 107 return new KeySpec(this); 108 } 109 } 110 111 /** Returns a builder for {@code KeySpec} objects. */ newBuilder()112 public static Builder newBuilder() { 113 return new Builder(); 114 } 115 KeySpec(Builder builder)116 private KeySpec(Builder builder) { 117 algorithm = builder.algorithm; 118 keyLength = builder.keyLength; 119 keyType = builder.keyType; 120 } 121 122 @Override equals(Object o)123 public boolean equals(Object o) { 124 if (this == o) { 125 return true; 126 } 127 if (o == null || getClass() != o.getClass()) { 128 return false; 129 } 130 KeySpec keySpec = (KeySpec) o; 131 return Objects.equals(algorithm, keySpec.algorithm) 132 && Objects.equals(keyLength, keySpec.keyLength) 133 && Objects.equals(keyType, keySpec.keyType); 134 } 135 136 @Override toString()137 public String toString() { 138 return MoreObjects.toStringHelper(this) 139 .add("algorithm", getAlgorithm()) 140 .add("keyLength", getKeyLength()) 141 .add("keyType", getKeyType()) 142 .toString(); 143 } 144 145 @Override hashCode()146 public int hashCode() { 147 return Objects.hash(algorithm, keyLength, keyType); 148 } 149 toPb()150 DnsKeySpec toPb() { 151 DnsKeySpec dnsKeySpecPb = new DnsKeySpec(); 152 dnsKeySpecPb.setAlgorithm(algorithm); 153 dnsKeySpecPb.setKeyLength(keyLength); 154 dnsKeySpecPb.setKeyType(keyType); 155 return dnsKeySpecPb; 156 } 157 fromPb(DnsKeySpec dnsKeySpec)158 static KeySpec fromPb(DnsKeySpec dnsKeySpec) { 159 Builder builder = newBuilder(); 160 builder.setAlgorithm(dnsKeySpec.getAlgorithm() == null ? null : dnsKeySpec.getAlgorithm()); 161 builder.setKeyLength(dnsKeySpec.getKeyLength() == null ? null : dnsKeySpec.getKeyLength()); 162 builder.setKeyType(dnsKeySpec.getKeyType() == null ? null : dnsKeySpec.getKeyType()); 163 return builder.build(); 164 } 165 166 /** Returns the DNSSEC algorithm of this key. */ getAlgorithm()167 public String getAlgorithm() { 168 return algorithm; 169 } 170 171 /** Returns the key length. */ getKeyLength()172 public Long getKeyLength() { 173 return keyLength; 174 } 175 176 /** Returns the key type. */ getKeyType()177 public String getKeyType() { 178 return keyType; 179 } 180 } 181 182 /** This class represents the DNSSEC configuration. */ 183 public static class DnsSecConfig { 184 185 private static final Set<String> VALID_STATE_VALUES = ImmutableSet.of("on", "off", "transfer"); 186 private static final Set<String> VALID_NONEXISTANCE_VALUES = ImmutableSet.of("nsec", "nsec3"); 187 188 private List<KeySpec> defaultKeySpecs; 189 private String nonExistence; 190 private String state; 191 192 public static class Builder { 193 private List<KeySpec> defaultKeySpecs; 194 private String nonExistence; 195 private String state; 196 Builder()197 private Builder() {} 198 Builder(DnsSecConfig dnsSecConfig)199 private Builder(DnsSecConfig dnsSecConfig) { 200 this.defaultKeySpecs = dnsSecConfig.defaultKeySpecs; 201 this.nonExistence = dnsSecConfig.nonExistence; 202 this.state = dnsSecConfig.state; 203 } 204 205 /** 206 * Specifies parameters for generating initial DnsKeys for this ManagedZone. This can be 207 * change while state is OFF. 208 */ setDefaultKeySpecs(List<KeySpec> defaultKeySpecs)209 public Builder setDefaultKeySpecs(List<KeySpec> defaultKeySpecs) { 210 this.defaultKeySpecs = defaultKeySpecs; 211 return this; 212 } 213 214 /** 215 * Specifies the mechanism for authenticated denial-of-existence responses. This can be change 216 * while state is OFF. Acceptable values are 'nsec' or 'nsec3'. 217 * 218 * @throws IllegalArgumentException if nonExistence value is not acceptable 219 */ setNonExistence(String nonExistence)220 public Builder setNonExistence(String nonExistence) { 221 validateValue(nonExistence, VALID_NONEXISTANCE_VALUES); 222 this.nonExistence = nonExistence; 223 return this; 224 } 225 226 /** 227 * Specifies whether DNSSEC is enabled, and what mode it is in. Acceptable values are 'on', 228 * 'off' or 'transfer'. 229 * 230 * @throws IllegalArgumentException if state value is not acceptable 231 */ setState(String state)232 public Builder setState(String state) { 233 validateValue(state, VALID_STATE_VALUES); 234 this.state = state; 235 return this; 236 } 237 238 /** Creates a {@code DnsSecConfig} object. */ build()239 public DnsSecConfig build() { 240 return new DnsSecConfig(this); 241 } 242 } 243 244 /** Returns a builder for the current blob. */ toBuilder()245 public Builder toBuilder() { 246 return new Builder(this); 247 } 248 249 /** Returns a builder for {@code DnsSecConfig} objects. */ newBuilder()250 public static Builder newBuilder() { 251 return new Builder(); 252 } 253 DnsSecConfig(Builder builder)254 private DnsSecConfig(Builder builder) { 255 defaultKeySpecs = builder.defaultKeySpecs; 256 nonExistence = builder.nonExistence; 257 state = builder.state; 258 } 259 260 @Override equals(Object o)261 public boolean equals(Object o) { 262 if (this == o) { 263 return true; 264 } 265 if (o == null || getClass() != o.getClass()) { 266 return false; 267 } 268 DnsSecConfig that = (DnsSecConfig) o; 269 return Objects.equals(defaultKeySpecs, that.defaultKeySpecs) 270 && Objects.equals(nonExistence, that.nonExistence) 271 && Objects.equals(state, that.state); 272 } 273 274 @Override hashCode()275 public int hashCode() { 276 return Objects.hash(defaultKeySpecs, nonExistence, state); 277 } 278 279 @Override toString()280 public String toString() { 281 return MoreObjects.toStringHelper(this) 282 .add("defaultKeySpecs", getDefaultKeySpecs()) 283 .add("nonExistence", getNonExistence()) 284 .add("state", getState()) 285 .toString(); 286 } 287 toPb()288 ManagedZoneDnsSecConfig toPb() { 289 ManagedZoneDnsSecConfig dnsSecConfigPb = new ManagedZoneDnsSecConfig(); 290 if (defaultKeySpecs != null) { 291 dnsSecConfigPb.setDefaultKeySpecs( 292 transform( 293 defaultKeySpecs, 294 new Function<KeySpec, DnsKeySpec>() { 295 @Override 296 public DnsKeySpec apply(KeySpec keySpec) { 297 return keySpec.toPb(); 298 } 299 })); 300 } 301 dnsSecConfigPb.setNonExistence(nonExistence); 302 dnsSecConfigPb.setState(state); 303 return dnsSecConfigPb; 304 } 305 fromPb(ManagedZoneDnsSecConfig managedZoneDnsSecConfig)306 static DnsSecConfig fromPb(ManagedZoneDnsSecConfig managedZoneDnsSecConfig) { 307 Builder builder = newBuilder(); 308 if (managedZoneDnsSecConfig.getDefaultKeySpecs() != null) { 309 builder.setDefaultKeySpecs( 310 transform( 311 managedZoneDnsSecConfig.getDefaultKeySpecs(), 312 new Function<DnsKeySpec, KeySpec>() { 313 @Override 314 public KeySpec apply(DnsKeySpec dnsKeySpec) { 315 return KeySpec.fromPb(dnsKeySpec); 316 } 317 })); 318 } 319 builder.setNonExistence(managedZoneDnsSecConfig.getNonExistence()); 320 builder.setState(managedZoneDnsSecConfig.getState()); 321 return builder.build(); 322 } 323 324 /** Returns the DefaultKeySpecs. */ getDefaultKeySpecs()325 public List<KeySpec> getDefaultKeySpecs() { 326 return defaultKeySpecs; 327 } 328 329 /** Returns the authenticated denial-of-existence responses. */ getNonExistence()330 public String getNonExistence() { 331 return nonExistence; 332 } 333 334 /** Returns the DNSSEC state. */ getState()335 public String getState() { 336 return state; 337 } 338 validateValue(String value, Set<String> validValues)339 private static void validateValue(String value, Set<String> validValues) { 340 if (!validValues.contains(value)) { 341 throw new IllegalArgumentException( 342 "Invalid value, Use one of the value from acceptable values " + validValues); 343 } 344 } 345 } 346 347 /** Builder for {@code ZoneInfo}. */ 348 public abstract static class Builder { 349 350 /** Sets a mandatory user-provided name for the zone. It must be unique within the project. */ setName(String name)351 public abstract Builder setName(String name); 352 353 /** Sets service-generated id for the zone. */ setGeneratedId(String generatedId)354 abstract Builder setGeneratedId(String generatedId); 355 356 /** Sets the time when this zone was created. */ setCreationTimeMillis(long creationTimeMillis)357 abstract Builder setCreationTimeMillis(long creationTimeMillis); 358 359 /** Sets a mandatory DNS name of this zone, for instance "example.com.". */ setDnsName(String dnsName)360 public abstract Builder setDnsName(String dnsName); 361 362 /** 363 * Sets a mandatory description for this zone. The value is a string of at most 1024 characters 364 * which has no effect on the zone's function. 365 */ setDescription(String description)366 public abstract Builder setDescription(String description); 367 368 /** 369 * Optionally specifies the NameServerSet for this zone. A NameServerSet is a set of DNS name 370 * servers that all host the same zones. Most users will not need to specify this value. 371 */ setNameServerSet(String nameServerSet)372 abstract Builder setNameServerSet(String nameServerSet); 373 // this should not be included in tooling as per the service owners 374 375 /** 376 * Sets a list of servers that hold the information about the zone. This information is provided 377 * by Google Cloud DNS and is read only. 378 */ setNameServers(List<String> nameServers)379 abstract Builder setNameServers(List<String> nameServers); 380 381 /** Sets the DNSSEC configuration. */ setDnsSecConfig(DnsSecConfig dnsSecConfig)382 public Builder setDnsSecConfig(DnsSecConfig dnsSecConfig) { 383 return this; 384 } 385 386 /** Sets the label of this zone. */ setLabels(Map<String, String> labels)387 public Builder setLabels(Map<String, String> labels) { 388 return this; 389 } 390 391 /** Builds the instance of {@code ZoneInfo} based on the information set by this builder. */ build()392 public abstract ZoneInfo build(); 393 } 394 395 static class BuilderImpl extends Builder { 396 private String name; 397 private String generatedId; 398 private Long creationTimeMillis; 399 private String dnsName; 400 private String description; 401 private String nameServerSet; 402 private List<String> nameServers; 403 private DnsSecConfig dnsSecConfig; 404 private Map<String, String> labels; 405 BuilderImpl(String name)406 private BuilderImpl(String name) { 407 this.name = checkNotNull(name); 408 } 409 410 /** Creates a builder from an existing ZoneInfo object. */ BuilderImpl(ZoneInfo info)411 BuilderImpl(ZoneInfo info) { 412 this.name = info.name; 413 this.generatedId = info.generatedId; 414 this.creationTimeMillis = info.creationTimeMillis; 415 this.dnsName = info.dnsName; 416 this.description = info.description; 417 this.nameServerSet = info.nameServerSet; 418 if (info.nameServers != null) { 419 this.nameServers = ImmutableList.copyOf(info.nameServers); 420 } 421 this.dnsSecConfig = info.dnsSecConfig; 422 this.labels = info.labels; 423 } 424 425 @Override setName(String name)426 public Builder setName(String name) { 427 this.name = checkNotNull(name); 428 return this; 429 } 430 431 @Override setGeneratedId(String generatedId)432 Builder setGeneratedId(String generatedId) { 433 this.generatedId = generatedId; 434 return this; 435 } 436 437 @Override setCreationTimeMillis(long creationTimeMillis)438 Builder setCreationTimeMillis(long creationTimeMillis) { 439 this.creationTimeMillis = creationTimeMillis; 440 return this; 441 } 442 443 @Override setDnsName(String dnsName)444 public Builder setDnsName(String dnsName) { 445 this.dnsName = checkNotNull(dnsName); 446 return this; 447 } 448 449 @Override setDescription(String description)450 public Builder setDescription(String description) { 451 this.description = checkNotNull(description); 452 return this; 453 } 454 455 @Override setNameServerSet(String nameServerSet)456 Builder setNameServerSet(String nameServerSet) { 457 this.nameServerSet = checkNotNull(nameServerSet); 458 return this; 459 } 460 461 @Override setNameServers(List<String> nameServers)462 Builder setNameServers(List<String> nameServers) { 463 checkNotNull(nameServers); 464 this.nameServers = Lists.newLinkedList(nameServers); 465 return this; 466 } 467 468 @Override setDnsSecConfig(DnsSecConfig dnsSecConfig)469 public Builder setDnsSecConfig(DnsSecConfig dnsSecConfig) { 470 this.dnsSecConfig = checkNotNull(dnsSecConfig); 471 return this; 472 } 473 474 @Override setLabels(Map<String, String> labels)475 public Builder setLabels(Map<String, String> labels) { 476 if (labels != null) { 477 this.labels = 478 Maps.transformValues( 479 labels, 480 new Function<String, String>() { 481 @Override 482 public String apply(String input) { 483 // replace null values with empty strings 484 return input == null ? Data.<String>nullOf(String.class) : input; 485 } 486 }); 487 } 488 return this; 489 } 490 491 @Override build()492 public ZoneInfo build() { 493 return new ZoneInfo(this); 494 } 495 } 496 ZoneInfo(BuilderImpl builder)497 ZoneInfo(BuilderImpl builder) { 498 this.name = builder.name; 499 this.generatedId = builder.generatedId; 500 this.creationTimeMillis = builder.creationTimeMillis; 501 this.dnsName = builder.dnsName; 502 this.description = builder.description; 503 this.nameServerSet = builder.nameServerSet; 504 this.nameServers = 505 builder.nameServers == null ? null : ImmutableList.copyOf(builder.nameServers); 506 this.dnsSecConfig = builder.dnsSecConfig; 507 this.labels = builder.labels; 508 } 509 510 /** 511 * Returns a ZoneInfo object with assigned {@code name}, {@code dnsName} and {@code description}. 512 */ of(String name, String dnsName, String description)513 public static ZoneInfo of(String name, String dnsName, String description) { 514 return new BuilderImpl(name).setDnsName(dnsName).setDescription(description).build(); 515 } 516 517 /** Returns a {@code ZoneInfo} builder where the DNS name is set to the provided name. */ newBuilder(String name)518 public static Builder newBuilder(String name) { 519 return new BuilderImpl(name); 520 } 521 522 /** Returns the user-defined name of the zone. */ getName()523 public String getName() { 524 return name; 525 } 526 527 /** Returns the service-generated id for this zone. */ getGeneratedId()528 public String getGeneratedId() { 529 return generatedId; 530 } 531 532 /** Returns the time when this zone was created on the server. */ getCreationTimeMillis()533 public Long getCreationTimeMillis() { 534 return creationTimeMillis; 535 } 536 537 /** Returns the DNS name of this zone, for instance "example.com.". */ getDnsName()538 public String getDnsName() { 539 return dnsName; 540 } 541 542 /** Returns the description of this zone. */ getDescription()543 public String getDescription() { 544 return description; 545 } 546 547 /** 548 * Returns the optionally specified set of DNS name servers that all host this zone. This value is 549 * set only for specific use cases and is left empty for vast majority of users. 550 */ getNameServerSet()551 public String getNameServerSet() { 552 return nameServerSet; 553 } 554 555 /** Returns the labels for this zone. */ getLabels()556 public Map<String, String> getLabels() { 557 return labels; 558 } 559 560 /** 561 * The nameservers that the zone should be delegated to. This is defined by the Google DNS cloud. 562 */ getNameServers()563 public List<String> getNameServers() { 564 return nameServers == null ? ImmutableList.<String>of() : nameServers; 565 } 566 getDnsSecConfig()567 public DnsSecConfig getDnsSecConfig() { 568 return dnsSecConfig; 569 } 570 571 /** Returns a builder for {@code ZoneInfo} prepopulated with the metadata of this zone. */ toBuilder()572 public Builder toBuilder() { 573 return new BuilderImpl(this); 574 } 575 toPb()576 ManagedZone toPb() { 577 ManagedZone pb = new ManagedZone(); 578 pb.setDescription(this.getDescription()); 579 pb.setDnsName(this.getDnsName()); 580 if (this.getGeneratedId() != null) { 581 pb.setId(new BigInteger(this.getGeneratedId())); 582 } 583 pb.setName(this.getName()); 584 pb.setNameServers(this.nameServers); // do use real attribute value which may be null 585 pb.setNameServerSet(this.getNameServerSet()); 586 if (this.getCreationTimeMillis() != null) { 587 pb.setCreationTime( 588 DATE_TIME_FORMATTER.format(Instant.ofEpochMilli(this.getCreationTimeMillis()))); 589 } 590 if (this.dnsSecConfig != null) { 591 pb.setDnssecConfig(this.dnsSecConfig.toPb()); 592 } 593 if (this.getLabels() != null) { 594 pb.setLabels(labels); 595 } 596 return pb; 597 } 598 fromPb(ManagedZone pb)599 static ZoneInfo fromPb(ManagedZone pb) { 600 Builder builder = new BuilderImpl(pb.getName()); 601 if (pb.getDescription() != null) { 602 builder.setDescription(pb.getDescription()); 603 } 604 if (pb.getDnsName() != null) { 605 builder.setDnsName(pb.getDnsName()); 606 } 607 if (pb.getId() != null) { 608 builder.setGeneratedId(pb.getId().toString()); 609 } 610 if (pb.getNameServers() != null) { 611 builder.setNameServers(pb.getNameServers()); 612 } 613 if (pb.getNameServerSet() != null) { 614 builder.setNameServerSet(pb.getNameServerSet()); 615 } 616 if (pb.getCreationTime() != null) { 617 builder.setCreationTimeMillis( 618 DATE_TIME_FORMATTER.parse(pb.getCreationTime(), Instant.FROM).toEpochMilli()); 619 } 620 if (pb.getDnssecConfig() != null) { 621 builder.setDnsSecConfig(DnsSecConfig.fromPb(pb.getDnssecConfig())); 622 } 623 if (pb.getLabels() != null) { 624 builder.setLabels(pb.getLabels()); 625 } 626 return builder.build(); 627 } 628 629 @Override equals(Object obj)630 public boolean equals(Object obj) { 631 return obj == this 632 || obj != null 633 && obj.getClass().equals(ZoneInfo.class) 634 && Objects.equals(toPb(), ((ZoneInfo) obj).toPb()); 635 } 636 637 @Override hashCode()638 public int hashCode() { 639 return Objects.hash( 640 name, 641 generatedId, 642 creationTimeMillis, 643 dnsName, 644 description, 645 nameServerSet, 646 nameServers, 647 dnsSecConfig, 648 labels); 649 } 650 651 @Override toString()652 public String toString() { 653 return MoreObjects.toStringHelper(this) 654 .add("name", getName()) 655 .add("generatedId", getGeneratedId()) 656 .add("description", getDescription()) 657 .add("dnsName", getDnsName()) 658 .add("nameServerSet", getNameServerSet()) 659 .add("nameServers", getNameServers()) 660 .add("creationTimeMillis", getCreationTimeMillis()) 661 .add("dnsSecConfig", getDnsSecConfig()) 662 .add("labels", getLabels()) 663 .toString(); 664 } 665 } 666