2
0
mirror of https://github.com/VinylDNS/vinyldns synced 2025-08-22 02:02:14 +00:00

Upgrading scalafmt (#904)

* Upgrading scalafmt to 2.2.1
This commit is contained in:
Paul Cleary 2019-11-11 13:11:41 -05:00 committed by GitHub
parent e4c131af43
commit ea7c77951c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
263 changed files with 5339 additions and 2823 deletions

2
.gitignore vendored
View File

@ -28,4 +28,4 @@ package-lock.json
.bloop
.metals
tmp.out
.vscode

View File

@ -1,3 +1,5 @@
version=2.2.1
style = default
maxColumn = 100

View File

@ -57,14 +57,16 @@ class BatchChangeRepositoryIntegrationSpec
None,
None,
None
)),
)
),
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
val f = for {
saveBatchChangeResult <- batchChangeRepository.save(batchChange)
getSingleChangesResult <- batchChangeRepository.getSingleChanges(
batchChange.changes.map(_.id))
batchChange.changes.map(_.id)
)
} yield (saveBatchChangeResult, getSingleChangesResult)
val (saveResponse, singleChanges) = f.unsafeRunSync()

View File

@ -83,7 +83,8 @@ class DnsConversionsIntegrationSpec extends WordSpec with Matchers with ResultHe
// deleting the record just added
val deleteResult: DnsResponse =
rightResultOf(
conn.deleteRecord(RecordSetChangeGenerator.forAdd(testRecord, testZone)).value)
conn.deleteRecord(RecordSetChangeGenerator.forAdd(testRecord, testZone)).value
)
deleteResult shouldBe a[NoError]

View File

@ -75,7 +75,8 @@ class RecordSetServiceIntegrationSpec
"test@test.com",
status = ZoneStatus.Active,
connection = testConnection,
adminGroupId = group.id)
adminGroupId = group.id
)
private val apexTestRecordA = RecordSet(
zone.id,
@ -85,7 +86,8 @@ class RecordSetServiceIntegrationSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(AData("10.1.1.1")))
List(AData("10.1.1.1"))
)
private val apexTestRecordAAAA = RecordSet(
zone.id,
"live-zone-test",
@ -94,7 +96,8 @@ class RecordSetServiceIntegrationSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(AAAAData("fd69:27cc:fe91::60")))
List(AAAAData("fd69:27cc:fe91::60"))
)
private val subTestRecordA = RecordSet(
zone.id,
"a-record",
@ -103,7 +106,8 @@ class RecordSetServiceIntegrationSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(AData("10.1.1.1")))
List(AData("10.1.1.1"))
)
private val subTestRecordAAAA = RecordSet(
zone.id,
"aaaa-record",
@ -112,7 +116,8 @@ class RecordSetServiceIntegrationSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(AAAAData("fd69:27cc:fe91::60")))
List(AAAAData("fd69:27cc:fe91::60"))
)
private val subTestRecordNS = RecordSet(
zone.id,
"ns-record",
@ -121,14 +126,16 @@ class RecordSetServiceIntegrationSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(NSData("172.17.42.1.")))
List(NSData("172.17.42.1."))
)
private val zoneTestNameConflicts = Zone(
s"zone-test-name-conflicts.",
"test@test.com",
status = ZoneStatus.Active,
connection = testConnection,
adminGroupId = group.id)
adminGroupId = group.id
)
private val apexTestRecordNameConflict = RecordSet(
zoneTestNameConflicts.id,
"zone-test-name-conflicts.",
@ -137,7 +144,8 @@ class RecordSetServiceIntegrationSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(AData("10.1.1.1")))
List(AData("10.1.1.1"))
)
private val subTestRecordNameConflict = RecordSet(
zoneTestNameConflicts.id,
"relative-name-conflict",
@ -146,14 +154,16 @@ class RecordSetServiceIntegrationSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(AData("10.1.1.1")))
List(AData("10.1.1.1"))
)
private val zoneTestAddRecords = Zone(
s"zone-test-add-records.",
"test@test.com",
status = ZoneStatus.Active,
connection = testConnection,
adminGroupId = group.id)
adminGroupId = group.id
)
private val highValueDomainRecord = RecordSet(
zone.id,
@ -172,7 +182,8 @@ class RecordSetServiceIntegrationSpec
status = ZoneStatus.Active,
connection = testConnection,
adminGroupId = group.id,
shared = true)
shared = true
)
private val sharedTestRecord = RecordSet(
sharedZone.id,
@ -216,8 +227,9 @@ class RecordSetServiceIntegrationSpec
zoneRepo = zoneRepository
groupRepo = groupRepository
List(group, group2, sharedGroup).map(g => waitForSuccess(groupRepo.save(g)))
List(zone, zoneTestNameConflicts, zoneTestAddRecords, sharedZone).map(z =>
waitForSuccess(zoneRepo.save(z)))
List(zone, zoneTestNameConflicts, zoneTestAddRecords, sharedZone).map(
z => waitForSuccess(zoneRepo.save(z))
)
// Seeding records in DB
val records = List(
@ -242,7 +254,8 @@ class RecordSetServiceIntegrationSpec
mock[RecordChangeRepository],
mock[UserRepository],
TestMessageQueue,
new AccessValidations())
new AccessValidations()
)
}
def tearDown(): Unit = ()
@ -270,7 +283,8 @@ class RecordSetServiceIntegrationSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(AData("10.1.1.1")))
List(AData("10.1.1.1"))
)
val result =
testRecordSetService
.addRecordSet(newRecord, auth)
@ -387,7 +401,8 @@ class RecordSetServiceIntegrationSpec
.unsafeRunSync()
leftValue(result) shouldBe InvalidRequest(
HighValueDomainError("high-value-domain-new.live-zone-test.").message)
HighValueDomainError("high-value-domain-new.live-zone-test.").message
)
}
"fail to update a record whose name is a high value domain" in {
@ -399,7 +414,8 @@ class RecordSetServiceIntegrationSpec
.unsafeRunSync()
leftValue(result) shouldBe InvalidRequest(
HighValueDomainError("high-value-domain-existing.live-zone-test.").message)
HighValueDomainError("high-value-domain-existing.live-zone-test.").message
)
}
"fail to delete a record whose name is a high value domain" in {
@ -409,7 +425,8 @@ class RecordSetServiceIntegrationSpec
.unsafeRunSync()
leftValue(result) shouldBe InvalidRequest(
HighValueDomainError("high-value-domain-existing.live-zone-test.").message)
HighValueDomainError("high-value-domain-existing.live-zone-test.").message
)
}
"get a shared record when user is in assigned ownerGroup" in {
@ -518,7 +535,8 @@ class RecordSetServiceIntegrationSpec
.deleteRecordSet(
testOwnerGroupRecordInNormalZone.id,
testOwnerGroupRecordInNormalZone.zoneId,
auth2)
auth2
)
.value
)

View File

@ -83,7 +83,8 @@ class ZoneServiceIntegrationSpec
ttl = 38400,
status = RecordSetStatus.Active,
created = DateTime.now,
records = List(AData("10.1.1.1")))
records = List(AData("10.1.1.1"))
)
private val changeSetSOA = ChangeSet(RecordSetChangeGenerator.forAdd(testRecordSOA, okZone))
private val changeSetNS = ChangeSet(RecordSetChangeGenerator.forAdd(testRecordNS, okZone))
@ -159,7 +160,8 @@ class ZoneServiceIntegrationSpec
"getBackendIds" should {
"return backend ids in config" in {
testZoneService.getBackendIds().value.unsafeRunSync() shouldBe Right(
List("func-test-backend"))
List("func-test-backend")
)
}
}

View File

@ -32,17 +32,21 @@ class ZoneViewLoaderIntegrationSpec extends WordSpec with Matchers {
assertThrows[IllegalArgumentException](
DnsZoneViewLoader(
Zone("vinyldns.", "bad@transfer.connection")
.copy(transferConnection =
Some(ZoneConnection("invalid-connection.", "bad-key", "invalid-key", "10.1.1.1"))))
.load()
.unsafeRunSync())
.copy(
transferConnection =
Some(ZoneConnection("invalid-connection.", "bad-key", "invalid-key", "10.1.1.1"))
)
).load()
.unsafeRunSync()
)
}
"return a failure if the zone doesn't exist in the DNS backend" in {
assertThrows[ZoneTransferException](
DnsZoneViewLoader(Zone("non-existent-zone", "bad@zone.test"))
.load()
.unsafeRunSync())
.unsafeRunSync()
)
}
"return a failure if the zone is larger than the max zone size" in {

View File

@ -72,7 +72,8 @@ class EmailNotifierIntegrationSpec
None,
None,
None
)),
)
),
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)

View File

@ -68,7 +68,8 @@ class SnsNotifierIntegrationSpec
None,
None,
None
)),
)
),
approvalStatus = BatchChangeApprovalStatus.AutoApproved,
id = "a615e2bb-8b35-4a39-8947-1edd0e653afa"
)
@ -76,12 +77,16 @@ class SnsNotifierIntegrationSpec
val credentialsProvider = new AWSStaticCredentialsProvider(
new BasicAWSCredentials(
snsConfig.getString("access-key"),
snsConfig.getString("secret-key")))
snsConfig.getString("secret-key")
)
)
val sns = AmazonSNSClientBuilder.standard
.withEndpointConfiguration(
new EndpointConfiguration(
snsConfig.getString("service-endpoint"),
snsConfig.getString("signing-region")))
snsConfig.getString("signing-region")
)
)
.withCredentials(credentialsProvider)
.build()
val sqs = AmazonSQSClientBuilder
@ -113,7 +118,8 @@ class SnsNotifierIntegrationSpec
val notification = parse(messages.get(0).getBody)
(notification \ "Message").extract[String] should be(
"""{"userId":"ok","userName":"ok","createdTimestamp":"2019-07-22T19:38:23Z",""" +
""""status":"Complete","approvalStatus":"AutoApproved","id":"a615e2bb-8b35-4a39-8947-1edd0e653afa"}""")
""""status":"Complete","approvalStatus":"AutoApproved","id":"a615e2bb-8b35-4a39-8947-1edd0e653afa"}"""
)
}
}

View File

@ -79,7 +79,8 @@ object Boot extends App {
repositories.userRepository,
repositories.groupRepository,
repositories.zoneRepository,
repositories.membershipRepository)
repositories.membershipRepository
)
queueConfig <- VinylDNSConfig.messageQueueConfig
messageQueue <- MessageQueueLoader.load(queueConfig)
processingDisabled <- IO(VinylDNSConfig.vinyldnsConfig.getBoolean("processing-disabled"))
@ -128,16 +129,19 @@ object Boot extends App {
connectionValidator,
messageQueue,
zoneValidations,
recordAccessValidations)
recordAccessValidations
)
val healthService = new HealthService(
messageQueue.healthCheck :: connectionValidator.healthCheck(healthCheckTimeout) ::
loaderResponse.healthChecks)
loaderResponse.healthChecks
)
val batchChangeConverter =
new BatchChangeConverter(repositories.batchChangeRepository, messageQueue)
val authPrincipalProvider =
new MembershipAuthPrincipalProvider(
repositories.userRepository,
repositories.membershipRepository)
repositories.membershipRepository
)
val batchChangeService = BatchChangeService(
repositories,
batchChangeValidations,

View File

@ -76,7 +76,8 @@ object VinylDNSConfig {
lazy val highValueRegexList: List[Regex] =
ZoneRecordValidations.toCaseIgnoredRegexList(
getOptionalStringList("high-value-domains.regex-list"))
getOptionalStringList("high-value-domains.regex-list")
)
lazy val highValueIpList: List[IpAddress] =
getOptionalStringList("high-value-domains.ip-list").flatMap(ip => IpAddress(ip))
@ -148,13 +149,15 @@ object VinylDNSConfig {
lazy val domainListRequiringManualReview: List[Regex] =
ZoneRecordValidations.toCaseIgnoredRegexList(
getOptionalStringList("manual-review-domains.domain-list"))
getOptionalStringList("manual-review-domains.domain-list")
)
lazy val ipListRequiringManualReview: List[IpAddress] =
getOptionalStringList("manual-review-domains.ip-list").flatMap(ip => IpAddress(ip))
lazy val zoneNameListRequiringManualReview: Set[String] = {
Set() ++ getOptionalStringList("manual-review-domains.zone-name-list").map(zn =>
DomainHelpers.ensureTrailingDot(zn.toLowerCase))
Set() ++ getOptionalStringList("manual-review-domains.zone-name-list").map(
zn => DomainHelpers.ensureTrailingDot(zn.toLowerCase)
)
}
}

View File

@ -59,7 +59,8 @@ object CommandHandler {
pollingInterval: FiniteDuration,
pauseSignal: SignallingRef[IO, Boolean],
connections: ConfiguredDnsConnections,
maxOpen: Int = 4)(implicit timer: Timer[IO]): Stream[IO, Unit] = {
maxOpen: Int = 4
)(implicit timer: Timer[IO]): Stream[IO, Unit] = {
// Polls queue for message batches, connected to the signal which is toggled in the status endpoint
val messageSource = startPolling(mq, count, pollingInterval).pauseWhen(pauseSignal)
@ -73,7 +74,8 @@ object CommandHandler {
recordChangeHandler,
zoneSyncHandler,
batchChangeHandler,
connections)
connections
)
// Delete messages from message queue when complete
val updateQueue = messageSink(mq)
@ -84,7 +86,8 @@ object CommandHandler {
.map(
_.observe(increaseTimeoutWhenSyncing)
.through(changeRequestProcessor)
.through(updateQueue))
.through(updateQueue)
)
.parJoin(maxOpen)
.handleErrorWith { error =>
logger.error("Encountered unexpected error in main flow", error)
@ -98,7 +101,8 @@ object CommandHandler {
/* Polls Message Queue for messages */
def startPolling(mq: MessageQueue, count: MessageCount, pollingInterval: FiniteDuration)(
implicit timer: Timer[IO]): Stream[IO, Stream[IO, CommandMessage]] = {
implicit timer: Timer[IO]
): Stream[IO, Stream[IO, CommandMessage]] = {
def pollingStream(): Stream[IO, Stream[IO, CommandMessage]] =
// every delay duration, we poll
@ -145,7 +149,8 @@ object CommandHandler {
recordChangeProcessor: (DnsConnection, RecordSetChange) => IO[RecordSetChange],
zoneSyncProcessor: ZoneChange => IO[ZoneChange],
batchChangeProcessor: BatchChangeCommand => IO[Option[BatchChange]],
connections: ConfiguredDnsConnections): Pipe[IO, CommandMessage, MessageOutcome] =
connections: ConfiguredDnsConnections
): Pipe[IO, CommandMessage, MessageOutcome] =
_.evalMap[IO, MessageOutcome] { message =>
message.command match {
case sync: ZoneChange
@ -202,7 +207,8 @@ object CommandHandler {
recordChangeRepo: RecordChangeRepository,
batchChangeRepo: BatchChangeRepository,
notifiers: AllNotifiers,
connections: ConfiguredDnsConnections)(implicit timer: Timer[IO]): IO[Unit] = {
connections: ConfiguredDnsConnections
)(implicit timer: Timer[IO]): IO[Unit] = {
// Handlers for each type of change request
val zoneChangeHandler =
ZoneChangeHandler(zoneRepo, zoneChangeRepo, recordSetRepo)

View File

@ -95,7 +95,8 @@ object DomainValidations {
def validateStringLength(
value: Option[String],
minInclusive: Option[Int],
maxInclusive: Int): ValidatedNel[DomainValidationError, Option[String]] =
maxInclusive: Int
): ValidatedNel[DomainValidationError, Option[String]] =
validateIfDefined(value) { d =>
validateStringLength(d, minInclusive, maxInclusive)
}
@ -103,7 +104,8 @@ object DomainValidations {
def validateStringLength(
value: String,
minInclusive: Option[Int],
maxInclusive: Int): ValidatedNel[DomainValidationError, String] =
maxInclusive: Int
): ValidatedNel[DomainValidationError, String] =
if (minInclusive.forall(m => value.length >= m) && value.length <= maxInclusive)
value.validNel
else InvalidLength(value, minInclusive.getOrElse(0), maxInclusive).invalidNel

View File

@ -57,7 +57,8 @@ object ReverseZoneHelpers {
handleIpv6RecordValidation(zone: Zone, recordName)
} else {
InvalidRequest(
s"RecordSet $recordName does not specify a valid IP address in zone ${zone.name}").asLeft
s"RecordSet $recordName does not specify a valid IP address in zone ${zone.name}"
).asLeft
}
def convertPTRtoIPv4(zone: Zone, recordName: String): String = {
@ -83,7 +84,8 @@ object ReverseZoneHelpers {
private def recordsetIsWithinCidrMaskIpv4(
mask: String,
zone: Zone,
recordName: String): Boolean = {
recordName: String
): Boolean = {
val recordIpAddr = convertPTRtoIPv4(zone, recordName)
Try {
@ -125,14 +127,16 @@ object ReverseZoneHelpers {
private def handleIpv4RecordValidation(
zone: Zone,
recordName: String): Either[Throwable, Unit] = {
recordName: String
): Either[Throwable, Unit] = {
val isValid = for {
cidrMask <- getZoneAsCIDRString(zone)
validated <- if (recordsetIsWithinCidrMask(cidrMask, zone, recordName)) {
true.asRight
} else {
InvalidRequest(
s"RecordSet $recordName does not specify a valid IP address in zone ${zone.name}").asLeft
s"RecordSet $recordName does not specify a valid IP address in zone ${zone.name}"
).asLeft
}
} yield validated
@ -141,14 +145,16 @@ object ReverseZoneHelpers {
private def handleIpv6RecordValidation(
zone: Zone,
recordName: String): Either[Throwable, Unit] = {
recordName: String
): Either[Throwable, Unit] = {
val v6Regex = "([0-9a-f][.]){32}ip6.arpa.".r
s"$recordName.${zone.name}" match {
case v6Regex(_*) => ().asRight
case _ =>
InvalidRequest(
s"RecordSet $recordName does not specify a valid IP address in zone ${zone.name}").asLeft
s"RecordSet $recordName does not specify a valid IP address in zone ${zone.name}"
).asLeft
}
}

View File

@ -26,8 +26,9 @@ object ValidationImprovements {
* If the value is not present, will return None as success
*
*/
def validateIfDefined[E, A](value: => Option[A])(
validator: A => ValidatedNel[E, A]): ValidatedNel[E, Option[A]] =
def validateIfDefined[E, A](
value: => Option[A]
)(validator: A => ValidatedNel[E, A]): ValidatedNel[E, Option[A]] =
value match {
case None => Option.empty[A].validNel[E]
case Some(a) => validator(a).map(Some(_))

View File

@ -31,31 +31,39 @@ class AccessValidations(globalAcls: GlobalAcls = GlobalAcls(List.empty))
def canSeeZone(auth: AuthPrincipal, zone: Zone): Either[Throwable, Unit] =
ensuring(
NotAuthorizedError(s"User ${auth.signedInUser.userName} cannot access zone '${zone.name}'"))(
NotAuthorizedError(s"User ${auth.signedInUser.userName} cannot access zone '${zone.name}'")
)(
auth.isSystemAdmin || auth
.isGroupMember(zone.adminGroupId) || userHasAclRules(auth, zone))
.isGroupMember(zone.adminGroupId) || userHasAclRules(auth, zone)
)
def canChangeZone(
auth: AuthPrincipal,
zoneName: String,
zoneAdminGroupId: String): Either[Throwable, Unit] =
zoneAdminGroupId: String
): Either[Throwable, Unit] =
ensuring(
NotAuthorizedError(
s"""User '${auth.signedInUser.userName}' cannot create or modify zone '$zoneName' because
|they are not in the Zone Admin Group '$zoneAdminGroupId'""".stripMargin
.replace("\n", " ")))(auth.isSuper || auth.isGroupMember(zoneAdminGroupId))
.replace("\n", " ")
)
)(auth.isSuper || auth.isGroupMember(zoneAdminGroupId))
def canAddRecordSet(
auth: AuthPrincipal,
recordName: String,
recordType: RecordType,
zone: Zone,
recordData: List[RecordData] = List.empty): Either[Throwable, Unit] = {
recordData: List[RecordData] = List.empty
): Either[Throwable, Unit] = {
val accessLevel = getAccessLevel(auth, recordName, recordType, zone, None, recordData)
ensuring(
NotAuthorizedError(s"User ${auth.signedInUser.userName} does not have access to create " +
s"$recordName.${zone.name}"))(
accessLevel == AccessLevel.Delete || accessLevel == AccessLevel.Write)
NotAuthorizedError(
s"User ${auth.signedInUser.userName} does not have access to create " +
s"$recordName.${zone.name}"
)
)(accessLevel == AccessLevel.Delete || accessLevel == AccessLevel.Write)
}
def canUpdateRecordSet(
@ -64,13 +72,16 @@ class AccessValidations(globalAcls: GlobalAcls = GlobalAcls(List.empty))
recordType: RecordType,
zone: Zone,
recordOwnerGroupId: Option[String],
newRecordData: List[RecordData] = List.empty): Either[Throwable, Unit] = {
newRecordData: List[RecordData] = List.empty
): Either[Throwable, Unit] = {
val accessLevel =
getAccessLevel(auth, recordName, recordType, zone, recordOwnerGroupId, newRecordData)
ensuring(
NotAuthorizedError(s"User ${auth.signedInUser.userName} does not have access to update " +
s"$recordName.${zone.name}"))(
accessLevel == AccessLevel.Delete || accessLevel == AccessLevel.Write)
NotAuthorizedError(
s"User ${auth.signedInUser.userName} does not have access to update " +
s"$recordName.${zone.name}"
)
)(accessLevel == AccessLevel.Delete || accessLevel == AccessLevel.Write)
}
def canDeleteRecordSet(
@ -79,16 +90,16 @@ class AccessValidations(globalAcls: GlobalAcls = GlobalAcls(List.empty))
recordType: RecordType,
zone: Zone,
recordOwnerGroupId: Option[String],
existingRecordData: List[RecordData] = List.empty): Either[Throwable, Unit] =
existingRecordData: List[RecordData] = List.empty
): Either[Throwable, Unit] =
ensuring(
NotAuthorizedError(s"User ${auth.signedInUser.userName} does not have access to delete " +
s"$recordName.${zone.name}"))(getAccessLevel(
auth,
recordName,
recordType,
zone,
recordOwnerGroupId,
existingRecordData) == AccessLevel.Delete)
NotAuthorizedError(
s"User ${auth.signedInUser.userName} does not have access to delete " +
s"$recordName.${zone.name}"
)
)(
getAccessLevel(auth, recordName, recordType, zone, recordOwnerGroupId, existingRecordData) == AccessLevel.Delete
)
def canViewRecordSet(
auth: AuthPrincipal,
@ -96,17 +107,22 @@ class AccessValidations(globalAcls: GlobalAcls = GlobalAcls(List.empty))
recordType: RecordType,
zone: Zone,
recordOwnerGroupId: Option[String],
recordData: List[RecordData] = List.empty): Either[Throwable, Unit] =
recordData: List[RecordData] = List.empty
): Either[Throwable, Unit] =
ensuring(
NotAuthorizedError(s"User ${auth.signedInUser.userName} does not have access to view " +
s"$recordName.${zone.name}"))(
NotAuthorizedError(
s"User ${auth.signedInUser.userName} does not have access to view " +
s"$recordName.${zone.name}"
)
)(
getAccessLevel(auth, recordName, recordType, zone, recordOwnerGroupId, recordData) != AccessLevel.NoAccess
)
def getListAccessLevels(
auth: AuthPrincipal,
recordSets: List[RecordSetInfo],
zone: Zone): List[RecordSetListInfo] =
zone: Zone
): List[RecordSetListInfo] =
if (auth.isSuper || auth.isGroupMember(zone.adminGroupId))
recordSets.map(RecordSetListInfo(_, AccessLevel.Delete))
else {
@ -136,13 +152,15 @@ class AccessValidations(globalAcls: GlobalAcls = GlobalAcls(List.empty))
auth: AuthPrincipal,
recordName: String,
recordType: RecordType,
zone: Zone): AccessLevel = {
zone: Zone
): AccessLevel = {
val validRules = zone.acl.rules.filter { rule =>
ruleAppliesToUser(auth, rule) && ruleAppliesToRecordType(recordType, rule) && ruleAppliesToRecordName(
recordName,
recordType,
zone,
rule)
rule
)
}
getPrioritizedAccessLevel(recordType, validRules)
}
@ -164,7 +182,8 @@ class AccessValidations(globalAcls: GlobalAcls = GlobalAcls(List.empty))
recordName: String,
recordType: RecordType,
zone: Zone,
rule: ACLRule): Boolean =
rule: ACLRule
): Boolean =
rule.recordMask match {
case Some(mask) if recordType == RecordType.PTR =>
ReverseZoneHelpers.recordsetIsWithinCidrMask(mask, zone, recordName)
@ -192,7 +211,8 @@ class AccessValidations(globalAcls: GlobalAcls = GlobalAcls(List.empty))
recordType: RecordType,
zone: Zone,
recordOwnerGroupId: Option[String] = None,
recordData: List[RecordData] = List.empty): AccessLevel = auth match {
recordData: List[RecordData] = List.empty
): AccessLevel = auth match {
case testUser if testUser.isTestUser && !zone.isTest => AccessLevel.NoAccess
case admin if admin.isGroupMember(zone.adminGroupId) =>
AccessLevel.Delete
@ -211,7 +231,8 @@ class AccessValidations(globalAcls: GlobalAcls = GlobalAcls(List.empty))
def sharedRecordAccess(
auth: AuthPrincipal,
recordType: RecordType,
recordOwnerGroupId: Option[String]): Boolean =
recordOwnerGroupId: Option[String]
): Boolean =
recordOwnerGroupId.exists(auth.isGroupMember) ||
(recordOwnerGroupId.isEmpty && VinylDNSConfig.sharedApprovedTypes.contains(recordType))
}

View File

@ -30,14 +30,16 @@ trait AccessValidationsAlgebra {
def canChangeZone(
auth: AuthPrincipal,
zoneName: String,
zoneAdminGroupId: String): Either[Throwable, Unit]
zoneAdminGroupId: String
): Either[Throwable, Unit]
def canAddRecordSet(
auth: AuthPrincipal,
recordName: String,
recordType: RecordType,
zone: Zone,
recordData: List[RecordData] = List.empty): Either[Throwable, Unit]
recordData: List[RecordData] = List.empty
): Either[Throwable, Unit]
def canUpdateRecordSet(
auth: AuthPrincipal,
@ -45,7 +47,8 @@ trait AccessValidationsAlgebra {
recordType: RecordType,
zone: Zone,
recordOwnerGroupId: Option[String],
newRecordData: List[RecordData] = List.empty): Either[Throwable, Unit]
newRecordData: List[RecordData] = List.empty
): Either[Throwable, Unit]
def canDeleteRecordSet(
auth: AuthPrincipal,
@ -53,7 +56,8 @@ trait AccessValidationsAlgebra {
recordType: RecordType,
zone: Zone,
recordOwnerGroupId: Option[String],
existingRecordData: List[RecordData] = List.empty): Either[Throwable, Unit]
existingRecordData: List[RecordData] = List.empty
): Either[Throwable, Unit]
def canViewRecordSet(
auth: AuthPrincipal,
@ -61,12 +65,14 @@ trait AccessValidationsAlgebra {
recordType: RecordType,
zone: Zone,
recordOwnerGroupId: Option[String],
recordData: List[RecordData] = List.empty): Either[Throwable, Unit]
recordData: List[RecordData] = List.empty
): Either[Throwable, Unit]
def getListAccessLevels(
auth: AuthPrincipal,
recordSets: List[RecordSetInfo],
zone: Zone): List[RecordSetListInfo]
zone: Zone
): List[RecordSetListInfo]
def getZoneAccess(auth: AuthPrincipal, zone: Zone): AccessLevel
}

View File

