mirror of
https://github.com/google/nomulus
synced 2026-05-20 06:41:51 +00:00
Compare commits
3 Commits
nomulus-20
...
nomulus-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f13fda2c15 | ||
|
|
f72a0d2f16 | ||
|
|
1eef260da9 |
6
console-webapp/package-lock.json
generated
6
console-webapp/package-lock.json
generated
@@ -9678,9 +9678,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ip": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
|
||||
"integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz",
|
||||
"integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ipaddr.js": {
|
||||
|
||||
@@ -21,7 +21,6 @@ import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR;
|
||||
import static org.apache.http.HttpStatus.SC_OK;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -125,7 +124,7 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
|
||||
return Streams.stream(Registrar.loadAllCached())
|
||||
.map(
|
||||
registrar ->
|
||||
RegistrarInfo.create(
|
||||
new RegistrarInfo(
|
||||
registrar,
|
||||
registrar.getClientCertificate().isPresent()
|
||||
&& certificateChecker.shouldReceiveExpiringNotification(
|
||||
@@ -333,19 +332,6 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
|
||||
}
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
public abstract static class RegistrarInfo {
|
||||
|
||||
static RegistrarInfo create(
|
||||
Registrar registrar, boolean isCertExpiring, boolean isFailOverCertExpiring) {
|
||||
return new AutoValue_SendExpiringCertificateNotificationEmailAction_RegistrarInfo(
|
||||
registrar, isCertExpiring, isFailOverCertExpiring);
|
||||
}
|
||||
|
||||
public abstract Registrar registrar();
|
||||
|
||||
public abstract boolean isCertExpiring();
|
||||
|
||||
public abstract boolean isFailOverCertExpiring();
|
||||
}
|
||||
record RegistrarInfo(
|
||||
Registrar registrar, boolean isCertExpiring, boolean isFailOverCertExpiring) {}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ package google.registry.beam.spec11;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import dagger.Component;
|
||||
import dagger.Module;
|
||||
@@ -199,7 +198,7 @@ public class Spec11Pipeline implements Serializable {
|
||||
(KV<DomainNameInfo, ThreatMatch> kv) ->
|
||||
KV.of(
|
||||
kv.getKey().registrarId(),
|
||||
EmailAndThreatMatch.create(
|
||||
new EmailAndThreatMatch(
|
||||
kv.getKey().registrarEmailAddress(), kv.getValue()))))
|
||||
.apply("Group by registrar client ID", GroupByKey.create())
|
||||
.apply(
|
||||
@@ -281,15 +280,5 @@ public class Spec11Pipeline implements Serializable {
|
||||
Spec11Pipeline spec11Pipeline();
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
abstract static class EmailAndThreatMatch implements Serializable {
|
||||
|
||||
abstract String email();
|
||||
|
||||
abstract ThreatMatch threatMatch();
|
||||
|
||||
static EmailAndThreatMatch create(String email, ThreatMatch threatMatch) {
|
||||
return new AutoValue_Spec11Pipeline_EmailAndThreatMatch(email, threatMatch);
|
||||
}
|
||||
}
|
||||
record EmailAndThreatMatch(String email, ThreatMatch threatMatch) implements Serializable {}
|
||||
}
|
||||
|
||||
@@ -18,19 +18,13 @@ import static google.registry.bsa.BsaStringUtils.DOMAIN_JOINER;
|
||||
import static google.registry.bsa.BsaStringUtils.PROPERTY_JOINER;
|
||||
import static google.registry.bsa.BsaStringUtils.PROPERTY_SPLITTER;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A domain name whose second-level domain (SLD) matches a BSA label but is not blocked. It may be
|
||||
* already registered, or on the TLD's reserve list.
|
||||
*/
|
||||
// TODO(1/15/2024): rename to UnblockableDomain.
|
||||
@AutoValue
|
||||
public abstract class UnblockableDomain {
|
||||
public abstract String domainName();
|
||||
|
||||
public abstract Reason reason();
|
||||
public record UnblockableDomain(String domainName, Reason reason) {
|
||||
|
||||
/** Reasons why a valid domain name cannot be blocked. */
|
||||
public enum Reason {
|
||||
@@ -45,14 +39,10 @@ public abstract class UnblockableDomain {
|
||||
|
||||
public static UnblockableDomain deserialize(String text) {
|
||||
List<String> items = PROPERTY_SPLITTER.splitToList(text);
|
||||
return of(items.get(0), Reason.valueOf(items.get(1)));
|
||||
}
|
||||
|
||||
public static UnblockableDomain of(String domainName, Reason reason) {
|
||||
return new AutoValue_UnblockableDomain(domainName, reason);
|
||||
return new UnblockableDomain(items.get(0), Reason.valueOf(items.get(1)));
|
||||
}
|
||||
|
||||
public static UnblockableDomain of(String label, String tld, Reason reason) {
|
||||
return of(DOMAIN_JOINER.join(label, tld), reason);
|
||||
return new UnblockableDomain(DOMAIN_JOINER.join(label, tld), reason);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public abstract class UnblockableDomainChange {
|
||||
@Memoized
|
||||
public UnblockableDomain newValue() {
|
||||
verify(newReason().isPresent(), "Removed unblockable does not have new value.");
|
||||
return UnblockableDomain.of(unblockable().domainName(), newReason().get());
|
||||
return new UnblockableDomain(unblockable().domainName(), newReason().get());
|
||||
}
|
||||
|
||||
public boolean isNewOrChange() {
|
||||
@@ -78,7 +78,7 @@ public abstract class UnblockableDomainChange {
|
||||
public static UnblockableDomainChange deserialize(String text) {
|
||||
List<String> items = BsaStringUtils.PROPERTY_SPLITTER.splitToList(text);
|
||||
return of(
|
||||
UnblockableDomain.of(items.get(0), Reason.valueOf(items.get(1))),
|
||||
new UnblockableDomain(items.get(0), Reason.valueOf(items.get(1))),
|
||||
Objects.equals(items.get(2), DELETE_REASON_PLACEHOLDER)
|
||||
? Optional.empty()
|
||||
: Optional.of(Reason.valueOf(items.get(2))));
|
||||
|
||||
@@ -239,10 +239,10 @@ public final class DomainsRefresher {
|
||||
|
||||
Streams.concat(
|
||||
newCreated.stream()
|
||||
.map(name -> UnblockableDomain.of(name, Reason.REGISTERED))
|
||||
.map(name -> new UnblockableDomain(name, Reason.REGISTERED))
|
||||
.map(UnblockableDomainChange::ofNew),
|
||||
reservedNotCreated.stream()
|
||||
.map(name -> UnblockableDomain.of(name, Reason.RESERVED))
|
||||
.map(name -> new UnblockableDomain(name, Reason.RESERVED))
|
||||
.map(UnblockableDomainChange::ofNew))
|
||||
.forEach(changes::add);
|
||||
return changes.build();
|
||||
|
||||
@@ -152,7 +152,7 @@ public final class LabelDiffUpdates {
|
||||
ImmutableSet<String> registeredDomainNames =
|
||||
ImmutableSet.copyOf(ForeignKeyUtils.load(Domain.class, validDomainNames, now).keySet());
|
||||
for (String domain : registeredDomainNames) {
|
||||
nonBlockedDomains.add(UnblockableDomain.of(domain, Reason.REGISTERED));
|
||||
nonBlockedDomains.add(new UnblockableDomain(domain, Reason.REGISTERED));
|
||||
tm().put(BsaUnblockableDomain.of(domain, BsaUnblockableDomain.Reason.REGISTERED));
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ public final class LabelDiffUpdates {
|
||||
.filter(domain -> isReservedDomain(domain, now))
|
||||
.collect(toImmutableSet());
|
||||
for (String domain : reservedDomainNames) {
|
||||
nonBlockedDomains.add(UnblockableDomain.of(domain, Reason.RESERVED));
|
||||
nonBlockedDomains.add(new UnblockableDomain(domain, Reason.RESERVED));
|
||||
tm().put(BsaUnblockableDomain.of(domain, BsaUnblockableDomain.Reason.RESERVED));
|
||||
}
|
||||
return nonBlockedDomains.build();
|
||||
|
||||
@@ -14,25 +14,21 @@
|
||||
|
||||
package google.registry.flows;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import com.google.auto.value.AutoBuilder;
|
||||
|
||||
/** Object to hold metadata specific to a particular execution of a flow. */
|
||||
@AutoValue
|
||||
public abstract class FlowMetadata extends ImmutableObject {
|
||||
|
||||
public abstract boolean isSuperuser();
|
||||
public record FlowMetadata(boolean isSuperuser) {
|
||||
|
||||
public static Builder newBuilder() {
|
||||
return new AutoValue_FlowMetadata.Builder();
|
||||
return new AutoBuilder_FlowMetadata_Builder();
|
||||
}
|
||||
|
||||
/** Builder for {@link FlowMetadata} */
|
||||
@AutoValue.Builder
|
||||
public abstract static class Builder {
|
||||
@AutoBuilder
|
||||
public interface Builder {
|
||||
|
||||
public abstract Builder setSuperuser(boolean isSuperuser);
|
||||
Builder setIsSuperuser(boolean isSuperuser);
|
||||
|
||||
public abstract FlowMetadata build();
|
||||
FlowMetadata build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,7 +314,7 @@ public class FlowModule {
|
||||
|
||||
@Provides
|
||||
static FlowMetadata provideFlowMetadata(@Superuser boolean isSuperuser) {
|
||||
return FlowMetadata.newBuilder().setSuperuser(isSuperuser).build();
|
||||
return FlowMetadata.newBuilder().setIsSuperuser(isSuperuser).build();
|
||||
}
|
||||
|
||||
/** Wrapper class to carry an {@link EppException} to the calling code. */
|
||||
|
||||
@@ -47,7 +47,6 @@ import google.registry.model.eppinput.EppInput.Options;
|
||||
import google.registry.model.eppinput.EppInput.Services;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.util.PasswordUtils.HashAlgorithm;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.inject.Inject;
|
||||
@@ -142,17 +141,8 @@ public class LoginFlow implements MutatingFlow {
|
||||
throw new RegistrarAccountNotActiveException();
|
||||
}
|
||||
|
||||
if (login.getNewPassword().isPresent()
|
||||
|| registrar.get().getCurrentHashAlgorithm(login.getPassword()).orElse(null)
|
||||
!= HashAlgorithm.SCRYPT) {
|
||||
String newPassword =
|
||||
login
|
||||
.getNewPassword()
|
||||
.orElseGet(
|
||||
() -> {
|
||||
logger.atInfo().log("Rehashing existing registrar password with Scrypt");
|
||||
return login.getPassword();
|
||||
});
|
||||
if (login.getNewPassword().isPresent()) {
|
||||
String newPassword = login.getNewPassword().get();
|
||||
// Load fresh from database (bypassing the cache) to ensure we don't save stale data.
|
||||
Optional<Registrar> freshRegistrar = Registrar.loadByRegistrarId(login.getClientId());
|
||||
if (freshRegistrar.isEmpty()) {
|
||||
|
||||
@@ -86,8 +86,7 @@ public class User extends UpdateAutoTimestampEntity implements Buildable {
|
||||
return false;
|
||||
}
|
||||
return PasswordUtils.verifyPassword(
|
||||
registryLockPassword, registryLockPasswordHash, registryLockPasswordSalt)
|
||||
.isPresent();
|
||||
registryLockPassword, registryLockPasswordHash, registryLockPasswordSalt);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -62,7 +62,6 @@ import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import google.registry.util.PasswordUtils;
|
||||
import google.registry.util.PasswordUtils.HashAlgorithm;
|
||||
import java.security.cert.CertificateParsingException;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
@@ -642,10 +641,6 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
}
|
||||
|
||||
public boolean verifyPassword(String password) {
|
||||
return getCurrentHashAlgorithm(password).isPresent();
|
||||
}
|
||||
|
||||
public Optional<HashAlgorithm> getCurrentHashAlgorithm(String password) {
|
||||
return PasswordUtils.verifyPassword(password, passwordHash, salt);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ import google.registry.model.UnsafeSerializable;
|
||||
import google.registry.model.registrar.RegistrarPoc.RegistrarPocId;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.util.PasswordUtils;
|
||||
import google.registry.util.PasswordUtils.HashAlgorithm;
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -242,10 +241,6 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
|
||||
|| isNullOrEmpty(registryLockPasswordHash)) {
|
||||
return false;
|
||||
}
|
||||
return getCurrentHashAlgorithm(registryLockPassword).isPresent();
|
||||
}
|
||||
|
||||
public Optional<HashAlgorithm> getCurrentHashAlgorithm(String registryLockPassword) {
|
||||
return PasswordUtils.verifyPassword(
|
||||
registryLockPassword, registryLockPasswordHash, registryLockPasswordSalt);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@ import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.security.JsonResponseHelper;
|
||||
import google.registry.tools.DomainLockUtils;
|
||||
import google.registry.util.EmailMessage;
|
||||
import google.registry.util.PasswordUtils.HashAlgorithm;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -223,19 +222,6 @@ public class RegistryLockPostAction implements Runnable, JsonActionRunner.JsonAc
|
||||
checkArgument(
|
||||
registrarPoc.verifyRegistryLockPassword(postInput.password),
|
||||
"Incorrect registry lock password for contact");
|
||||
if (registrarPoc.getCurrentHashAlgorithm(postInput.password).orElse(null)
|
||||
!= HashAlgorithm.SCRYPT) {
|
||||
logger.atInfo().log("Rehashing existing registry lock password with Scrypt.");
|
||||
tm().transact(
|
||||
() -> {
|
||||
tm().update(
|
||||
tm().loadByEntity(registrarPoc)
|
||||
.asBuilder()
|
||||
.setAllowedToSetRegistryLockPassword(true)
|
||||
.setRegistryLockPassword(postInput.password)
|
||||
.build());
|
||||
});
|
||||
}
|
||||
return registrarPoc
|
||||
.getRegistryLockEmailAddress()
|
||||
.orElseThrow(
|
||||
|
||||
@@ -139,7 +139,7 @@ class BsaRefreshFunctionalTest {
|
||||
RESERVED_LIST_NAME, ImmutableMap.of("blocked1", RESERVED_FOR_SPECIFIC_USE));
|
||||
String jobName = getRefreshJobName(fakeClock.nowUtc());
|
||||
action.run();
|
||||
UnblockableDomain newUnblockable = UnblockableDomain.of("blocked1.app", Reason.RESERVED);
|
||||
UnblockableDomain newUnblockable = new UnblockableDomain("blocked1.app", Reason.RESERVED);
|
||||
assertThat(queryUnblockableDomains()).containsExactly(newUnblockable);
|
||||
assertThat(gcsClient.readRefreshChanges(jobName))
|
||||
.containsExactly(UnblockableDomainChange.ofNew(newUnblockable));
|
||||
@@ -154,7 +154,7 @@ class BsaRefreshFunctionalTest {
|
||||
persistActiveDomain("dummy.dev", fakeClock.nowUtc());
|
||||
String jobName = getRefreshJobName(fakeClock.nowUtc());
|
||||
action.run();
|
||||
UnblockableDomain newUnblockable = UnblockableDomain.of("blocked1.dev", Reason.REGISTERED);
|
||||
UnblockableDomain newUnblockable = new UnblockableDomain("blocked1.dev", Reason.REGISTERED);
|
||||
assertThat(queryUnblockableDomains()).containsExactly(newUnblockable);
|
||||
assertThat(gcsClient.readRefreshChanges(jobName))
|
||||
.containsExactly(UnblockableDomainChange.ofNew(newUnblockable));
|
||||
@@ -169,7 +169,7 @@ class BsaRefreshFunctionalTest {
|
||||
Domain domain = persistActiveDomain("blocked1.dev", fakeClock.nowUtc());
|
||||
action.run();
|
||||
assertThat(queryUnblockableDomains())
|
||||
.containsExactly(UnblockableDomain.of("blocked1.dev", Reason.REGISTERED));
|
||||
.containsExactly(new UnblockableDomain("blocked1.dev", Reason.REGISTERED));
|
||||
fakeClock.advanceOneMilli();
|
||||
deleteTestDomain(domain, fakeClock.nowUtc());
|
||||
fakeClock.advanceOneMilli();
|
||||
@@ -181,7 +181,7 @@ class BsaRefreshFunctionalTest {
|
||||
assertThat(gcsClient.readRefreshChanges(jobName))
|
||||
.containsExactly(
|
||||
UnblockableDomainChange.ofDeleted(
|
||||
UnblockableDomain.of("blocked1.dev", Reason.REGISTERED)));
|
||||
new UnblockableDomain("blocked1.dev", Reason.REGISTERED)));
|
||||
|
||||
verify(bsaReportSender, never()).addUnblockableDomainsUpdates(anyString());
|
||||
verify(bsaReportSender, times(1)).removeUnblockableDomainsUpdates("[\n \"blocked1.dev\"\n]");
|
||||
@@ -193,7 +193,7 @@ class BsaRefreshFunctionalTest {
|
||||
RESERVED_LIST_NAME, ImmutableMap.of("blocked1", RESERVED_FOR_SPECIFIC_USE));
|
||||
action.run();
|
||||
assertThat(queryUnblockableDomains())
|
||||
.containsExactly(UnblockableDomain.of("blocked1.app", Reason.RESERVED));
|
||||
.containsExactly(new UnblockableDomain("blocked1.app", Reason.RESERVED));
|
||||
fakeClock.advanceOneMilli();
|
||||
removeReservedDomainFromList(RESERVED_LIST_NAME, ImmutableSet.of("blocked1"));
|
||||
|
||||
@@ -204,7 +204,7 @@ class BsaRefreshFunctionalTest {
|
||||
assertThat(gcsClient.readRefreshChanges(jobName))
|
||||
.containsExactly(
|
||||
UnblockableDomainChange.ofDeleted(
|
||||
UnblockableDomain.of("blocked1.app", Reason.RESERVED)));
|
||||
new UnblockableDomain("blocked1.app", Reason.RESERVED)));
|
||||
|
||||
verify(bsaReportSender, never()).addUnblockableDomainsUpdates(anyString());
|
||||
verify(bsaReportSender, times(1)).removeUnblockableDomainsUpdates("[\n \"blocked1.app\"\n]");
|
||||
@@ -217,7 +217,7 @@ class BsaRefreshFunctionalTest {
|
||||
Domain domain = persistActiveDomain("blocked1.app", fakeClock.nowUtc());
|
||||
action.run();
|
||||
assertThat(queryUnblockableDomains())
|
||||
.containsExactly(UnblockableDomain.of("blocked1.app", Reason.REGISTERED));
|
||||
.containsExactly(new UnblockableDomain("blocked1.app", Reason.REGISTERED));
|
||||
fakeClock.advanceOneMilli();
|
||||
deleteTestDomain(domain, fakeClock.nowUtc());
|
||||
fakeClock.advanceOneMilli();
|
||||
@@ -226,11 +226,11 @@ class BsaRefreshFunctionalTest {
|
||||
Mockito.reset(bsaReportSender);
|
||||
action.run();
|
||||
assertThat(queryUnblockableDomains())
|
||||
.containsExactly(UnblockableDomain.of("blocked1.app", Reason.RESERVED));
|
||||
.containsExactly(new UnblockableDomain("blocked1.app", Reason.RESERVED));
|
||||
assertThat(gcsClient.readRefreshChanges(jobName))
|
||||
.containsExactly(
|
||||
UnblockableDomainChange.ofChanged(
|
||||
UnblockableDomain.of("blocked1.app", Reason.REGISTERED), Reason.RESERVED));
|
||||
new UnblockableDomain("blocked1.app", Reason.REGISTERED), Reason.RESERVED));
|
||||
InOrder inOrder = Mockito.inOrder(bsaReportSender);
|
||||
inOrder.verify(bsaReportSender).removeUnblockableDomainsUpdates("[\n \"blocked1.app\"\n]");
|
||||
inOrder
|
||||
@@ -244,7 +244,7 @@ class BsaRefreshFunctionalTest {
|
||||
RESERVED_LIST_NAME, ImmutableMap.of("blocked1", RESERVED_FOR_SPECIFIC_USE));
|
||||
action.run();
|
||||
assertThat(queryUnblockableDomains())
|
||||
.containsExactly(UnblockableDomain.of("blocked1.app", Reason.RESERVED));
|
||||
.containsExactly(new UnblockableDomain("blocked1.app", Reason.RESERVED));
|
||||
fakeClock.advanceOneMilli();
|
||||
persistActiveDomain("blocked1.app", fakeClock.nowUtc());
|
||||
fakeClock.advanceOneMilli();
|
||||
@@ -252,12 +252,12 @@ class BsaRefreshFunctionalTest {
|
||||
Mockito.reset(bsaReportSender);
|
||||
String jobName = getRefreshJobName(fakeClock.nowUtc());
|
||||
action.run();
|
||||
UnblockableDomain changed = UnblockableDomain.of("blocked1.app", Reason.REGISTERED);
|
||||
UnblockableDomain changed = new UnblockableDomain("blocked1.app", Reason.REGISTERED);
|
||||
assertThat(queryUnblockableDomains()).containsExactly(changed);
|
||||
assertThat(gcsClient.readRefreshChanges(jobName))
|
||||
.containsExactly(
|
||||
UnblockableDomainChange.ofChanged(
|
||||
UnblockableDomain.of("blocked1.app", Reason.RESERVED), Reason.REGISTERED));
|
||||
new UnblockableDomain("blocked1.app", Reason.RESERVED), Reason.REGISTERED));
|
||||
InOrder inOrder = Mockito.inOrder(bsaReportSender);
|
||||
inOrder.verify(bsaReportSender).removeUnblockableDomainsUpdates("[\n \"blocked1.app\"\n]");
|
||||
inOrder
|
||||
@@ -272,7 +272,7 @@ class BsaRefreshFunctionalTest {
|
||||
persistActiveDomain("blocked1.app", fakeClock.nowUtc());
|
||||
String jobName = getRefreshJobName(fakeClock.nowUtc());
|
||||
action.run();
|
||||
UnblockableDomain newUnblockable = UnblockableDomain.of("blocked1.app", Reason.REGISTERED);
|
||||
UnblockableDomain newUnblockable = new UnblockableDomain("blocked1.app", Reason.REGISTERED);
|
||||
assertThat(queryUnblockableDomains()).containsExactly(newUnblockable);
|
||||
assertThat(gcsClient.readRefreshChanges(jobName))
|
||||
.containsExactly(UnblockableDomainChange.ofNew(newUnblockable));
|
||||
@@ -285,7 +285,7 @@ class BsaRefreshFunctionalTest {
|
||||
persistActiveDomain("blocked1.app", fakeClock.nowUtc());
|
||||
action.run();
|
||||
assertThat(queryUnblockableDomains())
|
||||
.containsExactly(UnblockableDomain.of("blocked1.app", Reason.REGISTERED));
|
||||
.containsExactly(new UnblockableDomain("blocked1.app", Reason.REGISTERED));
|
||||
fakeClock.advanceOneMilli();
|
||||
removeReservedDomainFromList(RESERVED_LIST_NAME, ImmutableSet.of("blocked1"));
|
||||
fakeClock.advanceOneMilli();
|
||||
@@ -294,7 +294,7 @@ class BsaRefreshFunctionalTest {
|
||||
String jobName = getRefreshJobName(fakeClock.nowUtc());
|
||||
action.run();
|
||||
assertThat(queryUnblockableDomains())
|
||||
.containsExactly(UnblockableDomain.of("blocked1.app", Reason.REGISTERED));
|
||||
.containsExactly(new UnblockableDomain("blocked1.app", Reason.REGISTERED));
|
||||
// Verify that refresh change file does not exist (404 error) since there is no change.
|
||||
assertThat(
|
||||
assertThrows(
|
||||
@@ -309,7 +309,7 @@ class BsaRefreshFunctionalTest {
|
||||
persistActiveDomain("blocked1.app", fakeClock.nowUtc());
|
||||
action.run();
|
||||
assertThat(queryUnblockableDomains())
|
||||
.containsExactly(UnblockableDomain.of("blocked1.app", Reason.REGISTERED));
|
||||
.containsExactly(new UnblockableDomain("blocked1.app", Reason.REGISTERED));
|
||||
fakeClock.advanceOneMilli();
|
||||
addReservedDomainToList(
|
||||
RESERVED_LIST_NAME, ImmutableMap.of("blocked1", RESERVED_FOR_SPECIFIC_USE));
|
||||
@@ -319,7 +319,7 @@ class BsaRefreshFunctionalTest {
|
||||
String jobName = getRefreshJobName(fakeClock.nowUtc());
|
||||
action.run();
|
||||
assertThat(queryUnblockableDomains())
|
||||
.containsExactly(UnblockableDomain.of("blocked1.app", Reason.REGISTERED));
|
||||
.containsExactly(new UnblockableDomain("blocked1.app", Reason.REGISTERED));
|
||||
// Verify that refresh change file does not exist (404 error) since there is no change.
|
||||
assertThat(
|
||||
assertThrows(
|
||||
|
||||
@@ -134,10 +134,10 @@ class JsonSerializationsTest {
|
||||
assertThat(
|
||||
toUnblockableDomainsReport(
|
||||
Stream.of(
|
||||
UnblockableDomain.of("a.ing", Reason.REGISTERED),
|
||||
UnblockableDomain.of("b.app", Reason.INVALID),
|
||||
UnblockableDomain.of("c.dev", Reason.RESERVED),
|
||||
UnblockableDomain.of("d.page", Reason.REGISTERED))))
|
||||
new UnblockableDomain("a.ing", Reason.REGISTERED),
|
||||
new UnblockableDomain("b.app", Reason.INVALID),
|
||||
new UnblockableDomain("c.dev", Reason.RESERVED),
|
||||
new UnblockableDomain("d.page", Reason.REGISTERED))))
|
||||
.hasValue(expected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class UnblockableDomainTest {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
unit = UnblockableDomain.of("buy.app", Reason.REGISTERED);
|
||||
unit = new UnblockableDomain("buy.app", Reason.REGISTERED);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -69,7 +69,7 @@ public class DomainsRefresherTest {
|
||||
assertThat(refresher.refreshStaleUnblockables())
|
||||
.containsExactly(
|
||||
UnblockableDomainChange.ofDeleted(
|
||||
UnblockableDomain.of("label.tld", UnblockableDomain.Reason.REGISTERED)));
|
||||
new UnblockableDomain("label.tld", UnblockableDomain.Reason.REGISTERED)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -79,7 +79,7 @@ public class DomainsRefresherTest {
|
||||
assertThat(refresher.refreshStaleUnblockables())
|
||||
.containsExactly(
|
||||
UnblockableDomainChange.ofDeleted(
|
||||
UnblockableDomain.of("label.tld", UnblockableDomain.Reason.RESERVED)));
|
||||
new UnblockableDomain("label.tld", UnblockableDomain.Reason.RESERVED)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -89,7 +89,7 @@ public class DomainsRefresherTest {
|
||||
assertThat(refresher.getNewUnblockables())
|
||||
.containsExactly(
|
||||
UnblockableDomainChange.ofNew(
|
||||
UnblockableDomain.of("label.tld", UnblockableDomain.Reason.REGISTERED)));
|
||||
new UnblockableDomain("label.tld", UnblockableDomain.Reason.REGISTERED)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -100,7 +100,7 @@ public class DomainsRefresherTest {
|
||||
assertThat(refresher.getNewUnblockables())
|
||||
.containsExactly(
|
||||
UnblockableDomainChange.ofNew(
|
||||
UnblockableDomain.of("label.tld", UnblockableDomain.Reason.RESERVED)));
|
||||
new UnblockableDomain("label.tld", UnblockableDomain.Reason.RESERVED)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -113,7 +113,7 @@ public class DomainsRefresherTest {
|
||||
assertThat(refresher.refreshStaleUnblockables())
|
||||
.containsExactly(
|
||||
UnblockableDomainChange.ofChanged(
|
||||
UnblockableDomain.of("label.tld", UnblockableDomain.Reason.REGISTERED),
|
||||
new UnblockableDomain("label.tld", UnblockableDomain.Reason.REGISTERED),
|
||||
UnblockableDomain.Reason.RESERVED));
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ public class DomainsRefresherTest {
|
||||
assertThat(refresher.refreshStaleUnblockables())
|
||||
.containsExactly(
|
||||
UnblockableDomainChange.ofChanged(
|
||||
UnblockableDomain.of("label.tld", UnblockableDomain.Reason.RESERVED),
|
||||
new UnblockableDomain("label.tld", UnblockableDomain.Reason.RESERVED),
|
||||
UnblockableDomain.Reason.REGISTERED));
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ public class DomainsRefresherTest {
|
||||
assertThat(refresher.refreshStaleUnblockables())
|
||||
.containsExactly(
|
||||
UnblockableDomainChange.ofChanged(
|
||||
UnblockableDomain.of("label.tld", UnblockableDomain.Reason.RESERVED),
|
||||
new UnblockableDomain("label.tld", UnblockableDomain.Reason.RESERVED),
|
||||
UnblockableDomain.Reason.REGISTERED));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,8 +126,8 @@ class LabelDiffUpdatesTest {
|
||||
fakeClock.nowUtc());
|
||||
assertThat(unblockableDomains)
|
||||
.containsExactly(
|
||||
UnblockableDomain.of("label.app", UnblockableDomain.Reason.REGISTERED),
|
||||
UnblockableDomain.of("label.dev", UnblockableDomain.Reason.INVALID));
|
||||
new UnblockableDomain("label.app", UnblockableDomain.Reason.REGISTERED),
|
||||
new UnblockableDomain("label.dev", UnblockableDomain.Reason.INVALID));
|
||||
assertThat(tm().transact(() -> tm().loadByKeyIfPresent(BsaLabel.vKey("label")))).isPresent();
|
||||
assertThat(
|
||||
tm().transact(() -> tm().loadByKeyIfPresent(BsaUnblockableDomain.vKey("label", "app"))))
|
||||
@@ -153,9 +153,9 @@ class LabelDiffUpdatesTest {
|
||||
fakeClock.nowUtc());
|
||||
assertThat(unblockableDomains)
|
||||
.containsExactly(
|
||||
UnblockableDomain.of("label.app", UnblockableDomain.Reason.REGISTERED),
|
||||
UnblockableDomain.of("label.page", UnblockableDomain.Reason.RESERVED),
|
||||
UnblockableDomain.of("label.dev", UnblockableDomain.Reason.INVALID));
|
||||
new UnblockableDomain("label.app", UnblockableDomain.Reason.REGISTERED),
|
||||
new UnblockableDomain("label.page", UnblockableDomain.Reason.RESERVED),
|
||||
new UnblockableDomain("label.dev", UnblockableDomain.Reason.INVALID));
|
||||
assertThat(tm().transact(() -> tm().loadByKeyIfPresent(BsaLabel.vKey("label")))).isPresent();
|
||||
assertThat(
|
||||
tm().transact(() -> tm().loadByKey(BsaUnblockableDomain.vKey("label", "app")).reason))
|
||||
|
||||
@@ -14,15 +14,11 @@
|
||||
|
||||
package google.registry.flows.session;
|
||||
|
||||
import static com.google.common.io.BaseEncoding.base64;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.deleteResource;
|
||||
import static google.registry.testing.DatabaseHelper.loadRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
|
||||
import static google.registry.util.PasswordUtils.HashAlgorithm.SCRYPT;
|
||||
import static google.registry.util.PasswordUtils.HashAlgorithm.SHA256;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
@@ -42,7 +38,6 @@ import google.registry.model.eppoutput.EppOutput;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.Registrar.State;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.util.PasswordUtils;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -191,33 +186,6 @@ public abstract class LoginFlowTestCase extends FlowTestCase<LoginFlow> {
|
||||
doFailingTest("login_valid.xml", RegistrarAccountNotActiveException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_sha256Password() throws Exception {
|
||||
String password = "foo-BAR2";
|
||||
tm().transact(
|
||||
() -> {
|
||||
// The salt is not exposed by Registrar (nor should it be), so we query it
|
||||
// directly.
|
||||
String encodedSalt =
|
||||
tm().query("SELECT salt FROM Registrar WHERE registrarId = :id", String.class)
|
||||
.setParameter("id", registrar.getRegistrarId())
|
||||
.getSingleResult();
|
||||
byte[] salt = base64().decode(encodedSalt);
|
||||
String newHash = PasswordUtils.hashPassword(password, salt, SHA256);
|
||||
// Set password directly, as the Java method would have used Scrypt.
|
||||
tm().query("UPDATE Registrar SET passwordHash = :hash WHERE registrarId = :id")
|
||||
.setParameter("id", registrar.getRegistrarId())
|
||||
.setParameter("hash", newHash)
|
||||
.executeUpdate();
|
||||
});
|
||||
assertThat(loadRegistrar("NewRegistrar").getCurrentHashAlgorithm(password).get())
|
||||
.isEqualTo(SHA256);
|
||||
doSuccessfulTest("login_valid.xml");
|
||||
// Verifies that after successfully login, the password is re-hased with Scrypt.
|
||||
assertThat(loadRegistrar("NewRegistrar").getCurrentHashAlgorithm(password).get())
|
||||
.isEqualTo(SCRYPT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_incorrectPassword() {
|
||||
persistResource(getRegistrarBuilder().setPassword("diff password").build());
|
||||
|
||||
@@ -15,10 +15,8 @@
|
||||
package google.registry.ui.server.registrar;
|
||||
|
||||
import static com.google.common.collect.ImmutableSetMultimap.toImmutableSetMultimap;
|
||||
import static com.google.common.io.BaseEncoding.base64;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.loadRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
@@ -27,8 +25,6 @@ import static google.registry.testing.SqlHelper.getRegistryLockByVerificationCod
|
||||
import static google.registry.testing.SqlHelper.saveRegistryLock;
|
||||
import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STATUSES;
|
||||
import static google.registry.ui.server.registrar.RegistryLockGetActionTest.userFromRegistrarPoc;
|
||||
import static google.registry.util.PasswordUtils.HashAlgorithm.SCRYPT;
|
||||
import static google.registry.util.PasswordUtils.HashAlgorithm.SHA256;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -42,9 +38,6 @@ import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.RegistryLock;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.model.registrar.RegistrarPoc.RegistrarPocId;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.persistence.transaction.JpaTransactionManagerExtension;
|
||||
@@ -61,7 +54,6 @@ import google.registry.testing.DeterministicStringGenerator;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.tools.DomainLockUtils;
|
||||
import google.registry.util.EmailMessage;
|
||||
import google.registry.util.PasswordUtils;
|
||||
import google.registry.util.StringGenerator.Alphabets;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -131,48 +123,6 @@ final class RegistryLockPostActionTest {
|
||||
assertSuccess(response, "lock", "Marla.Singer.RegistryLock@crr.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_lock_sha256Password() throws Exception {
|
||||
tm().transact(
|
||||
() -> {
|
||||
// The salt is not exposed by RegistrarPoc (nor should it be), so we query
|
||||
// it directly.
|
||||
String encodedSalt =
|
||||
tm().query(
|
||||
"SELECT registryLockPasswordSalt FROM RegistrarPoc "
|
||||
+ "WHERE emailAddress = :email "
|
||||
+ "AND registrarId = :registrarId",
|
||||
String.class)
|
||||
.setParameter("email", "Marla.Singer@crr.com")
|
||||
.setParameter("registrarId", "TheRegistrar")
|
||||
.getSingleResult();
|
||||
byte[] salt = base64().decode(encodedSalt);
|
||||
String newHash = PasswordUtils.hashPassword("hi", salt, SHA256);
|
||||
// Set password directly, as the Java method would have used Scrypt.
|
||||
tm().query("UPDATE RegistrarPoc SET registryLockPasswordHash = :hash")
|
||||
.setParameter("hash", newHash)
|
||||
.executeUpdate();
|
||||
});
|
||||
RegistrarPoc registrarPoc =
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().loadByKey(
|
||||
VKey.create(
|
||||
RegistrarPoc.class,
|
||||
new RegistrarPocId("Marla.Singer@crr.com", "TheRegistrar"))));
|
||||
assertThat(registrarPoc.getCurrentHashAlgorithm("hi").get()).isEqualTo(SHA256);
|
||||
Map<String, ?> response = action.handleJsonRequest(lockRequest());
|
||||
RegistrarPoc updatedRegistrarPoc =
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().loadByKey(
|
||||
VKey.create(
|
||||
RegistrarPoc.class,
|
||||
new RegistrarPocId("Marla.Singer@crr.com", "TheRegistrar"))));
|
||||
assertThat(updatedRegistrarPoc.getCurrentHashAlgorithm("hi").get()).isEqualTo(SCRYPT);
|
||||
assertSuccess(response, "lock", "Marla.Singer.RegistryLock@crr.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_unlock() throws Exception {
|
||||
saveRegistryLock(createLock().asBuilder().setLockCompletionTime(clock.nowUtc()).build());
|
||||
|
||||
@@ -15,82 +15,29 @@
|
||||
package google.registry.util;
|
||||
|
||||
import static com.google.common.io.BaseEncoding.base64;
|
||||
import static google.registry.util.PasswordUtils.HashAlgorithm.SCRYPT;
|
||||
import static google.registry.util.PasswordUtils.HashAlgorithm.SHA256;
|
||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.primitives.Bytes;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import org.bouncycastle.crypto.generators.SCrypt;
|
||||
|
||||
/** Common utility class to handle password hashing and salting */
|
||||
/**
|
||||
* Common utility class to handle password hashing and salting /*
|
||||
*
|
||||
* <p>We use a memory-hard hashing algorithm (Scrypt) to prevent brute-force attacks on passwords.
|
||||
*
|
||||
* <p>Note that in tests, we simply concatenate the password and salt which is much faster and
|
||||
* reduces the overall test run time by a half. Our tests are not verifying that SCRYPT is
|
||||
* implemented correctly anyway.
|
||||
*
|
||||
* @see <a href="https://en.wikipedia.org/wiki/Scrypt">Scrypt</a>
|
||||
*/
|
||||
public final class PasswordUtils {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
private static final Supplier<MessageDigest> SHA256_DIGEST_SUPPLIER =
|
||||
Suppliers.memoize(
|
||||
() -> {
|
||||
try {
|
||||
return MessageDigest.getInstance("SHA-256");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// All implementations of MessageDigest are required to support SHA-256.
|
||||
throw new RuntimeException(
|
||||
"All MessageDigest implementations are required to support SHA-256 but this one"
|
||||
+ " didn't",
|
||||
e);
|
||||
}
|
||||
});
|
||||
|
||||
private PasswordUtils() {}
|
||||
|
||||
/**
|
||||
* Password hashing algorithm that takes a password and a salt (both as {@code byte[]}) and
|
||||
* returns a hash.
|
||||
*/
|
||||
public enum HashAlgorithm {
|
||||
/**
|
||||
* SHA-2 that returns a 256-bit digest.
|
||||
*
|
||||
* @see <a href="https://en.wikipedia.org/wiki/SHA-2">SHA-2</a>
|
||||
*/
|
||||
@Deprecated
|
||||
SHA256 {
|
||||
@Override
|
||||
byte[] hash(byte[] password, byte[] salt) {
|
||||
return SHA256_DIGEST_SUPPLIER
|
||||
.get()
|
||||
.digest((new String(password, US_ASCII) + base64().encode(salt)).getBytes(US_ASCII));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Memory-hard hashing algorithm, preferred over SHA-256.
|
||||
*
|
||||
* <p>Note that in tests, we simply concatenate the password and salt which is much faster and
|
||||
* reduces the overall test run time by a half. Our tests are not verifying that SCRYPT is
|
||||
* implemented correctly anyway.
|
||||
*
|
||||
* @see <a href="https://en.wikipedia.org/wiki/Scrypt">Scrypt</a>
|
||||
*/
|
||||
SCRYPT {
|
||||
@Override
|
||||
byte[] hash(byte[] password, byte[] salt) {
|
||||
return RegistryEnvironment.get() == RegistryEnvironment.UNITTEST
|
||||
? Bytes.concat(password, salt)
|
||||
: SCrypt.generate(password, salt, 32768, 8, 1, 256);
|
||||
}
|
||||
};
|
||||
|
||||
abstract byte[] hash(byte[] password, byte[] salt);
|
||||
}
|
||||
|
||||
public static final Supplier<byte[]> SALT_SUPPLIER =
|
||||
() -> {
|
||||
// The generated hashes are 256 bits, and the salt should generally be of the same size.
|
||||
@@ -99,38 +46,25 @@ public final class PasswordUtils {
|
||||
return salt;
|
||||
};
|
||||
|
||||
public static String hashPassword(String password, byte[] salt) {
|
||||
return hashPassword(password, salt, SCRYPT);
|
||||
private static byte[] hashPassword(byte[] password, byte[] salt) {
|
||||
return RegistryEnvironment.get() == RegistryEnvironment.UNITTEST
|
||||
? Bytes.concat(password, salt)
|
||||
: SCrypt.generate(password, salt, 32768, 8, 1, 256);
|
||||
}
|
||||
|
||||
/** Returns the hash of the password using the provided salt and {@link HashAlgorithm}. */
|
||||
public static String hashPassword(String password, byte[] salt, HashAlgorithm algorithm) {
|
||||
return base64().encode(algorithm.hash(password.getBytes(US_ASCII), salt));
|
||||
/** Returns the hash of the password using the provided salt. */
|
||||
public static String hashPassword(String password, byte[] salt) {
|
||||
return base64().encode(hashPassword(password.getBytes(US_ASCII), salt));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies a password by regenerating the hash with the provided salt and comparing it to the
|
||||
* provided hash.
|
||||
*
|
||||
* <p>This method will first try to use {@link HashAlgorithm#SCRYPT} to verify the password, and
|
||||
* falls back to {@link HashAlgorithm#SHA256} if the former fails.
|
||||
*
|
||||
* @return the {@link HashAlgorithm} used to successfully verify the password, or {@link
|
||||
* Optional#empty()} if neither works.
|
||||
*/
|
||||
public static Optional<HashAlgorithm> verifyPassword(String password, String hash, String salt) {
|
||||
public static boolean verifyPassword(String password, String hash, String salt) {
|
||||
byte[] decodedHash = base64().decode(hash);
|
||||
byte[] decodedSalt = base64().decode(salt);
|
||||
byte[] calculatedHash = SCRYPT.hash(password.getBytes(US_ASCII), decodedSalt);
|
||||
if (Arrays.equals(decodedHash, calculatedHash)) {
|
||||
logger.atInfo().log("Scrypt hash verified.");
|
||||
return Optional.of(SCRYPT);
|
||||
}
|
||||
calculatedHash = SHA256.hash(password.getBytes(US_ASCII), decodedSalt);
|
||||
if (Arrays.equals(decodedHash, calculatedHash)) {
|
||||
logger.atInfo().log("SHA256 hash verified.");
|
||||
return Optional.of(SHA256);
|
||||
}
|
||||
return Optional.empty();
|
||||
byte[] calculatedHash = hashPassword(password.getBytes(US_ASCII), decodedSalt);
|
||||
return Arrays.equals(decodedHash, calculatedHash);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@ package google.registry.util;
|
||||
|
||||
import static com.google.common.io.BaseEncoding.base64;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.util.PasswordUtils.HashAlgorithm.SCRYPT;
|
||||
import static google.registry.util.PasswordUtils.HashAlgorithm.SHA256;
|
||||
import static google.registry.util.PasswordUtils.SALT_SUPPLIER;
|
||||
import static google.registry.util.PasswordUtils.hashPassword;
|
||||
import static google.registry.util.PasswordUtils.verifyPassword;
|
||||
@@ -53,18 +51,8 @@ final class PasswordUtilsTest {
|
||||
byte[] salt = SALT_SUPPLIER.get();
|
||||
String password = "mySuperSecurePassword";
|
||||
String hashedPassword = hashPassword(password, salt);
|
||||
assertThat(hashedPassword).isEqualTo(hashPassword(password, salt, SCRYPT));
|
||||
assertThat(verifyPassword(password, hashedPassword, base64().encode(salt)).get())
|
||||
.isEqualTo(SCRYPT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVerify_sha256() {
|
||||
byte[] salt = SALT_SUPPLIER.get();
|
||||
String password = "mySuperSecurePassword";
|
||||
String hashedPassword = hashPassword(password, salt, SHA256);
|
||||
assertThat(verifyPassword(password, hashedPassword, base64().encode(salt)).get())
|
||||
.isEqualTo(SHA256);
|
||||
assertThat(hashedPassword).isEqualTo(hashPassword(password, salt));
|
||||
assertThat(verifyPassword(password, hashedPassword, base64().encode(salt))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -72,7 +60,6 @@ final class PasswordUtilsTest {
|
||||
byte[] salt = SALT_SUPPLIER.get();
|
||||
String password = "mySuperSecurePassword";
|
||||
String hashedPassword = hashPassword(password, salt);
|
||||
assertThat(verifyPassword(password + "a", hashedPassword, base64().encode(salt)).isPresent())
|
||||
.isFalse();
|
||||
assertThat(verifyPassword(password + "a", hashedPassword, base64().encode(salt))).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user