google-nomulus/java/google/registry/tools/CreateOrUpdateDomainCommand.java
guyben 2e62ad2658 Allow setting DS records in create_domain and update_domain
The DS records consist of 4 values:
- keyTag: unsigned short (2 bytes)
- alg: unsigned byte
- digestType: unsigned byte
- digest: binary hex

NOTE: the current CL doesn't support keyData, neither as the optional field in dsData nor as a replacement for dsData

The command tool accepts DS records as a string, where the 4 values are given
as one string separated by white-spaces as follows:
<keyTag> <alg>  <digestType>  <digest>

e.g. something like:
60485 5  2  D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A

which is how it's written in Zone files, allowing easy copy-paste from existing values.
ommas is confusing when using spaces.

The various "numbers" (keyTag, alg, digestType) are only checked that they are
positive integers - the rest is left for the server.

digest it checked to be an even-lengthed hex string.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=184583068
2018-02-05 23:56:16 -05:00

161 lines
5.2 KiB
Java

// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.tools;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static google.registry.util.CollectionUtils.findDuplicates;
import com.beust.jcommander.IStringConverter;
import com.beust.jcommander.Parameter;
import com.google.auto.value.AutoValue;
import com.google.common.base.Ascii;
import com.google.common.base.CharMatcher;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.BaseEncoding;
import com.google.template.soy.data.SoyListData;
import com.google.template.soy.data.SoyMapData;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/** Shared base class for commands to create or update a Domain via EPP. */
abstract class CreateOrUpdateDomainCommand extends MutatingEppToolCommand {
@Parameter(
names = {"-c", "--client"},
description = "Client identifier of the registrar to execute the command as",
required = true
)
String clientId;
@Parameter(description = "Names of the domains", required = true)
private List<String> mainParameters;
@Parameter(
names = {"-n", "--nameservers"},
description = "Comma-separated list of nameservers, up to 13."
)
List<String> nameservers = new ArrayList<>();
@Parameter(
names = {"-r", "--registrant"},
description = "Domain registrant."
)
String registrant;
@Parameter(
names = {"-a", "--admins"},
description = "Comma-separated list of admin contacts."
)
List<String> admins = new ArrayList<>();
@Parameter(
names = {"-t", "--techs"},
description = "Comma-separated list of technical contacts."
)
List<String> techs = new ArrayList<>();
@Parameter(
names = {"-p", "--password"},
description = "Password."
)
String password;
@Parameter(
names = "--ds_records",
description =
"Comma-separated list of DS records. Each DS record is given as "
+ "<keyTag> <alg> <digestType> <digest>, in order, as it appears in the Zonefile.",
converter = DsRecordConverter.class
)
List<DsRecord> dsRecords = new ArrayList<>();
Set<String> domains;
@AutoValue
abstract static class DsRecord {
private static final Splitter SPLITTER =
Splitter.on(CharMatcher.whitespace()).omitEmptyStrings();
public abstract int keyTag();
public abstract int alg();
public abstract int digestType();
public abstract String digest();
private static DsRecord create(int keyTag, int alg, int digestType, String digest) {
digest = Ascii.toUpperCase(digest);
checkArgument(
BaseEncoding.base16().canDecode(digest),
"digest should be even-lengthed hex, but is %s (length %s)",
digest,
digest.length());
return new AutoValue_CreateOrUpdateDomainCommand_DsRecord(keyTag, alg, digestType, digest);
}
/**
* Parses a string representation of the DS record.
*
* <p>The string format accepted is "[keyTag] [alg] [digestType] [digest]" (i.e., the 4
* arguments separated by any number of spaces, as it appears in the Zone file)
*/
public static DsRecord parse(String dsRecord) {
List<String> elements = SPLITTER.splitToList(dsRecord);
checkArgument(
elements.size() == 4,
"dsRecord %s should have 4 parts, but has %s",
dsRecord,
elements.size());
return DsRecord.create(
Integer.parseUnsignedInt(elements.get(0)),
Integer.parseUnsignedInt(elements.get(1)),
Integer.parseUnsignedInt(elements.get(2)),
elements.get(3));
}
public SoyMapData toSoyData() {
return new SoyMapData(
"keyTag", keyTag(),
"alg", alg(),
"digestType", digestType(),
"digest", digest());
}
public static SoyListData convertToSoy(List<DsRecord> dsRecords) {
return new SoyListData(
dsRecords.stream().map(DsRecord::toSoyData).collect(toImmutableList()));
}
}
public static class DsRecordConverter implements IStringConverter<DsRecord> {
@Override
public DsRecord convert(String dsRecord) {
return DsRecord.parse(dsRecord);
}
}
@Override
protected void initEppToolCommand() throws Exception {
checkArgument(nameservers.size() <= 13, "There can be at most 13 nameservers.");
String duplicates = Joiner.on(", ").join(findDuplicates(mainParameters));
checkArgument(duplicates.isEmpty(), "Duplicate arguments found: '%s'", duplicates);
domains = ImmutableSet.copyOf(mainParameters);
initMutatingEppToolCommand();
}
}