@ -47,7 +47,8 @@ final case class GlobalAcls(acls: List[GlobalAcl]) {
recordName: String,
recordType: RecordType,
zone: Zone,
recordData: List[RecordData] = List.empty): Boolean = {
recordData: List[RecordData] = List.empty
): Boolean = {
def isAuthorized(authPrincipal: AuthPrincipal, fqdn: String): Boolean = {
val regexList = authPrincipal.memberGroupIds.flatMap(aclMap.getOrElse(_, List.empty)).toList

View File

@ -28,8 +28,8 @@ trait AuthPrincipalProvider extends Monitored {
class MembershipAuthPrincipalProvider(
userRepo: UserRepository,
membershipRepo: MembershipRepository)
extends AuthPrincipalProvider {
membershipRepo: MembershipRepository
) extends AuthPrincipalProvider {
def getAuthPrincipal(accessKey: String): IO[Option[AuthPrincipal]] =
getUserByAccessKey(accessKey).flatMap {

View File

@ -40,16 +40,19 @@ class BatchChangeConverter(batchChangeRepo: BatchChangeRepository, messageQueue:
batchChange: BatchChange,
existingZones: ExistingZones,
groupedChanges: ChangeForValidationMap,
ownerGroupId: Option[String]): BatchResult[BatchConversionOutput] = {
ownerGroupId: Option[String]
): BatchResult[BatchConversionOutput] = {
logger.info(
s"Converting BatchChange [${batchChange.id}] with SingleChanges [${batchChange.changes.map(_.id)}]")
s"Converting BatchChange [${batchChange.id}] with SingleChanges [${batchChange.changes.map(_.id)}]"
)
for {
recordSetChanges <- createRecordSetChangesForBatch(
batchChange.changes,
existingZones,
groupedChanges,
batchChange.userId,
ownerGroupId).toRightBatchResult
ownerGroupId
).toRightBatchResult
_ <- allChangesWereConverted(batchChange.changes, recordSetChanges)
_ <- batchChangeRepo
.save(batchChange)
@ -62,7 +65,8 @@ class BatchChangeConverter(batchChangeRepo: BatchChangeRepository, messageQueue:
def allChangesWereConverted(
singleChanges: List[SingleChange],
recordSetChanges: List[RecordSetChange]): BatchResult[Unit] = {
recordSetChanges: List[RecordSetChange]
): BatchResult[Unit] = {
val convertedIds = recordSetChanges.flatMap(_.singleBatchChangeIds).toSet
singleChanges.find(ch => !convertedIds.contains(ch.id)) match {
@ -76,7 +80,8 @@ class BatchChangeConverter(batchChangeRepo: BatchChangeRepository, messageQueue:
def putChangesOnQueue(
recordSetChanges: List[RecordSetChange],
batchChangeId: String): BatchResult[List[RecordSetChange]] =
batchChangeId: String
): BatchResult[List[RecordSetChange]] =
recordSetChanges.toNel match {
case None =>
recordSetChanges.toRightBatchResult // If list is empty, return normally without queueing
@ -94,7 +99,8 @@ class BatchChangeConverter(batchChangeRepo: BatchChangeRepository, messageQueue:
def updateWithQueueingFailures(
batchChange: BatchChange,
recordSetChanges: List[RecordSetChange]): BatchChange = {
recordSetChanges: List[RecordSetChange]
): BatchChange = {
// idsMap maps batchId to recordSetId
val idsMap = recordSetChanges.flatMap { rsChange =>
rsChange.singleBatchChangeIds.map(batchId => (batchId, rsChange.id))
@ -128,7 +134,8 @@ class BatchChangeConverter(batchChangeRepo: BatchChangeRepository, messageQueue:
existingZones: ExistingZones,
groupedChanges: ChangeForValidationMap,
userId: String,
ownerGroupId: Option[String]): List[RecordSetChange] = {
ownerGroupId: Option[String]
): List[RecordSetChange] = {
// NOTE: this also assumes we are past approval and know the zone/record split at this point
val supportedChangesByKey = changes
.filter(sc => SupportedBatchChangeRecordTypes.get.contains(sc.typ))
@ -155,7 +162,8 @@ class BatchChangeConverter(batchChangeRepo: BatchChangeRepository, messageQueue:
proposedRecordData,
userId,
existingRecordSet,
ownerGroupId)
ownerGroupId
)
} yield recordSetChange
}
.toList
@ -170,7 +178,8 @@ class BatchChangeConverter(batchChangeRepo: BatchChangeRepository, messageQueue:
proposedRecordData: Set[RecordData],
userId: String,
existingRecordSet: Option[RecordSet],
ownerGroupId: Option[String]): Option[RecordSetChange] = {
ownerGroupId: Option[String]
): Option[RecordSetChange] = {
val singleChangeIds = singleChangeNel.map(_.id).toList

View File

@ -30,5 +30,6 @@ trait BatchChangeConverterAlgebra {
batchChange: BatchChange,
existingZones: ExistingZones,
groupedChanges: ChangeForValidationMap,
ownerGroupId: Option[String]): BatchResult[BatchConversionOutput]
ownerGroupId: Option[String]
): BatchResult[BatchConversionOutput]
}

View File

@ -30,8 +30,8 @@ final case class InvalidBatchChangeInput(errors: List[DomainValidationError])
// This separates error by change requested
final case class InvalidBatchChangeResponses(
changeRequests: List[ChangeInput],
changeRequestResponses: ValidatedBatch[ChangeForValidation])
extends BatchChangeErrorResponse
changeRequestResponses: ValidatedBatch[ChangeForValidation]
) extends BatchChangeErrorResponse
final case class BatchChangeFailedApproval(batchChange: BatchChange)
extends BatchChangeErrorResponse

View File

@ -29,7 +29,8 @@ final case class BatchChangeInput(
comments: Option[String],
changes: List[ChangeInput],
ownerGroupId: Option[String] = None,
scheduledTime: Option[DateTime] = None)
scheduledTime: Option[DateTime] = None
)
object BatchChangeInput {
def apply(batchChange: BatchChange): BatchChangeInput = {
@ -51,8 +52,8 @@ final case class AddChangeInput(
inputName: String,
typ: RecordType,
ttl: Option[Long],
record: RecordData)
extends ChangeInput {
record: RecordData
) extends ChangeInput {
def asNewStoredChange(errors: NonEmptyList[DomainValidationError]): SingleChange = {
val knownTtl = ttl.getOrElse(VinylDNSConfig.defaultTtl)
@ -76,8 +77,8 @@ final case class AddChangeInput(
final case class DeleteRRSetChangeInput(
inputName: String,
typ: RecordType,
record: Option[RecordData])
extends ChangeInput {
record: Option[RecordData]
) extends ChangeInput {
def asNewStoredChange(errors: NonEmptyList[DomainValidationError]): SingleChange =
SingleDeleteRRSetChange(
None,
@ -99,7 +100,8 @@ object AddChangeInput {
inputName: String,
typ: RecordType,
ttl: Option[Long],
record: RecordData): AddChangeInput = {
record: RecordData
): AddChangeInput = {
val transformName = typ match {
case PTR => inputName
case _ => ensureTrailingDot(inputName)
@ -115,7 +117,8 @@ object DeleteRRSetChangeInput {
def apply(
inputName: String,
typ: RecordType,
record: Option[RecordData] = None): DeleteRRSetChangeInput = {
record: Option[RecordData] = None
): DeleteRRSetChangeInput = {
val transformName = typ match {
case PTR => inputName
case _ => ensureTrailingDot(inputName)

View File

@ -21,4 +21,5 @@ import org.joda.time.DateTime
final case class BatchChangeReviewInfo(
reviewerId: String,
reviewComment: Option[String],
reviewTimestamp: DateTime = DateTime.now())
reviewTimestamp: DateTime = DateTime.now()
)

View File

@ -59,7 +59,8 @@ object BatchChangeService {
authProvider: AuthPrincipalProvider,
notifiers: AllNotifiers,
scheduledChangesEnabled: Boolean,
v6DiscoveryNibbleBoundaries: V6DiscoveryNibbleBoundaries): BatchChangeService =
v6DiscoveryNibbleBoundaries: V6DiscoveryNibbleBoundaries
): BatchChangeService =
new BatchChangeService(
dataAccessor.zoneRepository,
dataAccessor.recordSetRepository,
@ -88,8 +89,8 @@ class BatchChangeService(
authProvider: AuthPrincipalProvider,
notifiers: AllNotifiers,
scheduledChangesEnabled: Boolean,
v6zoneNibbleBoundaries: V6DiscoveryNibbleBoundaries)
extends BatchChangeServiceAlgebra {
v6zoneNibbleBoundaries: V6DiscoveryNibbleBoundaries
) extends BatchChangeServiceAlgebra {
import batchChangeValidations._
@ -97,7 +98,8 @@ class BatchChangeService(
def applyBatchChange(
batchChangeInput: BatchChangeInput,
auth: AuthPrincipal,
allowManualReview: Boolean): BatchResult[BatchChange] =
allowManualReview: Boolean
): BatchResult[BatchChange] =
for {
validationOutput <- applyBatchChangeValidationFlow(batchChangeInput, auth, isApproved = false)
changeForConversion <- buildResponse(
@ -110,13 +112,15 @@ class BatchChangeService(
changeForConversion,
validationOutput.existingZones,
validationOutput.groupedChanges,
batchChangeInput.ownerGroupId)
batchChangeInput.ownerGroupId
)
} yield serviceCompleteBatch
def applyBatchChangeValidationFlow(
batchChangeInput: BatchChangeInput,
auth: AuthPrincipal,
isApproved: Boolean): BatchResult[BatchValidationFlowOutput] =
isApproved: Boolean
): BatchResult[BatchValidationFlowOutput] =
for {
existingGroup <- getOwnerGroup(batchChangeInput.ownerGroupId)
_ <- validateBatchChangeInput(batchChangeInput, existingGroup, auth)
@ -130,13 +134,15 @@ class BatchChangeService(
groupedChanges,
auth,
isApproved,
batchChangeInput.ownerGroupId)
batchChangeInput.ownerGroupId
)
errorGroupIds <- getGroupIdsFromUnauthorizedErrors(validatedSingleChanges)
validatedSingleChangesWithGroups = errorGroupMapping(errorGroupIds, validatedSingleChanges)
} yield BatchValidationFlowOutput(validatedSingleChangesWithGroups, zoneMap, groupedChanges)
def getGroupIdsFromUnauthorizedErrors(
changes: ValidatedBatch[ChangeForValidation]): BatchResult[Set[Group]] = {
changes: ValidatedBatch[ChangeForValidation]
): BatchResult[Set[Group]] = {
val list = changes.getInvalid.collect {
case d: UserIsNotAuthorizedError => d.ownerGroupId
}.toSet
@ -145,7 +151,8 @@ class BatchChangeService(
def errorGroupMapping(
groups: Set[Group],
validations: ValidatedBatch[ChangeForValidation]): ValidatedBatch[ChangeForValidation] =
validations: ValidatedBatch[ChangeForValidation]
): ValidatedBatch[ChangeForValidation] =
validations.map {
case Invalid(err) =>
err.map {
@ -156,7 +163,8 @@ class BatchChangeService(
e.ownerGroupId,
e.ownerType,
group.map(_.email),
group.map(_.name))
group.map(_.name)
)
logger.error(updatedError.message)
updatedError
case e =>
@ -169,7 +177,8 @@ class BatchChangeService(
def rejectBatchChange(
batchChangeId: String,
authPrincipal: AuthPrincipal,
rejectBatchChangeInput: RejectBatchChangeInput): BatchResult[BatchChange] =
rejectBatchChangeInput: RejectBatchChangeInput
): BatchResult[BatchChange] =
for {
batchChange <- getExistingBatchChange(batchChangeId)
bypassTestCheck <- getBypassTestCheckForReject(authPrincipal, batchChange)
@ -177,14 +186,16 @@ class BatchChangeService(
rejectedBatchChange <- rejectBatchChange(
batchChange,
rejectBatchChangeInput.reviewComment,
authPrincipal.signedInUser.id)
authPrincipal.signedInUser.id
)
_ <- notifiers.notify(Notification(rejectedBatchChange)).toBatchResult
} yield rejectedBatchChange
def approveBatchChange(
batchChangeId: String,
authPrincipal: AuthPrincipal,
approveBatchChangeInput: ApproveBatchChangeInput): BatchResult[BatchChange] =
approveBatchChangeInput: ApproveBatchChangeInput
): BatchResult[BatchChange] =
for {
batchChange <- getExistingBatchChange(batchChangeId)
requesterAuth <- EitherT.fromOptionF(
@ -195,23 +206,27 @@ class BatchChangeService(
asInput = BatchChangeInput(batchChange)
reviewInfo = BatchChangeReviewInfo(
authPrincipal.userId,
approveBatchChangeInput.reviewComment)
approveBatchChangeInput.reviewComment
)
validationOutput <- applyBatchChangeValidationFlow(asInput, requesterAuth, isApproved = true)
changeForConversion = rebuildBatchChangeForUpdate(
batchChange,
validationOutput.validatedChanges,
reviewInfo)
reviewInfo
)
serviceCompleteBatch <- convertOrSave(
changeForConversion,
validationOutput.existingZones,
validationOutput.groupedChanges,
batchChange.ownerGroupId)
batchChange.ownerGroupId
)
response <- buildResponseForApprover(serviceCompleteBatch).toBatchResult
} yield response
def cancelBatchChange(
batchChangeId: String,
authPrincipal: AuthPrincipal): BatchResult[BatchChange] =
authPrincipal: AuthPrincipal
): BatchResult[BatchChange] =
for {
batchChange <- getExistingBatchChange(batchChangeId)
_ <- validateBatchChangeCancellation(batchChange, authPrincipal).toBatchResult
@ -279,7 +294,8 @@ class BatchChangeService(
def getExistingRecordSets(
changes: ValidatedBatch[ChangeForValidation],
zoneMap: ExistingZones): IO[ExistingRecordSets] = {
zoneMap: ExistingZones
): IO[ExistingRecordSets] = {
val uniqueGets = changes.getValid.map { change =>
change.inputChange.typ match {
case PTR => s"${change.recordName}.${change.zone.name}"
@ -295,7 +311,8 @@ class BatchChangeService(
def doTtlMapping(
changes: ValidatedBatch[ChangeForValidation],
existingRecordSets: ExistingRecordSets): ValidatedBatch[ChangeForValidation] =
existingRecordSets: ExistingRecordSets
): ValidatedBatch[ChangeForValidation] =
changes.mapValid {
case add: AddChangeForValidation =>
existingRecordSets
@ -326,7 +343,8 @@ class BatchChangeService(
def zoneDiscovery(
changes: ValidatedBatch[ChangeInput],
zoneMap: ExistingZones): ValidatedBatch[ChangeForValidation] =
zoneMap: ExistingZones
): ValidatedBatch[ChangeForValidation] =
changes.mapValid { change =>
change.typ match {
case A | AAAA | CNAME | MX | TXT => forwardZoneDiscovery(change, zoneMap)
@ -340,7 +358,8 @@ class BatchChangeService(
def forwardZoneDiscovery(
change: ChangeInput,
zoneMap: ExistingZones): SingleValidation[ChangeForValidation] = {
zoneMap: ExistingZones
): SingleValidation[ChangeForValidation] = {
// getAllPossibleZones is ordered most to least specific, so 1st match is right
val zone = getAllPossibleZones(change.inputName).map(zoneMap.getByName).collectFirst {
@ -358,7 +377,8 @@ class BatchChangeService(
def ptrIpv4ZoneDiscovery(
change: ChangeInput,
zoneMap: ExistingZones): SingleValidation[ChangeForValidation] = {
zoneMap: ExistingZones
): SingleValidation[ChangeForValidation] = {
val recordName = change.inputName.split('.').takeRight(1).mkString
val validZones = zoneMap.getipv4PTRMatches(change.inputName)
@ -374,7 +394,8 @@ class BatchChangeService(
def ptrIpv6ZoneDiscovery(
change: ChangeInput,
zoneMap: ExistingZones): SingleValidation[ChangeForValidation] = {
zoneMap: ExistingZones
): SingleValidation[ChangeForValidation] = {
val zones = zoneMap.getipv6PTRMatches(change.inputName)
if (zones.isEmpty)
@ -403,7 +424,8 @@ class BatchChangeService(
batchChangeInput: BatchChangeInput,
transformed: ValidatedBatch[ChangeForValidation],
auth: AuthPrincipal,
allowManualReview: Boolean): Either[BatchChangeErrorResponse, BatchChange] = {
allowManualReview: Boolean
): Either[BatchChangeErrorResponse, BatchChange] = {
// Respond with a fatal error that kicks the change out to the user
def errorResponse =
@ -480,7 +502,8 @@ class BatchChangeService(
def rebuildBatchChangeForUpdate(
existingBatchChange: BatchChange,
transformed: ValidatedBatch[ChangeForValidation],
reviewInfo: BatchChangeReviewInfo): BatchChange = {
reviewInfo: BatchChangeReviewInfo
): BatchChange = {
val changes = transformed.zip(existingBatchChange.changes).map {
case (validated, existing) =>
validated match {
@ -508,7 +531,8 @@ class BatchChangeService(
batchChange: BatchChange,
existingZones: ExistingZones,
groupedChanges: ChangeForValidationMap,
ownerGroupId: Option[String]): BatchResult[BatchChange] = batchChange.approvalStatus match {
ownerGroupId: Option[String]
): BatchResult[BatchChange] = batchChange.approvalStatus match {
case AutoApproved =>
// send on to the converter, it will be saved there
batchChangeConverter
@ -527,12 +551,14 @@ class BatchChangeService(
// this should not be called with a rejected change (or if manual review is off)!
logger.error(
s"convertOrSave called with a rejected batch change; " +
s"batchChangeId=${batchChange.id}; manualReviewEnabled=$manualReviewEnabled")
s"batchChangeId=${batchChange.id}; manualReviewEnabled=$manualReviewEnabled"
)
UnknownConversionError("Cannot convert a rejected batch change").toLeftBatchResult
}
def buildResponseForApprover(
batchChange: BatchChange): Either[BatchChangeErrorResponse, BatchChange] =
batchChange: BatchChange
): Either[BatchChangeErrorResponse, BatchChange] =
batchChange.approvalStatus match {
case ManuallyApproved => batchChange.asRight
case _ => BatchChangeFailedApproval(batchChange).asLeft
@ -540,7 +566,8 @@ class BatchChangeService(
def addOwnerGroupNamesToSummaries(
summaries: List[BatchChangeSummary],
groups: Set[Group]): List[BatchChangeSummary] =
groups: Set[Group]
): List[BatchChangeSummary] =
summaries.map { summary =>
val groupName =
summary.ownerGroupId.flatMap(groupId => groups.find(_.id == groupId).map(_.name))
@ -549,7 +576,8 @@ class BatchChangeService(
def addReviewerUserNamesToSummaries(
summaries: List[BatchChangeSummary],
reviewers: ListUsersResults): List[BatchChangeSummary] =
reviewers: ListUsersResults
): List[BatchChangeSummary] =
summaries.map { summary =>
val userName =
summary.reviewerId.flatMap(userId => reviewers.users.find(_.id == userId).map(_.userName))
@ -561,8 +589,8 @@ class BatchChangeService(
startFrom: Option[Int] = None,
maxItems: Int = 100,
ignoreAccess: Boolean = false,
approvalStatus: Option[BatchChangeApprovalStatus] = None)
: BatchResult[BatchChangeSummaryList] = {
approvalStatus: Option[BatchChangeApprovalStatus] = None
): BatchResult[BatchChangeSummaryList] = {
val userId = if (ignoreAccess && auth.isSystemAdmin) None else Some(auth.userId)
for {
listResults <- batchChangeRepo
@ -572,23 +600,27 @@ class BatchChangeService(
rsOwnerGroups <- groupRepository.getGroups(rsOwnerGroupIds).toBatchResult
summariesWithGroupNames = addOwnerGroupNamesToSummaries(
listResults.batchChanges,
rsOwnerGroups)
rsOwnerGroups
)
reviewerIds = listResults.batchChanges.flatMap(_.reviewerId).toSet
reviewerUserNames <- userRepository.getUsers(reviewerIds, None, Some(maxItems)).toBatchResult
summariesWithReviewerUserNames = addReviewerUserNamesToSummaries(
summariesWithGroupNames,
reviewerUserNames)
reviewerUserNames
)
listWithGroupNames = listResults.copy(
batchChanges = summariesWithReviewerUserNames,
ignoreAccess = ignoreAccess,
approvalStatus = approvalStatus)
approvalStatus = approvalStatus
)
} yield listWithGroupNames
}
def rejectBatchChange(
batchChange: BatchChange,
reviewComment: Option[String],
reviewerId: String): BatchResult[BatchChange] = {
reviewerId: String
): BatchResult[BatchChange] = {
val rejectedSingleChanges = batchChange.changes.map(_.reject)
// Update rejection attributes and single changes for batch change
@ -618,7 +650,8 @@ class BatchChangeService(
def getBypassTestCheckForReject(
rejecterAuth: AuthPrincipal,
batchChange: BatchChange): BatchResult[Boolean] =
batchChange: BatchChange
): BatchResult[Boolean] =
if (!rejecterAuth.isTestUser) {
// if the rejecting user isnt a test user, we dont need to get the batch creator's info, can just pass along
// true to bypass the test check

View File

@ -26,7 +26,8 @@ trait BatchChangeServiceAlgebra {
def applyBatchChange(
batchChangeInput: BatchChangeInput,
auth: AuthPrincipal,
allowManualReview: Boolean): BatchResult[BatchChange]
allowManualReview: Boolean
): BatchResult[BatchChange]
def getBatchChange(id: String, auth: AuthPrincipal): BatchResult[BatchChangeInfo]
@ -35,17 +36,20 @@ trait BatchChangeServiceAlgebra {
startFrom: Option[Int],
maxItems: Int,
ignoreAccess: Boolean,
approvalStatus: Option[BatchChangeApprovalStatus]): BatchResult[BatchChangeSummaryList]
approvalStatus: Option[BatchChangeApprovalStatus]
): BatchResult[BatchChangeSummaryList]
def rejectBatchChange(
batchChangeId: String,
authPrincipal: AuthPrincipal,
rejectBatchChangeInput: RejectBatchChangeInput): BatchResult[BatchChange]
rejectBatchChangeInput: RejectBatchChangeInput
): BatchResult[BatchChange]
def approveBatchChange(
batchChangeId: String,
authPrincipal: AuthPrincipal,
approveBatchChangeInput: ApproveBatchChangeInput): BatchResult[BatchChange]
approveBatchChangeInput: ApproveBatchChangeInput
): BatchResult[BatchChange]
def cancelBatchChange(id: String, auth: AuthPrincipal): BatchResult[BatchChange]
}

View File

@ -35,42 +35,49 @@ trait BatchChangeValidationsAlgebra {
def validateBatchChangeInput(
input: BatchChangeInput,
existingGroup: Option[Group],
authPrincipal: AuthPrincipal): BatchResult[Unit]
authPrincipal: AuthPrincipal
): BatchResult[Unit]
def validateInputChanges(
input: List[ChangeInput],
isApproved: Boolean): ValidatedBatch[ChangeInput]
isApproved: Boolean
): ValidatedBatch[ChangeInput]
def validateChangesWithContext(
groupedChanges: ChangeForValidationMap,
auth: AuthPrincipal,
isApproved: Boolean,
batchOwnerGroupId: Option[String]): ValidatedBatch[ChangeForValidation]
batchOwnerGroupId: Option[String]
): ValidatedBatch[ChangeForValidation]
def canGetBatchChange(
batchChange: BatchChange,
auth: AuthPrincipal): Either[BatchChangeErrorResponse, Unit]
auth: AuthPrincipal
): Either[BatchChangeErrorResponse, Unit]
def validateBatchChangeRejection(
batchChange: BatchChange,
authPrincipal: AuthPrincipal,
bypassTestValidation: Boolean): Either[BatchChangeErrorResponse, Unit]
bypassTestValidation: Boolean
): Either[BatchChangeErrorResponse, Unit]
def validateBatchChangeApproval(
batchChange: BatchChange,
authPrincipal: AuthPrincipal,
isTestChange: Boolean): Either[BatchChangeErrorResponse, Unit]
isTestChange: Boolean
): Either[BatchChangeErrorResponse, Unit]
def validateBatchChangeCancellation(
batchChange: BatchChange,
authPrincipal: AuthPrincipal): Either[BatchChangeErrorResponse, Unit]
authPrincipal: AuthPrincipal
): Either[BatchChangeErrorResponse, Unit]
}
class BatchChangeValidations(
changeLimit: Int,
accessValidation: AccessValidationsAlgebra,
scheduledChangesEnabled: Boolean = false)
extends BatchChangeValidationsAlgebra {
scheduledChangesEnabled: Boolean = false
) extends BatchChangeValidationsAlgebra {
import RecordType._
import accessValidation._
@ -78,11 +85,13 @@ class BatchChangeValidations(
def validateBatchChangeInput(
input: BatchChangeInput,
existingGroup: Option[Group],
authPrincipal: AuthPrincipal): BatchResult[Unit] = {
authPrincipal: AuthPrincipal
): BatchResult[Unit] = {
val validations = validateBatchChangeInputSize(input) |+| validateOwnerGroupId(
input.ownerGroupId,
existingGroup,
authPrincipal)
authPrincipal
)
for {
_ <- validations
@ -105,7 +114,8 @@ class BatchChangeValidations(
def validateOwnerGroupId(
ownerGroupId: Option[String],
existingGroup: Option[Group],
authPrincipal: AuthPrincipal): SingleValidation[Unit] =
authPrincipal: AuthPrincipal
): SingleValidation[Unit] =
(ownerGroupId, existingGroup) match {
case (None, _) => ().validNel
case (Some(groupId), None) => GroupDoesNotExist(groupId).invalidNel
@ -117,26 +127,33 @@ class BatchChangeValidations(
def validateBatchChangeRejection(
batchChange: BatchChange,
authPrincipal: AuthPrincipal,
bypassTestValidation: Boolean): Either[BatchChangeErrorResponse, Unit] =
bypassTestValidation: Boolean
): Either[BatchChangeErrorResponse, Unit] =
validateAuthorizedReviewer(authPrincipal, batchChange, bypassTestValidation) |+| validateBatchChangePendingReview(
batchChange)
batchChange
)
def validateBatchChangeApproval(
batchChange: BatchChange,
authPrincipal: AuthPrincipal,
isTestChange: Boolean): Either[BatchChangeErrorResponse, Unit] =
isTestChange: Boolean
): Either[BatchChangeErrorResponse, Unit] =
validateAuthorizedReviewer(authPrincipal, batchChange, isTestChange) |+| validateBatchChangePendingReview(
batchChange) |+| validateScheduledApproval(batchChange)
batchChange
) |+| validateScheduledApproval(batchChange)
def validateBatchChangeCancellation(
batchChange: BatchChange,
authPrincipal: AuthPrincipal): Either[BatchChangeErrorResponse, Unit] =
authPrincipal: AuthPrincipal
): Either[BatchChangeErrorResponse, Unit] =
validateBatchChangePendingReview(batchChange) |+| validateCreatorCancellation(
batchChange,
authPrincipal)
authPrincipal
)
def validateBatchChangePendingReview(
batchChange: BatchChange): Either[BatchChangeErrorResponse, Unit] =
batchChange: BatchChange
): Either[BatchChangeErrorResponse, Unit] =
batchChange.approvalStatus match {
case BatchChangeApprovalStatus.PendingReview => ().asRight
case _ => BatchChangeNotPendingReview(batchChange.id).asLeft
@ -145,7 +162,8 @@ class BatchChangeValidations(
def validateAuthorizedReviewer(
auth: AuthPrincipal,
batchChange: BatchChange,
bypassTestValidation: Boolean): Either[BatchChangeErrorResponse, Unit] =
bypassTestValidation: Boolean
): Either[BatchChangeErrorResponse, Unit] =
if (auth.isSystemAdmin && (bypassTestValidation || !auth.isTestUser)) {
// bypassTestValidation = true for a test change
().asRight
@ -161,7 +179,8 @@ class BatchChangeValidations(
def validateCreatorCancellation(
batchChange: BatchChange,
auth: AuthPrincipal): Either[BatchChangeErrorResponse, Unit] =
auth: AuthPrincipal
): Either[BatchChangeErrorResponse, Unit] =
if (batchChange.userId == auth.userId) {
().asRight
} else {
@ -171,7 +190,8 @@ class BatchChangeValidations(
def validateInputChanges(
input: List[ChangeInput],
isApproved: Boolean): ValidatedBatch[ChangeInput] =
isApproved: Boolean
): ValidatedBatch[ChangeInput] =
input.map {
case a: AddChangeInput => validateAddChangeInput(a, isApproved).map(_ => a)
case d: DeleteRRSetChangeInput => validateDeleteRRSetChangeInput(d, isApproved).map(_ => d)
@ -179,7 +199,8 @@ class BatchChangeValidations(
def validateAddChangeInput(
addChangeInput: AddChangeInput,
isApproved: Boolean): SingleValidation[Unit] = {
isApproved: Boolean
): SingleValidation[Unit] = {
val validTTL = addChangeInput.ttl.map(validateTTL(_).asUnit).getOrElse(().valid)
val validRecord = validateRecordData(addChangeInput.record)
val validInput = validateInputName(addChangeInput, isApproved)
@ -189,7 +210,8 @@ class BatchChangeValidations(
def validateDeleteRRSetChangeInput(
deleteRRSetChangeInput: DeleteRRSetChangeInput,
isApproved: Boolean): SingleValidation[Unit] = {
isApproved: Boolean
): SingleValidation[Unit] = {
val validRecord = deleteRRSetChangeInput.record match {
case Some(recordData) => validateRecordData(recordData)
case None => ().validNel
@ -244,7 +266,8 @@ class BatchChangeValidations(
groupedChanges: ChangeForValidationMap,
auth: AuthPrincipal,
isApproved: Boolean,
batchOwnerGroupId: Option[String]): ValidatedBatch[ChangeForValidation] =
batchOwnerGroupId: Option[String]
): ValidatedBatch[ChangeForValidation] =
// Updates are a combination of an add and delete for a record with the same name and type in a zone.
groupedChanges.changes.mapValid {
case add: AddChangeForValidation
@ -274,13 +297,15 @@ class BatchChangeValidations(
def ensureRecordExists(
change: ChangeForValidation,
groupedChanges: ChangeForValidationMap): SingleValidation[Unit] =
groupedChanges: ChangeForValidationMap
): SingleValidation[Unit] =
change match {
// For DeleteRecord inputs, need to verify that the record data actually exists
case DeleteRRSetChangeForValidation(
_,
_,
DeleteRRSetChangeInput(inputName, _, Some(recordData)))
DeleteRRSetChangeInput(inputName, _, Some(recordData))
)
if !groupedChanges
.getExistingRecordSet(change.recordKey)
.exists(_.records.contains(recordData)) =>
@ -293,7 +318,8 @@ class BatchChangeValidations(
change: ChangeForValidation,
groupedChanges: ChangeForValidationMap,
auth: AuthPrincipal,
isApproved: Boolean): SingleValidation[ChangeForValidation] = {
isApproved: Boolean
): SingleValidation[ChangeForValidation] = {
val validations =
groupedChanges.getExistingRecordSet(change.recordKey) match {
@ -311,7 +337,8 @@ class BatchChangeValidations(
groupedChanges: ChangeForValidationMap,
auth: AuthPrincipal,
isApproved: Boolean,
batchOwnerGroupId: Option[String]): SingleValidation[ChangeForValidation] = {
batchOwnerGroupId: Option[String]
): SingleValidation[ChangeForValidation] = {
// Updates require checking against other batch changes since multiple adds
// could potentially be grouped with a single delete
val typedValidations = change.inputChange.typ match {
@ -327,7 +354,8 @@ class BatchChangeValidations(
change,
groupedChanges.existingRecordSets
.get(change.zone.id, change.recordName, change.inputChange.typ),
batchOwnerGroupId) |+|
batchOwnerGroupId
) |+|
zoneDoesNotRequireManualReview(change, isApproved)
case None =>
RecordDoesNotExist(change.inputChange.inputName).invalidNel
@ -343,7 +371,8 @@ class BatchChangeValidations(
change: ChangeForValidation,
groupedChanges: ChangeForValidationMap,
auth: AuthPrincipal,
isApproved: Boolean): SingleValidation[ChangeForValidation] = {
isApproved: Boolean
): SingleValidation[ChangeForValidation] = {
val validations =
groupedChanges.getExistingRecordSet(change.recordKey) match {
case Some(rs) =>
@ -363,7 +392,8 @@ class BatchChangeValidations(
groupedChanges: ChangeForValidationMap,
auth: AuthPrincipal,
isApproved: Boolean,
ownerGroupId: Option[String]): SingleValidation[ChangeForValidation] = {
ownerGroupId: Option[String]
): SingleValidation[ChangeForValidation] = {
val typedValidations = change.inputChange.typ match {
case A | AAAA | MX =>
newRecordSetIsNotDotted(change)
@ -385,7 +415,8 @@ class BatchChangeValidations(
change.recordName,
change.inputChange.inputName,
change.inputChange.typ,
groupedChanges) |+|
groupedChanges
) |+|
ownerGroupProvidedIfNeeded(change, None, ownerGroupId) |+|
zoneDoesNotRequireManualReview(change, isApproved)
@ -394,7 +425,8 @@ class BatchChangeValidations(
def cnameHasUniqueNameInBatch(
cnameChange: AddChangeForValidation,
groupedChanges: ChangeForValidationMap): SingleValidation[Unit] = {
groupedChanges: ChangeForValidationMap
): SingleValidation[Unit] = {
val duplicateNameChangeInBatch = RecordType.values.toList.exists { recordType =>
val recordKey = RecordKey(cnameChange.zone.id, cnameChange.recordName, recordType)
@ -410,7 +442,8 @@ class BatchChangeValidations(
def recordIsUniqueInBatch(
change: AddChangeForValidation,
groupedChanges: ChangeForValidationMap): SingleValidation[Unit] = {
groupedChanges: ChangeForValidationMap
): SingleValidation[Unit] = {
// Ignore true duplicates, but identify multi-records
val proposedAdds = groupedChanges.getProposedAdds(change.recordKey)
@ -424,7 +457,8 @@ class BatchChangeValidations(
recordName: String,
inputName: String,
typ: RecordType,
groupedChanges: ChangeForValidationMap): SingleValidation[Unit] =
groupedChanges: ChangeForValidationMap
): SingleValidation[Unit] =
groupedChanges.getExistingRecordSet(RecordKey(zoneId, recordName, typ)) match {
case Some(_) => RecordAlreadyExists(inputName).invalidNel
case None => ().validNel
@ -432,7 +466,8 @@ class BatchChangeValidations(
def noIncompatibleRecordExists(
change: AddChangeForValidation,
groupedChanges: ChangeForValidationMap): SingleValidation[Unit] = {
groupedChanges: ChangeForValidationMap
): SingleValidation[Unit] = {
// find conflicting types in existing records
val conflictingExistingTypes = change.inputChange.typ match {
case CNAME =>
@ -462,13 +497,15 @@ class BatchChangeValidations(
def userCanAddRecordSet(
input: AddChangeForValidation,
authPrincipal: AuthPrincipal): SingleValidation[Unit] = {
authPrincipal: AuthPrincipal
): SingleValidation[Unit] = {
val result = canAddRecordSet(
authPrincipal,
input.recordName,
input.inputChange.typ,
input.zone,
List(input.inputChange.record))
List(input.inputChange.record)
)
result
.leftMap(
_ =>
@ -476,7 +513,9 @@ class BatchChangeValidations(
authPrincipal.signedInUser.userName,
input.zone.adminGroupId,
OwnerType.Zone,
Some(input.zone.email)))
Some(input.zone.email)
)
)
.toValidatedNel
}
@ -484,7 +523,8 @@ class BatchChangeValidations(
input: ChangeForValidation,
authPrincipal: AuthPrincipal,
ownerGroupId: Option[String],
addRecords: List[RecordData]): SingleValidation[Unit] = {
addRecords: List[RecordData]
): SingleValidation[Unit] = {
val result =
canUpdateRecordSet(
authPrincipal,
@ -495,17 +535,20 @@ class BatchChangeValidations(
addRecords
)
result
.leftMap(_ =>
ownerGroupId match {
case Some(id) if input.zone.shared =>
UserIsNotAuthorizedError(authPrincipal.signedInUser.userName, id, OwnerType.Record)
case _ =>
UserIsNotAuthorizedError(
authPrincipal.signedInUser.userName,
input.zone.adminGroupId,
OwnerType.Zone,
Some(input.zone.email))
})
.leftMap(
_ =>
ownerGroupId match {
case Some(id) if input.zone.shared =>
UserIsNotAuthorizedError(authPrincipal.signedInUser.userName, id, OwnerType.Record)
case _ =>
UserIsNotAuthorizedError(
authPrincipal.signedInUser.userName,
input.zone.adminGroupId,
OwnerType.Zone,
Some(input.zone.email)
)
}
)
.toValidatedNel
}
@ -513,7 +556,8 @@ class BatchChangeValidations(
input: ChangeForValidation,
authPrincipal: AuthPrincipal,
ownerGroupId: Option[String],
existingRecords: List[RecordData]): SingleValidation[Unit] = {
existingRecords: List[RecordData]
): SingleValidation[Unit] = {
val result =
canDeleteRecordSet(
authPrincipal,
@ -524,23 +568,27 @@ class BatchChangeValidations(
existingRecords
)
result
.leftMap(_ =>
ownerGroupId match {
case Some(id) if input.zone.shared =>
UserIsNotAuthorizedError(authPrincipal.signedInUser.userName, id, OwnerType.Record)
case _ =>
UserIsNotAuthorizedError(
authPrincipal.signedInUser.userName,
input.zone.adminGroupId,
OwnerType.Zone,
Some(input.zone.email))
})
.leftMap(
_ =>
ownerGroupId match {
case Some(id) if input.zone.shared =>
UserIsNotAuthorizedError(authPrincipal.signedInUser.userName, id, OwnerType.Record)
case _ =>
UserIsNotAuthorizedError(
authPrincipal.signedInUser.userName,
input.zone.adminGroupId,
OwnerType.Zone,
Some(input.zone.email)
)
}
)
.toValidatedNel
}
def canGetBatchChange(
batchChange: BatchChange,
auth: AuthPrincipal): Either[BatchChangeErrorResponse, Unit] =
auth: AuthPrincipal
): Either[BatchChangeErrorResponse, Unit] =
if (auth.isSystemAdmin || auth.userId == batchChange.userId) {
().asRight
} else {
@ -554,7 +602,8 @@ class BatchChangeValidations(
case _ =>
ZoneRecordValidations.isNotHighValueFqdn(
VinylDNSConfig.highValueRegexList,
change.inputName)
change.inputName
)
}
def doesNotRequireManualReview(change: ChangeInput, isApproved: Boolean): SingleValidation[Unit] =
@ -566,18 +615,21 @@ class BatchChangeValidations(
case RecordType.PTR =>
ZoneRecordValidations.ipDoesNotRequireManualReview(
VinylDNSConfig.ipListRequiringManualReview,
change.inputName)
change.inputName
)
case _ =>
ZoneRecordValidations.domainDoesNotRequireManualReview(
VinylDNSConfig.domainListRequiringManualReview,
change.inputName)
change.inputName
)
}
}
def ownerGroupProvidedIfNeeded(
change: AddChangeForValidation,
existingRecord: Option[RecordSet],
ownerGroupId: Option[String]): SingleValidation[Unit] =
ownerGroupId: Option[String]
): SingleValidation[Unit] =
if (!change.zone.shared || ownerGroupId.isDefined) {
().validNel
} else {
@ -591,7 +643,8 @@ class BatchChangeValidations(
def validateScheduledChange(
input: BatchChangeInput,
scheduledChangesEnabled: Boolean): Either[BatchChangeErrorResponse, Unit] =
scheduledChangesEnabled: Boolean
): Either[BatchChangeErrorResponse, Unit] =
(scheduledChangesEnabled, input.scheduledTime) match {
case (_, None) => Right(())
case (true, Some(scheduledTime)) if scheduledTime.isAfterNow => Right(())
@ -601,7 +654,8 @@ class BatchChangeValidations(
def zoneDoesNotRequireManualReview(
change: ChangeForValidation,
isApproved: Boolean): SingleValidation[Unit] =
isApproved: Boolean
): SingleValidation[Unit] =
if (isApproved) {
().validNel
} else {

View File

@ -94,8 +94,8 @@ object BatchTransformations {
zone: Zone,
recordName: String,
inputChange: AddChangeInput,
existingRecordTtl: Option[Long] = None)
extends ChangeForValidation {
existingRecordTtl: Option[Long] = None
) extends ChangeForValidation {
def asStoredChange(changeId: Option[String] = None): SingleChange = {
val ttl = inputChange.ttl.orElse(existingRecordTtl).getOrElse(VinylDNSConfig.defaultTtl)
@ -123,8 +123,8 @@ object BatchTransformations {
final case class DeleteRRSetChangeForValidation(
zone: Zone,
recordName: String,
inputChange: DeleteRRSetChangeInput)
extends ChangeForValidation {
inputChange: DeleteRRSetChangeInput
) extends ChangeForValidation {
def asStoredChange(changeId: Option[String] = None): SingleChange =
SingleDeleteRRSetChange(
Some(zone.id),
@ -146,11 +146,13 @@ object BatchTransformations {
final case class BatchConversionOutput(
batchChange: BatchChange,
recordSetChanges: List[RecordSetChange])
recordSetChanges: List[RecordSetChange]
)
final case class ChangeForValidationMap(
changes: ValidatedBatch[ChangeForValidation],
existingRecordSets: ExistingRecordSets) {
existingRecordSets: ExistingRecordSets
) {
import BatchChangeInterfaces._
val innerMap: Map[RecordKey, ValidationChanges] = {
@ -182,7 +184,8 @@ object BatchTransformations {
object ValidationChanges {
def apply(
changes: List[ChangeForValidation],
existingRecordSet: Option[RecordSet]): ValidationChanges = {
existingRecordSet: Option[RecordSet]
): ValidationChanges = {
// Collect add DNS entries
val addChangeRecordDataSet = changes.collect {
case add: AddChangeForValidation => add.inputChange.record
@ -197,7 +200,8 @@ object BatchTransformations {
case DeleteRRSetChangeForValidation(
_,
_,
DeleteRRSetChangeInput(_, _, Some(recordData))) =>
DeleteRRSetChangeInput(_, _, Some(recordData))
) =>
Set(recordData)
case _: DeleteRRSetChangeForValidation => existingRecords
}
@ -228,7 +232,8 @@ object BatchTransformations {
final case class ValidationChanges(
proposedAdds: Set[RecordData],
proposedRecordData: Set[RecordData],
logicalChangeType: LogicalChangeType)
logicalChangeType: LogicalChangeType
)
final case class BatchValidationFlowOutput(
validatedChanges: ValidatedBatch[ChangeForValidation],

View File

@ -107,7 +107,8 @@ class DnsConnection(val resolver: DNS.SimpleResolver) extends DnsConversions {
private[dns] def toQuery(
name: String,
zoneName: String,
typ: RecordType): Either[Throwable, DnsQuery] = {
typ: RecordType
): Either[Throwable, DnsQuery] = {
val dnsName = recordDnsName(name, zoneName)
logger.info(s"Querying for dns dnsRecordName='${dnsName.toString}'; recordType='$typ'")
val lookup = new DNS.Lookup(dnsName, toDnsRecordType(typ))
@ -160,7 +161,8 @@ class DnsConnection(val resolver: DNS.SimpleResolver) extends DnsConversions {
} yield resp
logger.info(
s"DnsConnection.send - Sending DNS Message ${obscuredDnsMessage(msg).toString}\n...received response $result")
s"DnsConnection.send - Sending DNS Message ${obscuredDnsMessage(msg).toString}\n...received response $result"
)
result
}

View File

@ -177,7 +177,8 @@ trait DnsConversions {
def toFlattenedRecordSets(
records: List[DNS.Record],
zoneName: DNS.Name,
zoneId: String = "unknown"): List[RecordSet] = {
zoneId: String = "unknown"
): List[RecordSet] = {
/* Combines record sets into a list of one or Nil in case there are no record sets in the list provided */
def combineRecordSets(lst: List[RecordSet]): RecordSet =
@ -194,7 +195,8 @@ trait DnsConversions {
// Do a "relativize" using the zoneName, this removes the zone name from the record itself
// For example "test-01.vinyldns." becomes "test-01"...this is necessary as we want to run comparisons upstream
def fromDnsRecord[A <: DNS.Record](r: A, zoneName: DNS.Name, zoneId: String)(
f: A => List[RecordData]): RecordSet =
f: A => List[RecordData]
): RecordSet =
record.RecordSet(
zoneId = zoneId,
name = relativize(r.getName, zoneName),
@ -240,7 +242,8 @@ trait DnsConversions {
DnsSecAlgorithm(data.getAlgorithm),
DigestType(data.getDigestID),
ByteVector(data.getDigest)
))
)
)
}
def fromMXRecord(r: DNS.MXRecord, zoneName: DNS.Name, zoneId: String): RecordSet =
@ -268,7 +271,9 @@ trait DnsConversions {
data.getRefresh,
data.getRetry,
data.getExpire,
data.getMinimum))
data.getMinimum
)
)
}
def fromSPFRecord(r: DNS.SPFRecord, zoneName: DNS.Name, zoneId: String): RecordSet =
@ -290,7 +295,9 @@ trait DnsConversions {
data.getFlags,
data.getService,
data.getRegexp,
data.getReplacement.toString))
data.getReplacement.toString
)
)
}
def fromSSHFPRecord(r: DNS.SSHFPRecord, zoneName: DNS.Name, zoneId: String): RecordSet =
@ -326,7 +333,8 @@ trait DnsConversions {
keyTag,
algorithm.value,
digestType.value,
digest.toArray)
digest.toArray
)
case NSData(nsdname) =>
new DNS.NSRecord(recordName, DNS.DClass.IN, ttl, DNS.Name.fromString(nsdname))
@ -337,7 +345,8 @@ trait DnsConversions {
DNS.DClass.IN,
ttl,
preference,
DNS.Name.fromString(exchange))
DNS.Name.fromString(exchange)
)
case PTRData(ptrdname) =>
new DNS.PTRRecord(recordName, DNS.DClass.IN, ttl, DNS.Name.fromString(ptrdname))
@ -353,7 +362,8 @@ trait DnsConversions {
refresh,
retry,
expire,
minimum)
minimum
)
case SRVData(priority, weight, port, target) =>
new DNS.SRVRecord(
@ -363,7 +373,8 @@ trait DnsConversions {
priority,
weight,
port,
DNS.Name.fromString(target))
DNS.Name.fromString(target)
)
case NAPTRData(order, preference, flags, service, regexp, replacement) =>
new DNS.NAPTRRecord(
@ -375,7 +386,8 @@ trait DnsConversions {
flags,
service,
regexp,
DNS.Name.fromString(replacement))
DNS.Name.fromString(replacement)
)
case SSHFPData(algorithm, typ, fingerprint) =>
new DNS.SSHFPRecord(recordName, DNS.DClass.IN, ttl, algorithm, typ, fingerprint.getBytes)
@ -422,7 +434,8 @@ trait DnsConversions {
def toUpdateRecordMessage(
r: DNS.RRset,
old: DNS.RRset,
zoneName: String): Either[Throwable, DNS.Update] = {
zoneName: String
): Either[Throwable, DNS.Update] = {
val update = new DNS.Update(zoneDnsName(zoneName))
if (!r.getName.equals(old.getName) || r.getTTL != old.getTTL) { // Name or TTL has changed

View File

@ -119,13 +119,15 @@ final case class ListMembersResponse(
members: Seq[MemberInfo],
startFrom: Option[String] = None,
nextId: Option[String] = None,
maxItems: Int)
maxItems: Int
)
final case class ListUsersResponse(
members: Seq[UserInfo],
startFrom: Option[String] = None,
nextId: Option[String] = None,
maxItems: Int)
maxItems: Int
)
final case class ListAdminsResponse(admins: Seq[UserInfo])
@ -133,7 +135,8 @@ final case class ListGroupChangesResponse(
changes: Seq[GroupChangeInfo],
startFrom: Option[String] = None,
nextId: Option[String] = None,
maxItems: Int)
maxItems: Int
)
final case class ListMyGroupsResponse(
groups: Seq[GroupInfo],
@ -141,7 +144,8 @@ final case class ListMyGroupsResponse(
startFrom: Option[String] = None,
nextId: Option[String] = None,
maxItems: Int,
ignoreAccess: Boolean)
ignoreAccess: Boolean
)
final case class GroupNotFoundError(msg: String) extends Throwable(msg)

View File

@ -43,8 +43,8 @@ class MembershipService(
membershipRepo: MembershipRepository,
zoneRepo: ZoneRepository,
groupChangeRepo: GroupChangeRepository,
recordSetRepo: RecordSetRepository)
extends MembershipServiceAlgebra {
recordSetRepo: RecordSetRepository
) extends MembershipServiceAlgebra {
import MembershipValidations._
@ -67,7 +67,8 @@ class MembershipService(
description: Option[String],
memberIds: Set[String],
adminUserIds: Set[String],
authPrincipal: AuthPrincipal): Result[Group] =
authPrincipal: AuthPrincipal
): Result[Group] =
for {
existingGroup <- getExistingGroup(groupId)
newGroup = existingGroup.withUpdates(name, email, description, memberIds, adminUserIds)
@ -112,17 +113,18 @@ class MembershipService(
groupId: String,
startFrom: Option[String],
maxItems: Int,
authPrincipal: AuthPrincipal): Result[ListMembersResponse] =
authPrincipal: AuthPrincipal
): Result[ListMembersResponse] =
for {
group <- getExistingGroup(groupId)
_ <- canSeeGroup(groupId, authPrincipal).toResult
result <- getUsers(group.memberIds, startFrom, Some(maxItems))
} yield
ListMembersResponse(
result.users.map(MemberInfo(_, group)),
startFrom,
result.lastEvaluatedId,
maxItems)
} yield ListMembersResponse(
result.users.map(MemberInfo(_, group)),
startFrom,
result.lastEvaluatedId,
maxItems
)
def listAdmins(groupId: String, authPrincipal: AuthPrincipal): Result[ListAdminsResponse] =
for {
@ -136,7 +138,8 @@ class MembershipService(
startFrom: Option[String],
maxItems: Int,
authPrincipal: AuthPrincipal,
ignoreAccess: Boolean): Result[ListMyGroupsResponse] = {
ignoreAccess: Boolean
): Result[ListMyGroupsResponse] = {
val groupsCall =
if (authPrincipal.isSystemAdmin || ignoreAccess) {
groupRepo.getAllGroups()
@ -154,7 +157,8 @@ class MembershipService(
groupNameFilter: Option[String],
startFrom: Option[String],
maxItems: Int,
ignoreAccess: Boolean): ListMyGroupsResponse = {
ignoreAccess: Boolean
): ListMyGroupsResponse = {
val allMyGroups = allGroups
.filter(_.status == GroupStatus.Active)
.sortBy(_.id)
@ -174,23 +178,25 @@ class MembershipService(
groupId: String,
startFrom: Option[String],
maxItems: Int,
authPrincipal: AuthPrincipal): Result[ListGroupChangesResponse] =
authPrincipal: AuthPrincipal
): Result[ListGroupChangesResponse] =
for {
_ <- canSeeGroup(groupId, authPrincipal).toResult
result <- groupChangeRepo
.getGroupChanges(groupId, startFrom, maxItems)
.toResult[ListGroupChangesResults]
} yield
ListGroupChangesResponse(
result.changes.map(GroupChangeInfo.apply),
startFrom,
result.lastEvaluatedTimeStamp,
maxItems)
} yield ListGroupChangesResponse(
result.changes.map(GroupChangeInfo.apply),
startFrom,
result.lastEvaluatedTimeStamp,
maxItems
)
def getUsers(
userIds: Set[String],
startFrom: Option[String] = None,
pageSize: Option[Int] = None): Result[ListUsersResults] =
pageSize: Option[Int] = None
): Result[ListUsersResults] =
userRepo
.getUsers(userIds, startFrom, pageSize)
.toResult[ListUsersResults]
@ -245,7 +251,8 @@ class MembershipService(
.getZonesByAdminGroupId(group.id)
.map { zones =>
ensuring(InvalidGroupRequestError(s"${group.name} is the admin of a zone. Cannot delete."))(
zones.isEmpty)
zones.isEmpty
)
}
.toResult
@ -255,8 +262,9 @@ class MembershipService(
.map { rsId =>
ensuring(
InvalidGroupRequestError(
s"${group.name} is the owner for a record set including $rsId. Cannot delete."))(
rsId.isEmpty)
s"${group.name} is the owner for a record set including $rsId. Cannot delete."
)
)(rsId.isEmpty)
}
.toResult
@ -264,15 +272,19 @@ class MembershipService(
zoneRepo
.getFirstOwnedZoneAclGroupId(group.id)
.map { zId =>
ensuring(InvalidGroupRequestError(
s"${group.name} has an ACL rule for a zone including $zId. Cannot delete."))(zId.isEmpty)
ensuring(
InvalidGroupRequestError(
s"${group.name} has an ACL rule for a zone including $zId. Cannot delete."
)
)(zId.isEmpty)
}
.toResult
def updateUserLockStatus(
userId: String,
lockStatus: LockStatus,
authPrincipal: AuthPrincipal): Result[User] =
authPrincipal: AuthPrincipal
): Result[User] =
for {
_ <- isSuperAdmin(authPrincipal).toResult
existingUser <- getExistingUser(userId)

View File

@ -32,7 +32,8 @@ trait MembershipServiceAlgebra {
description: Option[String],
memberIds: Set[String],
adminUserIds: Set[String],
authPrincipal: AuthPrincipal): Result[Group]
authPrincipal: AuthPrincipal
): Result[Group]
def deleteGroup(groupId: String, authPrincipal: AuthPrincipal): Result[Group]
@ -43,13 +44,15 @@ trait MembershipServiceAlgebra {
startFrom: Option[String],
maxItems: Int,
authPrincipal: AuthPrincipal,
ignoreAccess: Boolean): Result[ListMyGroupsResponse]
ignoreAccess: Boolean
): Result[ListMyGroupsResponse]
def listMembers(
groupId: String,
startFrom: Option[String],
maxItems: Int,
authPrincipal: AuthPrincipal): Result[ListMembersResponse]
authPrincipal: AuthPrincipal
): Result[ListMembersResponse]
def listAdmins(groupId: String, authPrincipal: AuthPrincipal): Result[ListAdminsResponse]
@ -57,10 +60,12 @@ trait MembershipServiceAlgebra {
groupId: String,
startFrom: Option[String],
maxItems: Int,
authPrincipal: AuthPrincipal): Result[ListGroupChangesResponse]
authPrincipal: AuthPrincipal
): Result[ListGroupChangesResponse]
def updateUserLockStatus(
userId: String,
lockStatus: LockStatus,
authPrincipal: AuthPrincipal): Result[User]
authPrincipal: AuthPrincipal
): Result[User]
}

View File

@ -24,17 +24,20 @@ case class ListRecordSetChangesResponse(
recordSetChanges: List[RecordSetChangeInfo] = Nil,
nextId: Option[String],
startFrom: Option[String],
maxItems: Int)
maxItems: Int
)
object ListRecordSetChangesResponse {
def apply(
zoneId: String,
listResults: ListRecordSetChangesResults,
info: List[RecordSetChangeInfo]): ListRecordSetChangesResponse =
info: List[RecordSetChangeInfo]
): ListRecordSetChangesResponse =
ListRecordSetChangesResponse(
zoneId,
info,
listResults.nextId,
listResults.startFrom,
listResults.maxItems)
listResults.maxItems
)
}

View File

@ -30,7 +30,8 @@ object RecordSetChangeGenerator extends DnsConversions {
recordSet: RecordSet,
zone: Zone,
userId: String,
singleBatchChangeIds: List[String]): RecordSetChange =
singleBatchChangeIds: List[String]
): RecordSetChange =
RecordSetChange(
zone = zone,
recordSet = recordSet.copy(
@ -48,7 +49,8 @@ object RecordSetChangeGenerator extends DnsConversions {
def forAdd(
recordSet: RecordSet,
zone: Zone,
auth: Option[AuthPrincipal] = None): RecordSetChange =
auth: Option[AuthPrincipal] = None
): RecordSetChange =
forAdd(recordSet, zone, auth.map(_.userId).getOrElse("system"), List())
def forAdd(recordSet: RecordSet, zone: Zone, auth: AuthPrincipal): RecordSetChange =
@ -59,14 +61,16 @@ object RecordSetChangeGenerator extends DnsConversions {
newRecordSet: RecordSet,
zone: Zone,
userId: String,
singleBatchChangeIds: List[String]): RecordSetChange =
singleBatchChangeIds: List[String]
): RecordSetChange =
RecordSetChange(
zone = zone,
recordSet = newRecordSet.copy(
id = replacing.id,
name = relativize(newRecordSet.name, zone.name),
status = RecordSetStatus.PendingUpdate,
updated = Some(DateTime.now)),
updated = Some(DateTime.now)
),
userId = userId,
changeType = RecordSetChangeType.Update,
status = RecordSetChangeStatus.Pending,
@ -78,21 +82,24 @@ object RecordSetChangeGenerator extends DnsConversions {
replacing: RecordSet,
newRecordSet: RecordSet,
zone: Zone,
auth: Option[AuthPrincipal] = None): RecordSetChange =
auth: Option[AuthPrincipal] = None
): RecordSetChange =
forUpdate(replacing, newRecordSet, zone, auth.map(_.userId).getOrElse("system"), List())
def forUpdate(
replacing: RecordSet,
newRecordSet: RecordSet,
zone: Zone,
auth: AuthPrincipal): RecordSetChange =
auth: AuthPrincipal
): RecordSetChange =
forUpdate(replacing, newRecordSet, zone, auth.userId, List())
def forDelete(
recordSet: RecordSet,
zone: Zone,
userId: String,
singleBatchChangeIds: List[String]): RecordSetChange =
singleBatchChangeIds: List[String]
): RecordSetChange =
RecordSetChange(
zone = zone,
recordSet = recordSet.copy(
@ -109,7 +116,8 @@ object RecordSetChangeGenerator extends DnsConversions {
def forDelete(
recordSet: RecordSet,
zone: Zone,
auth: Option[AuthPrincipal] = None): RecordSetChange =
auth: Option[AuthPrincipal] = None
): RecordSetChange =
forDelete(recordSet, zone, auth.map(_.userId).getOrElse("system"), List())
def forDelete(recordSet: RecordSet, zone: Zone, auth: AuthPrincipal): RecordSetChange =

View File

@ -33,7 +33,8 @@ object RecordSetService {
def apply(
dataAccessor: ApiDataAccessor,
messageQueue: MessageQueue,
accessValidation: AccessValidationsAlgebra): RecordSetService =
accessValidation: AccessValidationsAlgebra
): RecordSetService =
new RecordSetService(
dataAccessor.zoneRepository,
dataAccessor.groupRepository,
@ -52,8 +53,8 @@ class RecordSetService(
recordChangeRepository: RecordChangeRepository,
userRepository: UserRepository,
messageQueue: MessageQueue,
accessValidation: AccessValidationsAlgebra)
extends RecordSetServiceAlgebra {
accessValidation: AccessValidationsAlgebra
) extends RecordSetServiceAlgebra {
import RecordSetValidations._
import accessValidation._
@ -106,7 +107,8 @@ class RecordSetService(
def deleteRecordSet(
recordSetId: String,
zoneId: String,
auth: AuthPrincipal): Result[ZoneCommandResult] =
auth: AuthPrincipal
): Result[ZoneCommandResult] =
for {
zone <- getZone(zoneId)
existing <- getRecordSet(recordSetId, zone)
@ -121,7 +123,8 @@ class RecordSetService(
def getRecordSet(
recordSetId: String,
zoneId: String,
authPrincipal: AuthPrincipal): Result[RecordSetInfo] =
authPrincipal: AuthPrincipal
): Result[RecordSetInfo] =
for {
zone <- getZone(zoneId)
recordSet <- getRecordSet(recordSetId, zone)
@ -130,7 +133,8 @@ class RecordSetService(
recordSet.name,
recordSet.typ,
zone,
recordSet.ownerGroupId).toResult
recordSet.ownerGroupId
).toResult
groupName <- getGroupName(recordSet.ownerGroupId)
} yield RecordSetInfo(recordSet, groupName)
@ -139,7 +143,8 @@ class RecordSetService(
startFrom: Option[String],
maxItems: Option[Int],
recordNameFilter: Option[String],
authPrincipal: AuthPrincipal): Result[ListRecordSetsResponse] =
authPrincipal: AuthPrincipal
): Result[ListRecordSetsResponse] =
for {
zone <- getZone(zoneId)
_ <- canSeeZone(authPrincipal, zone).toResult
@ -150,39 +155,44 @@ class RecordSetService(
rsGroups <- groupRepository.getGroups(rsOwnerGroupIds).toResult[Set[Group]]
setsWithGroupName = getListWithGroupNames(recordSetResults.recordSets, rsGroups)
setsWithAccess <- getListAccessLevels(authPrincipal, setsWithGroupName, zone).toResult
} yield
ListRecordSetsResponse(
setsWithAccess,
recordSetResults.startFrom,
recordSetResults.nextId,
recordSetResults.maxItems,
recordSetResults.recordNameFilter)
} yield ListRecordSetsResponse(
setsWithAccess,
recordSetResults.startFrom,
recordSetResults.nextId,
recordSetResults.maxItems,
recordSetResults.recordNameFilter
)
def getRecordSetChange(
zoneId: String,
changeId: String,
authPrincipal: AuthPrincipal): Result[RecordSetChange] =
authPrincipal: AuthPrincipal
): Result[RecordSetChange] =
for {
zone <- getZone(zoneId)
change <- recordChangeRepository
.getRecordSetChange(zone.id, changeId)
.orFail(
RecordSetChangeNotFoundError(
s"Unable to find record set change with id $changeId in zone ${zone.name}"))
s"Unable to find record set change with id $changeId in zone ${zone.name}"
)
)
.toResult[RecordSetChange]
_ <- canViewRecordSet(
authPrincipal,
change.recordSet.name,
change.recordSet.typ,
zone,
change.recordSet.ownerGroupId).toResult
change.recordSet.ownerGroupId
).toResult
} yield change
def listRecordSetChanges(
zoneId: String,
startFrom: Option[String] = None,
maxItems: Int = 100,
authPrincipal: AuthPrincipal): Result[ListRecordSetChangesResponse] =
authPrincipal: AuthPrincipal
): Result[ListRecordSetChangesResponse] =
for {
zone <- getZone(zoneId)
_ <- canSeeZone(authPrincipal, zone).toResult
@ -203,7 +213,9 @@ class RecordSetService(
.getRecordSet(zone.id, recordsetId)
.orFail(
RecordSetNotFoundError(
s"RecordSet with id $recordsetId does not exist in zone ${zone.name}"))
s"RecordSet with id $recordsetId does not exist in zone ${zone.name}"
)
)
.toResult[RecordSet]
def recordSetDoesNotExist(recordSet: RecordSet, zone: Zone): Result[Unit] =
@ -215,18 +227,22 @@ class RecordSetService(
Left(
RecordSetAlreadyExists(
s"RecordSet with name ${recordSet.name} and type ${recordSet.typ} already " +
s"exists in zone ${zone.name}"))
s"exists in zone ${zone.name}"
)
)
}
.toResult
def buildRecordSetChangeInfo(
changes: List[RecordSetChange]): Result[List[RecordSetChangeInfo]] = {
changes: List[RecordSetChange]
): Result[List[RecordSetChangeInfo]] = {
val userIds = changes.map(_.userId).toSet
for {
users <- userRepository.getUsers(userIds, None, None).map(_.users).toResult[Seq[User]]
userMap = users.map(u => (u.id, u.userName)).toMap
recordSetChangesInfo = changes.map(change =>
RecordSetChangeInfo(change, userMap.get(change.userId)))
recordSetChangesInfo = changes.map(
change => RecordSetChangeInfo(change, userMap.get(change.userId))
)
} yield recordSetChangesInfo
}

View File

@ -31,29 +31,34 @@ trait RecordSetServiceAlgebra {
def deleteRecordSet(
recordSetId: String,
zoneId: String,
auth: AuthPrincipal): Result[ZoneCommandResult]
auth: AuthPrincipal
): Result[ZoneCommandResult]
def getRecordSet(
recordSetId: String,
zoneId: String,
authPrincipal: AuthPrincipal): Result[RecordSetInfo]
authPrincipal: AuthPrincipal
): Result[RecordSetInfo]
def listRecordSets(
zoneId: String,
startFrom: Option[String],
maxItems: Option[Int],
recordNameFilter: Option[String],
authPrincipal: AuthPrincipal): Result[ListRecordSetsResponse]
authPrincipal: AuthPrincipal
): Result[ListRecordSetsResponse]
def getRecordSetChange(
zoneId: String,
changeId: String,
authPrincipal: AuthPrincipal): Result[RecordSetChange]
authPrincipal: AuthPrincipal
): Result[RecordSetChange]
def listRecordSetChanges(
zoneId: String,
startFrom: Option[String],
maxItems: Int,
authPrincipal: AuthPrincipal): Result[ListRecordSetChangesResponse]
authPrincipal: AuthPrincipal
): Result[ListRecordSetChangesResponse]
}

View File

@ -38,7 +38,8 @@ object RecordSetValidations {
ensuring(InvalidRequest("PTR is not valid in forward lookup zone"))(zone.isReverse)
case _ =>
ensuring(InvalidRequest(s"${recordSet.typ} is not valid in reverse lookup zone."))(
!zone.isReverse)
!zone.isReverse
)
}
def validRecordNameLength(recordSet: RecordSet, zone: Zone): Either[Throwable, Unit] = {
@ -52,38 +53,51 @@ object RecordSetValidations {
ensuring(
PendingUpdateError(
s"RecordSet with id ${recordSet.id}, name ${recordSet.name} and type ${recordSet.typ} " +
s"currently has a pending change"))(
s"currently has a pending change"
)
)(
!recordSet.isPending
)
def noCnameWithNewName(
newRecordSet: RecordSet,
existingRecordsWithName: List[RecordSet],
zone: Zone): Either[Throwable, Unit] =
zone: Zone
): Either[Throwable, Unit] =
ensuring(
RecordSetAlreadyExists(s"RecordSet with name ${newRecordSet.name} and type CNAME already " +
s"exists in zone ${zone.name}"))(
RecordSetAlreadyExists(
s"RecordSet with name ${newRecordSet.name} and type CNAME already " +
s"exists in zone ${zone.name}"
)
)(
!existingRecordsWithName.exists(rs => rs.id != newRecordSet.id && rs.typ == CNAME)
)
def isUniqueUpdate(
newRecordSet: RecordSet,
existingRecordsWithName: List[RecordSet],
zone: Zone): Either[Throwable, Unit] =
zone: Zone
): Either[Throwable, Unit] =
ensuring(
RecordSetAlreadyExists(
s"RecordSet with name ${newRecordSet.name} and type ${newRecordSet.typ} already " +
s"exists in zone ${zone.name}"))(
s"exists in zone ${zone.name}"
)
)(
!existingRecordsWithName.exists(rs => rs.id != newRecordSet.id && rs.typ == newRecordSet.typ)
)
def isNotDotted(
newRecordSet: RecordSet,
zone: Zone,
existingRecordSet: Option[RecordSet] = None): Either[Throwable, Unit] =
ensuring(InvalidRequest(
s"Record with name ${newRecordSet.name} and type ${newRecordSet.typ} is a dotted host which" +
s" is not allowed in zone ${zone.name}"))(
existingRecordSet: Option[RecordSet] = None
): Either[Throwable, Unit] =
ensuring(
InvalidRequest(
s"Record with name ${newRecordSet.name} and type ${newRecordSet.typ} is a dotted host which" +
s" is not allowed in zone ${zone.name}"
)
)(
newRecordSet.name == zone.name || !newRecordSet.name.contains(".") ||
existingRecordSet.exists(_.name == newRecordSet.name)
)
@ -92,7 +106,8 @@ object RecordSetValidations {
newRecordSet: RecordSet,
existingRecordsWithName: List[RecordSet],
zone: Zone,
existingRecordSet: Option[RecordSet] = None): Either[Throwable, Unit] =
existingRecordSet: Option[RecordSet] = None
): Either[Throwable, Unit] =
newRecordSet.typ match {
case CNAME => cnameValidations(newRecordSet, existingRecordsWithName, zone, existingRecordSet)
case NS => nsValidations(newRecordSet, zone, existingRecordSet)
@ -110,7 +125,8 @@ object RecordSetValidations {
isNotOrigin(
recordSet,
zone,
s"Record with name ${recordSet.name} is an NS record at apex and cannot be edited")
s"Record with name ${recordSet.name} is an NS record at apex and cannot be edited"
)
case SOA => InvalidRequest("SOA records cannot be deleted").asLeft
case _ => ().asRight
}
@ -120,12 +136,16 @@ object RecordSetValidations {
newRecordSet: RecordSet,
existingRecordsWithName: List[RecordSet],
zone: Zone,
existingRecordSet: Option[RecordSet] = None): Either[Throwable, Unit] = {
existingRecordSet: Option[RecordSet] = None
): Either[Throwable, Unit] = {
// cannot create a cname record if a record with the same exists
val noRecordWithName = {
ensuring(
RecordSetAlreadyExists(s"RecordSet with name ${newRecordSet.name} already " +
s"exists in zone ${zone.name}, CNAME record cannot use duplicate name"))(
RecordSetAlreadyExists(
s"RecordSet with name ${newRecordSet.name} already " +
s"exists in zone ${zone.name}, CNAME record cannot use duplicate name"
)
)(
existingRecordsWithName.forall(_.id == newRecordSet.id)
)
}
@ -134,7 +154,8 @@ object RecordSetValidations {
_ <- isNotOrigin(
newRecordSet,
zone,
"CNAME RecordSet cannot have name '@' because it points to zone origin")
"CNAME RecordSet cannot have name '@' because it points to zone origin"
)
_ <- noRecordWithName
_ <- isNotDotted(newRecordSet, zone, existingRecordSet)
} yield ()
@ -144,17 +165,20 @@ object RecordSetValidations {
def dsValidations(
newRecordSet: RecordSet,
existingRecordsWithName: List[RecordSet],
zone: Zone): Either[Throwable, Unit] = {
zone: Zone
): Either[Throwable, Unit] = {
// see https://tools.ietf.org/html/rfc4035#section-2.4
val nsChecks = existingRecordsWithName.find(_.typ == NS) match {
case Some(ns) if ns.ttl == newRecordSet.ttl => ().asRight
case Some(ns) =>
InvalidRequest(
s"DS record [${newRecordSet.name}] must have TTL matching its linked NS (${ns.ttl})").asLeft
s"DS record [${newRecordSet.name}] must have TTL matching its linked NS (${ns.ttl})"
).asLeft
case None =>
InvalidRequest(
s"DS record [${newRecordSet.name}] is invalid because there is no NS record with that " +
s"name in the zone [${zone.name}]").asLeft
s"name in the zone [${zone.name}]"
).asLeft
}
for {
@ -162,7 +186,8 @@ object RecordSetValidations {
_ <- isNotOrigin(
newRecordSet,
zone,
s"Record with name [${newRecordSet.name}] is an DS record at apex and cannot be added")
s"Record with name [${newRecordSet.name}] is an DS record at apex and cannot be added"
)
_ <- nsChecks
} yield ()
}
@ -170,7 +195,8 @@ object RecordSetValidations {
def nsValidations(
newRecordSet: RecordSet,
zone: Zone,
oldRecordSet: Option[RecordSet] = None): Either[Throwable, Unit] = {
oldRecordSet: Option[RecordSet] = None
): Either[Throwable, Unit] = {
// TODO kept consistency with old validation. Not sure why NS could be dotted in reverse specifically
val isNotDottedHost = if (!zone.isReverse) isNotDotted(newRecordSet, zone) else ().asRight
@ -179,14 +205,16 @@ object RecordSetValidations {
_ <- isNotOrigin(
newRecordSet,
zone,
s"Record with name ${newRecordSet.name} is an NS record at apex and cannot be added")
s"Record with name ${newRecordSet.name} is an NS record at apex and cannot be added"
)
_ <- containsApprovedNameServers(newRecordSet)
_ <- oldRecordSet
.map { rs =>
isNotOrigin(
rs,
zone,
s"Record with name ${newRecordSet.name} is an NS record at apex and cannot be edited")
s"Record with name ${newRecordSet.name} is an NS record at apex and cannot be edited"
)
}
.getOrElse(().asRight)
} yield ()
@ -233,7 +261,8 @@ object RecordSetValidations {
def canUseOwnerGroup(
ownerGroupId: Option[String],
group: Option[Group],
authPrincipal: AuthPrincipal): Either[Throwable, Unit] =
authPrincipal: AuthPrincipal
): Either[Throwable, Unit] =
(ownerGroupId, group) match {
case (None, _) => ().asRight
case (Some(groupId), None) =>

View File

@ -40,11 +40,13 @@ trait ACLRuleOrdering extends Ordering[ACLRule] {
sortableUserValue(rule1),
sortableRecordMaskValue(rule1),
sortableRecordTypeValue(rule1),
rule1.accessLevel).compare(
rule1.accessLevel
).compare(
sortableUserValue(rule2),
sortableRecordMaskValue(rule2),
sortableRecordTypeValue(rule2),
rule2.accessLevel)
rule2.accessLevel
)
}
object ACLRuleOrdering extends ACLRuleOrdering {

View File

@ -24,7 +24,8 @@ case class ListZoneChangesResponse(
zoneChanges: List[ZoneChange] = Nil,
nextId: Option[String],
startFrom: Option[String],
maxItems: Int)
maxItems: Int
)
object ListZoneChangesResponse {
def apply(zoneId: String, listResults: ListZoneChangesResults): ListZoneChangesResponse =
@ -33,5 +34,6 @@ object ListZoneChangesResponse {
listResults.items,
listResults.nextId,
listResults.startFrom,
listResults.maxItems)
listResults.maxItems
)
}

View File

@ -30,7 +30,8 @@ object ZoneChangeGenerator {
def forAdd(
zone: Zone,
authPrincipal: AuthPrincipal,
status: ZoneChangeStatus = Pending): ZoneChange =
status: ZoneChangeStatus = Pending
): ZoneChange =
ZoneChange(
zone
.copy(id = UUID.randomUUID().toString, created = DateTime.now, status = ZoneStatus.Syncing),
@ -68,6 +69,7 @@ object ZoneChangeGenerator {
val oldConn = oldZ.connection.getOrElse(newConn)
newConn.copy(
key =
if (oldConn.key == newConn.decrypted(Crypto.instance).key) oldConn.key else newConn.key)
if (oldConn.key == newConn.decrypted(Crypto.instance).key) oldConn.key else newConn.key
)
})
}

View File

@ -43,27 +43,31 @@ object ZoneConnectionValidator {
def getZoneConnection(
zone: Zone,
configuredDnsConnections: ConfiguredDnsConnections): ZoneConnection =
configuredDnsConnections: ConfiguredDnsConnections
): ZoneConnection =
zone.connection
.orElse(getDnsBackend(zone, configuredDnsConnections).map(_.zoneConnection))
.getOrElse(configuredDnsConnections.defaultZoneConnection)
def getTransferConnection(
zone: Zone,
configuredDnsConnections: ConfiguredDnsConnections): ZoneConnection =
configuredDnsConnections: ConfiguredDnsConnections
): ZoneConnection =
zone.transferConnection
.orElse(getDnsBackend(zone, configuredDnsConnections).map(_.transferConnection))
.getOrElse(configuredDnsConnections.defaultTransferConnection)
def getDnsBackend(
zone: Zone,
configuredDnsConnections: ConfiguredDnsConnections): Option[DnsBackend] =
configuredDnsConnections: ConfiguredDnsConnections
): Option[DnsBackend] =
zone.backendId
.flatMap { bid =>
val backend = configuredDnsConnections.dnsBackends.find(_.id == bid)
if (backend.isEmpty) {
logger.error(
s"BackendId [$bid] for zone [${zone.id}: ${zone.name}] is not defined in config")
s"BackendId [$bid] for zone [${zone.id}: ${zone.name}] is not defined in config"
)
}
backend
}
@ -96,7 +100,9 @@ class ZoneConnectionValidator(connections: ConfiguredDnsConnections)
ZoneValidationFailed(
zoneView.zone,
nel.toList,
"Zone could not be loaded due to validation errors."))
"Zone could not be loaded due to validation errors."
)
)
.toEither
.toResult
}
@ -108,7 +114,8 @@ class ZoneConnectionValidator(connections: ConfiguredDnsConnections)
withTimeout(
loadDns(zone),
opTimeout,
ConnectionFailed(zone, "Unable to connect to zone: Transfer connection invalid"))
ConnectionFailed(zone, "Unable to connect to zone: Transfer connection invalid")
)
def hasSOA(records: List[RecordSet], zone: Zone): Result[Unit] = {
if (records.isEmpty) {
@ -138,8 +145,10 @@ class ZoneConnectionValidator(connections: ConfiguredDnsConnections)
def healthCheck(timeout: Int): HealthCheck =
Resource
.fromAutoCloseable(IO(new Socket()))
.use(socket =>
IO(socket.connect(new InetSocketAddress(healthCheckAddress, healthCheckPort), timeout)))
.use(
socket =>
IO(socket.connect(new InetSocketAddress(healthCheckAddress, healthCheckPort), timeout))
)
.attempt
.asHealthCheck

View File

@ -44,14 +44,16 @@ case class ZoneInfo(
adminGroupName: String,
latestSync: Option[DateTime],
backendId: Option[String],
accessLevel: AccessLevel)
accessLevel: AccessLevel
)
object ZoneInfo {
def apply(
zone: Zone,
aclInfo: ZoneACLInfo,
groupName: String,
accessLevel: AccessLevel): ZoneInfo =
accessLevel: AccessLevel
): ZoneInfo =
ZoneInfo(
name = zone.name,
email = zone.email,
@ -88,7 +90,8 @@ case class ZoneSummaryInfo(
adminGroupName: String,
latestSync: Option[DateTime],
backendId: Option[String],
accessLevel: AccessLevel)
accessLevel: AccessLevel
)
object ZoneSummaryInfo {
def apply(zone: Zone, groupName: String, accessLevel: AccessLevel): ZoneSummaryInfo =
@ -125,7 +128,8 @@ case class RecordSetListInfo(
account: String,
accessLevel: AccessLevel,
ownerGroupId: Option[String],
ownerGroupName: Option[String])
ownerGroupName: Option[String]
)
object RecordSetListInfo {
def apply(recordSet: RecordSetInfo, accessLevel: AccessLevel): RecordSetListInfo =
@ -158,7 +162,8 @@ case class RecordSetInfo(
id: String,
account: String,
ownerGroupId: Option[String],
ownerGroupName: Option[String])
ownerGroupName: Option[String]
)
object RecordSetInfo {
def apply(recordSet: RecordSet, groupName: Option[String]): RecordSetInfo =
@ -188,7 +193,8 @@ case class RecordSetChangeInfo(
systemMessage: Option[String],
updates: Option[RecordSet],
id: String,
userName: String)
userName: String
)
object RecordSetChangeInfo {
def apply(recordSetChange: RecordSetChange, name: Option[String]): RecordSetChangeInfo =
@ -212,7 +218,8 @@ case class ListZonesResponse(
startFrom: Option[String] = None,
nextId: Option[String] = None,
maxItems: Int = 100,
ignoreAccess: Boolean = false)
ignoreAccess: Boolean = false
)
// Errors
case class InvalidRequest(msg: String) extends Throwable(msg)

View File

@ -46,7 +46,8 @@ object ZoneRecordValidations {
/* Checks to see if an individual ns data is part of the approved server list */
def isApprovedNameServer(
approvedServerList: List[Regex],
nsData: NSData): ValidatedNel[String, NSData] =
nsData: NSData
): ValidatedNel[String, NSData] =
if (isStringInRegexList(approvedServerList, nsData.nsdname)) {
nsData.validNel[String]
} else {
@ -56,7 +57,8 @@ object ZoneRecordValidations {
/* Inspects each record in the rdata, returning back the record set itself or all ns records that are not approved */
def containsApprovedNameServers(
approvedServerList: List[Regex],
nsRecordSet: RecordSet): ValidatedNel[String, RecordSet] = {
nsRecordSet: RecordSet
): ValidatedNel[String, RecordSet] = {
val validations: List[ValidatedNel[String, NSData]] = nsRecordSet.records
.collect { case ns: NSData => ns }
.map(isApprovedNameServer(approvedServerList, _))
@ -66,7 +68,8 @@ object ZoneRecordValidations {
def isNotHighValueFqdn(
highValueRegexList: List[Regex],
fqdn: String): ValidatedNel[DomainValidationError, Unit] =
fqdn: String
): ValidatedNel[DomainValidationError, Unit] =
if (!isStringInRegexList(highValueRegexList, fqdn)) {
().validNel
} else {
@ -75,7 +78,8 @@ object ZoneRecordValidations {
def isNotHighValueIp(
highValueIpList: List[IpAddress],
ip: String): ValidatedNel[DomainValidationError, Unit] =
ip: String
): ValidatedNel[DomainValidationError, Unit] =
if (!isIpInIpList(highValueIpList, ip)) {
().validNel
} else {
@ -84,7 +88,8 @@ object ZoneRecordValidations {
def domainDoesNotRequireManualReview(
regexList: List[Regex],
fqdn: String): ValidatedNel[DomainValidationError, Unit] =
fqdn: String
): ValidatedNel[DomainValidationError, Unit] =
if (!isStringInRegexList(regexList, fqdn)) {
().validNel
} else {
@ -93,7 +98,8 @@ object ZoneRecordValidations {
def ipDoesNotRequireManualReview(
regexList: List[IpAddress],
ip: String): ValidatedNel[DomainValidationError, Unit] =
ip: String
): ValidatedNel[DomainValidationError, Unit] =
if (!isIpInIpList(regexList, ip)) {
().validNel
} else {
@ -103,7 +109,8 @@ object ZoneRecordValidations {
def zoneDoesNotRequireManualReview(
zonesRequiringReview: Set[String],
zoneName: String,
fqdn: String): ValidatedNel[DomainValidationError, Unit] =
fqdn: String
): ValidatedNel[DomainValidationError, Unit] =
if (!zonesRequiringReview.contains(DomainHelpers.ensureTrailingDot(zoneName.toLowerCase))) {
().validNel
} else {

View File

@ -32,7 +32,8 @@ object ZoneService {
connectionValidator: ZoneConnectionValidatorAlgebra,
messageQueue: MessageQueue,
zoneValidations: ZoneValidations,
accessValidation: AccessValidationsAlgebra): ZoneService =
accessValidation: AccessValidationsAlgebra
): ZoneService =
new ZoneService(
dataAccessor.zoneRepository,
dataAccessor.groupRepository,
@ -53,8 +54,8 @@ class ZoneService(
connectionValidator: ZoneConnectionValidatorAlgebra,
messageQueue: MessageQueue,
zoneValidations: ZoneValidations,
accessValidation: AccessValidationsAlgebra)
extends ZoneServiceAlgebra {
accessValidation: AccessValidationsAlgebra
) extends ZoneServiceAlgebra {
import accessValidation._
import zoneValidations._
@ -62,7 +63,8 @@ class ZoneService(
def connectToZone(
createZoneInput: CreateZoneInput,
auth: AuthPrincipal): Result[ZoneCommandResult] =
auth: AuthPrincipal
): Result[ZoneCommandResult] =
for {
_ <- isValidZoneAcl(createZoneInput.acl).toResult
_ <- connectionValidator.isValidBackendId(createZoneInput.backendId).toResult
@ -84,7 +86,8 @@ class ZoneService(
_ <- validateSharedZoneAuthorized(
existingZone.shared,
updateZoneInput.shared,
auth.signedInUser).toResult
auth.signedInUser
).toResult
_ <- canChangeZone(auth, existingZone.name, existingZone.adminGroupId).toResult
_ <- adminGroupExists(updateZoneInput.adminGroupId)
// if admin group changes, this confirms user has access to new group
@ -136,32 +139,35 @@ class ZoneService(
nameFilter: Option[String] = None,
startFrom: Option[String] = None,
maxItems: Int = 100,
ignoreAccess: Boolean = false): Result[ListZonesResponse] = {
ignoreAccess: Boolean = false
): Result[ListZonesResponse] = {
for {
listZonesResult <- zoneRepository.listZones(
authPrincipal,
nameFilter,
startFrom,
maxItems,
ignoreAccess)
ignoreAccess
)
zones = listZonesResult.zones
groupIds = zones.map(_.adminGroupId).toSet
groups <- groupRepository.getGroups(groupIds)
zoneSummaryInfos = zoneSummaryInfoMapping(zones, authPrincipal, groups)
} yield
ListZonesResponse(
zoneSummaryInfos,
listZonesResult.zonesFilter,
listZonesResult.startFrom,
listZonesResult.nextId,
listZonesResult.maxItems,
listZonesResult.ignoreAccess)
} yield ListZonesResponse(
zoneSummaryInfos,
listZonesResult.zonesFilter,
listZonesResult.startFrom,
listZonesResult.nextId,
listZonesResult.maxItems,
listZonesResult.ignoreAccess
)
}.toResult
def zoneSummaryInfoMapping(
zones: List[Zone],
auth: AuthPrincipal,
groups: Set[Group]): List[ZoneSummaryInfo] =
groups: Set[Group]
): List[ZoneSummaryInfo] =
zones.map { zn =>
val groupName = groups.find(_.id == zn.adminGroupId) match {
case Some(group) => group.name
@ -175,7 +181,8 @@ class ZoneService(
zoneId: String,
authPrincipal: AuthPrincipal,
startFrom: Option[String] = None,
maxItems: Int = 100): Result[ListZoneChangesResponse] =
maxItems: Int = 100
): Result[ListZoneChangesResponse] =
for {
zone <- getZoneOrFail(zoneId)
_ <- canSeeZone(authPrincipal, zone).toResult
@ -187,7 +194,8 @@ class ZoneService(
def addACLRule(
zoneId: String,
aclRuleInfo: ACLRuleInfo,
authPrincipal: AuthPrincipal): Result[ZoneCommandResult] = {
authPrincipal: AuthPrincipal
): Result[ZoneCommandResult] = {
val newRule = ACLRule(aclRuleInfo)
for {
zone <- getZoneOrFail(zoneId)
@ -207,7 +215,8 @@ class ZoneService(
def deleteACLRule(
zoneId: String,
aclRuleInfo: ACLRuleInfo,
authPrincipal: AuthPrincipal): Result[ZoneCommandResult] = {
authPrincipal: AuthPrincipal
): Result[ZoneCommandResult] = {
val newRule = ACLRule(aclRuleInfo)
for {
zone <- getZoneOrFail(zoneId)
@ -233,7 +242,8 @@ class ZoneService(
case Some(existingZone) if existingZone.status != ZoneStatus.Deleted =>
ZoneAlreadyExistsError(
s"Zone with name $zoneName already exists. " +
s"Please contact ${existingZone.email} to request access to the zone.").asLeft
s"Please contact ${existingZone.email} to request access to the zone."
).asLeft
case _ => ().asRight
}
.toResult

View File

@ -24,7 +24,8 @@ trait ZoneServiceAlgebra {
def connectToZone(
createZoneInput: CreateZoneInput,
auth: AuthPrincipal): Result[ZoneCommandResult]
auth: AuthPrincipal
): Result[ZoneCommandResult]
def updateZone(updateZoneInput: UpdateZoneInput, auth: AuthPrincipal): Result[ZoneCommandResult]
@ -41,23 +42,27 @@ trait ZoneServiceAlgebra {
nameFilter: Option[String],
startFrom: Option[String],
maxItems: Int,
ignoreAccess: Boolean): Result[ListZonesResponse]
ignoreAccess: Boolean
): Result[ListZonesResponse]
def listZoneChanges(
zoneId: String,
authPrincipal: AuthPrincipal,
startFrom: Option[String],
maxItems: Int): Result[ListZoneChangesResponse]
maxItems: Int
): Result[ListZoneChangesResponse]
def addACLRule(
zoneId: String,
aclRuleInfo: ACLRuleInfo,
authPrincipal: AuthPrincipal): Result[ZoneCommandResult]
authPrincipal: AuthPrincipal
): Result[ZoneCommandResult]
def deleteACLRule(
zoneId: String,
aclRuleInfo: ACLRuleInfo,
authPrincipal: AuthPrincipal): Result[ZoneCommandResult]
authPrincipal: AuthPrincipal
): Result[ZoneCommandResult]
def getBackendIds(): Result[List[String]]

View File

@ -74,15 +74,18 @@ class ZoneValidations(syncDelayMillis: Int) {
// Validates that the zone is either not shared or shared and the user is a super or support user
def validateSharedZoneAuthorized(zoneShared: Boolean, user: User): Either[Throwable, Unit] =
ensuring(NotAuthorizedError("Not authorized to create shared zones."))(
!zoneShared || user.isSuper || user.isSupport)
!zoneShared || user.isSuper || user.isSupport
)
// Validates that the zone shared status has not been changed, or changed and the user is a super user
def validateSharedZoneAuthorized(
currentShared: Boolean,
updateShared: Boolean,
user: User): Either[Throwable, Unit] =
user: User
): Either[Throwable, Unit] =
ensuring(
NotAuthorizedError(
s"Not authorized to update zone shared status from $currentShared to $updateShared."))(
currentShared == updateShared || user.isSuper || user.isSupport)
s"Not authorized to update zone shared status from $currentShared to $updateShared."
)
)(currentShared == updateShared || user.isSuper || user.isSupport)
}

View File

@ -62,8 +62,8 @@ object DnsZoneViewLoader extends DnsConversions {
case class DnsZoneViewLoader(
zone: Zone,
zoneTransfer: Zone => ZoneTransferIn,
maxZoneSize: Int = VinylDNSConfig.maxZoneSize)
extends ZoneViewLoader
maxZoneSize: Int = VinylDNSConfig.maxZoneSize
) extends ZoneViewLoader
with DnsConversions
with Monitored {
@ -76,17 +76,21 @@ case class DnsZoneViewLoader(
xfr.run()
xfr.getAXFR.asScala.map(_.asInstanceOf[DNS.Record]).toList.distinct
}
rawDnsRecords = zoneXfr.filter(record =>
fromDnsRecordType(record.getType) != RecordType.UNKNOWN)
rawDnsRecords = zoneXfr.filter(
record => fromDnsRecordType(record.getType) != RecordType.UNKNOWN
)
_ <- if (rawDnsRecords.length > maxZoneSize)
IO.raiseError(ZoneTooLargeError(zone, rawDnsRecords.length, maxZoneSize))
else IO.pure(Unit)
dnsZoneName <- IO(zoneDnsName(zone.name))
recordSets <- IO(rawDnsRecords.map(toRecordSet(_, dnsZoneName, zone.id)))
_ <- IO(DnsZoneViewLoader.logger.info(
s"dns.loadDnsView zoneName=${zone.name}; rawRsCount=${zoneXfr.size}; rsCount=${recordSets.size}"))
_ <- IO(
DnsZoneViewLoader.logger.info(
s"dns.loadDnsView zoneName=${zone.name}; rawRsCount=${zoneXfr.size}; rsCount=${recordSets.size}"
)
)
} yield ZoneView(zone, recordSets)
}
}
}
object VinylDNSZoneViewLoader {
@ -103,11 +107,13 @@ case class VinylDNSZoneViewLoader(zone: Zone, recordSetRepository: RecordSetRepo
zoneId = zone.id,
startFrom = None,
maxItems = None,
recordNameFilter = None)
recordNameFilter = None
)
.map { result =>
VinylDNSZoneViewLoader.logger.info(
s"vinyldns.loadZoneView zoneName=${zone.name}; rsCount=${result.recordSets.size}")
s"vinyldns.loadZoneView zoneName=${zone.name}; rsCount=${result.recordSets.size}"
)
ZoneView(zone, result.recordSets)
}
}
}
}

View File

@ -31,7 +31,8 @@ object BatchChangeHandler {
def apply(
batchChangeRepository: BatchChangeRepository,
notifiers: AllNotifiers): BatchChangeCommand => IO[Option[BatchChange]] =
notifiers: AllNotifiers
): BatchChangeCommand => IO[Option[BatchChange]] =
batchChangeId => {
process(
batchChangeRepository: BatchChangeRepository,
@ -43,7 +44,8 @@ object BatchChangeHandler {
def process(
batchChangeRepository: BatchChangeRepository,
notifiers: AllNotifiers,
batchChangeCommand: BatchChangeCommand): IO[Option[BatchChange]] =
batchChangeCommand: BatchChangeCommand
): IO[Option[BatchChange]] =
for {
batchChange <- batchChangeRepository.getBatchChange(batchChangeCommand.id)
_ <- notify(notifiers, batchChange, batchChangeCommand)
@ -55,15 +57,16 @@ object BatchChangeHandler {
private final case class Pending(
change: Option[BatchChange],
batchChangeCommand: BatchChangeCommand)
extends BatchChangeProcessorState
batchChangeCommand: BatchChangeCommand
) extends BatchChangeProcessorState
private final case class PendingBatchNotificationError(change: BatchChange) extends Throwable
private def notify(
notifiers: AllNotifiers,
change: Option[BatchChange],
batchChangeCommand: BatchChangeCommand): IO[Unit] =
batchChangeCommand: BatchChangeCommand
): IO[Unit] =
change match {
case Some(pendingBatch)
if pendingBatch.status == BatchChangeStatus.PendingProcessing ||
@ -75,7 +78,8 @@ object BatchChangeHandler {
notifiers.notify(Notification(completedBatch))
case None =>
logger.error(
s"Notification not sent since batch change with ID ${batchChangeCommand.id} not found.")
s"Notification not sent since batch change with ID ${batchChangeCommand.id} not found."
)
IO.unit
}

View File

@ -38,15 +38,16 @@ object RecordSetChangeHandler {
def apply(
recordSetRepository: RecordSetRepository,
recordChangeRepository: RecordChangeRepository,
batchChangeRepository: BatchChangeRepository)(
implicit timer: Timer[IO]): (DnsConnection, RecordSetChange) => IO[RecordSetChange] =
batchChangeRepository: BatchChangeRepository
)(implicit timer: Timer[IO]): (DnsConnection, RecordSetChange) => IO[RecordSetChange] =
(conn, recordSetChange) => {
process(
recordSetRepository,
recordChangeRepository,
batchChangeRepository,
conn,
recordSetChange)
recordSetChange
)
}
def process(
@ -54,7 +55,8 @@ object RecordSetChangeHandler {
recordChangeRepository: RecordChangeRepository,
batchChangeRepository: BatchChangeRepository,
conn: DnsConnection,
recordSetChange: RecordSetChange)(implicit timer: Timer[IO]): IO[RecordSetChange] =
recordSetChange: RecordSetChange
)(implicit timer: Timer[IO]): IO[RecordSetChange] =
for {
wildCardExists <- wildCardExistsForRecord(recordSetChange.recordSet, recordSetRepository)
completedState <- fsm(Pending(recordSetChange), conn, wildCardExists)
@ -62,14 +64,16 @@ object RecordSetChangeHandler {
_ <- recordSetRepository.apply(changeSet)
_ <- recordChangeRepository.save(changeSet)
singleBatchChanges <- batchChangeRepository.getSingleChanges(
recordSetChange.singleBatchChangeIds)
recordSetChange.singleBatchChangeIds
)
singleChangeStatusUpdates = updateBatchStatuses(singleBatchChanges, completedState.change)
_ <- batchChangeRepository.updateSingleChanges(singleChangeStatusUpdates)
} yield completedState.change
def updateBatchStatuses(
singleChanges: List[SingleChange],
recordSetChange: RecordSetChange): List[SingleChange] =
recordSetChange: RecordSetChange
): List[SingleChange] =
recordSetChange.status match {
case RecordSetChangeStatus.Complete =>
singleChanges.map(_.complete(recordSetChange.id, recordSetChange.recordSet.id))
@ -146,7 +150,8 @@ object RecordSetChangeHandler {
}
private def fsm(state: ProcessorState, conn: DnsConnection, wildcardExists: Boolean)(
implicit timer: Timer[IO]): IO[ProcessorState] = {
implicit timer: Timer[IO]
): IO[ProcessorState] = {
/**
* If there is a wildcard record with the same type, then we skip validation and verification steps.
@ -161,8 +166,9 @@ object RecordSetChangeHandler {
* We also skip verification for NS records. We cannot create delegations for zones hosted on the
* same ANS if we attempt to validate NS records
*/
def bypassValidation(skip: => ProcessorState)(
orElse: => IO[ProcessorState]): IO[ProcessorState] = {
def bypassValidation(
skip: => ProcessorState
)(orElse: => IO[ProcessorState]): IO[ProcessorState] = {
val toRun =
if (wildcardExists || state.change.recordSet.typ == RecordType.NS) IO.pure(skip) else orElse
@ -199,8 +205,11 @@ object RecordSetChangeHandler {
case AlreadyApplied(_) => Completed(change.successful)
case ReadyToApply(_) => Validated(change)
case Failure(_, message) =>
Completed(change.failed(
s"Failed validating update to DNS for change ${change.id}:${change.recordSet.name}: " + message))
Completed(
change.failed(
s"Failed validating update to DNS for change ${change.id}:${change.recordSet.name}: " + message
)
)
}
/* Step 2: Apply the change to the dns backend */
@ -209,8 +218,11 @@ object RecordSetChangeHandler {
case Right(_: NoError) =>
Applied(change)
case Left(error) =>
Completed(change.failed(
s"Failed applying update to DNS for change ${change.id}:${change.recordSet.name}: ${error.getMessage}"))
Completed(
change.failed(
s"Failed applying update to DNS for change ${change.id}:${change.recordSet.name}: ${error.getMessage}"
)
)
}
/* Step 3: Verify the record was created. We attempt 12 times over 6 seconds */
@ -219,13 +231,22 @@ object RecordSetChangeHandler {
getProcessingStatus(change, dnsConn).flatMap {
case AlreadyApplied(_) => IO.pure(Completed(change.successful))
case ReadyToApply(_) if retries <= 0 =>
IO.pure(Completed(change.failed(s"""Failed verifying update to DNS for
|change ${change.id}:${change.recordSet.name}: Verify out of retries.""".stripMargin)))
IO.pure(
Completed(
change.failed(s"""Failed verifying update to DNS for
|change ${change.id}:${change.recordSet.name}: Verify out of retries.""".stripMargin)
)
)
case ReadyToApply(_) =>
IO.sleep(500.milliseconds) *> loop(retries - 1)
case Failure(_, message) =>
IO.pure(Completed(change.failed(
s"Failed verifying update to DNS for change ${change.id}:${change.recordSet.name}: $message")))
IO.pure(
Completed(
change.failed(
s"Failed verifying update to DNS for change ${change.id}:${change.recordSet.name}: $message"
)
)
)
}
loop()
@ -245,10 +266,11 @@ object RecordSetChangeHandler {
private def wildCardExistsForRecord(
recordSet: RecordSet,
recordSetRepository: RecordSetRepository): IO[Boolean] =
recordSetRepository: RecordSetRepository
): IO[Boolean] =
(
recordSetRepository.getRecordSets(recordSet.zoneId, "*", recordSet.typ),
recordSetRepository.getRecordSets(recordSet.zoneId, "*", RecordType.CNAME))
.parMapN(_ ++ _)
recordSetRepository.getRecordSets(recordSet.zoneId, "*", RecordType.CNAME)
).parMapN(_ ++ _)
.map(_.nonEmpty)
}

View File

@ -24,14 +24,16 @@ object ZoneChangeHandler {
def apply(
zoneRepository: ZoneRepository,
zoneChangeRepository: ZoneChangeRepository,
recordSetRepository: RecordSetRepository): ZoneChange => IO[ZoneChange] =
recordSetRepository: RecordSetRepository
): ZoneChange => IO[ZoneChange] =
zoneChange =>
zoneRepository.save(zoneChange.zone).flatMap {
case Left(duplicateZoneError) =>
zoneChangeRepository.save(
zoneChange.copy(
status = ZoneChangeStatus.Failed,
systemMessage = Some(duplicateZoneError.message))
systemMessage = Some(duplicateZoneError.message)
)
)
case Right(_) if zoneChange.changeType == ZoneChangeType.Delete =>
recordSetRepository
@ -42,5 +44,5 @@ object ZoneChangeHandler {
}
case Right(_) =>
zoneChangeRepository.save(zoneChange.copy(status = ZoneChangeStatus.Synced))
}
}
}

View File

@ -45,7 +45,8 @@ object ZoneSyncHandler extends DnsConversions with Monitored {
zoneRepository: ZoneRepository,
dnsLoader: Zone => DnsZoneViewLoader = DnsZoneViewLoader.apply,
vinyldnsLoader: (Zone, RecordSetRepository) => VinylDNSZoneViewLoader =
VinylDNSZoneViewLoader.apply): ZoneChange => IO[ZoneChange] =
VinylDNSZoneViewLoader.apply
): ZoneChange => IO[ZoneChange] =
zoneChange =>
for {
_ <- saveZoneAndChange(zoneRepository, zoneChangeRepository, zoneChange) // initial save to store zone status
@ -55,7 +56,8 @@ object ZoneSyncHandler extends DnsConversions with Monitored {
recordChangeRepository,
zoneChange,
dnsLoader,
vinyldnsLoader)
vinyldnsLoader
)
_ <- saveZoneAndChange(zoneRepository, zoneChangeRepository, syncChange) // final save to store zone status
// as Active
} yield syncChange
@ -63,13 +65,15 @@ object ZoneSyncHandler extends DnsConversions with Monitored {
def saveZoneAndChange(
zoneRepository: ZoneRepository,
zoneChangeRepository: ZoneChangeRepository,
zoneChange: ZoneChange): IO[ZoneChange] =
zoneChange: ZoneChange
): IO[ZoneChange] =
zoneRepository.save(zoneChange.zone).flatMap {
case Left(duplicateZoneError) =>
zoneChangeRepository.save(
zoneChange.copy(
status = ZoneChangeStatus.Failed,
systemMessage = Some(duplicateZoneError.message))
systemMessage = Some(duplicateZoneError.message)
)
)
case Right(_) =>
zoneChangeRepository.save(zoneChange)
@ -81,7 +85,8 @@ object ZoneSyncHandler extends DnsConversions with Monitored {
zoneChange: ZoneChange,
dnsLoader: Zone => DnsZoneViewLoader = DnsZoneViewLoader.apply,
vinyldnsLoader: (Zone, RecordSetRepository) => VinylDNSZoneViewLoader =
VinylDNSZoneViewLoader.apply): IO[ZoneChange] =
VinylDNSZoneViewLoader.apply
): IO[ZoneChange] =
monitor("zone.sync") {
time(s"zone.sync; zoneName='${zoneChange.zone.name}'") {
val zone = zoneChange.zone
@ -91,7 +96,8 @@ object ZoneSyncHandler extends DnsConversions with Monitored {
s"zone.sync.loadDnsView; zoneName='${zone.name}'; zoneChange='${zoneChange.id}'"
)(dnsLoader(zone).load())
val vinyldnsView = time(s"zone.sync.loadVinylDNSView; zoneName='${zone.name}'")(
vinyldnsLoader(zone, recordSetRepository).load())
vinyldnsLoader(zone, recordSetRepository).load()
)
val recordSetChanges = (dnsView, vinyldnsView).parTupled.map {
case (dnsZoneView, vinylDnsZoneView) => vinylDnsZoneView.diff(dnsZoneView)
}
@ -101,11 +107,14 @@ object ZoneSyncHandler extends DnsConversions with Monitored {
if (changesWithUserIds.isEmpty) {
logger.info(
s"zone.sync.changes; zoneName='${zone.name}'; changeCount=0; zoneChange='${zoneChange.id}'")
s"zone.sync.changes; zoneName='${zone.name}'; changeCount=0; zoneChange='${zoneChange.id}'"
)
IO.pure(
zoneChange.copy(
zone.copy(status = ZoneStatus.Active, latestSync = Some(DateTime.now)),
status = ZoneChangeStatus.Synced))
status = ZoneChangeStatus.Synced
)
)
} else {
changesWithUserIds
.filter { chg =>
@ -120,29 +129,33 @@ object ZoneSyncHandler extends DnsConversions with Monitored {
logger.info(
s"Zone sync for zoneName='${zone.name}'; zoneId='${zone.id}'; " +
s"zoneChange='${zoneChange.id}' includes the following ${dottedGroup.length} " +
s"dotted host records: [$dottedGroupString]")
s"dotted host records: [$dottedGroupString]"
)
}
logger.info(
s"zone.sync.changes; zoneName='${zone.name}'; " +
s"changeCount=${changesWithUserIds.size}; zoneChange='${zoneChange.id}'")
s"changeCount=${changesWithUserIds.size}; zoneChange='${zoneChange.id}'"
)
val changeSet = ChangeSet(changesWithUserIds).copy(status = ChangeSetStatus.Applied)
// we want to make sure we write to both the change repo and record set repo
// at the same time as this can take a while
val saveRecordChanges = time(s"zone.sync.saveChanges; zoneName='${zone.name}'")(
recordChangeRepository.save(changeSet))
recordChangeRepository.save(changeSet)
)
val saveRecordSets = time(s"zone.sync.saveRecordSets; zoneName='${zone.name}'")(
recordSetRepository.apply(changeSet))
recordSetRepository.apply(changeSet)
)
// join together the results of saving both the record changes as well as the record sets
for {
_ <- saveRecordChanges
_ <- saveRecordSets
} yield
zoneChange.copy(
zone.copy(status = ZoneStatus.Active, latestSync = Some(DateTime.now)),
status = ZoneChangeStatus.Synced)
} yield zoneChange.copy(
zone.copy(status = ZoneStatus.Active, latestSync = Some(DateTime.now)),
status = ZoneChangeStatus.Synced
)
}
}
}
@ -151,11 +164,13 @@ object ZoneSyncHandler extends DnsConversions with Monitored {
case Left(e: Throwable) =>
logger.error(
s"Encountered error syncing ; zoneName='${zoneChange.zone.name}'; zoneChange='${zoneChange.id}'",
e)
e
)
// We want to just move back to an active status, do not update latest sync
zoneChange.copy(
zone = zoneChange.zone.copy(status = ZoneStatus.Active),
status = ZoneChangeStatus.Failed)
status = ZoneChangeStatus.Failed
)
case Right(ok) => ok
}
}

View File

@ -45,7 +45,8 @@ object APIMetrics {
def initialize(
settings: APIMetricsSettings,
reporter: ScheduledReporter = logReporter): IO[Unit] = IO {
reporter: ScheduledReporter = logReporter
): IO[Unit] = IO {
if (settings.memory.logEnabled) {
reporter.start(settings.memory.logSeconds, TimeUnit.SECONDS)
}

View File

@ -77,7 +77,8 @@ class EmailNotifier(config: EmailNotifierConfig, session: Session, userRepositor
case Some(user: User) if user.email.isDefined =>
IO {
logger.warn(
s"Unable to properly parse email for ${user.id}: ${user.email.getOrElse("<none>")}")
s"Unable to properly parse email for ${user.id}: ${user.email.getOrElse("<none>")}"
)
}
case None => IO { logger.warn(s"Unable to find user: ${bc.userId}") }
case _ => IO.unit
@ -95,16 +96,23 @@ class EmailNotifier(config: EmailNotifierConfig, session: Session, userRepositor
// For manually reviewed e-mails, add additional info; e-mails are not sent for pending batch changes
if (bc.approvalStatus != AutoApproved) {
bc.reviewComment.foreach(reviewComment =>
sb.append(s"<b>Review comment:</b> $reviewComment <br/>"))
bc.reviewTimestamp.foreach(reviewTimestamp =>
sb.append(
s"<b>Time reviewed:</b> ${reviewTimestamp.toString(DateTimeFormat.fullDateTime)} <br/>"))
bc.reviewComment.foreach(
reviewComment => sb.append(s"<b>Review comment:</b> $reviewComment <br/>")
)
bc.reviewTimestamp.foreach(
reviewTimestamp =>
sb.append(
s"<b>Time reviewed:</b> ${reviewTimestamp.toString(DateTimeFormat.fullDateTime)} <br/>"
)
)
}
bc.cancelledTimestamp.foreach(cancelledTimestamp =>
sb.append(
s"<b>Time cancelled:</b> ${cancelledTimestamp.toString(DateTimeFormat.fullDateTime)} <br/>"))
bc.cancelledTimestamp.foreach(
cancelledTimestamp =>
sb.append(
s"<b>Time cancelled:</b> ${cancelledTimestamp.toString(DateTimeFormat.fullDateTime)} <br/>"
)
)
// Single change data table
sb.append(s"""<br/><table border = "1">
@ -138,7 +146,8 @@ class EmailNotifier(config: EmailNotifierConfig, session: Session, userRepositor
_,
_,
_,
_) =>
_
) =>
s"""<tr><td>${index + 1}</td><td>Add</td><td>$typ</td><td>$inputName</td>
| <td>$ttl</td><td>${formatRecordData(recordData)}</td><td>$status</td>
| <td>${systemMessage.getOrElse("")}</td></tr>"""
@ -154,7 +163,8 @@ class EmailNotifier(config: EmailNotifierConfig, session: Session, userRepositor
_,
_,
_,
_) =>
_
) =>
val recordDataValue = recordData.map(_.toString).getOrElse("")
s"""<tr><td>${index + 1}</td><td>Delete</td><td>$typ</td><td>$inputName</td>
| <td></td><td>$recordDataValue</td><td>$status</td><td>${systemMessage

View File

@ -48,7 +48,8 @@ class SnsNotifier(config: SnsNotifierConfig, sns: AmazonSNS)
val request = new PublishRequest(config.topicArn, message)
request.addMessageAttributesEntry(
"userName",
new MessageAttributeValue().withDataType("String").withStringValue(bc.userName))
new MessageAttributeValue().withDataType("String").withStringValue(bc.userName)
)
sns.publish(request)
logger.info(s"Sending batch change success; batchChange='${bc.id}'")
}.handleErrorWith { e =>

View File

@ -42,12 +42,17 @@ class SnsNotifierProvider extends NotifierProvider {
"Setting up sns notifier client with settings: " +
s"service endpoint: ${config.serviceEndpoint}; " +
s"signing region: ${config.signingRegion}; " +
s"topic name: ${config.topicArn}")
s"topic name: ${config.topicArn}"
)
AmazonSNSClientBuilder.standard
.withEndpointConfiguration(
new EndpointConfiguration(config.serviceEndpoint, config.signingRegion))
.withCredentials(new AWSStaticCredentialsProvider(
new BasicAWSCredentials(config.accessKey, config.secretKey)))
new EndpointConfiguration(config.serviceEndpoint, config.signingRegion)
)
.withCredentials(
new AWSStaticCredentialsProvider(
new BasicAWSCredentials(config.accessKey, config.secretKey)
)
)
.build()
}

View File

@ -36,5 +36,5 @@ final case class ApiDataAccessor(
recordChangeRepository: RecordChangeRepository,
zoneChangeRepository: ZoneChangeRepository,
zoneRepository: ZoneRepository,
batchChangeRepository: BatchChangeRepository)
extends DataAccessor
batchChangeRepository: BatchChangeRepository
) extends DataAccessor

View File

@ -37,10 +37,12 @@ object ApiDataAccessorProvider extends DataAccessorProvider[ApiDataAccessor] {
recordChange,
zoneChange,
zone,
batchChange)
batchChange
)
def create(
dataStores: List[(DataStoreConfig, DataStore)]): ValidatedNel[String, ApiDataAccessor] =
dataStores: List[(DataStoreConfig, DataStore)]
): ValidatedNel[String, ApiDataAccessor] =
(
getRepoOf[UserRepository](dataStores, user),
getRepoOf[GroupRepository](dataStores, group),

View File

@ -192,7 +192,8 @@ object TestDataLoader {
id = "shared-zone-group",
email = "email",
memberIds = Set(sharedZoneUser.id),
adminUserIds = Set(sharedZoneUser.id))
adminUserIds = Set(sharedZoneUser.id)
)
final val sharedZone = Zone(
name = "shared.",
@ -207,14 +208,16 @@ object TestDataLoader {
id = "global-acl-group-id",
email = "email",
memberIds = Set(okUser.id, dummyUser.id),
adminUserIds = Set(okUser.id, dummyUser.id))
adminUserIds = Set(okUser.id, dummyUser.id)
)
final val anotherGlobalACLGroup = Group(
name = "globalACLGroup",
id = "another-global-acl-group",
email = "email",
memberIds = Set(testUser.id),
adminUserIds = Set(testUser.id))
adminUserIds = Set(testUser.id)
)
final val duGroup = Group(
name = "duGroup",
@ -237,7 +240,8 @@ object TestDataLoader {
userRepo: UserRepository,
groupRepo: GroupRepository,
zoneRepo: ZoneRepository,
membershipRepo: MembershipRepository): IO[Unit] =
membershipRepo: MembershipRepository
): IO[Unit] =
for {
_ <- (testUser :: okUser :: dummyUser :: sharedZoneUser :: lockedUser :: listGroupUser :: listZonesUser ::
listBatchChangeSummariesUser :: listZeroBatchChangeSummariesUser :: zoneHistoryUser :: supportUser ::
@ -258,7 +262,8 @@ object TestDataLoader {
IO.raiseError(new RuntimeException(msg))
} else {
logger.info(
s"Deleting existing shared zones on startup: ${toDelete.map(z => (z.name, z.id))}")
s"Deleting existing shared zones on startup: ${toDelete.map(z => (z.name, z.id))}"
)
IO.unit
}
_ <- toDelete.map(zoneRepo.save).parSequence
@ -269,17 +274,21 @@ object TestDataLoader {
_ <- groupRepo.save(listBatchChangeSummariesGroup)
_ <- membershipRepo.addMembers(
groupId = "shared-zone-group",
memberUserIds = Set(sharedZoneUser.id))
memberUserIds = Set(sharedZoneUser.id)
)
_ <- membershipRepo.addMembers(
groupId = "global-acl-group-id",
memberUserIds = Set(okUser.id, dummyUser.id))
memberUserIds = Set(okUser.id, dummyUser.id)
)
_ <- membershipRepo.addMembers(
groupId = "another-global-acl-group",
memberUserIds = Set(testUser.id))
memberUserIds = Set(testUser.id)
)
_ <- membershipRepo.addMembers(groupId = duGroup.id, memberUserIds = duGroup.memberIds)
_ <- membershipRepo.addMembers(
groupId = listBatchChangeSummariesGroup.id,
memberUserIds = listBatchChangeSummariesGroup.memberIds)
memberUserIds = listBatchChangeSummariesGroup.memberIds
)
_ <- zoneRepo.save(sharedZone)
_ <- zoneRepo.save(nonTestSharedZone)
} yield ()

View File

@ -47,7 +47,8 @@ object Aws4Authenticator {
"akid",
"creds",
"shs",
"sig")
"sig"
)
def parseAuthHeader(auth: String): Option[Regex.Match] = aws4AuthRegex.findPrefixMatchOf(auth)
}
@ -99,12 +100,14 @@ class Aws4Authenticator {
req: HttpRequest,
authorization: List[String],
secret: String,
content: String): Boolean = {
content: String
): Boolean = {
val List(
_,
signatureScope, // signature scope
signatureHeaders, // signed headers
signatureReceived) = authorization
signatureReceived
) = authorization
val signedHeaders = Set() ++ signatureHeaders.split(';')
// convert Date header to canonical form required by AWS
val dateTime = iso8601Format.print(getDate(req).get)
@ -133,7 +136,8 @@ class Aws4Authenticator {
// XXX - need to canonicalize non-trimmed white space
def canonicalHeaders(
req: HttpRequest,
signedHeaderNames: Set[String]): TreeMap[String, Seq[String]] = {
signedHeaderNames: Set[String]
): TreeMap[String, Seq[String]] = {
def getHeaderValue(name: String): Option[String] = name match {
case "content-type" => Some(req.entity.contentType.value)
case "content-length" => req.entity.contentLengthOption.map(_.toString)
@ -160,7 +164,8 @@ class Aws4Authenticator {
val stringToSign = joinStrings('\n', aws4AuthScheme, dateTime, scope, hashString(creq))
hexString(
runHmac(Mac.getInstance(hmacAlgorithm))(signingKey(secret, scope).getEncoded, stringToSign))
runHmac(Mac.getInstance(hmacAlgorithm))(signingKey(secret, scope).getEncoded, stringToSign)
)
}
/**
@ -208,7 +213,8 @@ class Aws4Authenticator {
req: HttpRequest,
hdrs: CanonicalHeaders,
signedHeaders: String,
content: String): String = {
content: String
): String = {
val lines = MutableList[String](req.method.value) ++ canonicalURI(req)
// add canonical headers

View File

@ -88,7 +88,8 @@ trait BatchChangeJsonProtocol extends JsonValidation {
(js \ "inputName").required[String]("Missing BatchChangeInput.changes.inputName"),
recordType,
(js \ "ttl").optional[Long],
recordType.andThen(extractRecord(_, js \ "record"))).mapN(AddChangeInput.apply)
recordType.andThen(extractRecord(_, js \ "record"))
).mapN(AddChangeInput.apply)
}
override def toJson(aci: AddChangeInput): JValue =
@ -112,7 +113,8 @@ trait BatchChangeJsonProtocol extends JsonValidation {
(
(js \ "inputName").required[String]("Missing BatchChangeInput.changes.inputName"),
recordType,
recordData).mapN(DeleteRRSetChangeInput.apply)
recordData
).mapN(DeleteRRSetChangeInput.apply)
}
override def toJson(drsci: DeleteRRSetChangeInput): JValue =
@ -218,7 +220,8 @@ trait BatchChangeJsonProtocol extends JsonValidation {
(js \ "reviewComment")
.optional[String]
.check(
s"Comment length must not exceed $MAX_COMMENT_LENGTH characters." -> checkCommentLength)
s"Comment length must not exceed $MAX_COMMENT_LENGTH characters." -> checkCommentLength
)
.map(RejectBatchChangeInput)
}
@ -228,7 +231,8 @@ trait BatchChangeJsonProtocol extends JsonValidation {
(js \ "reviewComment")
.optional[String]
.check(
s"Comment length must not exceed $MAX_COMMENT_LENGTH characters." -> checkCommentLength)
s"Comment length must not exceed $MAX_COMMENT_LENGTH characters." -> checkCommentLength
)
.map(ApproveBatchChangeInput)
}
@ -242,7 +246,8 @@ trait BatchChangeJsonProtocol extends JsonValidation {
case TXT => js.required[TXTData]("Missing BatchChangeInput.changes.record.text")
case MX =>
js.required[MXData](
"Missing BatchChangeInput.changes.record.preference and BatchChangeInput.changes.record.exchange")
"Missing BatchChangeInput.changes.record.preference and BatchChangeInput.changes.record.exchange"
)
case _ =>
s"Unsupported type $typ, valid types include: A, AAAA, CNAME, PTR, TXT, and MX".invalidNel
}

View File

@ -24,8 +24,8 @@ import vinyldns.api.domain.batch._
class BatchChangeRoute(
batchChangeService: BatchChangeServiceAlgebra,
val vinylDNSAuthenticator: VinylDNSAuthenticator)
extends VinylDNSJsonProtocol
val vinylDNSAuthenticator: VinylDNSAuthenticator
) extends VinylDNSJsonProtocol
with VinylDNSDirectives[BatchChangeErrorResponse] {
def getRoutes: Route = batchChangeRoute
@ -59,7 +59,8 @@ class BatchChangeRoute(
authenticateAndExecuteWithEntity[BatchChange, BatchChangeInput](
(authPrincipal, batchChangeInput) =>
batchChangeService
.applyBatchChange(batchChangeInput, authPrincipal, allowManualReview)) { chg =>
.applyBatchChange(batchChangeInput, authPrincipal, allowManualReview)
) { chg =>
complete(StatusCodes.Accepted, chg)
}
}
@ -69,26 +70,31 @@ class BatchChangeRoute(
"startFrom".as[Int].?,
"maxItems".as[Int].?(MAX_ITEMS_LIMIT),
"ignoreAccess".as[Boolean].?(false),
"approvalStatus".as[String].?) {
"approvalStatus".as[String].?
) {
(
startFrom: Option[Int],
maxItems: Int,
ignoreAccess: Boolean,
approvalStatus: Option[String]) =>
approvalStatus: Option[String]
) =>
{
val convertApprovalStatus = approvalStatus.flatMap(BatchChangeApprovalStatus.find)
handleRejections(invalidQueryHandler) {
validate(
0 < maxItems && maxItems <= MAX_ITEMS_LIMIT,
s"maxItems was $maxItems, maxItems must be between 1 and $MAX_ITEMS_LIMIT, inclusive.") {
s"maxItems was $maxItems, maxItems must be between 1 and $MAX_ITEMS_LIMIT, inclusive."
) {
authenticateAndExecute(
batchChangeService.listBatchChangeSummaries(
_,
startFrom,
maxItems,
ignoreAccess,
convertApprovalStatus)) { summaries =>
convertApprovalStatus
)
) { summaries =>
complete(StatusCodes.OK, summaries)
}
}
@ -111,9 +117,9 @@ class BatchChangeRoute(
authenticateAndExecuteWithEntity[BatchChange, Option[RejectBatchChangeInput]](
(authPrincipal, input) =>
batchChangeService
.rejectBatchChange(id, authPrincipal, input.getOrElse(RejectBatchChangeInput()))) {
chg =>
complete(StatusCodes.OK, chg)
.rejectBatchChange(id, authPrincipal, input.getOrElse(RejectBatchChangeInput()))
) { chg =>
complete(StatusCodes.OK, chg)
}
// TODO: Update response entity to return modified batch change
}
@ -123,10 +129,8 @@ class BatchChangeRoute(
authenticateAndExecuteWithEntity[BatchChange, Option[ApproveBatchChangeInput]](
(authPrincipal, input) =>
batchChangeService
.approveBatchChange(
id,
authPrincipal,
input.getOrElse(ApproveBatchChangeInput()))) { chg =>
.approveBatchChange(id, authPrincipal, input.getOrElse(ApproveBatchChangeInput()))
) { chg =>
complete(StatusCodes.Accepted, chg)
// TODO: Update response entity to return modified batch change
}

View File

@ -208,8 +208,8 @@ trait DnsJsonProtocol extends JsonValidation {
override def fromJson(js: JValue): ValidatedNel[String, RecordSetListInfo] =
(
RecordSetInfoSerializer.fromJson(js),
(js \ "accessLevel").required[AccessLevel.AccessLevel]("Missing RecordSet.zoneId"))
.mapN(RecordSetListInfo.apply)
(js \ "accessLevel").required[AccessLevel.AccessLevel]("Missing RecordSet.zoneId")
).mapN(RecordSetListInfo.apply)
override def toJson(rs: RecordSetListInfo): JValue =
("type" -> Extraction.decompose(rs.typ)) ~

View File

@ -41,8 +41,11 @@ object VinylDateParser {
format.dateFormat
.parse(s)
.map(_.getTime)
.getOrElse(throw new MappingException(
s"Invalid date format $s; provide the date format as YYYY-MM-DDTHH:MM:SSZ"))
.getOrElse(
throw new MappingException(
s"Invalid date format $s; provide the date format as YYYY-MM-DDTHH:MM:SSZ"
)
)
}
case object VinylDateTimeSerializer
extends CustomSerializer[DateTime](
@ -54,7 +57,8 @@ case object VinylDateTimeSerializer
}, {
case d: DateTime => JString(format.dateFormat.format(d.toDate))
}
))
)
)
trait JsonValidationSupport extends Json4sSupport {
@ -72,7 +76,8 @@ trait JsonValidationSupport extends Json4sSupport {
IntervalSerializer(),
LocalDateSerializer(),
LocalTimeSerializer(),
PeriodSerializer)
PeriodSerializer
)
/**
* Returns an adjusted set of serializers excluding the serializer passed in. This is needed otherwise
@ -101,7 +106,8 @@ trait JsonValidation extends JsonValidationSupport {
def JsonV[A: Manifest](
validator: JValue => ValidatedNel[String, A],
serializer: A => JValue): ValidationSerializer[A] =
serializer: A => JValue
): ValidationSerializer[A] =
new ValidationSerializer[A] {
override def fromJson(jv: JValue) = validator(jv)
override def toJson(a: A) = serializer(a)
@ -215,8 +221,9 @@ trait JsonValidation extends JsonValidationSupport {
}
}
def extractEnum[E <: Enumeration](enum: E)(
default: => ValidatedNel[String, E#Value]): ValidatedNel[String, E#Value] = {
def extractEnum[E <: Enumeration](
enum: E
)(default: => ValidatedNel[String, E#Value]): ValidatedNel[String, E#Value] = {
lazy val invalidMsg =
s"Invalid ${enum.getClass.getSimpleName.replace("$", "")}".invalidNel[E#Value]

View File

@ -31,14 +31,16 @@ object MembershipJsonProtocol {
email: String,
description: Option[String],
members: Set[UserInfo],
admins: Set[UserInfo])
admins: Set[UserInfo]
)
final case class UpdateGroupInput(
id: String,
name: String,
email: String,
description: Option[String],
members: Set[UserInfo],
admins: Set[UserInfo])
admins: Set[UserInfo]
)
}
/* Defines the JSON serialization to support the Membership Routes */

View File

@ -25,8 +25,8 @@ import vinyldns.core.domain.membership.{Group, LockStatus}
class MembershipRoute(
membershipService: MembershipServiceAlgebra,
val vinylDNSAuthenticator: VinylDNSAuthenticator)
extends VinylDNSJsonProtocol
val vinylDNSAuthenticator: VinylDNSAuthenticator
) extends VinylDNSJsonProtocol
with VinylDNSDirectives[Throwable] {
final private val DEFAULT_MAX_ITEMS: Int = 100
final private val MAX_ITEMS_LIMIT: Int = 1000
@ -62,7 +62,8 @@ class MembershipRoute(
input.email,
input.description,
memberIds = (input.members ++ input.admins).map(_.id),
adminUserIds = input.admins.map(_.id))
adminUserIds = input.admins.map(_.id)
)
membershipService.createGroup(group, authPrincipal)
} { group =>
complete(StatusCodes.OK, GroupInfo(group))
@ -73,12 +74,14 @@ class MembershipRoute(
"startFrom".?,
"maxItems".as[Int].?(DEFAULT_MAX_ITEMS),
"groupNameFilter".?,
"ignoreAccess".as[Boolean].?(false)) {
"ignoreAccess".as[Boolean].?(false)
) {
(
startFrom: Option[String],
maxItems: Int,
groupNameFilter: Option[String],
ignoreAccess: Boolean) =>
ignoreAccess: Boolean
) =>
{
handleRejections(invalidQueryHandler) {
validate(
@ -88,10 +91,11 @@ class MembershipRoute(
| and $MAX_ITEMS_LIMIT inclusive"
""".stripMargin
) {
authenticateAndExecute(membershipService
.listMyGroups(groupNameFilter, startFrom, maxItems, _, ignoreAccess)) {
groups =>
complete(StatusCodes.OK, groups)
authenticateAndExecute(
membershipService
.listMyGroups(groupNameFilter, startFrom, maxItems, _, ignoreAccess)
) { groups =>
complete(StatusCodes.OK, groups)
}
}
}
@ -110,7 +114,9 @@ class MembershipRoute(
input.description,
(input.members ++ input.admins).map(_.id),
input.admins.map(_.id),
authPrincipal)) { group =>
authPrincipal
)
) { group =>
complete(StatusCodes.OK, GroupInfo(group))
}
}
@ -122,9 +128,12 @@ class MembershipRoute(
handleRejections(invalidQueryHandler) {
validate(
0 < maxItems && maxItems <= MAX_ITEMS_LIMIT,
s"maxItems was $maxItems, maxItems must be between 0 exclusive and $MAX_ITEMS_LIMIT inclusive") {
authenticateAndExecute(membershipService
.listMembers(groupId, startFrom, maxItems, _)) { members =>
s"maxItems was $maxItems, maxItems must be between 0 exclusive and $MAX_ITEMS_LIMIT inclusive"
) {
authenticateAndExecute(
membershipService
.listMembers(groupId, startFrom, maxItems, _)
) { members =>
complete(StatusCodes.OK, members)
}
}
@ -146,9 +155,12 @@ class MembershipRoute(
handleRejections(invalidQueryHandler) {
validate(
0 < maxItems && maxItems <= MAX_ITEMS_LIMIT,
s"maxItems was $maxItems, maxItems must be between 0 and $MAX_ITEMS_LIMIT") {
authenticateAndExecute(membershipService
.getGroupActivity(groupId, startFrom, maxItems, _)) { activity =>
s"maxItems was $maxItems, maxItems must be between 0 and $MAX_ITEMS_LIMIT"
) {
authenticateAndExecute(
membershipService
.getGroupActivity(groupId, startFrom, maxItems, _)
) { activity =>
complete(StatusCodes.OK, activity)
}
}

View File

@ -33,12 +33,13 @@ case class ListRecordSetsResponse(
startFrom: Option[String] = None,
nextId: Option[String] = None,
maxItems: Option[Int] = None,
recordNameFilter: Option[String] = None)
recordNameFilter: Option[String] = None
)
class RecordSetRoute(
recordSetService: RecordSetServiceAlgebra,
val vinylDNSAuthenticator: VinylDNSAuthenticator)
extends VinylDNSJsonProtocol
val vinylDNSAuthenticator: VinylDNSAuthenticator
) extends VinylDNSJsonProtocol
with VinylDNSDirectives[Throwable] {
def getRoutes: Route = recordSetRoute
@ -63,8 +64,9 @@ class RecordSetRoute(
val recordSetRoute: Route = path("zones" / Segment / "recordsets") { zoneId =>
(post & monitor("Endpoint.addRecordSet")) {
authenticateAndExecuteWithEntity[ZoneCommandResult, RecordSet]((authPrincipal, recordSet) =>
recordSetService.addRecordSet(recordSet, authPrincipal)) { rc =>
authenticateAndExecuteWithEntity[ZoneCommandResult, RecordSet](
(authPrincipal, recordSet) => recordSetService.addRecordSet(recordSet, authPrincipal)
) { rc =>
complete(StatusCodes.Accepted, rc)
}
} ~
@ -74,11 +76,13 @@ class RecordSetRoute(
handleRejections(invalidQueryHandler) {
validate(
0 < maxItems && maxItems <= DEFAULT_MAX_ITEMS,
s"maxItems was $maxItems, maxItems must be between 0 and $DEFAULT_MAX_ITEMS") {
authenticateAndExecute(recordSetService
.listRecordSets(zoneId, startFrom, Some(maxItems), recordNameFilter, _)) {
rsResponse =>
complete(StatusCodes.OK, rsResponse)
s"maxItems was $maxItems, maxItems must be between 0 and $DEFAULT_MAX_ITEMS"
) {
authenticateAndExecute(
recordSetService
.listRecordSets(zoneId, startFrom, Some(maxItems), recordNameFilter, _)
) { rsResponse =>
complete(StatusCodes.OK, rsResponse)
}
}
}
@ -129,8 +133,10 @@ class RecordSetRoute(
errorMsg = s"maxItems was $maxItems, maxItems must be between 0 exclusive " +
s"and $DEFAULT_MAX_ITEMS inclusive"
) {
authenticateAndExecute(recordSetService
.listRecordSetChanges(zoneId, startFrom, maxItems, _)) { changes =>
authenticateAndExecute(
recordSetService
.listRecordSetChanges(zoneId, startFrom, maxItems, _)
) { changes =>
complete(StatusCodes.OK, changes)
}
}

View File

@ -29,7 +29,8 @@ case class CurrentStatus(
processingDisabled: Boolean,
color: String,
keyName: String,
version: String)
version: String
)
object CurrentStatus {
val color = VinylDNSConfig.vinyldnsConfig.getString("color")
@ -50,7 +51,8 @@ trait StatusRoute extends Directives {
onSuccess(processingDisabled.get.unsafeToFuture()) { isProcessingDisabled =>
complete(
StatusCodes.OK,
CurrentStatus(isProcessingDisabled, color, vinyldnsKeyName, version))
CurrentStatus(isProcessingDisabled, color, vinyldnsKeyName, version)
)
}
} ~
(post & path("status")) {
@ -58,7 +60,8 @@ trait StatusRoute extends Directives {
onSuccess(processingDisabled.set(isProcessingDisabled).unsafeToFuture()) {
complete(
StatusCodes.OK,
CurrentStatus(isProcessingDisabled, color, vinyldnsKeyName, version))
CurrentStatus(isProcessingDisabled, color, vinyldnsKeyName, version)
)
}
}
}

View File

@ -36,18 +36,20 @@ final case class AccountLocked(reason: String) extends VinylDNSAuthenticationErr
trait VinylDNSAuthenticator {
def authenticate(
ctx: RequestContext,
content: String): IO[Either[VinylDNSAuthenticationError, AuthPrincipal]]
content: String
): IO[Either[VinylDNSAuthenticationError, AuthPrincipal]]
}
class ProductionVinylDNSAuthenticator(
val authenticator: Aws4Authenticator,
val authPrincipalProvider: AuthPrincipalProvider)
extends VinylDNSAuthenticator
val authPrincipalProvider: AuthPrincipalProvider
) extends VinylDNSAuthenticator
with Monitored {
def authenticate(
ctx: RequestContext,
content: String): IO[Either[VinylDNSAuthenticationError, AuthPrincipal]] =
content: String
): IO[Either[VinylDNSAuthenticationError, AuthPrincipal]] =
// Need to refactor getAuthPrincipal to be an IO[Either[E, A]] instead of how it is implemented.
getAuthPrincipal(ctx, content).attempt.flatMap {
case Left(e: VinylDNSAuthenticationError) => IO.pure(Left(e))
@ -104,7 +106,8 @@ class ProductionVinylDNSAuthenticator(
req: HttpRequest,
secretKey: String,
authHeaderRegex: Regex.Match,
content: String): IO[Unit] =
content: String
): IO[Unit] =
authHeaderRegex match {
case auth if authenticator.authenticateReq(req, auth.subgroups, secretKey, content) =>
IO.unit
@ -138,7 +141,8 @@ class ProductionVinylDNSAuthenticator(
ctx.request,
decryptSecret(authPrincipal.secretKey),
regexMatch,
content)
content
)
} yield authPrincipal
def decryptSecret(str: String, crypto: CryptoAlgebra = Crypto.instance): String =
@ -149,7 +153,8 @@ class ProductionVinylDNSAuthenticator(
case Some(ok) =>
if (ok.signedInUser.lockStatus == LockStatus.Locked) {
IO.raiseError(
AccountLocked(s"Account with username ${ok.signedInUser.userName} is locked"))
AccountLocked(s"Account with username ${ok.signedInUser.userName} is locked")
)
} else IO.pure(ok)
case None =>
IO.raiseError(AuthRejected(s"Account with accessKey $accessKey specified was not found"))

View File

@ -42,8 +42,8 @@ trait VinylDNSDirectives[E] extends Directives {
def authenticate: Directive1[AuthPrincipal] = extractRequestContext.flatMap { ctx =>
extractStrictEntity(10.seconds).flatMap { strictEntity =>
onSuccess(
vinylDNSAuthenticator.authenticate(ctx, strictEntity.data.utf8String).unsafeToFuture())
.flatMap {
vinylDNSAuthenticator.authenticate(ctx, strictEntity.data.utf8String).unsafeToFuture()
).flatMap {
case Right(authPrincipal)
provide(authPrincipal)
case Left(e)
@ -159,8 +159,9 @@ trait VinylDNSDirectives[E] extends Directives {
* return error to user.
* - Invoke service call, f, and return the response to the user.
*/
def authenticateAndExecuteWithEntity[A, B](f: (AuthPrincipal, B) => EitherT[IO, E, A])(
g: A => Route)(implicit um: FromRequestUnmarshaller[B]): Route =
def authenticateAndExecuteWithEntity[A, B](
f: (AuthPrincipal, B) => EitherT[IO, E, A]
)(g: A => Route)(implicit um: FromRequestUnmarshaller[B]): Route =
authenticate { authPrincipal =>
entity(as[B]) { deserializedEntity =>
onSuccess(f(authPrincipal, deserializedEntity).value.unsafeToFuture()) {

View File

@ -84,32 +84,34 @@ object VinylDNSService {
req: HttpRequest =>
{
val startTime = System.currentTimeMillis()
r: Any =>
{
val endTime = System.currentTimeMillis()
val duration = endTime - startTime
doNotLog.contains(req.uri.path) match {
case false => {
r match {
case res: HttpResponse =>
Some(LogEntry(VinylDNSService.logMessage(req, Some(res), duration), InfoLevel))
case res: Complete =>
Some(
LogEntry(
VinylDNSService.logMessage(req, Some(res.response), duration),
InfoLevel))
case _: Rejected =>
Some(LogEntry(VinylDNSService.logMessage(req, None, duration), ErrorLevel))
case x => // this can happen if sealRoute below cannot convert into a response.
val res = HttpResponse(
status = StatusCodes.InternalServerError,
entity = HttpEntity(x.toString))
Some(LogEntry(VinylDNSService.logMessage(req, Some(res), duration), ErrorLevel))
}
r: Any => {
val endTime = System.currentTimeMillis()
val duration = endTime - startTime
doNotLog.contains(req.uri.path) match {
case false => {
r match {
case res: HttpResponse =>
Some(LogEntry(VinylDNSService.logMessage(req, Some(res), duration), InfoLevel))
case res: Complete =>
Some(
LogEntry(
VinylDNSService.logMessage(req, Some(res.response), duration),
InfoLevel
)
)
case _: Rejected =>
Some(LogEntry(VinylDNSService.logMessage(req, None, duration), ErrorLevel))
case x => // this can happen if sealRoute below cannot convert into a response.
val res = HttpResponse(
status = StatusCodes.InternalServerError,
entity = HttpEntity(x.toString)
)
Some(LogEntry(VinylDNSService.logMessage(req, Some(res), duration), ErrorLevel))
}
case true => None
}
case true => None
}
}
}
}
@ -122,7 +124,8 @@ object VinylDNSService {
HttpResponse(
status = StatusCodes.BadRequest,
entity = HttpEntity(ContentTypes.`application/json`, msg)
))
)
)
}
.handleNotFound {
extractUnmatchedPath { p =>
@ -141,8 +144,8 @@ class VinylDNSService(
val recordSetService: RecordSetServiceAlgebra,
val batchChangeService: BatchChangeServiceAlgebra,
val collectorRegistry: CollectorRegistry,
authPrincipalProvider: AuthPrincipalProvider)
extends PingRoute
authPrincipalProvider: AuthPrincipalProvider
) extends PingRoute
with HealthCheckRoute
with BlueGreenRoute
with StatusRoute
@ -167,9 +170,10 @@ class VinylDNSService(
Uri.Path("/color"),
Uri.Path("/ping"),
Uri.Path("/status"),
Uri.Path("/metrics/prometheus"))
Uri.Path("/metrics/prometheus")
)
val unloggedRoutes
: Route = healthCheckRoute ~ pingRoute ~ colorRoute ~ statusRoute ~ prometheusRoute
: Route = healthCheckRoute ~ pingRoute ~ colorRoute ~ statusRoute ~ prometheusRoute
val allRoutes: Route = unloggedRoutes ~
batchChangeRoute ~

View File

@ -60,7 +60,8 @@ class ZoneRoute(zoneService: ZoneServiceAlgebra, val vinylDNSAuthenticator: Viny
(post & monitor("Endpoint.createZone")) {
authenticateAndExecuteWithEntity[ZoneCommandResult, CreateZoneInput](
(authPrincipal, createZoneInput) =>
zoneService.connectToZone(encrypt(createZoneInput), authPrincipal)) { chg =>
zoneService.connectToZone(encrypt(createZoneInput), authPrincipal)
) { chg =>
complete(StatusCodes.Accepted, chg)
}
} ~
@ -69,19 +70,24 @@ class ZoneRoute(zoneService: ZoneServiceAlgebra, val vinylDNSAuthenticator: Viny
"nameFilter".?,
"startFrom".as[String].?,
"maxItems".as[Int].?(DEFAULT_MAX_ITEMS),
"ignoreAccess".as[Boolean].?(false)) {
"ignoreAccess".as[Boolean].?(false)
) {
(
nameFilter: Option[String],
startFrom: Option[String],
maxItems: Int,
ignoreAccess: Boolean) =>
ignoreAccess: Boolean
) =>
{
handleRejections(invalidQueryHandler) {
validate(
0 < maxItems && maxItems <= MAX_ITEMS_LIMIT,
s"maxItems was $maxItems, maxItems must be between 0 and $MAX_ITEMS_LIMIT") {
authenticateAndExecute(zoneService
.listZones(_, nameFilter, startFrom, maxItems, ignoreAccess)) { result =>
s"maxItems was $maxItems, maxItems must be between 0 and $MAX_ITEMS_LIMIT"
) {
authenticateAndExecute(
zoneService
.listZones(_, nameFilter, startFrom, maxItems, ignoreAccess)
) { result =>
complete(StatusCodes.OK, result)
}
}
@ -111,7 +117,8 @@ class ZoneRoute(zoneService: ZoneServiceAlgebra, val vinylDNSAuthenticator: Viny
(put & monitor("Endpoint.updateZone")) {
authenticateAndExecuteWithEntity[ZoneCommandResult, UpdateZoneInput](
(authPrincipal, updateZoneInput) =>
zoneService.updateZone(encrypt(updateZoneInput), authPrincipal)) { chg =>
zoneService.updateZone(encrypt(updateZoneInput), authPrincipal)
) { chg =>
complete(StatusCodes.Accepted, chg)
}
} ~
@ -135,7 +142,8 @@ class ZoneRoute(zoneService: ZoneServiceAlgebra, val vinylDNSAuthenticator: Viny
handleRejections(invalidQueryHandler) {
validate(
0 < maxItems && maxItems <= DEFAULT_MAX_ITEMS,
s"maxItems was $maxItems, maxItems must be between 0 exclusive and $DEFAULT_MAX_ITEMS inclusive") {
s"maxItems was $maxItems, maxItems must be between 0 exclusive and $DEFAULT_MAX_ITEMS inclusive"
) {
authenticateAndExecute(zoneService.listZoneChanges(id, _, startFrom, maxItems)) {
changes =>
complete(StatusCodes.OK, changes)
@ -147,14 +155,16 @@ class ZoneRoute(zoneService: ZoneServiceAlgebra, val vinylDNSAuthenticator: Viny
} ~
path("zones" / Segment / "acl" / "rules") { id =>
(put & monitor("Endpoint.addZoneACLRule")) {
authenticateAndExecuteWithEntity[ZoneCommandResult, ACLRuleInfo]((authPrincipal, rule) =>
zoneService.addACLRule(id, rule, authPrincipal)) { chg =>
authenticateAndExecuteWithEntity[ZoneCommandResult, ACLRuleInfo](
(authPrincipal, rule) => zoneService.addACLRule(id, rule, authPrincipal)
) { chg =>
complete(StatusCodes.Accepted, chg)
}
} ~
(delete & monitor("Endpoint.deleteZoneACLRule")) {
authenticateAndExecuteWithEntity[ZoneCommandResult, ACLRuleInfo]((authPrincipal, rule) =>
zoneService.deleteACLRule(id, rule, authPrincipal)) { chg =>
authenticateAndExecuteWithEntity[ZoneCommandResult, ACLRuleInfo](
(authPrincipal, rule) => zoneService.deleteACLRule(id, rule, authPrincipal)
) { chg =>
complete(StatusCodes.Accepted, chg)
}
}

View File

@ -43,7 +43,8 @@ trait CatsHelpers {
// Waits for the future to complete, then returns the value as an Either[Throwable, T]
def awaitResultOf[E, T](
f: => IO[Either[E, T]],
duration: FiniteDuration = 1.second): Either[E, T] = {
duration: FiniteDuration = 1.second
): Either[E, T] = {
val timeOut = IO.sleep(duration) *> IO(new RuntimeException("Timed out waiting for result"))
IO.race(timeOut, f).unsafeRunSync().toOption.get
}
@ -76,10 +77,12 @@ trait ValidatedBatchMatcherImprovements {
MatchResult(
left.contains(expectedChange.validNel),
s"ValidatedBatch $left does not contain $expectedChange",
s"ValidatedBatch $left contains $expectedChange")
s"ValidatedBatch $left contains $expectedChange"
)
}
def containChangeForValidation(
expectedChange: ChangeForValidation): ValidatedBatchContainsChangeForValidation =
expectedChange: ChangeForValidation
): ValidatedBatchContainsChangeForValidation =
new ValidatedBatchContainsChangeForValidation(expectedChange)
}

View File

@ -40,10 +40,12 @@ trait ResultHelpers {
// Waits for the future to complete, then returns the value as an Either[Throwable, T]
def awaitResultOf[T](
f: => IO[Either[Throwable, T]],
duration: FiniteDuration = 1.second): Either[Throwable, T] = {
duration: FiniteDuration = 1.second
): Either[Throwable, T] = {
val timeOut = IO.sleep(duration) *> IO(
TimeoutException("Timed out waiting for result").asInstanceOf[Throwable])
TimeoutException("Timed out waiting for result").asInstanceOf[Throwable]
)
IO.race(timeOut, f.handleError(e => Left(e))).unsafeRunSync() match {
case Left(e) => Left(e)
@ -61,7 +63,8 @@ trait ResultHelpers {
// Assumes that the result of the future operation will fail, this will error on a right disjunction
def leftResultOf[T](
f: => IO[Either[Throwable, T]],
duration: FiniteDuration = 1.second): Throwable = awaitResultOf(f, duration).swap.toOption.get
duration: FiniteDuration = 1.second
): Throwable = awaitResultOf(f, duration).swap.toOption.get
def leftValue[T](t: Either[Throwable, T]): Throwable = t.swap.toOption.get
@ -71,7 +74,8 @@ trait ResultHelpers {
object ValidationTestImprovements extends PropSpec with Matchers with ValidatedMatchers {
implicit class ValidatedNelTestImprovements[DomainValidationError, A](
value: ValidatedNel[DomainValidationError, A]) {
value: ValidatedNel[DomainValidationError, A]
) {
def failures: List[DomainValidationError] = value match {
case Invalid(e) => e.toList

View File

@ -42,7 +42,8 @@ class VinylDNSConfigSpec extends WordSpec with Matchers {
zone,
batchChange,
user,
recordSet)
recordSet
)
}
"assign the correct dynamodb repositories" in {
val dynamodbConfig =

View File

@ -56,8 +56,10 @@ class CommandHandlerSpec
private implicit val timer: Timer[IO] = IO.timer(ExecutionContext.global)
private implicit val cs: ContextShift[IO] =
IO.contextShift(scala.concurrent.ExecutionContext.global)
private val messages = for { i <- 0 to 10 } yield
TestCommandMessage(pendingCreateAAAA, i.toString)
private val messages = for { i <- 0 to 10 } yield TestCommandMessage(
pendingCreateAAAA,
i.toString
)
private val count = MessageCount(10).right.value
private val mockZoneChangeProcessor = mock[ZoneChange => IO[ZoneChange]]
@ -224,7 +226,8 @@ class CommandHandlerSpec
"use the default zone connection when the change zone connection is not defined" in {
val noConnChange =
pendingCreateAAAA.copy(
zone = pendingCreateAAAA.zone.copy(connection = None, transferConnection = None))
zone = pendingCreateAAAA.zone.copy(connection = None, transferConnection = None)
)
val default = defaultConn.copy(primaryServer = "default.conn.test.com")
val defaultConnProcessor =
CommandHandler.processChangeRequests(

View File

@ -79,7 +79,8 @@ class ReverseZoneHelpersSpec
RecordType.PTR,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
ReverseZoneHelpers.convertPTRtoIPv6(zn1, rs1.name) shouldBe "2001:0db8:0000:0000:0000:0000:0567:89ab"
}
@ -92,7 +93,8 @@ class ReverseZoneHelpersSpec
RecordType.PTR,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
ReverseZoneHelpers.convertPTRtoIPv6(zn1, rs1.name) shouldBe "2001:0db8:0000:0000:0000:0000:0567:89ab"
}
@ -105,7 +107,8 @@ class ReverseZoneHelpersSpec
RecordType.PTR,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
ReverseZoneHelpers.convertPTRtoIPv6(zn1, rs1.name) shouldBe "2001:0db8:0000:0000:0000:0000:0567:89ab"
}
@ -179,7 +182,8 @@ class ReverseZoneHelpersSpec
RecordType.PTR,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
val znFalse = Zone("5.b.e.f.9.d.2.f.9.5.c.c.7.4.a.a.8.ip6.arpa.", "email")
val rsFalse = RecordSet(
"id",
@ -187,7 +191,8 @@ class ReverseZoneHelpersSpec
RecordType.PTR,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
ReverseZoneHelpers.recordsetIsWithinCidrMask(mask, znTrue, rsTrue.name) shouldBe true
ReverseZoneHelpers.recordsetIsWithinCidrMask(mask, znFalse, rsFalse.name) shouldBe false
@ -202,7 +207,8 @@ class ReverseZoneHelpersSpec
RecordType.PTR,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
val znFalse = Zone("5.b.e.f.9.d.2.f.9.5.c.c.7.4.a.a.8.ip6.arpa.", "email")
val rsFalse = RecordSet(
"id",
@ -210,7 +216,8 @@ class ReverseZoneHelpersSpec
RecordType.PTR,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
ReverseZoneHelpers.recordsetIsWithinCidrMask(mask, znTrue, rsTrue.name) shouldBe true
ReverseZoneHelpers.recordsetIsWithinCidrMask(mask, znFalse, rsFalse.name) shouldBe false
@ -225,7 +232,8 @@ class ReverseZoneHelpersSpec
RecordType.PTR,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
val znFalse = Zone("5.b.e.f.9.d.2.f.9.5.c.c.7.4.a.a.8.ip6.arpa.", "email")
val rsFalse = RecordSet(
"id",
@ -233,7 +241,8 @@ class ReverseZoneHelpersSpec
RecordType.PTR,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
ReverseZoneHelpers.recordsetIsWithinCidrMask(mask, znTrue, rsTrue.name) shouldBe true
ReverseZoneHelpers.recordsetIsWithinCidrMask(mask, znFalse, rsFalse.name) shouldBe false

View File

@ -91,7 +91,8 @@ class AccessValidationsSpec
"return true if the user is an admin or super user" in {
val auth = okAuth.copy(
signedInUser = okAuth.signedInUser.copy(isSuper = true),
memberGroupIds = Seq.empty)
memberGroupIds = Seq.empty
)
accessValidationTest.canSeeZone(auth, okZone) should be(right)
}
@ -105,7 +106,8 @@ class AccessValidationsSpec
"return true if the user is a support admin" in {
val supportAuth = okAuth.copy(
signedInUser = okAuth.signedInUser.copy(isSupport = true),
memberGroupIds = Seq.empty)
memberGroupIds = Seq.empty
)
accessValidationTest.canSeeZone(supportAuth, okZone) should be(right)
}
@ -119,7 +121,8 @@ class AccessValidationsSpec
"return a NotAuthorizedError if the user is not admin or super user" in {
val error = leftValue(
accessValidationTest
.canChangeZone(okAuth, zoneNotAuthorized.name, zoneNotAuthorized.adminGroupId))
.canChangeZone(okAuth, zoneNotAuthorized.name, zoneNotAuthorized.adminGroupId)
)
error shouldBe a[NotAuthorizedError]
}
@ -130,17 +133,20 @@ class AccessValidationsSpec
"return true if the user is an admin and a support user" in {
val auth = okAuth.copy(
signedInUser = okAuth.signedInUser.copy(isSupport = true),
memberGroupIds = Seq(okGroup.id))
memberGroupIds = Seq(okGroup.id)
)
accessValidationTest.canChangeZone(auth, okZone.name, okZone.adminGroupId) should be(right)
}
"return a NotAuthorizedError if the user is a support user only" in {
val auth = okAuth.copy(
signedInUser = okAuth.signedInUser.copy(isSupport = true),
memberGroupIds = Seq.empty)
memberGroupIds = Seq.empty
)
val error = leftValue(
accessValidationTest
.canChangeZone(auth, okZone.name, okZone.adminGroupId))
.canChangeZone(auth, okZone.name, okZone.adminGroupId)
)
error shouldBe a[NotAuthorizedError]
}
}
@ -149,25 +155,29 @@ class AccessValidationsSpec
"return a NotAuthorizedError if the user has AccessLevel.NoAccess" in {
val error = leftValue(
accessValidationTest
.canAddRecordSet(userAuthNone, "test", RecordType.A, zoneInNone))
.canAddRecordSet(userAuthNone, "test", RecordType.A, zoneInNone)
)
error shouldBe a[NotAuthorizedError]
}
"return a NotAuthorizedError if the user has AccessLevel.Read" in {
val error = leftValue(
accessValidationTest
.canAddRecordSet(userAuthRead, "test", RecordType.A, zoneInRead))
.canAddRecordSet(userAuthRead, "test", RecordType.A, zoneInRead)
)
error shouldBe a[NotAuthorizedError]
}
"return true if the user has AccessLevel.Write" in {
accessValidationTest.canAddRecordSet(userAuthWrite, "test", RecordType.A, zoneInWrite) should be(
right)
right
)
}
"return true if the user has AccessLevel.Delete" in {
accessValidationTest.canAddRecordSet(userAuthDelete, "test", RecordType.A, zoneInDelete) should be(
right)
right
)
}
"return a NotAuthorizedError if the user is a test user in a non-test zone" in {
@ -175,7 +185,8 @@ class AccessValidationsSpec
val error = leftValue(
accessValidationTest
.canAddRecordSet(auth, "test", RecordType.A, okZone))
.canAddRecordSet(auth, "test", RecordType.A, okZone)
)
error shouldBe a[NotAuthorizedError]
}
@ -197,7 +208,8 @@ class AccessValidationsSpec
userAuthWrite,
"someRecordName",
RecordType.NS,
zoneInWrite) should be(right)
zoneInWrite
) should be(right)
}
"return true if recordset matches the global ACL" in {
@ -205,7 +217,8 @@ class AccessValidationsSpec
userAuthGlobalAcl,
"someRecordName",
RecordType.A,
okZone.copy(name = "foo.comcast.com")) should be(right)
okZone.copy(name = "foo.comcast.com")
) should be(right)
}
"return false if the record set does not match the global ACL" in {
@ -213,7 +226,8 @@ class AccessValidationsSpec
val error = leftValue(
globalAclTest
.canAddRecordSet(auth, "test-foo", RecordType.A, okZone))
.canAddRecordSet(auth, "test-foo", RecordType.A, okZone)
)
error shouldBe a[NotAuthorizedError]
}
@ -231,14 +245,16 @@ class AccessValidationsSpec
"return a NotAuthorizedError if the user has AccessLevel.NoAccess" in {
val error = leftValue(
accessValidationTest
.canUpdateRecordSet(userAuthNone, "test", RecordType.A, zoneInNone, None))
.canUpdateRecordSet(userAuthNone, "test", RecordType.A, zoneInNone, None)
)
error shouldBe a[NotAuthorizedError]
}
"return a NotAuthorizedError if the user has AccessLevel.Read" in {
val error = leftValue(
accessValidationTest
.canUpdateRecordSet(userAuthRead, "test", RecordType.A, zoneInRead, None))
.canUpdateRecordSet(userAuthRead, "test", RecordType.A, zoneInRead, None)
)
error shouldBe a[NotAuthorizedError]
}
@ -248,7 +264,8 @@ class AccessValidationsSpec
"test",
RecordType.A,
zoneInWrite,
None) should be(right)
None
) should be(right)
}
"return true if the user has AccessLevel.Delete" in {
@ -257,7 +274,8 @@ class AccessValidationsSpec
val userAcl = ACLRule(AccessLevel.Delete, userId = Some(userAuth.userId), groupId = None)
val zoneIn = zoneNotAuthorized.copy(acl = ZoneACL(Set(userAcl)))
accessValidationTest.canUpdateRecordSet(userAuth, "test", RecordType.A, zoneIn, None) should be(
right)
right
)
}
"return true if the user is in the owner group and the zone is shared" in {
@ -293,7 +311,8 @@ class AccessValidationsSpec
val error = leftValue(
accessValidationTest
.canUpdateRecordSet(auth, "test", RecordType.A, okZone, None))
.canUpdateRecordSet(auth, "test", RecordType.A, okZone, None)
)
error shouldBe a[NotAuthorizedError]
}
@ -311,7 +330,8 @@ class AccessValidationsSpec
"someRecordName",
RecordType.A,
okZone.copy(name = "foo.comcast.com"),
None) should be(right)
None
) should be(right)
}
"return false if the record set does not match the global ACL" in {
@ -319,7 +339,8 @@ class AccessValidationsSpec
val error = leftValue(
globalAclTest
.canUpdateRecordSet(auth, "test-foo", RecordType.A, okZone, None))
.canUpdateRecordSet(auth, "test-foo", RecordType.A, okZone, None)
)
error shouldBe a[NotAuthorizedError]
}
@ -339,21 +360,24 @@ class AccessValidationsSpec
"return a NotAuthorizedError if the user has AccessLevel.NoAccess" in {
val error = leftValue(
accessValidationTest
.canDeleteRecordSet(userAuthNone, "test", RecordType.A, zoneInNone, None))
.canDeleteRecordSet(userAuthNone, "test", RecordType.A, zoneInNone, None)
)
error shouldBe a[NotAuthorizedError]
}
"return a NotAuthorizedError if the user has AccessLevel.Read" in {
val error = leftValue(
accessValidationTest
.canDeleteRecordSet(userAuthRead, "test", RecordType.A, zoneInRead, None))
.canDeleteRecordSet(userAuthRead, "test", RecordType.A, zoneInRead, None)
)
error shouldBe a[NotAuthorizedError]
}
"return a NotAuthorizedError if the user has AccessLevel.Write" in {
val error = leftValue(
accessValidationTest
.canDeleteRecordSet(userAuthWrite, "test", RecordType.A, zoneInWrite, None))
.canDeleteRecordSet(userAuthWrite, "test", RecordType.A, zoneInWrite, None)
)
error shouldBe a[NotAuthorizedError]
}
@ -363,7 +387,8 @@ class AccessValidationsSpec
"test",
RecordType.A,
zoneInDelete,
None) should be(right)
None
) should be(right)
}
"return a NotAuthorizedError if the user is a test user in a non-test zone" in {
@ -371,7 +396,8 @@ class AccessValidationsSpec
val error = leftValue(
accessValidationTest
.canDeleteRecordSet(auth, "test", RecordType.A, okZone, None))
.canDeleteRecordSet(auth, "test", RecordType.A, okZone, None)
)
error shouldBe a[NotAuthorizedError]
}
@ -389,7 +415,8 @@ class AccessValidationsSpec
"someRecordName",
RecordType.A,
okZone.copy(name = "foo.comcast.com"),
None) should be(right)
None
) should be(right)
}
"return false if the record set does not match the global ACL" in {
@ -397,7 +424,8 @@ class AccessValidationsSpec
val error = leftValue(
globalAclTest
.canDeleteRecordSet(auth, "test-foo", RecordType.A, okZone, None))
.canDeleteRecordSet(auth, "test-foo", RecordType.A, okZone, None)
)
error shouldBe a[NotAuthorizedError]
}
@ -417,18 +445,21 @@ class AccessValidationsSpec
"return a NotAuthorizedError if the user has AccessLevel.NoAccess" in {
val error = leftValue(
accessValidationTest
.canViewRecordSet(userAuthNone, "test", RecordType.A, zoneInNone, None))
.canViewRecordSet(userAuthNone, "test", RecordType.A, zoneInNone, None)
)
error shouldBe a[NotAuthorizedError]
}
"return true if the user has AccessLevel.Read" in {
accessValidationTest.canViewRecordSet(userAuthRead, "test", RecordType.A, zoneInRead, None) should be(
right)
right
)
}
"return true if the user has AccessLevel.Write" in {
accessValidationTest.canViewRecordSet(userAuthWrite, "test", RecordType.A, zoneInWrite, None) should be(
right)
right
)
}
"return true if the user has AccessLevel.Delete" in {
@ -437,7 +468,8 @@ class AccessValidationsSpec
"test",
RecordType.A,
zoneInDelete,
None) should be(right)
None
) should be(right)
}
"return true if the user is in the recordSet owner group and the recordSet is in a shared zone" in {
@ -447,7 +479,8 @@ class AccessValidationsSpec
recordSet.name,
recordSet.typ,
sharedZone,
recordSet.ownerGroupId) should be(right)
recordSet.ownerGroupId
) should be(right)
}
"return a NotAuthorizedError if the user is in the recordSet owner group but it is not in a shared zone" in {
@ -458,7 +491,9 @@ class AccessValidationsSpec
recordSet.name,
recordSet.typ,
zoneNotAuthorized,
recordSet.ownerGroupId))
recordSet.ownerGroupId
)
)
error shouldBe a[NotAuthorizedError]
}
@ -467,7 +502,8 @@ class AccessValidationsSpec
val error = leftValue(
accessValidationTest
.canViewRecordSet(auth, "test", RecordType.A, okZone, None))
.canViewRecordSet(auth, "test", RecordType.A, okZone, None)
)
error shouldBe a[NotAuthorizedError]
}
@ -485,7 +521,8 @@ class AccessValidationsSpec
"someRecordName",
RecordType.A,
okZone.copy(name = "foo.comcast.com"),
None) should be(right)
None
) should be(right)
}
"return false if the record set does not match the global ACL" in {
@ -493,7 +530,8 @@ class AccessValidationsSpec
val error = leftValue(
globalAclTest
.canViewRecordSet(auth, "test-foo", RecordType.A, okZone, None))
.canViewRecordSet(auth, "test-foo", RecordType.A, okZone, None)
)
error shouldBe a[NotAuthorizedError]
}
@ -518,7 +556,8 @@ class AccessValidationsSpec
mockRecordSet.name,
mockRecordSet.typ,
okZone,
None)
None
)
result shouldBe AccessLevel.Delete
}
@ -530,7 +569,8 @@ class AccessValidationsSpec
sharedZoneRecord.name,
sharedZoneRecord.typ,
sharedZone,
sharedZoneRecord.ownerGroupId)
sharedZoneRecord.ownerGroupId
)
result shouldBe AccessLevel.Delete
}
@ -541,7 +581,8 @@ class AccessValidationsSpec
sharedZoneRecordNoOwnerGroup.name,
RecordType.AAAA,
sharedZone,
None)
None
)
result shouldBe AccessLevel.Delete
}
@ -552,7 +593,8 @@ class AccessValidationsSpec
sharedZoneRecordNotApprovedRecordType.name,
RecordType.MX,
sharedZone,
None)
None
)
result shouldBe AccessLevel.NoAccess
}
@ -563,7 +605,8 @@ class AccessValidationsSpec
notSharedZoneRecordWithOwnerGroup.name,
notSharedZoneRecordWithOwnerGroup.typ,
zoneNotAuthorized,
notSharedZoneRecordWithOwnerGroup.ownerGroupId)
notSharedZoneRecordWithOwnerGroup.ownerGroupId
)
result shouldBe AccessLevel.NoAccess
}
@ -573,7 +616,8 @@ class AccessValidationsSpec
"test",
RecordType.A,
okZone.copy(adminGroupId = "not-a-real-group"),
None)
None
)
result shouldBe AccessLevel.Read
}
@ -583,7 +627,8 @@ class AccessValidationsSpec
"test",
RecordType.A,
okZone.copy(adminGroupId = "not-a-real-group"),
None)
None
)
result shouldBe AccessLevel.Read
}
@ -608,7 +653,8 @@ class AccessValidationsSpec
mockRecordSet.name,
mockRecordSet.typ,
zoneIn,
None)
None
)
result shouldBe AccessLevel.Write
}
@ -713,7 +759,8 @@ class AccessValidationsSpec
RecordType.A,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
val aclRuleA =
ACLRule(AccessLevel.Read, userId = Some(okAuth.userId), recordTypes = Set(RecordType.A))
@ -731,7 +778,8 @@ class AccessValidationsSpec
RecordType.A,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
val aclRuleA =
ACLRule(AccessLevel.Read, userId = Some(okAuth.userId), recordTypes = Set(RecordType.AAAA))
@ -749,13 +797,15 @@ class AccessValidationsSpec
RecordType.A,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
val aclRuleA =
ACLRule(AccessLevel.Write, userId = Some(okAuth.userId), recordTypes = Set(RecordType.A))
val aclRuleMany = ACLRule(
AccessLevel.Read,
userId = Some(okAuth.userId),
recordTypes = Set(RecordType.A, RecordType.AAAA))
recordTypes = Set(RecordType.A, RecordType.AAAA)
)
val zoneAcl = ZoneACL(Set(aclRuleA, aclRuleMany))
val zone = Zone("name", "email", acl = zoneAcl)
@ -771,7 +821,8 @@ class AccessValidationsSpec
RecordType.A,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
val aclRuleA =
ACLRule(AccessLevel.Write, userId = Some(okAuth.userId), recordTypes = Set(RecordType.A))
val aclRuleAll =
@ -791,7 +842,8 @@ class AccessValidationsSpec
RecordType.A,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
val aclRule = userReadAcl.copy(recordMask = Some("rs.*"))
val zoneAcl = ZoneACL(Set(aclRule))
@ -808,7 +860,8 @@ class AccessValidationsSpec
RecordType.A,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
val aclRule = userReadAcl.copy(recordMask = Some("bad.*"))
val zoneAcl = ZoneACL(Set(aclRule))
@ -825,7 +878,8 @@ class AccessValidationsSpec
RecordType.A,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
val aclRuleRM = userReadAcl.copy(recordMask = Some("rs.*"))
val aclRuleAll = userWriteAcl.copy(recordMask = None)
@ -891,7 +945,8 @@ class AccessValidationsSpec
RecordType.PTR,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
val aclRule = userReadAcl.copy(recordMask = Some(".*0.*"))
val zoneAcl = ZoneACL(Set(aclRule))
@ -913,7 +968,8 @@ class AccessValidationsSpec
RecordType.PTR,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
val znFalse = Zone("5.b.e.f.9.d.2.f.9.5.c.c.7.4.a.a.8.ip6.arpa.", "email")
val rsFalse = RecordSet(
"id",
@ -921,7 +977,8 @@ class AccessValidationsSpec
RecordType.PTR,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
accessValidationTest.ruleAppliesToRecordName(rsTrue.name, rsTrue.typ, znTrue, aclRule) shouldBe true
accessValidationTest.ruleAppliesToRecordName(rsFalse.name, rsFalse.typ, znFalse, aclRule) shouldBe false
@ -937,7 +994,8 @@ class AccessValidationsSpec
RecordType.PTR,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
val znFalse = Zone("5.b.e.f.9.d.2.f.9.5.c.c.7.4.a.a.8.ip6.arpa.", "email")
val rsFalse = RecordSet(
"id",
@ -945,7 +1003,8 @@ class AccessValidationsSpec
RecordType.PTR,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
accessValidationTest.ruleAppliesToRecordName(rsTrue.name, rsTrue.typ, znTrue, aclRule) shouldBe true
accessValidationTest.ruleAppliesToRecordName(rsFalse.name, rsFalse.typ, znFalse, aclRule) shouldBe false
@ -961,7 +1020,8 @@ class AccessValidationsSpec
RecordType.PTR,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
val znFalse = Zone("5.b.e.f.9.d.2.f.9.5.c.c.7.4.a.a.8.ip6.arpa.", "email")
val rsFalse = RecordSet(
"id",
@ -969,7 +1029,8 @@ class AccessValidationsSpec
RecordType.PTR,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
accessValidationTest.ruleAppliesToRecordName(rsTrue.name, rsTrue.typ, znTrue, aclRule) shouldBe true
accessValidationTest.ruleAppliesToRecordName(rsFalse.name, rsFalse.typ, znFalse, aclRule) shouldBe false
@ -984,7 +1045,8 @@ class AccessValidationsSpec
RecordType.PTR,
200,
RecordSetStatus.Active,
DateTime.now)
DateTime.now
)
accessValidationTest.ruleAppliesToRecordName(rs.name, rs.typ, zn, aclRule) shouldBe true
}
@ -994,7 +1056,8 @@ class AccessValidationsSpec
val multiRecordList = List("rs1", "rs2", "rs3").map { name =>
RecordSetInfo(
RecordSet("zoneId", name, RecordType.A, 100, RecordSetStatus.Active, DateTime.now()),
None)
None
)
}
"return access level DELETE if the user is admin/super of the zone" in {
@ -1020,7 +1083,8 @@ class AccessValidationsSpec
"return access level Read if there is no ACL rule for the user and user is a support admin" in {
val supportAuth = okAuth.copy(
signedInUser = okAuth.signedInUser.copy(isSupport = true),
memberGroupIds = Seq.empty)
memberGroupIds = Seq.empty
)
val zone = Zone("test", "test")
@ -1034,7 +1098,8 @@ class AccessValidationsSpec
val rs1 =
RecordSetInfo(
RecordSet("zoneId", "rs1", RecordType.A, 100, RecordSetStatus.Active, DateTime.now()),
None)
None
)
val rs2 = rs1.copy(name = "rs2")
val rs3 = rs1.copy(name = "rs3")
val recordList = List(rs1, rs2, rs3)
@ -1051,7 +1116,8 @@ class AccessValidationsSpec
val expected = List(
RecordSetListInfo(rs1, AccessLevel.Write),
RecordSetListInfo(rs2, AccessLevel.NoAccess),
RecordSetListInfo(rs3, AccessLevel.Read))
RecordSetListInfo(rs3, AccessLevel.Read)
)
result shouldBe expected
}

View File

@ -33,7 +33,8 @@ class GlobalAclSpec
import vinyldns.core.TestZoneData._
private val globalAcls = GlobalAcls(
List(GlobalAcl(List(okGroup.id, dummyGroup.id), List(".*foo.*", ".*bar.com."))))
List(GlobalAcl(List(okGroup.id, dummyGroup.id), List(".*foo.*", ".*bar.com.")))
)
"isAuthorized" should {
"return false if the acl list is empty" in {
@ -54,7 +55,8 @@ class GlobalAclSpec
"foo",
RecordType.PTR,
zoneIp4,
List(PTRData("foo.com"), PTRData("bar.com"))) shouldBe true
List(PTRData("foo.com"), PTRData("bar.com"))
) shouldBe true
}
"return false for a PTR record if one of the PTR records does not match an acl" in {
globalAcls.isAuthorized(
@ -62,7 +64,8 @@ class GlobalAclSpec
"foo",
RecordType.PTR,
zoneIp4,
List(PTRData("foo.com"), PTRData("blah.net"))) shouldBe false
List(PTRData("foo.com"), PTRData("blah.net"))
) shouldBe false
}
"return false for a PTR record if the record data is empty" in {
globalAcls.isAuthorized(okAuth, "foo", RecordType.PTR, zoneIp4, Nil) shouldBe false

View File

@ -40,7 +40,8 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
name: String,
recordData: RecordData,
typ: RecordType = A,
zone: Zone = okZone) = {
zone: Zone = okZone
) = {
val fqdn = s"$name.${zone.name}"
SingleAddChange(
Some(zone.id),
@ -53,7 +54,8 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
SingleChangeStatus.Pending,
None,
None,
None)
None
)
}
private def makeSingleDeleteRRSetChange(name: String, typ: RecordType, zone: Zone = okZone) = {
@ -68,25 +70,30 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
SingleChangeStatus.Pending,
None,
None,
None)
None
)
}
private def makeAddChangeForValidation(
recordName: String,
recordData: RecordData,
typ: RecordType = RecordType.A): AddChangeForValidation =
typ: RecordType = RecordType.A
): AddChangeForValidation =
AddChangeForValidation(
okZone,
s"$recordName",
AddChangeInput(s"$recordName.ok.", typ, Some(123), recordData))
AddChangeInput(s"$recordName.ok.", typ, Some(123), recordData)
)
private def makeDeleteRRSetChangeForValidation(
recordName: String,
typ: RecordType = RecordType.A): DeleteRRSetChangeForValidation =
typ: RecordType = RecordType.A
): DeleteRRSetChangeForValidation =
DeleteRRSetChangeForValidation(
okZone,
s"$recordName",
DeleteRRSetChangeInput(s"$recordName.ok.", typ))
DeleteRRSetChangeInput(s"$recordName.ok.", typ)
)
private val addSingleChangesGood = List(
makeSingleAddChange("one", AData("1.1.1.1")),
@ -184,7 +191,8 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
RecordSetStatus.Active,
DateTime.now,
None,
List(AData("1.1.1.1")))
List(AData("1.1.1.1"))
)
private val cnameToDelete = RecordSet(
okZone.id,
"cnameToDelete",
@ -193,7 +201,8 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
RecordSetStatus.Active,
DateTime.now,
None,
List(CNAMEData("old.com.")))
List(CNAMEData("old.com."))
)
private val aToUpdate = RecordSet(
okZone.id,
"aToUpdate",
@ -202,7 +211,8 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
RecordSetStatus.Active,
DateTime.now,
None,
List(AData("1.1.1.1")))
List(AData("1.1.1.1"))
)
private val cnameToUpdate = RecordSet(
okZone.id,
"cnameToUpdate",
@ -211,7 +221,8 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
RecordSetStatus.Active,
DateTime.now,
None,
List(CNAMEData("old.com.")))
List(CNAMEData("old.com."))
)
private val txtToUpdate = RecordSet(
okZone.id,
"txtToUpdate",
@ -220,7 +231,8 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
RecordSetStatus.Active,
DateTime.now,
None,
List(TXTData("old")))
List(TXTData("old"))
)
private val txtToDelete = RecordSet(
okZone.id,
"txtToDelete",
@ -229,7 +241,8 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
RecordSetStatus.Active,
DateTime.now,
None,
List(TXTData("test")))
List(TXTData("test"))
)
private val mxToUpdate = RecordSet(
okZone.id,
"mxToUpdate",
@ -238,7 +251,8 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
RecordSetStatus.Active,
DateTime.now,
None,
List(MXData(1, "old.com.")))
List(MXData(1, "old.com."))
)
private val mxToDelete = RecordSet(
okZone.id,
"mxToDelete",
@ -247,7 +261,8 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
RecordSetStatus.Active,
DateTime.now,
None,
List(MXData(1, "delete.com.")))
List(MXData(1, "delete.com."))
)
private def existingRecordSets =
ExistingRecordSets(
List(
@ -258,7 +273,9 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
txtToUpdate,
txtToDelete,
mxToUpdate,
mxToDelete))
mxToDelete
)
)
private val batchChangeRepo = new InMemoryBatchChangeRepository
private val underTest = new BatchChangeConverter(batchChangeRepo, TestMessageQueue)
@ -272,15 +289,18 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
None,
DateTime.now,
addSingleChangesGood,
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
val result = rightResultOf(
underTest
.sendBatchForProcessing(
batchChange,
existingZones,
ChangeForValidationMap(addChangeForValidationGood.map(_.validNel), existingRecordSets),
None)
.value)
None
)
.value
)
val rsChanges = result.recordSetChanges
// validate recordset changes generated from batch
@ -303,7 +323,8 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
None,
DateTime.now,
deleteSingleChangesGood,
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
val result = rightResultOf(
underTest
.sendBatchForProcessing(
@ -311,9 +332,12 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
existingZones,
ChangeForValidationMap(
deleteRRSetChangeForValidationGood.map(_.validNel),
existingRecordSets),
None)
.value)
existingRecordSets
),
None
)
.value
)
val rsChanges = result.recordSetChanges
// validate recordset change basics generated from batch
@ -323,7 +347,8 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
cnameToDelete.name,
rsChanges,
batchChange,
RecordSetChangeType.Delete)
RecordSetChangeType.Delete
)
validateRecordSetChange(txtToDelete.name, rsChanges, batchChange, RecordSetChangeType.Delete)
validateRecordSetChange(mxToDelete.name, rsChanges, batchChange, RecordSetChangeType.Delete)
@ -332,7 +357,8 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
aToDelete.id,
cnameToDelete.id,
txtToDelete.id,
mxToDelete.id)
mxToDelete.id
)
// validate statuses unchanged as returned
result.batchChange shouldBe batchChange
@ -346,7 +372,8 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
None,
DateTime.now,
updateSingleChangesGood,
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
val result = rightResultOf(
underTest
.sendBatchForProcessing(
@ -354,9 +381,12 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
existingZones,
ChangeForValidationMap(
updateChangeForValidationGood.map(_.validNel),
existingRecordSets),
None)
.value)
existingRecordSets
),
None
)
.value
)
val rsChanges = result.recordSetChanges
// validate recordset changes generated from batch
@ -371,7 +401,8 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
aToUpdate.id,
cnameToUpdate.id,
txtToUpdate.id,
mxToUpdate.id)
mxToUpdate.id
)
// validate statuses unchanged as returned
result.batchChange shouldBe batchChange
@ -388,15 +419,18 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
None,
DateTime.now,
changes,
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
val result = rightResultOf(
underTest
.sendBatchForProcessing(
batchChange,
existingZones,
ChangeForValidationMap(changeForValidation.map(_.validNel), existingRecordSets),
None)
.value)
None
)
.value
)
val rsChanges = result.recordSetChanges
// validate recordset changes generated from batch
@ -436,15 +470,18 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
None,
DateTime.now,
List(),
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
val result = rightResultOf(
underTest
.sendBatchForProcessing(
batchChange,
existingZones,
ChangeForValidationMap(List(), existingRecordSets),
None)
.value)
None
)
.value
)
result.batchChange shouldBe batchChange
}
@ -457,15 +494,18 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
None,
DateTime.now,
singleChangesOneBad,
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
val result = rightResultOf(
underTest
.sendBatchForProcessing(
batchWithBadChange,
existingZones,
ChangeForValidationMap(changeForValidationOneBad.map(_.validNel), existingRecordSets),
None)
.value)
None
)
.value
)
val rsChanges = result.recordSetChanges
rsChanges.length shouldBe 3
@ -500,7 +540,8 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
None,
DateTime.now,
singleChangesOneUnsupported,
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
val result = leftResultOf(
underTest
.sendBatchForProcessing(
@ -508,9 +549,12 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
existingZones,
ChangeForValidationMap(
changeForValidationOneUnsupported.map(_.validNel),
existingRecordSets),
None)
.value)
existingRecordSets
),
None
)
.value
)
result shouldBe an[BatchConversionError]
val notSaved: Option[BatchChange] =
@ -533,7 +577,8 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
Set(singleAddChange.recordData),
okUser.id,
None,
None)
None
)
result shouldBe defined
result.foreach(_.recordSet.ownerGroupId shouldBe None)
}
@ -548,7 +593,8 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
Set(singleAddChange.recordData),
okUser.id,
None,
ownerGroupId)
ownerGroupId
)
result shouldBe defined
result.foreach(_.recordSet.ownerGroupId shouldBe ownerGroupId)
}
@ -563,7 +609,8 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
Set(singleAddChange.recordData),
okUser.id,
None,
ownerGroupId)
ownerGroupId
)
result shouldBe defined
result.foreach(_.recordSet.ownerGroupId shouldBe None)
}
@ -630,7 +677,8 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
name: String,
recordSetChanges: List[RecordSetChange],
batchChange: BatchChange,
typ: RecordSetChangeType) = {
typ: RecordSetChangeType
) = {
val singleChangesOut = batchChange.changes.filter { change =>
change.recordName match {
case Some(rn) if rn == name => true
@ -659,7 +707,8 @@ class BatchChangeConverterSpec extends WordSpec with Matchers with CatsHelpers {
private def validateRecordDataCombination(
name: String,
recordSetChanges: List[RecordSetChange],
batchChange: BatchChange) = {
batchChange: BatchChange
) = {
val singleChangesOut = batchChange.changes.filter { change =>
change.recordName match {
case Some(rn) if rn == name => true

View File

@ -42,7 +42,8 @@ class BatchChangeInputSpec extends WordSpec with Matchers {
val input = BatchChangeInput(
None,
List(changeA, changeAAAA, changeCname, changeADotted, changeAAAADotted, changeCnameDotted),
None)
None
)
input.changes(0).inputName shouldBe "apex.test.com."
input.changes(1).inputName shouldBe "aaaa.test.com."
@ -141,7 +142,8 @@ class BatchChangeInputSpec extends WordSpec with Matchers {
BatchChangeInput(
Some("comments"),
List(expectedAddChange, expectedDelChange),
Some("owner"))
Some("owner")
)
BatchChangeInput(change) shouldBe expectedInput
}

View File

@ -87,7 +87,8 @@ class BatchChangeServiceSpec
"cname.55.144.10.in-addr.arpa",
RecordType.CNAME,
ttl,
CNAMEData("testing.cname.com."))
CNAMEData("testing.cname.com.")
)
private val ptrAdd = AddChangeInput("10.144.55.11", RecordType.PTR, ttl, PTRData("ptr"))
private val ptrAdd2 = AddChangeInput("10.144.55.255", RecordType.PTR, ttl, PTRData("ptr"))
private val ptrDelegatedAdd = AddChangeInput("192.0.2.193", RecordType.PTR, ttl, PTRData("ptr"))
@ -121,7 +122,8 @@ class BatchChangeServiceSpec
private val ptrV6AddForVal = AddChangeForValidation(
ipv6PTRZone,
"9.2.3.8.2.4.0.0.0.0.f.f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",
ptrV6Add)
ptrV6Add
)
private val defaultv6Discovery = new V6DiscoveryNibbleBoundaries(5, 16)
@ -136,7 +138,8 @@ class BatchChangeServiceSpec
SingleChangeStatus.Pending,
None,
None,
None)
None
)
private val singleChangeGood = SingleAddChange(
Some(baseZone.id),
@ -184,14 +187,16 @@ class BatchChangeServiceSpec
batchChange: BatchChange,
existingZones: ExistingZones,
groupedChanges: ChangeForValidationMap,
ownerGroupId: Option[String]): BatchResult[BatchConversionOutput] =
ownerGroupId: Option[String]
): BatchResult[BatchConversionOutput] =
batchChange.comments match {
case Some("conversionError") => BatchConversionError(pendingChange).toLeftBatchResult
case Some("checkConverter") =>
// hacking reviewComment to determine if things were sent to the converter
BatchConversionOutput(
batchChange.copy(reviewComment = Some("batchSentToConverter")),
List()).toRightBatchResult
List()
).toRightBatchResult
case _ => BatchConversionOutput(batchChange, List()).toRightBatchResult
}
}
@ -202,7 +207,8 @@ class BatchChangeServiceSpec
zoneId: String,
name: String,
typ: RecordType,
recordData: Option[List[RecordData]] = None): RecordSet = {
recordData: Option[List[RecordData]] = None
): RecordSet = {
val records = recordData.getOrElse(List())
RecordSet(zoneId, name, typ, 100, RecordSetStatus.Active, DateTime.now(), records = records)
}
@ -214,7 +220,8 @@ class BatchChangeServiceSpec
nonApexAddForVal.zone.id,
nonApexAddForVal.recordName,
TXT,
recordData = Some(List(TXTData("some data"))))
recordData = Some(List(TXTData("some data")))
)
private val existingPtr: RecordSet =
makeRS(ptrAddForVal.zone.id, ptrAddForVal.recordName, PTR)
private val existingPtrDelegated: RecordSet =
@ -233,7 +240,8 @@ class BatchChangeServiceSpec
(existingPtrDelegated, "193.64/25.55.144.10.in-addr.arpa."),
(
existingPtrV6,
"9.2.3.8.2.4.0.0.0.0.f.f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.2.ip6.arpa."),
"9.2.3.8.2.4.0.0.0.0.f.f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.2.ip6.arpa."
),
(deletedZoneApex, "apex.test.com.")
)
@ -304,7 +312,8 @@ class BatchChangeServiceSpec
otherPTRZone,
ipv6PTR16Zone,
ipv6PTR17Zone,
ipv6PTR18Zone)
ipv6PTR18Zone
)
override def getZonesByNames(zoneNames: Set[String]): IO[Set[Zone]] =
IO.pure(dbZones.filter(zn => zoneNames.contains(zn.name)))
@ -338,7 +347,8 @@ class BatchChangeServiceSpec
override def getUsers(
userIds: Set[String],
startFrom: Option[String],
maxItems: Option[Int]): IO[ListUsersResults] =
maxItems: Option[Int]
): IO[ListUsersResults] =
IO.pure(ListUsersResults(Seq(superUser), None))
}
@ -415,7 +425,8 @@ class BatchChangeServiceSpec
"2001:0000:0000:0001:0000:ff00:0042:8329",
RecordType.PTR,
ttl,
PTRData("ptr"))
PTRData("ptr")
)
val input = BatchChangeInput(None, List(ptr), Some(authGrp.id))
@ -444,7 +455,8 @@ class BatchChangeServiceSpec
"2001:0000:0000:0001:0000:ff00:0042:8329",
RecordType.PTR,
ttl,
PTRData("ptr"))
PTRData("ptr")
)
val input = BatchChangeInput(None, List(ptr), Some(authGrp.id))
@ -476,7 +488,8 @@ class BatchChangeServiceSpec
result shouldBe
InvalidBatchChangeInput(
List(NotAMemberOfOwnerGroup(ownerGroupId, notAuth.signedInUser.userName)))
List(NotAMemberOfOwnerGroup(ownerGroupId, notAuth.signedInUser.userName))
)
}
"succeed if owner group ID is provided and user is a member of the group" in {
@ -493,7 +506,8 @@ class BatchChangeServiceSpec
rightResultOf(
underTest
.applyBatchChange(input, AuthPrincipal(superUser, Seq(baseZone.adminGroupId)), true)
.value)
.value
)
result.changes.length shouldBe 1
}
@ -525,7 +539,8 @@ class BatchChangeServiceSpec
None,
DateTime.now,
List(pendingChange),
approvalStatus = BatchChangeApprovalStatus.PendingReview)
approvalStatus = BatchChangeApprovalStatus.PendingReview
)
batchChangeRepo.save(batchChange)
doReturn(IO.unit).when(mockNotifier).notify(any[Notification[_]])
@ -536,8 +551,10 @@ class BatchChangeServiceSpec
.rejectBatchChange(
batchChange.id,
supportUserAuth,
RejectBatchChangeInput(Some("review comment")))
.value)
RejectBatchChangeInput(Some("review comment"))
)
.value
)
result.status shouldBe BatchChangeStatus.Rejected
result.approvalStatus shouldBe BatchChangeApprovalStatus.ManuallyRejected
@ -557,7 +574,8 @@ class BatchChangeServiceSpec
None,
DateTime.now,
List(pendingChange),
approvalStatus = BatchChangeApprovalStatus.PendingReview)
approvalStatus = BatchChangeApprovalStatus.PendingReview
)
batchChangeRepo.save(batchChange)
val rejectAuth = AuthPrincipal(supportUser.copy(isTest = true), List())
@ -565,7 +583,8 @@ class BatchChangeServiceSpec
rightResultOf(
underTestManualEnabled
.rejectBatchChange(batchChange.id, rejectAuth, RejectBatchChangeInput(Some("bad")))
.value)
.value
)
result.status shouldBe BatchChangeStatus.Rejected
}
@ -577,7 +596,8 @@ class BatchChangeServiceSpec
None,
DateTime.now,
List(pendingChange),
approvalStatus = BatchChangeApprovalStatus.PendingReview)
approvalStatus = BatchChangeApprovalStatus.PendingReview
)
batchChangeRepo.save(batchChange)
val rejectAuth = AuthPrincipal(supportUser.copy(isTest = true), List())
@ -585,7 +605,8 @@ class BatchChangeServiceSpec
leftResultOf(
underTestManualEnabled
.rejectBatchChange(batchChange.id, rejectAuth, RejectBatchChangeInput(Some("bad")))
.value)
.value
)
result shouldBe UserNotAuthorizedError(batchChange.id)
}
@ -597,14 +618,16 @@ class BatchChangeServiceSpec
None,
DateTime.now,
List(),
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChange)
val result =
leftResultOf(
underTest
.rejectBatchChange(batchChange.id, supportUserAuth, RejectBatchChangeInput())
.value)
.value
)
result shouldBe BatchChangeNotPendingReview(batchChange.id)
}
@ -617,12 +640,14 @@ class BatchChangeServiceSpec
None,
DateTime.now,
List(),
approvalStatus = BatchChangeApprovalStatus.PendingReview)
approvalStatus = BatchChangeApprovalStatus.PendingReview
)
batchChangeRepo.save(batchChange)
val result =
leftResultOf(
underTest.rejectBatchChange(batchChange.id, auth, RejectBatchChangeInput()).value)
underTest.rejectBatchChange(batchChange.id, auth, RejectBatchChangeInput()).value
)
result shouldBe UserNotAuthorizedError(batchChange.id)
}
@ -635,12 +660,14 @@ class BatchChangeServiceSpec
None,
DateTime.now,
List(),
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChange)
val result =
leftResultOf(
underTest.rejectBatchChange(batchChange.id, auth, RejectBatchChangeInput()).value)
underTest.rejectBatchChange(batchChange.id, auth, RejectBatchChangeInput()).value
)
result shouldBe UserNotAuthorizedError(batchChange.id)
}
@ -665,8 +692,10 @@ class BatchChangeServiceSpec
.approveBatchChange(
batchChangeNeedsApproval.id,
supportUserAuth,
ApproveBatchChangeInput(Some("reviewed!")))
.value)
ApproveBatchChangeInput(Some("reviewed!"))
)
.value
)
result.userId shouldBe batchChangeNeedsApproval.userId
result.userName shouldBe batchChangeNeedsApproval.userName
@ -691,8 +720,10 @@ class BatchChangeServiceSpec
.approveBatchChange(
batchChangeNeedsApproval.id,
auth,
ApproveBatchChangeInput(Some("reviewed!")))
.value)
ApproveBatchChangeInput(Some("reviewed!"))
)
.value
)
result shouldBe UserNotAuthorizedError(batchChangeNeedsApproval.id)
}
@ -705,7 +736,8 @@ class BatchChangeServiceSpec
leftResultOf(
underTest
.approveBatchChange(batchChange.id, supportUserAuth, ApproveBatchChangeInput())
.value)
.value
)
result shouldBe BatchChangeNotPendingReview(batchChange.id)
}
@ -717,7 +749,8 @@ class BatchChangeServiceSpec
leftResultOf(
underTest
.approveBatchChange(batchChangeNeedsApproval.id, auth, ApproveBatchChangeInput())
.value)
.value
)
result shouldBe UserNotAuthorizedError(batchChangeNeedsApproval.id)
}
@ -729,7 +762,8 @@ class BatchChangeServiceSpec
val result =
leftResultOf(
underTest.approveBatchChange(batchChange.id, auth, ApproveBatchChangeInput()).value)
underTest.approveBatchChange(batchChange.id, auth, ApproveBatchChangeInput()).value
)
result shouldBe UserNotAuthorizedError(batchChange.id)
}
@ -742,14 +776,16 @@ class BatchChangeServiceSpec
None,
DateTime.now,
List(),
approvalStatus = BatchChangeApprovalStatus.PendingReview)
approvalStatus = BatchChangeApprovalStatus.PendingReview
)
batchChangeRepo.save(batchChange)
val result =
leftResultOf(
underTest
.approveBatchChange(batchChange.id, superUserAuth, ApproveBatchChangeInput())
.value)
.value
)
result shouldBe BatchRequesterNotFound("someOtherUserId", "someUn")
}
@ -764,14 +800,16 @@ class BatchChangeServiceSpec
None,
DateTime.now,
List(pendingChange),
approvalStatus = BatchChangeApprovalStatus.PendingReview)
approvalStatus = BatchChangeApprovalStatus.PendingReview
)
batchChangeRepo.save(batchChange)
val result =
rightResultOf(
underTest
.cancelBatchChange(batchChange.id, auth)
.value)
.value
)
result.status shouldBe BatchChangeStatus.Cancelled
result.approvalStatus shouldBe BatchChangeApprovalStatus.Cancelled
@ -787,7 +825,8 @@ class BatchChangeServiceSpec
None,
DateTime.now,
List(pendingChange),
approvalStatus = BatchChangeApprovalStatus.PendingReview)
approvalStatus = BatchChangeApprovalStatus.PendingReview
)
batchChangeRepo.save(batchChange)
val result =
@ -804,14 +843,16 @@ class BatchChangeServiceSpec
None,
DateTime.now,
List(),
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChange)
val result =
leftResultOf(
underTest
.cancelBatchChange(batchChange.id, auth)
.value)
.value
)
result shouldBe BatchChangeNotPendingReview(batchChange.id)
}
@ -824,14 +865,16 @@ class BatchChangeServiceSpec
None,
DateTime.now,
List(),
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChange)
val result =
leftResultOf(
underTest
.cancelBatchChange(batchChange.id, supportUserAuth)
.value)
.value
)
result shouldBe BatchChangeNotPendingReview(batchChange.id)
}
@ -846,7 +889,8 @@ class BatchChangeServiceSpec
None,
DateTime.now,
List(),
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChange)
val result = rightResultOf(underTest.getBatchChange(batchChange.id, auth).value)
@ -867,7 +911,8 @@ class BatchChangeServiceSpec
None,
DateTime.now,
List(),
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChange)
val result = leftResultOf(underTest.getBatchChange(batchChange.id, notAuth).value)
@ -882,7 +927,8 @@ class BatchChangeServiceSpec
None,
DateTime.now,
List(),
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChange)
val authSuper = notAuth.copy(signedInUser = notAuth.signedInUser.copy(isSuper = true))
@ -899,7 +945,8 @@ class BatchChangeServiceSpec
None,
DateTime.now,
List(),
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChange)
val authSuper = notAuth.copy(signedInUser = notAuth.signedInUser.copy(isSupport = true))
@ -918,7 +965,8 @@ class BatchChangeServiceSpec
DateTime.now,
List(),
ownerGroupId = Some(okGroup.id),
BatchChangeApprovalStatus.AutoApproved)
BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChange)
val result = rightResultOf(underTest.getBatchChange(batchChange.id, auth).value)
@ -934,7 +982,8 @@ class BatchChangeServiceSpec
DateTime.now,
List(),
ownerGroupId = Some("no-existo"),
BatchChangeApprovalStatus.AutoApproved)
BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChange)
val result = rightResultOf(underTest.getBatchChange(batchChange.id, auth).value)
@ -973,7 +1022,8 @@ class BatchChangeServiceSpec
ptrAddForVal.validNel,
ptrDelegatedAddForVal.validNel,
ptrV6AddForVal.validNel,
error)
error
)
val zoneMap = ExistingZones(Set(apexZone, baseZone, ptrZone, delegatedPTRZone, ipv6PTRZone))
val result = await(underTest.getExistingRecordSets(in, zoneMap))
@ -989,7 +1039,8 @@ class BatchChangeServiceSpec
ptrAddForVal.validNel,
ptrDelegatedAddForVal.validNel,
ptrV6AddForVal.validNel,
error)
error
)
val zoneMap = ExistingZones(Set(apexZone, baseZone, ptrZone, ipv6PTRZone))
val result = await(underTest.getExistingRecordSets(in, zoneMap))
@ -1184,7 +1235,8 @@ class BatchChangeServiceSpec
underTest.zoneDiscovery(List(onlyApexAddA.validNel), ExistingZones(Set(onlyApexZone)))
result should containChangeForValidation(
AddChangeForValidation(onlyApexZone, "only.apex.exists.", onlyApexAddA))
AddChangeForValidation(onlyApexZone, "only.apex.exists.", onlyApexAddA)
)
}
"map the batch change input to the base zone if only the base zone exists for A records" in {
@ -1192,7 +1244,8 @@ class BatchChangeServiceSpec
underTest.zoneDiscovery(List(onlyBaseAddAAAA.validNel), ExistingZones(Set(onlyBaseZone)))
result should containChangeForValidation(
AddChangeForValidation(onlyBaseZone, "have", onlyBaseAddAAAA))
AddChangeForValidation(onlyBaseZone, "have", onlyBaseAddAAAA)
)
}
"map the batch change input to the base zone only for CNAME records" in {
@ -1218,7 +1271,8 @@ class BatchChangeServiceSpec
val discovered = underTest.zoneDiscovery(
List(aApex.validNel, aNormal.validNel, aDotted.validNel),
ExistingZones(Set(apexZone, baseZone)))
ExistingZones(Set(apexZone, baseZone))
)
discovered.getValid shouldBe expected
}
@ -1239,7 +1293,8 @@ class BatchChangeServiceSpec
val discovered = underTest.zoneDiscovery(
List(txtApex.validNel, txtNormal.validNel, txtDotted.validNel),
ExistingZones(Set(apexZone, baseZone)))
ExistingZones(Set(apexZone, baseZone))
)
discovered.getValid shouldBe expected
}
@ -1266,29 +1321,35 @@ class BatchChangeServiceSpec
"handle mapping a combination of zone discovery successes and failures" in {
val result = underTest.zoneDiscovery(
List(apexAddA.validNel, onlyApexAddA.validNel, onlyBaseAddAAAA.validNel, cnameAdd.validNel),
ExistingZones(Set(apexZone, baseZone, onlyBaseZone)))
ExistingZones(Set(apexZone, baseZone, onlyBaseZone))
)
result.head should beValid[ChangeForValidation](apexAddForVal)
result(1) should haveInvalid[DomainValidationError](ZoneDiscoveryError("only.apex.exists."))
result(2) should beValid[ChangeForValidation](
AddChangeForValidation(onlyBaseZone, "have", onlyBaseAddAAAA))
AddChangeForValidation(onlyBaseZone, "have", onlyBaseAddAAAA)
)
result(3) should beValid[ChangeForValidation](
AddChangeForValidation(baseZone, "cname", cnameAdd))
AddChangeForValidation(baseZone, "cname", cnameAdd)
)
}
"map the batch change input to the delegated PTR zone for PTR records (ipv4)" in {
val result = underTest.zoneDiscovery(
List(ptrAdd.validNel),
ExistingZones(Set(delegatedPTRZone, ptrZone)))
ExistingZones(Set(delegatedPTRZone, ptrZone))
)
result should containChangeForValidation(
AddChangeForValidation(delegatedPTRZone, "11", ptrAdd))
AddChangeForValidation(delegatedPTRZone, "11", ptrAdd)
)
}
"map the batch change input to the non delegated PTR zone for PTR records (ipv4)" in {
val result = underTest.zoneDiscovery(
List(ptrAdd2.validNel),
ExistingZones(Set(delegatedPTRZone, ptrZone)))
ExistingZones(Set(delegatedPTRZone, ptrZone))
)
result should containChangeForValidation(AddChangeForValidation(ptrZone, "255", ptrAdd2))
}
@ -1302,7 +1363,8 @@ class BatchChangeServiceSpec
"return an error for PTR if there are zone matches for the IP but no match on the record name" in {
val result = underTest.zoneDiscovery(
List(ptrAdd.validNel),
ExistingZones(Set(delegatedPTRZone.copy(name = "192/30.55.144.10.in-addr.arpa."))))
ExistingZones(Set(delegatedPTRZone.copy(name = "192/30.55.144.10.in-addr.arpa.")))
)
result.head should haveInvalid[DomainValidationError](ZoneDiscoveryError(ptrAdd.inputName))
}
@ -1318,41 +1380,52 @@ class BatchChangeServiceSpec
"2001:0db8:0111:0000:0000:ff00:0042:8329",
RecordType.PTR,
ttl,
PTRData("ptr"))
PTRData("ptr")
)
val bigZoneAdd = AddChangeInput(
"2001:0000:0000:0000:0000:ff00:0042:8329",
RecordType.PTR,
ttl,
PTRData("ptr"))
PTRData("ptr")
)
val notFoundZoneAdd = AddChangeInput("::1", RecordType.PTR, ttl, PTRData("ptr"))
val ptripv6Adds = List(
smallZoneAdd.validNel,
medZoneAdd.validNel,
bigZoneAdd.validNel,
notFoundZoneAdd.validNel)
notFoundZoneAdd.validNel
)
val result = underTest.zoneDiscovery(
ptripv6Adds,
ExistingZones(Set(ptrv6ZoneSmall, ptrv6ZoneMed, ptrv6ZoneBig)))
ExistingZones(Set(ptrv6ZoneSmall, ptrv6ZoneMed, ptrv6ZoneBig))
)
result should containChangeForValidation(
AddChangeForValidation(
ptrv6ZoneSmall,
"9.2.3.8.2.4.0.0.0.0.f.f.0.0.0.0.0.0.0.0",
smallZoneAdd))
smallZoneAdd
)
)
result should containChangeForValidation(
AddChangeForValidation(
ptrv6ZoneMed,
"9.2.3.8.2.4.0.0.0.0.f.f.0.0.0.0.0.0.0.0.1.1.1",
medZoneAdd))
medZoneAdd
)
)
result should containChangeForValidation(
AddChangeForValidation(
ptrv6ZoneBig,
"9.2.3.8.2.4.0.0.0.0.f.f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",
bigZoneAdd))
bigZoneAdd
)
)
result(3) should haveInvalid[DomainValidationError](
ZoneDiscoveryError(notFoundZoneAdd.inputName))
ZoneDiscoveryError(notFoundZoneAdd.inputName)
)
}
}
@ -1490,7 +1563,8 @@ class BatchChangeServiceSpec
None,
List(apexAddA),
Some("owner-group-id"),
scheduledTime = Some(DateTime.now.plusMinutes(1))),
scheduledTime = Some(DateTime.now.plusMinutes(1))
),
List(
AddChangeForValidation(apexZone, "apex.test.com.", apexAddA).validNel
),
@ -1526,7 +1600,8 @@ class BatchChangeServiceSpec
None,
List(apexAddA),
None,
scheduledTime = Some(DateTime.now.plusMinutes(1))),
scheduledTime = Some(DateTime.now.plusMinutes(1))
),
List(
AddChangeForValidation(apexZone, "apex.test.com.", apexAddA).validNel
),
@ -1625,7 +1700,8 @@ class BatchChangeServiceSpec
None,
List(apexAddA),
Some("owner-group-id"),
Some(DateTime.now.plusMinutes(1))),
Some(DateTime.now.plusMinutes(1))
),
List(
AddChangeForValidation(apexZone, "apex.test.com.", apexAddA).validNel,
nonFatalError.invalidNel,
@ -1662,7 +1738,8 @@ class BatchChangeServiceSpec
List(
ZoneDiscoveryError("no.zone.match.").invalidNel,
AddChangeForValidation(baseZone, "non-apex", nonApexAddA).validNel,
nonFatalError.invalidNel),
nonFatalError.invalidNel
),
okAuth,
true
)
@ -1672,9 +1749,11 @@ class BatchChangeServiceSpec
result shouldBe an[InvalidBatchChangeResponses]
val ibcr = result.asInstanceOf[InvalidBatchChangeResponses]
ibcr.changeRequestResponses.head should haveInvalid[DomainValidationError](
ZoneDiscoveryError("no.zone.match."))
ZoneDiscoveryError("no.zone.match.")
)
ibcr.changeRequestResponses(1) shouldBe Valid(
AddChangeForValidation(baseZone, "non-apex", nonApexAddA))
AddChangeForValidation(baseZone, "non-apex", nonApexAddA)
)
ibcr.changeRequestResponses(2) should haveInvalid[DomainValidationError](nonFatalError)
}
@ -1706,7 +1785,8 @@ class BatchChangeServiceSpec
None,
List(apexAddA, onlyBaseAddAAAA, delete),
None,
Some(DateTime.now.plusMinutes(1))),
Some(DateTime.now.plusMinutes(1))
),
List(
AddChangeForValidation(apexZone, "apex.test.com.", apexAddA).validNel,
nonFatalError.invalidNel,
@ -1728,7 +1808,8 @@ class BatchChangeServiceSpec
None,
List(noZoneAddA, nonApexAddA),
None,
Some(DateTime.now.plusMinutes(1))),
Some(DateTime.now.plusMinutes(1))
),
List(
ZoneDiscoveryError("no.zone.match.", fatal = true).invalidNel,
AddChangeForValidation(baseZone, "non-apex", nonApexAddA).validNel
@ -1770,7 +1851,8 @@ class BatchChangeServiceSpec
None,
List(apexAddA),
Some("owner-group-id"),
Some(DateTime.now.plusMinutes(1))),
Some(DateTime.now.plusMinutes(1))
),
List(
AddChangeForValidation(apexZone, "apex.test.com.", apexAddA).validNel,
nonFatalError.invalidNel,
@ -1825,7 +1907,8 @@ class BatchChangeServiceSpec
AddChangeForValidation(
baseZone,
singleChangeGood.inputName.split('.').head,
asAdds.head).validNel,
asAdds.head
).validNel,
AddChangeForValidation(baseZone, singleChangeNR.inputName.split('.').head, asAdds(1)).validNel
),
reviewInfo
@ -1844,7 +1927,8 @@ class BatchChangeServiceSpec
AddChangeForValidation(
baseZone,
singleChangeGood.inputName.split('.').head,
asAdds.head).validNel,
asAdds.head
).validNel,
fatalError.invalidNel
),
reviewInfo
@ -1894,7 +1978,8 @@ class BatchChangeServiceSpec
None,
DateTime.now,
List(),
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChangeOne)
val batchChangeTwo = BatchChange(
@ -1903,7 +1988,8 @@ class BatchChangeServiceSpec
None,
new DateTime(DateTime.now.getMillis + 1000),
List(),
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChangeTwo)
val result = rightResultOf(underTest.listBatchChangeSummaries(auth, maxItems = 100).value)
@ -1926,7 +2012,8 @@ class BatchChangeServiceSpec
None,
DateTime.now,
List(),
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChangeOne)
val batchChangeTwo = BatchChange(
@ -1935,7 +2022,8 @@ class BatchChangeServiceSpec
None,
new DateTime(DateTime.now.getMillis + 1000),
List(),
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChangeTwo)
val result = rightResultOf(underTest.listBatchChangeSummaries(auth, maxItems = 1).value)
@ -1957,7 +2045,8 @@ class BatchChangeServiceSpec
None,
DateTime.now,
List(),
approvalStatus = BatchChangeApprovalStatus.PendingReview)
approvalStatus = BatchChangeApprovalStatus.PendingReview
)
batchChangeRepo.save(batchChangeOne)
val batchChangeTwo = BatchChange(
@ -1966,15 +2055,18 @@ class BatchChangeServiceSpec
None,
new DateTime(DateTime.now.getMillis + 1000),
List(),
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChangeTwo)
val result = rightResultOf(
underTest
.listBatchChangeSummaries(
auth,
approvalStatus = Some(BatchChangeApprovalStatus.PendingReview))
.value)
approvalStatus = Some(BatchChangeApprovalStatus.PendingReview)
)
.value
)
result.maxItems shouldBe 100
result.nextId shouldBe None
@ -1994,7 +2086,8 @@ class BatchChangeServiceSpec
None,
DateTime.now,
List(),
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChangeOne)
val batchChangeTwo = BatchChange(
@ -2003,7 +2096,8 @@ class BatchChangeServiceSpec
None,
new DateTime(DateTime.now.getMillis + 1000),
List(),
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChangeTwo)
val result =
@ -2026,7 +2120,8 @@ class BatchChangeServiceSpec
None,
DateTime.now,
List(),
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChangeUserOne)
val batchChangeUserTwo = BatchChange(
@ -2035,7 +2130,8 @@ class BatchChangeServiceSpec
None,
new DateTime(DateTime.now.getMillis + 1000),
List(),
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChangeUserTwo)
val result =
@ -2053,7 +2149,8 @@ class BatchChangeServiceSpec
None,
DateTime.now,
List(),
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChangeUserOne)
val batchChangeUserTwo = BatchChange(
@ -2062,7 +2159,8 @@ class BatchChangeServiceSpec
None,
new DateTime(DateTime.now.getMillis + 1000),
List(),
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChangeUserTwo)
val result =
@ -2080,7 +2178,8 @@ class BatchChangeServiceSpec
None,
DateTime.now,
List(),
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChangeUserOne)
val batchChangeUserTwo = BatchChange(
@ -2089,7 +2188,8 @@ class BatchChangeServiceSpec
None,
new DateTime(DateTime.now.getMillis + 1000),
List(),
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChangeUserTwo)
val result =
@ -2121,7 +2221,8 @@ class BatchChangeServiceSpec
DateTime.now,
List(),
ownerGroupId = Some(okGroup.id),
BatchChangeApprovalStatus.AutoApproved)
BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChange)
val result = rightResultOf(underTest.listBatchChangeSummaries(auth, maxItems = 100).value)
@ -2145,7 +2246,8 @@ class BatchChangeServiceSpec
DateTime.now,
List(),
ownerGroupId = Some("no-existo"),
BatchChangeApprovalStatus.AutoApproved)
BatchChangeApprovalStatus.AutoApproved
)
batchChangeRepo.save(batchChange)
val result = rightResultOf(underTest.listBatchChangeSummaries(auth, maxItems = 100).value)
@ -2198,7 +2300,8 @@ class BatchChangeServiceSpec
Some("checkConverter"),
DateTime.now,
List(),
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
val result = rightResultOf(
underTestManualEnabled
@ -2206,8 +2309,10 @@ class BatchChangeServiceSpec
batchChange,
ExistingZones(Set()),
ChangeForValidationMap(List(), ExistingRecordSets(List())),
None)
.value)
None
)
.value
)
result.reviewComment shouldBe Some("batchSentToConverter")
}
"not send to the converter, save the change if PendingReview and MA enabled" in {
@ -2218,7 +2323,8 @@ class BatchChangeServiceSpec
Some("checkConverter"),
DateTime.now,
List(),
approvalStatus = BatchChangeApprovalStatus.PendingReview)
approvalStatus = BatchChangeApprovalStatus.PendingReview
)
val result = rightResultOf(
underTestManualEnabled
@ -2226,8 +2332,10 @@ class BatchChangeServiceSpec
batchChange,
ExistingZones(Set()),
ChangeForValidationMap(List(), ExistingRecordSets(List())),
None)
.value)
None
)
.value
)
// not sent to converter
result.reviewComment shouldBe None
@ -2242,7 +2350,8 @@ class BatchChangeServiceSpec
Some("checkConverter"),
DateTime.now,
List(),
approvalStatus = BatchChangeApprovalStatus.PendingReview)
approvalStatus = BatchChangeApprovalStatus.PendingReview
)
val result = leftResultOf(
underTest
@ -2250,8 +2359,10 @@ class BatchChangeServiceSpec
batchChange,
ExistingZones(Set()),
ChangeForValidationMap(List(), ExistingRecordSets(List())),
None)
.value)
None
)
.value
)
result shouldBe an[UnknownConversionError]
}
@ -2263,7 +2374,8 @@ class BatchChangeServiceSpec
Some("checkConverter"),
DateTime.now,
List(),
approvalStatus = BatchChangeApprovalStatus.ManuallyApproved)
approvalStatus = BatchChangeApprovalStatus.ManuallyApproved
)
val result = leftResultOf(
underTest
@ -2271,8 +2383,10 @@ class BatchChangeServiceSpec
batchChange,
ExistingZones(Set()),
ChangeForValidationMap(List(), ExistingRecordSets(List())),
None)
.value)
None
)
.value
)
result shouldBe an[UnknownConversionError]
}
}
@ -2357,10 +2471,13 @@ class BatchChangeServiceSpec
okGroup.id,
OwnerType.Zone,
Some(okGroup.email),
Some(okGroup.name)))
Some(okGroup.name)
)
)
result(1) should beValid[ChangeForValidation](
AddChangeForValidation(apexZone, "apex.test.com.", apexAddA))
AddChangeForValidation(apexZone, "apex.test.com.", apexAddA)
)
}
}
}

View File

@ -49,13 +49,16 @@ class BatchTransformationsSpec extends WordSpec with Matchers {
ipv6nonMatch1,
ipv6nonMatch2,
ipv6nonMatch3,
forwardMatch1))
forwardMatch1
)
)
"getipv4PTRMatches" should {
"return all possible matches including proper delegations" in {
existingZones.getipv4PTRMatches("3.2.1.2") should contain theSameElementsAs List(
ip4base1,
ip4del1)
ip4del1
)
}
"return all possible matches excluding similar delegations" in {
existingZones.getipv4PTRMatches("3.2.1.55") should contain theSameElementsAs List(ip4base1)
@ -75,7 +78,8 @@ class BatchTransformationsSpec extends WordSpec with Matchers {
// only matches 1st option
existingZones.getipv6PTRMatches("2001:0db0:0000:0000:0000:0000:0000:0000") shouldBe List(
ipv6match1)
ipv6match1
)
existingZones.getipv6PTRMatches("2001:db0::ff00:42:8329") shouldBe List(ipv6match1)
}
"return empty if there are no matches" in {

View File

@ -55,7 +55,8 @@ class DnsConnectionSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(AData("10.1.1.1")))
List(AData("10.1.1.1"))
)
private val testAMultiple = RecordSet(
testZone.id,
@ -65,13 +66,15 @@ class DnsConnectionSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(AData("1.1.1.1"), AData("2.2.2.2")))
List(AData("1.1.1.1"), AData("2.2.2.2"))
)
private val testDnsA = new DNS.ARecord(
DNS.Name.fromString("a-record."),
DNS.DClass.IN,
200L,
InetAddress.getByName("10.1.1.1"))
InetAddress.getByName("10.1.1.1")
)
private val mockResolver = mock[DNS.SimpleResolver]
private val mockMessage = mock[DNS.Message]
@ -81,7 +84,8 @@ class DnsConnectionSpec
override def toQuery(
name: String,
zoneName: String,
typ: RecordType): Either[Throwable, DnsQuery] =
typ: RecordType
): Either[Throwable, DnsQuery] =
name match {
case "try-again" =>
Right(new DnsQuery(new Lookup("try-again.vinyldns.", 0, 0), new Name(testZone.name)))
@ -165,12 +169,14 @@ class DnsConnectionSpec
DNS.Name.fromString("a-record."),
DNS.DClass.IN,
200L,
InetAddress.getByName("1.1.1.1"))
InetAddress.getByName("1.1.1.1")
)
val a2 = new DNS.ARecord(
DNS.Name.fromString("a-record."),
DNS.DClass.IN,
200L,
InetAddress.getByName("2.2.2.2"))
InetAddress.getByName("2.2.2.2")
)
doReturn(List(a1, a2)).when(mockDnsQuery).run()
val records: List[RecordSet] =
@ -342,7 +348,8 @@ class DnsConnectionSpec
}
"send an appropriate replace message to the resolver for multiple records" in {
val change = updateRsChange(testZone, testAMultiple).copy(
updates = Some(testAMultiple.copy(name = "updated-a-record")))
updates = Some(testAMultiple.copy(name = "updated-a-record"))
)
val result: DnsResponse = rightResultOf(underTest.updateRecord(change).value)
@ -439,7 +446,8 @@ class DnsConnectionSpec
}
"send an appropriate replace message to the resolver for multiple records" in {
val change = updateRsChange(testZone, testAMultiple).copy(
updates = Some(testAMultiple.copy(records = List(AData("4.4.4.4"), AData("3.3.3.3")))))
updates = Some(testAMultiple.copy(records = List(AData("4.4.4.4"), AData("3.3.3.3"))))
)
val result: DnsResponse = rightResultOf(underTest.updateRecord(change).value)
@ -535,17 +543,20 @@ class DnsConnectionSpec
"applyChange" should {
"yield a successful DNS response for a create if there are no errors" in {
underTest.applyChange(addRsChange()).value.unsafeRunSync() shouldBe Right(
NoError(mockMessage))
NoError(mockMessage)
)
}
"yield a successful DNS response for an update if there are no errors" in {
underTest.applyChange(updateRsChange()).value.unsafeRunSync() shouldBe Right(
NoError(mockMessage))
NoError(mockMessage)
)
}
"yield a successful DNS response for a delete if there are no errors" in {
underTest.applyChange(deleteRsChange()).value.unsafeRunSync() shouldBe Right(
NoError(mockMessage))
NoError(mockMessage)
)
}
}

View File

@ -50,7 +50,8 @@ class DnsConversionsSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(AData("10.1.1.1")))
List(AData("10.1.1.1"))
)
private val testAMultiple = RecordSet(
testZone.id,
"a-record",
@ -59,7 +60,8 @@ class DnsConversionsSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(AData("1.1.1.1"), AData("2.2.2.2")))
List(AData("1.1.1.1"), AData("2.2.2.2"))
)
private val testAAAA = RecordSet(
testZone.id,
"aaaa-record",
@ -68,7 +70,8 @@ class DnsConversionsSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(AAAAData("2001:db8:0:0:0:0:0:3")))
List(AAAAData("2001:db8:0:0:0:0:0:3"))
)
private val testCNAME = RecordSet(
testZone.id,
"cname-record",
@ -77,7 +80,8 @@ class DnsConversionsSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(CNAMEData("cname.foo.vinyldns.")))
List(CNAMEData("cname.foo.vinyldns."))
)
private val testMX = RecordSet(
testZone.id,
"mx-record",
@ -86,7 +90,8 @@ class DnsConversionsSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(MXData(100, "exchange.vinyldns.")))
List(MXData(100, "exchange.vinyldns."))
)
private val testNS = RecordSet(
testZone.id,
"ns-record",
@ -95,7 +100,8 @@ class DnsConversionsSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(NSData("nsdname.vinyldns.")))
List(NSData("nsdname.vinyldns."))
)
private val testPTR = RecordSet(
testZone.id,
"ptr-record",
@ -104,7 +110,8 @@ class DnsConversionsSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(PTRData("ptr.vinyldns.")))
List(PTRData("ptr.vinyldns."))
)
private val testSOA = RecordSet(
testZone.id,
"soa-record",
@ -123,7 +130,8 @@ class DnsConversionsSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(SPFData("spf")))
List(SPFData("spf"))
)
private val testLongSPF = RecordSet(
testZone.id,
"long-spf-record",
@ -132,7 +140,8 @@ class DnsConversionsSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(SPFData("s" * 256)))
List(SPFData("s" * 256))
)
private val testSRV = RecordSet(
testZone.id,
"srv-record",
@ -141,7 +150,8 @@ class DnsConversionsSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(SRVData(1, 2, 3, "target.vinyldns.")))
List(SRVData(1, 2, 3, "target.vinyldns."))
)
private val testNAPTR = RecordSet(
testZone.id,
"naptr-record",
@ -160,7 +170,8 @@ class DnsConversionsSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(SSHFPData(1, 2, "fingerprint")))
List(SSHFPData(1, 2, "fingerprint"))
)
private val testTXT = RecordSet(
testZone.id,
"txt-record",
@ -169,7 +180,8 @@ class DnsConversionsSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(TXTData("text")))
List(TXTData("text"))
)
private val testLongTXT = RecordSet(
testZone.id,
"long-txt-record",
@ -178,7 +190,8 @@ class DnsConversionsSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(TXTData("a" * 64763)))
List(TXTData("a" * 64763))
)
private val testAt = RecordSet(
testZone.id,
"vinyldns.",
@ -187,45 +200,53 @@ class DnsConversionsSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(AData("10.1.1.1")))
List(AData("10.1.1.1"))
)
private val testDS = ds.copy(zoneId = testZone.id)
private val testDnsUnknown = DNS.Record.newRecord(
DNS.Name.fromString("unknown-record."),
DNS.Type.AFSDB,
DNS.DClass.IN,
200L)
200L
)
private val testDnsA = new DNS.ARecord(
DNS.Name.fromString("a-record."),
DNS.DClass.IN,
200L,
InetAddress.getByName("10.1.1.1"))
InetAddress.getByName("10.1.1.1")
)
private val testDnsA1 = new DNS.ARecord(
DNS.Name.fromString("a-record."),
DNS.DClass.IN,
200L,
InetAddress.getByName("1.1.1.1"))
InetAddress.getByName("1.1.1.1")
)
private val testDnsA2 = new DNS.ARecord(
DNS.Name.fromString("a-record."),
DNS.DClass.IN,
200L,
InetAddress.getByName("2.2.2.2"))
InetAddress.getByName("2.2.2.2")
)
private val testDnsAReplace = new DNS.ARecord(
DNS.Name.fromString("a-record-2."),
DNS.DClass.IN,
200L,
InetAddress.getByName("2.2.2.2"))
InetAddress.getByName("2.2.2.2")
)
private val testDnsAAAA = new DNS.AAAARecord(
DNS.Name.fromString("aaaa-record."),
DNS.DClass.IN,
200L,
InetAddress.getByName("2001:db8::3"))
InetAddress.getByName("2001:db8::3")
)
private val testDnsAt = new DNS.ARecord(
DNS.Name.fromString("@", DNS.Name.fromString("vinyldns.")),
DNS.DClass.IN,
200L,
InetAddress.getByName("10.1.1.1"))
InetAddress.getByName("10.1.1.1")
)
private val testDnsARRset = new DNS.RRset()
testDnsARRset.addRR(testDnsA1)

View File

@ -54,7 +54,8 @@ class MembershipServiceSpec
mockMembershipRepo,
mockZoneRepo,
mockGroupChangeRepo,
mockRecordSetRepo)
mockRecordSetRepo
)
private val underTest = spy(backingService)
private val okUserInfo: UserInfo = UserInfo(okUser)
@ -74,7 +75,8 @@ class MembershipServiceSpec
private val existingGroup = okGroup.copy(
id = "id",
memberIds = Set("user1", "user2", "user3", "user4"),
adminUserIds = Set("user1", "user2", "ok"))
adminUserIds = Set("user1", "user2", "ok")
)
// the update will remove users 3 and 4, add users 5 and 6, as well as a new admin user 7 and remove user2 as admin
private val updatedInfo = Group(
@ -101,7 +103,8 @@ class MembershipServiceSpec
mockMembershipRepo,
mockGroupChangeRepo,
mockRecordSetRepo,
underTest)
underTest
)
"MembershipService" should {
"create a new group" should {
@ -157,7 +160,8 @@ class MembershipServiceSpec
doReturn(IO.pure(Some(okUser))).when(mockUserRepo).getUser("ok")
val info = groupInfo.copy(
memberIds = Set(okUserInfo.id, dummyUserInfo.id),
adminUserIds = Set(okUserInfo.id, dummyUserInfo.id))
adminUserIds = Set(okUserInfo.id, dummyUserInfo.id)
)
val expectedMembersAdded = Set(okUserInfo.id, dummyUserInfo.id)
doReturn(().toResult).when(underTest).groupWithSameNameDoesNotExist(info.name)
@ -275,8 +279,10 @@ class MembershipServiceSpec
updatedInfo.description,
updatedInfo.memberIds,
updatedInfo.adminUserIds,
okAuth)
.value)
okAuth
)
.value
)
val groupCaptor = ArgumentCaptor.forClass(classOf[Group])
val addedMemberCaptor = ArgumentCaptor.forClass(classOf[Set[String]])
@ -326,8 +332,10 @@ class MembershipServiceSpec
updatedInfo.description,
updatedInfo.memberIds,
updatedInfo.adminUserIds,
dummyAuth)
.value)
dummyAuth
)
.value
)
error shouldBe a[NotAuthorizedError]
}
@ -350,8 +358,10 @@ class MembershipServiceSpec
updatedInfo.description,
updatedInfo.memberIds,
updatedInfo.adminUserIds,
okAuth)
.value)
okAuth
)
.value
)
error shouldBe a[GroupAlreadyExistsError]
}
@ -367,8 +377,10 @@ class MembershipServiceSpec
updatedInfo.description,
updatedInfo.memberIds,
updatedInfo.adminUserIds,
okAuth)
.value)
okAuth
)
.value
)
error shouldBe a[GroupNotFoundError]
}
@ -392,8 +404,10 @@ class MembershipServiceSpec
updatedInfo.description,
updatedInfo.memberIds,
updatedInfo.adminUserIds,
okAuth)
.value)
okAuth
)
.value
)
error shouldBe a[UserNotFoundError]
}
@ -411,8 +425,10 @@ class MembershipServiceSpec
updatedInfo.description,
Set(),
Set(),
okAuth)
.value)
okAuth
)
.value
)
error shouldBe an[InvalidGroupError]
}
}
@ -537,7 +553,8 @@ class MembershipServiceSpec
None,
nextId = Some(listOfDummyGroups(99).id),
maxItems = 100,
ignoreAccess = false)
ignoreAccess = false
)
}
"return only return groups whose name matches the filter" in {
doReturn(IO.pure(listOfDummyGroups.toSet))
@ -550,15 +567,18 @@ class MembershipServiceSpec
startFrom = None,
maxItems = 100,
listOfDummyGroupsAuth,
false)
.value)
false
)
.value
)
result shouldBe ListMyGroupsResponse(
groups = listOfDummyGroupInfo.slice(10, 20),
groupNameFilter = Some("name-dummy01"),
startFrom = None,
nextId = None,
maxItems = 100,
ignoreAccess = false)
ignoreAccess = false
)
}
"return only return groups after startFrom" in {
doReturn(IO.pure(listOfDummyGroups.toSet))
@ -571,15 +591,18 @@ class MembershipServiceSpec
startFrom = Some(listOfDummyGroups(99).id),
maxItems = 100,
listOfDummyGroupsAuth,
ignoreAccess = false)
.value)
ignoreAccess = false
)
.value
)
result shouldBe ListMyGroupsResponse(
groups = listOfDummyGroupInfo.slice(100, 200),
groupNameFilter = None,
startFrom = Some(listOfDummyGroups(99).id),
nextId = None,
maxItems = 100,
ignoreAccess = false)
ignoreAccess = false
)
}
"return only return maxItems groups" in {
doReturn(IO.pure(listOfDummyGroups.toSet))
@ -592,15 +615,18 @@ class MembershipServiceSpec
startFrom = None,
maxItems = 10,
listOfDummyGroupsAuth,
ignoreAccess = false)
.value)
ignoreAccess = false
)
.value
)
result shouldBe ListMyGroupsResponse(
groups = listOfDummyGroupInfo.slice(0, 10),
groupNameFilter = None,
startFrom = None,
nextId = Some(listOfDummyGroups(9).id),
maxItems = 10,
ignoreAccess = false)
ignoreAccess = false
)
}
"return an empty set if the user is not a member of any groups" in {
doReturn(IO.pure(Set())).when(mockGroupRepo).getGroups(any[Set[String]])
@ -615,7 +641,8 @@ class MembershipServiceSpec
verify(mockGroupRepo).getAllGroups()
result.groups should contain theSameElementsAs Seq(
GroupInfo(dummyGroup),
GroupInfo(okGroup))
GroupInfo(okGroup)
)
}
"return all groups from the database for super users even if ignoreAccess is false" in {
doReturn(IO.pure(Set(okGroup, dummyGroup))).when(mockGroupRepo).getAllGroups()
@ -624,7 +651,8 @@ class MembershipServiceSpec
verify(mockGroupRepo).getAllGroups()
result.groups should contain theSameElementsAs Seq(
GroupInfo(dummyGroup),
GroupInfo(okGroup))
GroupInfo(okGroup)
)
}
"return all groups from the database for super users if ignoreAccess is true" in {
doReturn(IO.pure(Set(okGroup, dummyGroup))).when(mockGroupRepo).getAllGroups()
@ -633,7 +661,8 @@ class MembershipServiceSpec
verify(mockGroupRepo).getAllGroups()
result.groups should contain theSameElementsAs Seq(
GroupInfo(dummyGroup),
GroupInfo(okGroup))
GroupInfo(okGroup)
)
}
"return all groups from the database for support users even if ignoreAccess is false" in {
val supportAuth = AuthPrincipal(okUser.copy(isSupport = true), Seq())
@ -643,7 +672,8 @@ class MembershipServiceSpec
verify(mockGroupRepo).getAllGroups()
result.groups should contain theSameElementsAs Seq(
GroupInfo(dummyGroup),
GroupInfo(okGroup))
GroupInfo(okGroup)
)
}
"return all groups from the database for support users if ignoreAccess is true" in {
val supportAuth = AuthPrincipal(okUser.copy(isSupport = true), Seq())
@ -653,7 +683,8 @@ class MembershipServiceSpec
verify(mockGroupRepo).getAllGroups()
result.groups should contain theSameElementsAs Seq(
GroupInfo(dummyGroup),
GroupInfo(okGroup))
GroupInfo(okGroup)
)
}
"do not return deleted groups" in {
val deletedGroupAuth: AuthPrincipal = AuthPrincipal(okUser, Seq(deletedGroup.id))
@ -670,7 +701,8 @@ class MembershipServiceSpec
"return the group activity" in {
val groupChangeRepoResponse = ListGroupChangesResults(
listOfDummyGroupChanges.take(100),
Some(listOfDummyGroupChanges(100).id))
Some(listOfDummyGroupChanges(100).id)
)
doReturn(IO.pure(groupChangeRepoResponse))
.when(mockGroupChangeRepo)
.getGroupChanges(anyString, any[Option[String]], anyInt)
@ -748,7 +780,8 @@ class MembershipServiceSpec
val expectedMembers = List(MemberInfo(okUser, okGroup), MemberInfo(dummyUser, dummyGroup))
val supportAuth = okAuth.copy(
signedInUser = dummyAuth.signedInUser.copy(isSupport = true),
memberGroupIds = Seq.empty)
memberGroupIds = Seq.empty
)
doReturn(IO.pure(Some(testGroup))).when(mockGroupRepo).getGroup(testGroup.id)
doReturn(IO.pure(testListUsersResult))
@ -830,7 +863,8 @@ class MembershipServiceSpec
doReturn(IO.pure(Some(okGroup))).when(mockGroupRepo).getGroupByName(okGroup.name)
val result = awaitResultOf(
underTest.differentGroupWithSameNameDoesNotExist(okGroup.name, okGroup.id).value)
underTest.differentGroupWithSameNameDoesNotExist(okGroup.name, okGroup.id).value
)
result should be(right)
}
@ -842,7 +876,8 @@ class MembershipServiceSpec
.getGroupByName(okGroup.name)
val result = awaitResultOf(
underTest.differentGroupWithSameNameDoesNotExist(okGroup.name, okGroup.id).value)
underTest.differentGroupWithSameNameDoesNotExist(okGroup.name, okGroup.id).value
)
result should be(right)
}
}
@ -942,7 +977,8 @@ class MembershipServiceSpec
val error = leftResultOf(
underTest
.updateUserLockStatus(okUser.id, LockStatus.Locked, dummyAuth)
.value)
.value
)
error shouldBe a[NotAuthorizedError]
}
@ -950,11 +986,13 @@ class MembershipServiceSpec
"return an error if the signed in user is only a support admin" in {
val supportAuth = okAuth.copy(
signedInUser = dummyAuth.signedInUser.copy(isSupport = true),
memberGroupIds = Seq.empty)
memberGroupIds = Seq.empty
)
val error = leftResultOf(
underTest
.updateUserLockStatus(okUser.id, LockStatus.Locked, supportAuth)
.value)
.value
)
error shouldBe a[NotAuthorizedError]
}
@ -965,7 +1003,8 @@ class MembershipServiceSpec
val error = leftResultOf(
underTest
.updateUserLockStatus(okUser.id, LockStatus.Locked, superUserAuth)
.value)
.value
)
error shouldBe a[UserNotFoundError]
}

View File

@ -68,7 +68,8 @@ class RecordSetServiceSpec
mockRecordChangeRepo,
mockUserRepo,
mockMessageQueue,
new AccessValidations())
new AccessValidations()
)
"addRecordSet" should {
"return the recordSet change as the result" in {
@ -83,7 +84,8 @@ class RecordSetServiceSpec
val result: RecordSetChange =
rightResultOf(
underTest.addRecordSet(record, okAuth).map(_.asInstanceOf[RecordSetChange]).value)
underTest.addRecordSet(record, okAuth).map(_.asInstanceOf[RecordSetChange]).value
)
matches(result.recordSet, record, okZone.name) shouldBe true
result.changeType shouldBe RecordSetChangeType.Create
@ -148,7 +150,8 @@ class RecordSetServiceSpec
val result = leftResultOf(underTest.addRecordSet(record, okAuth).value)
result shouldBe InvalidRequest(
HighValueDomainError(s"high-value-domain.${okZone.name}").message)
HighValueDomainError(s"high-value-domain.${okZone.name}").message
)
}
"succeed if record is apex with dot" in {
val name = okZone.name
@ -163,7 +166,8 @@ class RecordSetServiceSpec
.getRecordSetsByName(okZone.id, record.name)
val result: RecordSetChange = rightResultOf(
underTest.addRecordSet(record, okAuth).map(_.asInstanceOf[RecordSetChange]).value)
underTest.addRecordSet(record, okAuth).map(_.asInstanceOf[RecordSetChange]).value
)
result.recordSet.name shouldBe okZone.name
}
@ -180,7 +184,8 @@ class RecordSetServiceSpec
.getRecordSetsByName(okZone.id, record.name)
val result: RecordSetChange = rightResultOf(
underTest.addRecordSet(record, okAuth).map(_.asInstanceOf[RecordSetChange]).value)
underTest.addRecordSet(record, okAuth).map(_.asInstanceOf[RecordSetChange]).value
)
result.recordSet.name shouldBe okZone.name
}
@ -197,7 +202,8 @@ class RecordSetServiceSpec
.getRecordSetsByName(okZone.id, record.name)
val result: RecordSetChange = rightResultOf(
underTest.addRecordSet(record, okAuth).map(_.asInstanceOf[RecordSetChange]).value)
underTest.addRecordSet(record, okAuth).map(_.asInstanceOf[RecordSetChange]).value
)
result.recordSet.name shouldBe okZone.name
}
@ -215,7 +221,8 @@ class RecordSetServiceSpec
.getGroup(okGroup.id)
val result: RecordSetChange = rightResultOf(
underTest.addRecordSet(record, okAuth).map(_.asInstanceOf[RecordSetChange]).value)
underTest.addRecordSet(record, okAuth).map(_.asInstanceOf[RecordSetChange]).value
)
result.recordSet.ownerGroupId shouldBe Some(okGroup.id)
}
@ -268,7 +275,8 @@ class RecordSetServiceSpec
.getRecordSetsByName(okZone.id, newRecord.name)
val result: RecordSetChange = rightResultOf(
underTest.updateRecordSet(newRecord, okAuth).map(_.asInstanceOf[RecordSetChange]).value)
underTest.updateRecordSet(newRecord, okAuth).map(_.asInstanceOf[RecordSetChange]).value
)
matches(result.recordSet, newRecord, okZone.name) shouldBe true
matches(result.updates.get, oldRecord, okZone.name) shouldBe true
@ -280,7 +288,8 @@ class RecordSetServiceSpec
.when(mockRecordRepo)
.getRecordSet(zoneNotAuthorized.id, aaaa.id)
val result = leftResultOf(
underTest.updateRecordSet(aaaa.copy(zoneId = zoneNotAuthorized.id), okAuth).value)
underTest.updateRecordSet(aaaa.copy(zoneId = zoneNotAuthorized.id), okAuth).value
)
result shouldBe a[NotAuthorizedError]
}
"fail if the new record name is dotted" in {
@ -324,7 +333,8 @@ class RecordSetServiceSpec
.getRecordSetsByName(okZone.id, newRecord.name)
val result: RecordSetChange = rightResultOf(
underTest.updateRecordSet(newRecord, okAuth).map(_.asInstanceOf[RecordSetChange]).value)
underTest.updateRecordSet(newRecord, okAuth).map(_.asInstanceOf[RecordSetChange]).value
)
result.recordSet.name shouldBe okZone.name
}
@ -341,7 +351,8 @@ class RecordSetServiceSpec
.getRecordSetsByName(okZone.id, newRecord.name)
val result: RecordSetChange = rightResultOf(
underTest.updateRecordSet(newRecord, okAuth).map(_.asInstanceOf[RecordSetChange]).value)
underTest.updateRecordSet(newRecord, okAuth).map(_.asInstanceOf[RecordSetChange]).value
)
result.recordSet.name shouldBe okZone.name
}
@ -358,7 +369,8 @@ class RecordSetServiceSpec
.getRecordSetsByName(okZone.id, newRecord.name)
val result: RecordSetChange = rightResultOf(
underTest.updateRecordSet(newRecord, okAuth).map(_.asInstanceOf[RecordSetChange]).value)
underTest.updateRecordSet(newRecord, okAuth).map(_.asInstanceOf[RecordSetChange]).value
)
result.recordSet.name shouldBe okZone.name
}
@ -370,7 +382,8 @@ class RecordSetServiceSpec
val result = leftResultOf(underTest.updateRecordSet(newRecord, okAuth).value)
result shouldBe InvalidRequest(
HighValueDomainError(s"high-value-domain.${okZone.name}").message)
HighValueDomainError(s"high-value-domain.${okZone.name}").message
)
}
"fail if user is in owner group but zone is not shared" in {
val auth = AuthPrincipal(listOfDummyUsers.head, Seq(oneUserDummyGroup.id))
@ -378,7 +391,8 @@ class RecordSetServiceSpec
name = "test-owner-group-failure",
zoneId = okZone.id,
status = RecordSetStatus.Active,
ownerGroupId = Some(oneUserDummyGroup.id))
ownerGroupId = Some(oneUserDummyGroup.id)
)
val newRecord = oldRecord.copy(ttl = oldRecord.ttl + 1000)
@ -393,7 +407,8 @@ class RecordSetServiceSpec
name = "test-owner-group-failure",
zoneId = zone.id,
status = RecordSetStatus.Active,
ownerGroupId = Some(oneUserDummyGroup.id))
ownerGroupId = Some(oneUserDummyGroup.id)
)
val newRecord = oldRecord.copy(ownerGroupId = Some("doesnt-exist"))
@ -421,7 +436,8 @@ class RecordSetServiceSpec
name = "test-owner-group-failure",
zoneId = zone.id,
status = RecordSetStatus.Active,
ownerGroupId = Some(oneUserDummyGroup.id))
ownerGroupId = Some(oneUserDummyGroup.id)
)
val newRecord = oldRecord.copy(ownerGroupId = Some(okGroup.id))
@ -448,7 +464,8 @@ class RecordSetServiceSpec
name = "test-owner-group-success",
zoneId = zone.id,
status = RecordSetStatus.Active,
ownerGroupId = Some(oneUserDummyGroup.id))
ownerGroupId = Some(oneUserDummyGroup.id)
)
val newRecord = oldRecord.copy(ttl = oldRecord.ttl + 1000)
@ -466,7 +483,8 @@ class RecordSetServiceSpec
.getGroup(oneUserDummyGroup.id)
val result = rightResultOf(
underTest.updateRecordSet(newRecord, auth).map(_.asInstanceOf[RecordSetChange]).value)
underTest.updateRecordSet(newRecord, auth).map(_.asInstanceOf[RecordSetChange]).value
)
result.recordSet.ttl shouldBe newRecord.ttl
result.recordSet.ownerGroupId shouldBe Some(oneUserDummyGroup.id)
@ -478,7 +496,8 @@ class RecordSetServiceSpec
name = "test-owner-group-success",
zoneId = zone.id,
status = RecordSetStatus.Active,
ownerGroupId = Some(oneUserDummyGroup.id))
ownerGroupId = Some(oneUserDummyGroup.id)
)
val newRecord = oldRecord.copy(ownerGroupId = None)
@ -493,7 +512,8 @@ class RecordSetServiceSpec
.getRecordSetsByName(zone.id, newRecord.name)
val result = rightResultOf(
underTest.updateRecordSet(newRecord, auth).map(_.asInstanceOf[RecordSetChange]).value)
underTest.updateRecordSet(newRecord, auth).map(_.asInstanceOf[RecordSetChange]).value
)
result.recordSet.ttl shouldBe newRecord.ttl
result.recordSet.ownerGroupId shouldBe None
@ -527,7 +547,8 @@ class RecordSetServiceSpec
underTest
.deleteRecordSet(record.id, okZone.id, okAuth)
.map(_.asInstanceOf[RecordSetChange])
.value)
.value
)
matches(result.recordSet, record, okZone.name) shouldBe true
result.changeType shouldBe RecordSetChangeType.Delete
@ -552,12 +573,14 @@ class RecordSetServiceSpec
val result =
leftResultOf(underTest.deleteRecordSet(record.id, okZone.id, okAuth).value)
result shouldBe InvalidRequest(
HighValueDomainError(s"high-value-domain.${okZone.name}").message)
HighValueDomainError(s"high-value-domain.${okZone.name}").message
)
}
"fail for user who is not in record owner group in shared zone" in {
val result =
leftResultOf(
underTest.deleteRecordSet(sharedZoneRecord.id, sharedZoneRecord.zoneId, dummyAuth).value)
underTest.deleteRecordSet(sharedZoneRecord.id, sharedZoneRecord.zoneId, dummyAuth).value
)
result shouldBe a[NotAuthorizedError]
}
@ -568,7 +591,8 @@ class RecordSetServiceSpec
val result =
leftResultOf(
underTest.deleteRecordSet(sharedZoneRecord.id, sharedZoneRecord.zoneId, okAuth).value)
underTest.deleteRecordSet(sharedZoneRecord.id, sharedZoneRecord.zoneId, okAuth).value
)
result shouldBe a[NotAuthorizedError]
}
@ -682,7 +706,8 @@ class RecordSetServiceSpec
val result: RecordSetInfo =
rightResultOf(
underTest.getRecordSet(sharedZoneRecordNoOwnerGroup.id, sharedZone.id, sharedAuth).value)
underTest.getRecordSet(sharedZoneRecordNoOwnerGroup.id, sharedZone.id, sharedAuth).value
)
result shouldBe expectedRecordSetInfo
}
@ -697,7 +722,8 @@ class RecordSetServiceSpec
leftResultOf(
underTest
.getRecordSet(sharedZoneRecordNotApprovedRecordType.id, sharedZone.id, okAuth)
.value)
.value
)
result shouldBe a[NotAuthorizedError]
}
@ -726,7 +752,8 @@ class RecordSetServiceSpec
val result = leftResultOf(
underTest
.getRecordSet(notSharedZoneRecordWithOwnerGroup.id, zoneNotAuthorized.id, okAuth)
.value)
.value
)
result shouldBe a[NotAuthorizedError]
}
}
@ -752,13 +779,14 @@ class RecordSetServiceSpec
.getGroups(Set(okGroup.id, "not-in-backend"))
doReturn(
IO.pure(ListRecordSetResults(List(sharedZoneRecord, sharedZoneRecordNotFoundOwnerGroup))))
.when(mockRecordRepo)
IO.pure(ListRecordSetResults(List(sharedZoneRecord, sharedZoneRecordNotFoundOwnerGroup)))
).when(mockRecordRepo)
.listRecordSets(
zoneId = sharedZone.id,
startFrom = None,
maxItems = None,
recordNameFilter = None)
recordNameFilter = None
)
val result: ListRecordSetsResponse = rightResultOf(
underTest
@ -767,16 +795,20 @@ class RecordSetServiceSpec
startFrom = None,
maxItems = None,
recordNameFilter = None,
authPrincipal = sharedAuth)
.value)
authPrincipal = sharedAuth
)
.value
)
result.recordSets shouldBe
List(
RecordSetListInfo(
RecordSetInfo(sharedZoneRecord, Some(okGroup.name)),
AccessLevel.Delete),
AccessLevel.Delete
),
RecordSetListInfo(
RecordSetInfo(sharedZoneRecordNotFoundOwnerGroup, None),
AccessLevel.Delete)
AccessLevel.Delete
)
)
}
"return the recordSet for support admin" in {
@ -790,7 +822,8 @@ class RecordSetServiceSpec
zoneId = okZone.id,
startFrom = None,
maxItems = None,
recordNameFilter = None)
recordNameFilter = None
)
val result: ListRecordSetsResponse = rightResultOf(
underTest
@ -801,9 +834,11 @@ class RecordSetServiceSpec
recordNameFilter = None,
authPrincipal = AuthPrincipal(okAuth.signedInUser.copy(isSupport = true), Seq.empty)
)
.value)
.value
)
result.recordSets shouldBe List(
RecordSetListInfo(RecordSetInfo(aaaa, None), AccessLevel.Read))
RecordSetListInfo(RecordSetInfo(aaaa, None), AccessLevel.Read)
)
}
"fails when the account is not authorized" in {
val result = leftResultOf(
@ -813,8 +848,10 @@ class RecordSetServiceSpec
startFrom = None,
maxItems = None,
recordNameFilter = None,
authPrincipal = okAuth)
.value)
authPrincipal = okAuth
)
.value
)
result shouldBe a[NotAuthorizedError]
}
}
@ -840,7 +877,8 @@ class RecordSetServiceSpec
recordSetChanges = changesWithName,
nextId = None,
startFrom = None,
maxItems = 100)
maxItems = 100
)
result shouldBe expectedResults
}
@ -856,13 +894,15 @@ class RecordSetServiceSpec
recordSetChanges = List(),
nextId = None,
startFrom = None,
maxItems = 100)
maxItems = 100
)
result shouldBe expectedResults
}
"return a NotAuthorizedError" in {
val error = leftResultOf(
underTest.listRecordSetChanges(zoneNotAuthorized.id, authPrincipal = okAuth).value)
underTest.listRecordSetChanges(zoneNotAuthorized.id, authPrincipal = okAuth).value
)
error shouldBe a[NotAuthorizedError]
}
@ -883,7 +923,8 @@ class RecordSetServiceSpec
recordSetChanges = changesWithName,
nextId = None,
startFrom = None,
maxItems = 100)
maxItems = 100
)
result shouldBe expectedResults
}
}
@ -906,7 +947,8 @@ class RecordSetServiceSpec
val actual: RecordSetChange =
rightResultOf(
underTest.getRecordSetChange(sharedZone.id, pendingCreateSharedRecord.id, okAuth).value)
underTest.getRecordSetChange(sharedZone.id, pendingCreateSharedRecord.id, okAuth).value
)
actual shouldBe pendingCreateSharedRecord
}
@ -926,7 +968,8 @@ class RecordSetServiceSpec
.getRecordSetChange(zoneActive.id, pendingCreateAAAA.id)
val error = leftResultOf(
underTest.getRecordSetChange(zoneActive.id, pendingCreateAAAA.id, dummyAuth).value)
underTest.getRecordSetChange(zoneActive.id, pendingCreateAAAA.id, dummyAuth).value
)
error shouldBe a[NotAuthorizedError]
}
@ -937,9 +980,15 @@ class RecordSetServiceSpec
.when(mockRecordChangeRepo)
.getRecordSetChange(zoneNotAuthorized.id, pendingCreateSharedRecordNotSharedZone.id)
val error = leftResultOf(underTest
.getRecordSetChange(zoneNotAuthorized.id, pendingCreateSharedRecordNotSharedZone.id, okAuth)
.value)
val error = leftResultOf(
underTest
.getRecordSetChange(
zoneNotAuthorized.id,
pendingCreateSharedRecordNotSharedZone.id,
okAuth
)
.value
)
error shouldBe a[NotAuthorizedError]
}

View File

@ -209,7 +209,8 @@ class RecordSetValidationsSpec
dottedARecord,
List(),
okZone,
Some(dottedARecord.copy(ttl = 300))) should be(right)
Some(dottedARecord.copy(ttl = 300))
) should be(right)
}
"return a success for any existing record with dotted hosts in forward zones (CNAME)" in {
@ -218,7 +219,8 @@ class RecordSetValidationsSpec
dottedCNAMERecord,
List(),
okZone,
Some(dottedCNAMERecord.copy(ttl = 300))) should be(right)
Some(dottedCNAMERecord.copy(ttl = 300))
) should be(right)
}
"return a failure for any existing record with dotted hosts in forward zones (NS)" in {
@ -228,7 +230,8 @@ class RecordSetValidationsSpec
dottedNSRecord,
List(),
okZone,
Some(dottedNSRecord.copy(ttl = 300)))
Some(dottedNSRecord.copy(ttl = 300))
)
) shouldBe an[InvalidRequest]
}
}
@ -311,7 +314,8 @@ class RecordSetValidationsSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(SOAData("something", "other", 1, 2, 3, 5, 6)))
List(SOAData("something", "other", 1, 2, 3, 5, 6))
)
typeSpecificValidations(test, List(), zoneIp4) should be(right)
}
@ -323,7 +327,8 @@ class RecordSetValidationsSpec
"return ok if the record is an NS record but not origin" in {
val valid = invalidNsApexRs.copy(
name = "this-is-not-origin-mate",
records = List(NSData("some.test.ns.")))
records = List(NSData("some.test.ns."))
)
nsValidations(valid, okZone) should be(right)
}
@ -406,12 +411,14 @@ class RecordSetValidationsSpec
}
"return ok if new recordset name does not contain dot" in {
cnameValidations(cname, List(), okZone, Some(cname.copy(name = "not-dotted"))) should be(
right)
right
)
}
"return ok if dotted host name doesn't change" in {
val newRecord = cname.copy(name = "dot.ted", ttl = 500)
cnameValidations(newRecord, List(), okZone, Some(newRecord.copy(ttl = 300))) should be(
right)
right
)
}
"return an InvalidRequest if a cname record set name is updated to '@'" in {
val error = leftValue(cnameValidations(cname.copy(name = "@"), List(), okZone, Some(cname)))
@ -485,7 +492,8 @@ class RecordSetValidationsSpec
val ownerGroupIdBad = "bar"
val auth = okAuth.copy(
memberGroupIds = Seq("foo"),
signedInUser = okAuth.signedInUser.copy(isSuper = true))
signedInUser = okAuth.signedInUser.copy(isSuper = true)
)
val ownerGroup = Group(id = ownerGroupIdGood, name = "test", email = "test@test.com")
canUseOwnerGroup(Some(ownerGroupIdGood), Some(ownerGroup), auth) should be(right)

View File

@ -103,7 +103,8 @@ class ZoneConnectionValidatorSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(SOAData("something", "other", 1, 2, 3, 5, 6)))
List(SOAData("something", "other", 1, 2, 3, 5, 6))
)
private val successNS = RecordSet(
testZone.id,
@ -113,7 +114,8 @@ class ZoneConnectionValidatorSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(NSData("some.test.ns.")))
List(NSData("some.test.ns."))
)
private val failureNs = RecordSet(
testZone.id,
@ -123,7 +125,8 @@ class ZoneConnectionValidatorSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(NSData("some.test.ns."), NSData("not.approved.")))
List(NSData("some.test.ns."), NSData("not.approved."))
)
private val delegatedNS = RecordSet(
testZone.id,
@ -133,7 +136,8 @@ class ZoneConnectionValidatorSpec
RecordSetStatus.Active,
DateTime.now,
None,
List(NSData("sub.some.test.ns.")))
List(NSData("sub.some.test.ns."))
)
private val mockRecordSet = mock[RecordSet]
@ -142,7 +146,8 @@ class ZoneConnectionValidatorSpec
val backend = DnsBackend(
"some-backend-id",
zc.copy(name = "backend-conn"),
transfer.copy(name = "backend-transfer"))
transfer.copy(name = "backend-transfer")
)
val connections = ConfiguredDnsConnections(zc, transfer, List(backend))
"ConnectionValidator" should {
@ -172,7 +177,8 @@ class ZoneConnectionValidatorSpec
result shouldBe ZoneValidationFailed(
testZone,
List(s"Name Server not.approved. is not an approved name server."),
"Zone could not be loaded due to validation errors.")
"Zone could not be loaded due to validation errors."
)
}
"respond with a failure if no records are returned from the backend" in {
@ -187,7 +193,8 @@ class ZoneConnectionValidatorSpec
result shouldBe ZoneValidationFailed(
testZone,
List("Missing apex NS record"),
"Zone could not be loaded due to validation errors.")
"Zone could not be loaded due to validation errors."
)
}
"respond with a failure if any failure is returned from the backend" in {
@ -256,7 +263,8 @@ class ZoneConnectionValidatorSpec
val backend = DnsBackend("some-test-backend", testDefaultConnection, testDefaultConnection)
val underTest =
new ZoneConnectionValidator(
ConfiguredDnsConnections(testDefaultConnection, testDefaultConnection, List(backend)))
ConfiguredDnsConnections(testDefaultConnection, testDefaultConnection, List(backend))
)
"return success if the backendId exists" in {
underTest.isValidBackendId(Some("some-test-backend")) shouldBe right

View File

@ -77,7 +77,8 @@ class ZoneRecordValidationsSpec extends WordSpec with Matchers with ValidatedMat
"return an error if fqdn is in high value list" in {
val result = isNotHighValueFqdn(highValueRegexList, "high-value-domain.foo.")
result should haveInvalid[DomainValidationError](
HighValueDomainError("high-value-domain.foo."))
HighValueDomainError("high-value-domain.foo.")
)
}
}
@ -134,14 +135,15 @@ class ZoneRecordValidationsSpec extends WordSpec with Matchers with ValidatedMat
"return failure if none of the name servers are in the list of approved name servers" in {
val test = ns.copy(records = List(NSData("blah1."), NSData("blah2.")))
containsApprovedNameServers(approvedNameServers, test) should haveInvalid(
"Name Server blah1. is not an approved name server.").and(
haveInvalid("Name Server blah2. is not an approved name server."))
"Name Server blah1. is not an approved name server."
).and(haveInvalid("Name Server blah2. is not an approved name server."))
}
"return a failure if any of the name servers are not in the list of approved name servers" in {
val test = ns.copy(records = List(NSData("blah1."), NSData("ns1.test.com")))
containsApprovedNameServers(approvedNameServers, test) should haveInvalid(
"Name Server blah1. is not an approved name server.")
"Name Server blah1. is not an approved name server."
)
}
"return success if the name server matches a regular expression" in {
@ -160,7 +162,8 @@ class ZoneRecordValidationsSpec extends WordSpec with Matchers with ValidatedMat
val test = ns.copy(records = List(NSData("test-foo-ns.")))
val approved = List(".*bar.*".r, "www.*".r)
containsApprovedNameServers(approved, test) should haveInvalid(
"Name Server test-foo-ns. is not an approved name server.")
"Name Server test-foo-ns. is not an approved name server."
)
}
}

View File

@ -74,20 +74,23 @@ class ZoneServiceSpec
TestConnectionValidator,
mockMessageQueue,
new ZoneValidations(1000),
new AccessValidations())
new AccessValidations()
)
private val createZoneAuthorized = CreateZoneInput(
"ok.zone.recordsets.",
"test@test.com",
connection = testConnection,
adminGroupId = okGroup.id)
adminGroupId = okGroup.id
)
private val updateZoneAuthorized = UpdateZoneInput(
okZone.id,
"ok.zone.recordsets.",
"updated-test@test.com",
connection = testConnection,
adminGroupId = okGroup.id)
adminGroupId = okGroup.id
)
override protected def beforeEach(): Unit = {
reset(mockGroupRepo, mockZoneRepo, mockUserRepo)
@ -100,7 +103,8 @@ class ZoneServiceSpec
doReturn(IO.pure(None)).when(mockZoneRepo).getZoneByName(anyString)
val resultChange: ZoneChange = rightResultOf(
underTest.connectToZone(createZoneAuthorized, okAuth).map(_.asInstanceOf[ZoneChange]).value)
underTest.connectToZone(createZoneAuthorized, okAuth).map(_.asInstanceOf[ZoneChange]).value
)
resultChange.changeType shouldBe ZoneChangeType.Create
Option(resultChange.created) shouldBe defined
@ -124,7 +128,8 @@ class ZoneServiceSpec
underTest
.connectToZone(createZoneAuthorized, nonTestUser)
.map(_.asInstanceOf[ZoneChange])
.value)
.value
)
resultChange.zone.isTest shouldBe false
}
@ -138,7 +143,8 @@ class ZoneServiceSpec
underTest
.connectToZone(createZoneAuthorized, testUser)
.map(_.asInstanceOf[ZoneChange])
.value)
.value
)
resultChange.zone.isTest shouldBe true
}
@ -164,7 +170,8 @@ class ZoneServiceSpec
doReturn(IO.pure(Some(zoneDeleted))).when(mockZoneRepo).getZoneByName(anyString)
val resultChange: ZoneChange = rightResultOf(
underTest.connectToZone(createZoneAuthorized, okAuth).map(_.asInstanceOf[ZoneChange]).value)
underTest.connectToZone(createZoneAuthorized, okAuth).map(_.asInstanceOf[ZoneChange]).value
)
resultChange.changeType shouldBe ZoneChangeType.Create
}
@ -181,7 +188,8 @@ class ZoneServiceSpec
doReturn(IO.pure(None)).when(mockZoneRepo).getZoneByName(anyString)
val resultZone = rightResultOf(
underTest.connectToZone(newZone, superUserAuth).map(_.asInstanceOf[ZoneChange]).value).zone
underTest.connectToZone(newZone, superUserAuth).map(_.asInstanceOf[ZoneChange]).value
).zone
Option(resultZone.id) should not be None
resultZone.email shouldBe okZone.email
@ -199,7 +207,8 @@ class ZoneServiceSpec
underTest
.connectToZone(newZone, supportUserAuth)
.map(_.asInstanceOf[ZoneChange])
.value).zone
.value
).zone
Option(resultZone.id) should not be None
resultZone.email shouldBe okZone.email
@ -237,7 +246,8 @@ class ZoneServiceSpec
.updateZone(updateZoneInput, doubleAuth)
.map(_.asInstanceOf[ZoneChange])
.value,
duration = 2.seconds)
duration = 2.seconds
)
resultChange.zone.id shouldBe okZone.id
resultChange.changeType shouldBe ZoneChangeType.Update
@ -259,7 +269,8 @@ class ZoneServiceSpec
underTest
.updateZone(newZone, doubleAuth)
.map(_.asInstanceOf[ZoneChange])
.value)
.value
)
resultChange.zone.id shouldBe oldZone.id
resultChange.zone.connection shouldBe oldZone.connection
}
@ -302,7 +313,8 @@ class ZoneServiceSpec
val result = rightResultOf(
underTest
.updateZone(newZone, AuthPrincipal(superUser, List.empty))
.value)
.value
)
result shouldBe a[ZoneChange]
}
@ -315,7 +327,8 @@ class ZoneServiceSpec
val result = rightResultOf(
underTest
.updateZone(newZone, supportUserAuth)
.value)
.value
)
result shouldBe a[ZoneChange]
}
@ -449,7 +462,8 @@ class ZoneServiceSpec
zoneWithRules,
ZoneACLInfo(Set(goodUserRuleInfo, goodGroupRuleInfo, goodAllRuleInfo)),
goodGroup.name,
AccessLevel.Delete)
AccessLevel.Delete
)
val result: ZoneInfo = rightResultOf(underTest.getZone(zoneWithRules.id, abcAuth).value)
result shouldBe expectedZoneInfo
}
@ -564,8 +578,10 @@ class ZoneServiceSpec
List(abcZone, xyzZone),
maxItems = 2,
nextId = Some("zone2."),
ignoreAccess = false)))
.when(mockZoneRepo)
ignoreAccess = false
)
)
).when(mockZoneRepo)
.listZones(abcAuth, None, None, 2, false)
doReturn(IO.pure(Set(abcGroup, xyzGroup)))
.when(mockGroupRepo)
@ -588,8 +604,10 @@ class ZoneServiceSpec
zonesFilter = Some("foo"),
maxItems = 2,
nextId = Some("zone2."),
ignoreAccess = false)))
.when(mockZoneRepo)
ignoreAccess = false
)
)
).when(mockZoneRepo)
.listZones(abcAuth, Some("foo"), None, 2, false)
doReturn(IO.pure(Set(abcGroup, xyzGroup)))
.when(mockGroupRepo)
@ -610,8 +628,10 @@ class ZoneServiceSpec
List(abcZone, xyzZone),
startFrom = Some("zone4."),
maxItems = 2,
ignoreAccess = false)))
.when(mockZoneRepo)
ignoreAccess = false
)
)
).when(mockZoneRepo)
.listZones(abcAuth, None, Some("zone4."), 2, false)
doReturn(IO.pure(Set(abcGroup, xyzGroup)))
.when(mockGroupRepo)
@ -631,8 +651,10 @@ class ZoneServiceSpec
startFrom = Some("zone4."),
maxItems = 2,
nextId = Some("zone6."),
ignoreAccess = false)))
.when(mockZoneRepo)
ignoreAccess = false
)
)
).when(mockZoneRepo)
.listZones(abcAuth, None, Some("zone4."), 2, false)
doReturn(IO.pure(Set(abcGroup, xyzGroup)))
.when(mockGroupRepo)
@ -719,7 +741,8 @@ class ZoneServiceSpec
underTest
.addACLRule(okZone.id, userAclRuleInfo, okAuth)
.map(_.asInstanceOf[ZoneChange])
.value)
.value
)
result.changeType shouldBe ZoneChangeType.Update
result.zone.acl.rules.size shouldBe 1
@ -754,7 +777,8 @@ class ZoneServiceSpec
underTest
.deleteACLRule(zone.id, userAclRuleInfo, okAuth)
.map(_.asInstanceOf[ZoneChange])
.value)
.value
)
result.changeType shouldBe ZoneChangeType.Update
result.zone.acl.rules.size shouldBe 0

View File

@ -62,7 +62,8 @@ class ZoneValidationsSpec
"fail if given an invalid CIDR rule" in {
val invalidPtrAclRuleInfo = baseAclRuleInfo.copy(
recordMask = Some("not a cidr rule"),
recordTypes = Set(RecordType.PTR))
recordTypes = Set(RecordType.PTR)
)
val error = leftValue(isValidAclRule(ACLRule(invalidPtrAclRuleInfo)))
error shouldBe a[InvalidRequest]
error.getMessage shouldBe "PTR types must have no mask or a valid CIDR mask: Invalid CIDR block"
@ -71,7 +72,8 @@ class ZoneValidationsSpec
"fail if there are multiple record types including PTR and mask is regex" in {
val invalidMultipleTypeInfo = baseAclRuleInfo.copy(
recordMask = Some("regex"),
recordTypes = Set(RecordType.A, RecordType.AAAA, RecordType.CNAME, RecordType.PTR))
recordTypes = Set(RecordType.A, RecordType.AAAA, RecordType.CNAME, RecordType.PTR)
)
val error = leftValue(isValidAclRule(ACLRule(invalidMultipleTypeInfo)))
error shouldBe a[InvalidRequest]
}
@ -79,14 +81,16 @@ class ZoneValidationsSpec
"fail if there are multiple record types including PTR and mask is cidr" in {
val invalidMultipleTypeInfo = baseAclRuleInfo.copy(
recordMask = Some("10.10.10.10/5"),
recordTypes = Set(RecordType.A, RecordType.AAAA, RecordType.CNAME, RecordType.PTR))
recordTypes = Set(RecordType.A, RecordType.AAAA, RecordType.CNAME, RecordType.PTR)
)
val error = leftValue(isValidAclRule(ACLRule(invalidMultipleTypeInfo)))
error shouldBe a[InvalidRequest]
}
"pass if there are multiple record types including PTR and mask is None" in {
val validMultipleTypeNoneAclRuleInfo = baseAclRuleInfo.copy(
recordTypes = Set(RecordType.A, RecordType.AAAA, RecordType.CNAME, RecordType.PTR))
recordTypes = Set(RecordType.A, RecordType.AAAA, RecordType.CNAME, RecordType.PTR)
)
isValidAclRule(ACLRule(validMultipleTypeNoneAclRuleInfo)) should be(right)
}

View File

@ -38,7 +38,8 @@ class ZoneViewLoaderSpec extends WordSpec with Matchers with MockitoSugar with D
val testZoneName = "vinyldns."
val testZoneConnection: Option[ZoneConnection] = Some(
ZoneConnection(testZoneName, testZoneName, "nzisn+4G2ldMn0q1CV3vsg==", "127.0.0.1:19001"))
ZoneConnection(testZoneName, testZoneName, "nzisn+4G2ldMn0q1CV3vsg==", "127.0.0.1:19001")
)
private val testZone = Zone("vinyldns.", "test@test.com")
private val records = List(
@ -49,7 +50,8 @@ class ZoneViewLoaderSpec extends WordSpec with Matchers with MockitoSugar with D
ttl = 100,
status = RecordSetStatus.Active,
created = DateTime.now,
records = List(AData("1.1.1.1"))),
records = List(AData("1.1.1.1"))
),
RecordSet(
zoneId = testZone.id,
name = "abc",
@ -57,7 +59,8 @@ class ZoneViewLoaderSpec extends WordSpec with Matchers with MockitoSugar with D
ttl = 100,
status = RecordSetStatus.Active,
created = DateTime.now,
records = List(AData("2.2.2.2"))),
records = List(AData("2.2.2.2"))
),
RecordSet(
zoneId = testZone.id,
name = "abc",
@ -74,7 +77,8 @@ class ZoneViewLoaderSpec extends WordSpec with Matchers with MockitoSugar with D
ttl = 100,
status = RecordSetStatus.Active,
created = DateTime.now,
records = List(AData("3.3.3.3")))
records = List(AData("3.3.3.3"))
)
)
"VinylDNSZoneViewLoader" should {
@ -115,7 +119,8 @@ class ZoneViewLoaderSpec extends WordSpec with Matchers with MockitoSugar with D
ttl = 38400,
status = RecordSetStatus.Active,
created = DateTime.now,
records = List(AData("1.1.1.1"))),
records = List(AData("1.1.1.1"))
),
RecordSet(
zoneId = testZone.id,
name = "abc",
@ -123,7 +128,8 @@ class ZoneViewLoaderSpec extends WordSpec with Matchers with MockitoSugar with D
ttl = 38400,
status = RecordSetStatus.Active,
created = DateTime.now,
records = List(AData("2.2.2.2"))),
records = List(AData("2.2.2.2"))
),
RecordSet(
zoneId = testZone.id,
name = "abc",
@ -140,7 +146,8 @@ class ZoneViewLoaderSpec extends WordSpec with Matchers with MockitoSugar with D
ttl = 38400,
status = RecordSetStatus.Active,
created = DateTime.now,
records = List(AData("3.3.3.3")))
records = List(AData("3.3.3.3"))
)
)
val dnsRecords = new mutable.ArrayBuffer[DNS.Record]()
@ -149,25 +156,33 @@ class ZoneViewLoaderSpec extends WordSpec with Matchers with MockitoSugar with D
new Name("abc.vinyldns."),
DNS.DClass.IN,
38400,
InetAddress.getByName("1.1.1.1")))
InetAddress.getByName("1.1.1.1")
)
)
dnsRecords.append(
new DNS.ARecord(
new Name("abc.vinyldns."),
DNS.DClass.IN,
38400,
InetAddress.getByName("2.2.2.2")))
InetAddress.getByName("2.2.2.2")
)
)
dnsRecords.append(
new DNS.AAAARecord(
new Name("abc.vinyldns."),
DNS.DClass.IN,
38400,
InetAddress.getByName("2001:db8:a0b:12f0::1")))
InetAddress.getByName("2001:db8:a0b:12f0::1")
)
)
dnsRecords.append(
new DNS.ARecord(
new Name("def.vinyldns."),
DNS.DClass.IN,
38400,
InetAddress.getByName("3.3.3.3")))
InetAddress.getByName("3.3.3.3")
)
)
doReturn(dnsRecords.asJava).when(mockTransfer).getAXFR
@ -228,7 +243,8 @@ class ZoneViewLoaderSpec extends WordSpec with Matchers with MockitoSugar with D
status = RecordSetStatus.Active,
created = DateTime.now,
records = List(
SOAData("172.17.42.1.", "admin.vinyldns.com.", 1439234395, 10800, 3600, 604800, 38400))
SOAData("172.17.42.1.", "admin.vinyldns.com.", 1439234395, 10800, 3600, 604800, 38400)
)
)
)
@ -244,37 +260,49 @@ class ZoneViewLoaderSpec extends WordSpec with Matchers with MockitoSugar with D
10800,
3600,
604800,
38400))
38400
)
)
dnsRecords.append(
new DNS.ARecord(
new Name("abc.vinyldns."),
DNS.DClass.IN,
38400,
InetAddress.getByName("1.1.1.1")))
InetAddress.getByName("1.1.1.1")
)
)
dnsRecords.append(
new DNS.ARecord(
new Name("abc.vinyldns."),
DNS.DClass.IN,
38400,
InetAddress.getByName("1.1.1.1")))
InetAddress.getByName("1.1.1.1")
)
)
dnsRecords.append(
new DNS.ARecord(
new Name("abc.vinyldns."),
DNS.DClass.IN,
38400,
InetAddress.getByName("1.1.1.1")))
InetAddress.getByName("1.1.1.1")
)
)
dnsRecords.append(
new DNS.ARecord(
new Name("abc.vinyldns."),
DNS.DClass.IN,
38400,
InetAddress.getByName("1.1.1.1")))
InetAddress.getByName("1.1.1.1")
)
)
dnsRecords.append(
new DNS.ARecord(
new Name("abc.vinyldns."),
DNS.DClass.IN,
38400,
InetAddress.getByName("2.2.2.2")))
InetAddress.getByName("2.2.2.2")
)
)
dnsRecords.append(
new DNS.SOARecord(
new Name("vinyldns."),
@ -286,7 +314,9 @@ class ZoneViewLoaderSpec extends WordSpec with Matchers with MockitoSugar with D
10800,
3600,
604800,
38400))
38400
)
)
dnsRecords.append(
new DNS.NULLRecord(
new Name("some.unsupported.record.type."),

View File

@ -35,7 +35,8 @@ class ZoneViewSpec extends WordSpec with Matchers with VinylDNSTestHelpers {
ttl = 100,
status = RecordSetStatus.Active,
created = DateTime.now,
records = List(AData("1.1.1.1"))),
records = List(AData("1.1.1.1"))
),
RecordSet(
zoneId = testZone.id,
name = "abc",
@ -43,7 +44,8 @@ class ZoneViewSpec extends WordSpec with Matchers with VinylDNSTestHelpers {
ttl = 100,
status = RecordSetStatus.Active,
created = DateTime.now,
records = List(AData("2.2.2.2"))),
records = List(AData("2.2.2.2"))
),
RecordSet(
zoneId = testZone.id,
name = "abc",
@ -71,7 +73,8 @@ class ZoneViewSpec extends WordSpec with Matchers with VinylDNSTestHelpers {
ttl = 100,
status = RecordSetStatus.Active,
created = DateTime.now,
records = List(AData("1.1.1.1"))),
records = List(AData("1.1.1.1"))
),
RecordSet(
zoneId = testZone.id,
name = "vinyldns.",
@ -93,7 +96,8 @@ class ZoneViewSpec extends WordSpec with Matchers with VinylDNSTestHelpers {
case Some(records) =>
records.records should contain theSameElementsAs List(
AData("1.1.1.1"),
AData("2.2.2.2"))
AData("2.2.2.2")
)
case None => fail()
}
@ -193,7 +197,8 @@ class ZoneViewSpec extends WordSpec with Matchers with VinylDNSTestHelpers {
ttl = 100,
status = RecordSetStatus.Active,
created = DateTime.now,
records = List(AData("4.4.4.4"))) //updated
records = List(AData("4.4.4.4"))
) //updated
)
val dnsView = ZoneView(testZone, dnsRecords)
@ -268,7 +273,8 @@ class ZoneViewSpec extends WordSpec with Matchers with VinylDNSTestHelpers {
ttl = 100,
status = RecordSetStatus.Active,
created = DateTime.now,
records = List(AData("4.4.4.4"))) //updated
records = List(AData("4.4.4.4"))
) //updated
)
val dnsView = ZoneView(testZone, dnsRecords)

View File

@ -66,7 +66,8 @@ class BatchChangeHandlerSpec
DateTime.now,
List(addChange),
Some("ownerGroupId"),
BatchChangeApprovalStatus.AutoApproved)
BatchChangeApprovalStatus.AutoApproved
)
override protected def beforeEach(): Unit =
batchRepo.clear()

View File

@ -73,7 +73,8 @@ class RecordSetChangeHandlerSpec
SingleChangeStatus.Pending,
None,
None,
None)
None
)
}
private val notUpdatedChange = SingleAddChange(
Some("someId"),
@ -86,7 +87,8 @@ class RecordSetChangeHandlerSpec
SingleChangeStatus.Pending,
None,
None,
None)
None
)
private val singleChanges = notUpdatedChange :: completeCreateAAAASingleChanges
private val batchChange = BatchChange(
"userId",
@ -94,7 +96,8 @@ class RecordSetChangeHandlerSpec
None,
DateTime.now,
singleChanges,
approvalStatus = BatchChangeApprovalStatus.AutoApproved)
approvalStatus = BatchChangeApprovalStatus.AutoApproved
)
private val rsChange =
completeCreateAAAA.copy(singleBatchChangeIds = completeCreateAAAASingleChanges.map(_.id))
@ -147,7 +150,8 @@ class RecordSetChangeHandlerSpec
ch.copy(
status = SingleChangeStatus.Complete,
recordChangeId = Some(rsChange.id),
recordSetId = Some(rsChange.recordSet.id))
recordSetId = Some(rsChange.recordSet.id)
)
}
val scExpected = notUpdatedChange :: updatedSingleChanges
batchChangeUpdates.get.changes shouldBe scExpected
@ -188,7 +192,8 @@ class RecordSetChangeHandlerSpec
ch.copy(
status = SingleChangeStatus.Complete,
recordChangeId = Some(rsChange.id),
recordSetId = Some(rsChange.recordSet.id))
recordSetId = Some(rsChange.recordSet.id)
)
}
val scExpected = notUpdatedChange :: updatedSingleChanges
batchChangeUpdates.get.changes shouldBe scExpected
@ -232,7 +237,8 @@ class RecordSetChangeHandlerSpec
ch.copy(
status = SingleChangeStatus.Failed,
recordChangeId = Some(rsChange.id),
systemMessage = savedCs.changes.head.systemMessage)
systemMessage = savedCs.changes.head.systemMessage
)
}
val scExpected = notUpdatedChange :: updatedSingleChanges
batchChangeUpdates.get.changes shouldBe scExpected
@ -277,7 +283,8 @@ class RecordSetChangeHandlerSpec
ch.copy(
status = SingleChangeStatus.Complete,
recordChangeId = Some(rsChange.id),
recordSetId = Some(rsChange.recordSet.id))
recordSetId = Some(rsChange.recordSet.id)
)
}
val scExpected = notUpdatedChange :: updatedSingleChanges
batchChangeUpdates.get.changes shouldBe scExpected
@ -318,7 +325,8 @@ class RecordSetChangeHandlerSpec
ch.copy(
status = SingleChangeStatus.Failed,
recordChangeId = Some(rsChange.id),
systemMessage = savedCs.changes.head.systemMessage)
systemMessage = savedCs.changes.head.systemMessage
)
}
val scExpected = notUpdatedChange :: updatedSingleChanges
batchChangeUpdates.get.changes shouldBe scExpected
@ -361,7 +369,8 @@ class RecordSetChangeHandlerSpec
ch.copy(
status = SingleChangeStatus.Failed,
recordChangeId = Some(rsChange.id),
systemMessage = savedCs.changes.head.systemMessage)
systemMessage = savedCs.changes.head.systemMessage
)
}
val scExpected = notUpdatedChange :: updatedSingleChanges
batchChangeUpdates.get.changes shouldBe scExpected
@ -400,7 +409,8 @@ class RecordSetChangeHandlerSpec
ch.copy(
status = SingleChangeStatus.Failed,
recordChangeId = Some(rsChange.id),
systemMessage = savedCs.changes.head.systemMessage)
systemMessage = savedCs.changes.head.systemMessage
)
}
val scExpected = notUpdatedChange :: updatedSingleChanges
batchChangeUpdates.get.changes shouldBe scExpected
@ -438,7 +448,8 @@ class RecordSetChangeHandlerSpec
ch.copy(
status = SingleChangeStatus.Failed,
recordChangeId = Some(rsChange.id),
systemMessage = savedCs.changes.head.systemMessage)
systemMessage = savedCs.changes.head.systemMessage
)
}
val scExpected = notUpdatedChange :: updatedSingleChanges
batchChangeUpdates.get.changes shouldBe scExpected
@ -491,7 +502,8 @@ class RecordSetChangeHandlerSpec
ch.copy(
status = SingleChangeStatus.Complete,
recordChangeId = Some(rsChange.id),
recordSetId = Some(rsChange.recordSet.id))
recordSetId = Some(rsChange.recordSet.id)
)
}
val scExpected = notUpdatedChange :: updatedSingleChanges
batchChangeUpdates.get.changes shouldBe scExpected
@ -549,7 +561,8 @@ class RecordSetChangeHandlerSpec
ch.copy(
status = SingleChangeStatus.Complete,
recordChangeId = Some(rsChange.id),
recordSetId = Some(rsChange.recordSet.id))
recordSetId = Some(rsChange.recordSet.id)
)
}
val scExpected = notUpdatedChange :: updatedSingleChanges
batchChangeUpdates.get.changes shouldBe scExpected
@ -605,7 +618,8 @@ class RecordSetChangeHandlerSpec
"complete an update successfully if the requested record set change matches the DNS backend" in {
val updateChange = rsChange.copy(
changeType = RecordSetChangeType.Update,
updates = Some(rsChange.recordSet.copy(ttl = 87)))
updates = Some(rsChange.recordSet.copy(ttl = 87))
)
doReturn(Interfaces.result(Right(List(updateChange.recordSet))))
.when(mockConn)
.resolve(rsChange.recordSet.name, rsChange.zone.name, rsChange.recordSet.typ)
@ -635,7 +649,8 @@ class RecordSetChangeHandlerSpec
ch.copy(
status = SingleChangeStatus.Complete,
recordChangeId = Some(rsChange.id),
recordSetId = Some(rsChange.recordSet.id))
recordSetId = Some(rsChange.recordSet.id)
)
}
val scExpected = notUpdatedChange :: updatedSingleChanges
batchChangeUpdates.get.changes shouldBe scExpected
@ -644,7 +659,8 @@ class RecordSetChangeHandlerSpec
"fail an update if current record does not match the DNS backend and the change has not already been applied" in {
val updateChange = rsChange.copy(
changeType = RecordSetChangeType.Update,
updates = Some(rsChange.recordSet.copy(ttl = 87)))
updates = Some(rsChange.recordSet.copy(ttl = 87))
)
doReturn(Interfaces.result(Right(List(updateChange.recordSet.copy(ttl = 30)))))
.when(mockConn)
.resolve(rsChange.recordSet.name, rsChange.zone.name, rsChange.recordSet.typ)
@ -669,7 +685,8 @@ class RecordSetChangeHandlerSpec
changeSet.systemMessage shouldBe Some(
s"Failed validating update to DNS for change ${changeSet.id}:${changeSet.recordSet.name}: " +
s"This record set is out of sync with the DNS backend; sync this zone before attempting to " +
"update this record set.")
"update this record set."
)
val savedCs = changeRepoCaptor.getValue
savedCs.status shouldBe ChangeSetStatus.Complete
@ -731,7 +748,8 @@ class RecordSetChangeHandlerSpec
.getProcessingStatus(
rsChange
.copy(changeType = RecordSetChangeType.Update, updates = Some(rs.copy(ttl = 300))),
mockConn)
mockConn
)
.unsafeRunSync()
processorStatus shouldBe a[ReadyToApply]
}
@ -755,7 +773,8 @@ class RecordSetChangeHandlerSpec
val processorStatus = RecordSetChangeHandler
.getProcessingStatus(
rsChange.copy(changeType = RecordSetChangeType.Update, updates = None),
mockConn)
mockConn
)
.unsafeRunSync()
processorStatus shouldBe a[Failure]
}

View File

@ -75,7 +75,8 @@ class ZoneSyncHandlerSpec
ttl = 100,
status = RecordSetStatus.Active,
created = DateTime.now,
records = List(AData("1.1.1.1")))
records = List(AData("1.1.1.1"))
)
private val testRecord2 = RecordSet(
zoneId = testZone.id,
name = "def",
@ -83,7 +84,8 @@ class ZoneSyncHandlerSpec
ttl = 100,
status = RecordSetStatus.Active,
created = DateTime.now,
records = List(AData("2.2.2.2")))
records = List(AData("2.2.2.2"))
)
private val testRecordDotted = RecordSet(
zoneId = testZone.id,
name = "gh.i.",
@ -91,7 +93,8 @@ class ZoneSyncHandlerSpec
ttl = 100,
status = RecordSetStatus.Active,
created = DateTime.now,
records = List(AData("3.3.3.3")))
records = List(AData("3.3.3.3"))
)
private val testRecordDottedOk = RecordSet(
zoneId = testZone.id,
name = s"ok-dotted.${testZone.name}",
@ -144,14 +147,16 @@ class ZoneSyncHandlerSpec
zoneChangeRepo,
zoneRepo,
_ => mockDNSLoader,
(_, _) => mockVinylDNSLoader)
(_, _) => mockVinylDNSLoader
)
private val runSync = ZoneSyncHandler.runSync(
recordSetRepo,
recordChangeRepo,
testZoneChange,
_ => mockDNSLoader,
(_, _) => mockVinylDNSLoader)
(_, _) => mockVinylDNSLoader
)
override def beforeEach(): Unit = {
reset(recordSetRepo)
@ -309,7 +314,8 @@ class ZoneSyncHandlerSpec
recordChangeRepo,
testZoneChange,
dnsLoader,
(_, _) => mockVinylDNSLoader)
(_, _) => mockVinylDNSLoader
)
.unsafeRunSync()
verify(dnsLoader).apply(captor.capture())
@ -325,7 +331,8 @@ class ZoneSyncHandlerSpec
recordChangeRepo,
testZoneChange,
_ => mockDNSLoader,
(_, _) => mockVinylDNSLoader)
(_, _) => mockVinylDNSLoader
)
.unsafeRunSync()
verify(mockDNSLoader, times(1)).load
@ -344,7 +351,8 @@ class ZoneSyncHandlerSpec
recordChangeRepo,
testZoneChange,
_ => mockDNSLoader,
vinyldnsLoader)
vinyldnsLoader
)
.unsafeRunSync()
verify(vinyldnsLoader).apply(zoneCaptor.capture(), repoCaptor.capture())
@ -444,7 +452,8 @@ class ZoneSyncHandlerSpec
recordChangeRepo,
zoneChange,
_ => mockDNSLoader,
(_, _) => mockVinylDNSLoader)
(_, _) => mockVinylDNSLoader
)
.unsafeRunSync()
captor.getValue.changes should contain theSameElementsAs expectedChanges

View File

@ -40,7 +40,8 @@ class APIMetricsSpec extends WordSpec with Matchers with MockitoSugar with Eithe
""".stripMargin
)
APIMetrics.loadSettings(config).attempt.unsafeRunSync() shouldBe Right(
APIMetricsSettings(MemoryMetricsSettings(logEnabled = true, logSeconds = 5)))
APIMetricsSettings(MemoryMetricsSettings(logEnabled = true, logSeconds = 5))
)
}
"fail with invalid config" in {
val config = ConfigFactory.parseString(
@ -70,7 +71,8 @@ class APIMetricsSpec extends WordSpec with Matchers with MockitoSugar with Eithe
APIMetrics
.initialize(
APIMetricsSettings(MemoryMetricsSettings(logEnabled = true, logSeconds = 5)),
reporter)
reporter
)
.unsafeRunSync()
verify(reporter).start(5, TimeUnit.SECONDS)
}
@ -79,7 +81,8 @@ class APIMetricsSpec extends WordSpec with Matchers with MockitoSugar with Eithe
APIMetrics
.initialize(
APIMetricsSettings(MemoryMetricsSettings(logEnabled = false, logSeconds = 5)),
reporter)
reporter
)
.unsafeRunSync()
verifyZeroInteractions(reporter)
}

View File

@ -72,14 +72,17 @@ class EmailNotifierSpec
"smtp",
"vinyldns.api.notifier.email.MockTransport",
"vinyl",
"1.0"))
"1.0"
)
)
override protected def beforeEach(): Unit =
reset(mockUserRepository, mockTransport)
def batchChange(
description: Option[String] = None,
changes: List[SingleChange] = List.empty): BatchChange =
changes: List[SingleChange] = List.empty
): BatchChange =
BatchChange(
"test",
"testUser",
@ -91,7 +94,8 @@ class EmailNotifierSpec
None,
None,
None,
"testBatch")
"testBatch"
)
"Email Notifier" should {
"do nothing for unsupported Notifications" in {
@ -100,7 +104,8 @@ class EmailNotifierSpec
"from" -> "Testing <test@test.com>",
"smtp.host" -> "wouldfail.mail.com",
"smtp.auth.mechanisms" -> "PLAIN"
).asJava)
).asJava
)
val notifier = new EmailNotifierProvider()
.load(NotifierConfig("", emailConfig), mockUserRepository)
.unsafeRunSync()
@ -149,8 +154,8 @@ class EmailNotifierSpec
)
doReturn(
IO.pure(Some(User("testUser", "access", "secret", None, None, Some("testuser@test.com")))))
.when(mockUserRepository)
IO.pure(Some(User("testUser", "access", "secret", None, None, Some("testuser@test.com"))))
).when(mockUserRepository)
.getUser("test")
val expectedAddresses = Array[Address](new InternetAddress("testuser@test.com"))
@ -176,7 +181,8 @@ class EmailNotifierSpec
None,
None,
None,
List.empty),
List.empty
),
SingleDeleteRRSetChange(
Some(""),
Some(""),
@ -188,7 +194,8 @@ class EmailNotifierSpec
Some("message for you"),
None,
None,
List.empty)
List.empty
)
)
val change = batchChange(Some(description), singleChanges)

Some files were not shown because too many files have changed in this diff Show More